Skip to content

Commit ffc9ad6

Browse files
committed
Optimization of drawing recursive geometry structures
1 parent ea9434a commit ffc9ad6

File tree

7 files changed

+94
-62
lines changed

7 files changed

+94
-62
lines changed

docs/src/examples/B2aVis.lit

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@
1818
# ## Loading the necessary Julia modules
1919
# Load the `Geant4` and `Geant4.SystemOfUnits` modules.
2020
# In order to trigger the load of the G4Vis extension we need to load the
21-
# following modules: `GLMakie`, `Rotations`, `LinearAlgebra`, `IGLWrap_jll`
21+
# following modules: `GLMakie`
2222
using Geant4
2323
using Geant4.SystemOfUnits
24-
using CairoMakie, Rotations, LinearAlgebra, IGLWrap_jll # to force loading G4Vis extension
24+
using CairoMakie # to force loading G4Vis extension
2525
#md import DisplayAs: PNG
2626
#nb import DisplayAs: PNG
2727

docs/src/examples/B3a.lit

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ beamOn(app, 10000)
222222
## ui`/tracking/verbose 0`
223223
# ## Visualize the detector geometry
224224
# Load the needed modules for visualization
225-
using CairoMakie, Rotations, LinearAlgebra, IGLWrap_jll ## to force loading G4Vis extension
225+
using CairoMakie ## to force loading G4Vis extension
226226
#md import DisplayAs: PNG
227227
#nb import DisplayAs: PNG
228228
# Display the detector geometry

docs/src/examples/HBC30.lit

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
using Geant4
2222
using Geant4.SystemOfUnits
2323
using Printf, GeometryBasics
24-
using CairoMakie, Rotations, LinearAlgebra, IGLWrap_jll # to force loading G4Vis extension
24+
using CairoMakie # to force loading G4Vis extension
2525
#md import DisplayAs: PNG #hide
2626
#nb import DisplayAs: PNG #hide
2727

docs/src/examples/TPCSim.lit

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ using Parameters
2929
using PYTHIA8
3030

3131
## to force loading G4Vis extension we need to load the following modules
32-
#md using CairoMakie, Rotations, IGLWrap_jll, LinearAlgebra
33-
#nb using CairoMakie, Rotations, IGLWrap_jll, LinearAlgebra
34-
#jl using CairoMakie, Rotations, IGLWrap_jll, LinearAlgebra
32+
#md using CairoMakie
33+
#nb using CairoMakie
34+
#jl using GLMakie
3535
using PYTHIA8: px, py, pz
3636
#md import DisplayAs: PNG
3737
#nb import DisplayAs: PNG

docs/src/examples/UserLib.lit

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
using Geant4
2121
using Geant4.SystemOfUnits
2222
using Libdl
23-
using CairoMakie, Rotations, LinearAlgebra, IGLWrap_jll # to force loading G4Vis extension
23+
using CairoMakie # to force loading G4Vis extension
2424
#md import DisplayAs: PNG #hide
2525

2626
# ## Building the custom library

docs/src/releases.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
### 0.2.4
44
- New Features:
55
- Added new G4 classes to the Wrapper: G4VTwistedFaceted, G4Hype, G4TessellatedSolid, G4TriangularFacet, G4Polyhedron, HepPolyhedron
6-
- Change the way to create the meshes of Solids for G4Vis using the GetPolyhedron() virtual function. Remove all obsolete code.
6+
- Change the way to create the meshes of Solids for G4Vis using the GetPolyhedron() virtual function. Remove all obsolete code.
7+
- When drawing recursive geometry hierarchies, merging meshes is done before drawing them.
78

89
### 0.2.3 - 29-Aug-2025
910
- New Features:

ext/G4Vis/G4Vis.jl

Lines changed: 84 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
module G4Vis
22
using Geant4
3+
using Geant4.SystemOfUnits: g, cm3, cm
34
using Makie
45
using Colors
56

