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) 17 #include <type_traits> 35 using SelectorProperties = std::map<std::string, AttributeMap>;
36 using SVGAttrib = std::map<std::string, std::string>;
37 using Point = std::pair<double, double>;
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);
44 std::vector<Point> bounding_polygon(
const std::vector<Shape*>& shapes);
47 const static Margins DEFAULT_MARGINS = { 10, 10, 10, 10 };
48 const static Margins NO_MARGINS = { 0, 0, 0, 0 };
55 COLINEAR, CLOCKWISE, COUNTERCLOCKWISE
58 inline std::vector<Point> polar_points(
int n,
int a,
int b,
double radius);
65 if (isnan(first) && isnan(second))
67 else if (isnan(first) || isnan(second))
68 return isnan(first) ? second : first;
70 return std::min(first, second);
78 if (isnan(first) && isnan(second))
80 else if (isnan(first) || isnan(second))
81 return isnan(first) ? second : first;
83 return std::max(first, second);
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));
90 if (value == 0)
return COLINEAR;
91 else if (value > 0)
return CLOCKWISE;
92 else return COUNTERCLOCKWISE;
95 inline std::vector<Point>
convex_hull(std::vector<Point>& points) {
102 if (points.size() < 3)
return {};
103 std::vector<Point> hull;
107 for (
size_t i = 0; i < points.size(); i++)
108 if (points[i].first < points[left].first) left = (int)i;
111 int current = left, next;
114 hull.push_back(points[current]);
117 next = (current + 1) % points.size();
118 for (
size_t i = 0; i < points.size(); i++) {
120 if (orientation(points[current], points[next], points[i]) == COUNTERCLOCKWISE)
125 }
while (current != left);
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) {
140 a + radius * cos(degree * (PI/180)),
141 b + radius * sin(degree * (PI/180))
151 std::stringstream ss;
152 ss << std::fixed << std::setprecision(1);
172 AttributeMap& set_attr(
const std::string key, T value) {
179 inline AttributeMap& AttributeMap::set_attr(
const std::string key,
const double value) {
186 inline AttributeMap& AttributeMap::set_attr(
const std::string key,
const char * value) {
188 this->attr[key] = value;
193 inline AttributeMap& AttributeMap::set_attr(
const std::string key,
const std::string value) {
195 this->attr[key] = value;
209 using QuadCoord::QuadCoord;
215 using namespace util;
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);
224 using ChildList = std::vector<Element*>;
225 using ChildMap = std::map<std::string, ChildList>;
231 SVGAttrib({ {
"id",
id } })) {};
232 using AttributeMap::AttributeMap;
235 operator std::string() {
return this->svg_to_string(0); };
237 template<
typename T,
typename... Args>
241 this->children.push_back(std::make_unique<T>(std::forward<Args>(args)...));
242 return (T*)this->children.back().get();
249 this->children.push_back(std::make_unique<T>(std::move(node)));
258 auto child_elems = this->get_children_helper();
260 for (
auto& child: child_elems)
261 if (
typeid(*child) ==
typeid(T)) ret.push_back((T*)child);
271 for (
auto& child : this->children)
272 if (
typeid(*child) ==
typeid(T)) ret.push_back((T*)child.get());
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);
282 ChildMap get_children();
285 std::vector<std::unique_ptr<Element>> children;
286 std::vector<Element*> get_children_helper();
288 virtual std::string svg_to_string(
const size_t indent_level);
289 virtual std::string tag() = 0;
296 if (attr.find(key) != attr.end())
297 return std::stof(attr[key]);
305 Element::ChildList ret;
306 for (
auto& child : this->children) ret.push_back(child.get());
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;
322 std::vector<Element*> ret;
323 auto child_elems = this->get_children_helper();
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);
336 return { NAN, NAN, NAN, NAN };
344 using Element::Element;
348 return std::make_pair(this->x(), this->y());
353 auto bbox = this->get_bbox();
355 Point(bbox.x1, bbox.y1),
356 Point(bbox.x2, bbox.y1),
357 Point(bbox.x1, bbox.y2),
358 Point(bbox.x2, bbox.y2)
362 virtual double x() {
return this->find_numeric(
"x"); }
363 virtual double y() {
return this->find_numeric(
"y"); }
368 return this->find_numeric(
"width");
374 return this->find_numeric(
"height");
383 using Element::Element;
388 std::string svg_to_string(
const size_t)
override;
389 std::string
tag()
override {
return "style"; };
392 SVG(SVGAttrib _attr =
393 { {
"xmlns",
"http://www.w3.org/2000/svg" } }
396 if (!this->css) this->css = this->add_child<Style>();
397 return this->css->css[key];
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];
412 std::string
tag()
override {
return "svg"; }
436 if (this->attr.find(
"d") == this->attr.end())
443 inline void line_to(std::pair<double, double> coord) {
444 this->line_to(coord.first, coord.second);
449 this->line_to(x_start, y_start);
453 std::string
tag()
override {
return "path"; }
463 using Element::Element;
465 Text(
double x,
double y, std::string _content) {
471 Text(std::pair<double, double> xy, std::string _content) :
472 Text(xy.first, xy.second, _content) {};
476 std::string svg_to_string(
const size_t)
override;
477 std::string
tag()
override {
return "text"; }
482 using Element::Element;
484 std::string
tag()
override {
return "g"; }
492 Line(
double x1,
double x2,
double y1,
double y2) :
Shape({
499 Line(Point x, Point y) :
Line(x.first, y.first, x.second, y.second) {};
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"]); }
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()); }
511 std::pair<double, double> along(
double percent);
515 std::string
tag()
override {
return "line"; }
524 double x,
double y,
double width,
double height) :
534 std::string
tag()
override {
return "rect"; }
542 Circle(
double cx,
double cy,
double radius) :
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(); }
559 std::string
tag()
override {
return "circle"; }
565 using Element::Element;
567 Polygon(
const std::vector<Point>& points) {
569 std::string& point_str = this->attr[
"points"];
570 for (
auto& pt : points)
575 std::string
tag()
override {
return "polygon"; }
579 return { x1(), x2(), y1(), y2() };
583 double x = this->x(), y = this->y(),
584 width = this->width(), height = this->height();
585 return { x, x + width, y, y + height };
589 double x = this->x(), y = this->y(), radius = this->radius();
607 double length = percent * this->get_length();
608 double discrim = std::sqrt(4 * pow(length, 2) * (1 / (1 + pow(get_slope(), 2))));
610 double x_a = (2 * x1() + discrim) / 2;
611 double x_b = (2 * x1() - discrim) / 2;
614 if ((x_a > x1() && x_a > x2()) || (x_a < x1() && x_a < x2()))
617 y_pos = get_slope() * (x_pos - x1()) + y1();
623 y_pos = y1() - percent * this->get_length();
625 y_pos = y1() + percent * this->get_length();
628 return std::make_pair(x_pos, y_pos);
637 auto indent = std::string(indent_level,
'\t');
638 std::string ret = indent +
"<" + tag();
641 for (
auto& pair: attr)
642 ret +=
" " + pair.first +
"=" +
"\"" + pair.second +
"\"";
644 if (!this->children.empty()) {
648 for (
auto& child: children)
649 ret += child->svg_to_string(indent_level + 1) +
"\n";
651 return ret += indent +
"</" + tag() +
">";
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) {
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";
672 auto indent = std::string(indent_level,
'\t');
673 std::string ret = indent +
"<style type=\"text/css\">\n" +
674 indent +
"\t<![CDATA[\n";
677 ret +=
to_string(this->css, indent_level);
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";
686 ret += indent +
"\t]]>\n";
687 return ret + indent +
"</style>";
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>";
701 this->get_bbox(bbox);
702 double width = abs(bbox.x1) + abs(bbox.x2),
703 height = abs(bbox.y1) + abs(bbox.y2);
706 width * margin, width * margin,
707 height * margin, height * margin
720 this->get_bbox(bbox);
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;
725 this->set_attr(
"width", width)
726 .set_attr(
"height", height);
728 if (x1 < 0 || y1 < 0) {
729 std::stringstream viewbox;
730 viewbox << std::fixed << std::setprecision(1)
735 this->set_attr(
"viewBox", viewbox.str());
741 auto this_bbox = this->get_bbox();
742 box = this_bbox + box;
743 for (
auto& child: this->children) child->get_bbox(box);
748 Element::ChildMap child_map;
749 for (
auto& child : this->get_children_helper())
750 child_map[child->tag()].push_back(child);
756 std::deque<Element*> temp;
757 std::vector<Element*> ret;
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()); }
774 ret << std::move(left) << std::move(right);
778 svg_child->autoscale(margins);
781 double x = 0, height = 0;
783 svg_child->set_attr(
"x", x).set_attr(
"y", 0);
784 x += svg_child->width();
785 height = std::max(height, svg_child->height());
788 ret.set_attr(
"width", x).set_attr(
"height", height);
792 inline std::vector<Point> bounding_polygon(std::vector<Shape*>& shapes) {
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));
802 return util::convex_hull(points);
812 const double duration = (double)frames.size() / fps;
813 const double frame_step = 1.0 / fps;
814 int current_frame = 0;
816 root.
style(
"svg.animated").set_attr(
"animation-iteration-count",
"infinite")
817 .set_attr(
"animation-timing-function",
"step-end")
819 .set_attr(
"opacity", 0);
822 for (
auto& frame : frames) {
824 frame.set_attr(
"id", frame_id).set_attr(
"class",
"animated");
825 root.
style(
"#" + frame_id).set_attr(
"animation-name",
828 root << std::move(frame);
832 for (
size_t i = 0, ilen = frames.size(); i < ilen; 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);
842 double width = 0, height = 0;
846 width = std::max(width, child->width());
847 height = std::max(height, child->height());
854 child->set_attr(
"x", (width - child->width())/2).set_attr(
"y", (height - child->height())/2);
T * add_child(Args &&... args)
Definition: svg.hpp:238
SVG frame_animate(std::vector< SVG > &frames, const double fps)
Definition: svg.hpp:805
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
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
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
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
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
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
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
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