diff --git a/docs/src/tutorial_lit.jl b/docs/src/tutorial_lit.jl index 5c05953d9..85eae8908 100644 --- a/docs/src/tutorial_lit.jl +++ b/docs/src/tutorial_lit.jl @@ -7,7 +7,12 @@ using Unitful T = Float32 simulation = Simulation{T}(SSD_examples[:InvertedCoax]) -plot(simulation.detector, size = (700, 700)) +plot(simulation.detector) +# This will use the defaul plotting method (SSD_style = :wireframe). You can also use SSD_style = :samplesurface, which will show the "solid" geometry. + + +plot(simulation.detector, SSD_style = :samplesurface, alpha_factor = 1) +# alpha_factor ϵ [0,1) will increase transparency and alpa_factor > 1 will increase opacity. You can choose from the default collor palettes like using keyword 'palette = :tab10' or create your own palette like 'palette = [:yellow, :blue, :red]' # One can also have a look at how the initial conditions look like on the grid (its starts with a very coarse grid): diff --git a/src/Geometries/LinePrimitives/LinePrimitives.jl b/src/Geometries/LinePrimitives/LinePrimitives.jl index e437fb290..b8a495a9e 100644 --- a/src/Geometries/LinePrimitives/LinePrimitives.jl +++ b/src/Geometries/LinePrimitives/LinePrimitives.jl @@ -88,7 +88,7 @@ struct PartialCircle{T,N,S} <: AbstractLine{T,N,S} Rotate::AbstractMatrix{T} end -function PartialCircle(r::T, phiStart::T, phiStop::T, Translate::CartesianVector{T} = CartesianVector{T}([0,0,0]), Rotate::Rotation{3,Float32} = RotZ{T}(0)) where {T} +function PartialCircle(r::T, phiStart::T, phiStop::T, Translate::CartesianVector{T} = CartesianVector{T}([0,0,0]), Rotate::Rotation{3,T} = RotZ{T}(0)) where {T} PartialCircle{T, 3, :cartesian}(r, phiStart, phiStop, Translate, Rotate) end @@ -96,7 +96,7 @@ function PartialCircle(r::T, phiStart::T, phiStop::T, Translate::CartesianVector PartialCircle{T, 3, :cartesian}(r, phiStart, phiStop, Translate, RotZ{T}(0)) end -function PartialCircle(r::T, phiStart::T, phiStop::T, Translate::Missing, Rotate::Rotation{3,Float32}) where {T} +function PartialCircle(r::T, phiStart::T, phiStop::T, Translate::Missing, Rotate::Rotation{3,T}) where {T} PartialCircle{T, 3, :cartesian}(r, phiStart, phiStop, CartesianVector{T}([0,0,0]), Rotate) end diff --git a/src/Geometries/VolumePrimitives/HexagonalPrism.jl b/src/Geometries/VolumePrimitives/HexagonalPrism.jl index 114fc86af..9005965c7 100644 --- a/src/Geometries/VolumePrimitives/HexagonalPrism.jl +++ b/src/Geometries/VolumePrimitives/HexagonalPrism.jl @@ -63,44 +63,78 @@ function (+)(hp::HexagonalPrism{T}, translate::Union{CartesianVector{T}, Missing end end +function LineSegments(hp::HexagonalPrism{T})::Vector{AbstractLine{T,3,:cartesian}} where {T <: SSDFloat} + pts_top_outer = [] + pts_bottom_outer = [] + pts_top_inner = [] + pts_bottom_inner = [] + #find all vertices, this loop has been tested and works + for φ in deg2rad.(30:60:330) .- hp.rotZ + pt_top_outer = CartesianPoint{T}(hp.translate.x + hp.rOuter * cos(φ), hp.translate.y + hp.rOuter * sin(φ), hp.translate.z + hp.h/2) + push!(pts_top_outer, pt_top_outer) + pt_top_inner = CartesianPoint{T}(hp.translate.x + hp.rInner * cos(φ), hp.translate.y + hp.rInner * sin(φ), hp.translate.z + hp.h/2) + push!(pts_top_inner, pt_top_inner) + pt_bottom_outer = CartesianPoint{T}(hp.translate.x + hp.rOuter * cos(φ), hp.translate.y + hp.rOuter * sin(φ), hp.translate.z -hp.h/2) + push!(pts_bottom_outer, pt_bottom_outer) + pt_bottom_inner = CartesianPoint{T}(hp.translate.x + hp.rInner * cos(φ), hp.translate.y + hp.rInner * sin(φ), hp.translate.z -hp.h/2) + push!(pts_bottom_inner, pt_bottom_inner) + end + + #create Linesegments connecting the vertices + lines = LineSegment{T, 3, :cartesian}[] + N = length(pts_top_outer) + for i in 1:N + push!(lines, LineSegment(pts_top_outer[i%N+1], pts_top_outer[i])) #top outer hexagon + push!(lines, LineSegment(pts_bottom_outer[i%N+1], pts_bottom_outer[i])) #bottom outer hexagon + push!(lines, LineSegment(pts_bottom_outer[i], pts_top_outer[i])) #lines connecting outer hexagons + if hp.rInner > 0 + push!(lines, LineSegment(pts_bottom_inner[i%N+1], pts_bottom_inner[i])) #bottom inner hexagon + push!(lines, LineSegment(pts_top_inner[i%N+1], pts_top_inner[i])) #top inner hexagon + push!(lines, LineSegment(pts_bottom_inner[i], pts_top_inner[i])) #lines connecting inner hexagons + push!(lines, LineSegment(pts_top_outer[i], pts_top_inner[i])) #lines connecting hexagons + push!(lines, LineSegment(pts_bottom_outer[i], pts_bottom_inner[i])) #lines connecting hexagons + end + end + return lines +end # Also a plot recipe for this new primitive should be provided: -@recipe function f(hp::HexagonalPrism{T}) where {T <: SSDFloat} - label --> "HexagonalPrism" +@recipe function f(hp::HexagonalPrism{T}; seriescolor = :purple, SSD_style = :wireframe, world_size = missing, geometry_negative = [], alpha_factor = 1) where {T <: SSDFloat} + linewidth --> 2 @series begin - pts_top_outer = [] - pts_bottom_outer = [] - pts_top_inner = [] - pts_bottom_inner = [] - - #find all vertices, this loop has been tested and works - for φ in deg2rad.(30:60:330) .- hp.rotZ - pt_top_outer = CartesianPoint{T}(hp.translate.x + hp.rOuter * cos(φ), hp.translate.y + hp.rOuter * sin(φ), hp.translate.z + hp.h/2) - push!(pts_top_outer, pt_top_outer) - pt_top_inner = CartesianPoint{T}(hp.translate.x + hp.rInner * cos(φ), hp.translate.y + hp.rInner * sin(φ), hp.translate.z + hp.h/2) - push!(pts_top_inner, pt_top_inner) - pt_bottom_outer = CartesianPoint{T}(hp.translate.x + hp.rOuter * cos(φ), hp.translate.y + hp.rOuter * sin(φ), hp.translate.z -hp.h/2) - push!(pts_bottom_outer, pt_bottom_outer) - pt_bottom_inner = CartesianPoint{T}(hp.translate.x + hp.rInner * cos(φ), hp.translate.y + hp.rInner * sin(φ), hp.translate.z -hp.h/2) - push!(pts_bottom_inner, pt_bottom_inner) + seriescolor --> seriescolor + label --> "HexagonalPrism" + [] + end + label := "" + seriescolor := seriescolor + α = 1 + st = :path + if SSD_style == :wireframe + plotobject = LineSegments(hp) + elseif SSD_style == :samplesurface + st = :scatter + if ismissing(world_size) + max_dim = max(hp.rOuter, hp.h) + world_size = CartesianVector{T}(max_dim, max_dim, max_dim) end - - #create Linesegments connecting the vertices - lines = LineSegment{T, 3, :cartesian}[] - N = length(pts_top_outer) - for i in 1:N - push!(lines, LineSegment(pts_top_outer[i%N+1], pts_top_outer[i])) #top outer hexagon - push!(lines, LineSegment(pts_bottom_outer[i%N+1], pts_bottom_outer[i])) #bottom outer hexagon - push!(lines, LineSegment(pts_bottom_outer[i], pts_top_outer[i])) #lines connecting outer hexagons - if hp.rInner > 0 - push!(lines, LineSegment(pts_bottom_inner[i%N+1], pts_bottom_inner[i])) #bottom inner hexagon - push!(lines, LineSegment(pts_top_inner[i%N+1], pts_top_inner[i])) #top inner hexagon - push!(lines, LineSegment(pts_bottom_inner[i], pts_top_inner[i])) #lines connecting inner hexagons - push!(lines, LineSegment(pts_top_outer[i], pts_top_inner[i])) #lines connecting hexagons - push!(lines, LineSegment(pts_bottom_outer[i], pts_bottom_inner[i])) #lines connecting hexagons - end + points = 100 + if typeof(world_size) == CylindricalVector{T} + sampling_vector = Array{T}(world_size/points) + sampling_vector[2] = sampling_vector[1] + plotobject = CylindricalPoint.(sample(hp, sampling_vector)) + elseif typeof(world_size) == CartesianVector{T} + sampling_vector = Array{T}(world_size/points) + plotobject = CartesianPoint.(sample(hp, sampling_vector)) + end + for neg_geo in geometry_negative + filter!(x -> !(x in neg_geo), plotobject) end - lines + α = min(alpha_factor*max(1-length(plotobject)/3000,0.05),1) end + seriestype := st + markerstrokewidth := 0 + seriesalpha := α + plotobject end # For proper grid creation we also need the function get_important_points: diff --git a/src/Geometries/VolumePrimitives/Tube.jl b/src/Geometries/VolumePrimitives/Tube.jl index eff8f4c7e..3f690cdfb 100644 --- a/src/Geometries/VolumePrimitives/Tube.jl +++ b/src/Geometries/VolumePrimitives/Tube.jl @@ -28,10 +28,10 @@ function Tube{T}(dict::Dict{Any, Any}, inputunit_dict::Dict{String,Unitful.Units else @warn "please specify a height of the Tube 'h'" end - - φ_interval = if haskey(dict, "phi") - Interval(geom_round(T(ustrip(uconvert(u"rad", T(dict["phi"]["from"]) * inputunit_dict["angle"])))), - geom_round(T(ustrip(uconvert(u"rad", T(dict["phi"]["to"]) * inputunit_dict["angle"]))))) + + φ_interval = if haskey(dict, "phi") + Interval(geom_round(T(ustrip(uconvert(u"rad", T(dict["phi"]["from"]) * inputunit_dict["angle"])))), + geom_round(T(ustrip(uconvert(u"rad", T(dict["phi"]["to"]) * inputunit_dict["angle"]))))) else Interval(T(0), geom_round(T(2π))) end diff --git a/src/Geometries/VolumePrimitives/plot_recipes.jl b/src/Geometries/VolumePrimitives/plot_recipes.jl index 32eda9423..f9633299a 100644 --- a/src/Geometries/VolumePrimitives/plot_recipes.jl +++ b/src/Geometries/VolumePrimitives/plot_recipes.jl @@ -26,7 +26,7 @@ function LineSegments(t::Tube{T})::Vector{AbstractLine{T,3,:cartesian}} where {T return ls end -@recipe function f(t::Tube{T}; n = 30, seriescolor = :green) where {T} +@recipe function f(t::Tube{T}; n = 30, seriescolor = :green, SSD_style = :wireframe, world_size = missing, geometry_negative = [], alpha_factor = 1) where {T} linewidth --> 2 n --> n @series begin @@ -36,7 +36,34 @@ end end label := "" seriescolor := seriescolor - LineSegments(t) + α = 1 + st = :path + if SSD_style == :wireframe + plotobject = LineSegments(t) + elseif SSD_style == :samplesurface + st = :scatter + if ismissing(world_size) + r_size = max(t.r_interval.right*(1-cos(min(width(t.φ_interval), π))), width(t.r_interval)) + max_dim = max(r_size, width(t.z_interval)) + world_size = CylindricalVector{T}(max_dim, width(t.φ_interval)/2, max_dim) + end + points = 100 + if typeof(world_size) == CylindricalVector{T} + sampling_vector = Array{T}(world_size/points) + plotobject = CylindricalPoint.(sample(t, sampling_vector)) + elseif typeof(world_size) == CartesianVector{T} + sampling_vector = T.([sqrt(world_size.x^2+world_size.y^2), π, world_size.z]/points) + plotobject = CartesianPoint.(sample(t, sampling_vector)) + end + for neg_geo in geometry_negative + filter!(x -> !(x in neg_geo), plotobject) + end + α = min(alpha_factor*max(1-length(plotobject)/3000,0.05),1) + end + seriestype := st + markerstrokewidth := 0 + seriesalpha := α + plotobject end @@ -69,7 +96,7 @@ function LineSegments(c::Cone{T})::Vector{AbstractLine{T, 3, :cartesian}} where return ls end -@recipe function f(c::Cone{T}; n = 30, seriescolor = :orange) where {T} +@recipe function f(c::Cone{T}; n = 30, seriescolor = :orange, SSD_style = :wireframe, world_size = missing, geometry_negative = [], alpha_factor = 1) where {T} linewidth --> 2 n --> n @series begin @@ -79,7 +106,35 @@ end end seriescolor := seriescolor label := "" - LineSegments(c) + α = 1 + st = :path + if SSD_style == :wireframe + plotobject = LineSegments(c) + elseif SSD_style == :samplesurface + st = :scatter + if ismissing(world_size) + r_max = max(c.rStop1, c.rStop2) + r_size = max(r_max*(1-cos(min(width(t.φ_interval), π))), abs(r_max - min(c.rStart1, c.rStart2))) + max_dim = max(r_size, abs(c.zStop-c.zStart)) + world_size = CylindricalVector{T}(max_dim, width(t.φ_interval)/2, max_dim) + end + points = 100 + if typeof(world_size) == CylindricalVector{T} + sampling_vector = Array{T}(world_size/points) + plotobject = CylindricalPoint.(sample(c, sampling_vector)) + elseif typeof(world_size) == CartesianVector{T} + sampling_vector = T.([sqrt(world_size.x^2+world_size.y^2), π, world_size.z]/points) + plotobject = CartesianPoint.(sample(c, sampling_vector)) + end + for neg_geo in geometry_negative + filter!(x -> !(x in neg_geo), plotobject) + end + α = min(alpha_factor*max(1-length(plotobject)/3000,0.05),1) + end + seriestype := st + markerstrokewidth := 0 + seriesalpha := α + plotobject end function LineSegments(t::Torus{T})::Vector{AbstractLine{T,3,:cartesian}} where {T <: SSDFloat} @@ -108,7 +163,7 @@ function LineSegments(t::Torus{T})::Vector{AbstractLine{T,3,:cartesian}} where { return ls end -@recipe function f(t::Torus{T}; n = 30, seriescolor = :green) where {T} +@recipe function f(t::Torus{T}; n = 30, seriescolor = :red, SSD_style = :wireframe, world_size = missing, geometry_negative = [], alpha_factor = 1) where {T} linewidth --> 2 n --> n @series begin @@ -118,5 +173,34 @@ end end label := "" seriescolor := seriescolor - LineSegments(t) + α = 1 + st = :path + if SSD_style == :wireframe + plotobject = LineSegments(t) + elseif SSD_style == :samplesurface + st = :scatter + own_world = false + if ismissing(world_size) + own_world = true + r_size = max((t.r_torus+t.r_tube_interval.right)*(1-cos(min(width(t.φ_interval), π))), t.r_tube_interval.right*(1-cos(min(width(t.θ_interval), π))), width(t.r_tube_interval)) + world_size = CylindricalVector{T}(r_size, width(t.φ_interval)/2, width(t.θ_interval)/2) + end + points = 100 + if typeof(world_size) == CylindricalVector{T} + sampling_vector = Array{T}(world_size/points) + own_world ? nothing : sampling_vector[3] = π/points + plotobject = CylindricalPoint.(sample(t, sampling_vector)) + elseif typeof(world_size) == CartesianVector{T} + sampling_vector = T.([sqrt(world_size.x^2+world_size.y^2), π, π]/points) + plotobject = CartesianPoint.(sample(t, sampling_vector)) + end + for neg_geo in geometry_negative + filter!(x -> !(x in neg_geo), plotobject) + end + α = min(alpha_factor*max(1-length(plotobject)/3000,0.05),1) + end + seriestype := st + markerstrokewidth := 0 + seriesalpha := α + plotobject end diff --git a/src/SolidStateDetector/plot_recipes.jl b/src/SolidStateDetector/plot_recipes.jl index 25634485e..172c36151 100644 --- a/src/SolidStateDetector/plot_recipes.jl +++ b/src/SolidStateDetector/plot_recipes.jl @@ -1,10 +1,25 @@ -@recipe function f(det::SolidStateDetector{T}; n = 30, φ = missing, seriescolor = missing, label = missing) where {T} - +@recipe function f(det::SolidStateDetector{T}; SSD_style = :wireframe, n = 30, φ = missing, seriescolor = missing, label = missing, alpha_factor = 1) where {T} + if !(SSD_style in [:wireframe, :samplesurface]) + @warn "Chose SSD_style from [:wireframe, :samplesurface]. Defaulting to :wireframe" + SSD_style = :wireframe + end clabel = (ismissing(label) ? map(c -> (c.name == "" ? c.id : c.name), det.contacts) : label) if !(typeof(clabel) <: AbstractArray) clabel = [clabel] end ccolor = (ismissing(seriescolor) ? map(c -> c.id, det.contacts) : seriescolor) if !(typeof(ccolor) <: AbstractArray) ccolor = [ccolor] end - + world_size = missing + if SSD_style == :samplesurface + grid = Grid(det) + coord_sys = get_coordinate_system(grid) + coord_sys = get_coordinate_system(det) + if coord_sys == :cylindrical + world_size = CylindricalVector{T}(width(grid.r.interval), π, width(grid.z.interval)) + elseif coord_sys == :cartesian + world_size = CartesianVector{T}(width(grid.x.interval), width(grid.y.interval), width(grid.z.interval)) + else + @error "Could not determine the world size, try SSD_style = :wireframe" + end + end if ismissing(φ) xguide --> "x / m" yguide --> "y / m" @@ -16,6 +31,9 @@ @series begin label := "" n --> n + SSD_style --> SSD_style + world_size --> world_size + alpha_factor --> alpha_factor contact end @series begin @@ -30,7 +48,7 @@ end -@recipe function f(contact::Contact{T}; n = 30, seriescolor = missing) where {T} +@recipe function f(contact::Contact{T}; SSD_style = :wireframe, n = 30, seriescolor = missing, world_size = missing, alpha_factor = 1) where {T} ccolor = (ismissing(seriescolor) ? contact.id : seriescolor) @series begin seriescolor := ccolor @@ -42,6 +60,10 @@ end seriescolor := ccolor label := "" n --> n + SSD_style --> SSD_style + world_size --> world_size + geometry_negative --> contact.geometry_negative + alpha_factor --> alpha_factor c end end @@ -249,4 +271,4 @@ end end end -=# \ No newline at end of file +=#