@@ -14,6 +15,7 @@ module G4Vis
1415
function convert(::Type{Point3{Float64}}, v::G4ThreeVector)
1516
Point3{Float64}(x(v), y(v), z(v))
1617
end
18+
1719
function convert(::Type{RotMatrix3{Float64}}, r::CxxPtr{G4RotationMatrix})
1820
if r == C_NULL
1921
one(RotMatrix3{Float64})
@@ -22,12 +24,14 @@ module G4Vis
2224
end
2325
end
2426

27+
#---Color conversion-----------------------------------------------------------------------------
28+
const LVColor = Union{ColorTypes.Color, Tuple{ColorTypes.RGB{Float64}, Float64}}
2529
function convert(::Type{Tuple{RGB, Float64}}, c::ConstCxxRef{G4Colour})
2630
return (RGB(GetRed(c),GetGreen(c), GetBlue(c)),GetAlpha(c))
2731
end
28-
#---Create GeometryBasics Mesh from G4VSolid-------------------------------------------------------
29-
function GeometryBasics.mesh(s::G4VSolid; withnormals::Bool=false)
30-
println("Creating mesh for solid of type: ", GetEntityType(s))
32+
33+
#---Get vertices and facets from G4VSolid---------------------------------------------------------
34+
function mesh_constituents(s::G4VSolid; withnormals::Bool=false)
3135
ph = GetPolyhedron(s)
3236
nv = GetNoVertices(ph)
3337
nf = GetNoFacets(ph)
@@ -48,34 +52,28 @@ module G4Vis
4852
end
4953
end
5054
if withnormals
51-
face_normals = map(1:nf) do i
55+
normals = map(1:nf) do i
5256
n = GetNormal(ph, i)
5357
Vec3{Float64}(n[1], n[2], n[3])
5458
end
55-
return GeometryBasics.Mesh(points, faces; normal=per_face(face_normals,faces))
59+
return (points, faces, normals)
5660
else
57-
return GeometryBasics.Mesh(points, faces)
61+
return (points, faces, nothing)
5862
end
5963
end
6064

61-
#---Visualization functions---------------------------------------------------------------------
62-
colors = colormap("Grays", 8)
63-
64-
function Geant4.draw(pv::G4VPhysicalVolume; wireframe::Bool=false, maxlevel::Int64=999)
65-
lv = GetLogicalVolume(pv)
66-
fig = Figure(size=(1280, 720))
67-
s = LScene(fig[1,1])
68-
draw!(s, lv[], one(Transformation3D{Float64}), 1, wireframe, maxlevel)
69-
return fig
70-
end
71-
72-
function Geant4.draw(lv::G4LogicalVolume; wireframe::Bool=false, maxlevel::Int64=999)
73-
fig = Figure(size=(1280, 720))
74-
s = LScene(fig[1,1])
75-
draw!(s, lv, one(Transformation3D{Float64}), 1, wireframe, maxlevel)
76-
return fig
65+
#---Create GeometryBasics Mesh from G4VSolid-------------------------------------------------------
66+
function GeometryBasics.mesh(s::G4VSolid; withnormals::Bool=false)
67+
p, f, n = mesh_constituents(s; withnormals=withnormals)
68+
if withnormals
69+
return GeometryBasics.Mesh(p, f; normal=per_face(n,f))
70+
else
71+
return GeometryBasics.Mesh(p, f)
72+
end
7773
end
7874

75+
#---Visualization functions---------------------------------------------------------------------
76+
#---Solid-level drawing---------------------------------------------------------------------
7977
function Geant4.draw(solid::G4VSolid; wireframe::Bool=false, kwargs...)
8078
if wireframe
8179
m = GeometryBasics.mesh(solid)
@@ -86,46 +84,69 @@ module G4Vis
8684
end
8785
end
8886

