diff --git a/src/Optical/Fresnel.jl b/src/Optical/Fresnel.jl index b2da2d2b4..6a295c9fb 100644 --- a/src/Optical/Fresnel.jl +++ b/src/Optical/Fresnel.jl @@ -179,11 +179,11 @@ function processintersection(opticalinterface::FresnelInterface{T}, point::SVect # So could leave this turned on all the time with little impact on performance and get approximate scattering effects for free. r = !test * rand() # assuming (powᵣ + powₜ) <= 1 (asserted in constructor) - if r < powₜ + if interfacemode(opticalinterface) == Transmit || (interfacemode(opticalinterface) == ReflectOrTransmit && r < powₜ) # refraction raydirection = refractedray(nᵢ, nₜ, normal, direction(incidentray)) raypower = powₜ * incident_pow - elseif r < (powᵣ + powₜ) + elseif interfacemode(opticalinterface) == Reflect || (interfacemode(opticalinterface) == ReflectOrTransmit && r < (powᵣ + powₜ)) # reflection raypower = powᵣ * incident_pow raydirection = reflectedray(normal, direction(incidentray)) diff --git a/src/Optical/Lenses.jl b/src/Optical/Lenses.jl index 4b3c3f06d..ca153a19e 100644 --- a/src/Optical/Lenses.jl +++ b/src/Optical/Lenses.jl @@ -20,40 +20,40 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE -opticinterface(::Type{S}, insidematerial, outsidematerial, reflectance = zero(S)) where {S<:Real} = FresnelInterface{S}(insidematerial, outsidematerial, reflectance = reflectance, transmission = one(S) - reflectance) +opticinterface(::Type{S}, insidematerial, outsidematerial, reflectance = zero(S), interfacemode = ReflectOrTransmit) where {S<:Real} = FresnelInterface{S}(insidematerial, outsidematerial, reflectance = reflectance, transmission = one(S) - reflectance, interfacemode = interfacemode) """ - SphericalLens(insidematerial, frontvertex, frontradius, backradius, thickness, semidiameter; lastmaterial = OpticSim.GlassCat.Air, nextmaterial = OpticSim.GlassCat.Air, frontsurfacereflectance = 0.0, backsurfacereflectance = 0.0, frontdecenter = (0, 0), backdecenter = (0, 0)) + SphericalLens(insidematerial, frontvertex, frontradius, backradius, thickness, semidiameter; lastmaterial = OpticSim.GlassCat.Air, nextmaterial = OpticSim.GlassCat.Air, frontsurfacereflectance = 0.0, backsurfacereflectance = 0.0, frontdecenter = (0, 0), backdecenter = (0, 0), interfacemode = ReflectOrTransmit) Constructs a simple cylindrical lens with spherical front and back surfaces. The side walls of the lens are absorbing. """ -function SphericalLens(insidematerial::T, frontvertex::S, frontradius::S, backradius::S, thickness::S, semidiameter::S; lastmaterial::Q = OpticSim.GlassCat.Air, nextmaterial::R = OpticSim.GlassCat.Air, frontsurfacereflectance::S = zero(S), backsurfacereflectance::S = zero(S), frontdecenter::Tuple{S,S} = (zero(S), zero(S)), backdecenter::Tuple{S,S} = (zero(S), zero(S))) where {R<:OpticSim.GlassCat.AbstractGlass,Q<:OpticSim.GlassCat.AbstractGlass,T<:OpticSim.GlassCat.AbstractGlass,S<:Real} +function SphericalLens(insidematerial::T, frontvertex::S, frontradius::S, backradius::S, thickness::S, semidiameter::S; lastmaterial::Q = OpticSim.GlassCat.Air, nextmaterial::R = OpticSim.GlassCat.Air, frontsurfacereflectance::S = zero(S), backsurfacereflectance::S = zero(S), frontdecenter::Tuple{S,S} = (zero(S), zero(S)), backdecenter::Tuple{S,S} = (zero(S), zero(S)), interfacemode = ReflectOrTransmit) where {R<:OpticSim.GlassCat.AbstractGlass,Q<:OpticSim.GlassCat.AbstractGlass,T<:OpticSim.GlassCat.AbstractGlass,S<:Real} @assert !isnan(frontradius) @assert !isnan(backradius) - return ConicLens(insidematerial, frontvertex, frontradius, zero(S), backradius, zero(S), thickness, semidiameter, lastmaterial = lastmaterial, nextmaterial = nextmaterial, frontsurfacereflectance = frontsurfacereflectance, backsurfacereflectance = backsurfacereflectance, frontdecenter = frontdecenter, backdecenter = backdecenter) + return ConicLens(insidematerial, frontvertex, frontradius, zero(S), backradius, zero(S), thickness, semidiameter, lastmaterial = lastmaterial, nextmaterial = nextmaterial, frontsurfacereflectance = frontsurfacereflectance, backsurfacereflectance = backsurfacereflectance, frontdecenter = frontdecenter, backdecenter = backdecenter, interfacemode = interfacemode) end export SphericalLens """ - ConicLens(insidematerial, frontvertex, frontradius, frontconic, backradius, backconic, thickness, semidiameter; lastmaterial = OpticSim.GlassCat.Air, nextmaterial = OpticSim.GlassCat.Air, frontsurfacereflectance = 0.0, backsurfacereflectance = 0.0, frontdecenter = (0, 0), backdecenter = (0, 0)) + ConicLens(insidematerial, frontvertex, frontradius, frontconic, backradius, backconic, thickness, semidiameter; lastmaterial = OpticSim.GlassCat.Air, nextmaterial = OpticSim.GlassCat.Air, frontsurfacereflectance = 0.0, backsurfacereflectance = 0.0, frontdecenter = (0, 0), backdecenter = (0, 0), interfacemode = ReflectOrTransmit) Constructs a simple cylindrical lens with front and back surfaces with a radius and conic term. The side walls of the lens are absorbing. """ -function ConicLens(insidematerial::T, frontvertex::S, frontradius::S, frontconic::S, backradius::S, backconic::S, thickness::S, semidiameter::S; lastmaterial::Q = OpticSim.GlassCat.Air, nextmaterial::R = OpticSim.GlassCat.Air, frontsurfacereflectance::S = zero(S), backsurfacereflectance::S = zero(S), nsamples::Int = 17, frontdecenter::Tuple{S,S} = (zero(S), zero(S)), backdecenter::Tuple{S,S} = (zero(S), zero(S))) where {R<:OpticSim.GlassCat.AbstractGlass,Q<:OpticSim.GlassCat.AbstractGlass,T<:OpticSim.GlassCat.AbstractGlass,S<:Real} +function ConicLens(insidematerial::T, frontvertex::S, frontradius::S, frontconic::S, backradius::S, backconic::S, thickness::S, semidiameter::S; lastmaterial::Q = OpticSim.GlassCat.Air, nextmaterial::R = OpticSim.GlassCat.Air, frontsurfacereflectance::S = zero(S), backsurfacereflectance::S = zero(S), nsamples::Int = 17, frontdecenter::Tuple{S,S} = (zero(S), zero(S)), backdecenter::Tuple{S,S} = (zero(S), zero(S)), interfacemode = ReflectOrTransmit) where {R<:OpticSim.GlassCat.AbstractGlass,Q<:OpticSim.GlassCat.AbstractGlass,T<:OpticSim.GlassCat.AbstractGlass,S<:Real} @assert !isnan(frontradius) @assert !isnan(frontconic) - return AsphericLens(insidematerial, frontvertex, frontradius, frontconic, nothing, backradius, backconic, nothing, thickness, semidiameter, lastmaterial = lastmaterial, nextmaterial = nextmaterial, frontsurfacereflectance = frontsurfacereflectance, backsurfacereflectance = backsurfacereflectance, nsamples = nsamples, frontdecenter = frontdecenter, backdecenter = backdecenter) + return AsphericLens(insidematerial, frontvertex, frontradius, frontconic, nothing, backradius, backconic, nothing, thickness, semidiameter, lastmaterial = lastmaterial, nextmaterial = nextmaterial, frontsurfacereflectance = frontsurfacereflectance, backsurfacereflectance = backsurfacereflectance, nsamples = nsamples, frontdecenter = frontdecenter, backdecenter = backdecenter, interfacemode = interfacemode) end export ConicLens """ - AsphericLens(insidematerial, frontvertex, frontradius, frontconic, frontaspherics, backradius, backconic, backaspherics, thickness, semidiameter; lastmaterial = OpticSim.GlassCat.Air, nextmaterial = OpticSim.GlassCat.Air, frontsurfacereflectance = 0.0, backsurfacereflectance = 0.0, frontdecenter = (0, 0), backdecenter = (0, 0)) + AsphericLens(insidematerial, frontvertex, frontradius, frontconic, frontaspherics, backradius, backconic, backaspherics, thickness, semidiameter; lastmaterial = OpticSim.GlassCat.Air, nextmaterial = OpticSim.GlassCat.Air, frontsurfacereflectance = 0.0, backsurfacereflectance = 0.0, frontdecenter = (0, 0), backdecenter = (0, 0), interfacemode = ReflectOrTransmit) Cosntructs a simple cylindrical lens with front and back surfaces with a radius, conic and apsheric terms. The side walls of the lens are absorbing. """ -function AsphericLens(insidematerial::T, frontvertex::S, frontradius::S, frontconic::S, frontaspherics::Union{Nothing,Vector{Tuple{Int,S}}}, backradius::S, backconic::S, backaspherics::Union{Nothing,Vector{Tuple{Int,S}}}, thickness::S, semidiameter::S; lastmaterial::Q = OpticSim.GlassCat.Air, nextmaterial::R = OpticSim.GlassCat.Air, frontsurfacereflectance::S = zero(S), backsurfacereflectance::S = zero(S), nsamples::Int = 17, frontdecenter::Tuple{S,S} = (zero(S), zero(S)), backdecenter::Tuple{S,S} = (zero(S), zero(S))) where {R<:OpticSim.GlassCat.AbstractGlass,Q<:OpticSim.GlassCat.AbstractGlass,T<:OpticSim.GlassCat.AbstractGlass,S<:Real} +function AsphericLens(insidematerial::T, frontvertex::S, frontradius::S, frontconic::S, frontaspherics::Union{Nothing,Vector{Tuple{Int,S}}}, backradius::S, backconic::S, backaspherics::Union{Nothing,Vector{Tuple{Int,S}}}, thickness::S, semidiameter::S; lastmaterial::Q = OpticSim.GlassCat.Air, nextmaterial::R = OpticSim.GlassCat.Air, frontsurfacereflectance::S = zero(S), backsurfacereflectance::S = zero(S), nsamples::Int = 17, frontdecenter::Tuple{S,S} = (zero(S), zero(S)), backdecenter::Tuple{S,S} = (zero(S), zero(S)), interfacemode = ReflectOrTransmit) where {R<:OpticSim.GlassCat.AbstractGlass,Q<:OpticSim.GlassCat.AbstractGlass,T<:OpticSim.GlassCat.AbstractGlass,S<:Real} @assert semidiameter > zero(S) @assert !isnan(frontradius) @@ -62,20 +62,20 @@ function AsphericLens(insidematerial::T, frontvertex::S, frontradius::S, frontco if isinf(frontradius) && (frontaspherics === nothing) #planar - lens_front = leaf(Plane(SVector{3,S}(0, 0, 1), SVector{3,S}(0, 0, frontvertex), vishalfsizeu = semidiameter, vishalfsizev = semidiameter, interface = interface = opticinterface(S, insidematerial, lastmaterial, frontsurfacereflectance))) + lens_front = leaf(Plane(SVector{3,S}(0, 0, 1), SVector{3,S}(0, 0, frontvertex), vishalfsizeu = semidiameter, vishalfsizev = semidiameter, interface = interface = opticinterface(S, insidematerial, lastmaterial, frontsurfacereflectance, interfacemode))) elseif frontconic == zero(S) && (frontaspherics === nothing) #spherical if frontradius < zero(S) ϕmax = NaNsafeasin(min(abs((min(semidiameter, abs(frontradius)) + frontdecenter_l) / frontradius), one(S))) + S(π / 50) - lens_front = leaf(SphericalCap(abs(frontradius), ϕmax, SVector{3,S}(0, 0, 1), SVector{3,S}(frontdecenter[1], frontdecenter[2], frontvertex), interface = opticinterface(S, insidematerial, lastmaterial, frontsurfacereflectance))) + lens_front = leaf(SphericalCap(abs(frontradius), ϕmax, SVector{3,S}(0, 0, 1), SVector{3,S}(frontdecenter[1], frontdecenter[2], frontvertex), interface = opticinterface(S, insidematerial, lastmaterial, frontsurfacereflectance, interfacemode))) else offset = translation(S, frontdecenter[1], frontdecenter[2], frontvertex - frontradius) - surf = Sphere(abs(frontradius), interface = opticinterface(S, insidematerial, lastmaterial, frontsurfacereflectance)) + surf = Sphere(abs(frontradius), interface = opticinterface(S, insidematerial, lastmaterial, frontsurfacereflectance, interfacemode)) lens_front = leaf(surf, offset) end if abs(frontradius) - frontdecenter_l <= semidiameter # if optical surface is smaller than semidiameter then use a plane to fill the gap - plane = leaf(Plane(SVector{3,S}(0, 0, 1), SVector{3,S}(0, 0, frontvertex - frontradius), vishalfsizeu = semidiameter, vishalfsizev = semidiameter, interface = interface = opticinterface(S, insidematerial, lastmaterial, frontsurfacereflectance))) + plane = leaf(Plane(SVector{3,S}(0, 0, 1), SVector{3,S}(0, 0, frontvertex - frontradius), vishalfsizeu = semidiameter, vishalfsizev = semidiameter, interface = interface = opticinterface(S, insidematerial, lastmaterial, frontsurfacereflectance, interfacemode))) if frontradius > zero(S) lens_front = csgunion(lens_front, plane) else @@ -87,17 +87,17 @@ function AsphericLens(insidematerial::T, frontvertex::S, frontradius::S, frontco if frontaspherics !== nothing frontaspherics = [(i, -k) for (i, k) in frontaspherics] end - surf = AcceleratedParametricSurface(ZernikeSurface(semidiameter + frontdecenter_l + S(0.01), radius = -frontradius, conic = frontconic, aspherics = frontaspherics), nsamples, interface = opticinterface(S, insidematerial, lastmaterial, frontsurfacereflectance)) + surf = AcceleratedParametricSurface(ZernikeSurface(semidiameter + frontdecenter_l + S(0.01), radius = -frontradius, conic = frontconic, aspherics = frontaspherics), nsamples, interface = opticinterface(S, insidematerial, lastmaterial, frontsurfacereflectance, interfacemode)) lens_front = leaf(surf, translation(S, frontdecenter[1], frontdecenter[2], frontvertex)) end if isinf(backradius) && (backaspherics === nothing) #planar - lens_rear = leaf(Plane(SVector{3,S}(0, 0, -1), SVector{3,S}(0, 0, frontvertex - thickness), vishalfsizeu = semidiameter, vishalfsizev = semidiameter, interface = interface = opticinterface(S, insidematerial, nextmaterial, backsurfacereflectance))) + lens_rear = leaf(Plane(SVector{3,S}(0, 0, -1), SVector{3,S}(0, 0, frontvertex - thickness), vishalfsizeu = semidiameter, vishalfsizev = semidiameter, interface = interface = opticinterface(S, insidematerial, nextmaterial, backsurfacereflectance, interfacemode))) elseif backconic == zero(S) && (backaspherics === nothing) #spherical if backradius < zero(S) offset = translation(S, backdecenter[1], backdecenter[2], (frontvertex - thickness) - backradius) - surf = Sphere(abs(backradius), interface = opticinterface(S, insidematerial, nextmaterial, backsurfacereflectance)) + surf = Sphere(abs(backradius), interface = opticinterface(S, insidematerial, nextmaterial, backsurfacereflectance, interfacemode)) lens_rear = leaf(surf, offset) if backradius == semidiameter # in this case we need to clip the sphere @@ -106,10 +106,10 @@ function AsphericLens(insidematerial::T, frontvertex::S, frontradius::S, frontco end else ϕmax = NaNsafeasin(min(abs((min(semidiameter, abs(backradius)) + backdecenter_l) / backradius), one(S))) + S(π / 50) - lens_rear = leaf(SphericalCap(abs(backradius), ϕmax, SVector{3,S}(0, 0, -1), SVector{3,S}(backdecenter[1], backdecenter[2], frontvertex - thickness), interface = opticinterface(S, insidematerial, nextmaterial, backsurfacereflectance))) + lens_rear = leaf(SphericalCap(abs(backradius), ϕmax, SVector{3,S}(0, 0, -1), SVector{3,S}(backdecenter[1], backdecenter[2], frontvertex - thickness), interface = opticinterface(S, insidematerial, nextmaterial, backsurfacereflectance, interfacemode))) end if abs(backradius) - backdecenter_l <= semidiameter - plane = leaf(Plane(SVector{3,S}(0, 0, -1), SVector{3,S}(0, 0, frontvertex - thickness - backradius), vishalfsizeu = semidiameter, vishalfsizev = semidiameter, interface = interface = opticinterface(S, insidematerial, lastmaterial, backsurfacereflectance))) + plane = leaf(Plane(SVector{3,S}(0, 0, -1), SVector{3,S}(0, 0, frontvertex - thickness - backradius), vishalfsizeu = semidiameter, vishalfsizev = semidiameter, interface = interface = opticinterface(S, insidematerial, lastmaterial, backsurfacereflectance, interfacemode))) if backradius < zero(S) lens_rear = csgunion(lens_rear, plane) else @@ -118,7 +118,7 @@ function AsphericLens(insidematerial::T, frontvertex::S, frontradius::S, frontco end else #conic or aspheric - surf = AcceleratedParametricSurface(ZernikeSurface(semidiameter + backdecenter_l + S(0.01), radius = backradius, conic = backconic, aspherics = backaspherics), nsamples, interface = opticinterface(S, insidematerial, nextmaterial, backsurfacereflectance)) + surf = AcceleratedParametricSurface(ZernikeSurface(semidiameter + backdecenter_l + S(0.01), radius = backradius, conic = backconic, aspherics = backaspherics), nsamples, interface = opticinterface(S, insidematerial, nextmaterial, backsurfacereflectance, interfacemode)) lens_rear = leaf(surf, Transform{S}(zero(S), S(π), zero(S), backdecenter[1], backdecenter[2], frontvertex - thickness)) end extra_front = frontradius >= zero(S) || isinf(frontradius) ? zero(S) : abs(frontradius) - sqrt(frontradius^2 - semidiameter^2) diff --git a/src/Optical/OpticalInterface.jl b/src/Optical/OpticalInterface.jl index 7fb7768ac..d4c8f7989 100644 --- a/src/Optical/OpticalInterface.jl +++ b/src/Optical/OpticalInterface.jl @@ -42,6 +42,12 @@ transmission(i::OpticalInterface{T}) -> T abstract type OpticalInterface{T<:Real} end export OpticalInterface +""" +Valid modes for deterministic raytracing +""" +@enum InterfaceMode Reflect Transmit ReflectOrTransmit +export InterfaceMode, Reflect, Transmit, ReflectOrTransmit + ###################################################################### """ @@ -107,8 +113,15 @@ Interface between two materials with behavior defined according to the [Fresnel Assumes unpolarized light. ```julia -FresnelInterface{T}(insidematerial, outsidematerial; reflectance = 0, transmission = 1) +FresnelInterface{T}(insidematerial, outsidematerial; reflectance = 0, transmission = 1, interfacemode = ReflectOrTransmit) ``` + +The interfacemode can be used to trace rays deterministically. Valid values are defined in the InterfaceMode enum. +Reflect means that all values are reflected, Transmit means that all values are transmitted. ReflectOrTransmit will randomly +reflect and transmit rays with the distribution given by the reflection and transmission arguments. This is also the default. +In all cases the power recorded with the ray is correctly updated. This can be used to fake sequential raytracing. For +example a beamsplitter surface may be set to either Reflect or Transmit to switch between the two outgoing ray paths. + """ struct FresnelInterface{T} <: OpticalInterface{T} # storing glasses as IDs (integer) rather than the whole thing seems to improve performance significantly, even when the Glass type is a fixed size (i.e. the interface is pointer-free) @@ -116,28 +129,30 @@ struct FresnelInterface{T} <: OpticalInterface{T} outsidematerial::GlassID reflectance::T transmission::T + interfacemode::InterfaceMode - function FresnelInterface{T}(insidematerial::Z, outsidematerial::Y; reflectance::T = zero(T), transmission::T = one(T)) where {Z<:OpticSim.GlassCat.AbstractGlass,Y<:OpticSim.GlassCat.AbstractGlass,T<:Real} - return FresnelInterface{T}(glassid(insidematerial), glassid(outsidematerial), reflectance = reflectance, transmission = transmission) + function FresnelInterface{T}(insidematerial::Z, outsidematerial::Y; reflectance::T = zero(T), transmission::T = one(T), interfacemode = ReflectOrTransmit) where {Z<:OpticSim.GlassCat.AbstractGlass,Y<:OpticSim.GlassCat.AbstractGlass,T<:Real} + return FresnelInterface{T}(glassid(insidematerial), glassid(outsidematerial), reflectance = reflectance, transmission = transmission, interfacemode = interfacemode) end - function FresnelInterface{T}(insidematerialid::GlassID, outsidematerialid::GlassID; reflectance::T = zero(T), transmission::T = one(T)) where {T<:Real} + function FresnelInterface{T}(insidematerialid::GlassID, outsidematerialid::GlassID; reflectance::T = zero(T), transmission::T = one(T), interfacemode = ReflectOrTransmit) where {T<:Real} @assert zero(T) <= reflectance <= one(T) @assert zero(T) <= transmission <= one(T) @assert reflectance + transmission <= one(T) - return new{T}(insidematerialid, outsidematerialid, reflectance, transmission) + return new{T}(insidematerialid, outsidematerialid, reflectance, transmission, interfacemode) end end export FresnelInterface function Base.show(io::IO, a::FresnelInterface{R}) where {R<:Real} - print(io, "FresnelInterface($(glassname(a.insidematerial)), $(glassname(a.outsidematerial)), $(a.reflectance), $(a.transmission))") + print(io, "FresnelInterface($(glassname(a.insidematerial)), $(glassname(a.outsidematerial)), $(a.reflectance), $(a.transmission), $(a.interfacemode))") end insidematerialid(a::FresnelInterface{T}) where {T<:Real} = a.insidematerial outsidematerialid(a::FresnelInterface{T}) where {T<:Real} = a.outsidematerial reflectance(a::FresnelInterface{T}) where {T<:Real} = a.reflectance transmission(a::FresnelInterface{T}) where {T<:Real} = a.transmission +interfacemode(a::FresnelInterface{T}) where {T<:Real} = a.interfacemode transmissiveinterface(::Type{T}, insidematerial::X, outsidematerial::Y) where {T<:Real,X<:OpticSim.GlassCat.AbstractGlass,Y<:OpticSim.GlassCat.AbstractGlass} = FresnelInterface{T}(insidematerial, outsidematerial, reflectance = zero(T), transmission = one(T)) reflectiveinterface(::Type{T}, insidematerial::X, outsidematerial::Y) where {T<:Real,X<:OpticSim.GlassCat.AbstractGlass,Y<:OpticSim.GlassCat.AbstractGlass} = FresnelInterface{T}(insidematerial, outsidematerial, reflectance = one(T), transmission = zero(T))