izzi
SVG SUBSET C++ API
Loading...
Searching...
No Matches
a60-svg-render-basics.h
Go to the documentation of this file.
1// svg render basics -*- mode: C++ -*-
2
3// Copyright (C) 2014-2025 Benjamin De Kosnik <b.dekosnik@gmail.com>
4
5// This file is part of the alpha60-MiL SVG library. This library is
6// free software; you can redistribute it and/or modify it under the
7// terms of the GNU General Public License as published by the Free
8// Software Foundation; either version 3, or (at your option) any
9// later version.
10
11// This library is distributed in the hope that it will be useful, but
12// WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14// General Public License for more details.
15
16#ifndef MiL_SVG_RENDER_BASICS_H
17#define MiL_SVG_RENDER_BASICS_H 1
18
19//#include "a60-svg-codecvt.h"
20
21namespace svg {
22
23double
24scale_proportional_to_area(double radius, double weight)
25{
26 // Scale proportional to area of generated circle.
27 // π = 3.14159265358979323846
28 const double pi(22/7);
29 double a1 = radius * radius * pi;
30 double ap = a1 * weight;
31 double rpa = std::sqrt(ap/pi);
32 return rpa;
33}
34
35
36double
37scale_proportional_to_weight(double radius, double weight)
38{
39 double rpr = radius * weight;
40 return rpr;
41}
42
43
44/// Text elemetn at @param origin, with style.
45text_element
46style_text(const string text, const point_2t origin, const typography typo)
47{
48 auto [ x, y ] = origin;
49 text_element::data dt = { x, y, text, typo };
51 t.start_element();
52 t.add_data(dt);
54 return t;
55}
56
57
58/// Text at @param origin, with style.
59void
60styled_text(element_base& obj, const string text, const point_2t origin,
61 const typography typo)
62{
63 text_element t = style_text(text, origin, typo);
64 obj.add_element(t);
65}
66
67
68/// Text at @param origin, with style and transform
69void
70styled_text(element_base& obj, const string text, const point_2t origin,
71 const typography typo, const string xform)
72{
73 auto [ x, y ] = origin;
74 text_element::data dt = { space_type(x), space_type(y), text, typo };
76 t.start_element();
77 t.add_data(dt, xform);
79 obj.add_element(t);
80}
81
82
83/// Text at @param origin, with style and ...
84/// a transformation=rotation of @param deg about origin.
85void
86styled_text_r(element_base& obj, const string text, const point_2t origin,
87 const typography typo, const double deg)
88{
89 auto [ x, y ] = origin;
90 text_element::data dt = { space_type(x), space_type(y), text, typo };
92 t.start_element();
93 t.add_data(dt, svg::transform::rotate(deg, x, y));
95 obj.add_element(t);
96}
97
98
99/// Text at @param origin, with style and ...
100/// a transformation=rotation of @param deg about @param rorigin.
101void
102styled_text_r(element_base& obj, const string text, const point_2t origin,
103 const typography typo, const double deg, const point_2t rorigin)
104{
105 auto [ x, y ] = origin;
106 auto [ rx, ry ] = rorigin;
107 text_element::data dt = { space_type(x), space_type(y), text, typo };
108 text_element t;
109 t.start_element();
110 t.add_data(dt, svg::transform::rotate(deg, rx, ry));
111 t.finish_element();
112 obj.add_element(t);
113}
114
115
116/// XXX
117/// Text at @param origin, with style and link.
118void
119styled_text_link(element_base& obj, const string text, const point_2t origin,
120 const typography typo, const string uri)
121{
122 auto [ x, y ] = origin;
123 text_element::data dt = { space_type(x), space_type(y), text, typo };
124 text_element t;
125 t.start_element();
126 t.add_data(dt);
127 t.finish_element();
128
129 // Convert uri to utf8 so that it can be inserted into svg_element
130 // Fucking '&', mostly.
131 // string uriconv = convert_to_utf8(uri);
132 string uriconv(uri);
133 size_t pos = 0;
134 while ((pos = uriconv.find('&', pos)) != std::string::npos)
135 {
136 uriconv.replace(pos, 1, "&amp;");
137 pos += 5;
138 }
139
140 string astart = "<a href=";
141 astart += k::quote;
142 astart += uriconv;
143 astart += k::quote;
144 astart +=">";
145 astart += k::newline;
146 obj.add_raw(astart);
147
148 obj.add_element(t);
149
150 string afinish = "</a>";
151 afinish += k::newline;
152 obj.add_raw(afinish);
153}
154
155
156/// Text at size.
157void
158sized_text(element_base& obj, svg::typography typo, const int sz,
159 const string text, const int tx, const int ty)
160{
161 typo._M_size = sz;
162 text_element::data dt = { space_type(tx), space_type(ty), text, typo };
163 text_element t;
164 t.start_element();
165 t.add_data(dt);
166 t.finish_element();
167 obj.add_element(t);
168}
169
170
171/// Text at size, with a transformation=rotation.
172void
174 const string text, const int tx, const int ty, const double deg)
175{
176 typo._M_size = sz;
177 text_element::data dt = { space_type(tx), space_type(ty), text, typo };
178 text_element t;
179 t.start_element();
180 t.add_data(dt, svg::transform::rotate(deg, tx, ty));
181 t.finish_element();
182 obj.add_element(t);
183}
184
185
186/// Text of maxlen length, overflow goes on line below.
187uint
188text_line_n(svg_element& obj, const point_2t origin, const string text,
189 const svg::typography typo, const int sz, const uint maxlen)
190{
191 auto [ x, y ] = origin;
192
193 string textcut(text);
194 while (textcut.size() > maxlen)
195 {
196 // Find last space character in the specified maxium range, aka mark.
197 auto sppos = textcut.find_last_of(k::space, maxlen);
198 if (sppos == string::npos)
199 sppos = maxlen;
200 else
201 {
202 // Cut after space (mark).
203 sppos += 1;
204 }
205
206 string namesubs = textcut.substr(0, sppos);
207 sized_text(obj, typo, sz, namesubs, x, y);
208 textcut = textcut.substr(sppos);
209 y += sz;
210 }
211 sized_text(obj, typo, sz, textcut, x, y);
212 return y;
213}
214
215
216/// Text of maxlen length rotated, overflow goes on line below.
217uint
218text_line_n_r(svg_element& obj, const point_2t origin, const string text,
219 const svg::typography typo, const uint sz, const uint maxlen,
220 const uint lettingsz = 0)
221{
222 const auto [ x, y ] = origin;
223 const double line_max = std::ceil(double(text.size()) / maxlen);
224 const uint lines(line_max);
225
226 uint xmin(x);
227 string textcut(text);
228
229 uint linen(0);
230 while (linen < lines)
231 {
232 // Find x offset, with first line being the max length above the
233 // origin, working down. The last line is at the origin.
234 auto xp = x - ((sz + lettingsz) * (lines - linen - 1));
235
236 size_t epos(0);
237 if (textcut.size() < maxlen)
238 epos = textcut.size();
239 else
240 {
241 // Find last space character in the specified maxium range, aka mark.
242 auto sppos = textcut.find_last_of(k::space, maxlen);
243 if (sppos == string::npos)
244 sppos = maxlen;
245 else
246 {
247 // Cut after space (mark).
248 sppos += 1;
249 }
250 epos = sppos;
251 }
252
253 string namesubs = textcut.substr(0, epos);
254 sized_text_r(obj, typo, sz, namesubs, xp, y, -90);
255 textcut = textcut.substr(epos);
256 ++linen;
257 xmin = std::min(xmin, uint(xp));
258 }
259 return xmin;
260}
261
262
263/// Line primitive.
264line_element
265make_line(const point_2t origin, const point_2t end, style s,
266 const string dasharray = "")
267{
268 auto [ xo, yo ] = origin;
269 auto [ xe, ye ] = end;
270 line_element l;
271 line_element::data dr = { xo, xe, yo, ye };
272 l.start_element();
273 l.add_data(dr, dasharray);
274 l.add_style(s);
275 l.finish_element();
276 return l;
277}
278
279
280/// Line between two points.
281void
283 const point_2t origin, const point_2t end,
284 const string dasharray = "")
285{
286 line_element l = make_line(origin, end, s, dasharray);
287 obj.add_element(l);
288}
289
290
291/// Polyline primitive.
292polyline_element
293make_polyline(const vrange& points, const style s,
294 const stroke_style sstyle = { })
295{
296 polyline_element pl(points);
297 pl.start_element();
298 pl.add_data(sstyle);
299 pl.add_style(s);
300 pl.finish_element();
301 return pl;
302}
303
304
305/// Create rect_element at origin
307make_rect(const point_2t origin, const style s, const area<> a,
308 const string filterstr = "")
309{
310 auto [ width, height ] = a;
311 auto [ x, y ] = origin;
312
313 rect_element r;
314 rect_element::data dr = { x, y, width, height };
315 r.start_element();
316 r.add_data(dr);
317 r.add_style(s);
318
319 // Add blur with filter here.
320 if (!filterstr.empty())
321 r.add_filter(filterstr);
322 r.finish_element();
323 return r;
324}
325
326
327/// Create rect_element centered at origin
328rect_element
329make_rect_centered(const point_2t origin, const style s, const area<> a,
330 const string filterstr = "")
331{
332 auto [ width, height ] = a;
333 auto [ x, y ] = origin;
334 x -= (width / 2);
335 y -= (height / 2);
336 point_2t oprime { x, y };
337
338 rect_element r = make_rect(oprime, s, a, filterstr);
339 return r;
340}
341
342
343/// Rectangle at this point.
344void
346 double width = 4, double height = 4,
347 const string filterstr = "")
348{
349 rect_element r = make_rect(origin, s, { width, height}, filterstr);
350 obj.add_element(r);
351}
352
353
354/// Center a rectangle at this point.
355void
357 double width = 4, double height = 4,
358 const string filterstr = "")
359{
360 rect_element r = make_rect_centered(origin, s, { width, height}, filterstr);
361 obj.add_element(r);
362}
363
364
365/// Make circle element.
366circle_element
367make_circle(const point_2t origin, style s,
368 const space_type r = 4, const string xform = "")
369{
371 auto [ x, y ] = origin;
372 circle_element::data dc = { x, y, r };
373 c.start_element();
374 c.add_data(dc, xform);
375 c.add_style(s);
376 c.finish_element();
377 return c;
378}
379
380
381/// Draws a circle around a point (x,y), of style (s), of radius (r).
382void
384 const space_type r = 4, const string xform = "")
385{
386 circle_element c = make_circle(origin, s, r, xform);
387 obj.add_element(c);
388}
389
390
391/// Draws a ring centered at origin of radius r, with outer and inner
392/// radial gradient of blurspace in each direction.
393/// klr == fade from
394/// fadeklr == fade to. Background is transparent if none.
395void
397 const space_type radius, const double blurspace,
398 const svg::color klr,
399 const svg::color fadeklr = color::none,
400 const double opacity = 1)
401{
402 using atype = decltype(obj._M_area)::atype;
403
404 auto [ xd, yd ] = origin;
405 const atype x(xd);
406 const atype y(yd);
407
408 // outer ring == upper bound, radius + variance.
409 const double oring = radius + blurspace;
410
411 // inner ring == lower bound, radius - variance.
412 const double iring = radius - blurspace;
413
414 // mangled args for name.
415 std::ostringstream oss;
416 oss << "x" << std::to_string(x) << k::hyphen
417 << "y" << std::to_string(y) << k::hyphen
418 << "r" << std::to_string(radius) << k::hyphen
419 << "blurspace" << std::to_string(blurspace);
420 const string mangle(oss.str());
421
422 // outer
423 // strategy: make a bigger circle cprime, then do a radial gradient to it
424 // starting gradient from color at radius to 100% white/tranparent at cprime.
425 const string rgrado_name(string("radialout") + k::hyphen + mangle);
426 radial_gradient rgrado;
427 rgrado.start_element(rgrado_name);
428 rgrado.stop(rgrado.offset_percentage(radius, oring), fadeklr, 0);
429 rgrado.stop(rgrado.offset_percentage(radius, oring), klr, opacity);
430 rgrado.stop("100%", color::white, 0);
431 rgrado.finish_element();
432 obj.add_element(rgrado);
433
435 circle_element::data dco = { x, y, atype(oring) };
436 co.start_element();
437 co.add_data(dco);
438 co.add_fill(rgrado_name);
439 co.finish_element();
440 obj.add_element(co);
441
442 // inner
443 // strategy: make a smaller circle cprime, then do a radial gradient from it
444 // starting gradient from white/transparent at cprime to color at r.
445 const string rgradi_name(string("radialin") + k::hyphen + mangle);
446 radial_gradient rgradi;
447 rgradi.start_element(rgradi_name);
448 rgradi.stop(rgradi.offset_percentage(iring, radius), fadeklr, 0);
449 rgradi.stop("100%", klr, opacity);
450 rgradi.finish_element();
451 obj.add_element(rgradi);
452
453 // Float/Int conversion and rounding, add one to radius to close gap.
455 circle_element::data dci = { x, y, radius + 1 };
456 ci.start_element();
457 ci.add_data(dci);
458 ci.add_fill(rgradi_name);
459 ci.finish_element();
460 obj.add_element(ci);
461}
462
463
464/// Lines radiating from center point (x,y).
465void
466point_2d_to_ray(svg_element& obj, double x, double y, style s,
467 space_type r = 4, const uint nrays = 10)
468{
469 using atype = decltype(obj._M_area)::atype;
470
471 // End points on the ray.
472 // Pick a random ray, use an angle in the range [0, 2pi].
473 static std::mt19937_64 rg(std::random_device{}());
474 auto distr = std::uniform_real_distribution<>(0.0, 2 * 22/7);
475 auto disti = std::uniform_int_distribution<>(-3, 3);
476
477 for (uint i = 0; i < nrays; ++i)
478 {
479 double theta = distr(rg);
480 double rvary = disti(rg);
481
482 atype xe = x + (r + rvary) * std::cos(theta);
483 atype ye = y + (r + rvary) * std::sin(theta);
484
485 line_element::data dr = { atype(x), xe, atype(y), ye };
486 line_element ray;
487 ray.start_element();
488 ray.add_data(dr);
489 ray.add_style(s);
490 ray.finish_element();
491 obj.add_element(ray);
492 }
493}
494
495
496void
498 const point_2t& circump, const style& s,
499 const string id = "")
500{
501 using atype = decltype(obj._M_area)::atype;
502
503 auto [xo, yo] = origin;
504 auto [xc, yc] = circump;
505
506 line_element::data dr = { atype(xo), atype(xc), atype(yo), atype(yc) };
507 line_element ray;
508
509 if (id.empty())
510 ray.start_element();
511 else
512 ray.start_element(id);
513 ray.add_data(dr);
514 ray.add_style(s);
515 ray.finish_element();
516 obj.add_element(ray);
517}
518
519
520/// Angle in radians.
522get_circumference_point_r(const double angler, const double r,
523 const point_2t origin)
524{
525 auto [ cx, cy ] = origin;
526 double x(cx + (r * std::cos(angler)));
527 double y(cy - (r * std::sin(angler)));
528 return std::make_tuple(x, y);
529}
530
531
532/// Angle in degrees.
534get_circumference_point_d(const double ad, const double r,
535 const point_2t origin)
536{
537 double angler = (k::pi / 180.0) * ad;
538 return get_circumference_point_r(angler, r, origin);
539}
540
541
542/// Zero degrees is top, going clockwise (cw).
543double
545{
546 // Change rotation to CW instead of CCW (or anti-clockwise).
547 angled = 360 - angled;
548
549 // Rotate 90 CCW, so that the first element will be at the top
550 // vertical axis, instead of the right middle axis.
551 angled += 90;
552
553 return angled;
554}
555
556
557/// Zero degrees is top, going clockwise (cw).
558double
560{
561 // Rotate 90 CCW, so that the first element will be at the top
562 // vertical axis, instead of the right middle axis.
563 angled += 90;
564
565 return angled;
566}
567
568
569/// Make single path segment.
570string
572{
573 std::ostringstream ossa;
574 for (uint i = 0; i < lpoints.size(); ++i)
575 {
576 auto [x, y ] = lpoints[i];
577 // SVG path_element.
578 // start at "M x y" and
579 // each subsequent line segment is of form "L x y"
580 if (i == 0)
581 ossa << "M" << k::space;
582 else
583 ossa << "L" << k::space;
584 ossa << x << svg::k::space << y << k::space;
585 }
586 return ossa.str();
587}
588
589
590/// Draw path given serialized path data.
591/// Can be used to make pinstripes, ie top and bottom line layers.
592/// top style defaults: fill opac(0), stroke opac(1), stroke sz 1
593/// bottom style defaults: fill opac(0), stroke opac(1), stroke sz 1.25
594path_element
595make_path(const string& pathda, const style& styl, const string id = "",
596 const bool selfclosingtagp = true, const string xattr = "")
597{
598 // Draw path with this endpoint.
599 path_element pe;
601 {
602 path_element::data da = { pathda, 0 };
603 if (id.empty())
604 pe.start_element();
605 else
606 pe.start_element(id);
607 pe.add_data(da);
608 pe.add_style(styl);
609 if (!xattr.empty())
610 pe.add_raw(xattr);
611 if (selfclosingtagp)
612 pe.finish_element();
613 else
615 }
616 return pe;
617}
618
619
620/// Center a triangle at this point.
621path_element
622make_path_triangle(const point_2t origin, const style styl,
623 const double r = 4, const double angle = 120,
624 const bool selfclosingtagp = true, const string xattr = "")
625{
626 // Find points: orig, orig + (120 x 1), orig + (120 x 2).
627 double zo = zero_angle_north_cw(angle);
628 point_2t p1 = get_circumference_point_d(zo, r, origin);
629 point_2t p2 = get_circumference_point_d(zo + (angle * 1), r, origin);
630 point_2t p3 = get_circumference_point_d(zo + (angle * 2), r, origin);
631 vrange pointz = { p1, p2, p3, p1 };
632 string pathda = make_path_data_from_points(pointz);
633
634 path_element::data pthdata = { pathda, 0 };
635 path_element tri = make_path(pathda, styl, "", selfclosingtagp, xattr);
636 return tri;
637}
638
639
640/// Center a triangle at this point.
641void
642point_to_triangle(svg_element& obj, const point_2t origin, const style s,
643 const double r = 4, const double angle = 120)
644{
645 path_element tri = make_path_triangle(origin, s, r, angle);
646 obj.add_element(tri);
647}
648
649
650/// Center an octogon at this point.
651/// radius 4 is pixels to draw out from center point.
652/// pointsn is number of points to draw (8 for octogon)
653path_element
654make_path_octogon(const point_2t origin, const style styl,
655 const double r = 4, const uint pointsn = 8)
656{
657 // Find points: orig, orig + (120 x 1), orig + (120 x 2).
658 const double angle(360.0/8);
659 double zo = zero_angle_north_cw(angle);
660
661 // Eight points on a circle, connnected.
662 vrange pointz;
663 for (uint i = 0; i < pointsn; ++i)
664 {
665 point_2t p = get_circumference_point_d(zo + (angle * i), r, origin);
666 pointz.push_back(p);
667 }
668
669 // Final point to close path.
670 pointz.push_back(pointz.front());
671 string pathda = make_path_data_from_points(pointz);
672
673 const string id = "octogon-" + std::to_string(r);
674 path_element oct = make_path(pathda, styl, id);
675 return oct;
676}
677
678
679/// Center an octogon at this point.
680void
681point_to_octogon(svg_element& obj, const point_2t origin, const style s,
682 const double r = 4)
683{
684 path_element oct = make_path_octogon(origin, s, r);
685 obj.add_element(oct);
686}
687
688
689/// Make path segment between two points on a circumference of radius r.
690/// Points like: get_circumference_point_d(zero_angle_north_cw(0), r, origin)
691string
693 const space_type r, const int arcflag = 0,
694 const int sweepflag = 1)
695{
696 // Define arc.
697 // A rx ry x-axis-rotation large-arc-flag sweep-flag x y
698 std::ostringstream oss;
699 oss << "M" << k::space << to_string(start) << k::space;
700 oss << "A" << k::space;
701 oss << std::to_string(r) << k::space << std::to_string(r) << k::space;
702 oss << 0 << k::space << arcflag << k::space << sweepflag << k::space;
703 oss << to_string(end) << k::space;
704 return oss.str();
705}
706
707
708/// Make closed path between two points and the center of a circle of radius r.
709/// Points like: get_circumference_point_d(zero_angle_north_cw(0), r, origin)
710string
711make_path_arc_closed(const point_2t& origin, const point_2t& start,
712 const point_2t& end, const space_type r,
713 const int arcflag = 0, const int sweepflag = 0)
714{
715 // Define path as starting at origin, line to circumference point start,
716 // arc to circumfernce point end, line back to origin.
717 // A rx ry x-axis-rotation large-arc-flag sweep-flag x y
718 // where (large) arc flag is true if arc angle delta is > 180
719 // where sweep flag is
720 // true if outer (movement CW)
721 // false if inner (CCW).
722 std::ostringstream oss;
723 oss << "M" << k::space << to_string(origin) << k::space;
724 oss << "L" << k::space << to_string(start) << k::space;
725 oss << "A" << k::space;
726 oss << std::to_string(r) << k::space << std::to_string(r) << k::space;
727 oss << 0 << k::space << arcflag << k::space << sweepflag << k::space;
728 oss << to_string(end) << k::space;
729 oss << "L" << k::space << to_string(origin) << k::space;
730 return oss.str();
731}
732
733
734/// Same but with degree range arguments instead of points.
735/// NB: Assumes appropriate zero_angle_north_cw/ccw adjustments on startd/endd.
736string
737make_path_arc_closed(const point_2t& origin, const double startd,
738 const double endd, const space_type r,
739 const int arcflag = 0, const int sweepflag = 0)
740{
741 const point_2t start = get_circumference_point_d(startd, r, origin);
742 const point_2t end = get_circumference_point_d(endd, r, origin);
743 return make_path_arc_closed(origin, start, end, r, arcflag, sweepflag);
744}
745
746
747/// Plus or x tilt mark as closed path that can be filled.
748path_element
749make_path_center_mark(const point_2t& origin, const style styl,
750 const int len, const int width)
751{
752 // Define path as starting at origin, move half width up and to left then
753 // move around as if making a plus sign.
754 const auto [ xo, yo ] = origin;
755
756 // Move to top left of origin, start here.
757 const double whalf(width / 2);
758 const int lenw = len - whalf;
759 const auto x = xo - whalf;
760 const auto y = yo - whalf;
761
762 std::ostringstream oss;
763 oss << "M" << k::space << x << k::comma << y << k::space;
764
765 // left
766 oss << "H" << k::space << x - lenw << k::space;
767 oss << "V" << k::space << y + width << k::space;
768 oss << "H" << k::space << x << k::space;
769
770 // bottom
771 oss << "V" << k::space << y + lenw + width << k::space;
772 oss << "H" << k::space << x + width << k::space;
773 oss << "V" << k::space << y + width << k::space; // bottom part
774
775 // right
776 oss << "H" << k::space << x + lenw + width << k::space;
777 oss << "V" << k::space << y << k::space;
778 oss << "H" << k::space << x + width << k::space;
779
780 // top
781 oss << "V" << k::space << y - lenw << k::space;
782 oss << "H" << k::space << x << k::space;
783 oss << "V" << k::space << y << k::space;
784
785 const string pathdata = oss.str();
786
787 string id("center-mark-");
788 string attr(std::to_string(width) + "-" + std::to_string(len));
789 id += attr;
790
791 path_element cm = make_path(pathdata, styl, id);
792 return cm;
793}
794
795
796/// Crossed lines, no fill. X marks the ....
797string
798make_crossed_lines(const point_2t origin, const style s,
799 const double radius, const double tiltd = 0.0)
800{
801 auto d0 = zero_angle_north_cw(0 + tiltd);
802 auto d6 = zero_angle_north_cw(180 + tiltd);
803 auto d3 = zero_angle_north_cw(90 + tiltd);
804 auto d9 = zero_angle_north_cw(270 + tiltd);
805 point_2t p0 = get_circumference_point_d(d0, radius, origin);
806 point_2t p6 = get_circumference_point_d(d6, radius, origin);
807 point_2t p3 = get_circumference_point_d(d3, radius, origin);
808 point_2t p9 = get_circumference_point_d(d9, radius, origin);
809
810 line_element l1 = make_line(p0, p6, s);
811 line_element l2 = make_line(p3, p9, s);
812
813 std::ostringstream oss;
814 oss << l1.str();
815 oss << l2.str();
816 return oss.str();
817}
818
819
820/// Point to center mark as crossed lines.
821/// Default is a plus sign at origin, but @param tiltd can rotate.
822void
824 const style& styl, const int radius,
825 const double tiltd = 0.0)
826{
827 string pl = make_crossed_lines(origin, styl, radius, tiltd);
828 obj.add_raw(pl);
829}
830
831
832/// Make grid palette for display.
833/// NB @param klrs can be color_qis or array/palette.
834svg_element
835display_color_qis(const auto& klrs,
836 const area<> a, const typography& typobase)
837{
838 const auto [ awidth, aheight ] = a;
839
840 typography typo = typobase;
841 typo._M_style = k::w_style;
842 typo._M_size = 9;
846
847 // Draw out colors.
848 auto rwidth = 20;
849 auto rheight = 80;
850 auto rspace = 4;
851 auto typsz = 7;
852
853 // Turn off RAII.
854 svg_element obj("color_qis_" + std::to_string(klrs.size()) + "_palette",
855 a, false);
856 obj.start();
857
858 auto x = rwidth, y = rheight;
859 auto xoffset = 0;
860 for (const auto& klr : klrs)
861 {
862 // Color block
863 const style s = { klr, 1.0, klr, 0.0, 2 };
864 point_2t p = { x + xoffset, y };
865 point_to_rect_centered(obj, p, s, rwidth, rheight);
866
867 // Label.
868 sized_text_r(obj, typo, typsz, to_string(klr),
869 x + xoffset, y - rheight / 2 + rspace, 90);
870
871 if (xoffset + rwidth + rspace < awidth - rwidth - rspace)
872 xoffset += rwidth + rspace;
873 else
874 {
875 xoffset = 0;
876 y += (rheight + rspace + rspace);
877 }
878 }
879
880 obj.finish_element();
881 return obj;
882}
883
884} // namespace svg
885
886#endif
void add_filter(const string id)
void add_data(const data &d)
Either serialize immediately (as below), or create data structure that adds data to data_vec and then...
void add_style(const style &sty)
void add_data(const data &d, const string dasharray="")
const string offset_percentage(const ssize_type numer, const ssize_type denom)
void add_data(const data &d, string trans="")
void add_data(const data &d)
Either serialize immediately (as below), or create data structure that adds data to data_vec and then...
void stop(const string off, const color &klr, const double opacity=1.0)
void add_data(const data &d, const string trans="", const unit utype=svg::unit::point)
Either serialize immediately (as below), or create data structure that adds data to data_vec and then...
void add_raw(const string &raw)
void add_fill(const string id)
void start(const string &desc="")
string str() const
void add_element(const element_base &e)
static constexpr string finish_tag_hard
Abstract base class for all SVG Elements.
Circular gradients https://developer.mozilla.org/en-US/docs/Web/SVG/Element/radialGradient.
constexpr char space(' ')
Formatting character constants.
double zero_angle_north_ccw(double angled)
Zero degrees is top, going clockwise (cw).
double scale_proportional_to_area(double radius, double weight)
circle_element make_circle(const point_2t origin, style s, const space_type r=4, const string xform="")
Make circle element.
line_element make_line(const point_2t origin, const point_2t end, style s, const string dasharray="")
Line primitive.
void point_to_ring_halo(svg_element &obj, const point_2t origin, const space_type radius, const double blurspace, const svg::color klr, const svg::color fadeklr=color::none, const double opacity=1)
Draws a ring centered at origin of radius r, with outer and inner radial gradient of blurspace in eac...
color
Color enumerated as types.
text_element style_text(const string text, const point_2t origin, const typography typo)
Text elemetn at.
@ cm
Centimeter.
void sized_text_r(element_base &obj, svg::typography typo, const int sz, const string text, const int tx, const int ty, const double deg)
Text at size, with a transformation=rotation.
void point_to_circle(svg_element &obj, const point_2t origin, style s, const space_type r=4, const string xform="")
Draws a circle around a point (x,y), of style (s), of radius (r).
uint text_line_n(svg_element &obj, const point_2t origin, const string text, const svg::typography typo, const int sz, const uint maxlen)
Text of maxlen length, overflow goes on line below.
string make_path_data_from_points(const vrange &lpoints)
Make single path segment.
path_element make_path_center_mark(const point_2t &origin, const style styl, const int len, const int width)
Plus or x tilt mark as closed path that can be filled.
path_element make_path(const string &pathda, const style &styl, const string id="", const bool selfclosingtagp=true, const string xattr="")
Draw path given serialized path data. Can be used to make pinstripes, ie top and bottom line layers....
const string to_string(const unit e)
double zero_angle_north_cw(double angled)
Zero degrees is top, going clockwise (cw).
void point_to_octogon(svg_element &obj, const point_2t origin, const style s, const double r=4)
Center an octogon at this point.
double space_type
Base floating point type.
Definition a60-svg.h:62
@ text
metadata, header
void place_ray_at_angle(svg_element &obj, const point_2t &origin, const point_2t &circump, const style &s, const string id="")
void points_to_line(svg_element &obj, const style s, const point_2t origin, const point_2t end, const string dasharray="")
Line between two points.
void point_to_crossed_lines(svg_element &obj, const point_2t origin, const style &styl, const int radius, const double tiltd=0.0)
Point to center mark as crossed lines. Default is a plus sign at origin, but.
path_element make_path_triangle(const point_2t origin, const style styl, const double r=4, const double angle=120, const bool selfclosingtagp=true, const string xattr="")
Center a triangle at this point.
void styled_text(element_base &obj, const string text, const point_2t origin, const typography typo)
Text at.
svg_element display_color_qis(const auto &klrs, const area<> a, const typography &typobase)
Make grid palette for display. NB.
void point_2d_to_ray(svg_element &obj, double x, double y, style s, space_type r=4, const uint nrays=10)
Lines radiating from center point (x,y).
rect_element make_rect_centered(const point_2t origin, const style s, const area<> a, const string filterstr="")
Create rect_element centered at origin.
void styled_text_link(element_base &obj, const string text, const point_2t origin, const typography typo, const string uri)
XXX Text at.
string make_path_arc_closed(const point_2t &origin, const point_2t &start, const point_2t &end, const space_type r, const int arcflag=0, const int sweepflag=0)
Make closed path between two points and the center of a circle of radius r. Points like: get_circumfe...
std::vector< point_2t > vrange
Definition a60-svg.h:86
void point_to_rect_centered(element_base &obj, const point_2t origin, style s, double width=4, double height=4, const string filterstr="")
Center a rectangle at this point.
rect_element make_rect(const point_2t origin, const style s, const area<> a, const string filterstr="")
Create rect_element at origin.
point_2t get_circumference_point_d(const double ad, const double r, const point_2t origin)
Angle in degrees.
string make_crossed_lines(const point_2t origin, const style s, const double radius, const double tiltd=0.0)
Crossed lines, no fill. X marks the ....
void sized_text(element_base &obj, svg::typography typo, const int sz, const string text, const int tx, const int ty)
Text at size.
string make_path_arc_circumference(const point_2t &start, const point_2t &end, const space_type r, const int arcflag=0, const int sweepflag=1)
Make path segment between two points on a circumference of radius r. Points like: get_circumference_p...
void point_to_rect(element_base &obj, const point_2t origin, style s, double width=4, double height=4, const string filterstr="")
Rectangle at this point.
unsigned int uint
Definition a60-svg.h:57
void styled_text_r(element_base &obj, const string text, const point_2t origin, const typography typo, const double deg)
Text at.
uint text_line_n_r(svg_element &obj, const point_2t origin, const string text, const svg::typography typo, const uint sz, const uint maxlen, const uint lettingsz=0)
Text of maxlen length rotated, overflow goes on line below.
polyline_element make_polyline(const vrange &points, const style s, const stroke_style sstyle={ })
Polyline primitive.
double scale_proportional_to_weight(double radius, double weight)
point_2t get_circumference_point_r(const double angler, const double r, const point_2t origin)
Angle in radians.
std::tuple< space_type, space_type > point_2t
Point (x,y) in 2D space.
Definition a60-svg.h:65
path_element make_path_octogon(const point_2t origin, const style styl, const double r=4, const uint pointsn=8)
Center an octogon at this point. radius 4 is pixels to draw out from center point....
void point_to_triangle(svg_element &obj, const point_2t origin, const style s, const double r=4, const double angle=120)
Center a triangle at this point.
Additional path/line/polyline stroke styles.
Datum consolidating style preferences.
color_qi _M_stroke_color
static string rotate(int deg)
@ left
Left-most part of text block.
@ start
Start the text block at point.