File indexing completed on 2025-01-18 09:28:06
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011 #include <algorithm>
0012 #include <array>
0013 #include <limits>
0014 #include <map>
0015 #include <optional>
0016 #include <string>
0017 #include <vector>
0018
0019 #include "defs.hpp"
0020 #include "style.hpp"
0021
0022 namespace actsvg {
0023
0024 namespace svg {
0025
0026
0027
0028
0029
0030
0031
0032
0033 struct object {
0034
0035
0036
0037
0038
0039
0040
0041
0042 struct summary {
0043 std::array<scalar, 2> _scale = {1., 1.};
0044 };
0045
0046
0047 std::string _tag = "";
0048
0049
0050 std::string _id = "";
0051
0052
0053 std::vector<std::string> _aux_info = {};
0054
0055
0056 bool _sterile = false;
0057
0058
0059 bool _active = true;
0060
0061
0062 std::vector<std::string> _field = {};
0063 scalar _field_span = 12;
0064
0065
0066 style::fill _fill = style::fill();
0067
0068
0069 style::stroke _stroke = style::stroke();
0070
0071
0072 style::transform _transform = style::transform{};
0073
0074
0075 std::map<std::string, std::string> _attribute_map = {};
0076
0077
0078 std::vector<object> _sub_objects = {};
0079
0080
0081 std::vector<object> _definitions = {};
0082
0083
0084 std::array<scalar, 2> _barycenter = {0., 0.};
0085
0086
0087 std::array<scalar, 2> _x_range = {std::numeric_limits<scalar>::max(),
0088 std::numeric_limits<scalar>::lowest()};
0089
0090 std::array<scalar, 2> _y_range = {std::numeric_limits<scalar>::max(),
0091 std::numeric_limits<scalar>::lowest()};
0092
0093 std::array<scalar, 2> _r_range = {std::numeric_limits<scalar>::max(),
0094 std::numeric_limits<scalar>::lowest()};
0095
0096 std::array<scalar, 2> _phi_range = {std::numeric_limits<scalar>::max(),
0097 std::numeric_limits<scalar>::lowest()};
0098
0099
0100 summary _summary = summary{};
0101
0102
0103 bool is_defined() const { return (not _tag.empty()); }
0104
0105
0106
0107
0108
0109 void add_object(const svg::object &o_) {
0110
0111 _sub_objects.push_back(o_);
0112
0113 _definitions.insert(_definitions.end(), o_._definitions.begin(),
0114 o_._definitions.end());
0115 if (not _sterile) {
0116
0117 _x_range = {std::min(_x_range[0], o_._x_range[0]),
0118 std::max(_x_range[1], o_._x_range[1])};
0119 _y_range = {std::min(_y_range[0], o_._y_range[0]),
0120 std::max(_y_range[1], o_._y_range[1])};
0121 _r_range = {std::min(_r_range[0], o_._r_range[0]),
0122 std::max(_r_range[1], o_._r_range[1])};
0123 _phi_range = {std::min(_phi_range[0], o_._phi_range[0]),
0124 std::max(_phi_range[1], o_._phi_range[1])};
0125 }
0126 }
0127
0128
0129
0130
0131
0132
0133
0134
0135
0136 std::optional<svg::object> find_object(const std::string &id_) const {
0137
0138 auto found_object =
0139 std::find_if(_sub_objects.begin(), _sub_objects.end(),
0140 [&](const svg::object &candidate_) {
0141 return (candidate_._id == id_);
0142 });
0143
0144 if (found_object != _sub_objects.end()) {
0145 return (*found_object);
0146 }
0147 return std::nullopt;
0148 }
0149
0150
0151
0152
0153
0154 template <typename object_container>
0155 void add_objects(const object_container &oc_) {
0156
0157 for (const auto &o : oc_) {
0158 add_object(o);
0159 }
0160 }
0161
0162 friend std::ostream &operator<<(std::ostream &os_, const object &o_);
0163 };
0164
0165
0166
0167 inline std::ostream &operator<<(std::ostream &os_, const object &o_) {
0168
0169
0170
0171 object o_copy = o_;
0172
0173
0174 os_ << __l << o_copy._tag;
0175 if (not o_copy._id.empty()) {
0176 os_ << __blk << "id=\"" << o_copy._id << "\"";
0177 }
0178
0179
0180 if (not o_._sterile) {
0181 o_._fill.attach_attributes(o_copy);
0182 o_._stroke.attach_attributes(o_copy);
0183 o_._transform.attach_attributes(o_copy);
0184 }
0185
0186
0187 for (auto [key, value] : o_copy._attribute_map) {
0188 os_ << __blk << key << "=\"" << value << "\"";
0189 }
0190
0191
0192 if (o_copy._sub_objects.empty() and o_copy._field.empty()) {
0193 os_ << __er;
0194 return os_;
0195 }
0196
0197 os_ << __r;
0198 for (const auto &a : o_copy._sub_objects) {
0199 os_ << a;
0200 }
0201 if (not o_copy._field.empty()) {
0202 if (o_copy._field.size() == 1) {
0203 os_ << o_copy._field[0];
0204 } else {
0205 for (const auto &fl : o_copy._field) {
0206 os_ << "<tspan x=\"";
0207 os_ << o_copy._x_range[0] << "\"";
0208 os_ << " dy=\"" << o_copy._field_span << "\">";
0209 os_ << fl;
0210 os_ << "</tspan>" << __nl;
0211 }
0212 }
0213 }
0214
0215 os_ << __el << o_copy._tag << __r;
0216 return os_;
0217 }
0218
0219
0220
0221
0222
0223
0224
0225 struct file {
0226
0227
0228 bool _add_html = false;
0229
0230
0231 std::string _html_head = "<html>\n<body>\n";
0232 std::string _svg_head = "<svg version=\"1.1\"";
0233
0234 std::string _svg_def_end =
0235 " xmlns=\"http://www.w3.org/2000/svg\" "
0236 "xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n";
0237
0238 std::string _svg_tail = "</svg>\n";
0239 std::string _html_tail = "</body>\n</html>\n";
0240
0241 scalar _width = 800;
0242 scalar _height = 800;
0243 scalar _border = 10;
0244
0245 std::vector<object> _objects = {};
0246
0247
0248
0249
0250
0251 void add_object(const svg::object &o_) {
0252
0253 if (o_._active) {
0254 _objects.push_back(o_);
0255 }
0256 }
0257
0258 friend std::ostream &operator<<(std::ostream &os_, const file &f_);
0259 };
0260
0261
0262
0263 inline std::ostream &operator<<(std::ostream &os_, const file &f_) {
0264
0265 std::array<scalar, 2> x_range = {std::numeric_limits<scalar>::max(),
0266 std::numeric_limits<scalar>::min()};
0267 std::array<scalar, 2> y_range = {std::numeric_limits<scalar>::max(),
0268 std::numeric_limits<scalar>::min()};
0269
0270 std::array<scalar, 4> viewBox = {-800, -800, 1600, 1600};
0271
0272 std::map<std::string, svg::object> definitions;
0273
0274 for (const auto &o : f_._objects) {
0275 x_range[0] = std::min(x_range[0], o._x_range[0]);
0276 x_range[1] = std::max(x_range[1], o._x_range[1]);
0277 y_range[0] = std::min(y_range[0], o._y_range[0]);
0278 y_range[1] = std::max(y_range[1], o._y_range[1]);
0279 for (const auto &d : o._definitions) {
0280 definitions[d._id] = d;
0281 }
0282 }
0283
0284 viewBox[2] = 1.2 * (x_range[1] - x_range[0]);
0285 viewBox[3] = 1.2 * (y_range[1] - y_range[0]);
0286
0287
0288 viewBox[0] = (x_range[0] - 0.1 * viewBox[2]) - f_._border;
0289 viewBox[1] = (y_range[0] - 0.1 * viewBox[3]) - f_._border;
0290 viewBox[2] += f_._border;
0291 viewBox[3] += f_._border;
0292
0293 if (f_._add_html) {
0294 os_ << f_._html_head;
0295 }
0296 os_ << f_._svg_head;
0297 os_ << " width=\"" << f_._width << "\" height=\"" << f_._height << "\"";
0298 os_ << " viewBox=\"" << viewBox[0] << " " << viewBox[1] << " " << viewBox[2]
0299 << " " << viewBox[3] << "\"";
0300 os_ << f_._svg_def_end;
0301
0302 if (not definitions.empty()) {
0303 os_ << "<defs>";
0304 for (auto [key, value] : definitions) {
0305 os_ << value;
0306 }
0307 os_ << "</defs>";
0308 }
0309
0310
0311 for (auto &o : f_._objects) {
0312 os_ << o;
0313 }
0314 os_ << f_._svg_tail;
0315 if (f_._add_html) {
0316 os_ << f_._html_tail;
0317 }
0318 return os_;
0319 }
0320
0321 }
0322 }