File indexing completed on 2025-03-13 08:35:29
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011 #include <iostream>
0012 #include <map>
0013 #include <stdexcept>
0014 #include <string>
0015 #include <vector>
0016
0017 #include "defs.hpp"
0018 #include "generators.hpp"
0019 #include "style.hpp"
0020 #include "svg.hpp"
0021 #include "utils.hpp"
0022
0023 namespace actsvg {
0024
0025 namespace detail {
0026
0027
0028
0029
0030
0031 static inline void adapt_range(svg::object &_o_,
0032 const std::vector<point2> &vertices_) {
0033 for (const auto &v : vertices_) {
0034 _o_._x_range = {std::min(_o_._x_range[0], v[0]),
0035 std::max(_o_._x_range[1], v[0])};
0036 _o_._y_range = {std::min(_o_._y_range[0], v[1]),
0037 std::max(_o_._y_range[1], v[1])};
0038
0039 scalar r = std::sqrt(v[0] * v[0] + v[1] * v[1]);
0040 scalar phi = std::atan2(-v[1], v[0]);
0041 _o_._r_range = {std::min(_o_._r_range[0], r),
0042 std::max(_o_._r_range[1], r)};
0043 _o_._phi_range = {std::min(_o_._phi_range[0], phi),
0044 std::max(_o_._phi_range[1], phi)};
0045 }
0046 }
0047
0048 }
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059 namespace draw {
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077 static inline svg::object line(
0078 const std::string &id_, const point2 &start_, const point2 &end_,
0079 const style::stroke &stroke_ = style::stroke(),
0080 const style::transform &transform_ = style::transform()) {
0081 svg::object l;
0082 l._tag = "line";
0083 l._id = id_;
0084
0085
0086
0087 scalar tx = transform_._tr[0];
0088 scalar ty = transform_._tr[1];
0089 scalar sx = transform_._scale[0];
0090 scalar sy = transform_._scale[1];
0091
0092 scalar st_x = sx * (start_[0] + tx);
0093 scalar st_y = sx * (-start_[1] - ty);
0094 scalar en_x = sy * (end_[0] + ty);
0095 scalar en_y = sy * (-end_[1] - ty);
0096
0097 l._barycenter =
0098 utils::barycenter<std::array<scalar, 2>>({{st_x, st_y}, {en_x, en_y}});
0099
0100
0101 l._attribute_map["x1"] = utils::to_string(st_x);
0102 l._attribute_map["y1"] = utils::to_string(st_y);
0103 l._attribute_map["x2"] = utils::to_string(en_x);
0104 l._attribute_map["y2"] = utils::to_string(en_y);
0105
0106
0107 detail::adapt_range(l, {{st_x, st_y}, {en_x, en_y}});
0108
0109
0110 l._stroke = stroke_;
0111 return l;
0112 }
0113
0114
0115
0116
0117
0118
0119
0120
0121
0122
0123
0124
0125
0126
0127
0128 static inline svg::object arc(
0129 const std::string &id_, scalar r_, const point2 &start_, const point2 &end_,
0130 const style::fill &fill_ = style::fill(),
0131 const style::stroke &stroke_ = style::stroke(),
0132 const style::transform &transform_ = style::transform()) {
0133 svg::object a;
0134 a._tag = "path";
0135 a._id = id_;
0136
0137
0138
0139 scalar tx = transform_._tr[0];
0140 scalar ty = transform_._tr[1];
0141 scalar sx = transform_._scale[0];
0142 scalar sy = transform_._scale[1];
0143
0144 scalar x_min = sx * (start_[0] + tx);
0145 scalar y_min = -sy * (start_[1] + ty);
0146 scalar x_max = sx * (end_[0] + tx);
0147 scalar y_max = -sy * (end_[1] + ty);
0148
0149 std::string arc_string =
0150 "M " + utils::to_string(x_min) + " " + utils::to_string(y_min);
0151 arc_string +=
0152 " A " + utils::to_string(sx * r_) + " " + utils::to_string(sy * r_);
0153 arc_string += " 0 0 0 ";
0154 arc_string += utils::to_string(x_max) + " " + utils::to_string(y_max);
0155 a._attribute_map["d"] = arc_string;
0156
0157
0158 detail::adapt_range(a, {{x_min, y_min}, {x_max, y_max}});
0159
0160
0161 a._barycenter = utils::barycenter<std::array<scalar, 2>>(
0162 {{x_min, y_min}, {x_max, y_max}});
0163
0164
0165 a._stroke = stroke_;
0166 a._fill = fill_;
0167 return a;
0168 }
0169
0170
0171
0172
0173
0174
0175
0176
0177
0178
0179
0180
0181
0182
0183
0184
0185 static inline svg::object circle(
0186 const std::string &id_, const point2 &p_, scalar r_,
0187 const style::fill &fill_ = style::fill(),
0188 const style::stroke &stroke_ = style::stroke(),
0189 const style::transform &transform_ = style::transform()) {
0190
0191 svg::object e;
0192 e._tag = "ellipse";
0193 e._id = id_;
0194
0195
0196
0197 scalar sx = transform_._scale[0];
0198 scalar sy = transform_._scale[1];
0199 scalar cx = sx * (p_[0] + transform_._tr[0]);
0200 scalar cy = sy * (-p_[1] - transform_._tr[1]);
0201
0202
0203 e._attribute_map["cx"] = utils::to_string(cx);
0204 e._attribute_map["cy"] = utils::to_string(cy);
0205 e._attribute_map["rx"] = utils::to_string(r_ * sx);
0206 e._attribute_map["ry"] = utils::to_string(r_ * sy);
0207
0208
0209 detail::adapt_range(
0210 e, {{cx - sx * r_, cy - sy * r_}, {cx + sx * r_, cy + sy * r_}});
0211
0212 e._barycenter = {cx, cy};
0213
0214
0215 e._fill = fill_;
0216 e._stroke = stroke_;
0217
0218
0219 return e;
0220 }
0221
0222
0223
0224
0225
0226
0227
0228
0229
0230
0231
0232
0233
0234
0235
0236 static inline svg::object ellipse(
0237 const std::string &id_, const point2 &p_, const std::array<scalar, 2> &rs_,
0238 const style::fill &fill_ = style::fill(),
0239 const style::stroke &stroke_ = style::stroke(),
0240 const style::transform &transform_ = style::transform()) {
0241
0242
0243 svg::object e;
0244 e._tag = "ellipse";
0245 e._id = id_;
0246
0247
0248 scalar sx = transform_._scale[0];
0249 scalar sy = transform_._scale[1];
0250 scalar cx = sx * (p_[0] + transform_._tr[0]);
0251 scalar cy = sy * (-p_[1] - transform_._tr[1]);
0252
0253
0254 e._attribute_map["cx"] = utils::to_string(cx);
0255 e._attribute_map["cy"] = utils::to_string(cy);
0256 e._attribute_map["rx"] = utils::to_string(rs_[0] * sx);
0257 e._attribute_map["ry"] = utils::to_string(rs_[1] * sy);
0258
0259
0260 detail::adapt_range(e, {{cx - sx * rs_[0], cy - sy * rs_[1]},
0261 {cx + sx * rs_[0], cy + sy * rs_[1]}});
0262
0263
0264 e._barycenter = {cx, cy};
0265
0266
0267 e._fill = fill_;
0268 e._stroke = stroke_;
0269
0270 return e;
0271 }
0272
0273
0274
0275
0276
0277
0278
0279
0280
0281
0282
0283
0284
0285
0286 static inline svg::object polygon(
0287 const std::string &id_, const std::vector<point2> &polygon_,
0288 const style::fill &fill_ = style::fill(),
0289 const style::stroke &stroke_ = style::stroke(),
0290 const style::transform &transform_ = style::transform())
0291
0292 {
0293
0294 svg::object p;
0295 p._tag = "polygon";
0296 p._id = id_;
0297
0298 scalar tx = transform_._tr[0];
0299 scalar ty = transform_._tr[1];
0300 scalar sx = transform_._scale[0];
0301 scalar sy = transform_._scale[1];
0302
0303 std::string svg_points_string;
0304 std::vector<point2> display_vertices;
0305 display_vertices.reserve(polygon_.size());
0306 for (auto v : polygon_) {
0307 scalar alpha = transform_._rot[0];
0308 if (alpha != 0.) {
0309 scalar alpha_rad = static_cast<scalar>(alpha / 180. * M_PI);
0310 v = utils::rotate(v, alpha_rad);
0311 }
0312
0313
0314 v[0] *= sx;
0315 v[1] *= sy;
0316
0317 v[0] += sx * tx;
0318 v[1] += sy * ty;
0319 v[1] *= -1;
0320
0321 detail::adapt_range(p, {v});
0322
0323
0324 svg_points_string += utils::to_string(v[0]);
0325 svg_points_string += ",";
0326 svg_points_string += utils::to_string(v[1]);
0327 svg_points_string += " ";
0328 }
0329
0330 p._barycenter = utils::barycenter(display_vertices);
0331
0332 p._attribute_map["points"] = svg_points_string;
0333
0334 p._fill = fill_;
0335 p._stroke = stroke_;
0336
0337 return p;
0338 }
0339
0340
0341
0342
0343
0344
0345
0346
0347
0348
0349
0350
0351
0352 static inline svg::object rectangle(
0353 const std::string &id_, const point2 &c_, scalar half_x, scalar half_y,
0354 const style::fill &fill_, const style::stroke &stroke_,
0355 const style::transform &transform_ = style::transform()) {
0356
0357 svg::object r;
0358 r._id = id_;
0359 r._tag = "rect";
0360
0361 r._attribute_map["x"] = utils::to_string(c_[0] - half_x);
0362 r._attribute_map["y"] = utils::to_string(-c_[1] - half_y);
0363 r._attribute_map["width"] = utils::to_string(half_x * 2);
0364 r._attribute_map["height"] = utils::to_string(half_y * 2);
0365
0366 r._fill = fill_;
0367 r._stroke = stroke_;
0368 r._transform = transform_;
0369
0370 return r;
0371 }
0372
0373
0374
0375
0376
0377
0378
0379
0380
0381
0382
0383
0384 static inline svg::object text(
0385 const std::string &id_, const point2 &p_,
0386 const std::vector<std::string> &text_,
0387 const style::font &font_ = style::font(),
0388 const style::transform &transform_ = style::transform()) {
0389
0390 svg::object t;
0391 t._tag = "text";
0392 t._id = id_;
0393
0394 scalar x = p_[0];
0395 scalar y = p_[1];
0396
0397 x *= transform_._scale[0];
0398 y *= transform_._scale[1];
0399
0400 t._fill = font_._fc;
0401
0402
0403 t._field = text_;
0404 t._attribute_map["x"] = utils::to_string(x);
0405 t._attribute_map["y"] = utils::to_string(-y);
0406 t._attribute_map["font-family"] = font_._family;
0407 t._attribute_map["font-size"] = std::to_string(font_._size);
0408 t._field_span = font_._size * font_._line_spacing;
0409
0410 size_t l = 0;
0411 for (const auto &tl : text_) {
0412 l = l > tl.size() ? l : tl.size();
0413 }
0414
0415 scalar fs = font_._size;
0416
0417 detail::adapt_range(
0418 t, {{x, y - l}, {static_cast<scalar>(x + 0.7 * fs * l), y + l}});
0419
0420 t._barycenter = utils::barycenter<std::array<scalar, 2>>(
0421 {{x, y - l}, {static_cast<scalar>(x + 0.7 * fs * l), y + l}});
0422
0423 return t;
0424 }
0425
0426
0427
0428
0429
0430
0431
0432
0433
0434
0435
0436
0437
0438
0439 static inline svg::object connected_text(
0440 const std::string &id_, const point2 &p_,
0441 const std::vector<std::string> &text_, const style::font &font_,
0442 const style::transform &transform_, const svg::object &object_,
0443 const std::vector<std::string> &highlight_ = {"mouseover", "mouseout"}) {
0444 auto t = text(id_, p_, text_, font_, transform_);
0445
0446 t._attribute_map["display"] = "none";
0447
0448 svg::object on;
0449 on._tag = "animate";
0450 on._attribute_map["fill"] = "freeze";
0451 on._attribute_map["attributeName"] = "display";
0452 on._attribute_map["from"] = "none";
0453 on._attribute_map["to"] = "block";
0454 on._attribute_map["begin"] = object_._id + __d + highlight_[1];
0455
0456 svg::object off;
0457
0458 off._tag = "animate";
0459 off._attribute_map["fill"] = "freeze";
0460 off._attribute_map["attributeName"] = "display";
0461 off._attribute_map["to"] = "none";
0462 off._attribute_map["from"] = "block";
0463 off._attribute_map["begin"] = object_._id + __d + highlight_[0];
0464
0465
0466 t._sub_objects.push_back(on);
0467 t._sub_objects.push_back(off);
0468 return t;
0469 }
0470
0471
0472
0473
0474
0475
0476
0477
0478
0479
0480
0481
0482
0483
0484
0485
0486
0487
0488 static inline svg::object connected_info_box(
0489 const std::string &id_, const point2 &p_, const std::string &title_,
0490 const style::fill &title_fill_, const style::font &title_font_,
0491 const std::vector<std::string> &text_, const style::fill &text_fill_,
0492 const style::font &text_font_, const style::stroke &stroke_,
0493 const svg::object &object_,
0494 const std::vector<std::string> &highlight_ = {"mouseover", "mouseout"}) {
0495
0496 svg::object ib;
0497 ib._tag = "g";
0498 ib._id = id_;
0499
0500 size_t tew = 0;
0501 for (const auto &t : text_) {
0502 tew = t.size() > tew ? t.size() : tew;
0503 }
0504 scalar tews = (tew + 2) * text_font_._size;
0505 scalar tiws = (title_.size() + 2) * title_font_._size;
0506 scalar ws = std::max(tews, tiws);
0507
0508 scalar tih = 2 * title_font_._size;
0509 scalar teh =
0510 static_cast<scalar>(1.5 * text_.size() + 0.5) * text_font_._size;
0511
0512 std::vector<std::array<scalar, 2>> tic = {p_,
0513 {p_[0], p_[1] - tih},
0514 {p_[0] + ws, p_[1] - tih},
0515 {p_[0] + ws, p_[1]}};
0516
0517 auto tibox = polygon(id_ + "_title_box", tic, title_fill_, stroke_);
0518 ib.add_object(tibox);
0519 auto ti = text(id_ + "_title",
0520 {p_[0] + title_font_._size,
0521 static_cast<scalar>(p_[1] - 1.5 * title_font_._size)},
0522 {title_}, title_font_);
0523 ib.add_object(ti);
0524
0525 std::vector<std::array<scalar, 2>> tec = {{p_[0], p_[1] - tih},
0526 {p_[0] + ws, p_[1] - tih},
0527 {p_[0] + ws, p_[1] - tih - teh},
0528 {p_[0], p_[1] - tih - teh}};
0529
0530 auto tebox = polygon(id_ + "_text_box", tec, text_fill_, stroke_);
0531 ib.add_object(tebox);
0532 auto te =
0533 text(id_ + "_text",
0534 {p_[0] + title_font_._size, static_cast<scalar>(p_[1] - tih)},
0535 text_, text_font_);
0536 ib.add_object(te);
0537
0538
0539 if (object_.is_defined()) {
0540 ib._attribute_map["display"] = "none";
0541
0542 svg::object on;
0543 on._tag = "animate";
0544 on._attribute_map["fill"] = "freeze";
0545 on._attribute_map["attributeName"] = "display";
0546 on._attribute_map["from"] = "none";
0547 on._attribute_map["to"] = "block";
0548 on._attribute_map["begin"] = object_._id + __d + highlight_[1];
0549
0550 svg::object off;
0551
0552 off._tag = "animate";
0553 off._attribute_map["fill"] = "freeze";
0554 off._attribute_map["attributeName"] = "display";
0555 off._attribute_map["to"] = "none";
0556 off._attribute_map["from"] = "block";
0557 off._attribute_map["begin"] = object_._id + __d + highlight_[0];
0558
0559
0560 ib._sub_objects.push_back(on);
0561 ib._sub_objects.push_back(off);
0562 }
0563 return ib;
0564 }
0565
0566
0567
0568
0569
0570
0571
0572
0573
0574
0575
0576
0577 static inline svg::object cartesian_grid(
0578 const std::string &id_, const std::vector<scalar> &l0_edges_,
0579 const std::vector<scalar> &l1_edges_,
0580 const style::stroke &stroke_ = style::stroke(),
0581 const style::transform &transform_ = style::transform()) {
0582
0583 svg::object grid;
0584 grid._tag = "g";
0585 grid._id = id_;
0586
0587 scalar l0_min = l0_edges_[0];
0588 scalar l0_max = l0_edges_[l0_edges_.size() - 1];
0589
0590 scalar l1_min = l1_edges_[0];
0591 scalar l1_max = l1_edges_[l1_edges_.size() - 1];
0592
0593 for (auto [i0, l0] : utils::enumerate(l0_edges_)) {
0594 grid.add_object(line(id_ + "_l0_" + std::to_string(i0), {l0, l1_min},
0595 {l0, l1_max}, stroke_, transform_));
0596 }
0597
0598 for (auto [i1, l1] : utils::enumerate(l1_edges_)) {
0599 grid.add_object(line(id_ + "_l1_" + std::to_string(i1), {l0_min, l1},
0600 {l0_max, l1}, stroke_, transform_));
0601 }
0602
0603 return grid;
0604 }
0605
0606
0607
0608
0609
0610
0611
0612
0613
0614
0615
0616
0617
0618
0619
0620
0621 static inline svg::object tiled_cartesian_grid(
0622 const std::string &id_, const std::vector<scalar> &l0_edges_,
0623 const std::vector<scalar> &l1_edges_,
0624 const style::fill &fill_ = style::fill(),
0625 const style::stroke &stroke_ = style::stroke(),
0626 const style::transform &transform_ = style::transform()) {
0627
0628 svg::object grid;
0629 grid._tag = "g";
0630 grid._id = id_;
0631
0632
0633 for (size_t il0 = 1; il0 < l0_edges_.size(); ++il0) {
0634
0635 std::string gs = id_ + "_";
0636 gs += std::to_string(il0 - 1);
0637 gs += "_";
0638 for (size_t il1 = 1; il1 < l1_edges_.size(); ++il1) {
0639 std::array<scalar, 2u> llc = {l0_edges_[il0 - 1],
0640 l1_edges_[il1 - 1]};
0641 std::array<scalar, 2u> lrc = {l0_edges_[il0], l1_edges_[il1 - 1]};
0642 std::array<scalar, 2u> rrc = {l0_edges_[il0], l1_edges_[il1]};
0643 std::array<scalar, 2u> rlc = {l0_edges_[il0 - 1], l1_edges_[il1]};
0644
0645 std::vector<std::array<scalar, 2u>> tile = {llc, lrc, rrc, rlc};
0646
0647 auto grid_tile = polygon(gs + std::to_string(il1 - 1), tile, fill_,
0648 stroke_, transform_);
0649 grid_tile._aux_info = {std::string("* bin (" +
0650 std::to_string(il0 - 1) + "," +
0651 std::to_string(il1 - 1) + ")")};
0652 grid.add_object(grid_tile);
0653 }
0654 }
0655 return grid;
0656 }
0657
0658
0659
0660
0661
0662
0663
0664
0665
0666
0667
0668
0669 static inline svg::object fan_grid(
0670 const std::string &id_, const std::vector<scalar> &x_low_edges_,
0671 const std::vector<scalar> &x_high_edges_,
0672 const std::vector<scalar> &y_edges_,
0673 const style::stroke &stroke_ = style::stroke(),
0674 const style::transform &transform_ = style::transform()) noexcept(false) {
0675
0676 svg::object grid;
0677 grid._tag = "g";
0678 grid._id = id_;
0679
0680 if (x_low_edges_.size() != x_high_edges_.size()) {
0681 throw std::invalid_argument(
0682 "fan_grid: mismatch in low/high edge numbers");
0683 }
0684
0685 scalar x_low_min = x_low_edges_[0];
0686 scalar x_low_max = x_low_edges_[x_low_edges_.size() - 1];
0687
0688 scalar x_high_min = x_high_edges_[0];
0689 scalar x_high_max = x_high_edges_[x_high_edges_.size() - 1];
0690
0691 scalar y_min = y_edges_[0];
0692 scalar y_max = y_edges_[y_edges_.size() - 1];
0693
0694
0695 scalar x_low_slope = (x_high_min - x_low_min) / (y_max - y_min);
0696 scalar x_high_slope = (x_high_max - x_low_max) / (y_max - y_min);
0697
0698 for (const auto [ix, x] : utils::enumerate(x_low_edges_)) {
0699 scalar x_min = x;
0700 scalar x_max = x_high_edges_[ix];
0701 grid.add_object(line(id_ + "_l0_" + std::to_string(ix), {x_min, y_min},
0702 {x_max, y_max}, stroke_, transform_));
0703 }
0704
0705 for (const auto &[iy, y] : utils::enumerate(y_edges_)) {
0706 scalar x_min = x_low_min + (y - y_min) * x_low_slope;
0707 scalar x_max = x_low_max + (y - y_min) * x_high_slope;
0708 grid.add_object(line(id_ + "_l1_" + std::to_string(iy), {x_min, y},
0709 {x_max, y}, stroke_, transform_));
0710 }
0711
0712 return grid;
0713 }
0714
0715
0716
0717
0718
0719
0720
0721
0722
0723
0724
0725
0726
0727
0728
0729
0730
0731 static inline svg::object tiled_fan_grid(
0732 const std::string &id_, const std::vector<scalar> &x_low_edges_,
0733 const std::vector<scalar> &x_high_edges_,
0734 const std::vector<scalar> &y_edges_,
0735 const style::fill &fill_ = style::fill(),
0736 const style::stroke &stroke_ = style::stroke(),
0737 const style::transform &transform_ = style::transform()) noexcept(false) {
0738 svg::object grid;
0739 grid._tag = "g";
0740 grid._id = id_;
0741
0742
0743 std::vector<svg::object> grid_tiles;
0744
0745 if (x_low_edges_.size() != x_high_edges_.size()) {
0746 throw std::invalid_argument(
0747 "fan_grid: mismatch in low/high edge numbers");
0748 }
0749
0750 scalar y_min = y_edges_[0];
0751 scalar y_max = y_edges_[y_edges_.size() - 1];
0752
0753 std::vector<scalar> slopes;
0754 for (auto [ix, xl] : utils::enumerate(x_low_edges_)) {
0755 scalar xh = x_high_edges_[ix];
0756 slopes.push_back((xh - xl) / (y_max - y_min));
0757 }
0758
0759 for (auto [iy, yl] : utils::enumerate(y_edges_)) {
0760 if (iy + 1 == y_edges_.size()) {
0761 continue;
0762 }
0763 scalar yh = y_edges_[iy + 1];
0764 for (auto [ix, xll] : utils::enumerate(x_low_edges_)) {
0765 if (ix + 1 == x_low_edges_.size()) {
0766 continue;
0767 }
0768 scalar xlr = x_low_edges_[ix + 1];
0769
0770 scalar x_l_slope = slopes[ix];
0771 scalar x_r_slope = slopes[ix + 1];
0772
0773
0774 scalar xll_c = xll + x_l_slope * (yl - y_min);
0775 scalar xlr_c = xlr + x_r_slope * (yl - y_min);
0776
0777 scalar xhl_c = xll + x_l_slope * (yh - y_min);
0778 scalar xhr_c = xlr + x_r_slope * (yh - y_min);
0779
0780
0781 std::string g_n =
0782 id_ + "_" + std::to_string(ix) + "_" + std::to_string(iy);
0783 auto grid_tile = draw::polygon(
0784 g_n, {{xll_c, yl}, {xlr_c, yl}, {xhr_c, yh}, {xhl_c, yh}},
0785 fill_, stroke_, transform_);
0786 grid_tile._aux_info = {std::string("* bin (" + std::to_string(ix) +
0787 "," + std::to_string(iy) + ")")};
0788 grid.add_object(grid_tile);
0789 }
0790 }
0791
0792 return grid;
0793 }
0794
0795
0796
0797
0798
0799
0800
0801
0802
0803
0804
0805 static inline svg::object polar_grid(
0806 const std::string &id_, const std::vector<scalar> &r_edges_,
0807 const std::vector<scalar> &phi_edges_,
0808 const style::stroke &stroke_ = style::stroke(),
0809 const style::transform &transform_ = style::transform()) {
0810
0811 svg::object grid;
0812 grid._tag = "g";
0813 grid._id = id_;
0814
0815 scalar r_min = r_edges_[0];
0816 scalar r_max = r_edges_[r_edges_.size() - 1];
0817
0818 scalar phi_min = phi_edges_[0];
0819 scalar phi_max = phi_edges_[phi_edges_.size() - 1];
0820
0821 scalar cos_phi_min = std::cos(phi_min);
0822 scalar sin_phi_min = std::sin(phi_min);
0823 scalar cos_phi_max = std::cos(phi_max);
0824 scalar sin_phi_max = std::sin(phi_max);
0825
0826 style::fill fill;
0827
0828 for (const auto [ir, r] : utils::enumerate(r_edges_)) {
0829 grid.add_object(draw::arc(id_ + "_r_" + std::to_string(ir), r,
0830 {r * cos_phi_min, r * sin_phi_min},
0831 {r * cos_phi_max, r * sin_phi_max}, fill,
0832 stroke_, transform_));
0833 }
0834
0835 for (const auto &[iphi, phi] : utils::enumerate(phi_edges_)) {
0836 scalar cos_phi = std::cos(phi);
0837 scalar sin_phi = std::sin(phi);
0838 grid.add_object(draw::line(id_ + "_l_" + std::to_string(iphi),
0839 {r_min * cos_phi, r_min * sin_phi},
0840 {r_max * cos_phi, r_max * sin_phi}, stroke_,
0841 transform_));
0842 }
0843 return grid;
0844 }
0845
0846
0847
0848
0849
0850
0851
0852
0853
0854
0855
0856
0857
0858
0859
0860
0861 static inline svg::object tiled_polar_grid(
0862 const std::string &id_, const std::vector<scalar> &r_edges_,
0863 const std::vector<scalar> &phi_edges_,
0864 const style::fill &fill_ = style::fill(),
0865 const style::stroke &stroke_ = style::stroke(),
0866 const style::transform &transform_ = style::transform()) {
0867 svg::object grid;
0868 grid._tag = "g";
0869 grid._id = id_;
0870
0871 for (size_t ir = 1; ir < r_edges_.size(); ++ir) {
0872
0873 std::string gs = id_ + "_";
0874 gs += std::to_string(ir - 1);
0875 gs += "_";
0876 for (size_t iphi = 1; iphi < phi_edges_.size(); ++iphi) {
0877 auto sector_contour = generators::sector_contour(
0878 r_edges_[ir - 1], r_edges_[ir], phi_edges_[iphi - 1],
0879 phi_edges_[iphi]);
0880
0881 auto grid_tile =
0882 polygon(gs + std::to_string(iphi - 1), sector_contour, fill_,
0883 stroke_, transform_);
0884 grid_tile._aux_info = {std::string("* bin (" +
0885 std::to_string(ir - 1) + "," +
0886 std::to_string(iphi - 1) + ")")};
0887 grid.add_object(grid_tile);
0888 }
0889 }
0890 return grid;
0891 }
0892
0893
0894
0895
0896
0897
0898
0899
0900
0901
0902
0903 static inline svg::object marker(const std::string &id_, const point2 &at_,
0904 const style::marker &marker_,
0905 scalar rot_ = 0.) {
0906 svg::object marker_group;
0907 marker_group._tag = "g";
0908
0909 std::vector<point2> arrow_head;
0910 auto size = marker_._size;
0911
0912
0913 scalar m_offset = 0.;
0914
0915
0916 if (marker_._type.substr(0u, 1u) == "|") {
0917 auto measure_line = line(
0918 id_ + "_line", {at_[0], static_cast<scalar>(at_[1] - 2 * size)},
0919 {at_[0], static_cast<scalar>(at_[1] + 2 * size)}, marker_._stroke);
0920 marker_group.add_object(measure_line);
0921 m_offset = -size;
0922 }
0923
0924 if (marker_._type.find("<") != std::string::npos) {
0925 arrow_head = {{at_[0] - size + m_offset, at_[1] - size},
0926 {at_[0] + size + m_offset, at_[1]},
0927 {at_[0] - size + m_offset, at_[1] + size}};
0928
0929
0930 if (marker_._type.find("<<") != std::string::npos) {
0931
0932 arrow_head.push_back(
0933 {static_cast<scalar>(at_[0] - 0.25 * size + m_offset), at_[1]});
0934 } else if (marker_._type.substr(1u, marker_._type.size()).find("|") ==
0935 std::string::npos) {
0936 arrow_head.push_back(
0937 {static_cast<scalar>(at_[0] + 1 * size + m_offset), at_[1]});
0938 }
0939 } else if (marker_._type.find("o") != std::string::npos) {
0940
0941 svg::object dot =
0942 circle(id_, at_, 0.5 * size, marker_._fill, marker_._stroke);
0943 marker_group.add_object(dot);
0944 } else if (marker_._type.find("x") != std::string::npos) {
0945 scalar a_x = at_[0];
0946 scalar a_y = at_[1];
0947 scalar h_s = 0.5 * size;
0948 marker_group.add_object(
0949 line(id_ + "_ml0", {a_x - h_s, a_y - h_s}, {a_x + h_s, a_y + h_s}));
0950 marker_group.add_object(
0951 line(id_ + "_ml1", {a_x - h_s, a_y + h_s}, {a_x + h_s, a_y - h_s}));
0952 }
0953
0954
0955 if (not arrow_head.empty()) {
0956 auto arrow = polygon(id_, arrow_head, marker_._fill, marker_._stroke);
0957 marker_group.add_object(arrow);
0958 }
0959
0960
0961 if (rot_ != 0.) {
0962 style::transform group_transform = style::transform{};
0963 group_transform._rot = {static_cast<scalar>(rot_ * 180. / M_PI), at_[0],
0964 at_[1]};
0965 marker_group._transform = group_transform;
0966 }
0967
0968 return marker_group;
0969 }
0970
0971
0972
0973
0974
0975
0976
0977
0978
0979
0980
0981
0982
0983
0984
0985 static inline svg::object measure(
0986 const std::string &id_, const point2 &start_, const point2 &end_,
0987 const style::stroke &stroke_ = style::stroke(),
0988 const style::marker &start_marker_ = style::marker({"|<"}),
0989 const style::marker &end_marker_ = style::marker({"|<"}),
0990 const style::font &font_ = style::font(), const std::string &label_ = "",
0991 const point2 &label_pos_ = {0., 0.}) {
0992
0993 svg::object measure_group;
0994 measure_group._tag = "g";
0995 measure_group._id = id_;
0996
0997 auto mline = line(id_ + "_line", start_, end_, stroke_);
0998 measure_group.add_object(mline);
0999
1000
1001 scalar theta = std::atan2(end_[1] - start_[1], end_[0] - start_[0]);
1002 if (std::abs(end_[1] - start_[1]) <
1003 std::numeric_limits<scalar>::epsilon()) {
1004 theta = (end_[0] > start_[0]) ? 0. : M_PI;
1005 }
1006
1007 measure_group.add_object(marker(id_ + "_start_tag", {start_[0], start_[1]},
1008 start_marker_,
1009 M_PI + static_cast<scalar>(theta)));
1010 measure_group.add_object(marker(id_ + "_end_tag", {end_[0], end_[1]},
1011 end_marker_, static_cast<scalar>(theta)));
1012
1013 if (not label_.empty()) {
1014 auto ltext = text(id_ + "_label", label_pos_, {label_}, font_);
1015 measure_group.add_object(ltext);
1016 }
1017 return measure_group;
1018 }
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035 static inline svg::object arc_measure(
1036 const std::string &id_, scalar r_, const point2 &start_, const point2 &end_,
1037 const style::stroke &stroke_ = style::stroke(),
1038 const style::marker &start_marker_ = style::marker(),
1039 const style::marker &end_marker_ = style::marker({"|<"}),
1040 const style::font &font_ = style::font(), const std::string &label_ = "",
1041 const point2 &label_pos_ = {0., 0.}) {
1042
1043 svg::object measure_group;
1044 measure_group._tag = "g";
1045 measure_group._id = id_;
1046
1047 measure_group.add_object(
1048 arc((id_ + "_arc"), r_, start_, end_, style::fill(), stroke_));
1049
1050
1051 if (not start_marker_._type.empty() and
1052 start_marker_._type != std::string("none")) {
1053 scalar theta_start = atan2(start_[1], start_[0]);
1054 measure_group.add_object(
1055 marker(id_ + "_start_tag", {start_[0], start_[1]}, start_marker_,
1056 static_cast<scalar>(theta_start - 0.5 * M_PI)));
1057 }
1058
1059 scalar theta_end = atan2(end_[1], end_[0]);
1060 measure_group.add_object(
1061 marker(id_ + "_end_tag", {end_[0], end_[1]}, end_marker_,
1062 static_cast<scalar>(theta_end + 0.5 * M_PI)));
1063
1064 if (not label_.empty()) {
1065 auto ltext = text(id_ + "_label", label_pos_, {label_}, font_);
1066 measure_group.add_object(ltext);
1067 }
1068
1069 return measure_group;
1070 }
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083 static inline svg::object arrow(
1084 const std::string &id_, const point2 &start_, const point2 &end_,
1085 const style::stroke &stroke_ = style::stroke(),
1086 const style::marker &start_marker_ = style::marker({"|<"}),
1087 const style::marker &end_marker_ = style::marker({"|<"})) {
1088
1089 return measure(id_, start_, end_, stroke_, start_marker_, end_marker_);
1090 }
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105 static inline svg::object x_y_axes(
1106 const std::string &id_, const std::array<scalar, 2> &x_range_,
1107 const std::array<scalar, 2> &y_range_,
1108 const style::stroke &stroke_ = style::stroke(),
1109 const std::string &x_label_ = "", const std::string &y_label_ = "",
1110 const style::font &font_ = style::font(),
1111 const style::axis_markers<2u> &markers_ = {__standard_axis_markers,
1112 __standard_axis_markers}) {
1113 svg::object axes;
1114 axes._tag = "g";
1115 axes._id = id_;
1116
1117 auto x =
1118 line(id_ + "_x_axis", {x_range_[0], 0.}, {x_range_[1], 0.}, stroke_);
1119 auto y =
1120 line(id_ + "_y_axis", {0., y_range_[0]}, {0., y_range_[1]}, stroke_);
1121
1122 axes.add_object(x);
1123 axes.add_object(y);
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133 auto add_marker = [&](const point2 &p_, unsigned int b0_, unsigned int b1_,
1134 scalar rot_, const std::string &mid_) -> void {
1135 auto lmarker = markers_[b0_][b1_];
1136 if (not lmarker._type.empty()) {
1137 axes.add_object(marker(mid_, {p_[0], p_[1]}, lmarker, rot_));
1138 }
1139 };
1140
1141
1142 add_marker({x_range_[0], 0.}, 0, 0, M_PI, id_ + "_neg_x_head");
1143 add_marker({x_range_[1], 0.}, 0, 1, 0., id_ + "pos_x_head");
1144 add_marker({0., y_range_[0]}, 1, 0, -0.5 * M_PI, id_ + "neg_y_head");
1145 add_marker({0., y_range_[1]}, 1, 1, 0.5 * M_PI, id_ + "pos_y_head");
1146
1147
1148 if (not x_label_.empty()) {
1149 scalar size = markers_[0][1]._size;
1150 auto xlab = text(id_ + "_x_label", {x_range_[1] + 2 * size, size},
1151 {x_label_}, font_);
1152 axes.add_object(xlab);
1153 }
1154
1155 if (not y_label_.empty()) {
1156 scalar size = markers_[1][1]._size;
1157 auto ylab = text(id_ + "_y_label", {-size, y_range_[1] + 2 * size},
1158 {y_label_}, font_);
1159 axes.add_object(ylab);
1160 }
1161
1162 return axes;
1163 }
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175 static inline svg::object from_template(
1176 const std::string &id_, const svg::object &ro_,
1177 const style::fill &f_ = style::fill(),
1178 const style::stroke &s_ = style::stroke(),
1179 const style::transform t_ = style::transform()) {
1180
1181 svg::object nsvg;
1182 nsvg._sterile = true;
1183 nsvg._id = id_;
1184 nsvg._tag = "g";
1185
1186
1187 svg::object use_obj;
1188 use_obj._tag = "use";
1189 use_obj._id = id_ + "_use";
1190 use_obj._attribute_map["xlink:href"] = "#" + ro_._id;
1191 use_obj._definitions.push_back(ro_);
1192
1193
1194 use_obj._barycenter = {(ro_._barycenter[0] + t_._tr[0]),
1195 (ro_._barycenter[1] + t_._tr[1])};
1196
1197
1198 use_obj._fill = f_;
1199 use_obj._stroke = s_;
1200 use_obj._transform = t_;
1201
1202
1203 nsvg.add_object(use_obj);
1204
1205 return nsvg;
1206 }
1207
1208 }
1209
1210 }