87+
#---PhysicalVolume-level drawing--------------------------------------------------------------
88+
function Geant4.draw(pv::G4VPhysicalVolume; wireframe::Bool=false, maxlevel::Int64=999)
89+
fig = Figure()
90+
s = LScene(fig[1, 1])
91+
draw!(s, GetLogicalVolume(pv)[]; wireframe=wireframe, maxlevel=maxlevel)
92+
end
8993
function Geant4.draw!(s, pv::G4VPhysicalVolume; wireframe::Bool=false, maxlevel::Int64=999)
90-
lv = GetLogicalVolume(pv)
91-
draw!(s, lv[], one(Transformation3D{Float64}), 1, wireframe, maxlevel)
94+
draw!(s, GetLogicalVolume(pv)[]; wireframe=wireframe, maxlevel=maxlevel)
9295
end
9396

94-
using Geant4.SystemOfUnits:cm3,g
97+
#---CxxPtr overloads---------------------------------------------------------------------------
98+
function Geant4.draw(pv::CxxPtr{G4VPhysicalVolume}; wireframe::Bool=false, maxlevel::Int64=999)
99+
fig = Figure()
100+
s = LScene(fig[1, 1])
101+
draw!(s, GetLogicalVolume(pv[])[]; wireframe=wireframe, maxlevel=maxlevel)
102+
end
103+
function Geant4.draw!(s, pv::CxxPtr{G4VPhysicalVolume}; wireframe::Bool=false, maxlevel::Int64=999)
104+
draw!(s, GetLogicalVolume(pv[])[]; wireframe=wireframe, maxlevel=maxlevel)
105+
end
95106

96-
function GetReplicaParameters(pv::CxxPtr{G4VPhysicalVolume})
97-
axis = Ref{EAxis}(kYAxis)
98-
nReplicas = Ref{Int32}(0)
99-
width = Ref{Float64}(0.)
100-
offset = Ref{Float64}(0.)
101-
consuming = Ref{UInt8}(0)
102-
GetReplicationData(pv, axis, nReplicas, width, offset, consuming)
103-
return (axis[], nReplicas[], width[])
107+
#---LogicalVolume-level drawing----------------------------------------------------------------
108+
function Geant4.draw(lv::G4LogicalVolume; wireframe::Bool=false, maxlevel::Int64=999)
109+
fig = Figure()
110+
s = LScene(fig[1, 1])
111+
Geant4.draw!(s, lv; wireframe=wireframe, maxlevel=maxlevel)
112+
return fig
104113
end
105114

115+
function Geant4.draw!(s, lv::G4LogicalVolume; wireframe::Bool=false, maxlevel::Int64=999)
116+
#---Collect the meshes recursively and Vis attributes recursively--------------------------
117+
# This assumes that all Placements of a given LogicalVolume are drawn with the same visibility and color
118+
lv_meshes = Dict{G4LogicalVolume, Tuple{Vector{GeometryBasics.Mesh}, LVColor, Bool}}()
119+
collect_meshes(lv, lv_meshes, one(Transformation3D{Float64}), 1, maxlevel)
120+
#---Draw all collected meshes----------------------------------------------------------------
121+
for (clv, (meshes, color, visible)) in lv_meshes
122+
m = merge(meshes)
123+
if wireframe
124+
Makie.wireframe!(s, m; linewidth=1, visible=visible)
125+
else
126+
Makie.mesh!(s, m; color=color, transparency=true, visible=visible)
127+
end
128+
end
129+
end
130+
131+
#---Helper functions for recursive drawing------------------------------------------------------
132+
const colors = colormap("Grays", 8)
106133
const UnitOnAxis = [( 1,0,0), (0,1,0), (0,0,1)]
107134

