Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added new SVG functionality #3649

Merged
merged 5 commits into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading