diff --git a/core/include/actsvg/core/utils.hpp b/core/include/actsvg/core/utils.hpp index 671e89a..c581dc6 100644 --- a/core/include/actsvg/core/utils.hpp +++ b/core/include/actsvg/core/utils.hpp @@ -195,33 +195,65 @@ point2_type rotate(const point2_type &p_, scalar a_) { return p_rot; } -/** Helper method toscala a point3 object +/** Helper method to scale object * - * @param p0_ the point3 object and @param s_ the scale + * @param p0_ the point object and @param s_ the scale * - * @return a new point3 + * @return a new point_type object **/ -template -point3_type scale(const point3_type &p0_, scalar s_) { - point3_type scaled; +template +point_type scale(const point_type &p0_, scalar s_) { + point_type scaled; scaled[0] = s_ * p0_[0]; scaled[1] = s_ * p0_[1]; - scaled[2] = s_ * p0_[2]; + if constexpr (kDIM == 3u) { + scaled[2] = s_ * p0_[2]; + } return scaled; } +/** Helper method for making a unit vector + * + * @param x0_ the starting point + * @param x1_ the ending point + * + * @return the distance + **/ +template +point_type unit_direction(const point_type &x0_, const point_type &x1_) { + // 2 dimensional case + if constexpr (kDIM == 2u) { + scalar length = std::sqrt(std::pow(x1_[0] - x0_[0], 2) + + std::pow(x1_[1] - x0_[1], 2)); + scalar norml = 1. / length; + return point_type{norml * (x1_[0] - x0_[0]), norml * (x1_[1] - x0_[1])}; + } + // 3 dimensional case + if constexpr (kDIM == 3u) { + scalar length = std::sqrt(std::pow(x1_[0] - x0_[0], 2) + + std::pow(x1_[1] - x0_[1], 2) + + std::pow(x1_[2] - x0_[2], 2)); + scalar norml = 1. / length; + return point_type{norml * (x1_[0] - x0_[0]), norml * (x1_[1] - x0_[1]), + norml * (x1_[2] - x0_[2])}; + } + return point_type{}; +} + /** Helper method to add two point3 objects * - * @param p0_ and @param p1_ the 3D points + * @param p0_ and @param p1_ the points * - * @return a new point3 + * @return a new point_type object **/ -template -point3_type add(const point3_type &p0_, const point3_type &p1_) { - point3_type added; +template +point_type add(const point_type &p0_, const point_type &p1_) { + point_type added; added[0] = p0_[0] + p1_[0]; added[1] = p0_[1] + p1_[1]; - added[2] = p0_[2] + p1_[2]; + if constexpr (kDIM == 3u) { + added[2] = p0_[2] + p1_[2]; + } return added; } diff --git a/core/src/core/draw.cpp b/core/src/core/draw.cpp index b0de3ca..51c3614 100644 --- a/core/src/core/draw.cpp +++ b/core/src/core/draw.cpp @@ -488,9 +488,11 @@ svg::object connected_info_box( auto tebox = polygon(id_ + "_text_box", tec, text_fill_, stroke_); ib.add_object(tebox); + + scalar toffset = text_.size() == 1u ? tih + title_font_._size : tih; auto te = text(id_ + "_text", - {p_[0] + title_font_._size, static_cast(p_[1] - tih)}, + {p_[0] + title_font_._size, static_cast(p_[1] - toffset)}, text_, text_font_); ib.add_object(te); diff --git a/meta/include/actsvg/display/geometry.hpp b/meta/include/actsvg/display/geometry.hpp index 973bc1f..1828ee9 100644 --- a/meta/include/actsvg/display/geometry.hpp +++ b/meta/include/actsvg/display/geometry.hpp @@ -17,6 +17,7 @@ #include "actsvg/core.hpp" #include "actsvg/display/tools.hpp" #include "actsvg/proto/surface.hpp" +#include "actsvg/styles/defaults.hpp" namespace actsvg { @@ -24,22 +25,32 @@ using namespace defaults; namespace display { +struct surface_options { + + /// @param _boolean_shape draw the boolean + bool _boolean_shape = true; + /// @param _focus draw at focus + bool _focus = false; + /// @param _draw_at_scale draw at scale + bool _draw_at_scale = false; + /// @param _draw_as_template draw as template + bool _draw_as_template = false; + /// @param _label_measures draw the labels + bool _label_measures = false; +}; + /** Draw the surface with a dedicated view * * @param id_ the identification of this surface * @param s_ the surface type - * @param v_ the view type - * @param b_ draw the boolean - * @param fs_ draw at focus - * @param sc_ draw at scale - * @param dt_ draw as template + * @param o_ the options struct * * @note template surfaces ignore the view_type::scene range restriction */ template svg::object surface(const std::string& id_, const surface_type& s_, - const view_type& v_, bool _b = true, bool fs_ = false, - bool sc_ = false, bool dt_ = false) { + const view_type& v_, + const surface_options& o_ = surface_options{}) { svg::object s; @@ -48,12 +59,12 @@ svg::object surface(const std::string& id_, const surface_type& s_, style::transform draw_transform = s_._transform; // No rotation nor shift as template - if (dt_) { + if (o_._draw_as_template) { draw_transform._tr = {0., 0.}; draw_transform._rot = {0., 0., 0.}; } // Apply scale or not - if (not sc_) { + if (!o_._draw_at_scale) { draw_transform._scale = {1., 1.}; } @@ -63,7 +74,8 @@ svg::object surface(const std::string& id_, const surface_type& s_, return s; } - style::transform draw_transform = fs_ ? style::transform{} : s_._transform; + style::transform draw_transform = + o_._focus ? style::transform{} : s_._transform; draw_transform._scale = s_._transform._scale; // Surface directly @@ -86,32 +98,86 @@ svg::object surface(const std::string& id_, const surface_type& s_, annulusCircleIx(origin_x, origin_y, max_r, min_phi_rel); auto out_left_s_xy = annulusCircleIx(origin_x, origin_y, max_r, max_phi_rel); - + // For drawing, this needs a shift + scalar irx = in_right_s_xy[0] - origin_x; + scalar iry = in_right_s_xy[1] - origin_y; + scalar ilx = in_left_s_xy[0] - origin_x; + scalar ily = in_left_s_xy[1] - origin_y; + scalar orx = out_right_s_xy[0] - origin_x; + scalar ory = out_right_s_xy[1] - origin_y; + scalar olx = out_left_s_xy[0] - origin_x; + scalar oly = out_left_s_xy[1] - origin_y; // Dedicated path drawing of the annulus bounds s._tag = "path"; s._id = id_; - std::string path = "M " + std::to_string(in_right_s_xy[0]) + " " + - std::to_string(-in_right_s_xy[1]); + std::string path = + "M " + std::to_string(irx) + " " + std::to_string(-iry); path += " A " + std::to_string(min_r) + " " + std::to_string(min_r); path += " 0 0 0 "; - path += std::to_string(in_left_s_xy[0]) + " " + - std::to_string(-in_left_s_xy[1]); - path += " L " + std::to_string(out_left_s_xy[0]) + " " + - std::to_string(-out_left_s_xy[1]); + path += std::to_string(ilx) + " " + std::to_string(-ily); + path += " L " + std::to_string(olx) + " " + std::to_string(-oly); path += " A " + std::to_string(max_r) + " " + std::to_string(max_r); path += " 0 0 1 "; - path += std::to_string(out_right_s_xy[0]) + " " + - std::to_string(-out_right_s_xy[1]); - path += " L " + std::to_string(in_right_s_xy[0]) + " " + - std::to_string(-in_right_s_xy[1]); + path += std::to_string(orx) + " " + std::to_string(-ory); + path += " L " + std::to_string(irx) + " " + std::to_string(-iry); s._attribute_map["d"] = path; s._fill = s_._fill; s._stroke = s_._stroke; s._transform = draw_transform; - if (not fs_) { + if (!o_._focus) { s._x_range = {-max_r, max_r}; s._y_range = {-max_r, max_r}; } + + if (o_._label_measures) { + + // make a copy & a group out of the object + auto sc = s; + s = svg::object{}; + s._tag = "g"; + s._id = id_ + "_labeled_group"; + s.add_object(sc); + + // Draw min / max circles + s.add_object(draw::circle(id_ + "_inner_circle", {0., 0}, min_r, + style::fill{}, + defaults::__bl_dotted_stroke)); + s.add_object(draw::circle(id_ + "_outer_circle", {0., 0}, max_r, + style::fill{}, + defaults::__bl_dotted_stroke)); + + // Define the origin of the strip system & draw the lines + point2 origin = {-origin_x, -origin_y}; + auto el0 = point2{olx, oly}; + auto el1 = point2{orx, ory}; + std::array lines = {el0, el1}; + for (const auto [il, lline] : utils::enumerate(lines)) { + point2 dline = utils::unit_direction<>(origin, lline); + point2 dneg = utils::scale(dline, -0.2 * min_r); + point2 dpos = utils::scale(dneg, -1.); + + auto line = draw::line(id_ + "_line" + std::to_string(il), + utils::add(origin, dneg), + utils::add(lline, dpos), + defaults::__bl_dotted_stroke); + line._transform = draw_transform; + s.add_object(line); + } + } + + // Package into a translated group if we have a surfade transform + if (s_._surface_transform.has_value()) { + const auto& sfg = s_._surface_transform.value(); + if (sfg._translation[0] != 0. || sfg._translation[1] != 0.) { + auto tg = s; + s = svg::object{}; + s._id = id_ + "_translated"; + s._tag = "g"; + s._transform._tr = {-sfg._translation[0], sfg._translation[1]}; + s.add_object(tg); + } + } + } else if (s_._type == surface_type::type::e_disc) { // x-y view for discs @@ -145,14 +211,14 @@ svg::object surface(const std::string& id_, const surface_type& s_, s_c_._radii = {0., s_._radii[1u]}; svg::object outer_mask = - surface(id_ + "_mask_surface_outer", s_c_, v_, false); + surface(id_ + "_mask_surface_outer", s_c_, v_, {false}); outer_mask._fill = style::fill{true}; outer_mask._stroke = style::stroke{true}; outer_mask._attribute_map["fill"] = "white"; s_c_._radii = {0., s_._radii[0u]}; svg::object inner_mask = - surface(id_ + "_mask_surface_inner", s_c_, v_, false); + surface(id_ + "_mask_surface_inner", s_c_, v_, {false}); inner_mask._fill = style::fill{true}; inner_mask._stroke = style::stroke{true}; inner_mask._attribute_map["fill"] = "black"; @@ -216,7 +282,6 @@ svg::object surface(const std::string& id_, const surface_type& s_, } } else if (s_._type == surface_type::type::e_straw) { - // xy view if constexpr (std::is_same_v) { // Skin of the straw @@ -395,7 +460,7 @@ svg::object surface(const std::string& id_, const surface_type& s_, draw_transform); } - if (_b) { + if (o_._boolean_shape) { /// Boolean surfaces only supported for x-y view so far if constexpr (std::is_same_v) { if (s_._boolean_surface.size() == 1u and @@ -403,7 +468,7 @@ svg::object surface(const std::string& id_, const surface_type& s_, std::string mask_id = id_ + "_mask"; // make a new boolean surface svg::object outer_mask = - surface(id_ + "_mask_surface_outer", s_, v_, false); + surface(id_ + "_mask_surface_outer", s_, v_, {false}); outer_mask._fill = style::fill{true}; outer_mask._stroke = style::stroke{true}; outer_mask._attribute_map["fill"] = "white"; diff --git a/meta/include/actsvg/display/helpers.hpp b/meta/include/actsvg/display/helpers.hpp index 892cd98..e041f02 100644 --- a/meta/include/actsvg/display/helpers.hpp +++ b/meta/include/actsvg/display/helpers.hpp @@ -1,6 +1,6 @@ // This file is part of the actsvg packge. // -// Copyright (C) 2022 CERN for the benefit of the ACTS project +// Copyright (C) 2022-2024 CERN for the benefit of the ACTS project // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -236,8 +236,8 @@ process_modules(const volume_type& v_, const view_type& view_, } auto surface_module = - display::surface(draw_surface._name, draw_surface, view_, true, - false, true, false); + display::surface(draw_surface._name, draw_surface, view_, + {true, false, true, false}); modules.push_back(surface_module); } all_modules.push_back(modules); diff --git a/meta/include/actsvg/display/sheets.hpp b/meta/include/actsvg/display/sheets.hpp index d3aff99..01af0d1 100644 --- a/meta/include/actsvg/display/sheets.hpp +++ b/meta/include/actsvg/display/sheets.hpp @@ -91,7 +91,7 @@ svg::object surface_sheet_xy(const std::string& id_, // The boolean parameters are : we draw the surface with booleans, focusses, // scaled, and as template auto surface = display::surface(s_._name + "_in_sheet", draw_surface, - x_y_view, true, true, true, true); + x_y_view, {true, true, true, true}); so.add_object(surface); // disc/annulus may overwrite axis drawing diff --git a/meta/include/actsvg/styles/defaults.hpp b/meta/include/actsvg/styles/defaults.hpp index b5458b4..999588f 100644 --- a/meta/include/actsvg/styles/defaults.hpp +++ b/meta/include/actsvg/styles/defaults.hpp @@ -65,6 +65,11 @@ extern style::stroke __r_stroke; extern style::fill __bl_fill; extern style::stroke __bl_stroke; +// Black, dashed stroke +extern style::stroke __bl_dashed_stroke; +// Black, dashed stroke +extern style::stroke __bl_dotted_stroke; + // No fill, no stroke extern style::fill __nn_fill; extern style::stroke __nn_stroke; diff --git a/meta/src/styles/defaults.cpp b/meta/src/styles/defaults.cpp index 59cea1c..ed8ea19 100644 --- a/meta/src/styles/defaults.cpp +++ b/meta/src/styles/defaults.cpp @@ -38,6 +38,8 @@ style::fill __r_fill; style::stroke __r_stroke; style::fill __bl_fill; style::stroke __bl_stroke; +style::stroke __bl_dashed_stroke; +style::stroke __bl_dotted_stroke; style::fill __nn_fill; style::stroke __nn_stroke; style::gradient __rgb_gradient; @@ -103,6 +105,14 @@ static bool create_defaults() { __bl_fill._fc._rgb = {0, 0, 0}; __bl_stroke._sc._rgb = {0, 0, 0}; + // Black dashed + __bl_dashed_stroke._sc._rgb = {0, 0, 0}; + __bl_dashed_stroke._dasharray = {3, 3}; + + // Black dashed + __bl_dotted_stroke._sc._rgb = {0, 0, 0}; + __bl_dotted_stroke._dasharray = {1, 1}; + // Nulls __nn_fill = style::fill(); diff --git a/python/src/core.cpp b/python/src/core.cpp index ad78de4..8e4ebe4 100644 --- a/python/src/core.cpp +++ b/python/src/core.cpp @@ -111,7 +111,6 @@ void add_core_module(context& ctx) { /// Draw a gradient box d.def("gradient_box", &draw::gradient_box); - } } } // namespace python diff --git a/python/src/dummies/examples.cpp b/python/src/dummies/examples.cpp index b6cb69c..b3ff97b 100644 --- a/python/src/dummies/examples.cpp +++ b/python/src/dummies/examples.cpp @@ -12,7 +12,6 @@ #include "../utilities.hpp" - namespace py = pybind11; using namespace pybind11::literals; diff --git a/python/src/examples.cpp b/python/src/examples.cpp index aa2ac12..976cde4 100644 --- a/python/src/examples.cpp +++ b/python/src/examples.cpp @@ -10,10 +10,9 @@ #include #include -#include "utilities.hpp" - #include "actsvg/data/odd_pixel_barrel.hpp" #include "actsvg/data/odd_pixel_endcap.hpp" +#include "utilities.hpp" namespace py = pybind11; using namespace pybind11::literals; @@ -30,13 +29,11 @@ void add_examples_module(context& ctx) { // Generate modules ressembling a barrel or endcap { - e.def("generate_barrel_modules", []() { - return data::generate_barrel_modules(); - }); + e.def("generate_barrel_modules", + []() { return data::generate_barrel_modules(); }); - e.def("generate_endcap_modules", []() { - return data::generate_endcap_modules(); - }); + e.def("generate_endcap_modules", + []() { return data::generate_endcap_modules(); }); } } } // namespace python diff --git a/python/src/modules.cpp b/python/src/modules.cpp index 0858048..fbb27f7 100644 --- a/python/src/modules.cpp +++ b/python/src/modules.cpp @@ -6,37 +6,37 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -#include "utilities.hpp" - #include #include #include +#include "utilities.hpp" + namespace py = pybind11; using namespace pybind11::literals; namespace actsvg::python { - /// The modules, for each - /// @param ctx is the python context - void add_core_module(context& ctx); - void add_io_module(context& ctx); - void add_style_module(context& ctx); - void add_proto_module(context& ctx); - void add_display_module(context& ctx); - void add_examples_module(context& ctx); -} +/// The modules, for each +/// @param ctx is the python context +void add_core_module(context& ctx); +void add_io_module(context& ctx); +void add_style_module(context& ctx); +void add_proto_module(context& ctx); +void add_display_module(context& ctx); +void add_examples_module(context& ctx); +} // namespace actsvg::python PYBIND11_MODULE(pyactsvg, m) { - actsvg::python::context ctx; - ctx.modules["main"] = m; - // These modules are always present - add_core_module(ctx); - add_io_module(ctx); - add_style_module(ctx); - add_proto_module(ctx); - add_display_module(ctx); - // Conditionally: could be dummy module - add_examples_module(ctx); + actsvg::python::context ctx; + ctx.modules["main"] = m; + // These modules are always present + add_core_module(ctx); + add_io_module(ctx); + add_style_module(ctx); + add_proto_module(ctx); + add_display_module(ctx); + // Conditionally: could be dummy module + add_examples_module(ctx); } diff --git a/python/src/proto.cpp b/python/src/proto.cpp index 980f443..1ad60ca 100644 --- a/python/src/proto.cpp +++ b/python/src/proto.cpp @@ -109,12 +109,12 @@ void add_proto_module(context& ctx) { surface::transform3{translation, rotation}; // Only x, y view will be transformed correctly point3 sfx = rotation[0]; - scalar tr_y = (sfx[1] > 0.) ? -translation[1] - : translation[1]; - // Calculate the rotation angle - sf._transform._tr = {translation[0], tr_y}; + // Force the translation of the transofrm to 0 + sf._transform._tr = {0., 0.}; scalar alpha = std::acos(sfx[0]) / M_PI * 180; - alpha += (sfx[1] > 0.) ? 180 : 0; + if (sfx[1] < 0.) { + alpha += 180.; + } sf._transform._rot = {alpha, 0., 0.}; } sf._fill = f; diff --git a/python/src/style.cpp b/python/src/style.cpp index 8d2dc2e..a234b56 100644 --- a/python/src/style.cpp +++ b/python/src/style.cpp @@ -47,7 +47,13 @@ void add_style_module(context& ctx) { py::class_>(s, "fill") .def(py::init<>()) .def(py::init(), py::arg("color")) - .def(py::init(), py::arg("sterile")); + .def(py::init(), py::arg("sterile")) + .def(py::init([](const std::array& rgb, scalar opacity) { + auto color = style::color{rgb}; + color._opacity = opacity; + return style::fill{color}; + }), + py::arg("rgb"), py::arg("opacity") = 1.); } { @@ -56,7 +62,13 @@ void add_style_module(context& ctx) { .def(py::init<>()) .def(py::init>(), py::arg("color"), py::arg("width"), py::arg("dash")) - .def(py::init(), py::arg("sterile")); + .def(py::init(), py::arg("sterile")) + .def(py::init([](const std::array& rgb, scalar width, + std::vector dash) { + return style::stroke{style::color{rgb}, width, dash}; + }), + py::arg("rgb"), py::arg("width") = 1., + py::arg("dash") = std::vector{}); } { @@ -67,8 +79,9 @@ void add_style_module(context& ctx) { const style::fill& f, const style::stroke& s) { return style::marker{t, sz, f, s}; }), - py::arg("type"), py::arg("size"), py::arg("fill"), - py::arg("stroke")); + py::arg("type"), py::arg("size"), + py::arg("fill") = style::fill{}, + py::arg("stroke") = style::stroke{}); } { @@ -82,7 +95,12 @@ void add_style_module(context& ctx) { return style::font{c, f, s, l_s, st}; }), py::arg("color"), py::arg("font_family"), py::arg("size"), - py::arg("line_spacing"), py::arg("style")); + py::arg("line_spacing"), py::arg("style")) + .def(py::init([](const std::array& rgb, unsigned int size) { + return style::font{style::color{rgb}, "Andale Mono", size, + 1.4, ""}; + }), + py::arg("rgb"), py::arg("size")); } { @@ -124,7 +142,9 @@ void add_style_module(context& ctx) { d.def("white_stroke", []() { return defaults::__w_stroke; }); d.def("font", []() { return defaults::__t_font; }); - } + + d.def("axis_markers", []() { return defaults::__a_markers; }); + } } } // namespace python } // namespace actsvg diff --git a/python/src/utilities.hpp b/python/src/utilities.hpp index 349e0f7..26b06c4 100644 --- a/python/src/utilities.hpp +++ b/python/src/utilities.hpp @@ -42,7 +42,9 @@ struct to_int { using return_type = int; - return_type operator()(const std::string& s) const { return std::atoi(s.c_str()); } + return_type operator()(const std::string& s) const { + return std::atoi(s.c_str()); + } }; /// Translate to scalar @@ -50,7 +52,9 @@ struct to_scalar { using return_type = scalar; - return_type operator()(const std::string& s) const { return std::atof(s.c_str()); } + return_type operator()(const std::string& s) const { + return std::atof(s.c_str()); + } }; /// Translate to string @@ -72,7 +76,7 @@ template std::vector split_string( const std::string& source, char sc, std::vector ignore = {}) { - // Temporary values + // Temporary values std::string temp; std::stringstream stringstream{source}; std::vector result; diff --git a/tests/meta/barrel_sheet.cpp b/tests/meta/barrel_sheet.cpp index 380b4d5..ed0acc4 100644 --- a/tests/meta/barrel_sheet.cpp +++ b/tests/meta/barrel_sheet.cpp @@ -136,7 +136,7 @@ proto::volume generate_barrel( barrel_module._template_object = display::surface( barrel_module._name + "_template", barrel_module_template, - x_y_view, true, true, true, true); + x_y_view, {true, true, true, true}); regular.push_back(barrel_module); @@ -180,8 +180,8 @@ proto::volume generate_barrel( barrel_module_backside._template_object = display::surface(barrel_module._name + "_template", - barrel_module_template, x_y_view, true, - true, true, true); + barrel_module_template, x_y_view, + {true, true, true, true}); backside.push_back(barrel_module_backside); }