SVG for C++
svg.hpp
Go to the documentation of this file.
1 
2 #pragma once
3 #define PI 3.14159265
4 #define SVG_TYPE_CHECK static_assert(std::is_base_of<Element, T>::value, "Child must be an SVG element.")
5 #define APPROX_EQUALS(x, y, tol) bool(abs(x - y) < tol)
6 #include <iostream>
7 #include <algorithm> // min, max
8 #include <fstream> // ofstream
9 #include <math.h> // NAN
10 #include <map>
11 #include <deque>
12 #include <vector>
13 #include <string>
14 #include <sstream> // stringstream
15 #include <iomanip> // setprecision
16 #include <memory>
17 #include <type_traits> // is_base_of
18 #include <typeinfo>
19 
20 namespace SVG {
24  class AttributeMap;
25  class SVG;
26  class Shape;
27 
28  struct QuadCoord {
29  double x1;
30  double x2;
31  double y1;
32  double y2;
33  };
34 
35  using SelectorProperties = std::map<std::string, AttributeMap>;
36  using SVGAttrib = std::map<std::string, std::string>;
37  using Point = std::pair<double, double>;
38  using Margins = QuadCoord;
39 
40  inline std::string to_string(const double& value);
41  inline std::string to_string(const Point& point);
42  inline std::string to_string(const std::map<std::string, AttributeMap>& css, const size_t indent_level=0);
43 
44  std::vector<Point> bounding_polygon(const std::vector<Shape*>& shapes);
45  SVG frame_animate(std::vector<SVG>& frames, const double fps);
46  SVG merge(SVG& left, SVG& right, const Margins& margins = DEFAULT_MARGINS);
47  const static Margins DEFAULT_MARGINS = { 10, 10, 10, 10 };
48  const static Margins NO_MARGINS = { 0, 0, 0, 0 };
49 
53  namespace util {
54  enum Orientation {
55  COLINEAR, CLOCKWISE, COUNTERCLOCKWISE
56  };
57 
58  inline std::vector<Point> polar_points(int n, int a, int b, double radius);
59 
60  template<typename T>
61  inline T min_or_not_nan(T first, T second) {
65  if (isnan(first) && isnan(second))
66  return NAN;
67  else if (isnan(first) || isnan(second))
68  return isnan(first) ? second : first;
69  else
70  return std::min(first, second);
71  }
72 
73  template<typename T>
74  inline T max_or_not_nan(T first, T second) {
78  if (isnan(first) && isnan(second))
79  return NAN;
80  else if (isnan(first) || isnan(second))
81  return isnan(first) ? second : first;
82  else
83  return std::max(first, second);
84  }
85 
86  inline Orientation orientation(Point& p1, Point& p2, Point& p3) {
87  double value = ((p2.second - p1.second) * (p3.first - p2.first) -
88  (p2.first - p1.first) * (p3.second - p2.second));
89 
90  if (value == 0) return COLINEAR;
91  else if (value > 0) return CLOCKWISE;
92  else return COUNTERCLOCKWISE;
93  }
94 
95  inline std::vector<Point> convex_hull(std::vector<Point>& points) {
102  if (points.size() < 3) return {}; // Need at least three points
103  std::vector<Point> hull;
104 
105  // Find leftmost point (ties don't matter)
106  int left = 0;
107  for (size_t i = 0; i < points.size(); i++)
108  if (points[i].first < points[left].first) left = (int)i;
109 
110  // While we don't reach leftmost point
111  int current = left, next;
112  do {
113  // Add to convex hull
114  hull.push_back(points[current]);
115 
116  // Keep moving counterclockwise
117  next = (current + 1) % points.size();
118  for (size_t i = 0; i < points.size(); i++) {
119  // We've found a more counterclockwise point --> update next
120  if (orientation(points[current], points[next], points[i]) == COUNTERCLOCKWISE)
121  next = (int)i;
122  }
123 
124  current = next;
125  } while (current != left);
126 
127  return hull;
128  }
129 
130  inline std::vector<Point> polar_points(int n, int a, int b, double radius) {
137  std::vector<Point> ret;
138  for (double degree = 0; degree < 360; degree += 360/n) {
139  ret.push_back(Point(
140  a + radius * cos(degree * (PI/180)), // 1 degree = pi/180 radians
141  b + radius * sin(degree * (PI/180))
142  ));
143  }
144 
145  return ret;
146  }
147  }
148 
149  inline std::string to_string(const double& value) {
151  std::stringstream ss;
152  ss << std::fixed << std::setprecision(1);
153  ss << value;
154  return ss.str();
155  }
156 
157  inline std::string to_string(const Point& point) {
159  return to_string(point.first) + "," + to_string(point.second);
160  }
161 
165  class AttributeMap {
166  public:
167  AttributeMap() = default;
168  AttributeMap(SVGAttrib _attr) : attr(_attr) {};
169  SVGAttrib attr;
170 
171  template<typename T>
172  AttributeMap& set_attr(const std::string key, T value) {
173  this->attr[key] = std::to_string(value);
174  return *this;
175  }
176  };
177 
178  template<>
179  inline AttributeMap& AttributeMap::set_attr(const std::string key, const double value) {
181  this->attr[key] = to_string(value);
182  return *this;
183  }
184 
185  template<>
186  inline AttributeMap& AttributeMap::set_attr(const std::string key, const char * value) {
188  this->attr[key] = value;
189  return *this;
190  }
191 
192  template<>
193  inline AttributeMap& AttributeMap::set_attr(const std::string key, const std::string value) {
195  this->attr[key] = value;
196  return *this;
197  }
198 
202  class Element: public AttributeMap {
203  public:
207  class BoundingBox : public QuadCoord {
208  public:
209  using QuadCoord::QuadCoord;
210  BoundingBox() = default;
211  BoundingBox(double a, double b, double c, double d) : QuadCoord({ a, b, c, d }) {};
212 
213  BoundingBox operator+ (const BoundingBox& other) {
215  using namespace util;
216  BoundingBox new_box;
217  new_box.x1 = min_or_not_nan(this->x1, other.x1);
218  new_box.x2 = max_or_not_nan(this->x2, other.x2);
219  new_box.y1 = min_or_not_nan(this->y1, other.y1);
220  new_box.y2 = max_or_not_nan(this->y2, other.y2);
221  return new_box;
222  }
223  };
224  using ChildList = std::vector<Element*>;
225  using ChildMap = std::map<std::string, ChildList>;
226 
227  Element() = default;
228  Element(const Element& other) = delete; // No copy constructor
229  Element(Element&& other) = default; // Move constructor
230  Element(const char* id) : AttributeMap(
231  SVGAttrib({ { "id", id } })) {};
232  using AttributeMap::AttributeMap;
233 
234  // Implicit string conversion
235  operator std::string() { return this->svg_to_string(0); };
236 
237  template<typename T, typename... Args>
238  T* add_child(Args&&... args) {
240  SVG_TYPE_CHECK;
241  this->children.push_back(std::make_unique<T>(std::forward<Args>(args)...));
242  return (T*)this->children.back().get();
243  }
244 
245  template<typename T>
246  Element& operator<<(T&& node) {
248  SVG_TYPE_CHECK;
249  this->children.push_back(std::make_unique<T>(std::move(node)));
250  return *this;
251  }
252 
253  template<typename T>
254  std::vector<T*> get_children() {
256  SVG_TYPE_CHECK;
257  std::vector<T*> ret;
258  auto child_elems = this->get_children_helper();
259 
260  for (auto& child: child_elems)
261  if (typeid(*child) == typeid(T)) ret.push_back((T*)child);
262 
263  return ret;
264  }
265 
266  template<typename T>
267  std::vector<T*> get_immediate_children() {
269  SVG_TYPE_CHECK;
270  std::vector<T*> ret;
271  for (auto& child : this->children)
272  if (typeid(*child) == typeid(T)) ret.push_back((T*)child.get());
273 
274  return ret;
275  }
276 
277  Element* get_element_by_id(const std::string& id);
278  std::vector<Element*> get_elements_by_class(const std::string& clsname);
279  void autoscale(const Margins& margins=DEFAULT_MARGINS);
280  void autoscale(const double margin);
281  virtual BoundingBox get_bbox();
282  ChildMap get_children();
283 
284  protected:
285  std::vector<std::unique_ptr<Element>> children;
286  std::vector<Element*> get_children_helper();
287  void get_bbox(Element::BoundingBox&);
288  virtual std::string svg_to_string(const size_t indent_level);
289  virtual std::string tag() = 0;
291  double find_numeric(const std::string& key) {
296  if (attr.find(key) != attr.end())
297  return std::stof(attr[key]);
298  return NAN;
299  }
300  };
301 
302  template<>
303  inline Element::ChildList Element::get_immediate_children() {
305  Element::ChildList ret;
306  for (auto& child : this->children) ret.push_back(child.get());
307  return ret;
308  }
309 
310  inline Element* Element::get_element_by_id(const std::string &id) {
312  auto child_elems = this->get_children_helper();
313  for (auto& current: child_elems)
314  if (current->attr.find("id") != current->attr.end() &&
315  current->attr.find("id")->second == id) return current;
316 
317  return nullptr;
318  }
319 
320  inline std::vector<Element*> Element::get_elements_by_class(const std::string &clsname) {
322  std::vector<Element*> ret;
323  auto child_elems = this->get_children_helper();
324 
325  for (auto& current: child_elems) {
326  if ((current->attr.find("class") != current->attr.end())
327  && (current->attr.find("class")->second == clsname))
328  ret.push_back(current);
329  }
330 
331  return ret;
332  }
333 
336  return { NAN, NAN, NAN, NAN };
337  }
338 
342  class Shape: public Element {
343  public:
344  using Element::Element;
345 
346  operator Point() {
348  return std::make_pair(this->x(), this->y());
349  }
350 
351  virtual std::vector<Point> points() {
353  auto bbox = this->get_bbox();
354  return {
355  Point(bbox.x1, bbox.y1), // Top left
356  Point(bbox.x2, bbox.y1), // Top right
357  Point(bbox.x1, bbox.y2), // Bottom left
358  Point(bbox.x2, bbox.y2) // Bottom right
359  };
360  }
361 
362  virtual double x() { return this->find_numeric("x"); }
363  virtual double y() { return this->find_numeric("y"); }
364  virtual double width() {
368  return this->find_numeric("width");
369  }
370  virtual double height() {
374  return this->find_numeric("height");
375  }
376  };
377 
378  class SVG : public Shape {
379  public:
380  class Style : public Element {
381  public:
382  Style() = default;
383  using Element::Element;
384  SelectorProperties css;
385  std::map<std::string, SelectorProperties> keyframes;
387  protected:
388  std::string svg_to_string(const size_t) override;
389  std::string tag() override { return "style"; };
390  };
391 
392  SVG(SVGAttrib _attr =
393  { { "xmlns", "http://www.w3.org/2000/svg" } }
394  ) : Shape(_attr) {};
395  AttributeMap& style(const std::string& key) {
396  if (!this->css) this->css = this->add_child<Style>();
397  return this->css->css[key];
398  }
399 
400  std::map<std::string, AttributeMap>& keyframes(const std::string& key) {
405  if (!this->css) this->css = this->add_child<Style>();
406  return this->css->keyframes[key];
407  }
408 
409  Style* css = nullptr;
411  protected:
412  std::string tag() override { return "svg"; }
413  };
414 
415  class Path : public Shape {
416  public:
417  using Shape::Shape;
418 
419  template<typename T>
420  inline void start(T x, T y) {
424  this->attr["d"] = "M " + std::to_string(x) + " " + std::to_string(y);
425  this->x_start = x;
426  this->y_start = y;
427  }
428 
429  template<typename T>
430  inline void line_to(T x, T y) {
436  if (this->attr.find("d") == this->attr.end())
437  start(x, y);
438  else
439  this->attr["d"] += " L " + std::to_string(x) +
440  " " + std::to_string(y);
441  }
442 
443  inline void line_to(std::pair<double, double> coord) {
444  this->line_to(coord.first, coord.second);
445  }
446 
447  inline void to_origin() {
449  this->line_to(x_start, y_start);
450  }
451 
452  protected:
453  std::string tag() override { return "path"; }
454 
455  private:
456  double x_start;
457  double y_start;
458  };
459 
460  class Text : public Element {
461  public:
462  Text() = default;
463  using Element::Element;
464 
465  Text(double x, double y, std::string _content) {
466  set_attr("x", to_string(x));
467  set_attr("y", to_string(y));
468  content = _content;
469  }
470 
471  Text(std::pair<double, double> xy, std::string _content) :
472  Text(xy.first, xy.second, _content) {};
473 
474  protected:
475  std::string content;
476  std::string svg_to_string(const size_t) override;
477  std::string tag() override { return "text"; }
478  };
479 
480  class Group : public Element {
481  public:
482  using Element::Element;
483  protected:
484  std::string tag() override { return "g"; }
485  };
486 
487  class Line : public Shape {
488  public:
489  Line() = default;
490  using Shape::Shape;
491 
492  Line(double x1, double x2, double y1, double y2) : Shape({
493  { "x1", to_string(x1) },
494  { "x2", to_string(x2) },
495  { "y1", to_string(y1) },
496  { "y2", to_string(y2) }
497  }) {};
498 
499  Line(Point x, Point y) : Line(x.first, y.first, x.second, y.second) {};
500 
501  double x1() { return std::stof(this->attr["x1"]); }
502  double x2() { return std::stof(this->attr["x2"]); }
503  double y1() { return std::stof(this->attr["y1"]); }
504  double y2() { return std::stof(this->attr["y2"]); }
505 
506  double width() override { return std::abs(x2() - x1()); }
507  double height() override { return std::abs(y2() - y1()); }
508  double get_length() { return std::sqrt(pow(width(), 2) + pow(height(), 2)); }
509  double get_slope() { return (y2() - y1()) / (x2() - x1()); }
510 
511  std::pair<double, double> along(double percent);
512 
513  protected:
514  Element::BoundingBox get_bbox() override;
515  std::string tag() override { return "line"; }
516  };
517 
518  class Rect : public Shape {
519  public:
520  Rect() = default;
521  using Shape::Shape;
522 
523  Rect(
524  double x, double y, double width, double height) :
525  Shape({
526  { "x", to_string(x) },
527  { "y", to_string(y) },
528  { "width", to_string(width) },
529  { "height", to_string(height) }
530  }) {};
531 
532  Element::BoundingBox get_bbox() override;
533  protected:
534  std::string tag() override { return "rect"; }
535  };
536 
537  class Circle : public Shape {
538  public:
539  Circle() = default;
540  using Shape::Shape;
541 
542  Circle(double cx, double cy, double radius) :
543  Shape({
544  { "cx", to_string(cx) },
545  { "cy", to_string(cy) },
546  { "r", to_string(radius) }
547  }) {
548  };
549 
550  Circle(std::pair<double, double> xy, double radius) : Circle(xy.first, xy.second, radius) {};
551  double radius() { return this->find_numeric("r"); }
552  virtual double x() override { return this->find_numeric("cx"); }
553  virtual double y() override { return this->find_numeric("cy"); }
554  virtual double width() override { return this->radius() * 2; }
555  virtual double height() override { return this->width(); }
556  Element::BoundingBox get_bbox() override;
557 
558  protected:
559  std::string tag() override { return "circle"; }
560  };
561 
562  class Polygon : public Element {
563  public:
564  Polygon() = default;
565  using Element::Element;
566 
567  Polygon(const std::vector<Point>& points) {
568  // Quick and dirty
569  std::string& point_str = this->attr["points"];
570  for (auto& pt : points)
571  point_str += to_string(pt) + " ";
572  };
573 
574  protected:
575  std::string tag() override { return "polygon"; }
576  };
577 
578  inline Element::BoundingBox Line::get_bbox() {
579  return { x1(), x2(), y1(), y2() };
580  }
581 
582  inline Element::BoundingBox Rect::get_bbox() {
583  double x = this->x(), y = this->y(),
584  width = this->width(), height = this->height();
585  return { x, x + width, y, y + height };
586  }
587 
588  inline Element::BoundingBox Circle::get_bbox() {
589  double x = this->x(), y = this->y(), radius = this->radius();
590 
591  return {
592  x - radius,
593  x + radius,
594  y - radius,
595  y + radius
596  };
597  }
598 
599  inline std::pair<double, double> Line::along(double percent) {
604  double x_pos, y_pos;
605 
606  if (x1() != x2()) {
607  double length = percent * this->get_length();
608  double discrim = std::sqrt(4 * pow(length, 2) * (1 / (1 + pow(get_slope(), 2))));
609 
610  double x_a = (2 * x1() + discrim) / 2;
611  double x_b = (2 * x1() - discrim) / 2;
612  x_pos = x_a;
613 
614  if ((x_a > x1() && x_a > x2()) || (x_a < x1() && x_a < x2()))
615  x_pos = x_b;
616 
617  y_pos = get_slope() * (x_pos - x1()) + y1();
618  }
619  else { // Edge case:: Completely vertical lines
620  x_pos = x1();
621 
622  if (y1() > y2()) // Downward pointing
623  y_pos = y1() - percent * this->get_length();
624  else
625  y_pos = y1() + percent * this->get_length();
626  }
627 
628  return std::make_pair(x_pos, y_pos);
629  }
630 
631  inline std::string Element::svg_to_string(const size_t indent_level) {
637  auto indent = std::string(indent_level, '\t');
638  std::string ret = indent + "<" + tag();
639 
640  // Set attributes
641  for (auto& pair: attr)
642  ret += " " + pair.first + "=" + "\"" + pair.second + "\"";
643 
644  if (!this->children.empty()) {
645  ret += ">\n";
646 
647  // Recursively get strings for child elements
648  for (auto& child: children)
649  ret += child->svg_to_string(indent_level + 1) + "\n";
650 
651  return ret += indent + "</" + tag() + ">";
652  }
653 
654  return ret += " />";
655  }
656 
657  inline std::string to_string(const std::map<std::string, AttributeMap>& css, const size_t indent_level) {
659  auto indent = std::string(indent_level, '\t'), ret = std::string();
660  for (auto& selector : css) {
661  // Loop over each selector's attribute/value pairs
662  ret += indent + "\t\t" + selector.first + " {\n";
663  for (auto& attr : selector.second.attr)
664  ret += indent + "\t\t\t" + attr.first + ": " + attr.second + ";\n";
665  ret += indent + "\t\t" + "}\n";
666  }
667  return ret;
668  }
669 
670  inline std::string SVG::Style::svg_to_string(const size_t indent_level) {
672  auto indent = std::string(indent_level, '\t');
673  std::string ret = indent + "<style type=\"text/css\">\n" +
674  indent + "\t<![CDATA[\n";
675 
676  // Begin CSS stylesheet
677  ret += to_string(this->css, indent_level);
678 
679  // Animation frames
680  for (auto& anim : this->keyframes) {
681  ret += indent + "\t\t@keyframes " + anim.first + " {\n" +
682  to_string(anim.second, indent_level + 1) +
683  indent + "\t\t" + "}\n";
684  }
685 
686  ret += indent + "\t]]>\n";
687  return ret + indent + "</style>";
688  }
689 
690  inline std::string Text::svg_to_string(const size_t indent_level) {
691  auto indent = std::string(indent_level, '\t');
692  std::string ret = indent + "<text";
693  for (auto& pair: attr)
694  ret += " " + pair.first + "=" + "\"" + pair.second + "\"";
695  return ret += ">" + this->content + "</text>";
696  }
697 
698  inline void Element::autoscale(const double margin) {
700  Element::BoundingBox bbox = this->get_bbox();
701  this->get_bbox(bbox);
702  double width = abs(bbox.x1) + abs(bbox.x2),
703  height = abs(bbox.y1) + abs(bbox.y2);
704 
705  this->autoscale({
706  width * margin, width * margin,
707  height * margin, height * margin
708  });
709  }
710 
711  inline void Element::autoscale(const Margins& margins) {
717  using std::stof;
718 
719  Element::BoundingBox bbox = this->get_bbox();
720  this->get_bbox(bbox); // Compute the bounding box (recursive)
721  double width = abs(bbox.x1) + abs(bbox.x2) + margins.x1 + margins.x2,
722  height = abs(bbox.y1) + abs(bbox.y2) + margins.y1 + margins.y2,
723  x1 = bbox.x1 - margins.x1, y1 = bbox.y1 - margins.y1;
724 
725  this->set_attr("width", width)
726  .set_attr("height", height);
727 
728  if (x1 < 0 || y1 < 0) {
729  std::stringstream viewbox;
730  viewbox << std::fixed << std::setprecision(1)
731  << x1 << " " // min-x
732  << y1 << " " // min-y
733  << width << " "
734  << height;
735  this->set_attr("viewBox", viewbox.str());
736  }
737  }
738 
741  auto this_bbox = this->get_bbox();
742  box = this_bbox + box; // Take union of both
743  for (auto& child: this->children) child->get_bbox(box); // Recursion
744  }
745 
746  inline Element::ChildMap Element::get_children() {
748  Element::ChildMap child_map;
749  for (auto& child : this->get_children_helper())
750  child_map[child->tag()].push_back(child);
751  return child_map;
752  }
753 
754  inline std::vector<Element*> Element::get_children_helper() {
756  std::deque<Element*> temp;
757  std::vector<Element*> ret;
758 
759  for (auto& child : this->children) { temp.push_back(child.get()); }
760  while (!temp.empty()) {
761  ret.push_back(temp.front());
762  for (auto& child : temp.front()->children) { temp.push_back(child.get()); }
763  temp.pop_front();
764  }
765 
766  return ret;
767  };
768 
769  inline SVG merge(SVG& left, SVG& right, const Margins& margins) {
771  SVG ret;
772 
773  // Move items
774  ret << std::move(left) << std::move(right);
775 
776  // Set bounding box of individual pieces
777  for (auto& svg_child: ret.get_immediate_children<SVG>())
778  svg_child->autoscale(margins);
779 
780  // Set x position for child SVG elements, and compute width/height for this
781  double x = 0, height = 0;
782  for (auto& svg_child: ret.get_immediate_children<SVG>()) {
783  svg_child->set_attr("x", x).set_attr("y", 0);
784  x += svg_child->width();
785  height = std::max(height, svg_child->height());
786  }
787 
788  ret.set_attr("width", x).set_attr("height", height);
789  return ret;
790  }
791 
792  inline std::vector<Point> bounding_polygon(std::vector<Shape*>& shapes) {
793  /* Convert shapes into sets of points, aggregate them, and then calculate
794  * convex hull for aggregate set
795  */
796  std::vector<Point> points;
797  for (auto& shp : shapes) {
798  auto temp_points = shp->points();
799  std::move(temp_points.begin(), temp_points.end(), std::back_inserter(points));
800  }
801 
802  return util::convex_hull(points);
803  }
804 
805  inline SVG frame_animate(std::vector<SVG>& frames, const double fps) {
811  SVG root;
812  const double duration = (double)frames.size() / fps; // [seconds]
813  const double frame_step = 1.0 / fps; // duration of each frame [seconds]
814  int current_frame = 0;
815 
816  root.style("svg.animated").set_attr("animation-iteration-count", "infinite")
817  .set_attr("animation-timing-function", "step-end")
818  .set_attr("animation-duration", std::to_string(duration) + "s")
819  .set_attr("opacity", 0);
820 
821  // Move frames into new SVG
822  for (auto& frame : frames) {
823  std::string frame_id = "frame_" + std::to_string(current_frame);
824  frame.set_attr("id", frame_id).set_attr("class", "animated");
825  root.style("#" + frame_id).set_attr("animation-name",
826  "anim_" + std::to_string(current_frame));
827  current_frame++;
828  root << std::move(frame);
829  }
830 
831  // Set animation frames
832  for (size_t i = 0, ilen = frames.size(); i < ilen; i++) {
833  auto& anim = root.keyframes("anim_" + std::to_string(i));
834  double begin_pct = (double)i / frames.size(),
835  end_pct = (double)(i + 1) / frames.size();
836  anim["0%"].set_attr("opacity", 0);
837  anim[std::to_string(begin_pct * 100) + "%"].set_attr("opacity", 1);
838  anim[std::to_string(end_pct * 100) + "%"].set_attr("opacity", 0);
839  }
840 
841  // Scale and center child SVGs
842  double width = 0, height = 0;
843 
844  for (auto& child : root.get_immediate_children<SVG>()) {
845  child->autoscale();
846  width = std::max(width, child->width());
847  height = std::max(height, child->height());
848  }
849 
850  root.set_attr("viewBox", "0 0 " + std::to_string(width) + " " + std::to_string(height));
851 
852  // Center child SVGs
853  for (auto& child : root.get_immediate_children<SVG>())
854  child->set_attr("x", (width - child->width())/2).set_attr("y", (height - child->height())/2);
855 
856  return root;
857  }
858 }
Definition: svg.hpp:518
T * add_child(Args &&... args)
Definition: svg.hpp:238
SVG frame_animate(std::vector< SVG > &frames, const double fps)
Definition: svg.hpp:805
Definition: svg.hpp:415
Various utility and mathematical functions.
virtual std::vector< Point > points()
Definition: svg.hpp:351
Abstract base class for all SVG elements.
Definition: svg.hpp:202
T max_or_not_nan(T first, T second)
Definition: svg.hpp:74
std::string tag() override
Definition: svg.hpp:515
std::string tag() override
Definition: svg.hpp:575
Definition: svg.hpp:480
virtual std::string svg_to_string(const size_t indent_level)
Definition: svg.hpp:631
std::map< std::string, SelectorProperties > keyframes
Definition: svg.hpp:385
std::map< std::string, AttributeMap > & keyframes(const std::string &key)
Definition: svg.hpp:400
void to_origin()
Definition: svg.hpp:447
std::vector< T * > get_children()
Definition: svg.hpp:254
Main namespace for SVG for C++.
Definition: svg.hpp:20
virtual double height()
Definition: svg.hpp:370
Definition: svg.hpp:537
T min_or_not_nan(T first, T second)
Definition: svg.hpp:61
virtual double width()
Definition: svg.hpp:364
std::string tag() override
Definition: svg.hpp:559
std::string tag() override
Definition: svg.hpp:484
SVG merge(SVG &left, SVG &right, const Margins &margins=DEFAULT_MARGINS)
Definition: svg.hpp:769
Definition: svg.hpp:460
std::string tag() override
Definition: svg.hpp:453
std::string tag() override
Definition: svg.hpp:389
std::pair< double, double > along(double percent)
Definition: svg.hpp:599
Definition: svg.hpp:380
void line_to(T x, T y)
Definition: svg.hpp:430
Element * get_element_by_id(const std::string &id)
Definition: svg.hpp:310
void autoscale(const Margins &margins=DEFAULT_MARGINS)
Definition: svg.hpp:711
Definition: svg.hpp:487
std::vector< Point > convex_hull(std::vector< Point > &points)
Definition: svg.hpp:95
std::string tag() override
Definition: svg.hpp:412
Base class for any SVG elements that have a width and height.
Definition: svg.hpp:342
Base class for anything that has attributes (e.g. SVG elements, CSS stylesheets)
Definition: svg.hpp:165
std::string svg_to_string(const size_t) override
Definition: svg.hpp:670
AttributeMap & style(const std::string &key)
Definition: svg.hpp:395
SelectorProperties css
Definition: svg.hpp:384
Represents the top left and bottom right corners of a bounding rectangle.
Definition: svg.hpp:207
double find_numeric(const std::string &key)
Definition: svg.hpp:291
std::string to_string(const double &value)
Definition: svg.hpp:149
Definition: svg.hpp:562
std::string tag() override
Definition: svg.hpp:477
Element & operator<<(T &&node)
Definition: svg.hpp:246
virtual BoundingBox get_bbox()
Definition: svg.hpp:334
void start(T x, T y)
Definition: svg.hpp:420
Definition: svg.hpp:28
std::string tag() override
Definition: svg.hpp:534
std::vector< Element * > get_elements_by_class(const std::string &clsname)
Definition: svg.hpp:320
std::vector< T * > get_immediate_children()
Definition: svg.hpp:267
std::vector< Element * > get_children_helper()
Definition: svg.hpp:754
std::string to_string(const std::map< std::string, AttributeMap > &css, const size_t indent_level=0)
Definition: svg.hpp:657