Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
tikzgeom.H
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
31# ifndef TIKZGEOM_H
32# define TIKZGEOM_H
33
34# include <algorithm>
35# include <array>
36# include <cmath>
37# include <cstddef>
38# include <iomanip>
39# include <map>
40# include <ostream>
41# include <sstream>
42# include <string>
43# include <unordered_set>
44# include <type_traits>
45# include <utility>
46# include <variant>
47
48# include "line.H"
49# include "point.H"
50# include "polygon.H"
51# include "tpl_array.H"
52# include "tpl_sort_utils.H"
53
54namespace Aleph
55{
87 const std::string & library)
88 {
89 for (size_t i = 0; i < libraries.size(); ++i)
90 if (libraries(i) == library)
91 return;
92 libraries.append(library);
93 }
94
102 {
103 std::string tikz_style_name;
104 std::string draw_color = "black";
105 std::string fill_color;
106 std::string pattern;
107 std::string pattern_color;
108 std::string text_color;
109 std::string text_anchor;
110 std::string text_placement;
111 std::string extra_options;
112 std::string text_font_command;
113
114 double line_width_mm = -1.0;
115 double opacity = -1.0;
116
117 bool dashed = false;
118 bool dotted = false;
119 bool thick = false;
120 bool with_arrow = false;
121 bool fill = false;
122 };
123
139
156
166 {
168 bool closed = false;
169 };
170
172 inline Tikz_Style make_tikz_draw_style(const std::string & draw_color)
173 {
174 Tikz_Style s;
175 s.draw_color = draw_color;
176 return s;
177 }
178
180 inline Tikz_Style make_tikz_fill_style(const std::string & draw_color,
181 const std::string & fill_color)
182 {
183 Tikz_Style s;
184 s.draw_color = draw_color;
185 s.fill_color = fill_color;
186 s.fill = true;
187 return s;
188 }
189
200 {
201 public:
202 static constexpr int Layer_Background = -100;
203 static constexpr int Layer_Default = 0;
204 static constexpr int Layer_Foreground = 100;
205 static constexpr int Layer_Overlay = 1000;
206
208 using Object = std::variant<Point, Polar_Point, Segment, Triangle,
213
214 private:
223
224 struct Mapping
225 {
226 double sx = 1.0;
227 double sy = 1.0;
228 double dx = 0.0;
229 double dy = 0.0;
230 bool has_x_range = false;
231 bool has_y_range = false;
232 };
233
242
243 double wide_ = 0.0;
244 double height_ = 0.0;
245 double xoffset_ = 0.0;
246 double yoffset_ = 0.0;
247
250 bool with_grid_ticks_ = false;
252 bool with_auto_legend_ = false;
253 double point_radius_mm_ = 1.0;
254 double clip_padding_mm_ = -1.0;
255 double grid_step_x_ = 1.0;
256 double grid_step_y_ = 1.0;
257 double tick_size_mm_ = 1.5;
258 double legend_x_mm_ = 3.0;
259 double legend_y_mm_ = 3.0;
261
264 {
265 Tikz_Style s;
266 s.draw_color = "gray";
267 s.with_arrow = true;
268 return s;
269 }();
270
272 {
273 Tikz_Style s;
274 s.draw_color = "gray!40";
275 s.dotted = true;
276 s.line_width_mm = 0.2;
277 return s;
278 }();
279
281 {
282 std::string label;
284 };
285
287 std::map<std::string, Tikz_Style> tikz_style_defs_;
288
290 size_t next_order_ = 0;
291
292 [[nodiscard]] static std::string fmt_double(const double v)
293 {
294 std::ostringstream ss;
295 ss << std::fixed << std::setprecision(3) << v;
296
297 std::string out = ss.str();
298 if (const auto dot_pos = out.find('.'); dot_pos != std::string::npos)
299 {
300 while (not out.empty() and out.back() == '0')
301 out.pop_back();
302 if (not out.empty() and out.back() == '.')
303 out.pop_back();
304 }
305
306 if (out == "-0")
307 out = "0";
308
309 return out;
310 }
311
312 static void append_option(std::string & opts, const std::string & opt)
313 {
314 if (opt.empty())
315 return;
316 if (not opts.empty())
317 opts += ",";
318 opts += opt;
319 }
320
321 [[nodiscard]] static std::string draw_options(const Tikz_Style & style,
322 const bool allow_fill = false)
323 {
324 std::string opts;
325
326 if (not style.tikz_style_name.empty())
328
329 if (not style.draw_color.empty())
330 append_option(opts, "draw=" + style.draw_color);
331
332 if (allow_fill and style.fill)
333 {
334 if (not style.fill_color.empty())
335 append_option(opts, "fill=" + style.fill_color);
336 else if (not style.draw_color.empty())
337 append_option(opts, "fill=" + style.draw_color);
338
339 if (not style.pattern.empty())
340 {
341 append_option(opts, "pattern=" + style.pattern);
342 if (not style.pattern_color.empty())
343 append_option(opts, "pattern color=" + style.pattern_color);
344 }
345 }
346
347 if (style.line_width_mm > 0.0)
348 append_option(opts, "line width=" + fmt_double(style.line_width_mm) + "mm");
349
350 if (style.opacity >= 0.0)
351 append_option(opts, "opacity=" + fmt_double(style.opacity));
352
353 if (style.dashed)
354 append_option(opts, "dashed");
355 if (style.dotted)
356 append_option(opts, "dotted");
357 if (style.thick)
358 append_option(opts, "thick");
359 if (style.with_arrow)
360 append_option(opts, "->");
361
362 if (not style.extra_options.empty())
364
365 return opts;
366 }
367
368 [[nodiscard]] static std::string point_options(const Tikz_Style & style)
369 {
370 std::string opts;
371 if (not style.tikz_style_name.empty())
373
374 if (not style.fill_color.empty())
375 append_option(opts, "fill=" + style.fill_color);
376 else if (not style.draw_color.empty())
377 append_option(opts, "fill=" + style.draw_color);
378
379 if (style.opacity >= 0.0)
380 append_option(opts, "opacity=" + fmt_double(style.opacity));
381
382 if (not style.extra_options.empty())
384
385 return opts;
386 }
387
388 [[nodiscard]] static std::string text_options(const Tikz_Style & style)
389 {
390 std::string opts;
391 if (not style.tikz_style_name.empty())
393
394 if (not style.text_color.empty())
395 append_option(opts, "text=" + style.text_color);
396 else if (not style.draw_color.empty())
397 append_option(opts, "text=" + style.draw_color);
398
399 if (style.opacity >= 0.0)
400 append_option(opts, "opacity=" + fmt_double(style.opacity));
401
402 if (not style.text_anchor.empty())
403 append_option(opts, "anchor=" + style.text_anchor);
404 if (not style.text_placement.empty())
406
407 if (not style.extra_options.empty())
409
410 return opts;
411 }
412
415 const Tikz_Style & style) noexcept
416 {
417 return style.fill and not style.pattern.empty();
418 }
419
420 static Geom_Box make_box(const Geom_Number & xmin,
421 const Geom_Number & xmax,
422 const Geom_Number & ymin,
423 const Geom_Number & ymax)
424 {
426 box.xmin = xmin;
427 box.xmax = xmax;
428 box.ymin = ymin;
429 box.ymax = ymax;
430 box.initialized = true;
431 return box;
432 }
433
434 static void merge_box(Geom_Box & dst, const Geom_Box & src)
435 {
436 if (not src.initialized)
437 return;
438
439 if (not dst.initialized)
440 {
441 dst = src;
442 return;
443 }
444
445 dst.xmin = std::min(dst.xmin, src.xmin);
446 dst.xmax = std::max(dst.xmax, src.xmax);
447 dst.ymin = std::min(dst.ymin, src.ymin);
448 dst.ymax = std::max(dst.ymax, src.ymax);
449 }
450
451 static Geom_Box bbox_of(const Point & p)
452 {
453 return make_box(p.get_x(), p.get_x(), p.get_y(), p.get_y());
454 }
455
457 {
458 return bbox_of(Point(pp));
459 }
460
461 static Geom_Box bbox_of(const Segment & s)
462 {
465 }
466
467 static Geom_Box bbox_of(const Triangle & t)
468 {
471 }
472
473 static Geom_Box bbox_of(const Ellipse & e)
474 {
477 }
478
480 {
481 const auto extrema = e.extremal_points();
482 return make_box(extrema.left.get_x(), extrema.right.get_x(),
483 extrema.bottom.get_y(), extrema.top.get_y());
484 }
485
486 static Geom_Box bbox_of(const Text & t)
487 {
490 }
491
492 static Geom_Box bbox_of(const Polygon & poly)
493 {
494 if (poly.size() == 0)
495 return {};
496
498 for (Polygon::Vertex_Iterator it(poly); it.has_curr(); it.next_ne())
499 {
500 const Point p = it.get_current_vertex().to_point();
501 merge_box(box, bbox_of(p));
502 }
503 return box;
504 }
505
506 static Geom_Box bbox_of(const Regular_Polygon & poly)
507 {
508 if (poly.size() == 0)
509 return {};
510
512 for (size_t i = 0; i < poly.size(); ++i)
513 merge_box(box, bbox_of(poly.get_vertex(i)));
514 return box;
515 }
516
517 static Geom_Box bbox_of(const Rectangle & r)
518 {
519 return make_box(r.get_xmin(), r.get_xmax(), r.get_ymin(), r.get_ymax());
520 }
521
522 static Geom_Box bbox_of(const LineEq & l)
523 {
524 const auto x0 = Geom_Number(0);
525 const auto x1 = Geom_Number(1);
526 const Geom_Number y0 = l(x0);
527 const Geom_Number y1 = l(x1);
528 return make_box(std::min(x0, x1), std::max(x0, x1),
529 std::min(y0, y1), std::max(y0, y1));
530 }
531
533 {
535 merge_box(box, bbox_of(b.p0));
536 merge_box(box, bbox_of(b.p1));
537 merge_box(box, bbox_of(b.p2));
538 return box;
539 }
540
542 {
544 merge_box(box, bbox_of(b.p0));
545 merge_box(box, bbox_of(b.p1));
546 merge_box(box, bbox_of(b.p2));
547 merge_box(box, bbox_of(b.p3));
548 return box;
549 }
550
552 {
554 for (const auto & p : pl.vertices)
555 merge_box(box, bbox_of(p));
556 return box;
557 }
558
560 {
562 for (const auto & entry: objects_)
563 {
564 merge_box(box, std::visit(
565 [](const auto & val) { return bbox_of(val); }, entry.object));
566 }
567
568 return box;
569 }
570
572 {
573 return std::max(point_radius_mm_, clip_padding_mm_ > 0.0 ? clip_padding_mm_ : 0.0);
574 }
575
576 [[nodiscard]] static std::string escape_latex(const std::string & text)
577 {
578 std::string escaped;
579 escaped.reserve(text.size() + 8);
580 for (const char ch: text)
581 switch (ch)
582 {
583 case '\\': escaped += "\\textbackslash{}";
584 break;
585 case '{': escaped += "\\{";
586 break;
587 case '}': escaped += "\\}";
588 break;
589 case '$': escaped += "\\$";
590 break;
591 case '%': escaped += "\\%";
592 break;
593 case '#': escaped += "\\#";
594 break;
595 case '&': escaped += "\\&";
596 break;
597 case '_': escaped += "\\_";
598 break;
599 case '^': escaped += "\\^{}";
600 break;
601 case '~': escaped += "\\~{}";
602 break;
603 default: escaped.push_back(ch);
604 break;
605 }
606 return escaped;
607 }
608
609 [[nodiscard]] Mapping compute_mapping(const Geom_Box & box, const bool squarize) const
610 {
611 Mapping map;
612
613 if (not box.initialized)
614 {
615 map.dx = wide_ / 2.0;
616 map.dy = height_ / 2.0;
617 return map;
618 }
619
620 const Geom_Number x_range = box.xmax - box.xmin;
621 const Geom_Number y_range = box.ymax - box.ymin;
622
623 map.has_x_range = x_range != 0;
624 map.has_y_range = y_range != 0;
625
626 const double xr = map.has_x_range ? geom_number_to_double(x_range) : 0.0;
627 const double yr = map.has_y_range ? geom_number_to_double(y_range) : 0.0;
628
629 if (map.has_x_range)
630 map.sx = wide_ / xr;
631 if (map.has_y_range)
632 map.sy = height_ / yr;
633
635 {
636 const double s = std::min(map.sx, map.sy);
637 map.sx = s;
638 map.sy = s;
639
640 const double used_w = xr * s;
641 const double used_h = yr * s;
642 map.dx = (wide_ - used_w) / 2.0;
643 map.dy = (height_ - used_h) / 2.0;
644 }
645 else
646 {
647 map.dx = 0.0;
648 map.dy = 0.0;
649 }
650
651 if (not map.has_x_range)
652 map.dx = wide_ / 2.0;
653 if (not map.has_y_range)
654 map.dy = height_ / 2.0;
655
656 return map;
657 }
658
659 static double map_x(const Geom_Number & x,
660 const Geom_Box & box, const Mapping & map)
661 {
662 if (not map.has_x_range)
663 return map.dx;
664
665 return map.dx + geom_number_to_double(x - box.xmin) * map.sx;
666 }
667
668 static double map_y(const Geom_Number & y,
669 const Geom_Box & box, const Mapping & map)
670 {
671 if (not map.has_y_range)
672 return map.dy;
673
674 return map.dy + geom_number_to_double(y - box.ymin) * map.sy;
675 }
676
677 static std::pair<double, double>
678 map_point(const Point & p, const Geom_Box & box, const Mapping & map)
679 {
680 return {map_x(p.get_x(), box, map), map_y(p.get_y(), box, map)};
681 }
682
683 static double map_x_length(const Geom_Number & dx, const Mapping & map)
684 {
685 if (not map.has_x_range)
686 return 0.0;
687 return std::fabs(geom_number_to_double(dx)) * map.sx;
688 }
689
690 static double map_y_length(const Geom_Number & dy, const Mapping & map)
691 {
692 if (not map.has_y_range)
693 return 0.0;
694 return std::fabs(geom_number_to_double(dy)) * map.sy;
695 }
696
697 template <typename Out>
698 void draw_point(const Point & p, const Tikz_Style & style, Out & output,
699 const Geom_Box & box, const Mapping & map) const
700 {
701 const auto [x, y] = map_point(p, box, map);
702 output << " \\fill";
703 if (const std::string opts = point_options(style); not opts.empty())
704 output << "[" << opts << "]";
705
706 output << " (" << x << "," << y << ") "
707 << "circle[radius=" << point_radius_mm_ << "mm];" << std::endl;
708 }
709
710 template <typename Out>
712 const Array<Point> & vertices,
713 const bool closed,
714 const Tikz_Style & style,
715 const Geom_Box & box, const Mapping & map) const
716 {
717 if (vertices.is_empty())
718 return;
719
720 if (vertices.size() == 1)
721 {
722 draw_point(vertices.get_first(), style, output, box, map);
723 return;
724 }
725
726 output << " \\draw";
727 if (const std::string opts = draw_options(style, closed); not opts.empty())
728 output << "[" << opts << "]";
729 output << " ";
730
731 for (size_t i = 0; i < vertices.size(); ++i)
732 {
733 const auto [x, y] = map_point(vertices[i], box, map);
734 if (i == 0)
735 output << "(" << x << "," << y << ")";
736 else
737 output << " -- (" << x << "," << y << ")";
738 }
739
740 if (closed)
741 output << " -- cycle";
742 output << ";" << std::endl;
743 }
744
745 template <typename Out>
746 void draw_segment(const Segment & s, const Tikz_Style & style, Out & output,
747 const Geom_Box & box, const Mapping & map) const
748 {
749 Array<Point> vertices(2);
750 vertices.append(s.get_src_point());
751 vertices.append(s.get_tgt_point());
752 draw_polyline(output, vertices, false, style, box, map);
753 }
754
755 template <typename Out>
756 void draw_triangle(const Triangle & t, const Tikz_Style & style, Out & output,
757 const Geom_Box & box, const Mapping & map) const
758 {
759 Array<Point> vertices(3);
760 vertices.append(t.get_p1());
761 vertices.append(t.get_p2());
762 vertices.append(t.get_p3());
763 draw_polyline(output, vertices, true, style, box, map);
764 }
765
766 template <typename Out>
767 void draw_ellipse(const Ellipse & e, const Tikz_Style & style, Out & output,
768 const Geom_Box & box, const Mapping & map) const
769 {
770 const auto [cx, cy] = map_point(e.get_center(), box, map);
771 const double rx = map_x_length(e.get_hradius(), map);
772 const double ry = map_y_length(e.get_vradius(), map);
773
774 if (rx == 0.0 or ry == 0.0)
775 {
776 draw_point(e.get_center(), style, output, box, map);
777 return;
778 }
779
780 output << " \\draw";
781 if (const std::string opts = draw_options(style, true); not opts.empty())
782 output << "[" << opts << "]";
783 output << " (" << cx << "," << cy << ") "
784 << "ellipse [x radius=" << rx << ", y radius=" << ry << "];"
785 << std::endl;
786 }
787
788 template <typename Out>
789 static void draw_rotated_ellipse(const RotatedEllipse & e, const Tikz_Style & style,
790 Out & output, const Geom_Box & box,
791 const Mapping & map)
792 {
793 constexpr size_t sample_count = 96;
794 constexpr double two_pi = 2.0 * 3.14159265358979323846;
795
796 output << " \\draw";
797 if (const std::string opts = draw_options(style, true); not opts.empty())
798 output << "[" << opts << "]";
799 output << " ";
800
801 for (size_t i = 0; i <= sample_count; ++i)
802 {
803 const double t = two_pi * static_cast<double>(i) /
804 static_cast<double>(sample_count);
805
806 const Geom_Number cos_t = std::cos(t);
807 const Geom_Number sin_t = std::sin(t);
808
809 const Geom_Number local_x = e.get_a() * cos_t;
810 const Geom_Number local_y = e.get_b() * sin_t;
811
812 const Geom_Number world_x = e.get_center().get_x() +
813 local_x * e.get_cos() - local_y * e.get_sin();
814 const Geom_Number world_y = e.get_center().get_y() +
815 local_x * e.get_sin() + local_y * e.get_cos();
816
817 const auto [x, y] = map_point(Point(world_x, world_y), box, map);
818
819 if (i == 0)
820 output << "(" << x << "," << y << ")";
821 else
822 output << " -- (" << x << "," << y << ")";
823 }
824 output << " -- cycle;" << std::endl;
825 }
826
827 template <typename Out>
828 static void draw_text(const Text & t, const Tikz_Style & style, Out & output,
829 const Geom_Box & box, const Mapping & map)
830 {
831 const auto [x, y] = map_point(t.get_point(), box, map);
832
833 output << " \\node[inner sep=1pt";
834 if (const std::string opts = text_options(style); not opts.empty())
835 output << "," << opts;
836 output << "] at (" << x << "," << y << ") {";
837
838 if (not style.text_font_command.empty())
839 output << style.text_font_command << " ";
840
841 output << escape_latex(t.get_str()) << "};" << std::endl;
842 }
843
844 template <typename Out>
845 void draw_polygon(const Polygon & poly, const Tikz_Style & style, Out & output,
846 const Geom_Box & box, const Mapping & map) const
847 {
848 Array<Point> vertices;
849 vertices.reserve(poly.size());
850 for (Polygon::Vertex_Iterator it(poly); it.has_curr(); it.next_ne())
851 vertices.append(it.get_current_vertex().to_point());
852
853 draw_polyline(output, vertices, poly.is_closed(), style, box, map);
854 }
855
856 template <typename Out>
857 void draw_regular_polygon(const Regular_Polygon & poly, const Tikz_Style & style,
858 Out & output, const Geom_Box & box,
859 const Mapping & map) const
860 {
861 Array<Point> vertices;
862 vertices.reserve(poly.size());
863 for (size_t i = 0; i < poly.size(); ++i)
864 vertices.append(poly.get_vertex(i));
865
866 draw_polyline(output, vertices, true, style, box, map);
867 }
868
869 template <typename Out>
870 void draw_rectangle(const Rectangle & r, const Tikz_Style & style, Out & output,
871 const Geom_Box & box, const Mapping & map) const
872 {
873 Array<Point> vertices;
874 vertices.reserve(4);
875 const std::array<Point, 4> corners = r.corners();
876 for (const Point & p: corners)
877 vertices.append(p);
878 draw_polyline(output, vertices, true, style, box, map);
879 }
880
881 template <typename Out>
882 void draw_line_eq(const LineEq & l, const Tikz_Style & style, Out & output,
883 const Geom_Box & box, const Mapping & map) const
884 {
885 if (not box.initialized)
886 return;
887
889 clipped.reserve(4);
890
891 const auto append_if_new = [&](const Point & p)
892 {
893 for (const Point & existing: clipped)
894 if (existing == p)
895 return;
896 clipped.append(p);
897 };
898
899 const auto y_in_range = [&](const Geom_Number & y)
900 {
901 return box.ymin <= y and y <= box.ymax;
902 };
903 const auto x_in_range = [&](const Geom_Number & x)
904 {
905 return box.xmin <= x and x <= box.xmax;
906 };
907
908 if (const Geom_Number y_left = l(box.xmin); y_in_range(y_left))
910
911 if (const Geom_Number y_right = l(box.xmax); y_in_range(y_right))
913
914 if (l.m != Geom_Number(0))
915 {
916 if (const Geom_Number x_bottom = (box.ymin - l.y0) / l.m; x_in_range(x_bottom))
918
919 if (const Geom_Number x_top = (box.ymax - l.y0) / l.m; x_in_range(x_top))
921 }
922
923 if (clipped.size() < 2)
924 return;
925
926 size_t best_i = 0;
927 size_t best_j = 1;
928 Geom_Number best_d2 = clipped[0].distance_squared_to(clipped[1]);
929 for (size_t i = 0; i < clipped.size(); ++i)
930 for (size_t j = i + 1; j < clipped.size(); ++j)
931 {
932 const Geom_Number d2 = clipped[i].distance_squared_to(clipped[j]);
933 if (d2 > best_d2)
934 {
935 best_d2 = d2;
936 best_i = i;
937 best_j = j;
938 }
939 }
940
942 }
943
944 template <typename Out>
946 const Tikz_Style & style, Out & output,
947 const Geom_Box & box, const Mapping & map)
948 {
949 const auto [x0, y0] = map_point(b.p0, box, map);
950 const auto [x1, y1] = map_point(b.p1, box, map);
951 const auto [x2, y2] = map_point(b.p2, box, map);
952
953 output << " \\draw";
954 if (const std::string opts = draw_options(style, false); not opts.empty())
955 output << "[" << opts << "]";
956 output << " (" << x0 << "," << y0 << ") .. controls ("
957 << x1 << "," << y1 << ") .. (" << x2 << "," << y2 << ");"
958 << std::endl;
959 }
960
961 template <typename Out>
963 const Tikz_Style & style, Out & output,
964 const Geom_Box & box, const Mapping & map)
965 {
966 const auto [x0, y0] = map_point(b.p0, box, map);
967 const auto [x1, y1] = map_point(b.p1, box, map);
968 const auto [x2, y2] = map_point(b.p2, box, map);
969 const auto [x3, y3] = map_point(b.p3, box, map);
970
971 output << " \\draw";
972 if (const std::string opts = draw_options(style, false); not opts.empty())
973 output << "[" << opts << "]";
974 output << " (" << x0 << "," << y0 << ") .. controls ("
975 << x1 << "," << y1 << ") and (" << x2 << "," << y2
976 << ") .. (" << x3 << "," << y3 << ");"
977 << std::endl;
978 }
979
980 [[nodiscard]] static const char * layer_name(const int layer)
981 {
982 if (layer <= Layer_Background)
983 return "background";
984 if (layer >= Layer_Overlay)
985 return "overlay";
986 if (layer >= Layer_Foreground)
987 return "foreground";
988 return "main";
989 }
990
991 template <typename Out>
993 const Geom_Box & box, const Mapping & map) const
994 {
995 if (not with_coordinate_grid_ or not box.initialized)
996 return;
997
998 if (grid_step_x_ <= 0.0 or grid_step_y_ <= 0.0)
999 return;
1000
1001 const std::string opts = draw_options(grid_style_, false);
1002 const double xmin = geom_number_to_double(box.xmin);
1003 const double xmax = geom_number_to_double(box.xmax);
1004 const double ymin = geom_number_to_double(box.ymin);
1005 const double ymax = geom_number_to_double(box.ymax);
1006
1007 const double sx = grid_step_x_;
1008 const double sy = grid_step_y_;
1009 const int nx0 = static_cast<int>(std::ceil(xmin / sx));
1010 const int nx1 = static_cast<int>(std::floor(xmax / sx));
1011 const int ny0 = static_cast<int>(std::ceil(ymin / sy));
1012 const int ny1 = static_cast<int>(std::floor(ymax / sy));
1013
1014 for (int i = nx0; i <= nx1; ++i)
1015 {
1016 const Geom_Number x = Geom_Number(static_cast<double>(i) * sx);
1017 const double xm = map_x(x, box, map);
1018 output << " \\draw";
1019 if (not opts.empty())
1020 output << "[" << opts << "]";
1021 output << " (" << xm << ",0) -- (" << xm << "," << height_ << ");"
1022 << std::endl;
1023 }
1024
1025 for (int i = ny0; i <= ny1; ++i)
1026 {
1027 const auto y = Geom_Number(static_cast<double>(i) * sy);
1028 const double ym = map_y(y, box, map);
1029 output << " \\draw";
1030 if (not opts.empty())
1031 output << "[" << opts << "]";
1032 output << " (0," << ym << ") -- (" << wide_ << "," << ym << ");"
1033 << std::endl;
1034 }
1035
1037 return;
1038
1039 const std::string tick_opts = draw_options(axis_style_, false);
1040 const bool has_x_axis = map.has_y_range and box.ymin <= 0 and 0 <= box.ymax;
1041 const bool has_y_axis = map.has_x_range and box.xmin <= 0 and 0 <= box.xmax;
1042 const double y_axis_mm = has_x_axis ? map_y(Geom_Number(0), box, map) : -1.0;
1043 const double x_axis_mm = has_y_axis ? map_x(Geom_Number(0), box, map) : -1.0;
1044
1045 if (has_x_axis)
1046 for (int i = nx0; i <= nx1; ++i)
1047 {
1048 const auto x = Geom_Number(static_cast<double>(i) * sx);
1049 const double xm = map_x(x, box, map);
1050 output << " \\draw";
1051 if (not tick_opts.empty())
1052 output << "[" << tick_opts << "]";
1053 output << " (" << xm << "," << (y_axis_mm - tick_size_mm_ / 2.0)
1054 << ") -- (" << xm << "," << (y_axis_mm + tick_size_mm_ / 2.0) << ");"
1055 << std::endl;
1056 }
1057
1058 if (has_y_axis)
1059 for (int i = ny0; i <= ny1; ++i)
1060 {
1061 const auto y = Geom_Number(static_cast<double>(i) * sy);
1062 const double ym = map_y(y, box, map);
1063 output << " \\draw";
1064 if (not tick_opts.empty())
1065 output << "[" << tick_opts << "]";
1066 output << " (" << (x_axis_mm - tick_size_mm_ / 2.0) << "," << ym
1067 << ") -- (" << (x_axis_mm + tick_size_mm_ / 2.0) << "," << ym << ");"
1068 << std::endl;
1069 }
1070 }
1071
1073 {
1076 return entries;
1077
1078 std::unordered_set<std::string> seen;
1079 for (const auto & [label, style]: entries)
1080 seen.insert(label);
1081
1082 for (const auto & obj: objects_)
1083 {
1084 std::string color = obj.style.draw_color;
1085 if (color.empty() and not obj.style.fill_color.empty())
1086 color = obj.style.fill_color;
1087 if (color.empty())
1088 continue;
1089 if (seen.contains(color))
1090 continue;
1091
1094 legend_style.fill_color = obj.style.fill_color;
1095 legend_style.fill = obj.style.fill;
1096 legend_style.pattern = obj.style.pattern;
1097 legend_style.pattern_color = obj.style.pattern_color;
1099 seen.insert(color);
1100 }
1101
1102 return entries;
1103 }
1104
1105 template <typename Out>
1106 void draw_legend(Out & output) const
1107 {
1109 if (entries.is_empty())
1110 return;
1111
1112 for (size_t i = 0; i < entries.size(); ++i)
1113 {
1114 const double y = height_ - legend_y_mm_ - static_cast<double>(i) * legend_entry_step_mm_;
1115 const double x0 = legend_x_mm_;
1116 const double x1 = legend_x_mm_ + 7.0;
1117 const std::string opts = draw_options(entries[i].style, true);
1118
1119 output << " \\draw";
1120 if (not opts.empty())
1121 output << "[" << opts << "]";
1122 output << " (" << x0 << "," << y << ") -- (" << x1 << "," << y << ");"
1123 << std::endl;
1124
1125 output << " \\node[anchor=west] at (" << (x1 + 1.2) << "," << y
1126 << ") {" << escape_latex(entries[i].label) << "};" << std::endl;
1127 }
1128 }
1129
1130 template <typename Out>
1132 {
1133 if (tikz_style_defs_.empty())
1134 return;
1135
1136 for (const auto & [name, style]: tikz_style_defs_)
1137 {
1138 std::string opts = draw_options(style, true);
1139 if (const std::string txt = text_options(style); not txt.empty())
1141 output << " \\tikzset{" << name << "/.style={" << opts << "}}" << std::endl;
1142 }
1143 }
1144
1145 template <typename Out>
1147 const Geom_Box & box, const Mapping & map) const
1148 {
1149 if (not with_cartesian_axis_ or not box.initialized)
1150 return;
1151
1152 const std::string opts = draw_options(axis_style_, false);
1153
1154 if (map.has_y_range and box.ymin <= 0 and 0 <= box.ymax)
1155 {
1156 const double y0 = map_y(Geom_Number(0), box, map);
1157 output << " \\draw";
1158 if (not opts.empty())
1159 output << "[" << opts << "]";
1160 output << " (0," << y0 << ") -- (" << wide_ << "," << y0 << ");"
1161 << std::endl;
1162 }
1163
1164 if (map.has_x_range and box.xmin <= 0 and 0 <= box.xmax)
1165 {
1166 const double x0 = map_x(Geom_Number(0), box, map);
1167 output << " \\draw";
1168 if (not opts.empty())
1169 output << "[" << opts << "]";
1170 output << " (" << x0 << ",0) -- (" << x0 << "," << height_ << ");"
1171 << std::endl;
1172 }
1173 }
1174
1175 template <typename Out>
1176 void draw_object(const Styled_Object & entry, Out & output,
1177 const Geom_Box & box, const Mapping & map) const
1178 {
1179 std::visit([&]<typename T0>(const T0 & value)
1180 {
1181 using T = std::decay_t<T0>;
1182 if constexpr (std::is_same_v<T, Point>)
1183 draw_point(value, entry.style, output, box, map);
1184 else if constexpr (std::is_same_v<T, Polar_Point>)
1185 draw_point(Point(value), entry.style, output, box, map);
1186 else if constexpr (std::is_same_v<T, Segment>)
1187 draw_segment(value, entry.style, output, box, map);
1188 else if constexpr (std::is_same_v<T, Triangle>)
1189 draw_triangle(value, entry.style, output, box, map);
1190 else if constexpr (std::is_same_v<T, Ellipse>)
1191 draw_ellipse(value, entry.style, output, box, map);
1192 else if constexpr (std::is_same_v<T, RotatedEllipse>)
1193 draw_rotated_ellipse(value, entry.style, output, box, map);
1194 else if constexpr (std::is_same_v<T, Text>)
1195 draw_text(value, entry.style, output, box, map);
1196 else if constexpr (std::is_same_v<T, Polygon>)
1197 draw_polygon(value, entry.style, output, box, map);
1198 else if constexpr (std::is_same_v<T, Regular_Polygon>)
1199 draw_regular_polygon(value, entry.style, output, box, map);
1200 else if constexpr (std::is_same_v<T, Rectangle>)
1201 draw_rectangle(value, entry.style, output, box, map);
1202 else if constexpr (std::is_same_v<T, LineEq>)
1203 draw_line_eq(value, entry.style, output, box, map);
1204 else if constexpr (std::is_same_v<T, Tikz_Quadratic_Bezier>)
1206 else if constexpr (std::is_same_v<T, Tikz_Cubic_Bezier>)
1207 draw_cubic_bezier(value, entry.style, output, box, map);
1208 else if constexpr (std::is_same_v<T, Tikz_Polyline>)
1209 draw_polyline(output, value.vertices, value.closed,
1210 entry.style, box, map);
1211 },
1212 entry.object);
1213 }
1214
1215 public:
1225 Tikz_Plane(const double & wide, const double & height,
1226 const double & xoffset = 0.0, const double & yoffset = 0.0)
1227 : wide_(wide), height_(height), xoffset_(xoffset), yoffset_(yoffset)
1228 {
1230 << "Tikz_Plane width and height must be greater than zero";
1231 }
1232
1234 [[nodiscard]] const double &get_wide() const { return wide_; }
1236 [[nodiscard]] const double &get_height() const { return height_; }
1238 [[nodiscard]] const double &get_xoffset() const { return xoffset_; }
1240 [[nodiscard]] const double &get_yoffset() const { return yoffset_; }
1242 [[nodiscard]] const double &get_point_radius_mm() const { return point_radius_mm_; }
1244 [[nodiscard]] const double &get_clip_padding_mm() const { return clip_padding_mm_; }
1246 [[nodiscard]] size_t size() const { return objects_.size(); }
1247
1258 {
1260 const auto collect_style = [&libraries](const Tikz_Style & style)
1261 {
1262 if (style_requires_patterns(style))
1264 };
1265
1267 for (const auto & entry : legend_entries_)
1268 collect_style(entry.style);
1269 for (const auto & entry : tikz_style_defs_)
1270 collect_style(entry.second);
1271 for (const auto & object : objects_)
1272 collect_style(object.style);
1273
1274 return libraries;
1275 }
1276
1279
1281 [[nodiscard]] const Tikz_Style &get_axis_style() const { return axis_style_; }
1283 [[nodiscard]] const Tikz_Style &get_grid_style() const { return grid_style_; }
1284
1288 void set_default_style(const Tikz_Style & style)
1289 {
1290 default_style_ = style;
1291 }
1292
1296 void set_axis_style(const Tikz_Style & style)
1297 {
1298 axis_style_ = style;
1299 }
1300
1304 void set_grid_style(const Tikz_Style & style)
1305 {
1306 grid_style_ = style;
1307 }
1308
1314 void set_point_radius_mm(const double & radius_mm)
1315 {
1316 ah_domain_error_if(radius_mm <= 0) << "point radius must be greater than zero";
1318 }
1319
1324 void set_clip_padding_mm(const double & padding_mm)
1325 {
1327 }
1328
1331 {
1332 with_cartesian_axis_ = true;
1333 }
1334
1337 {
1338 with_cartesian_axis_ = false;
1339 }
1340
1342 void put_coordinate_grid(const double step_x = 1.0,
1343 const double step_y = 1.0,
1344 const bool draw_ticks = true)
1345 {
1346 ah_domain_error_if(step_x <= 0.0 or step_y <= 0.0)
1347 << "Grid steps must be greater than zero";
1348 with_coordinate_grid_ = true;
1352 }
1353
1356 {
1357 with_coordinate_grid_ = false;
1358 }
1359
1361 void enable_native_tikz_layers(const bool enabled = true)
1362 {
1363 with_native_layers_ = enabled;
1364 }
1365
1367 void enable_auto_legend(const bool enabled = true)
1368 {
1369 with_auto_legend_ = enabled;
1370 }
1371
1373 void add_legend_entry(const std::string & label, const Tikz_Style & style)
1374 {
1375 legend_entries_.append(Legend_Entry{label, style});
1376 }
1377
1380 {
1381 legend_entries_.clear();
1382 }
1383
1385 void register_tikz_style(const std::string & name, const Tikz_Style & style)
1386 {
1387 ah_domain_error_if(name.empty()) << "Style name cannot be empty";
1388 tikz_style_defs_[name] = style;
1389 }
1390
1393 {
1394 tikz_style_defs_.clear();
1395 }
1396
1398 void clear()
1399 {
1400 objects_.clear();
1401 next_order_ = 0;
1402 }
1403
1405 void put(const Object & obj)
1406 {
1408 }
1409
1411 void put(const Object & obj, const Tikz_Style & style, const int layer)
1412 {
1413 objects_.append(Styled_Object{obj, style, layer, next_order_++});
1414 }
1415
1422 void draw(std::ostream & output, const bool squarize = true) const
1423 {
1424 const Geom_Box box = compute_box();
1425 const Mapping map = compute_mapping(box, squarize);
1426
1427 output << std::fixed << std::setprecision(6);
1428 output << "% Requires: \\usepackage{tikz}" << std::endl;
1430 if (not libraries.is_empty())
1431 {
1432 output << "% TikZ libraries:";
1433 for (size_t i = 0; i < libraries.size(); ++i)
1434 output << (i == 0 ? " " : ",") << libraries(i);
1435 output << std::endl;
1436 }
1438 {
1439 output << "\\pgfdeclarelayer{background}" << std::endl
1440 << "\\pgfdeclarelayer{foreground}" << std::endl
1441 << "\\pgfdeclarelayer{overlay}" << std::endl;
1442 }
1443 output << "\\begin{tikzpicture}[x=1mm,y=1mm]" << std::endl;
1445 output << " \\pgfsetlayers{background,main,foreground,overlay}" << std::endl;
1447 output << " % " << objects_.size() << " geometric objects in plane" << std::endl;
1448 output << " \\begin{scope}[shift={(" << xoffset_ << "mm," << yoffset_ << "mm)}]"
1449 << std::endl;
1450 const double clip_padding = effective_clip_padding_mm();
1451 output << " \\clip (" << -clip_padding << "," << -clip_padding << ") rectangle ("
1452 << (wide_ + clip_padding) << "," << (height_ + clip_padding) << ");"
1453 << std::endl;
1454
1457
1459 for (size_t i = 0; i < objects_.size(); ++i)
1460 order(i) = i;
1461
1462 // objects_[...].order is a unique insertion index, so (layer, order)
1463 // is a total order with no real ties: an unstable sort here yields the
1464 // same result as a stable one.
1465 quicksort_op(order, [&](const size_t lhs, const size_t rhs)
1466 {
1467 if (objects_[lhs].layer != objects_[rhs].layer)
1468 return objects_[lhs].layer < objects_[rhs].layer;
1469 return objects_[lhs].order < objects_[rhs].order;
1470 });
1471
1473 {
1474 const char *current = nullptr;
1475 for (const size_t idx: order)
1476 {
1477 const char *target = layer_name(objects_[idx].layer);
1478 if (current == nullptr or std::string(current) != target)
1479 {
1480 if (current != nullptr)
1481 output << " \\end{pgfonlayer}" << std::endl;
1482 output << " \\begin{pgfonlayer}{" << target << "}" << std::endl;
1483 current = target;
1484 }
1485 draw_object(objects_[idx], output, box, map);
1486 }
1487 if (current != nullptr)
1488 output << " \\end{pgfonlayer}" << std::endl;
1489 }
1490 else
1491 for (const size_t idx: order)
1492 draw_object(objects_[idx], output, box, map);
1493
1495
1496 output << " \\end{scope}" << std::endl;
1497 output << "\\end{tikzpicture}" << std::endl;
1498 }
1499 };
1500
1505 template <typename Geom>
1507 std::is_constructible_v<Tikz_Plane::Object, std::decay_t<Geom>>;
1508
1510 template <TikzDrawable Geom>
1511 inline void put_in_plane(Tikz_Plane & plane, const Geom & geom_obj)
1512 {
1514 }
1515
1517 template <TikzDrawable Geom>
1518 inline void put_in_plane(Tikz_Plane & plane, const Geom & geom_obj,
1519 const Tikz_Style & style,
1520 const int layer = Tikz_Plane::Layer_Default)
1521 {
1522 plane.put(Tikz_Plane::Object(geom_obj), style, layer);
1523 }
1524
1527 const Point & p0,
1528 const Point & p1,
1529 const Point & p2,
1530 const size_t subdivisions = 64,
1531 const Tikz_Style & style = {},
1532 const int layer = Tikz_Plane::Layer_Default)
1533 {
1534 ah_domain_error_if(subdivisions == 0) << "Need at least 1 subdivision";
1535
1536 Tikz_Polyline curve;
1537 curve.vertices.reserve(subdivisions + 1);
1538 for (size_t i = 0; i <= subdivisions; ++i)
1539 {
1540 const Geom_Number t = Geom_Number(static_cast<long>(i))
1541 / Geom_Number(static_cast<long>(subdivisions));
1542 const Geom_Number s = Geom_Number(1) - t;
1543 const Geom_Number x = s * s * p0.get_x() +
1544 Geom_Number(2) * s * t * p1.get_x() +
1545 t * t * p2.get_x();
1546 const Geom_Number y = s * s * p0.get_y() +
1547 Geom_Number(2) * s * t * p1.get_y() +
1548 t * t * p2.get_y();
1549 curve.vertices.append(Point(x, y));
1550 }
1551
1552 put_in_plane(plane, curve, style, layer);
1553 }
1554
1557 const Point & p0,
1558 const Point & p1,
1559 const Point & p2,
1560 const Point & p3,
1561 const size_t subdivisions = 64,
1562 const Tikz_Style & style = {},
1563 const int layer = Tikz_Plane::Layer_Default)
1564 {
1565 ah_domain_error_if(subdivisions == 0) << "Need at least 1 subdivision";
1566
1567 Tikz_Polyline curve;
1568 curve.vertices.reserve(subdivisions + 1);
1569 for (size_t i = 0; i <= subdivisions; ++i)
1570 {
1571 const Geom_Number t = Geom_Number(static_cast<long>(i))
1572 / Geom_Number(static_cast<long>(subdivisions));
1573 const Geom_Number s = Geom_Number(1) - t;
1574 const Geom_Number s2 = s * s;
1575 const Geom_Number t2 = t * t;
1576 const Geom_Number x = s2 * s * p0.get_x() +
1577 Geom_Number(3) * s2 * t * p1.get_x() +
1578 Geom_Number(3) * s * t2 * p2.get_x() +
1579 t2 * t * p3.get_x();
1580 const Geom_Number y = s2 * s * p0.get_y() +
1581 Geom_Number(3) * s2 * t * p1.get_y() +
1582 Geom_Number(3) * s * t2 * p2.get_y() +
1583 t2 * t * p3.get_y();
1584 curve.vertices.append(Point(x, y));
1585 }
1586
1587 put_in_plane(plane, curve, style, layer);
1588 }
1589
1592 Tikz_Plane & plane,
1593 const Point & p0,
1594 const Point & p1,
1595 const Point & p2,
1596 const Tikz_Style & style = {},
1597 const int layer = Tikz_Plane::Layer_Default)
1598 {
1599 put_in_plane(plane, Tikz_Quadratic_Bezier{p0, p1, p2}, style, layer);
1600 }
1601
1604 Tikz_Plane & plane,
1605 const Point & p0,
1606 const Point & p1,
1607 const Point & p2,
1608 const Point & p3,
1609 const Tikz_Style & style = {},
1610 const int layer = Tikz_Plane::Layer_Default)
1611 {
1612 put_in_plane(plane, Tikz_Cubic_Bezier{p0, p1, p2, p3}, style, layer);
1613 }
1614
1617 Tikz_Plane & plane,
1618 const Point & point,
1619 const std::string & label,
1620 const std::string & placement = "above",
1621 const Tikz_Style & style = make_tikz_draw_style("black"),
1622 const int layer = Tikz_Plane::Layer_Overlay)
1623 {
1624 Tikz_Style label_style = style;
1626 put_in_plane(plane, Text(point, label), label_style, layer);
1627 }
1628} // namespace Aleph
1629
1630# endif // TIKZGEOM_H
#define ah_domain_error_if(C)
Throws std::domain_error if condition holds.
Definition ah-errors.H:527
size_t size_t int32_t value
Definition ca-c-api.h:116
size_t size_t int32_t * out
Definition ca-c-api.h:120
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:138
static Array create(size_t n)
Create an array with n logical elements.
Definition tpl_array.H:196
constexpr size_t size() const noexcept
Return the number of elements stored in the stack.
Definition tpl_array.H:365
constexpr bool is_empty() const noexcept
Checks if the container is empty.
Definition tpl_array.H:359
T & insert(const T &data)
insert a copy of data at the beginning of the array.
Definition tpl_array.H:286
T & get_first() noexcept
return a modifiable reference to the first element.
Definition tpl_array.H:378
T & append(const T &data)
Append a copy of data
Definition tpl_array.H:250
void reserve(size_t cap)
Reserves cap cells into the array.
Definition tpl_array.H:320
An axis-aligned ellipse.
Definition point.H:2076
const Geom_Number & get_vradius() const
Gets the vertical radius.
Definition point.H:2153
const Point & get_center() const
Gets the center point of the ellipse.
Definition point.H:2143
Point highest_point() const
Gets the highest point on the ellipse boundary.
Definition point.H:2234
Point rightmost_point() const
Gets the rightmost point on the ellipse boundary.
Definition point.H:2252
const Geom_Number & get_hradius() const
Gets the horizontal radius.
Definition point.H:2148
Point leftmost_point() const
Gets the leftmost point on the ellipse boundary.
Definition point.H:2246
Point lowest_point() const
Gets the lowest point on the ellipse boundary.
Definition point.H:2240
Represents a point with rectangular coordinates in a 2D plane.
Definition point.H:221
const Geom_Number & get_x() const noexcept
Gets the x-coordinate value.
Definition point.H:448
const Geom_Number & get_y() const noexcept
Gets the y-coordinate value.
Definition point.H:457
Polar representation of a 2D point.
Definition point.H:728
A general (irregular) 2D polygon defined by a sequence of vertices.
Definition polygon.H:247
const bool & is_closed() const
Check if the polygon is closed.
Definition polygon.H:474
const size_t & size() const
Get the number of vertices.
Definition polygon.H:478
An axis-aligned rectangle.
Definition point.H:1789
A regular polygon defined by center, side length, and vertex count.
Definition polygon.H:1135
Point get_vertex(const size_t &i) const
Get the i-th vertex of the polygon.
Definition polygon.H:1218
const size_t & size() const
Get the number of vertices.
Definition polygon.H:1196
An ellipse with arbitrary rotation.
Definition point.H:2473
const Geom_Number & get_b() const
Gets the semi-axis 'b' (local y-axis radius).
Definition point.H:2602
ExtremalPoints extremal_points() const
Computes the four axis-extremal points of the rotated ellipse.
Definition point.H:2743
const Geom_Number & get_sin() const
Gets the sine of the rotation angle.
Definition point.H:2612
const Geom_Number & get_a() const
Gets the semi-axis 'a' (local x-axis radius).
Definition point.H:2597
const Geom_Number & get_cos() const
Gets the cosine of the rotation angle.
Definition point.H:2607
const Point & get_center() const
Gets the center point.
Definition point.H:2592
Represents a line segment between two points.
Definition point.H:837
const Point & rightmost_point() const noexcept
Gets the endpoint with the largest x-coordinate.
Definition point.H:915
const Point & highest_point() const noexcept
Gets the endpoint with the largest y-coordinate.
Definition point.H:888
const Point & lowest_point() const noexcept
Gets the endpoint with the smallest y-coordinate.
Definition point.H:897
const Point & leftmost_point() const noexcept
Gets the endpoint with the smallest x-coordinate.
Definition point.H:906
const Point & get_tgt_point() const noexcept
Gets the target point of the segment.
Definition point.H:933
const Point & get_src_point() const noexcept
Gets the source point of the segment.
Definition point.H:924
Represents a text string positioned at a 2D point.
Definition point.H:2817
Point lowest_point() const
Gets the lowest point (the anchor point).
Definition point.H:2868
Point highest_point() const
Gets the highest point (the anchor point).
Definition point.H:2862
Point rightmost_point() const
Gets the rightmost point (the anchor point).
Definition point.H:2880
const std::string & get_str() const
Gets the text string.
Definition point.H:2856
const Point & get_point() const
Gets the position point of the text.
Definition point.H:2850
Point leftmost_point() const
Gets the leftmost point (the anchor point).
Definition point.H:2874
2D TikZ canvas storing geometry objects and emitting LaTeX output.
Definition tikzgeom.H:200
void draw_polyline(Out &output, const Array< Point > &vertices, const bool closed, const Tikz_Style &style, const Geom_Box &box, const Mapping &map) const
Definition tikzgeom.H:711
void set_axis_style(const Tikz_Style &style)
Configure the style used to draw Cartesian axes.
Definition tikzgeom.H:1296
void draw_object(const Styled_Object &entry, Out &output, const Geom_Box &box, const Mapping &map) const
Definition tikzgeom.H:1176
static std::pair< double, double > map_point(const Point &p, const Geom_Box &box, const Mapping &map)
Definition tikzgeom.H:678
Mapping compute_mapping(const Geom_Box &box, const bool squarize) const
Definition tikzgeom.H:609
static Geom_Box bbox_of(const Ellipse &e)
Definition tikzgeom.H:473
static std::string draw_options(const Tikz_Style &style, const bool allow_fill=false)
Definition tikzgeom.H:321
bool with_native_layers_
Definition tikzgeom.H:251
Array< Legend_Entry > collect_legend_entries() const
Definition tikzgeom.H:1072
void draw_regular_polygon(const Regular_Polygon &poly, const Tikz_Style &style, Out &output, const Geom_Box &box, const Mapping &map) const
Definition tikzgeom.H:857
Array< std::string > required_tikz_libraries() const
Collect TikZ libraries required by currently emitted features.
Definition tikzgeom.H:1257
std::variant< Point, Polar_Point, Segment, Triangle, Ellipse, RotatedEllipse, Text, Polygon, Regular_Polygon, Rectangle, LineEq, Tikz_Quadratic_Bezier, Tikz_Cubic_Bezier, Tikz_Polyline > Object
Runtime set of supported objects.
Definition tikzgeom.H:212
double tick_size_mm_
Definition tikzgeom.H:257
void draw(std::ostream &output, const bool squarize=true) const
Emit a complete tikzpicture with all inserted objects.
Definition tikzgeom.H:1422
void remove_coordinate_grid()
Disable coordinate grid rendering.
Definition tikzgeom.H:1355
void set_clip_padding_mm(const double &padding_mm)
Configure clip expansion around frame borders.
Definition tikzgeom.H:1324
void draw_point(const Point &p, const Tikz_Style &style, Out &output, const Geom_Box &box, const Mapping &map) const
Definition tikzgeom.H:698
void put_cartesian_axis()
Enable Cartesian axes drawing (only when 0 lies in range).
Definition tikzgeom.H:1330
static bool style_requires_patterns(const Tikz_Style &style) noexcept
Return true when rendering style needs the patterns library.
Definition tikzgeom.H:414
static constexpr int Layer_Default
Definition tikzgeom.H:203
static Geom_Box make_box(const Geom_Number &xmin, const Geom_Number &xmax, const Geom_Number &ymin, const Geom_Number &ymax)
Definition tikzgeom.H:420
void clear_tikz_styles()
Clear all registered \tikzset style definitions.
Definition tikzgeom.H:1392
Array< Legend_Entry > legend_entries_
Definition tikzgeom.H:286
void clear()
Remove all inserted objects from the plane.
Definition tikzgeom.H:1398
static std::string fmt_double(const double v)
Definition tikzgeom.H:292
static const char * layer_name(const int layer)
Definition tikzgeom.H:980
const double & get_wide() const
Plane width in millimeters.
Definition tikzgeom.H:1234
static void append_option(std::string &opts, const std::string &opt)
Definition tikzgeom.H:312
const double & get_height() const
Plane height in millimeters.
Definition tikzgeom.H:1236
void put(const Object &obj, const Tikz_Style &style, const int layer)
Insert an object with explicit style/layer.
Definition tikzgeom.H:1411
double grid_step_y_
Definition tikzgeom.H:256
void draw_tikzset_styles(Out &output) const
Definition tikzgeom.H:1131
static constexpr int Layer_Foreground
Definition tikzgeom.H:204
static std::string text_options(const Tikz_Style &style)
Definition tikzgeom.H:388
Tikz_Plane(const double &wide, const double &height, const double &xoffset=0.0, const double &yoffset=0.0)
Construct a TikZ plane with fixed frame size.
Definition tikzgeom.H:1225
double point_radius_mm_
Definition tikzgeom.H:253
static void draw_text(const Text &t, const Tikz_Style &style, Out &output, const Geom_Box &box, const Mapping &map)
Definition tikzgeom.H:828
void enable_auto_legend(const bool enabled=true)
Enable auto-legend generation from style colors.
Definition tikzgeom.H:1367
static double map_y(const Geom_Number &y, const Geom_Box &box, const Mapping &map)
Definition tikzgeom.H:668
void enable_native_tikz_layers(const bool enabled=true)
Enable native PGF layers (\pgfdeclarelayer).
Definition tikzgeom.H:1361
void draw_cartesian_axis(Out &output, const Geom_Box &box, const Mapping &map) const
Definition tikzgeom.H:1146
bool with_cartesian_axis_
Definition tikzgeom.H:248
Geom_Box compute_box() const
Definition tikzgeom.H:559
static std::string point_options(const Tikz_Style &style)
Definition tikzgeom.H:368
size_t size() const
Number of currently inserted objects.
Definition tikzgeom.H:1246
Tikz_Style grid_style_
Definition tikzgeom.H:271
void put_coordinate_grid(const double step_x=1.0, const double step_y=1.0, const bool draw_ticks=true)
Enable coordinate grid/ticks with user step sizes.
Definition tikzgeom.H:1342
static Geom_Box bbox_of(const Polar_Point &pp)
Definition tikzgeom.H:456
bool with_coordinate_grid_
Definition tikzgeom.H:249
static Geom_Box bbox_of(const Tikz_Cubic_Bezier &b)
Definition tikzgeom.H:541
void draw_legend(Out &output) const
Definition tikzgeom.H:1106
const double & get_yoffset() const
Vertical scope offset in millimeters.
Definition tikzgeom.H:1240
static constexpr int Layer_Background
Definition tikzgeom.H:202
static double map_x_length(const Geom_Number &dx, const Mapping &map)
Definition tikzgeom.H:683
Tikz_Style default_style_
Definition tikzgeom.H:262
static std::string escape_latex(const std::string &text)
Definition tikzgeom.H:576
void set_point_radius_mm(const double &radius_mm)
Configure point marker radius.
Definition tikzgeom.H:1314
void draw_rectangle(const Rectangle &r, const Tikz_Style &style, Out &output, const Geom_Box &box, const Mapping &map) const
Definition tikzgeom.H:870
static double map_x(const Geom_Number &x, const Geom_Box &box, const Mapping &map)
Definition tikzgeom.H:659
static void draw_cubic_bezier(const Tikz_Cubic_Bezier &b, const Tikz_Style &style, Out &output, const Geom_Box &box, const Mapping &map)
Definition tikzgeom.H:962
double legend_x_mm_
Definition tikzgeom.H:258
Array< Styled_Object > objects_
Definition tikzgeom.H:289
const Tikz_Style & get_default_style() const
Default style used by put(obj) and non-styled put_in_plane.
Definition tikzgeom.H:1278
void put(const Object &obj)
Insert an object with default style on default layer.
Definition tikzgeom.H:1405
static void draw_rotated_ellipse(const RotatedEllipse &e, const Tikz_Style &style, Out &output, const Geom_Box &box, const Mapping &map)
Definition tikzgeom.H:789
static Geom_Box bbox_of(const Polygon &poly)
Definition tikzgeom.H:492
static Geom_Box bbox_of(const LineEq &l)
Definition tikzgeom.H:522
void draw_polygon(const Polygon &poly, const Tikz_Style &style, Out &output, const Geom_Box &box, const Mapping &map) const
Definition tikzgeom.H:845
static Geom_Box bbox_of(const Triangle &t)
Definition tikzgeom.H:467
void set_grid_style(const Tikz_Style &style)
Configure the style used to draw coordinate grids.
Definition tikzgeom.H:1304
const Tikz_Style & get_axis_style() const
Style used for Cartesian axes when enabled.
Definition tikzgeom.H:1281
void draw_coordinate_grid(Out &output, const Geom_Box &box, const Mapping &map) const
Definition tikzgeom.H:992
static Geom_Box bbox_of(const Segment &s)
Definition tikzgeom.H:461
static Geom_Box bbox_of(const Point &p)
Definition tikzgeom.H:451
void draw_triangle(const Triangle &t, const Tikz_Style &style, Out &output, const Geom_Box &box, const Mapping &map) const
Definition tikzgeom.H:756
double grid_step_x_
Definition tikzgeom.H:255
static Geom_Box bbox_of(const Tikz_Quadratic_Bezier &b)
Definition tikzgeom.H:532
static Geom_Box bbox_of(const Tikz_Polyline &pl)
Definition tikzgeom.H:551
void register_tikz_style(const std::string &name, const Tikz_Style &style)
Register a reusable \tikzset style for the rendered picture.
Definition tikzgeom.H:1385
static double map_y_length(const Geom_Number &dy, const Mapping &map)
Definition tikzgeom.H:690
void add_legend_entry(const std::string &label, const Tikz_Style &style)
Add a legend entry to be rendered in the top-left corner.
Definition tikzgeom.H:1373
Tikz_Style axis_style_
Definition tikzgeom.H:263
static Geom_Box bbox_of(const Rectangle &r)
Definition tikzgeom.H:517
static void merge_box(Geom_Box &dst, const Geom_Box &src)
Definition tikzgeom.H:434
static Geom_Box bbox_of(const Regular_Polygon &poly)
Definition tikzgeom.H:506
static void draw_quadratic_bezier(const Tikz_Quadratic_Bezier &b, const Tikz_Style &style, Out &output, const Geom_Box &box, const Mapping &map)
Definition tikzgeom.H:945
const Tikz_Style & get_grid_style() const
Style used for coordinate grids when enabled.
Definition tikzgeom.H:1283
double legend_y_mm_
Definition tikzgeom.H:259
void draw_segment(const Segment &s, const Tikz_Style &style, Out &output, const Geom_Box &box, const Mapping &map) const
Definition tikzgeom.H:746
void clear_legend()
Remove user-provided legend entries.
Definition tikzgeom.H:1379
void draw_line_eq(const LineEq &l, const Tikz_Style &style, Out &output, const Geom_Box &box, const Mapping &map) const
Definition tikzgeom.H:882
static Geom_Box bbox_of(const Text &t)
Definition tikzgeom.H:486
double effective_clip_padding_mm() const
Definition tikzgeom.H:571
void set_default_style(const Tikz_Style &style)
Configure the default style for subsequent non-styled inserts.
Definition tikzgeom.H:1288
static constexpr int Layer_Overlay
Definition tikzgeom.H:205
const double & get_clip_padding_mm() const
Clip expansion around the frame; <= 0 means auto (point radius).
Definition tikzgeom.H:1244
const double & get_point_radius_mm() const
Radius used to draw point primitives (millimeters).
Definition tikzgeom.H:1242
double legend_entry_step_mm_
Definition tikzgeom.H:260
static Geom_Box bbox_of(const RotatedEllipse &e)
Definition tikzgeom.H:479
void draw_ellipse(const Ellipse &e, const Tikz_Style &style, Out &output, const Geom_Box &box, const Mapping &map) const
Definition tikzgeom.H:767
std::map< std::string, Tikz_Style > tikz_style_defs_
Definition tikzgeom.H:287
const double & get_xoffset() const
Horizontal scope offset in millimeters.
Definition tikzgeom.H:1238
double clip_padding_mm_
Definition tikzgeom.H:254
void remove_cartesian_axis()
Disable Cartesian axes drawing.
Definition tikzgeom.H:1336
A non-degenerate triangle defined by three points.
Definition point.H:1512
const Point & get_p3() const
Gets the third vertex.
Definition point.H:1629
const Point & get_p2() const
Gets the second vertex.
Definition point.H:1624
const Point & rightmost_point() const
Gets the vertex with the largest x-coordinate.
Definition point.H:1612
const Point & lowest_point() const
Gets the vertex with the smallest y-coordinate.
Definition point.H:1592
const Point & get_p1() const
Gets the first vertex.
Definition point.H:1619
const Point & highest_point() const
Gets the vertex with the largest y-coordinate.
Definition point.H:1582
const Point & leftmost_point() const
Gets the vertex with the smallest x-coordinate.
Definition point.H:1602
A geometry type that Tikz_Plane::Object can wrap.
Definition tikzgeom.H:1506
__gmp_expr< T, __gmp_unary_expr< __gmp_expr< T, U >, __gmp_y1_function > > y1(const __gmp_expr< T, U > &expr)
Definition gmpfrxx.h:4114
__gmp_expr< T, __gmp_unary_expr< __gmp_expr< T, U >, __gmp_y0_function > > y0(const __gmp_expr< T, U > &expr)
Definition gmpfrxx.h:4113
bool squarize
Definition graphpic.C:259
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
2D infinite line representation using slope-intercept form.
static mpfr_t y
Definition mpfr_mul_d.c:3
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
void put_cubic_bezier_native_in_plane(Tikz_Plane &plane, const Point &p0, const Point &p1, const Point &p2, const Point &p3, const Tikz_Style &style={}, const int layer=Tikz_Plane::Layer_Default)
Insert a cubic Bézier using native TikZ controls syntax.
Definition tikzgeom.H:1603
void put_quadratic_bezier_native_in_plane(Tikz_Plane &plane, const Point &p0, const Point &p1, const Point &p2, const Tikz_Style &style={}, const int layer=Tikz_Plane::Layer_Default)
Insert a quadratic Bézier using native TikZ controls syntax.
Definition tikzgeom.H:1591
void put_cubic_bezier_in_plane(Tikz_Plane &plane, const Point &p0, const Point &p1, const Point &p2, const Point &p3, const size_t subdivisions=64, const Tikz_Style &style={}, const int layer=Tikz_Plane::Layer_Default)
Approximate and insert a cubic Bézier as an open polyline.
Definition tikzgeom.H:1556
void put_in_plane(Tikz_Plane &plane, const Geom &geom_obj)
Insert any supported geometry type in a Tikz_Plane.
Definition tikzgeom.H:1511
and
Check uniqueness with explicit hash + equality functors.
std::decay_t< typename HeadC::Item_Type > T
Definition ah-zip.H:105
double geom_number_to_double(const Geom_Number &n)
Converts a Geom_Number to its double precision representation.
Definition point.H:120
void put_quadratic_bezier_in_plane(Tikz_Plane &plane, const Point &p0, const Point &p1, const Point &p2, const size_t subdivisions=64, const Tikz_Style &style={}, const int layer=Tikz_Plane::Layer_Default)
Approximate and insert a quadratic Bézier as an open polyline.
Definition tikzgeom.H:1526
mpq_class Geom_Number
Numeric type used by the geometry module.
Definition point.H:113
void put_point_label_in_plane(Tikz_Plane &plane, const Point &point, const std::string &label, const std::string &placement="above", const Tikz_Style &style=make_tikz_draw_style("black"), const int layer=Tikz_Plane::Layer_Overlay)
Insert a point label with configurable placement (above, etc.).
Definition tikzgeom.H:1616
void append_unique_tikz_library(Array< std::string > &libraries, const std::string &library)
Append library into libraries unless already present.
Definition tikzgeom.H:86
Tikz_Style make_tikz_draw_style(const std::string &draw_color)
Create a basic draw style with a custom color.
Definition tikzgeom.H:172
Tikz_Style make_tikz_fill_style(const std::string &draw_color, const std::string &fill_color)
Create a basic fill style with custom draw/fill colors.
Definition tikzgeom.H:180
static long & color(typename GT::Node *p)
void quicksort_op(C< T > &a, const Compare &cmp=Compare(), const size_t threshold=Quicksort_Threshold)
Optimized quicksort for containers using operator().
2D point and geometric utilities.
2D polygon representation and geometric operations.
static Rectangle box(const int x1, const int y1, const int x2, const int y2)
2D infinite line in slope-intercept form.
Definition line.H:126
Iterator over the vertices of a polygon.
Definition polygon.H:490
Represents a cubic Bézier curve for native TikZ rendering.
Definition tikzgeom.H:150
Point p3
End point.
Definition tikzgeom.H:154
Point p1
First control point.
Definition tikzgeom.H:152
Point p2
Second control point.
Definition tikzgeom.H:153
Point p0
Start point.
Definition tikzgeom.H:151
Definition tikzgeom.H:281
Tikz_Style style
Definition tikzgeom.H:283
std::string label
Definition tikzgeom.H:282
An object with its associated style, layer, and insertion order.
Definition tikzgeom.H:236
Tikz_Style style
The style used to render the object.
Definition tikzgeom.H:238
int layer
The rendering layer.
Definition tikzgeom.H:239
Object object
The geometric object.
Definition tikzgeom.H:237
size_t order
Insertion order within the layer.
Definition tikzgeom.H:240
An open or closed polyline for TikZ rendering.
Definition tikzgeom.H:166
Array< Point > vertices
Ordered vertices.
Definition tikzgeom.H:167
bool closed
Whether to close the polyline.
Definition tikzgeom.H:168
Represents a quadratic Bézier curve for native TikZ rendering.
Definition tikzgeom.H:134
Point p1
Control point.
Definition tikzgeom.H:136
Point p0
Start point.
Definition tikzgeom.H:135
Style descriptor for TikZ primitives.
Definition tikzgeom.H:102
std::string text_anchor
TikZ node anchor (anchor=<value>)
Definition tikzgeom.H:109
std::string fill_color
TikZ fill color (fill=<color>)
Definition tikzgeom.H:105
bool with_arrow
Add ->
Definition tikzgeom.H:120
std::string pattern
TikZ pattern (e.g. north east lines)
Definition tikzgeom.H:106
double opacity
opacity=<value> in [0,1], when >= 0
Definition tikzgeom.H:115
bool thick
Add thick
Definition tikzgeom.H:119
std::string extra_options
Raw extra options appended as-is.
Definition tikzgeom.H:111
std::string text_color
TikZ text color (text=<color>)
Definition tikzgeom.H:108
bool fill
Fill closed shapes (polygon/triangle/ellipse)
Definition tikzgeom.H:121
std::string text_placement
TikZ placement (above, below left, ...)
Definition tikzgeom.H:110
std::string tikz_style_name
Optional named style (from \tikzset)
Definition tikzgeom.H:103
double line_width_mm
line width=<value>mm when > 0
Definition tikzgeom.H:114
bool dashed
Add dashed
Definition tikzgeom.H:117
std::string draw_color
TikZ draw color (draw=<color>)
Definition tikzgeom.H:104
bool dotted
Add dotted
Definition tikzgeom.H:118
std::string text_font_command
e.g. \\small, \\footnotesize
Definition tikzgeom.H:112
std::string pattern_color
TikZ pattern color (pattern color=<color>)
Definition tikzgeom.H:107
gsl_rng * r
Dynamic array container with automatic resizing.
Comprehensive sorting algorithms and search utilities for Aleph-w.
DynList< int > l
ofstream output
Definition writeHeap.C:215