Skip to content

Commit

Permalink
feat: added new SVG functionality (#3649)
Browse files Browse the repository at this point in the history
This PR uses some new functionality of the updated SVG version, particularly the clipping.

It also fixes the Gen2 detector creation scripts that have diverged.


<img width="1267" alt="Screenshot 2024-09-25 at 11 55 01" src="https://github.com/user-attachments/assets/539075a2-4214-4b53-a66e-a56d2b3cde6f">
  • Loading branch information
asalzburger authored Sep 26, 2024
1 parent 4a62f68 commit 782c176
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 149 deletions.
68 changes: 57 additions & 11 deletions Examples/Python/src/Svg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ using ViewAndRange =
/// @param portalCache is a portal cache to avoid multiple drawings of the same portal
///
/// Returns an svg object in the right view
actsvg::svg::object viewDetectorVolume(const Svg::ProtoVolume& pVolume,
actsvg::svg::object drawDetectorVolume(const Svg::ProtoVolume& pVolume,
const std::string& identification,
const ViewAndRange& viewAndRange,
PortalCache& portalCache) {
Expand Down Expand Up @@ -154,13 +154,13 @@ actsvg::svg::object viewDetectorVolume(const Svg::ProtoVolume& pVolume,
}

// Helper function to be picked in different access patterns
void viewDetector(
std::vector<actsvg::svg::object> drawDetector(
const Acts::GeometryContext& gctx,
const Acts::Experimental::Detector& detector,
const std::string& identification,
const std::vector<std::tuple<int, Svg::DetectorVolumeConverter::Options>>&
volumeIdxOpts,
const std::vector<ViewAndRange>& viewAndRanges, const std::string& saveAs) {
const std::vector<ViewAndRange>& viewAndRanges) {
PortalCache portalCache;

// The svg object to be returned
Expand All @@ -182,17 +182,13 @@ void viewDetector(
for (auto [iv, var] : Acts::enumerate(viewAndRanges)) {
auto [view, selection, range] = var;
// Get the view and the range
auto svgVolView = viewDetectorVolume(
auto svgVolView = drawDetectorVolume(
pVolume, identification + "_vol" + std::to_string(vidx) + "_" + view,
var, portalCache);
svgDetViews[iv].add_object(svgVolView);
}
}

for (auto [iv, var] : Acts::enumerate(viewAndRanges)) {
auto [view, selection, range] = var;
Svg::toFile({svgDetViews[iv]}, saveAs + "_" + view + ".svg");
}
return svgDetViews;
}

} // namespace
Expand All @@ -206,6 +202,20 @@ void addSvg(Context& ctx) {
// Some basics
py::class_<actsvg::svg::object>(svg, "object");

py::class_<actsvg::svg::file>(svg, "file")
.def(py::init<>())
.def("addObject", &actsvg::svg::file::add_object)
.def("addObjects", &actsvg::svg::file::add_objects)
.def("clip",
[](actsvg::svg::file& self, std::array<actsvg::scalar, 4> box) {
self.set_view_box(box);
})
.def("write", [](actsvg::svg::file& self, const std::string& filename) {
std::ofstream file(filename);
file << self;
file.close();
});

// Core components, added as an acts.svg submodule
{
auto c = py::class_<Svg::Style>(svg, "Style").def(py::init<>());
Expand Down Expand Up @@ -283,6 +293,42 @@ void addSvg(Context& ctx) {
});
}

// Draw primitives
{
svg.def("drawArrow", &actsvg::draw::arrow);

svg.def("drawText", &actsvg::draw::text);
}

// Draw Eta Lines
{
svg.def(
"drawEtaLines",
[](const std::string& id, actsvg ::scalar z, actsvg::scalar r,
const std::vector<actsvg::scalar>& etaMain,
actsvg::scalar strokeWidthMain, unsigned int sizeMain,
bool labelMain, const std::vector<actsvg::scalar>& etaSub,
actsvg::scalar strokeWidthSub, const std::vector<int> strokeDashSub,
unsigned int sizeSub, bool labelSub) {
// The main eta lines
actsvg::style::stroke strokeMain;
strokeMain._width = strokeWidthMain;
actsvg::style::font fontMain;
fontMain._size = sizeMain;

actsvg::style::stroke strokeSub;
strokeSub._width = strokeWidthSub;
strokeSub._dasharray = strokeDashSub;
actsvg::style::font fontSub;
fontSub._size = sizeSub;

return actsvg::display::eta_lines(
id, z, r,
{std::tie(etaMain, strokeMain, labelMain, fontMain),
std::tie(etaSub, strokeSub, labelSub, fontSub)});
});
}

// How detector volumes are drawn: Svg DetectorVolume options & drawning
{
auto c = py::class_<Svg::DetectorVolumeConverter::Options>(
Expand All @@ -304,11 +350,11 @@ void addSvg(Context& ctx) {
svg.def("convertDetectorVolume", &Svg::DetectorVolumeConverter::convert);

// Define the view functions
svg.def("viewDetectorVolume", &viewDetectorVolume);
svg.def("drawDetectorVolume", &drawDetectorVolume);
}

// How a detector is drawn: Svg Detector options & drawning
{ svg.def("viewDetector", &viewDetector); }
{ svg.def("drawDetector", &drawDetector); }

// Legacy geometry drawing
{
Expand Down
44 changes: 22 additions & 22 deletions Examples/Scripts/Python/detector_creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@

# OBJ style output
surfaces = []
viewConfig = acts.ViewConfig()
viewConfig.nSegments = 100
for vol in detector.volumePtrs():
for surf in vol.surfacePtrs():
if surf.geometryId().sensitive() > 0:
surfaces.append(surf)
acts.examples.writeSurfacesObj(
surfaces, geoContext, [0, 120, 120], "odd-surfaces.obj"
)
acts.examples.writeSurfacesObj(surfaces, geoContext, viewConfig, "odd-surfaces.obj")

# SVG style output
surfaceStyle = acts.svg.Style()
Expand All @@ -69,28 +69,28 @@
volumeOptions = acts.svg.DetectorVolumeOptions()
volumeOptions.surfaceOptions = surfaceOptions

for ivol in range(detector.numberVolumes()):
acts.svg.viewDetector(
geoContext,
detector,
"odd-xy",
[[ivol, volumeOptions]],
[["xy", ["sensitives"], viewRange]],
"vol_" + str(ivol),
)

xyRange = acts.Extent([[acts.Binning.z, [-50, 50]]])
zrRange = acts.Extent([[acts.Binning.phi, [-0.1, 0.1]]])

acts.svg.viewDetector(
# Transverse view
xyRange = acts.Extent([[acts.BinningValue.binZ, [-50, 50]]])
xyView = acts.svg.drawDetector(
geoContext,
detector,
"odd",
[[ivol, volumeOptions] for ivol in range(detector.numberVolumes())],
[["xy", ["sensitives"], xyRange], ["zr", ["materials"], zrRange]],
"detector",
[["xy", ["sensitives"], xyRange]],
)
xyFile = acts.svg.file()
xyFile.addObjects(xyView)
xyFile.write("odd_xy.svg")

acts.examples.writeDetectorToJsonDetray(geoContext, detector, "odd-detray")

# det_detector = acts.examples.DetrayConverter(geoContext, detector,"odd-detray")
# Longitudinal view
zrRange = acts.Extent([[acts.BinningValue.binPhi, [-0.1, 0.1]]])
zrView = acts.svg.drawDetector(
geoContext,
detector,
"odd",
[[ivol, volumeOptions] for ivol in range(detector.numberVolumes())],
[["zr", ["sensitives", "portals"], zrRange]],
)
zrFile = acts.svg.file()
zrFile.addObjects(zrView)
zrFile.write("odd_zr.svg")
116 changes: 0 additions & 116 deletions Examples/Scripts/Python/geomodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ def main():

p.add_argument("-i", "--input", type=str, default="", help="Input SQL file")

p.add_argument(
"-o", "--output", type=str, default="GeoModel", help="Output file(s) base name"
)

p.add_argument(
"-q",
"--queries",
Expand Down Expand Up @@ -81,34 +77,6 @@ def main():
default=False,
)

p.add_argument(
"--output-svg",
help="Write the surfaces to SVG files",
action="store_true",
default=False,
)

p.add_argument(
"--output-internals-svg",
help="Write the internal navigation to SVG files",
action="store_true",
default=False,
)

p.add_argument(
"--output-obj",
help="Write the surfaces to OBJ files",
action="store_true",
default=False,
)

p.add_argument(
"--output-json",
help="Write the surfaces to OBJ files",
action="store_true",
default=False,
)

p.add_argument(
"--enable-blueprint",
help="Enable the usage of the blueprint",
Expand Down Expand Up @@ -179,90 +147,6 @@ def main():
gmDetectorBuilder = DetectorBuilder(gmDetectorConfig, args.top_node, logLevel)
detector = gmDetectorBuilder.construct(gContext)

# Output the detector to SVG
if args.output_svg:
surfaceStyle = acts.svg.Style()
surfaceStyle.fillColor = [5, 150, 245]
surfaceStyle.fillOpacity = 0.5

surfaceOptions = acts.svg.SurfaceOptions()
surfaceOptions.style = surfaceStyle

viewRange = acts.Extent([])
volumeOptions = acts.svg.DetectorVolumeOptions()
volumeOptions.surfaceOptions = surfaceOptions

xyRange = acts.Extent([[acts.Binning.z, [-50, 50]]])
zrRange = acts.Extent([[acts.Binning.phi, [-0.8, 0.8]]])

acts.svg.viewDetector(
gContext,
detector,
args.top_node,
[[ivol, volumeOptions] for ivol in range(detector.numberVolumes())],
[
["xy", ["sensitives", "portals"], xyRange],
["zr", ["sensitives", "portals", "materials"], zrRange],
],
args.output + "_detector",
)

gmDetectorBuilder = DetectorBuilder(gmDetectorConfig, args.top_node, logLevel)
detector = gmDetectorBuilder.construct(gContext)

materialSurfaces = detector.extractMaterialSurfaces()
print("Found ", len(materialSurfaces), " material surfaces")

# Output the detector to SVG
if args.output_svg:
surfaceStyle = acts.svg.Style()
surfaceStyle.fillColor = [5, 150, 245]
surfaceStyle.fillOpacity = 0.5

surfaceOptions = acts.svg.SurfaceOptions()
surfaceOptions.style = surfaceStyle

viewRange = acts.Extent([])
volumeOptions = acts.svg.DetectorVolumeOptions()
volumeOptions.surfaceOptions = surfaceOptions

xyRange = acts.Extent([[acts.Binning.z, [-50, 50]]])
zrRange = acts.Extent([[acts.Binning.phi, [-0.8, 0.8]]])

acts.svg.viewDetector(
gContext,
detector,
args.top_node,
[[ivol, volumeOptions] for ivol in range(detector.numberVolumes())],
[
["xy", ["sensitives", "portals"], xyRange],
["zr", ["sensitives", "portals", "materials"], zrRange],
],
args.output + "_detector",
)

# Output the internal navigation to SVG
if args.output_internals_svg:
for vol in detector.volumes():
acts.svg.viewInternalNavigation(
gContext, vol, [66, 111, 245, 245, 203, 66, 0.8], "/;:"
)

# Output the surface to an OBJ file
if args.output_obj:
segments = 720
ssurfaces = [ss[1] for ss in gmFactoryCache.sensitiveSurfaces]
acts.examples.writeSurfacesObj(
ssurfaces,
gContext,
[75, 220, 100],
segments,
args.output + "_sensitives.obj",
)
# Output to a JSON file
if args.output_json:
acts.examples.writeDetectorToJsonDetray(gContext, detector, args.output)

return


Expand Down

0 comments on commit 782c176

Please sign in to comment.