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