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