diff --git a/plugins/svgtools/include/detray/plugins/svgtools/conversion/intersection_record.hpp b/plugins/svgtools/include/detray/plugins/svgtools/conversion/intersection_record.hpp index 7344f3c4a3..7da20d8b3a 100644 --- a/plugins/svgtools/include/detray/plugins/svgtools/conversion/intersection_record.hpp +++ b/plugins/svgtools/include/detray/plugins/svgtools/conversion/intersection_record.hpp @@ -11,43 +11,31 @@ #include "detray/intersection/intersection.hpp" #include "detray/plugins/svgtools/conversion/landmark.hpp" #include "detray/plugins/svgtools/meta/proto/intersection_record.hpp" -#include "detray/plugins/svgtools/utils/intersection_utils.hpp" // System include(s) #include namespace detray::svgtools::conversion { -/// @returns The proto intersection record from a list of points -template -inline auto intersection_record(const container_t& points) { - using p_intersection_record_t = - svgtools::meta::proto::intersection_record; - p_intersection_record_t p_ir; - for (const auto& point : points) { - const auto p_lm = svgtools::conversion::landmark(point); - p_ir._landmarks.push_back(p_lm); - } - return p_ir; -} +/// @returns The proto intersection of a detray intersection. +template +inline auto intersection( + const detector_t& detector, const dvector& intersections, + const typename detector_t::vector3 dir = {}, + const typename detector_t::geometry_context& gctx = {}) { + using p_intersection_t = svgtools::meta::proto::intersection; + p_intersection_t p_ir; -/// @returns The proto intersection record of a detray intersection record. -template -inline auto intersection_record( - const typename detector_t::geometry_context& context, - const detector_t& detector, - const std::vector< - std::pair>>& - intersection_record) { - std::vector points; - for (const auto& record : intersection_record) { - const auto point = svgtools::utils::intersection_point( - context, detector, record.second); - points.push_back(point); + std::vector landmarks; + for (const auto& intr : intersections) { + const detray::surface surface{detector, intr.sf_desc}; + const auto position = surface.local_to_global(gctx, intr.local, dir); + const auto p_lm = svgtools::conversion::landmark(position); + landmarks.push_back(p_lm); } - return svgtools::conversion::intersection_record(points); + + p_ir._landmarks = std::move(landmarks); + return p_ir; } -} // namespace detray::svgtools::conversion \ No newline at end of file +} // namespace detray::svgtools::conversion diff --git a/plugins/svgtools/include/detray/plugins/svgtools/conversion/landmark.hpp b/plugins/svgtools/include/detray/plugins/svgtools/conversion/landmark.hpp index f3c5f3131c..84ab216139 100644 --- a/plugins/svgtools/include/detray/plugins/svgtools/conversion/landmark.hpp +++ b/plugins/svgtools/include/detray/plugins/svgtools/conversion/landmark.hpp @@ -12,29 +12,16 @@ #include "detray/intersection/intersection.hpp" #include "detray/plugins/svgtools/conversion/point.hpp" #include "detray/plugins/svgtools/meta/proto/landmark.hpp" -#include "detray/plugins/svgtools/utils/intersection_utils.hpp" namespace detray::svgtools::conversion { -/// @returns The proto landmark of a detray intersection. -template -inline auto landmark( - const typename detector_t::geometry_context& context, - const detector_t& detector, - const detray::intersection2D& - d_intersection) { - const auto position = - svgtools::utils::intersection_point(context, detector, d_intersection); - return svgtools::conversion::landmark(position); -} - /// @returns The proto landmark of a detray point. template inline auto landmark(d_point3_t& position) { using p_landmark_t = svgtools::meta::proto::landmark; p_landmark_t p_lm; p_lm._position = svgtools::conversion::point(position); + return p_lm; } diff --git a/plugins/svgtools/include/detray/plugins/svgtools/illustrator.hpp b/plugins/svgtools/include/detray/plugins/svgtools/illustrator.hpp index 4ce5c0a3ad..277f93747c 100644 --- a/plugins/svgtools/include/detray/plugins/svgtools/illustrator.hpp +++ b/plugins/svgtools/include/detray/plugins/svgtools/illustrator.hpp @@ -48,6 +48,9 @@ namespace detray::svgtools { template class illustrator { + using point3 = std::array; + using point3_container = std::vector; + public: illustrator() = delete; @@ -61,6 +64,19 @@ class illustrator { detray::svgtools::styling::tableau_colorblind) : _detector{detector}, _name_map{name_map}, _style{style} {} + /// @returns the detector and volume names + const typename detector_t::name_map& names() { return _name_map; } + + /// Access the illustrator style + styling::style& style() { return _style; } + + /// Toggle info boxes + void show_info(bool toggle = true) { _show_info = toggle; } + /// Toggle portal surfaces + void hide_portals(bool toggle = true) { _hide_portals = toggle; } + /// Toggle passive surfaces + void hide_passives(bool toggle = true) { _hide_passives = toggle; } + /// @brief Converts a detray surface in the detector to an svg. /// @param identification the id of the svg object. /// @param index the index of the surface in the detector. @@ -71,12 +87,15 @@ class illustrator { const std::string& identification, const std::size_t index, const view_t& view, const typename detector_t::geometry_context& gctx = {}) const { + const auto surface = detray::surface{ _detector, _detector.surface_lookup()[static_cast(index)]}; + auto ret = svgtools::utils::group(identification); actsvg::svg::object svg_sur; std::array color; + if (surface.is_portal()) { if (!_hide_portals) { auto p_portal = svgtools::conversion::portal( @@ -96,11 +115,14 @@ class illustrator { } else if (!(surface.is_passive() && _hide_passives)) { auto p_surface = svgtools::conversion::surface(gctx, surface); + svgtools::styling::apply_style(p_surface, _style._volume_style._surface_style, _style._do_random_coloring); + std::copy(p_surface._fill._fc._rgb.begin(), p_surface._fill._fc._rgb.end(), color.begin()); + svg_sur = actsvg::display::surface(identification, p_surface, view); } if (_show_info && @@ -110,10 +132,12 @@ class illustrator { surface); std::copy(color.begin(), color.end(), p_information_section._color.begin()); + ret.add_object(svgtools::meta::display::information_section( identification + "_information_section", p_information_section, view, _info_screen_offset, svg_sur)); } + ret.add_object(svg_sur); return ret; } @@ -150,8 +174,10 @@ class illustrator { const std::string& identification, const std::size_t index, const view_t& view, const typename detector_t::geometry_context& gctx = {}) const { + const auto surface_indices = svgtools::utils::surface_indices(_detector, index); + return draw_surfaces(identification, surface_indices, view, gctx); } @@ -162,11 +188,12 @@ class illustrator { /// convert. /// @param view the display view. /// @returns @c actsvg::svg::object of the detector's volumes. - template + template inline auto draw_volumes( - const std::string& identification, const iterator_t& indices, + const std::string& identification, const range_t& indices, const view_t& view, const typename detector_t::geometry_context& gctx = {}) const { + auto ret = svgtools::utils::group(identification); for (const auto index : indices) { const auto svg = @@ -174,6 +201,7 @@ class illustrator { index, view, gctx); ret.add_object(svg); } + return ret; } @@ -199,7 +227,8 @@ class illustrator { inline auto draw_landmark(const std::string& identification, const point_t& point, const view_t& view) const { auto p_landmark = svgtools::conversion::landmark(point); - svgtools::styling::apply_style(p_landmark, _style._landmark_style); + svgtools::styling::apply_style(p_landmark, _style._landmark_style, + _style._do_random_coloring); return svgtools::meta::display::landmark(identification, p_landmark, view); } @@ -209,21 +238,46 @@ class illustrator { /// @param intersection_record the intersection record. /// @param view the display view. /// @return @c actsvg::svg::object of the intersectio record. - template + template inline auto draw_intersections( const std::string& identification, - const std::vector< - std::pair>>& + const std::vector>& intersection_record, - const view_t& view, + const typename detector_t::vector3 dir, const view_t& view, + const typename detector_t::geometry_context& gctx = {}) const { + + dvector intersections{}; + intersections.reserve(intersection_record.size()); + + for (auto ir : intersection_record) { + intersections.push_back(ir.second); + } + + return draw_intersections(identification, intersections, dir, view, + gctx); + } + + /// @brief Converts a collection of intersections to an svg. + /// @param identification the id of the svg object. + /// @param context the geometry context. + /// @param intersections the intersection collection. + /// @param view the display view. + /// @return @c actsvg::svg::object of the intersectio record. + template + inline auto draw_intersections( + const std::string& identification, + const dvector& intersections, + const typename detector_t::vector3 dir, const view_t& view, const typename detector_t::geometry_context& gctx = {}) const { - auto p_ir = svgtools::conversion::intersection_record( - gctx, _detector, intersection_record); - svgtools::styling::apply_style(p_ir, _style._intersection_style); - return svgtools::meta::display::intersection_record(identification, - p_ir, view); + + auto p_ir = svgtools::conversion::intersection( + _detector, intersections, dir, gctx); + + svgtools::styling::apply_style(p_ir, _style._intersection_style, + _style._do_random_coloring); + + return svgtools::meta::display::intersection(identification, p_ir, + view); } /// @brief Converts a trajectory to an svg. @@ -235,10 +289,14 @@ class illustrator { typename transform3_t> inline auto draw_trajectory(const std::string& identification, const trajectory_t& trajectory, + const typename transform3_t::scalar_type path, const view_t& view) const { auto p_trajectory = - svgtools::conversion::trajectory(trajectory); - svgtools::styling::apply_style(p_trajectory, _style._trajectory_style); + svgtools::conversion::trajectory(trajectory, path); + + svgtools::styling::apply_style(p_trajectory, _style._trajectory_style, + _style._do_random_coloring); + return svgtools::meta::display::trajectory(identification, p_trajectory, view); } @@ -253,7 +311,8 @@ class illustrator { const point3_container& points, const view_t& view) const { auto p_trajectory = svgtools::conversion::trajectory(points); - svgtools::styling::apply_style(p_trajectory, _style._trajectory_style); + svgtools::styling::apply_style(p_trajectory, _style._trajectory_style, + _style._do_random_coloring); return svgtools::meta::display::trajectory(identification, p_trajectory, view); } @@ -266,28 +325,68 @@ class illustrator { /// @param view the display view. /// @return @c actsvg::svg::object of the trajectory and intersection /// record. - template class trajectory_t, - typename transform3_t> + template inline auto draw_intersections_and_trajectory( const std::string& identification, - std::vector< - std::pair>>& + const std::vector>& intersection_record, - const trajectory_t& trajectory, const view_t& view, + const trajectory_t& trajectory, const view_t& view, const typename detector_t::geometry_context& gctx = {}) const { - auto ret = svgtools::utils::group(identification); - auto i_style = svgtools::styling::copy_fill_colors( - _style._intersection_style, _style._trajectory_style); - auto p_ir = svgtools::conversion::intersection_record( - gctx, _detector, intersection_record); - svgtools::styling::apply_style(p_ir, i_style); - ret.add_object( - draw_trajectory(identification + "_trajectory", trajectory, view)); - ret.add_object(svgtools::meta::display::intersection_record( - identification + "_record", p_ir, view)); + dvector intersections{}; + intersections.reserve(intersection_record.size()); + for (auto ir : intersection_record) { + intersections.push_back(ir.second); + } + + return draw_intersections_and_trajectory(identification, intersections, + trajectory, view, gctx); + } + + /// @brief Converts a trajectory and its intersections to an svg with a + /// related coloring. + /// @param identification the id of the svg object. + /// @param trajectory the trajectory (eg. ray or helix). + /// @param intersections the intersection record. + /// @param view the display view. + /// @return @c actsvg::svg::object of the trajectory and intersections + template + inline auto draw_intersections_and_trajectory( + const std::string& identification, + const dvector& intersections, + const trajectory_t& trajectory, const view_t& view, + typename detector_t::scalar_type max_path = 500.f, + const typename detector_t::geometry_context& gctx = {}) const { + + actsvg::svg::object ret; + ret._tag = "g"; + ret._id = identification; + + if (not intersections.empty()) { + auto i_style = svgtools::styling::copy_fill_colors( + _style._intersection_style, _style._trajectory_style); + i_style._fill_colors[0]._opacity = 0.5f; + i_style._marker_type = "o"; + + auto p_ir = svgtools::conversion::intersection( + _detector, intersections, trajectory.dir(0.f), gctx); + svgtools::styling::apply_style(p_ir, i_style, + _style._do_random_coloring); + + ret.add_object(svgtools::meta::display::intersection( + identification + "_record", p_ir, view)); + + // The intersection record is always sorted by path length + // const auto max_path{intersections.back().path}; + const auto sf{ + detray::surface{_detector, intersections.back().sf_desc}}; + const auto final_pos = sf.local_to_global( + gctx, intersections.back().local, trajectory.dir(0.f)); + max_path = getter::norm(final_pos - trajectory.pos(0.f)); + } + + ret.add_object(draw_trajectory(identification + "_trajectory", + trajectory, max_path, view)); return ret; } @@ -303,25 +402,14 @@ class illustrator { return {}; } - /// Toggle info boxes - void show_info(bool toggle = true) { _show_info = toggle; } - /// Toggle portal surfaces - void hide_portals(bool toggle = true) { _hide_portals = toggle; } - /// Toggle passive surfaces - void hide_passives(bool toggle = true) { _hide_passives = toggle; } - private: - using point3 = std::array; - using point3_container = std::vector; - using geometry_context = typename detector_t::geometry_context; - const actsvg::point2 _info_screen_offset{-300, 300}; const detector_t& _detector; const typename detector_t::name_map& _name_map; - bool _show_info = true; + bool _show_info = false; bool _hide_portals = false; bool _hide_passives = false; - const styling::style _style = styling::style1; + styling::style _style = styling::style1; }; } // namespace detray::svgtools diff --git a/plugins/svgtools/include/detray/plugins/svgtools/meta/display/geometry.hpp b/plugins/svgtools/include/detray/plugins/svgtools/meta/display/geometry.hpp index c4ed6422e2..96d23a9ebd 100644 --- a/plugins/svgtools/include/detray/plugins/svgtools/meta/display/geometry.hpp +++ b/plugins/svgtools/include/detray/plugins/svgtools/meta/display/geometry.hpp @@ -31,17 +31,17 @@ inline auto landmark(const std::string& id, return actsvg::draw::marker(id, point_view, lm._marker); } -/// @brief Converts a proto intersection record to a SVG object. +/// @brief Converts a proto intersection to a SVG object. template -inline auto intersection_record( +inline auto intersection( const std::string& id, - const svgtools::meta::proto::intersection_record& ir, + const svgtools::meta::proto::intersection& intr, const view_t& view) { actsvg::svg::object ret; ret._tag = "g"; ret._id = id; - for (size_t index = 0; index < ir._landmarks.size(); index++) { - const auto lm = ir._landmarks[index]; + for (size_t index = 0; index < intr._landmarks.size(); index++) { + const auto lm = intr._landmarks[index]; const auto svg = svgtools::meta::display::landmark( id + "_intersection" + std::to_string(index), lm, view); ret.add_object(svg); diff --git a/plugins/svgtools/include/detray/plugins/svgtools/meta/proto/intersection_record.hpp b/plugins/svgtools/include/detray/plugins/svgtools/meta/proto/intersection_record.hpp index 405f955201..54ba541782 100644 --- a/plugins/svgtools/include/detray/plugins/svgtools/meta/proto/intersection_record.hpp +++ b/plugins/svgtools/include/detray/plugins/svgtools/meta/proto/intersection_record.hpp @@ -19,13 +19,14 @@ namespace detray::svgtools::meta::proto { -/// @brief A proto intersection record class as a simple translation layer from -/// a intersection record description. +/// @brief A proto intersection class as a simple translation layer from +/// an intersection description. template -class intersection_record { +class intersection { public: using landmark_type = svgtools::meta::proto::landmark; std::vector _landmarks; std::string _name; }; -} // namespace detray::svgtools::meta::proto \ No newline at end of file + +} // namespace detray::svgtools::meta::proto diff --git a/plugins/svgtools/include/detray/plugins/svgtools/styling/styling.hpp b/plugins/svgtools/include/detray/plugins/svgtools/styling/styling.hpp index fc81d0ba1d..5468c2efff 100644 --- a/plugins/svgtools/include/detray/plugins/svgtools/styling/styling.hpp +++ b/plugins/svgtools/include/detray/plugins/svgtools/styling/styling.hpp @@ -82,11 +82,11 @@ std::vector blue_theme(const actsvg::scalar opacity) { } std::vector green_theme(const actsvg::scalar opacity) { - return {{celadon, opacity}, + return {{emerald, opacity}, + {shamrock_green, opacity}, + {celadon, opacity}, {aquamarine1, opacity}, - {aquamarine2, opacity}, - {emerald, opacity}, - {shamrock_green, opacity}}; + {aquamarine2, opacity}}; } // Same color circle that is used in matplot plugin @@ -241,11 +241,14 @@ void apply_style(actsvg::proto::volume& p_volume, /// @brief Sets the style of the proto landmark. template void apply_style(meta::proto::landmark& p_landmark, - const landmark_style& styling) { - const auto fill_color = colors::pick_random(styling._fill_colors); + const landmark_style& styling, bool do_random_coloring) { + auto fill_color = do_random_coloring + ? colors::pick_random(styling._fill_colors) + : styling._fill_colors.front(); const auto fill = actsvg::style::fill(fill_color); const auto stroke = actsvg::style::stroke(fill_color, styling._stroke_width); + const auto marker = actsvg::style::marker{ styling._marker_type, styling._marker_size, fill, stroke}; p_landmark._marker = marker; @@ -253,16 +256,19 @@ void apply_style(meta::proto::landmark& p_landmark, /// @brief Sets the style of the proto intersection record. template -void apply_style( - meta::proto::intersection_record& p_intersection_record, - const landmark_style& styling) { - const auto fill_color = colors::pick_random(styling._fill_colors); +void apply_style(meta::proto::intersection& p_intersection, + const landmark_style& styling, bool do_random_coloring) { + auto fill_color = do_random_coloring + ? colors::pick_random(styling._fill_colors) + : styling._fill_colors.front(); + const auto stroke = actsvg::style::stroke(fill_color, styling._stroke_width); + fill_color._opacity *= 0.5f; const auto fill = actsvg::style::fill(fill_color); - const auto stroke = - actsvg::style::stroke(fill_color, styling._stroke_width); + const auto marker = actsvg::style::marker{ styling._marker_type, styling._marker_size, fill, stroke}; - for (auto& p_landmark : p_intersection_record._landmarks) { + + for (auto& p_landmark : p_intersection._landmarks) { p_landmark._marker = marker; } } @@ -270,8 +276,10 @@ void apply_style( /// @brief Sets the style of the proto trajectory. template void apply_style(meta::proto::trajectory& p_trajectory, - const trajectory_style& styling) { - auto fill_color = colors::pick_random(styling._fill_colors); + const trajectory_style& styling, bool do_random_coloring) { + auto fill_color = do_random_coloring + ? colors::pick_random(styling._fill_colors) + : styling._fill_colors.front(); p_trajectory._stroke = actsvg::style::stroke(fill_color, styling._stroke_width); } diff --git a/plugins/svgtools/include/detray/plugins/svgtools/utils/intersection_utils.hpp b/plugins/svgtools/include/detray/plugins/svgtools/utils/intersection_utils.hpp deleted file mode 100644 index dfbcc564e3..0000000000 --- a/plugins/svgtools/include/detray/plugins/svgtools/utils/intersection_utils.hpp +++ /dev/null @@ -1,29 +0,0 @@ -/** Detray library, part of the ACTS project (R&D line) - * - * (c) 2023 CERN for the benefit of the ACTS project - * - * Mozilla Public License Version 2.0 - */ - -#pragma once - -// Project include(s) -#include "detray/geometry/surface.hpp" -#include "detray/intersection/intersection.hpp" - -namespace detray::svgtools::utils { - -/// @returns detray point of an intersection. -template -inline auto intersection_point( - const typename detector_t::geometry_context& context, - const detector_t& detector, - const detray::intersection2D& - d_intersection) { - const typename detector_t::vector3 dir{}; - const detray::surface surface{detector, d_intersection.sf_desc}; - return surface.local_to_global(context, d_intersection.local, dir); -} - -} // namespace detray::svgtools::utils \ No newline at end of file diff --git a/tests/common/include/tests/common/tools/ray_scan_utils.hpp b/tests/common/include/tests/common/tools/ray_scan_utils.hpp index 241d78f1dc..8ac504a267 100644 --- a/tests/common/include/tests/common/tools/ray_scan_utils.hpp +++ b/tests/common/include/tests/common/tools/ray_scan_utils.hpp @@ -194,6 +194,7 @@ inline auto trace_intersections(const record_container &intersection_records, std::stringstream record_stream; /// Error messages std::stringstream err_stream; + bool error_code{true}; /// Readable access to the data of a recorded intersection struct record { @@ -222,11 +223,12 @@ inline auto trace_intersections(const record_container &intersection_records, }; /// Print errors of this function - auto print_err = [](const std::stringstream &stream) { + auto print_err = [&error_code](const std::stringstream &stream) { std::cerr << "\n<<<<<<<<<<<<<<< ERROR intersection trace\n" << std::endl; std::cerr << stream.str() << std::endl; std::cerr << "\n>>>>>>>>>>>>>>>\n" << std::endl; + error_code = false; }; // No intersections found by ray @@ -234,7 +236,7 @@ inline auto trace_intersections(const record_container &intersection_records, err_stream << "No surfaces found!"; print_err(err_stream); - return std::make_pair(portal_trace, module_trace); + return std::make_tuple(portal_trace, module_trace, error_code); } // If there is only one surface in the trace, it must be a portal @@ -253,11 +255,12 @@ inline auto trace_intersections(const record_container &intersection_records, print_err(err_stream); - return std::make_pair(portal_trace, module_trace); + return std::make_tuple(portal_trace, module_trace, error_code); } } // Go through recorded intersection (two at a time) + dindex current_vol = start_volume; for (std::size_t rec = 0u; rec < (intersection_records.size() - 1u);) { const record current_rec = record{intersection_records.at(rec)}; @@ -274,24 +277,34 @@ inline auto trace_intersections(const record_container &intersection_records, // Check that the surface was found in the volume it claims to // belong to const bool is_in_volume = - current_rec.volume_idx() == current_rec.surface_volume_idx(); + (current_rec.volume_idx() == + current_rec.surface_volume_idx()) and + (current_rec.surface_volume_idx() == current_vol); if (is_in_volume) { module_trace.emplace_back(current_rec.surface_idx(), current_rec.volume_idx()); } else { - err_stream << "\n(!!) Surface outside of its volume (Found: " - << current_rec.volume_idx() << ", belongs in: " - << current_rec.surface_volume_idx() << ")"; + err_stream << "\n(!!) Surface " << current_rec.surface_idx() + << " outside of its volume (Found in: " + << current_vol << ", belongs in: " + << current_rec.surface_volume_idx() << ")\n"; err_stream << record_stream.str(); print_err(err_stream); - return std::make_pair(portal_trace, module_trace); + return std::make_tuple(portal_trace, module_trace, error_code); } ++rec; continue; } + // If the record is a portal, the current volume switches + // the portals in the pair may be unordered, so check both + else if (current_vol == current_rec.volume_idx()) { + current_vol = current_rec.volume_link(); + } else if (current_vol == next_rec.volume_idx()) { + current_vol = next_rec.volume_link(); + } record_stream << next_rec.volume_idx() << "\t" << next_rec.inters() << std::endl; @@ -370,7 +383,7 @@ inline auto trace_intersections(const record_container &intersection_records, print_err(err_stream); - return std::make_pair(portal_trace, module_trace); + return std::make_tuple(portal_trace, module_trace, error_code); } // Advance to inspect next pair @@ -388,7 +401,7 @@ inline auto trace_intersections(const record_container &intersection_records, portal_trace.emplace_back(lower, upper); } - return std::make_pair(portal_trace, module_trace); + return std::make_tuple(portal_trace, module_trace, error_code); } /// Build an adjacency list from intersection traces. diff --git a/tests/unit_tests/svgtools/intersections.cpp b/tests/unit_tests/svgtools/intersections.cpp index 065b4458af..ca6b7ff53d 100644 --- a/tests/unit_tests/svgtools/intersections.cpp +++ b/tests/unit_tests/svgtools/intersections.cpp @@ -72,8 +72,8 @@ int main(int, char**) { "test_svgtools_intersection_record" + std::to_string(index); // Drawing the intersections. - const auto svg_ir = - il.draw_intersections(name, intersection_record, view); + const auto svg_ir = il.draw_intersections(name, intersection_record, + test_ray.dir(), view); detray::svgtools::write_svg(name, {axes, svg_det, svg_ir}); diff --git a/tests/unit_tests/svgtools/trajectories.cpp b/tests/unit_tests/svgtools/trajectories.cpp index 3c74155f6e..7365e878eb 100644 --- a/tests/unit_tests/svgtools/trajectories.cpp +++ b/tests/unit_tests/svgtools/trajectories.cpp @@ -64,10 +64,11 @@ int main(int, char**) { const auto ray_ir = detray::particle_gun::shoot_particle(det, ray); // Draw the trajectory. - const auto svg_ray = il.draw_trajectory("trajectory", ray, view); + const auto svg_ray = il.draw_trajectory("trajectory", ray, 500.f, view); // Draw the intersections. - const auto svg_ray_ir = il.draw_intersections("record", ray_ir, view); + const auto svg_ray_ir = + il.draw_intersections("record", ray_ir, ray.dir(), view); detray::svgtools::write_svg("test_svgtools_ray", {svg_volumes, svg_ray, svg_ray_ir}); @@ -83,10 +84,11 @@ int main(int, char**) { const auto helix_ir = detray::particle_gun::shoot_particle(det, helix); // Draw the trajectory. - const auto svg_helix = il.draw_trajectory("trajectory", helix, view); + const auto svg_helix = il.draw_trajectory("trajectory", helix, 500.f, view); // Draw the intersections. - const auto svg_helix_ir = il.draw_intersections("record", helix_ir, view); + const auto svg_helix_ir = + il.draw_intersections("record", helix_ir, helix.dir(), view); detray::svgtools::write_svg("test_svgtools_helix", {svg_volumes, svg_helix, svg_helix_ir}); diff --git a/tests/unit_tests/svgtools/web.cpp b/tests/unit_tests/svgtools/web.cpp index 5ea01fdfed..c10ad119b3 100644 --- a/tests/unit_tests/svgtools/web.cpp +++ b/tests/unit_tests/svgtools/web.cpp @@ -96,8 +96,8 @@ int main(int, char**) { il.draw_trajectory(name + "_trajectory", helix, view); // Draw the intersection record. - const auto svg_helix_ir = - il.draw_intersections(name + "_record", helix_ir, view); + const auto svg_helix_ir = il.draw_intersections( + name + "_record", helix_ir, helix.dir(), view); // We one the trajectory and intersection record to be considered as one // svg. Thus we group them together before adding the group to the svg diff --git a/tests/validation/CMakeLists.txt b/tests/validation/CMakeLists.txt index 1676a980c1..f459aab445 100644 --- a/tests/validation/CMakeLists.txt +++ b/tests/validation/CMakeLists.txt @@ -17,6 +17,6 @@ target_include_directories( detray_validation target_link_libraries( detray_validation INTERFACE GTest::gtest GTest::gtest_main Boost::program_options vecmem::core covfie::core detray::test detray_tests_common - detray::io detray::utils detray::core_array ) + detray::io detray::utils detray::core_array detray::svgtools ) add_subdirectory( src ) diff --git a/tests/validation/include/detray/validation/detail/svg_display.hpp b/tests/validation/include/detray/validation/detail/svg_display.hpp new file mode 100644 index 0000000000..813c7c213a --- /dev/null +++ b/tests/validation/include/detray/validation/detail/svg_display.hpp @@ -0,0 +1,147 @@ +/** Detray library, part of the ACTS project (R&D line) + * + * (c) 2023 CERN for the benefit of the ACTS project + * + * Mozilla Public License Version 2.0 + */ + +#pragma once + +// Project include(s) +#include "detray/plugins/svgtools/illustrator.hpp" +#include "detray/plugins/svgtools/styling/styling.hpp" +#include "detray/plugins/svgtools/writer.hpp" + +// System include(s) +#include +#include +#include +#include +#include + +namespace detray::detail { + +/// Find the unique volume indices that the trajectory crossed +/// - intersection record +template +std::unordered_set get_volume_indices( + const std::vector< + std::pair>> + &intersection_record) { + + std::unordered_set volumes{}; + volumes.reserve(intersection_record.size()); + for (const auto &single_ir : intersection_record) { + volumes.insert(single_ir.first); + } + + return volumes; +} + +/// Find the unique volume indices that the trajectory crossed +/// - intersection collection +template +std::unordered_set get_volume_indices( + const dvector> + &intersections) { + + std::unordered_set volumes{}; + volumes.reserve(intersections.size()); + for (const auto &intr : intersections) { + volumes.insert(intr.sf_desc.volume()); + } + + return volumes; +} + +/// Find the unique volume indices that the trajectory crossed +/// - intersection record +template +std::unordered_set get_volume_indices(const collection_t &) { + return {}; +} + +/// @returns the svg of the intersections (truth and track) and the trajectory +template +auto draw_intersection_and_traj_svg( + const typename detector_t::geometry_context gctx, + detray::svgtools::illustrator &il, + const std::vector> &intersections_truth, + const traj_t &traj, const std::string &traj_name, + const dvector &intersections, const view_t &view) { + auto svg_traj = il.draw_intersections( + "truth_intersections", intersections_truth, traj.dir(), view, gctx); + if (not intersections.empty()) { + svg_traj.add_object(il.draw_intersections_and_trajectory( + traj_name, intersections, traj, view, + intersections_truth.back().second.path, gctx)); + } else { + svg_traj.add_object(il.draw_trajectory( + traj_name, traj, intersections_truth.back().second.path, view)); + } + + return svg_traj; +} + +/// Display the geometry, intersection and track data via @c svgtools +template +inline void svg_display( + const typename detector_t::geometry_context gctx, + detray::svgtools::illustrator &il, + const std::vector> &intersections_truth, + const traj_t &traj, const std::string &traj_name, + const std::string &outfile = "detector_display", + const dvector &intersections = {}, + const std::string &outdir = "./plots/") { + + // Gather all volumes that need to be displayed + auto volumes = get_volume_indices(intersections_truth); + if (not intersections.empty()) { + const auto more_volumes = get_volume_indices(intersections_truth); + volumes.insert(more_volumes.begin(), more_volumes.end()); + } + + // General options + auto path = std::filesystem::path(outdir); + if (not std::filesystem::exists(path)) { + std::error_code err; + if (!std::filesystem::create_directories(path, err)) { + throw std::runtime_error(err.message()); + } + } + + actsvg::style::stroke stroke_black = actsvg::style::stroke(); + + // x-y axis. + auto xy_axis = actsvg::draw::x_y_axes("axes", {-250, 250}, {-250, 250}, + stroke_black, "x", "y"); + // z-r axis. + auto zr_axis = actsvg::draw::x_y_axes("axes", {-1500, 1500}, {-5, 250}, + stroke_black, "z", "r"); + // Creating the views. + const actsvg::views::x_y xy; + const actsvg::views::z_r zr; + + // xy - view + auto svg_traj = draw_intersection_and_traj_svg( + gctx, il, intersections_truth, traj, traj_name, intersections, xy); + + std::string name_xy = outfile + "_xy"; + const auto vol_xy_svg = il.draw_volumes(name_xy, volumes, xy, gctx); + detray::svgtools::write_svg(path / name_xy, + {xy_axis, vol_xy_svg, svg_traj}); + + // zr - view + svg_traj = draw_intersection_and_traj_svg( + gctx, il, intersections_truth, traj, traj_name, intersections, zr); + + std::string name_zr = outfile + "_zr"; + const auto vol_zr_svg = il.draw_detector(name_zr, zr, gctx); + detray::svgtools::write_svg(path / name_zr, + {zr_axis, vol_zr_svg, svg_traj}); + + std::cout << "INFO: Wrote debugging data in: " << path << "\n" << std::endl; +} + +} // namespace detray::detail diff --git a/tests/validation/include/detray/validation/detector_helix_scan.hpp b/tests/validation/include/detray/validation/detector_helix_scan.hpp index 661f3f99a1..a9d4acf319 100644 --- a/tests/validation/include/detray/validation/detector_helix_scan.hpp +++ b/tests/validation/include/detray/validation/detector_helix_scan.hpp @@ -11,8 +11,10 @@ #include "detray/geometry/surface.hpp" #include "detray/intersection/detail/trajectories.hpp" #include "detray/io/common/detail/file_handle.hpp" +#include "detray/plugins/svgtools/illustrator.hpp" #include "detray/simulation/event_generator/track_generators.hpp" #include "detray/test/types.hpp" +#include "detray/validation/detail/svg_display.hpp" #include "tests/common/test_base/fixture_base.hpp" #include "tests/common/tools/particle_gun.hpp" #include "tests/common/tools/ray_scan_utils.hpp" @@ -42,6 +44,9 @@ class helix_scan : public test::fixture_base<> { std::string m_name{"helix_scan"}; bool m_write_inters{false}; trk_gen_config_t m_trk_gen_cfg{}; + // Visualization style to be applied to the svgs + detray::svgtools::styling::style m_style = + detray::svgtools::styling::tableau_colorblind; /// Getters /// @{ @@ -51,6 +56,7 @@ class helix_scan : public test::fixture_base<> { const trk_gen_config_t &track_generator() const { return m_trk_gen_cfg; } + const auto &svg_style() const { return m_style; } /// @} /// Setters @@ -142,15 +148,31 @@ class helix_scan : public test::fixture_base<> { // Create a trace of the volume indices that were encountered // and check that portal intersections are connected - auto [portal_trace, surface_trace] = + auto [portal_trace, surface_trace, err_code] = trace_intersections(intersection_record, start_index); // Is the succession of volumes consistent ? - ASSERT_TRUE(check_connectivity(portal_trace)) - << "\nFailed on helix " << n_tracks << "/" - << trk_state_generator.size() << "\n" - << helix; + err_code &= check_connectivity(portal_trace); + + // Display the detector, track and intersections for debugging + if (not err_code) { + + // Creating the svg generator for the detector. + detray::svgtools::illustrator il{m_det, m_names, + m_cfg.svg_style()}; + il.show_info(true); + il.hide_portals(false); + il.hide_passives(false); + + detail::svg_display(gctx, il, intersection_record, helix, + "helix", m_cfg.name()); + } + + // Is the succession of volumes consistent ? + ASSERT_TRUE(err_code) << "\nFailed on helix " << n_tracks << "/" + << trk_state_generator.size() << "\n" + << helix; ++n_tracks; } diff --git a/tests/validation/include/detray/validation/detector_ray_scan.hpp b/tests/validation/include/detray/validation/detector_ray_scan.hpp index 2cdccaace5..d70f9d68a8 100644 --- a/tests/validation/include/detray/validation/detector_ray_scan.hpp +++ b/tests/validation/include/detray/validation/detector_ray_scan.hpp @@ -12,8 +12,10 @@ #include "detray/geometry/volume_graph.hpp" #include "detray/intersection/detail/trajectories.hpp" #include "detray/io/common/detail/file_handle.hpp" +#include "detray/plugins/svgtools/illustrator.hpp" #include "detray/simulation/event_generator/track_generators.hpp" #include "detray/test/types.hpp" +#include "detray/validation/detail/svg_display.hpp" #include "tests/common/test_base/fixture_base.hpp" #include "tests/common/tools/hash_tree.hpp" #include "tests/common/tools/particle_gun.hpp" @@ -73,8 +75,13 @@ class ray_scan : public test::fixture_base<> { typename uniform_track_generator::configuration; std::string m_name{"ray_scan"}; + // Write intersection points for plotting bool m_write_inters{false}; + // Configuration of the ray generator trk_gen_config_t m_trk_gen_cfg{}; + // Visualization style to be applied to the svgs + detray::svgtools::styling::style m_style = + detray::svgtools::styling::tableau_colorblind; /// Getters /// @{ @@ -84,6 +91,7 @@ class ray_scan : public test::fixture_base<> { const trk_gen_config_t &track_generator() const { return m_trk_gen_cfg; } + const auto &svg_style() const { return m_style; } /// @} /// Setters @@ -168,15 +176,30 @@ class ray_scan : public test::fixture_base<> { // Create a trace of the volume indices that were encountered // and check that portal intersections are connected - auto [portal_trace, surface_trace] = + auto [portal_trace, surface_trace, err_code] = trace_intersections(intersection_record, start_index); // Is the succession of volumes consistent ? - ASSERT_TRUE(check_connectivity(portal_trace)) - << "\nFailed on ray " << n_tracks << "/" << ray_generator.size() - << "\n" - << ray; + err_code &= check_connectivity(portal_trace); + + // Display the detector, track and intersections for debugging + if (not err_code) { + + // Creating the svg generator for the detector. + detray::svgtools::illustrator il{m_det, m_names, + m_cfg.svg_style()}; + il.show_info(true); + il.hide_portals(false); + il.hide_passives(false); + + detail::svg_display(gctx, il, intersection_record, ray, "ray", + m_cfg.name()); + } + + ASSERT_TRUE(err_code) << "\nFailed on ray " << n_tracks << "/" + << ray_generator.size() << "\n" + << ray; // Build an adjacency matrix from this trace that can be checked // against the geometry hash (see 'track_geometry_changes') diff --git a/tests/validation/include/detray/validation/helix_navigation.hpp b/tests/validation/include/detray/validation/helix_navigation.hpp index a1c98d9c43..42d7ef909e 100644 --- a/tests/validation/include/detray/validation/helix_navigation.hpp +++ b/tests/validation/include/detray/validation/helix_navigation.hpp @@ -10,6 +10,7 @@ // Project include(s) #include "detray/detectors/bfield.hpp" #include "detray/intersection/detail/trajectories.hpp" +#include "detray/plugins/svgtools/illustrator.hpp" #include "detray/propagator/actor_chain.hpp" #include "detray/propagator/actors/aborters.hpp" #include "detray/propagator/navigator.hpp" @@ -19,6 +20,7 @@ #include "detray/tracks/tracks.hpp" #include "detray/utils/inspectors.hpp" #include "detray/validation/detail/navigation_check_helper.hpp" +#include "detray/validation/detail/svg_display.hpp" #include "tests/common/test_base/fixture_base.hpp" #include "tests/common/tools/particle_gun.hpp" @@ -47,6 +49,9 @@ class helix_navigation : public test::fixture_base<> { std::string m_name{"helix_navigation"}; trk_gen_config_t m_trk_gen_cfg{}; + // Visualization style to be applied to the svgs + detray::svgtools::styling::style m_style = + detray::svgtools::styling::tableau_colorblind; /// Getters /// @{ @@ -55,6 +60,7 @@ class helix_navigation : public test::fixture_base<> { const trk_gen_config_t &track_generator() const { return m_trk_gen_cfg; } + const auto &svg_style() const { return m_style; } /// @} /// Setters @@ -106,6 +112,9 @@ class helix_navigation : public test::fixture_base<> { using actor_chain_t = actor_chain; using propagator_t = propagator; + // Use default context + typename detector_t::geometry_context gctx{}; + // Propagator propagator_t prop(stepper_t{}, navigator_t{}); @@ -152,16 +161,31 @@ class helix_navigation : public test::fixture_base<> { auto &debug_printer = inspector.template get(); // Run the propagation - ASSERT_TRUE(prop.propagate(propagation, actor_states)) + bool success = prop.propagate(propagation, actor_states); + + if (success) { + success &= detail::compare_traces(intersection_trace, + obj_tracer, helix, n_tracks, + trk_state_generator.size()); + } + if (not success) { + // Creating the svg generator for the detector. + detray::svgtools::illustrator il{m_det, m_names, + m_cfg.svg_style()}; + il.show_info(true); + il.hide_portals(false); + il.hide_passives(false); + + detail::svg_display(gctx, il, intersection_trace, helix, + "helix", m_cfg.name(), + obj_tracer.object_trace); + } + + ASSERT_TRUE(success) << "\nFailed on helix " << n_tracks << "/" << trk_state_generator.size() << ": " << helix << "\n\n" << debug_printer.to_string(); - ASSERT_TRUE(detail::compare_traces(intersection_trace, obj_tracer, - helix, n_tracks, - trk_state_generator.size())) - << debug_printer.to_string(); - ++n_tracks; } } diff --git a/tests/validation/include/detray/validation/straight_line_navigation.hpp b/tests/validation/include/detray/validation/straight_line_navigation.hpp index b8ef62087a..ca6fe21fd7 100644 --- a/tests/validation/include/detray/validation/straight_line_navigation.hpp +++ b/tests/validation/include/detray/validation/straight_line_navigation.hpp @@ -9,6 +9,7 @@ // Project include(s) #include "detray/intersection/detail/trajectories.hpp" +#include "detray/plugins/svgtools/illustrator.hpp" #include "detray/propagator/actor_chain.hpp" #include "detray/propagator/actors/aborters.hpp" #include "detray/propagator/line_stepper.hpp" @@ -18,6 +19,7 @@ #include "detray/tracks/tracks.hpp" #include "detray/utils/inspectors.hpp" #include "detray/validation/detail/navigation_check_helper.hpp" +#include "detray/validation/detail/svg_display.hpp" #include "tests/common/test_base/fixture_base.hpp" #include "tests/common/tools/particle_gun.hpp" @@ -47,6 +49,9 @@ class straight_line_navigation : public test::fixture_base<> { std::string m_name{"straight_line_navigation"}; trk_gen_config_t m_trk_gen_cfg{}; + // Visualization style to be applied to the svgs + detray::svgtools::styling::style m_style = + detray::svgtools::styling::tableau_colorblind; /// Getters /// @{ @@ -55,6 +60,7 @@ class straight_line_navigation : public test::fixture_base<> { const trk_gen_config_t &track_generator() const { return m_trk_gen_cfg; } + const auto &svg_style() const { return m_style; } /// @} /// Setters @@ -106,6 +112,9 @@ class straight_line_navigation : public test::fixture_base<> { using actor_chain_t = actor_chain; using propagator_t = propagator; + // Use default context + typename detector_t::geometry_context gctx{}; + // Propagator propagator_t prop(stepper_t{}, navigator_t{}); @@ -142,17 +151,31 @@ class straight_line_navigation : public test::fixture_base<> { auto &obj_tracer = inspector.template get(); auto &debug_printer = inspector.template get(); + bool success = prop.propagate(propagation, actor_states); + + if (success) { + success &= + detail::compare_traces(intersection_trace, obj_tracer, ray, + n_tracks, ray_generator.size()); + } + if (not success) { + // Creating the svg generator for the detector. + detray::svgtools::illustrator il{m_det, m_names, + m_cfg.svg_style()}; + il.show_info(true); + il.hide_portals(false); + il.hide_passives(false); + + detail::svg_display(gctx, il, intersection_trace, ray, "ray", + m_cfg.name(), obj_tracer.object_trace); + } + // Run the propagation - ASSERT_TRUE(prop.propagate(propagation, actor_states)) + ASSERT_TRUE(success) << "\nFailed on ray " << n_tracks << "/" << ray_generator.size() << ": " << ray << "\n\n" << debug_printer.to_string(); - ASSERT_TRUE(detail::compare_traces(intersection_trace, obj_tracer, - ray, n_tracks, - ray_generator.size())) - << debug_printer.to_string(); - ++n_tracks; } } diff --git a/tutorials/src/cpu/detector/detector_ray_scan.cpp b/tutorials/src/cpu/detector/detector_ray_scan.cpp index 2249a18c16..5fb0ab5657 100644 --- a/tutorials/src/cpu/detector/detector_ray_scan.cpp +++ b/tutorials/src/cpu/detector/detector_ray_scan.cpp @@ -73,9 +73,10 @@ int main() { // Create a trace of the volume indices that were encountered // and check that portal intersections are connected - auto [portal_trace, surface_trace] = + auto [portal_trace, surface_trace, err_code] = detray::trace_intersections(intersection_record, start_index); + success &= err_code; // Is the succession of volumes consistent ? success &= detray::check_connectivity(portal_trace);