108-
function Geant4.draw!(s, lv::G4LogicalVolume, t::Transformation3D{Float64}, level::Int64, wireframe::Bool, maxlevel::Int64)
109-
vsolid = GetSolid(lv)
110-
tsolid = GetEntityType(vsolid)
111-
shape = getproperty(Geant4,Symbol(tsolid))
112-
solid = CxxRef{shape}(vsolid)
113-
m = GeometryBasics.mesh(solid[])
135+
function collect_meshes(lv::G4LogicalVolume, lv_meshes::Dict{G4LogicalVolume, Tuple{Vector{GeometryBasics.Mesh}, LVColor, Bool}}, t::Transformation3D{Float64}, level::Int64, maxlevel::Int64)
136+
solid = GetSolid(lv)
114137
g4vis = GetVisAttributes(lv)
115-
if ! isempty(m)
116-
if ! isone(t)
117-
points = GeometryBasics.coordinates(m)
118-
faces = GeometryBasics.faces(m)
119-
map!(c -> c * t, points, points)
120-
m = GeometryBasics.mesh(points, faces, normal=GeometryBasics.normals(points, faces, Vec3f))
121-
end
138+
points, faces, normals = mesh_constituents(solid[], withnormals=true)
139+
if ! isone(t)
140+
map!(c -> c * t, points, points)
141+
map!(n -> t.rotation * n, normals, normals)
142+
end
143+
m = GeometryBasics.Mesh(points, faces, normal=per_face(normals, faces))
144+
if haskey(lv_meshes, lv)
145+
push!(lv_meshes[lv][1], m)
146+
else
122147
color = g4vis != C_NULL ? convert(Tuple{RGB, Float64}, GetColour(g4vis)) : (colors[level], GetDensity(GetMaterial(lv))/(12g/cm3))
123148
visible = g4vis != C_NULL ? IsVisible(g4vis) : true
124-
if wireframe
125-
wireframe!(s, m, linewidth=1, visible=visible)
126-
else
127-
mesh!(s, m, color=color, transparency=true, visible=visible )
128-
end
149+
lv_meshes[lv] = ([m], color, visible)
129150
end
130151
# Go down to the daughters (eventually)
131152
level >= maxlevel && return
@@ -139,18 +160,28 @@ module G4Vis
139160
for i in 1:nReplicas
140161
g4t = unitV * (-width*(nReplicas-1)*0.5 + (i-1)*width)
141162
transformation = Transformation3D{Float64}(one(RotMatrix3{Float64}), convert(Vector3{Float64}, g4t))
142-
draw!(s, volume[], transformation * t, level+1, wireframe, maxlevel)
163+
collect_meshes(volume[], lv_meshes, transformation * t, level+1, maxlevel)
143164
end
144165
else
145166
g4t = GetTranslation(daughter)
146167
g4r = GetRotation(daughter)
147168
transformation = Transformation3D{Float64}(convert(RotMatrix3{Float64}, g4r), convert(Vector3{Float64}, g4t))
148169
volume = GetLogicalVolume(daughter)
149-
draw!(s, volume[], transformation * t, level+1, wireframe, maxlevel)
170+
collect_meshes(volume[], lv_meshes, transformation * t, level+1, maxlevel)
150171
end
151172
end
152173
end
153174

175+
function GetReplicaParameters(pv::CxxPtr{G4VPhysicalVolume})
176+
axis = Ref{EAxis}(kYAxis)
177+
nReplicas = Ref{Int32}(0)
178+
width = Ref{Float64}(0.)
179+
offset = Ref{Float64}(0.)
180+
consuming = Ref{UInt8}(0)
181+
GetReplicationData(pv, axis, nReplicas, width, offset, consuming)
182+
return (axis[], nReplicas[], width[])
183+
end
184+
154185
#---Testing functions------------------------------------------------------------
155186
@inline Base.:*(a::G4ThreeVector, b::Vector3{Float64}) = G4ThreeVector(x(a)*b[1], y(a)*b[2], z(a)*b[3])
156187
function Geant4.drawDistanceToOut(solid::G4VSolid, N::Int)

0 commit comments

Comments
 (0)