From 9595f11156080f43518caf3f9c433a8664bd2081 Mon Sep 17 00:00:00 2001 From: Alfred Wong Date: Tue, 27 Apr 2021 23:04:04 +0100 Subject: [PATCH] Switch to new emitter code (#140) * Switch to new emitter code modified Examples.jl drawSchmidth to use new emitters. * Switch to new emitter code renamed Emitter.jl to OldEmitter.jl to find all places where old Emitter code is used. moved AbstractRayGenerator types from Emitter.jl to RayGenerator.jl fixed Examples.drawSchmidt so it draws correctly using the new emitter classes. merge with AbstractOpticalSystem changes * cooke triplet (examples.md) * @code_string usage * bump minor version number in advance (breaking changes) * license header for RayGenerator.jl * switch OldEmitter tests to new Emitters code * move @code_string to top of example * fix default arg: Transform is not a module * zoom lens * schmidt cassegrain telescope * HOEs * remove old emitters docs * complete the switch! * remove commented code * syntax highlighting attempt * split examples into docs/other; add exports * move all Vis calls to Examples/docs.jl this means that all we need to do in examples.md is show the (syntax-highlighted) code_string and then call the corresponding draw_* function in an @example block also consolidated the usage of Maybe{T} and MaybeVector{T} within OpticSim by definining them within OpticSim.jl (sources.jl still has to define it separately because it is included directly in deps/build.jl) fixes #144 * 0.5.0 -> 0.5.0-DEV * fix warning block * fix dataframe @show-ing * remove splatting Co-authored-by: BrianGun Co-authored-by: BrianGun <36491850+BrianGun@users.noreply.github.com> --- Project.toml | 2 +- docs/Project.toml | 1 + docs/make.jl | 1 - docs/src/emitters.md | 314 +++++++++++-- docs/src/emitters_new.md | 304 ------------- docs/src/examples.md | 230 +++------- src/Examples/Examples.jl | 377 +--------------- src/Examples/docs.jl | 255 +++++++++++ src/Examples/other.jl | 286 ++++++++++++ src/OpticSim.jl | 4 +- src/Optical/Emitter.jl | 699 ----------------------------- src/Optical/Emitters/Directions.jl | 4 + src/Optical/Optical.jl | 2 +- src/Optical/RayGenerator.jl | 20 + src/Vis/Visualization.jl | 87 +--- test/testsets/JuliaLang.jl | 3 - test/testsets/OpticalSystem.jl | 4 +- 17 files changed, 906 insertions(+), 1687 deletions(-) delete mode 100644 docs/src/emitters_new.md create mode 100644 src/Examples/docs.jl create mode 100644 src/Examples/other.jl delete mode 100644 src/Optical/Emitter.jl create mode 100644 src/Optical/RayGenerator.jl diff --git a/Project.toml b/Project.toml index 824b887eb..8e9d6cced 100644 --- a/Project.toml +++ b/Project.toml @@ -2,7 +2,7 @@ name = "OpticSim" uuid = "24114763-4efb-45e7-af0e-cde916beb153" authors = ["Brian Guenter", "Charlie Hewitt", "Ran Gal", "Alfred Wong"] repository = "https://github.com/microsoft/OpticSim.jl" -version = "0.4.4" +version = "0.5.0-DEV" [deps] CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" diff --git a/docs/Project.toml b/docs/Project.toml index fb2cc5781..6f8358b4e 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,4 +1,5 @@ [deps] +CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a" diff --git a/docs/make.jl b/docs/make.jl index 15f4b3a5f..a225f71ca 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -23,7 +23,6 @@ makedocs( "Optical" => [ "Systems" => "systems.md", "Emitters" => "emitters.md", - "Emitters (NEW)" => "emitters_new.md", "Interfaces" => "interfaces.md", "Lenses" => "lenses.md" ], diff --git a/docs/src/emitters.md b/docs/src/emitters.md index e036891fb..a2cbf4dcd 100644 --- a/docs/src/emitters.md +++ b/docs/src/emitters.md @@ -1,87 +1,307 @@ # Emitters -Emitters create rays in a certain pattern, usually controlled by some parameters. -The are constructed in a modular way, e.g. +!!! warning + **The old emitter implementation is deprecated as of v0.5!** See below for the new API. +Emitters create rays in a certain pattern, usually controlled by some parameters. Emitters are defined by Pixels and Spatial Layouts, and have a spectrum and an optical power distribution over the hemisphere. These are intrinsic physical properties of the emitter. + +The **basic emitter** ([`Source`](@ref sources)) is constructed as a combination of 4 basic elements and a 3D [`Transform`](@ref). The basic elements include: +- [`Spectrum`](@ref spectrum) +- [`Angular Power Distribution`](@ref angular_power_distribution) +- [`Rays Origins Distribution`](@ref rays_origins_distribution) +- [`Rays Directions Distribution`](@ref rays_directions_distribution) + +The [`OpticSim`](index.html) package comes with various implementations of each of these basic elements: +- Spectrum - the **generate** interface returns a tuple (power, wavelength) + * [`Emitters.Spectrum.Uniform`](@ref) - A flat spectrum bounded (default: from 450nm to 680nm). the range is sampled uniformly. + * [`Emitters.Spectrum.DeltaFunction`](@ref) - Constant wave length. + * [`Emitters.Spectrum.Measured`](@ref) - measured spectrum to compute emitter power and wavelength (created by reading CSV files – more details will follow). +- Angular Power Distribution - the interface **apply** returns an OpticalRay with modified power + * [`Emitters.AngularPower.Lambertian`](@ref) + * [`Emitters.AngularPower.Cosine`](@ref) + * [`Emitters.AngularPower.Gaussian`](@ref) +- Rays Origins Distribution - the interface **length** returns the number of samples, and **generate** returns the n'th sample. + * [`Emitters.Origins.Point`](@ref) - a single point + * [`Emitters.Origins.RectUniform`](@ref) - a uniformly sampled rectangle with user defined number of samples + * [`Emitters.Origins.RectGrid`](@ref) - a rectangle sampled in a grid fashion + * [`Emitters.Origins.Hexapolar`](@ref) - a circle (or an ellipse) sampled in an hexapolar fashion (rings) +- Rays Directions Distribution - the interface **length** returns the number of samples, and **generate** returns the n'th sample. + * [`Emitters.Directions.Constant`](@ref) + * [`Emitters.Directions.RectGrid`](@ref) + * [`Emitters.Directions.UniformCone`](@ref) + * [`Emitters.Directions.HexapolarCone`](@ref) + +## [Examples of Basic Emitters](@id basic_emitters) + +**Note**: All of the examples on this page assume that the following statement was executed: + +```@example +using OpticSim, OpticSim.Geometry, OpticSim.Emitters +``` + +### Point origin with various Direction distributions ```@example -using OpticSim, StaticArrays # hide -Vis.draw(UniformOpticalSource(CollimatedSource(HexapolarOriginPoints(4, 1.0, 1.0, position = SVector(0.0, 0.0, 10.0), direction = SVector(0.0, sind(30), -cosd(30)))), 0.55)) -Vis.save("assets/source1.png") # hide +using OpticSim, OpticSim.Geometry, OpticSim.Emitters # hide +src = Sources.Source(origins=Origins.Point(), directions=Directions.RectGrid(π/4, π/4, 15, 15)) +Vis.draw(src, debug=true) +Vis.save("assets/emitters_example_rect_grid.png") # hide nothing #hide ``` -![Emitter example 1 image](assets/source1.png) +```@raw html + + + +
+ + + + +An example of RectGrid Direction Distribution +
+``` ```@example -using OpticSim, StaticArrays # hide -Vis.draw(UniformOpticalSource(GridSource(OriginPoint{Float64}(1, position = SVector(0.0, 0.0, 10.0), direction = SVector(0.0, 0.0, -1.0)), 5, 5, π / 4, π / 4), 0.65)) -Vis.save("assets/source2.png") # hide +using OpticSim, OpticSim.Geometry, OpticSim.Emitters # hide +src = Sources.Source(origins=Origins.Point(), directions=Directions.UniformCone(π/6, 1000)) +Vis.draw(src, debug=true) +Vis.save("assets/emitters_example_uniform_cone.png") # hide nothing #hide ``` -![Emitter example 2 image](assets/source2.png) +```@raw html + + + +
+ + + + +An example of UniformCone Direction Distribution with 1000 sampled directions +
+``` ```@example -using OpticSim, StaticArrays # hide -Vis.draw(CosineOpticalSource(RandomSource(OriginPoint{Float64}(200, direction = SVector(0.0, 0.0, 1.0))), 1.0, 0.45)) -Vis.save("assets/source3.png") # hide +using OpticSim, OpticSim.Geometry, OpticSim.Emitters # hide +src = Sources.Source(origins=Origins.Point(), directions=Directions.HexapolarCone(π/6, 10)) +Vis.draw(src, debug=true) +Vis.save("assets/emitters_example_hexapolar_cone.png") # hide nothing #hide ``` -![Emitter example 3 image](assets/source3.png) +```@raw html + + + +
+ + + + +An example of HexapolarCone Direction Distribution +
+``` -## Rays +### Various origins distributions -```@docs -Ray -OpticalRay -OpticSim.generateray +```@example +using OpticSim, OpticSim.Geometry, OpticSim.Emitters # hide +src = Sources.Source(origins=Origins.RectGrid(1.0, 1.0, 10, 10), directions=Directions.Constant()) +Vis.draw(src, debug=true) +Vis.save("assets/emitters_example_origin_rectgrid.png") # hide +nothing #hide +``` + +```@raw html + + + +
+ + + + +An example of RectGrid origin distribution +
+``` + +```@example +using OpticSim, OpticSim.Geometry, OpticSim.Emitters # hide +src = Sources.Source(origins=Origins.Hexapolar(5, 1.0, 2.0), directions=Directions.Constant()) +Vis.draw(src, debug=true) +Vis.save("assets/emitters_example_origin_hexapolar.png") # hide +nothing #hide +``` + +```@raw html + + + +
+ + + + +An example of Hexapolar origin distribution +
+``` + +### Examples of Angular Power Distribution + +In these example, the arrow width is proportional to the ray power. + +```@example +using OpticSim, OpticSim.Geometry, OpticSim.Emitters # hide +src = Sources.Source( + origins=Origins.Hexapolar(1, 8.0, 8.0), # Hexapolar Origins + directions=Directions.RectGrid(π/6, π/6, 15, 15), # RectGrid Directions + power=AngularPower.Cosine(10.0) # Cosine Angular Power +) +Vis.draw(src, debug=true) +Vis.save("assets/emitters_example_angular1.png") # hide +nothing #hide ``` -## Origin Points +```@raw html + + + +
+ + + + +An example of Cosine angular power distribution +
+``` + +```@example +using OpticSim, OpticSim.Geometry, OpticSim.Emitters # hide +src = Sources.Source( + origins=Origins.RectGrid(1.0, 1.0, 3, 3), # RectGrid Origins + directions=Directions.HexapolarCone(π/6, 10), # HexapolarCone Directions + power=AngularPower.Gaussian(2.0, 2.0) # Gaussian Angular Power +) +Vis.draw(src, debug=true) +Vis.save("assets/emitters_example_angular2.png") # hide +nothing #hide +``` + +```@raw html + + + +
+ + + + +An example of Gaussian angular power distribution +
+``` + +### Composite Sources - Display Example + +```@example +using OpticSim, OpticSim.Geometry, OpticSim.Emitters # hide + +# construct the emitter's basic components +S = Spectrum.Uniform() +P = AngularPower.Lambertian() +O = Origins.RectGrid(1.0, 1.0, 3, 3) +D = Directions.HexapolarCone(deg2rad(5.0), 3) + +# construct the source. in this example a "pixel" source will contain only one source as we are simulating a "b/w" display. +# for RGB displays we can combine 3 sources to simulate "a pixel". +Tr = Transform(Vec3(0.5, 0.5, 0.0)) +source1 = Sources.Source(Tr, S, O, D, P) + +# create a list of pixels - each one is a composite source +pixels = Vector{Sources.CompositeSource{Float64}}(undef, 0) +for y in 1:5 # image_height + for x in 1:10 # image_width + # pixel position relative to the display's origin + local pixel_position = Vec3((x-1) * 1.1, (y-1) * 1.5, 0.0) + local Tr = Transform(pixel_position) + + # constructing the "pixel" + pixel = Sources.CompositeSource(Tr, [source1]) + + push!(pixels, pixel) + end +end + +Tr = Transform(Vec3(0.0, 0.0, 0.0)) +my_display = Sources.CompositeSource(Tr, pixels) + +Vis.draw(my_display) # render the display - nothing but the origins primitives +rays = AbstractArray{OpticalRay{Float64, 3}}(collect(my_display)) # collect the rays generated by the display +Vis.draw!(rays) # render the rays + +Vis.save("assets/emitters_example_composite_display.png") # hide +nothing #hide +``` + +```@raw html + + + +
+ + + + +A display example using composite sources. +
+``` + + + +## [Spectrum](@id spectrum) ```@docs -OpticSim.RayOriginGenerator -RandomRectOriginPoints -GridRectOriginPoints -RandomEllipseOriginPoints -HexapolarOriginPoints -OriginPoint -OpticSim.genorigin +Emitters.Spectrum.Uniform +Emitters.Spectrum.DeltaFunction +Emitters.Spectrum.Measured +Emitters.Spectrum.spectrumpower ``` -## Directions +## [Angular Power Distribution](@id angular_power_distribution) ```@docs -GeometricRayGenerator -CollimatedSource -GridSource -RandomSource -OpticSim.gendirection +Emitters.AngularPower.Lambertian +Emitters.AngularPower.Cosine +Emitters.AngularPower.Gaussian ``` -## Power +## [Rays Origins Distribution](@id rays_origins_distribution) ```@docs -OpticalRayGenerator -UniformOpticalSource -CosineOpticalSource -GaussianOpticalSource +Emitters.Origins.Point +Emitters.Origins.RectUniform +Emitters.Origins.RectGrid +Emitters.Origins.Hexapolar ``` -## Compound +## [Rays Directions Distribution](@id rays_directions_distribution) ```@docs -PixelSource -OpticalSourceArray -BasicDisplayPanel -OpticalSourceGroup -RayListSource +Emitters.Directions.Constant +Emitters.Directions.RectGrid +Emitters.Directions.UniformCone +Emitters.Directions.HexapolarCone ``` -## Fields +## [Sources](@id sources) ```@docs -HexapolarField -GridField +Emitters.Sources.Source +Emitters.Sources.CompositeSource ``` + + + + + + diff --git a/docs/src/emitters_new.md b/docs/src/emitters_new.md deleted file mode 100644 index 97420d768..000000000 --- a/docs/src/emitters_new.md +++ /dev/null @@ -1,304 +0,0 @@ -# Emitters (NEW) - -Emitters create rays in a certain pattern, usually controlled by some parameters. Emitters are defined by Pixels and Spatial Layouts, and have a spectrum and an optical power distribution over the hemisphere. These are intrinsic physical properties of the emitter. - -The **basic emitter** ([`Source`](@ref sources)) is constructed as a combination of 4 basic elements and a 3D [`Transform`](@ref). The basic elements include: -- [`Spectrum`](@ref spectrum) -- [`Angular Power Distribution`](@ref angular_power_distribution) -- [`Rays Origins Distribution`](@ref rays_origins_distribution) -- [`Rays Directions Distribution`](@ref rays_directions_distribution) - -The [`OpticSim`](index.html) package comes with various implementations of each of these basic elements: -- Spectrum - the **generate** interface returns a tuple (power, wavelength) - * [`Emitters.Spectrum.Uniform`](@ref) - A flat spectrum bounded (default: from 450nm to 680nm). the range is sampled uniformly. - * [`Emitters.Spectrum.DeltaFunction`](@ref) - Constant wave length. - * [`Emitters.Spectrum.Measured`](@ref) - measured spectrum to compute emitter power and wavelength (created by reading CSV files – more details will follow). -- Angular Power Distribution - the interface **apply** returns an OpticalRay with modified power - * [`Emitters.AngularPower.Lambertian`](@ref) - * [`Emitters.AngularPower.Cosine`](@ref) - * [`Emitters.AngularPower.Gaussian`](@ref) -- Rays Origins Distribution - the interface **length** returns the number of samples, and **generate** returns the n'th sample. - * [`Emitters.Origins.Point`](@ref) - a single point - * [`Emitters.Origins.RectUniform`](@ref) - a uniformly sampled rectangle with user defined number of samples - * [`Emitters.Origins.RectGrid`](@ref) - a rectangle sampled in a grid fashion - * [`Emitters.Origins.Hexapolar`](@ref) - a circle (or an ellipse) sampled in an hexapolar fashion (rings) -- Rays Directions Distribution - the interface **length** returns the number of samples, and **generate** returns the n'th sample. - * [`Emitters.Directions.Constant`](@ref) - * [`Emitters.Directions.RectGrid`](@ref) - * [`Emitters.Directions.UniformCone`](@ref) - * [`Emitters.Directions.HexapolarCone`](@ref) - -## [Examples of Basic Emitters](@id basic_emitters) - -**Note**: All of the examples on this page assume that the following statement was executed: - -```@example -using OpticSim, OpticSim.Geometry, OpticSim.Emitters -``` - -### Point origin with various Direction distributions -```@example -using OpticSim, OpticSim.Geometry, OpticSim.Emitters # hide -src = Sources.Source(origins=Origins.Point(), directions=Directions.RectGrid(π/4, π/4, 15, 15)) -Vis.draw(src, debug=true) -Vis.save("assets/emitters_example_rect_grid.png") # hide -nothing #hide -``` - -```@raw html - - - -
- - - - -An example of RectGrid Direction Distribution -
-``` - -```@example -using OpticSim, OpticSim.Geometry, OpticSim.Emitters # hide -src = Sources.Source(origins=Origins.Point(), directions=Directions.UniformCone(π/6, 1000)) -Vis.draw(src, debug=true) -Vis.save("assets/emitters_example_uniform_cone.png") # hide -nothing #hide -``` - -```@raw html - - - -
- - - - -An example of UniformCone Direction Distribution with 1000 sampled directions -
-``` - -```@example -using OpticSim, OpticSim.Geometry, OpticSim.Emitters # hide -src = Sources.Source(origins=Origins.Point(), directions=Directions.HexapolarCone(π/6, 10)) -Vis.draw(src, debug=true) -Vis.save("assets/emitters_example_hexapolar_cone.png") # hide -nothing #hide -``` - -```@raw html - - - -
- - - - -An example of HexapolarCone Direction Distribution -
-``` - -### Various origins distributions - -```@example -using OpticSim, OpticSim.Geometry, OpticSim.Emitters # hide -src = Sources.Source(origins=Origins.RectGrid(1.0, 1.0, 10, 10), directions=Directions.Constant()) -Vis.draw(src, debug=true) -Vis.save("assets/emitters_example_origin_rectgrid.png") # hide -nothing #hide -``` - -```@raw html - - - -
- - - - -An example of RectGrid origin distribution -
-``` - -```@example -using OpticSim, OpticSim.Geometry, OpticSim.Emitters # hide -src = Sources.Source(origins=Origins.Hexapolar(5, 1.0, 2.0), directions=Directions.Constant()) -Vis.draw(src, debug=true) -Vis.save("assets/emitters_example_origin_hexapolar.png") # hide -nothing #hide -``` - -```@raw html - - - -
- - - - -An example of Hexapolar origin distribution -
-``` - -### Examples of Angular Power Distribution - -In these example, the arrow width is proportional to the ray power. - -```@example -using OpticSim, OpticSim.Geometry, OpticSim.Emitters # hide -src = Sources.Source( - origins=Origins.Hexapolar(1, 8.0, 8.0), # Hexapolar Origins - directions=Directions.RectGrid(π/6, π/6, 15, 15), # RectGrid Directions - power=AngularPower.Cosine(10.0) # Cosine Angular Power -) -Vis.draw(src, debug=true) -Vis.save("assets/emitters_example_angular1.png") # hide -nothing #hide -``` - -```@raw html - - - -
- - - - -An example of Cosine angular power distribution -
-``` - -```@example -using OpticSim, OpticSim.Geometry, OpticSim.Emitters # hide -src = Sources.Source( - origins=Origins.RectGrid(1.0, 1.0, 3, 3), # RectGrid Origins - directions=Directions.HexapolarCone(π/6, 10), # HexapolarCone Directions - power=AngularPower.Gaussian(2.0, 2.0) # Gaussian Angular Power -) -Vis.draw(src, debug=true) -Vis.save("assets/emitters_example_angular2.png") # hide -nothing #hide -``` - -```@raw html - - - -
- - - - -An example of Gaussian angular power distribution -
-``` - -### Composite Sources - Display Example - -```@example -using OpticSim, OpticSim.Geometry, OpticSim.Emitters # hide - -# construct the emitter's basic components -S = Spectrum.Uniform() -P = AngularPower.Lambertian() -O = Origins.RectGrid(1.0, 1.0, 3, 3) -D = Directions.HexapolarCone(deg2rad(5.0), 3) - -# construct the source. in this example a "pixel" source will contain only one source as we are simulating a "b/w" display. -# for RGB displays we can combine 3 sources to simulate "a pixel". -Tr = Transform(Vec3(0.5, 0.5, 0.0)) -source1 = Sources.Source(Tr, S, O, D, P) - -# create a list of pixels - each one is a composite source -pixels = Vector{Sources.CompositeSource{Float64}}(undef, 0) -for y in 1:5 # image_height - for x in 1:10 # image_width - # pixel position relative to the display's origin - local pixel_position = Vec3((x-1) * 1.1, (y-1) * 1.5, 0.0) - local Tr = Transform(pixel_position) - - # constructing the "pixel" - pixel = Sources.CompositeSource(Tr, [source1]) - - push!(pixels, pixel) - end -end - -Tr = Transform(Vec3(0.0, 0.0, 0.0)) -my_display = Sources.CompositeSource(Tr, pixels) - -Vis.draw(my_display) # render the display - nothing but the origins primitives -rays = AbstractArray{OpticalRay{Float64, 3}}(collect(my_display)) # collect the rays generated by the display -Vis.draw!(rays) # render the rays - -Vis.save("assets/emitters_example_composite_display.png") # hide -nothing #hide -``` - -```@raw html - - - -
- - - - -A display example using composite sources. -
-``` - - - -## [Spectrum](@id spectrum) - -```@docs -Emitters.Spectrum.Uniform -Emitters.Spectrum.DeltaFunction -Emitters.Spectrum.Measured -Emitters.Spectrum.spectrumpower -``` - -## [Angular Power Distribution](@id angular_power_distribution) - -```@docs -Emitters.AngularPower.Lambertian -Emitters.AngularPower.Cosine -Emitters.AngularPower.Gaussian -``` - -## [Rays Origins Distribution](@id rays_origins_distribution) - -```@docs -Emitters.Origins.Point -Emitters.Origins.RectUniform -Emitters.Origins.RectGrid -Emitters.Origins.Hexapolar -``` - -## [Rays Directions Distribution](@id rays_directions_distribution) - -```@docs -Emitters.Directions.Constant -Emitters.Directions.RectGrid -Emitters.Directions.UniformCone -Emitters.Directions.HexapolarCone -``` - -## [Sources](@id sources) - -```@docs -Emitters.Sources.Source -Emitters.Sources.CompositeSource -``` - - - - - - diff --git a/docs/src/examples.md b/docs/src/examples.md index 11fc49470..206584ac8 100644 --- a/docs/src/examples.md +++ b/docs/src/examples.md @@ -1,5 +1,14 @@ # Examples +```@setup highlight +using CodeTracking, Markdown, OpticSim.Examples +mdparse(s) = Markdown.parse("```julia\n$s\n```") +``` + +```@setup example +using OpticSim.Examples +``` + ## Pluto Notebooks The [`OpticSim`](index.html) package comes with several [`Pluto`](https://github.com/fonsp/Pluto.jl) notebooks (code snippets are coming soon) that allow the user to change and run sample code and view the results in real-time. We highly recommend for you to try these out. @@ -14,118 +23,47 @@ NB.run_sample("EmittersIntro.jl") The **run_sample** method will **copy** the notebook to your current folder (if it does not exist) and launch Pluto to run the notebook in the browser. ## Cooke Triplet +```@example highlight +mdparse(@code_string draw_cooketriplet()) # hide +``` -```@example -using OpticSim - -using DataFrames -sys = AxisymmetricOpticalSystem( - DataFrame(Surface = [:Object, 1, 2, 3, :Stop, 5, 6, :Image], - Radius = [Inf, 26.777, 66.604, -35.571, 35.571, 35.571, -26.777, Inf], - Thickness = [Inf, 4.0, 2.0, 4.0, 2.0, 4.0, 44.748, missing], - Material = [OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_SK16, OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_SF2, OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_SK16, OpticSim.GlassCat.Air, missing], - SemiDiameter = [Inf, 8.580, 7.513, 7.054, 6.033, 7.003, 7.506, 15.0])) -@show sys -f1 = HexapolarField(sys, collimated = true, samples = 4, sourcenum = 1) -f2 = HexapolarField(sys, collimated = true, samples = 4, sourceangle = -10 / 180 * π, sourcenum = 2) -Vis.drawtracerays(sys, raygenerator = f1, test = true, trackallrays = true, colorbysourcenum = true, resolution = (1000, 700)) -Vis.drawtracerays!(sys, raygenerator = f2, test = true, trackallrays = true, colorbysourcenum = true) -Vis.make2dy() # hide -Vis.save("assets/cooke.png") # hide -nothing # hide +```@example example +sys = draw_cooketriplet("assets/cooke.png"); @show sys; nothing # hide ``` ![Cooke triplet visualization](assets/cooke.png) ## Zoom Lens +```@example highlight +mdparse(@code_string draw_zoomlenses()) # hide +``` -```@example -using OpticSim - -using DataFrames -function zoom_lens(pos = 1) - if pos == 0 - stop = 2.89 - zoom = 9.48 - dist = 4.46970613 - elseif pos == 1 - stop = 3.99 - zoom = 4.48 - dist = 21.21 - else - stop = 4.90 - zoom = 2.00 - dist = 43.81 - end - return AxisymmetricOpticalSystem{Float64}( - DataFrame(Surface = [:Object, :Stop, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, :Image], - Radius = [Inf64, Inf64, -1.6202203499676E+01, -4.8875855327468E+01, 1.5666614444619E+01, -4.2955326460481E+01, 1.0869565217391E+02, 2.3623907394283E+01, -1.6059097478722E+01, -4.2553191489362E+02, -3.5435861091425E+01, -1.4146272457208E+01, -2.5125628140704E+02, -2.2502250225023E+01, -1.0583130489999E+01, -4.4444444444444E+01, Inf64], - Aspherics = [missing, missing, missing, missing, missing, [(4, 1.03860000000E-04), (6, 1.42090000000E-07), (8, -8.84950000000E-09), (10, 1.24770000000E-10), (12, -1.03670000000E-12), (14, 3.65560000000E-15)], missing, missing, [(4, 4.27210000000E-05), (6, 1.24840000000E-07), (8, 9.70790000000E-09), (10, -1.84440000000E-10), (12, 1.86440000000E-12), (14, -7.79750000000E-15)], [(4, 1.13390000000E-04), (6, 4.81650000000E-07), (8, 1.87780000000E-08), (10, -5.75710000000E-10), (12, 8.99940000000E-12), (14, -4.67680000000E-14)], missing, missing, missing, missing, missing, missing, missing], - Thickness = [Inf64, 0.0, 5.18, 0.10, 4.40, 0.16, 1.0, 4.96, zoom, 4.04, 1.35, 1.0, 2.80, 3.0, 1.22, dist, missing], - Material = [OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, OpticSim.GlassCat.OHARA.S_LAH66, OpticSim.GlassCat.Air, OpticSim.GlassCat.NIKON.LLF6, OpticSim.GlassCat.Air, OpticSim.GlassCat.OHARA.S_TIH6, OpticSim.GlassCat.OHARA.S_FSL5, OpticSim.GlassCat.Air, OpticSim.GlassCat.OHARA.S_FSL5, OpticSim.GlassCat.Air, OpticSim.GlassCat.OHARA.S_LAL8, OpticSim.GlassCat.OHARA.S_FSL5, OpticSim.GlassCat.Air, OpticSim.GlassCat.OHARA.S_LAH66, OpticSim.GlassCat.Air, missing], - SemiDiameter = [Inf64, stop, 3.85433218451, 3.85433218451, 4.36304692871, 4.36304692871, 4.72505505439, 4.72505505439, 4.72505505439, 4.45240784026, 4.45240784026, 4.50974054117, 4.50974054117, 4.50974054117, 4.76271114409, 4.76271114409, 15.0])) -end -@show zoom_lens(0) -Vis.drawtracerays(zoom_lens(0), test = true, trackallrays = true, numdivisions = 50, resolution = (1200, 600)) -Vis.make2dy() # hide -Vis.save("assets/zoom0.png") # hide -Vis.drawtracerays(zoom_lens(1), test = true, trackallrays = true, numdivisions = 50, resolution = (1200, 600)) -Vis.make2dy() # hide -Vis.save("assets/zoom1.png") # hide -Vis.drawtracerays(zoom_lens(2), test = true, trackallrays = true, numdivisions = 50, resolution = (1200, 600)) -Vis.make2dy() # hide -Vis.save("assets/zoom2.png") # hide -nothing # hide -``` - -![Zoom position 1 visualization](assets/zoom0.png) -![Zoom position 2 visualization](assets/zoom1.png) -![Zoom position 3 visualization](assets/zoom2.png) - -## Schmidt Cassegrain Telescope - -```@example -using OpticSim, OpticSim.Geometry -using StaticArrays +```@example example +syss = draw_zoomlenses(["assets/zoom$i.png" for i in 1:3]); @show sys[1]; nothing # hide +``` +![Zoom position 1 visualization](assets/zoom1.png) +![Zoom position 2 visualization](assets/zoom2.png) +![Zoom position 3 visualization](assets/zoom3.png) -# glass entrance lens on telescope -topsurf = Plane(SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, 0.0), interface = FresnelInterface{Float64}(OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air), vishalfsizeu = 12.00075, vishalfsizev = 12.00075) -botsurf = AcceleratedParametricSurface(ZernikeSurface(12.00075, radius = -1.14659768e+4, aspherics = [(4, 3.68090959e-7), (6, 2.73643352e-11), (8, 3.20036892e-14)]), 17, interface = FresnelInterface{Float64}(OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air)) -coverlens = csgintersection(leaf(Cylinder(12.00075, 1.4)), csgintersection(leaf(topsurf), leaf(botsurf, Transform(OpticSim.rotmatd(0, 180, 0), Vec3(0.0, 0.0, -0.65))))) -# big mirror with a hole in it -bigmirror = ConicLens(OpticSim.GlassCat.SCHOTT.N_BK7, -72.65, -95.2773500000134, 0.077235, Inf, 0.0, 0.2, 12.18263, frontsurfacereflectance = 1.0) -bigmirror = csgdifference(bigmirror, leaf(Cylinder(4.0, 0.3, interface = opaqueinterface()), translation(0.0, 0.0, -72.75))) -# small mirror supported on a spider -smallmirror = SphericalLens(OpticSim.GlassCat.SCHOTT.N_BK7, -40.65, Inf, -49.6845, 1.13365, 4.3223859, backsurfacereflectance = 1.0) -obscuration1 = Circle(4.5, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, -40.649), interface = opaqueinterface()) -obscurations2 = Spider(3, 0.5, 12.0, SVector(0.0, 0.0, -40.65)) -# put it together with the detector -la = LensAssembly(coverlens(), bigmirror(), smallmirror(), obscuration1, obscurations2...) -det = Circle(3.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, -92.4542988), interface = opaqueinterface()) -tele = CSGOpticalSystem(la, det) +## Schmidt Cassegrain Telescope +```@example highlight +mdparse(@code_string draw_schmidtcassegraintelescope()) # hide +``` -Vis.drawtracerays(tele, raygenerator = UniformOpticalSource(CollimatedSource(GridRectOriginPoints(5, 5, 10.0, 10.0, position = SVector(0.0, 0.0, 20.0))), 0.55), trackallrays = true, colorbynhits = true, test = true) -Vis.save("assets/tele.png") # hide -nothing # hide +```@example example +draw_schmidtcassegraintelescope("assets/tele.png") # hide ``` ![Schmidt Cassegrain Telescope visualization](assets/tele.png) ## Lens Construction +```@example highlight +mdparse(@code_string draw_lensconstruction()) # hide +``` -```@example -using OpticSim, OpticSim.Geometry -using StaticArrays - -topsurface = leaf(AcceleratedParametricSurface(QTypeSurface(9.0, radius = -25.0, conic = 0.3, αcoeffs = [(1, 0, 0.3), (1, 1, 1.0)], βcoeffs = [(1, 0, -0.1), (2, 0, 0.4), (3, 0, -0.6)], normradius = 9.5), interface = FresnelInterface{Float64}(OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air)), translation(0.0, 0.0, 5.0)) -botsurface = leaf(Plane(0.0, 0.0, -1.0, 0.0, 0.0, -5.0, vishalfsizeu = 9.5, vishalfsizev = 9.5, interface = FresnelInterface{Float64}(OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air))) -barrel = leaf(Cylinder(9.0, 20.0, interface = FresnelInterface{Float64}(OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, reflectance = zero(Float64), transmission = zero(Float64)))) -lens = csgintersection(barrel, csgintersection(topsurface, botsurface))(Transform{Float64}(0.0, Float64(π), 0.0, 0.0, 0.0, -5.0)) -sys = CSGOpticalSystem(LensAssembly(lens), Rectangle(15.0, 15.0, [0.0, 0.0, 1.0], [0.0, 0.0, -67.8], interface = opaqueinterface())) -Vis.drawtracerays(sys, test = true, trackallrays = true) -Vis.save("assets/qtype.png") # hide -nothing # hide +```@example example +draw_lensconstruction("assets/qtype.png") # hide ``` ![lens construction example](assets/qtype.png) @@ -133,105 +71,45 @@ nothing # hide ## HOEs ### Focusing +```@example highlight +mdparse(@code_string draw_HOEfocus()) # hide +``` -```@example -using OpticSim -using StaticArrays - -rect = Rectangle(5.0, 5.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, 0.0)) -int = HologramInterface(SVector(0.0, -3.0, -20.0), ConvergingBeam, SVector(0.0, 0.0, -1.0), CollimatedBeam, 0.55, 9.0, OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, 0.05, false) -obj = HologramSurface(rect, int) -sys = CSGOpticalSystem(LensAssembly(obj), Rectangle(10.0, 10.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, -25.0), interface = opaqueinterface())) -Vis.drawtracerays(sys; raygenerator = UniformOpticalSource(CollimatedSource(GridRectOriginPoints(5, 5, 3.0, 3.0, position = SVector(0.0, 0.0, 10.0), direction = SVector(0.0, 0.0, -1.0))), 0.55), trackallrays = true, rayfilter = nothing, test = true) -Vis.save("assets/hoe_f.png") # hide -nothing # hide +```@example example +draw_HOEfocus("assets/hoe_f.png") # hide ``` ![Focusing HOE example](assets/hoe_f.png) ### Collimating +```@example highlight +mdparse(@code_string draw_HOEcollimate()) # hide +``` -```@example -using OpticSim -using StaticArrays - -rect = Rectangle(5.0, 5.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, 0.0)) -int = HologramInterface(SVector(0.1, -0.05, -1.0), CollimatedBeam, SVector(0.0, 0.0, 10), DivergingBeam, 0.55, 9.0, OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, 0.05, false) -obj = HologramSurface(rect, int) -sys = CSGOpticalSystem(LensAssembly(obj), Rectangle(10.0, 10.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, -25.0), interface = opaqueinterface())) -Vis.drawtracerays(sys; raygenerator = UniformOpticalSource(GridSource(OriginPoint{Float64}(1, position = SVector(0.0, 0.0, 10.0), direction = SVector(0.0, 0.0, -1.0)), 5, 5, π / 4, π / 4), 0.55), trackallrays = true, rayfilter = nothing, test = true) -Vis.save("assets/hoe_c.png") # hide -nothing # hide +```@example example +draw_HOEcollimate("assets/hoe_c.png") # hide ``` ![Collimating HOE example](assets/hoe_c.png) ### Multi +```@example highlight +mdparse(@code_string draw_multiHOE()) # hide +``` -```@example -using OpticSim -using StaticArrays - -rect = Rectangle(5.0, 5.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, 0.0)) -int1 = HologramInterface(SVector(-5.0, 0.0, -20.0), ConvergingBeam, SVector(0.0, -1.0, -1.0), CollimatedBeam, 0.55, 100.0, OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, 0.05, false) -int2 = HologramInterface(SVector(5.0, 0.0, -20.0), ConvergingBeam, SVector(0.0, 1.0, -1.0), CollimatedBeam, 0.55, 100.0, OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, 0.05, false) -mint = MultiHologramInterface(int1, int2) -obj = MultiHologramSurface(rect, mint) -sys = CSGOpticalSystem(LensAssembly(obj), Rectangle(10.0, 10.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, -20.0), interface = opaqueinterface())) -s1 = UniformOpticalSource(CollimatedSource(RandomRectOriginPoints(500, 3.0, 3.0, position = SVector(0.0, 3.0, 3.0), direction = SVector(0.0, -1.0, -1.0))), 0.55, sourcenum = 1) -s2 = UniformOpticalSource(CollimatedSource(RandomRectOriginPoints(500, 3.0, 3.0, position = SVector(0.0, -3.0, 3.0), direction = SVector(0.0, 1.0, -1.0))), 0.55, sourcenum = 2) -s3 = UniformOpticalSource(CollimatedSource(RandomRectOriginPoints(500, 3.0, 3.0, position = SVector(0.0, 0.0, 3.0), direction = SVector(0.0, 0.0, -1.0))), 0.55, sourcenum = 3) -Vis.drawtracerays(sys; raygenerator = s1, trackallrays = true, colorbysourcenum = true, rayfilter = nothing) -Vis.drawtracerays!(sys; raygenerator = s2, trackallrays = true, colorbysourcenum = true, rayfilter = nothing, drawgen = true) -Vis.drawtracerays!(sys; raygenerator = s3, trackallrays = true, colorbysourcenum = true, rayfilter = nothing, drawgen = true) -Vis.save("assets/hoe_m.png") # hide -nothing # hide +```@example example +draw_multiHOE("assets/hoe_m.png") # hide ``` ![Multi-HOE example](assets/hoe_m.png) ## Deterministic Raytracing +```@example highlight +mdparse(@code_string draw_stackedbeamsplitters()) # hide +``` -```@example -using OpticSim -using OpticSim.GlassCat -using OpticSim.Geometry -using StaticArrays - -function stacked_beamsplitters(interfacemode) - bs_1 = leaf( - leaf( - Cuboid(10.0, 20.0, 2.0; - interface = FresnelInterface{Float64}(SCHOTT.N_BK7, Air; - reflectance=0.5, transmission=0.5, - interfacemode=interfacemode)), - rotationX(pi/4)), - translation(0.0, 0.0, -30.0-2*sqrt(2))) - l1 = leaf(SphericalLens(SCHOTT.N_BK7, -70.0, 30.0, Inf, 5.0, 10.0), translation(0.0, -1.34, 0.0)) - bs_2 = leaf( - leaf( - Cuboid(10.0, 20.0, 2.0; - interface = FresnelInterface{Float64}(SCHOTT.N_BK7, Air; - reflectance=0.5, transmission=0.5, - interfacemode=interfacemode)), - rotationX(pi/4)), - translation(0.0, 40.0, -30.0+2*sqrt(2))) - l2 = leaf(SphericalLens(SCHOTT.N_BK7, -70.0, 30.0, Inf, 5.0, 10.0), translation(0.0, 40.0, 0.0)) - la = LensAssembly(bs_1(), l1(), bs_2(), l2()) - detector = Rectangle(20.0, 40.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 20.0, -130.0); interface = opaqueinterface()) - CSGOpticalSystem(la, detector) -end - -# nondeterministic -Vis.drawtracerays(stacked_beamsplitters(ReflectOrTransmit); trackallrays=true, rayfilter=nothing, colorbynhits=true) -Vis.save("assets/deterministic_trace_1.png") # hide -# deterministic, all beamsplitters transmissive -Vis.drawtracerays(stacked_beamsplitters(Transmit); trackallrays=true, rayfilter=nothing, colorbynhits=true) -Vis.save("assets/deterministic_trace_2.png") # hide -# deterministic, all beamsplitters reflective -Vis.drawtracerays(stacked_beamsplitters(Reflect); trackallrays=true, rayfilter=nothing, colorbynhits=true) -Vis.save("assets/deterministic_trace_3.png") # hide -nothing # hide +```@example example +draw_stackedbeamsplitters(["assets/deterministic_trace_$i.png" for i in 1:3]) # hide ``` ![Nondeterministic Raytrace](assets/deterministic_trace_1.png) diff --git a/src/Examples/Examples.jl b/src/Examples/Examples.jl index 803b7d862..3f5497a33 100644 --- a/src/Examples/Examples.jl +++ b/src/Examples/Examples.jl @@ -7,7 +7,9 @@ module Examples using ..OpticSim using ..OpticSim.Vis using ..OpticSim.Geometry -# using ..OpticSim.GlassCat use this if you want to type SCHOTT.N_BK7 rather than OpticSim.GlassCat.SCHOTT.N_BK7 +using ..OpticSim.Emitters +using ..OpticSim.GlassCat + using StaticArrays using DataFrames using Images @@ -15,377 +17,8 @@ using Unitful using Plots using LinearAlgebra -# Create a geometric hemisphere -function hemisphere()::CSGTree - sph = Sphere(10.0) - pln = Plane(0.0, 0.0, -1.0, 0.0, 0.0, 0.0) - csgintersection(sph, pln)() #csg operations create a csggenerator which instantiates the csg tree after applying a rigid body transformation. This allows you to make as many instances of the object as you want with different transformations. We just want the CSGTree object rather than a generator. -end - -# Create an optical hemisphere that has optical material properties so it will reflect and refract light. In the previous example the hemisphere object had optical properties of OpticSim.GlassCat.Air, which is the default optical interface, so it won't refract or reflect light. -function opticalhemisphere()::CSGOpticalSystem - sph = Sphere(10.0, interface = FresnelInterface{Float64}(OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air)) - pln = Plane(0.0, 0.0, -1.0, 0.0, 0.0, 0.0, interface = FresnelInterface{Float64}(OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air)) - assy = LensAssembly{Float64}(csgintersection(sph, pln)()) - return CSGOpticalSystem(assy, Rectangle(1.0, 1.0, SVector{3,Float64}(0.0, 0.0, 1.0), SVector{3,Float64}(0.0, 0.0, -11.0))) -end - -#! format: off - -cooketriplet(::Type{T} = Float64, detpix::Int = 1000) where {T<:Real} = AxisymmetricOpticalSystem{T}( - DataFrame(Surface = [:Object, 1, 2, 3, :Stop, 5, 6, :Image], - Radius = [Inf, 26.777, 66.604, -35.571, 35.571, 35.571, -26.777, Inf], - OptimizeRadius = [false,true,true,true,true,true,true,false], - Thickness = [Inf, 4.0, 2.0, 4.0, 2.0, 4.0, 44.748, missing], - OptimizeThickness = [false,true,true,true,true,true,true,false], - Material = [OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_SK16, OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_SF2, OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_SK16, OpticSim.GlassCat.Air, missing], - SemiDiameter = [Inf, 8.580, 7.513, 7.054, 6.033, 7.003, 7.506, 15.0]), detpix, detpix) -export cooketriplet - -#no longer works -cooketripletlensonly(::Type{T} = Float64) where {T<:Real} = AxisymmetricLens{T}( - DataFrame(Surface = [:Object, 1, 2, 3, :Stop, 5, 6, :Image], - Radius = [Inf, 26.777, 66.604, -35.571, 35.571, 35.571, -26.777, Inf], - OptimizeRadius = [false,true,true,true,true,true,true,false], - Thickness = [Inf, 4.0, 2.0, 4.0, 2.0, 4.0, 44.748, missing], - OptimizeThickness = [false,true,true,true,true,true,true,false], - Material = [OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_SK16, OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_SF2, OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_SK16, OpticSim.GlassCat.Air, missing], - SemiDiameter = [Inf, 8.580, 7.513, 7.054, 6.033, 7.003, 7.506, 15.0])) -export cooketripletlensonly - -cooketripletfirstelement(::Type{T} = Float64) where {T<:Real} = AxisymmetricOpticalSystem( - DataFrame(Surface = [:Object, 1, 2, :Image], - Radius = [Inf, -35.571, 35.571, Inf], - Thickness = [Inf, 4.0, 44.748, missing], - Material = [OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_SK16, OpticSim.GlassCat.Air, missing], - SemiDiameter = [Inf, 7.054, 6.033, 15.0])) - -convexplano(::Type{T} = Float64) where {T<:Real} = AxisymmetricOpticalSystem{T}( - DataFrame(Surface = [:Object, 1, 2, :Image], - Radius = [Inf, 60.0, Inf, Inf], - Thickness = [Inf, 10.0, 57.8, missing], - Material = [OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, missing], - SemiDiameter = [Inf, 9.0, 9.0, 15.0])) - -doubleconvex(frontradius::T,rearradius::T) where{T<:Real} = -AxisymmetricOpticalSystem{T}( - DataFrame(Surface = [:Object, 1, 2, :Image], - Radius = [T(Inf64), frontradius, rearradius, T(Inf64)], - OptimizeRadius = [false,true,true,false], - Thickness = [T(Inf64), T(10.0), T(57.8), missing], - OptimizeThickness = [false,false,false,false], - Material = [OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, missing], - SemiDiameter = [T(Inf64), T(9.0), T(9.0), T(15.0)])) - -doubleconvexconic(::Type{T} = Float64) where {T<:Real} = - AxisymmetricOpticalSystem{T}( - DataFrame(Surface = [:Object, 1, 2, :Image], - Radius = [Inf64, 60, -60, Inf64], - OptimizeRadius = [false,true,true,false], - Thickness = [Inf64, 10.0, 57.8, missing], - OptimizeThickness = [false,false,false,false], - Conic = [missing, 0.01, 0.01, missing], - OptimizeConic = [false, true, true, false], - Material = [OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, missing], - SemiDiameter = [Inf64, 9.0, 9.0, 15.0])) - -doubleconvexlensonly(frontradius::T,rearradius::T) where{T<:Real} = -AxisymmetricLens{T}( - DataFrame(Surface = [:Object, 1, 2, :Image], - Radius = [T(Inf64), frontradius, rearradius, T(Inf64)], - OptimizeRadius = [false,true,true,false], - Thickness = [T(Inf64), T(10.0), T(57.8), missing], - OptimizeThickness = [false,false,false,false], - Material = [OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, missing], - SemiDiameter = [T(Inf64), T(9.0), T(9.0), T(15.0)])) -export doubleconvexlensonly - -doubleconvexprescription() = DataFrame( - Surface = [:Object, 1, 2, :Image], - Radius = [Inf64, 60, -60, Inf64], - OptimizeRadius = [false,true,true,false], - Thickness = [Inf64, 10.0, 57.8, missing], - OptimizeThickness = [false,true,true,false], - Material = [OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, missing], - SemiDiameter = [Inf64, 9.0, 9.0, 15.0]) - -doubleconvex(::Type{T} = Float64; temperature::Unitful.Temperature = OpticSim.GlassCat.TEMP_REF_UNITFUL, pressure::T = T(OpticSim.GlassCat.PRESSURE_REF)) where {T<:Real} = AxisymmetricOpticalSystem{T}(doubleconvexprescription(),temperature = temperature, pressure = pressure) - -doubleconcave(::Type{T} = Float64) where {T<:Real} = AxisymmetricOpticalSystem{T}( - DataFrame(Surface = [:Object, 1, 2, :Image], - Radius = [Inf64, -41.0, 41.0, Inf64], - Thickness = [Inf64, 10.0, 57.8, missing], - Material = [OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, missing], - SemiDiameter = [Inf64, 9.0, 9.0, 15.0])) - -planoconcaverefl(::Type{T} = Float64) where {T<:Real} = AxisymmetricOpticalSystem{T}( - DataFrame(Surface = [:Object, 1, 2, :Image], - Radius = [Inf64, Inf64, -41.0, Inf64], - Thickness = [Inf64, 10.0, -57.8, missing], - Material = [OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, missing], - SemiDiameter = [Inf64, 9.0, 9.0, 25.0], - Reflectance = [missing, missing, 1.0, missing])) - -concaveplano(::Type{T} = Float64) where {T<:Real} = AxisymmetricOpticalSystem{T}( - DataFrame(Surface = [:Object, 1, 2, :Image], - Radius = [Inf64, -41.0, Inf64, Inf64], - Thickness = [Inf64, 10.0, 57.8, missing], - Material = [OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, missing], - SemiDiameter = [Inf64, 9.0, 9.0, 15.0])) - -planoplano(::Type{T} = Float64) where {T<:Real} = AxisymmetricOpticalSystem{T}( - DataFrame(Surface = [:Object, 1, 2, :Image], - Radius = [Inf64, Inf64, Inf64, Inf64], - Thickness = [Inf64, 10.0, 57.8, missing], - Material = [OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, missing], - SemiDiameter = [Inf64, 9.0, 9.0, 15.0])) - -#! format: on - -function autodrawrays(lens::AxisymmetricOpticalSystem = cooketriplet(), angle = 10; kwargs...) - f1 = HexapolarField(lens, collimated = true, wavelength = 0.45, sourcenum = 1) - Vis.drawtracerays(lens, raygenerator = f1, test = true, trackallrays = true, colorbysourcenum = true; kwargs...) - f2 = HexapolarField(lens, collimated = true, wavelength = 0.45, sourceangle = angle / 180 * π, sourcenum = 2) - Vis.drawtracerays!(lens, raygenerator = f2, test = true, trackallrays = true, colorbysourcenum = true; kwargs...) -end - -function autospotdiag(lens::AxisymmetricOpticalSystem = cooketriplet(); kwargs...) - f1 = HexapolarField(lens, collimated = true, wavelength = 0.45, sourcenum = 1) - f2 = HexapolarField(lens, collimated = true, wavelength = 0.45, sourceangle = 5 / 180 * π, sourcenum = 2) - f3 = HexapolarField(lens, collimated = true, wavelength = 0.45, sourceangle = 10 / 180 * π, sourcenum = 3) - Vis.spotdiaggrid(lens, [f1, f2, f3]; kwargs...) -end - -# Display the spot diagram of a simple cooketriplet lens -function hexapolarspotdiagramexample(lens = cooketriplet(), numrings::Int = 5, angle = 0.0) - Vis.spotdiag(lens, samples = numrings, sourceangle = angle) -end - -function cartesiangridspotdiagramexample(lens = cooketriplet(), numsamples::Int = 5, angle = 0.0) - Vis.spotdiag(lens, hexapolar = false, samples = numsamples, sourceangle = angle) -end - -function SchmidtCassegrainTelescope() - # glass entrance lens on telescope - topsurf = Plane(SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, 0.0), interface = FresnelInterface{Float64}(OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air), vishalfsizeu = 12.00075, vishalfsizev = 12.00075) - botsurf = AcceleratedParametricSurface(ZernikeSurface(12.00075, radius = -1.14659768e+4, aspherics = [(4, 3.68090959e-7), (6, 2.73643352e-11), (8, 3.20036892e-14)]), 17, interface = FresnelInterface{Float64}(OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air)) - coverlens = csgintersection(leaf(Cylinder(12.00075, 1.4)), csgintersection(leaf(topsurf), leaf(botsurf, Transform(OpticSim.rotmatd(0, 180, 0), Vec3(0.0, 0.0, -0.65))))) - # big mirror with a hole in it - bigmirror = ConicLens(OpticSim.GlassCat.SCHOTT.N_BK7, -72.65, -95.2773500000134, 0.077235, Inf, 0.0, 0.2, 12.18263, frontsurfacereflectance = 1.0) - bigmirror = csgdifference(bigmirror, leaf(Cylinder(4.0, 0.3, interface = opaqueinterface()), translation(0.0, 0.0, -72.75))) - # small mirror supported on a spider - smallmirror = SphericalLens(OpticSim.GlassCat.SCHOTT.N_BK7, -40.65, Inf, -49.6845, 1.13365, 4.3223859, backsurfacereflectance = 1.0) - obscuration1 = Circle(4.5, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, -40.649), interface = opaqueinterface()) - obscurations2 = Spider(3, 0.5, 12.0, SVector(0.0, 0.0, -40.65)) - # put it together with the detector - la = LensAssembly(coverlens(), bigmirror(), smallmirror(), obscuration1, obscurations2...) - det = Circle(3.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, -92.4542988), interface = opaqueinterface()) - - return CSGOpticalSystem(la, det) -end - -drawSchmidt(; kwargs...) = Vis.drawtracerays(SchmidtCassegrainTelescope(), raygenerator = UniformOpticalSource(CollimatedSource(GridRectOriginPoints(5, 5, 10.0, 10.0, position = SVector(0.0, 0.0, 20.0))), 0.55), trackallrays = true, colorbynhits = true, test = true, numdivisions = 100; kwargs...) - -function prism_refraction() - # build the triangular prism - int = FresnelInterface{Float64}(OpticSim.GlassCat.SCHOTT.N_SF14, OpticSim.GlassCat.Air) - s = 2.0 - prism = csgintersection(leaf(Plane(SVector(0.0, -1.0, 0.0), SVector(0.0, -s, 0.0), interface = int, vishalfsizeu = 2 * s, vishalfsizev = 2 * s)), csgintersection(Plane(SVector(0.0, sind(30), cosd(30)), SVector(0.0, s * sind(30), s * cosd(30)), interface = int, vishalfsizeu = 2 * s, vishalfsizev = 2 * s), Plane(SVector(0.0, sind(30), -cosd(30)), SVector(0.0, s * sind(30), -s * cosd(30)), interface = int, vishalfsizeu = 2 * s, vishalfsizev = 2 * s))) - sys = CSGOpticalSystem(LensAssembly(prism()), Rectangle(15.0, 15.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, -20.0), interface = opaqueinterface())) - # create some 'white' light - rays = Vector{OpticalRay{Float64,3}}(undef, 0) - for i in 0:7 - λ = ((i / 7) * 200 + 450) / 1000 - r = OpticalRay(SVector(0.0, -3.0, 10.0), SVector(0.0, 0.5, -1.0), 1.0, λ) - push!(rays, r) - end - raygen = RayListSource(rays) - # draw the result - Vis.drawtracerays(sys, raygenerator = raygen, test = true, trackallrays = true) -end - -function zoom_lens(pos = 1) - if pos == 0 - stop = 2.89 - zoom = 9.48 - dist = 4.46970613 - elseif pos == 1 - stop = 3.99 - zoom = 4.48 - dist = 21.21 - else - stop = 4.90 - zoom = 2.00 - dist = 43.81 - end - #! format: off - return AxisymmetricOpticalSystem{Float64}( - DataFrame(Surface = [:Object, :Stop, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, :Image], - Radius = [Inf64, Inf64, -1.6202203499676E+01, -4.8875855327468E+01, 1.5666614444619E+01, -4.2955326460481E+01, 1.0869565217391E+02, 2.3623907394283E+01, -1.6059097478722E+01, -4.2553191489362E+02, -3.5435861091425E+01, -1.4146272457208E+01, -2.5125628140704E+02, -2.2502250225023E+01, -1.0583130489999E+01, -4.4444444444444E+01, Inf64], - Aspherics = [missing, missing, missing, missing, missing, [(4, 1.03860000000E-04), (6, 1.42090000000E-07), (8, -8.84950000000E-09), (10, 1.24770000000E-10), (12, -1.03670000000E-12), (14, 3.65560000000E-15)], missing, missing, [(4, 4.27210000000E-05), (6, 1.24840000000E-07), (8, 9.70790000000E-09), (10, -1.84440000000E-10), (12, 1.86440000000E-12), (14, -7.79750000000E-15)], [(4, 1.13390000000E-04), (6, 4.81650000000E-07), (8, 1.87780000000E-08), (10, -5.75710000000E-10), (12, 8.99940000000E-12), (14, -4.67680000000E-14)], missing, missing, missing, missing, missing, missing, missing], - Thickness = [Inf64, 0.0, 5.18, 0.10, 4.40, 0.16, 1.0, 4.96, zoom, 4.04, 1.35, 1.0, 2.80, 3.0, 1.22, dist, missing], - Material = [OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, OpticSim.GlassCat.OHARA.S_LAH66, OpticSim.GlassCat.Air, OpticSim.GlassCat.NIKON.LLF6, OpticSim.GlassCat.Air, OpticSim.GlassCat.OHARA.S_TIH6, OpticSim.GlassCat.OHARA.S_FSL5, OpticSim.GlassCat.Air, OpticSim.GlassCat.OHARA.S_FSL5, OpticSim.GlassCat.Air, OpticSim.GlassCat.OHARA.S_LAL8, OpticSim.GlassCat.SCHOTT.S_FL4, OpticSim.GlassCat.Air, OpticSim.GlassCat.OHARA.S_LAH66, OpticSim.GlassCat.Air, missing], - SemiDiameter = [Inf64, stop, 3.85433218451, 3.85433218451, 4.36304692871, 4.36304692871, 4.72505505439, 4.72505505439, 4.72505505439, 4.45240784026, 4.45240784026, 4.50974054117, 4.50974054117, 4.50974054117, 4.76271114409, 4.76271114409, 15.0])) - #! format: on - -end - -function fresnel(convex = true; kwargs...) - lens = FresnelLens(OpticSim.GlassCat.SCHOTT.N_BK7, 0.0, convex ? 15.0 : -15.0, 1.0, 8.0, 0.8, conic = 0.1) - sys = CSGOpticalSystem(LensAssembly(lens()), Rectangle(15.0, 15.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, -25.0), interface = opaqueinterface())) - Vis.drawtracerays(sys; test = true, trackallrays = true, numdivisions = 30, kwargs...) -end - -function grating(; period = 1.0, θ = 0.0, λ = 0.55, kwargs...) - int = ThinGratingInterface(SVector(0.0, 1.0, 0.0), period, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, minorder = -2, maxorder = 2, reflectance = [0.0, 0.0, 0.1, 0.0, 0.0], transmission = [0.05, 0.1, 0.4, 0.1, 0.05]) - grating = ThinGratingSurface(Rectangle(5.0, 5.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, 0.0)), int) - back = Rectangle(30.0, 30.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, 25.0)) - sys = CSGOpticalSystem(LensAssembly(grating, back), Rectangle(30.0, 30.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, -25.0), interface = opaqueinterface())) - Vis.drawtracerays(sys; raygenerator = UniformOpticalSource(CollimatedSource(OriginPoint{Float64}(100, position = SVector(0.0, 0.0, 10.0), direction = SVector(0.0, sind(θ), -cosd(θ)))), λ), trackallrays = true, rayfilter = nothing, kwargs...) -end - -function reflgrating(; period = 1.0, θ = 0.0, λ = 0.55, kwargs...) - int = ThinGratingInterface(SVector(0.0, 1.0, 0.0), period, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, minorder = -2, maxorder = 2, transmission = [0.0, 0.0, 0.1, 0.0, 0.0], reflectance = [0.05, 0.1, 0.4, 0.1, 0.05]) - grating = ThinGratingSurface(Rectangle(5.0, 5.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, 0.0)), int) - back = Rectangle(30.0, 30.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, -25.0)) - sys = CSGOpticalSystem(LensAssembly(grating, back), Rectangle(30.0, 30.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, 25.0), interface = opaqueinterface())) - Vis.drawtracerays(sys; raygenerator = UniformOpticalSource(CollimatedSource(OriginPoint{Float64}(100, position = SVector(0.0, 0.0, 10.0), direction = SVector(0.0, sind(θ), -cosd(θ)))), λ), trackallrays = true, rayfilter = nothing, kwargs...) -end - -function HOE(refl = false, firstorderonly = false; kwargs...) - rect = Rectangle(5.0, 5.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, 0.0)) - if refl - int = HologramInterface(SVector(0.0, -10.0, 20.0), ConvergingBeam, SVector(0.0, 0.0, -200), ConvergingBeam, 0.55, 9.0, OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, 0.05, !firstorderonly) - else - int = HologramInterface(SVector(0.0, -10.0, -20.0), ConvergingBeam, SVector(0.0, 0.0, -200), ConvergingBeam, 0.55, 5.0, OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, 0.05, !firstorderonly) - end - obj = HologramSurface(rect, int) - back = Rectangle(50.0, 50.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, 25.0)) - sys = CSGOpticalSystem(LensAssembly(obj, back), Rectangle(50.0, 50.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, -25.0), interface = opaqueinterface())) - Vis.drawtracerays(sys; raygenerator = UniformOpticalSource(GridSource(OriginPoint{Float64}(10, position = SVector(0.0, 0.0, 10.0), direction = SVector(0.0, 0.0, -1.0)), 1, 15, 0.0, π / 6), 0.55), trackallrays = true, rayfilter = nothing, kwargs...) -end - -function HOEfocus(; kwargs...) - rect = Rectangle(5.0, 5.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, 0.0)) - int = HologramInterface(SVector(0.0, -3.0, -20.0), ConvergingBeam, SVector(0.0, 0.0, -1.0), CollimatedBeam, 0.55, 9.0, OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, 0.05, false) - obj = HologramSurface(rect, int) - sys = CSGOpticalSystem(LensAssembly(obj), Rectangle(10.0, 10.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, -25.0), interface = opaqueinterface())) - Vis.drawtracerays(sys; raygenerator = UniformOpticalSource(CollimatedSource(GridRectOriginPoints(5, 5, 3.0, 3.0, position = SVector(0.0, 0.0, 10.0), direction = SVector(0.0, 0.0, -1.0))), 0.55), trackallrays = true, rayfilter = nothing, test = true, kwargs...) -end - -function HOEcollimate(; kwargs...) - rect = Rectangle(5.0, 5.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, 0.0)) - int = HologramInterface(SVector(0.1, -0.05, -1.0), CollimatedBeam, SVector(0.0, 0.0, 10), DivergingBeam, 0.55, 9.0, OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, 0.05, false) - obj = HologramSurface(rect, int) - sys = CSGOpticalSystem(LensAssembly(obj), Rectangle(10.0, 10.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, -25.0), interface = opaqueinterface())) - Vis.drawtracerays(sys; raygenerator = UniformOpticalSource(GridSource(OriginPoint{Float64}(1, position = SVector(0.0, 0.0, 10.0), direction = SVector(0.0, 0.0, -1.0)), 5, 5, π / 4, π / 4), 0.55), trackallrays = true, rayfilter = nothing, test = true, kwargs...) -end - -function eyetrackHOE(nrays = 5000, det = false, showhead = true, zeroorder = false; kwargs...) - # TODO update for new specs from Chris - hoehalfwidth = 50.0 #25.0 - hoehalfheight = 50.0 #22.5 - hoecenter = SVector(-8.0 - 25.0, 0.0, -10.0 - 25.0) - rect = Rectangle(hoehalfheight, hoehalfwidth, SVector(0.0, 1.0, 0.0), hoecenter) - er = 15.0 - cornea_rad = 7.85 - corneavertex = SVector(0.0, er, 0.0) - sourceloc = SVector(-33.0, er, 0.0) - camloc = SVector(20.0, 3.0, -11.0) - camdir = corneavertex - camloc - camdir_norm = normalize(camdir) - - interfaces = [] - - # offset = SVector(-5.0, 10.0, -10.0) - # for θ in 0:(π / 6):(2π) - # ledloc = SVector(20 * cos(θ) + offset[1], 0 + offset[2], 15 * sin(θ) + offset[3]) - # int = HologramInterface(ledloc, ConvergingBeam, sourceloc, DivergingBeam, 0.78, 100.0, OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, 0.05, zeroorder) - # push!(interfaces, int) - # end - - dirs = [SVector(0.7713, 0.6350, -0.0437), SVector(0.5667, 0.8111, -0.1445), SVector(0.3400, 0.9349, -0.1017), SVector(0.1492, 0.9878, 0.0445), SVector(0.0249, 0.9686, 0.2474), SVector(-0.0184, 0.8855, 0.4643), SVector(0.0254, 0.7537, 0.6567), SVector(0.1548, 0.5964, 0.7876), SVector(0.3570, 0.4462, 0.8207), SVector(0.5959, 0.3470, 0.7242), SVector(0.7976, 0.3449, 0.4948), SVector(0.8680, 0.4555, 0.1978)] - - for d in dirs - int = HologramInterface(normalize(d), CollimatedBeam, sourceloc, DivergingBeam, 0.78, 100.0, OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, 0.05, zeroorder) - # int = HologramInterface(corneavertex - 10 * d, ConvergingBeam, sourceloc, DivergingBeam, 0.78, 100.0, OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, 0.05, zeroorder) - push!(interfaces, int) - end - - mint = MultiHologramInterface(interfaces...) - obj = MultiHologramSurface(rect, mint) - cornea = leaf(Sphere(cornea_rad, interface = FresnelInterface{Float64}(OpticSim.GlassCat.EYE.CORNEA, OpticSim.GlassCat.Air, reflectance = 1.0, transmission = 0.0)), translation(0.0, er + cornea_rad, 0.0))() - - # cam settings - fnum = 2.0 - fov = 80 - sensorrad = 1.0 - barrellength = sensorrad / tand(fov / 2) - aprad = barrellength / fnum / 2 - camrad = max(sensorrad, aprad) - - camap = Annulus(aprad, camrad, camdir_norm, camloc) - distfromcamtoeye = norm(camdir) - focallength = 1 / (1 / distfromcamtoeye + 1 / barrellength) - camlens = ParaxialLensEllipse(focallength, aprad, aprad, -camdir_norm, camloc) - barrelloc = camloc - barrellength / 2 * camdir_norm - barreltop = Plane(camdir_norm, camloc) - barrelbot = Plane(-camdir_norm, camloc - 3 * barrellength * camdir_norm) - barrelrot = OpticSim.rotmatbetween(SVector(0.0, 0.0, 1.0), camdir_norm) - cambarrel = csgintersection(barrelbot, csgintersection(barreltop, leaf(Cylinder(camrad, barrellength, interface = opaqueinterface(Float64)), Transform(barrelrot, barrelloc))))() - camdet = Circle(sensorrad, camdir_norm, camloc - barrellength * camdir_norm, interface = opaqueinterface(Float64)) - - # sourceleft = hoecenter[1] + hoehalfwidth - sourceloc[1] - # sourceright = hoecenter[1] - hoehalfwidth - sourceloc[1] - # sourceleftθ = atan(sourceleft, sourceloc[2]) - # sourcerightθ = atan(sourceright, sourceloc[2]) - # midθ = (sourceleftθ + sourcerightθ) / 2 - # sourcedir = normalize(SVector(er * tan(midθ), -er, 0.0)) - # sourceextentθ = abs(midθ - sourcerightθ) - # source = CosineOpticalSource(RandomSource(OriginPoint{Float64}(1, position = sourceloc, direction = sourcedir), nrays, sourceextentθ), 1.0, 0.78) - - rays = Vector{OpticalRay{Float64,3}}(undef, nrays) - @simd for i in 1:nrays - p = point(rect, rand() * 2 - 1, rand() * 2 - 1) - rays[i] = OpticalRay(sourceloc, p - sourceloc, 1.0, 0.78) - end - source = RayListSource(rays) - - sys = CSGOpticalSystem(LensAssembly(obj, cornea, camlens, cambarrel, camap), camdet, 800, 800) - if det - Vis.show(OpticSim.traceMT(sys, source)) - else - Vis.drawtracerays(sys; raygenerator = source, trackallrays = true, kwargs...) - # for θ in 0:(π / 6):(2π) - # ledloc = SVector(20 * cos(θ) + offset[1], 0 + offset[2], 15 * sin(θ) + offset[3]) - # Vis.draw!(leaf(Sphere(1.0), translation(ledloc...)), color = :red) - # end - for d in dirs - # Vis.draw!(leaf(Sphere(1.0), translation((corneavertex - 10 * d)...)), color = :red) - Vis.draw!((corneavertex - 50 * d, corneavertex), color = :red) - end - if showhead - Vis.draw!(joinpath(@__DIR__, "../../OBJ/glasses.obj"), scale = 100.0, transform = Transform(OpticSim.rotmatd(90, 0, 0), [27.0, 45.0, -8.0]), color = :black) - Vis.draw!(joinpath(@__DIR__, "../../OBJ/femalehead.obj"), scale = 13.0, transform = Transform(OpticSim.rotmatd(0, 0, 180), [27.0, 105.0, -148.0]), color = :white) - end - Vis.display() - end -end - -function multiHOE(; kwargs...) - rect = Rectangle(5.0, 5.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, 0.0)) - int1 = HologramInterface(SVector(-5.0, 0.0, -20.0), ConvergingBeam, SVector(0.0, -1.0, -1.0), CollimatedBeam, 0.55, 100.0, OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, 0.05, false) - int2 = HologramInterface(SVector(5.0, 0.0, -20.0), ConvergingBeam, SVector(0.0, 1.0, -1.0), CollimatedBeam, 0.55, 100.0, OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, 0.05, false) - mint = MultiHologramInterface(int1, int2) - obj = MultiHologramSurface(rect, mint) - sys = CSGOpticalSystem(LensAssembly(obj), Rectangle(10.0, 10.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, -20.0), interface = opaqueinterface())) - s1 = UniformOpticalSource(CollimatedSource(RandomRectOriginPoints(500, 3.0, 3.0, position = SVector(0.0, 3.0, 3.0), direction = SVector(0.0, -1.0, -1.0))), 0.55, sourcenum = 1) - s2 = UniformOpticalSource(CollimatedSource(RandomRectOriginPoints(500, 3.0, 3.0, position = SVector(0.0, -3.0, 3.0), direction = SVector(0.0, 1.0, -1.0))), 0.55, sourcenum = 2) - s3 = UniformOpticalSource(CollimatedSource(RandomRectOriginPoints(500, 3.0, 3.0, position = SVector(0.0, 0.0, 3.0), direction = SVector(0.0, 0.0, -1.0))), 0.55, sourcenum = 3) - Vis.drawtracerays(sys; raygenerator = s1, trackallrays = true, colorbysourcenum = true, rayfilter = nothing, kwargs...) - Vis.drawtracerays!(sys; raygenerator = s2, trackallrays = true, colorbysourcenum = true, rayfilter = nothing, drawgen = true, kwargs...) - Vis.drawtracerays!(sys; raygenerator = s3, trackallrays = true, colorbysourcenum = true, rayfilter = nothing, drawgen = true, kwargs...) -end +include("docs.jl") +include("other.jl") end #module Examples export Examples diff --git a/src/Examples/docs.jl b/src/Examples/docs.jl new file mode 100644 index 000000000..b389dd3fc --- /dev/null +++ b/src/Examples/docs.jl @@ -0,0 +1,255 @@ +# MIT license +# Copyright (c) Microsoft Corporation. All rights reserved. +# See LICENSE in the project root for full license information. + +# Group examples that are used in the docs (examples.md) +export draw_cooketriplet, draw_schmidtcassegraintelescope, draw_lensconstruction, draw_zoomlenses, draw_HOEfocus, draw_HOEcollimate, draw_multiHOE, draw_stackedbeamsplitters + +function draw_cooketriplet(filename::Maybe{AbstractString} = nothing) + g1, g2 = SCHOTT.N_SK16, SCHOTT.N_SF2 + sys = AxisymmetricOpticalSystem{Float64}(DataFrame( + Surface = [:Object, 1, 2, 3, :Stop, 5, 6, :Image ], + Radius = [Inf, 26.777, 66.604, -35.571, 35.571, 35.571, -26.777, Inf ], + Thickness = [Inf, 4.0, 2.0, 4.0, 2.0, 4.0, 44.748, missing], + Material = [Air, g1, Air, g2, Air, g1, Air, missing], + SemiDiameter = [Inf, 8.580, 7.513, 7.054, 6.033, 7.003, 7.506, 15.0 ], + )) + + origins = Origins.Hexapolar(8, 15.0, 15.0) + directions = Directions.Constant(0.0, 0.0, -1.0) + s1 = Sources.Source(; origins, directions, sourcenum=1) + + transform = Transform(rotmatd(10, 0, 0), unitZ3()) + s2 = Sources.Source(; transform, origins, directions, sourcenum=2) + + raygenerator = Sources.CompositeSource(Transform(), [s1, s2]) + + trackallrays = test = colorbysourcenum = true; resolution = (1000, 700) + Vis.drawtracerays(sys; raygenerator, trackallrays, test, colorbysourcenum, resolution) + Vis.make2dy(); Vis.save(filename) + return sys +end + +function draw_zoomlenses(filenames::MaybeVector{AbstractString} = cycle([nothing])) + stops = [2.89, 3.99, 4.90] + zooms = [9.48, 4.48, 2.00] + dists = [4.46970613, 21.21, 43.81] + + transform = translation(0.0, 0.0, 10.0) + origins = Origins.Hexapolar(8, 10.0, 10.0) + directions = Directions.Constant(0.0, 0.0, -1.0) + raygenerator = Sources.Source(; transform, origins, directions) + + syss = [ + AxisymmetricOpticalSystem{Float64}(DataFrame( + Surface = [:Object, :Stop, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, :Image], + Radius = [Inf64, Inf64, -1.6202203499676E+01, -4.8875855327468E+01, 1.5666614444619E+01, -4.2955326460481E+01, 1.0869565217391E+02, 2.3623907394283E+01, -1.6059097478722E+01, -4.2553191489362E+02, -3.5435861091425E+01, -1.4146272457208E+01, -2.5125628140704E+02, -2.2502250225023E+01, -1.0583130489999E+01, -4.4444444444444E+01, Inf64], + Aspherics = [missing, missing, missing, missing, missing, [(4, 1.0386E-04), (6, 1.4209E-07), (8, -8.8495E-09), (10, 1.2477E-10), (12, -1.0367E-12), (14, 3.6556E-15)], missing, missing, [(4, 4.2721E-05), (6, 1.2484E-07), (8, 9.7079E-09), (10, -1.8444E-10), (12, 1.8644E-12), (14, -7.7975E-15)], [(4, 1.1339E-04), (6, 4.8165E-07), (8, 1.8778E-08), (10, -5.7571E-10), (12, 8.9994E-12), (14, -4.6768E-14)], missing, missing, missing, missing, missing, missing, missing], + Thickness = [Inf64, 0.0, 5.18, 0.10, 4.40, 0.16, 1.0, 4.96, zoom, 4.04, 1.35, 1.0, 2.80, 3.0, 1.22, dist, missing], + Material = [Air, Air, OHARA.S_LAH66, Air, NIKON.LLF6, Air, OHARA.S_TIH6, OHARA.S_FSL5, Air, OHARA.S_FSL5, Air, OHARA.S_LAL8, OHARA.S_FSL5, Air, OHARA.S_LAH66, Air, missing], + SemiDiameter = [Inf64, stop, 3.85433218451, 3.85433218451, 4.36304692871, 4.36304692871, 4.72505505439, 4.72505505439, 4.72505505439, 4.45240784026, 4.45240784026, 4.50974054117, 4.50974054117, 4.50974054117, 4.76271114409, 4.76271114409, 15.0])) + for (stop, zoom, dist) in zip(stops, zooms, dists)] + + for (sys, filename) in zip(syss, filenames) + Vis.drawtracerays(sys; raygenerator, trackallrays=true, test=true, numdivisions=50, resolution=(1200, 600)) + Vis.make2dy(); Vis.save(filename) + end + return syss +end + +function draw_schmidtcassegraintelescope(filename::Maybe{AbstractString} = nothing) + # glass entrance lens on telescope + topsurf = Plane( + SVector(0.0, 0.0, 1.0), + SVector(0.0, 0.0, 0.0), + interface = FresnelInterface{Float64}(SCHOTT.N_BK7, Air), + vishalfsizeu = 12.00075, + vishalfsizev = 12.00075) + botsurf = AcceleratedParametricSurface(ZernikeSurface( + 12.00075, + radius = -1.14659768e+4, + aspherics = [(4, 3.68090959e-7), (6, 2.73643352e-11), (8, 3.20036892e-14)]), + 17, + interface = FresnelInterface{Float64}(SCHOTT.N_BK7, Air)) + coverlens = csgintersection( + leaf(Cylinder(12.00075, 1.4)), + csgintersection( + leaf(topsurf), + leaf(botsurf, Transform(rotmatd(0, 180, 0), Vec3(0.0, 0.0, -0.65))))) + + # big mirror with a hole in it + bigmirror = csgdifference( + ConicLens(SCHOTT.N_BK7, -72.65, -95.2773500000134, 0.077235, Inf, 0.0, 0.2, 12.18263, frontsurfacereflectance = 1.0), + leaf(Cylinder(4.0, 0.3, interface = opaqueinterface()), translation(0.0, 0.0, -72.75))) + + # small mirror supported on a spider + smallmirror = SphericalLens(SCHOTT.N_BK7, -40.65, Inf, -49.6845, 1.13365, 4.3223859, backsurfacereflectance = 1.0) + + obscuration1 = Circle(4.5, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, -40.649), interface = opaqueinterface()) + obscurations2 = Spider(3, 0.5, 12.0, SVector(0.0, 0.0, -40.65)) + + # put it together with the detector + la = LensAssembly(coverlens(), bigmirror(), smallmirror(), obscuration1, obscurations2...) + det = Circle(3.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, -92.4542988), interface = opaqueinterface()) + sys = CSGOpticalSystem(la, det) + + # define ray generator + transform = translation(0.0, 0.0, 10.0) + origins = Origins.Hexapolar(8, 20.0, 20.0) + directions = Directions.Constant(0.0, 0.0, -1.0) + raygenerator = Sources.Source(; transform, origins, directions) + + # draw and output + Vis.drawtracerays(sys; raygenerator, trackallrays = true, colorbynhits = true, test = true, numdivisions = 100, drawgen = false) + Vis.save(filename) + return nothing +end + +function draw_lensconstruction(filename::Maybe{AbstractString} = nothing) + topsurface = leaf( + AcceleratedParametricSurface( + QTypeSurface( + 9.0, + radius = -25.0, + conic = 0.3, + αcoeffs = [(1, 0, 0.3), (1, 1, 1.0)], + βcoeffs = [(1, 0, -0.1), (2, 0, 0.4), (3, 0, -0.6)], + normradius = 9.5), + interface = FresnelInterface{Float64}(SCHOTT.N_BK7, Air)), + translation(0.0, 0.0, 5.0)) + botsurface = leaf( + Plane( + SVector(0.0, 0.0, -1.0), + SVector(0.0, 0.0, -5.0), + vishalfsizeu = 9.5, + vishalfsizev = 9.5, + interface = FresnelInterface{Float64}(SCHOTT.N_BK7, Air))) + barrel = leaf( + Cylinder( + 9.0, + 20.0, + interface = FresnelInterface{Float64}(SCHOTT.N_BK7, Air, reflectance = 0.0, transmission = 0.0))) + lens = csgintersection( + barrel, + csgintersection(topsurface, botsurface), + Transform{Float64}(0.0, Float64(π), 0.0, 0.0, 0.0, -5.0))() + sys = CSGOpticalSystem( + LensAssembly(lens), + Rectangle(15.0, 15.0, [0.0, 0.0, 1.0], [0.0, 0.0, -67.8], interface = opaqueinterface())) + + Vis.drawtracerays(sys, test = true, trackallrays = true, colorbynhits = true) + Vis.save(filename) + return nothing +end + +function draw_HOEfocus(filename::Maybe{AbstractString} = nothing) + rect = Rectangle(5.0, 5.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, 0.0)) + int = HologramInterface( + SVector(0.0, -3.0, -20.0), ConvergingBeam, + SVector(0.0, 0.0, -1.0), CollimatedBeam, + 0.55, 9.0, Air, SCHOTT.N_BK7, Air, Air, Air, 0.05, false) + obj = HologramSurface(rect, int) + sys = CSGOpticalSystem( + LensAssembly(obj), + Rectangle(10.0, 10.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, -25.0), + interface = opaqueinterface())) + + raygenerator = Sources.Source(; + transform = translation(0.0, 0.0, 10.0), + spectrum = Spectrum.DeltaFunction(0.55), + origins = Origins.RectGrid(3.0, 3.0, 5, 5), + directions = Directions.Constant(0.0, 0.0, -1.0)) + + Vis.drawtracerays(sys; raygenerator, trackallrays = true, rayfilter = nothing, test = true) + Vis.save(filename) + return nothing +end + +function draw_HOEcollimate(filename::Maybe{AbstractString} = nothing) + rect = Rectangle(5.0, 5.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, 0.0)) + int = HologramInterface( + SVector(0.1, -0.05, -1.0), CollimatedBeam, + SVector(0.0, 0.0, 10), DivergingBeam, + 0.55, 9.0, Air, SCHOTT.N_BK7, Air, Air, Air, 0.05, false) + obj = HologramSurface(rect, int) + sys = CSGOpticalSystem( + LensAssembly(obj), + Rectangle(10.0, 10.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, -25.0), + interface = opaqueinterface())) + + raygenerator = Sources.Source( + transform = Transform(rotmatd(180, 0, 0), Vec3(0.0, 0.0, 10.0)), + spectrum = Spectrum.DeltaFunction(0.55), + origins = Origins.Point(), + directions = Directions.RectGrid(π/4, π/4, 8, 8)) + + Vis.drawtracerays(sys; raygenerator, trackallrays = true, rayfilter = nothing, test = true) + Vis.save(filename) + return nothing +end + +function draw_multiHOE(filename::Maybe{AbstractString} = nothing) + rect = Rectangle(5.0, 5.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, 0.0)) + int1 = HologramInterface( + SVector(-5.0, 0.0, -20.0), ConvergingBeam, + SVector(0.0, -1.0, -1.0), CollimatedBeam, + 0.55, 100.0, Air, SCHOTT.N_BK7, Air, Air, Air, 0.05, false) + int2 = HologramInterface( + SVector(5.0, 0.0, -20.0), ConvergingBeam, + SVector(0.0, 1.0, -1.0), CollimatedBeam, + 0.55, 100.0, Air, SCHOTT.N_BK7, Air, Air, Air, 0.05, false) + mint = MultiHologramInterface(int1, int2) + obj = MultiHologramSurface(rect, mint) + sys = CSGOpticalSystem( + LensAssembly(obj), + Rectangle(10.0, 10.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, -20.0), interface = opaqueinterface())) + + spectrum = Spectrum.DeltaFunction(0.55) + origins = Origins.RectUniform(3.0, 3.0, 500) + directions = Directions.Constant(0.0, 0.0, -1.0) + s1 = Sources.Source(; spectrum, origins, directions, sourcenum = 1, + transform = Transform(rotmatd(-45, 0, 0), Vec3(0.0, 3.0, 3.0))) + s2 = Sources.Source(; spectrum, origins, directions, sourcenum = 2, + transform = Transform(rotmatd(45, 0, 0), Vec3(0.0, -3.0, 3.0))) + s3 = Sources.Source(; spectrum, origins, directions, sourcenum = 3, + transform = translation(0.0, 0.0, 3.0)) + raygenerator = Sources.CompositeSource(Transform(), [s1, s2, s3]) + + Vis.drawtracerays(sys; raygenerator, trackallrays = true, colorbysourcenum = true, rayfilter = nothing, drawgen = true) + Vis.save(filename) + return nothing +end + +function draw_stackedbeamsplitters(filenames::MaybeVector{AbstractString} = cycle([nothing])) + # ReflectOrTransmit: nondeterministic + # Transmit: deterministic, all beamsplitters transmissive + # Reflect: deterministic, all beamsplitters reflective + interfacemodes = [ReflectOrTransmit, Transmit, Reflect] + + for (interfacemode, filename) in zip(interfacemodes, filenames) + interface = FresnelInterface{Float64}(SCHOTT.N_BK7, Air; reflectance=0.5, transmission=0.5, interfacemode) + bs_1 = leaf( + leaf( + Cuboid(10.0, 20.0, 2.0; interface), + rotationX(π/4)), + translation(0.0, 0.0, -30.0-2*sqrt(2))) + l1 = leaf( + SphericalLens(SCHOTT.N_BK7, -70.0, 30.0, Inf, 5.0, 10.0), + translation(0.0, -1.34, 0.0)) + bs_2 = leaf( + leaf( + Cuboid(10.0, 20.0, 2.0; interface), + rotationX(π/4)), + translation(0.0, 40.0, -30.0+2*sqrt(2))) + l2 = leaf( + SphericalLens(SCHOTT.N_BK7, -70.0, 30.0, Inf, 5.0, 10.0), + translation(0.0, 40.0, 0.0)) + la = LensAssembly(bs_1(), l1(), bs_2(), l2()) + detector = Rectangle(20.0, 40.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 20.0, -130.0); interface = opaqueinterface()) + sys = CSGOpticalSystem(la, detector) + + Vis.drawtracerays(sys; trackallrays=true, rayfilter=nothing, colorbynhits=true) + Vis.save(filename) + end + return nothing +end diff --git a/src/Examples/other.jl b/src/Examples/other.jl new file mode 100644 index 000000000..5e9c7c031 --- /dev/null +++ b/src/Examples/other.jl @@ -0,0 +1,286 @@ +# MIT license +# Copyright (c) Microsoft Corporation. All rights reserved. +# See LICENSE in the project root for full license information. + +export cooketriplet, doubleconvexlensonly + +# Create a geometric hemisphere +function hemisphere()::CSGTree + sph = Sphere(10.0) + pln = Plane(0.0, 0.0, -1.0, 0.0, 0.0, 0.0) + csgintersection(sph, pln)() #csg operations create a csggenerator which instantiates the csg tree after applying a rigid body transformation. This allows you to make as many instances of the object as you want with different transformations. We just want the CSGTree object rather than a generator. +end + +# Create an optical hemisphere that has optical material properties so it will reflect and refract light. In the previous example the hemisphere object had optical properties of OpticSim.GlassCat.Air, which is the default optical interface, so it won't refract or reflect light. +function opticalhemisphere()::CSGOpticalSystem + sph = Sphere(10.0, interface = FresnelInterface{Float64}(OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air)) + pln = Plane(0.0, 0.0, -1.0, 0.0, 0.0, 0.0, interface = FresnelInterface{Float64}(OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air)) + assy = LensAssembly{Float64}(csgintersection(sph, pln)()) + return CSGOpticalSystem(assy, Rectangle(1.0, 1.0, SVector{3,Float64}(0.0, 0.0, 1.0), SVector{3,Float64}(0.0, 0.0, -11.0))) +end + +#! format: off + +cooketriplet(::Type{T} = Float64, detpix::Int = 1000) where {T<:Real} = AxisymmetricOpticalSystem{T}( + DataFrame(Surface = [:Object, 1, 2, 3, :Stop, 5, 6, :Image], + Radius = [Inf, 26.777, 66.604, -35.571, 35.571, 35.571, -26.777, Inf], + OptimizeRadius = [false,true,true,true,true,true,true,false], + Thickness = [Inf, 4.0, 2.0, 4.0, 2.0, 4.0, 44.748, missing], + OptimizeThickness = [false,true,true,true,true,true,true,false], + Material = [OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_SK16, OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_SF2, OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_SK16, OpticSim.GlassCat.Air, missing], + SemiDiameter = [Inf, 8.580, 7.513, 7.054, 6.033, 7.003, 7.506, 15.0]), detpix, detpix) + +cooketripletfirstelement(::Type{T} = Float64) where {T<:Real} = AxisymmetricOpticalSystem( + DataFrame(Surface = [:Object, 1, 2, :Image], + Radius = [Inf, -35.571, 35.571, Inf], + Thickness = [Inf, 4.0, 44.748, missing], + Material = [OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_SK16, OpticSim.GlassCat.Air, missing], + SemiDiameter = [Inf, 7.054, 6.033, 15.0])) + +convexplano(::Type{T} = Float64) where {T<:Real} = AxisymmetricOpticalSystem{T}( + DataFrame(Surface = [:Object, 1, 2, :Image], + Radius = [Inf, 60.0, Inf, Inf], + Thickness = [Inf, 10.0, 57.8, missing], + Material = [OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, missing], + SemiDiameter = [Inf, 9.0, 9.0, 15.0])) + +doubleconvex(frontradius::T,rearradius::T) where{T<:Real} = +AxisymmetricOpticalSystem{T}( + DataFrame(Surface = [:Object, 1, 2, :Image], + Radius = [T(Inf64), frontradius, rearradius, T(Inf64)], + OptimizeRadius = [false,true,true,false], + Thickness = [T(Inf64), T(10.0), T(57.8), missing], + OptimizeThickness = [false,false,false,false], + Material = [OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, missing], + SemiDiameter = [T(Inf64), T(9.0), T(9.0), T(15.0)])) + +doubleconvexconic(::Type{T} = Float64) where {T<:Real} = + AxisymmetricOpticalSystem{T}( + DataFrame(Surface = [:Object, 1, 2, :Image], + Radius = [Inf64, 60, -60, Inf64], + OptimizeRadius = [false,true,true,false], + Thickness = [Inf64, 10.0, 57.8, missing], + OptimizeThickness = [false,false,false,false], + Conic = [missing, 0.01, 0.01, missing], + OptimizeConic = [false, true, true, false], + Material = [OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, missing], + SemiDiameter = [Inf64, 9.0, 9.0, 15.0])) + +doubleconvexlensonly(frontradius::T,rearradius::T) where{T<:Real} = +AxisymmetricLens{T}( + DataFrame(Surface = [:Object, 1, 2, :Image], + Radius = [T(Inf64), frontradius, rearradius, T(Inf64)], + OptimizeRadius = [false,true,true,false], + Thickness = [T(Inf64), T(10.0), T(57.8), missing], + OptimizeThickness = [false,false,false,false], + Material = [OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, missing], + SemiDiameter = [T(Inf64), T(9.0), T(9.0), T(15.0)])) + +doubleconvexprescription() = DataFrame( + Surface = [:Object, 1, 2, :Image], + Radius = [Inf64, 60, -60, Inf64], + OptimizeRadius = [false,true,true,false], + Thickness = [Inf64, 10.0, 57.8, missing], + OptimizeThickness = [false,true,true,false], + Material = [OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, missing], + SemiDiameter = [Inf64, 9.0, 9.0, 15.0]) + +doubleconvex(::Type{T} = Float64; temperature::Unitful.Temperature = OpticSim.GlassCat.TEMP_REF_UNITFUL, pressure::T = T(OpticSim.GlassCat.PRESSURE_REF)) where {T<:Real} = AxisymmetricOpticalSystem{T}(doubleconvexprescription(),temperature = temperature, pressure = pressure) + +doubleconcave(::Type{T} = Float64) where {T<:Real} = AxisymmetricOpticalSystem{T}( + DataFrame(Surface = [:Object, 1, 2, :Image], + Radius = [Inf64, -41.0, 41.0, Inf64], + Thickness = [Inf64, 10.0, 57.8, missing], + Material = [OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, missing], + SemiDiameter = [Inf64, 9.0, 9.0, 15.0])) + +planoconcaverefl(::Type{T} = Float64) where {T<:Real} = AxisymmetricOpticalSystem{T}( + DataFrame(Surface = [:Object, 1, 2, :Image], + Radius = [Inf64, Inf64, -41.0, Inf64], + Thickness = [Inf64, 10.0, -57.8, missing], + Material = [OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, missing], + SemiDiameter = [Inf64, 9.0, 9.0, 25.0], + Reflectance = [missing, missing, 1.0, missing])) + +concaveplano(::Type{T} = Float64) where {T<:Real} = AxisymmetricOpticalSystem{T}( + DataFrame(Surface = [:Object, 1, 2, :Image], + Radius = [Inf64, -41.0, Inf64, Inf64], + Thickness = [Inf64, 10.0, 57.8, missing], + Material = [OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, missing], + SemiDiameter = [Inf64, 9.0, 9.0, 15.0])) + +planoplano(::Type{T} = Float64) where {T<:Real} = AxisymmetricOpticalSystem{T}( + DataFrame(Surface = [:Object, 1, 2, :Image], + Radius = [Inf64, Inf64, Inf64, Inf64], + Thickness = [Inf64, 10.0, 57.8, missing], + Material = [OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, missing], + SemiDiameter = [Inf64, 9.0, 9.0, 15.0])) + +#! format: on + +function autodrawrays(lens::AxisymmetricOpticalSystem = cooketriplet(), angle = 10; kwargs...) + f1 = HexapolarField(lens, collimated = true, wavelength = 0.45, sourcenum = 1) + Vis.drawtracerays(lens, raygenerator = f1, test = true, trackallrays = true, colorbysourcenum = true; kwargs...) + f2 = HexapolarField(lens, collimated = true, wavelength = 0.45, sourceangle = angle / 180 * π, sourcenum = 2) + Vis.drawtracerays!(lens, raygenerator = f2, test = true, trackallrays = true, colorbysourcenum = true; kwargs...) +end + +function autospotdiag(lens::AxisymmetricOpticalSystem = cooketriplet(); kwargs...) + f1 = HexapolarField(lens, collimated = true, wavelength = 0.45, sourcenum = 1) + f2 = HexapolarField(lens, collimated = true, wavelength = 0.45, sourceangle = 5 / 180 * π, sourcenum = 2) + f3 = HexapolarField(lens, collimated = true, wavelength = 0.45, sourceangle = 10 / 180 * π, sourcenum = 3) + Vis.spotdiaggrid(lens, [f1, f2, f3]; kwargs...) +end + +# Display the spot diagram of a simple cooketriplet lens +function hexapolarspotdiagramexample(lens = cooketriplet(), numrings::Int = 5, angle = 0.0) + Vis.spotdiag(lens, samples = numrings, sourceangle = angle) +end + +function cartesiangridspotdiagramexample(lens = cooketriplet(), numsamples::Int = 5, angle = 0.0) + Vis.spotdiag(lens, hexapolar = false, samples = numsamples, sourceangle = angle) +end + +function prism_refraction() + # build the triangular prism + int = FresnelInterface{Float64}(OpticSim.GlassCat.SCHOTT.N_SF14, OpticSim.GlassCat.Air) + s = 2.0 + prism = csgintersection(leaf(Plane(SVector(0.0, -1.0, 0.0), SVector(0.0, -s, 0.0), interface = int, vishalfsizeu = 2 * s, vishalfsizev = 2 * s)), csgintersection(Plane(SVector(0.0, sind(30), cosd(30)), SVector(0.0, s * sind(30), s * cosd(30)), interface = int, vishalfsizeu = 2 * s, vishalfsizev = 2 * s), Plane(SVector(0.0, sind(30), -cosd(30)), SVector(0.0, s * sind(30), -s * cosd(30)), interface = int, vishalfsizeu = 2 * s, vishalfsizev = 2 * s))) + sys = CSGOpticalSystem(LensAssembly(prism()), Rectangle(15.0, 15.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, -20.0), interface = opaqueinterface())) + # create some 'white' light + rays = Vector{OpticalRay{Float64,3}}(undef, 0) + for i in 0:7 + λ = ((i / 7) * 200 + 450) / 1000 + r = OpticalRay(SVector(0.0, -3.0, 10.0), SVector(0.0, 0.5, -1.0), 1.0, λ) + push!(rays, r) + end + raygen = RayListSource(rays) + # draw the result + Vis.drawtracerays(sys, raygenerator = raygen, test = true, trackallrays = true) +end + +function fresnel(convex = true; kwargs...) + lens = FresnelLens(OpticSim.GlassCat.SCHOTT.N_BK7, 0.0, convex ? 15.0 : -15.0, 1.0, 8.0, 0.8, conic = 0.1) + sys = CSGOpticalSystem(LensAssembly(lens()), Rectangle(15.0, 15.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, -25.0), interface = opaqueinterface())) + Vis.drawtracerays(sys; test = true, trackallrays = true, numdivisions = 30, kwargs...) +end + +function grating(; period = 1.0, θ = 0.0, λ = 0.55, kwargs...) + int = ThinGratingInterface(SVector(0.0, 1.0, 0.0), period, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, minorder = -2, maxorder = 2, reflectance = [0.0, 0.0, 0.1, 0.0, 0.0], transmission = [0.05, 0.1, 0.4, 0.1, 0.05]) + grating = ThinGratingSurface(Rectangle(5.0, 5.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, 0.0)), int) + back = Rectangle(30.0, 30.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, 25.0)) + sys = CSGOpticalSystem(LensAssembly(grating, back), Rectangle(30.0, 30.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, -25.0), interface = opaqueinterface())) + Vis.drawtracerays(sys; raygenerator = UniformOpticalSource(CollimatedSource(OriginPoint{Float64}(100, position = SVector(0.0, 0.0, 10.0), direction = SVector(0.0, sind(θ), -cosd(θ)))), λ), trackallrays = true, rayfilter = nothing, kwargs...) +end + +function reflgrating(; period = 1.0, θ = 0.0, λ = 0.55, kwargs...) + int = ThinGratingInterface(SVector(0.0, 1.0, 0.0), period, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, minorder = -2, maxorder = 2, transmission = [0.0, 0.0, 0.1, 0.0, 0.0], reflectance = [0.05, 0.1, 0.4, 0.1, 0.05]) + grating = ThinGratingSurface(Rectangle(5.0, 5.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, 0.0)), int) + back = Rectangle(30.0, 30.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, -25.0)) + sys = CSGOpticalSystem(LensAssembly(grating, back), Rectangle(30.0, 30.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, 25.0), interface = opaqueinterface())) + Vis.drawtracerays(sys; raygenerator = UniformOpticalSource(CollimatedSource(OriginPoint{Float64}(100, position = SVector(0.0, 0.0, 10.0), direction = SVector(0.0, sind(θ), -cosd(θ)))), λ), trackallrays = true, rayfilter = nothing, kwargs...) +end + +function HOE(refl = false, firstorderonly = false; kwargs...) + rect = Rectangle(5.0, 5.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, 0.0)) + if refl + int = HologramInterface(SVector(0.0, -10.0, 20.0), ConvergingBeam, SVector(0.0, 0.0, -200), ConvergingBeam, 0.55, 9.0, OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, 0.05, !firstorderonly) + else + int = HologramInterface(SVector(0.0, -10.0, -20.0), ConvergingBeam, SVector(0.0, 0.0, -200), ConvergingBeam, 0.55, 5.0, OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, 0.05, !firstorderonly) + end + obj = HologramSurface(rect, int) + back = Rectangle(50.0, 50.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, 25.0)) + sys = CSGOpticalSystem(LensAssembly(obj, back), Rectangle(50.0, 50.0, SVector(0.0, 0.0, 1.0), SVector(0.0, 0.0, -25.0), interface = opaqueinterface())) + Vis.drawtracerays(sys; raygenerator = UniformOpticalSource(GridSource(OriginPoint{Float64}(10, position = SVector(0.0, 0.0, 10.0), direction = SVector(0.0, 0.0, -1.0)), 1, 15, 0.0, π / 6), 0.55), trackallrays = true, rayfilter = nothing, kwargs...) +end + +function eyetrackHOE(nrays = 5000, det = false, showhead = true, zeroorder = false; kwargs...) + # TODO update for new specs from Chris + hoehalfwidth = 50.0 #25.0 + hoehalfheight = 50.0 #22.5 + hoecenter = SVector(-8.0 - 25.0, 0.0, -10.0 - 25.0) + rect = Rectangle(hoehalfheight, hoehalfwidth, SVector(0.0, 1.0, 0.0), hoecenter) + er = 15.0 + cornea_rad = 7.85 + corneavertex = SVector(0.0, er, 0.0) + sourceloc = SVector(-33.0, er, 0.0) + camloc = SVector(20.0, 3.0, -11.0) + camdir = corneavertex - camloc + camdir_norm = normalize(camdir) + + interfaces = [] + + # offset = SVector(-5.0, 10.0, -10.0) + # for θ in 0:(π / 6):(2π) + # ledloc = SVector(20 * cos(θ) + offset[1], 0 + offset[2], 15 * sin(θ) + offset[3]) + # int = HologramInterface(ledloc, ConvergingBeam, sourceloc, DivergingBeam, 0.78, 100.0, OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, 0.05, zeroorder) + # push!(interfaces, int) + # end + + dirs = [SVector(0.7713, 0.6350, -0.0437), SVector(0.5667, 0.8111, -0.1445), SVector(0.3400, 0.9349, -0.1017), SVector(0.1492, 0.9878, 0.0445), SVector(0.0249, 0.9686, 0.2474), SVector(-0.0184, 0.8855, 0.4643), SVector(0.0254, 0.7537, 0.6567), SVector(0.1548, 0.5964, 0.7876), SVector(0.3570, 0.4462, 0.8207), SVector(0.5959, 0.3470, 0.7242), SVector(0.7976, 0.3449, 0.4948), SVector(0.8680, 0.4555, 0.1978)] + + for d in dirs + int = HologramInterface(normalize(d), CollimatedBeam, sourceloc, DivergingBeam, 0.78, 100.0, OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, 0.05, zeroorder) + # int = HologramInterface(corneavertex - 10 * d, ConvergingBeam, sourceloc, DivergingBeam, 0.78, 100.0, OpticSim.GlassCat.Air, OpticSim.GlassCat.SCHOTT.N_BK7, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, OpticSim.GlassCat.Air, 0.05, zeroorder) + push!(interfaces, int) + end + + mint = MultiHologramInterface(interfaces...) + obj = MultiHologramSurface(rect, mint) + cornea = leaf(Sphere(cornea_rad, interface = FresnelInterface{Float64}(OpticSim.GlassCat.EYE.CORNEA, OpticSim.GlassCat.Air, reflectance = 1.0, transmission = 0.0)), translation(0.0, er + cornea_rad, 0.0))() + + # cam settings + fnum = 2.0 + fov = 80 + sensorrad = 1.0 + barrellength = sensorrad / tand(fov / 2) + aprad = barrellength / fnum / 2 + camrad = max(sensorrad, aprad) + + camap = Annulus(aprad, camrad, camdir_norm, camloc) + distfromcamtoeye = norm(camdir) + focallength = 1 / (1 / distfromcamtoeye + 1 / barrellength) + camlens = ParaxialLensEllipse(focallength, aprad, aprad, -camdir_norm, camloc) + barrelloc = camloc - barrellength / 2 * camdir_norm + barreltop = Plane(camdir_norm, camloc) + barrelbot = Plane(-camdir_norm, camloc - 3 * barrellength * camdir_norm) + barrelrot = OpticSim.rotmatbetween(SVector(0.0, 0.0, 1.0), camdir_norm) + cambarrel = csgintersection(barrelbot, csgintersection(barreltop, leaf(Cylinder(camrad, barrellength, interface = opaqueinterface(Float64)), Transform(barrelrot, barrelloc))))() + camdet = Circle(sensorrad, camdir_norm, camloc - barrellength * camdir_norm, interface = opaqueinterface(Float64)) + + # sourceleft = hoecenter[1] + hoehalfwidth - sourceloc[1] + # sourceright = hoecenter[1] - hoehalfwidth - sourceloc[1] + # sourceleftθ = atan(sourceleft, sourceloc[2]) + # sourcerightθ = atan(sourceright, sourceloc[2]) + # midθ = (sourceleftθ + sourcerightθ) / 2 + # sourcedir = normalize(SVector(er * tan(midθ), -er, 0.0)) + # sourceextentθ = abs(midθ - sourcerightθ) + # source = CosineOpticalSource(RandomSource(OriginPoint{Float64}(1, position = sourceloc, direction = sourcedir), nrays, sourceextentθ), 1.0, 0.78) + + rays = Vector{OpticalRay{Float64,3}}(undef, nrays) + @simd for i in 1:nrays + p = point(rect, rand() * 2 - 1, rand() * 2 - 1) + rays[i] = OpticalRay(sourceloc, p - sourceloc, 1.0, 0.78) + end + source = RayListSource(rays) + + sys = CSGOpticalSystem(LensAssembly(obj, cornea, camlens, cambarrel, camap), camdet, 800, 800) + if det + Vis.show(OpticSim.traceMT(sys, source)) + else + Vis.drawtracerays(sys; raygenerator = source, trackallrays = true, kwargs...) + # for θ in 0:(π / 6):(2π) + # ledloc = SVector(20 * cos(θ) + offset[1], 0 + offset[2], 15 * sin(θ) + offset[3]) + # Vis.draw!(leaf(Sphere(1.0), translation(ledloc...)), color = :red) + # end + for d in dirs + # Vis.draw!(leaf(Sphere(1.0), translation((corneavertex - 10 * d)...)), color = :red) + Vis.draw!((corneavertex - 50 * d, corneavertex), color = :red) + end + if showhead + Vis.draw!(joinpath(@__DIR__, "../../OBJ/glasses.obj"), scale = 100.0, transform = Transform(OpticSim.rotmatd(90, 0, 0), [27.0, 45.0, -8.0]), color = :black) + Vis.draw!(joinpath(@__DIR__, "../../OBJ/femalehead.obj"), scale = 13.0, transform = Transform(OpticSim.rotmatd(0, 0, 180), [27.0, 105.0, -148.0]), color = :white) + end + Vis.display() + end +end diff --git a/src/OpticSim.jl b/src/OpticSim.jl index edf2d05d9..8967bca3d 100644 --- a/src/OpticSim.jl +++ b/src/OpticSim.jl @@ -3,6 +3,7 @@ # See LICENSE in the project root for full license information. module OpticSim +export Maybe, MaybeVector import Unitful using LinearAlgebra: eigen, svd, I, qr, dot, cross, norm, det, normalize, inv @@ -18,7 +19,8 @@ using Revise import GLMakie import Makie.AbstractPlotting -unzip(a) = map(x -> getfield.(a, x), fieldnames(eltype(a))) +const Maybe{T} = Union{T, Nothing} +const MaybeVector{T} = Union{Vector{<:T}, Iterators.Cycle{Vector{Nothing}}} include("GlassCat/GlassCat.jl") import OpticSim.GlassCat: plot_indices, index, polyfit_indices, absairindex, absorption, info, glassid, glassname, glassforid, isair, findglass, modelglass, glassfromMIL, GlassID diff --git a/src/Optical/Emitter.jl b/src/Optical/Emitter.jl deleted file mode 100644 index 59bd25ff0..000000000 --- a/src/Optical/Emitter.jl +++ /dev/null @@ -1,699 +0,0 @@ -# MIT license -# Copyright (c) Microsoft Corporation. All rights reserved. -# See LICENSE in the project root for full license information. - -abstract type EmissionShape end -abstract type EllipseEmission <: EmissionShape end -abstract type RectEmission <: EmissionShape end -abstract type PointEmission <: EmissionShape end - -""" - RayOriginGenerator{T<:Real} - -Generates 3D points in world space which serve as origins for rays. -""" -abstract type RayOriginGenerator{T<:Real} end - -abstract type AbstractRayGenerator{T<:Real} end - -""" - GeometricRayGenerator{T,O<:RayOriginGenerator{T}} <: AbstractRayGenerator{T} - -Generates geometric [`Ray`](@ref)s according to the specific implementation of the subclass. -""" -abstract type GeometricRayGenerator{T,O<:RayOriginGenerator{T}} <: AbstractRayGenerator{T} end - -""" - OpticalRayGenerator{T} <: AbstractRayGenerator{T} - -Generates [`OpticalRay`](@ref)s according to the specific implementation of the subclass. -""" -abstract type OpticalRayGenerator{T} <: AbstractRayGenerator{T} end -export AbstractRayGenerator, GeometricRayGenerator, OpticalRayGenerator - -Base.iterate(a::AbstractRayGenerator, state = 1) = state > length(a) ? nothing : (generateray(a, state - 1), state + 1) -Base.getindex(a::AbstractRayGenerator, index) = generateray(a, index) -Base.firstindex(a::AbstractRayGenerator) = 0 -Base.lastindex(a::AbstractRayGenerator) = length(a) - 1 -Base.copy(a::AbstractRayGenerator) = a # most don't have any heap allocated stuff so don't really need copying - -""" - randinsolidangle(direction::SVector{3,T}, uvec::SVector{3,T}, vvec::SVector{3,T}, θmax::T) - -Generates a unit vector pointing somewhere within the cone with half angle `θmax` around `direction`. `uvec` and `vvec` should be orthogonal to each other and `direction`. -""" -function randinsolidangle(direction::SVector{3,T}, uvec::SVector{3,T}, vvec::SVector{3,T}, θmax::T)::SVector{3,T} where {T<:Real} - ϕ = rand(T) * 2π - θ = NaNsafeacos(one(T) + rand(T) * (cos(θmax) - 1)) - return normalize(sin(θ) * (cos(ϕ) * uvec + sin(ϕ) * vvec) + cos(θ) * direction) -end - -################################################################ - -function getuvvecs(direction::SVector{3,T}, rotationvec::SVector{3,T}) where {T<:Real} - direction = normalize(direction) - if abs(dot(rotationvec, direction)) == one(T) - rotationvec = SVector{3,T}(1.0, 0.0, 0.0) - end - uvec = normalize(cross(normalize(rotationvec), direction)) - vvec = normalize(cross(direction, uvec)) - return direction, uvec, vvec -end - -""" - RandomRectOriginPoints{T} <: RayOriginGenerator{T} - -Generates ray origins randomly within a rectangle. - -```julia -RandomRectOriginPoints(numrays, halfsizeu, halfsizev; position = (0.0, 0.0, 0.0), direction = (0.0, 0.0, -1.0), rotationvec = (0.0, 1.0, 0.0)) -``` -""" -struct RandomRectOriginPoints{T} <: RayOriginGenerator{T} - position::SVector{3,T} - direction::SVector{3,T} - uvec::SVector{3,T} - vvec::SVector{3,T} - halfsizeu::T - halfsizev::T - nrays::Int - function RandomRectOriginPoints(numrays::Int, halfsizeu::T, halfsizev::T; position::SVector{3,T} = SVector{3,T}(0.0, 0.0, 0.0), direction::SVector{3,T} = SVector{3,T}(0.0, 0.0, -1.0), rotationvec::SVector{3,T} = SVector{3,T}(0.0, 1.0, 0.0)) where {T<:Real} - new{T}(position, getuvvecs(direction, rotationvec)..., halfsizeu, halfsizev, numrays) - end -end - -""" - GridRectOriginPoints{T} <: RayOriginGenerator{T} - -Generates ray origins on a rectangular grid. - -```julia -GridRectOriginPoints(numraysu, numraysv, halfsizeu, halfsizev; position = (0.0, 0.0, 0.0), direction = (0.0, 0.0, -1.0), rotationvec = (0.0, 1.0, 0.0)) -``` -""" -struct GridRectOriginPoints{T} <: RayOriginGenerator{T} - position::SVector{3,T} - direction::SVector{3,T} - uvec::SVector{3,T} - vvec::SVector{3,T} - halfsizeu::T - halfsizev::T - nraysu::Int - nraysv::Int - function GridRectOriginPoints(numraysu::Int, numraysv::Int, halfsizeu::T, halfsizev::T; position::SVector{3,T} = SVector{3,T}(0.0, 0.0, 0.0), direction::SVector{3,T} = SVector{3,T}(0.0, 0.0, -1.0), rotationvec::SVector{3,T} = SVector{3,T}(0.0, 1.0, 0.0)) where {T<:Real} - new{T}(position, getuvvecs(direction, rotationvec)..., halfsizeu, halfsizev, numraysu, numraysv) - end -end - -""" - RandomEllipseOriginPoints{T} <: RayOriginGenerator{T} - -Generates ray origins randomly across an ellipse. - -```julia -RandomEllipseOriginPoints(numrays, halfsizeu, halfsizev; position = (0.0, 0.0, 0.0), direction = (0.0, 0.0, -1.0), rotationvec = (0.0, 1.0, 0.0)) -``` -""" -struct RandomEllipseOriginPoints{T} <: RayOriginGenerator{T} - position::SVector{3,T} - direction::SVector{3,T} - uvec::SVector{3,T} - vvec::SVector{3,T} - halfsizeu::T - halfsizev::T - nrays::Int - function RandomEllipseOriginPoints(numrays::Int, halfsizeu::T, halfsizev::T; position::SVector{3,T} = SVector{3,T}(0.0, 0.0, 0.0), direction::SVector{3,T} = SVector{3,T}(0.0, 0.0, -1.0), rotationvec::SVector{3,T} = SVector{3,T}(0.0, 1.0, 0.0)) where {T<:Real} - new{T}(position, getuvvecs(direction, rotationvec)..., halfsizeu, halfsizev, numrays) - end -end - -""" - HexapolarOriginPoints{T} <: RayOriginGenerator{T} - -Generates ray origins in a hexapolar pattern. - -```julia -HexapolarOriginPoints(nrings::Int, halfsizeu::T, halfsizev::T; position = (0.0, 0.0, 0.0), direction = (0.0, 0.0, -1.0), rotationvec = (0.0, 1.0, 0.0)) -``` -""" -struct HexapolarOriginPoints{T} <: RayOriginGenerator{T} - position::SVector{3,T} - direction::SVector{3,T} - uvec::SVector{3,T} - vvec::SVector{3,T} - halfsizeu::T - halfsizev::T - nrings::Int - function HexapolarOriginPoints(nrings::Int, halfsizeu::T, halfsizev::T; position::SVector{3,T} = SVector{3,T}(0.0, 0.0, 0.0), direction::SVector{3,T} = SVector{3,T}(0.0, 0.0, -1.0), rotationvec::SVector{3,T} = SVector{3,T}(0.0, 1.0, 0.0)) where {T<:Real} - new{T}(position, getuvvecs(direction, rotationvec)..., halfsizeu, halfsizev, nrings) - end -end - -""" - OriginPoint{T} <: RayOriginGenerator{T} - -Single point origin for a source. - -```julia -OriginPoint{T}(numrays; position = (0.0, 0.0, 0.0), direction = (0.0, 0.0, -1.0), rotationvec = (0.0, 1.0, 0.0)) -``` -""" -struct OriginPoint{T} <: RayOriginGenerator{T} - position::SVector{3,T} - direction::SVector{3,T} - uvec::SVector{3,T} - vvec::SVector{3,T} - nrays::Int - function OriginPoint{T}(numrays::Int; position::SVector{3,T} = SVector{3,T}(0.0, 0.0, 0.0), direction::SVector{3,T} = SVector{3,T}(0.0, 0.0, -1.0), rotationvec::SVector{3,T} = SVector{3,T}(0.0, 1.0, 0.0)) where {T<:Real} - new{T}(position, getuvvecs(direction, rotationvec)..., numrays) - end -end - -export RandomRectOriginPoints, GridRectOriginPoints, HexapolarOriginPoints, RandomEllipseOriginPoints, OriginPoint - -Base.length(a::RayOriginGenerator) = a.nrays -Base.length(a::GridRectOriginPoints) = a.nraysu * a.nraysv -Base.length(a::HexapolarOriginPoints) = 1 + round(Int, (a.nrings * (a.nrings + 1) / 2) * 6) - -direction(a::RayOriginGenerator) = a.direction -position(a::RayOriginGenerator) = a.position -uvec(a::RayOriginGenerator) = a.uvec -vvec(a::RayOriginGenerator) = a.vvec - -""" - genorigin(o::RayOriginGenerator{T}, n::Int) -> SVector{3,T} - -Generate origin positions for rays based on the type of the generator, e.g., randomly within a rectangle or ellipse. `n` is the index of the point being generated, starting from 0. -This has little meaning for random generators, but is important for `HexapolarOriginPoints` and `GridRectOriginPoints`. -""" -genorigin(a::OriginPoint, ::Int) = a.position -function genorigin(a::RandomRectOriginPoints{T}, ::Int) where {T<:Real} - u = 2 * rand(T) * a.halfsizeu - a.halfsizeu - v = 2 * rand(T) * a.halfsizev - a.halfsizev - return a.position + u * a.uvec + v * a.vvec -end -function genorigin(a::RandomEllipseOriginPoints{T}, ::Int) where {T<:Real} - ϕ = rand(T) * 2π - ρ = sqrt(rand(T)) - u = cos(ϕ) * a.halfsizeu - v = sin(ϕ) * a.halfsizev - return a.position + ρ * (u * a.uvec + v * a.vvec) -end -function genorigin(a::HexapolarOriginPoints{T}, n::Int) where {T<:Real} - n = mod(n, length(a)) - if n == 0 - return a.position - else - t = 1 - ringi = 1 - for i in 1:(a.nrings) - t += 6 * i - if n < t - ringi = i - break - end - end - ρ = ringi / a.nrings - pind = n - (t - 6 * ringi) - ϕ = (pind / (6 * ringi)) * 2π - u = cos(ϕ) * a.halfsizeu - v = sin(ϕ) * a.halfsizev - return a.position + ρ * (u * a.uvec + v * a.vvec) - end -end -function genorigin(a::GridRectOriginPoints{T}, n::Int) where {T<:Real} - n = mod(n, length(a)) - v = a.nraysv == 1 ? zero(T) : 2 * Int(floor(n / a.nraysu)) / (a.nraysv - 1) - 1.0 - u = a.nraysu == 1 ? zero(T) : 2 * mod(n, a.nraysu) / (a.nraysu - 1) - 1.0 - return a.position + (a.halfsizeu * u * a.uvec) + (a.halfsizev * v * a.vvec) -end - -################################################################ - -""" - CollimatedSource{T,O} <: GeometricRayGenerator{T,O} - -Source which generates collimated rays. - -```julia -CollimatedSource(generator::RayOriginGenerator) -``` -""" -struct CollimatedSource{T,O<:RayOriginGenerator{T}} <: GeometricRayGenerator{T,O} - generator::O - function CollimatedSource(generator::O) where {T<:Real,O<:RayOriginGenerator{T}} - new{T,O}(generator) - end -end - -""" - GridSource{T,O} <: GeometricRayGenerator{T,O} - -Source which generates rays in directions which fall on an even grid on a rectangle subtended by angles halfangleu, halfanglev. - -```julia -GridSource(generator::RayOriginGenerator, numraysu, numraysv, halfangleu, halfanglev) -``` -""" -struct GridSource{T,O<:RayOriginGenerator{T}} <: GeometricRayGenerator{T,O} - generator::O - nraysu::Int - nraysv::Int - halfangleu::T - halfanglev::T - function GridSource(generator::O, numraysu::Int, numraysv::Int, halfangleu::T, halfanglev::T) where {T<:Real,O<:RayOriginGenerator{T}} - new{T,O}(generator, numraysu, numraysv, halfangleu, halfanglev) - end -end - -""" - RandomSource{T,O} <: GeometricRayGenerator{T,O} - -Source which generates rays in directions sampled randomly from the solid angle ±θmax centred on the direction of the source. - -```julia -RandomSource(generator::RayOriginGenerator, numrays = 1, θmax = T(π / 2)) -``` -""" -struct RandomSource{T,O<:RayOriginGenerator{T}} <: GeometricRayGenerator{T,O} - generator::O - nrays::Int - θmax::T - function RandomSource(generator::O, numrays::Int = 1, θmax::T = T(π / 2)) where {T<:Real,O<:RayOriginGenerator{T}} - @assert θmax <= π - new{T,O}(generator, numrays, θmax) - end -end - -export CollimatedSource, GridSource, RandomSource - -Base.length(a::RandomSource) = a.nrays * length(a.generator) -Base.length(a::GridSource) = a.nraysu * a.nraysv * length(a.generator) -Base.length(a::GeometricRayGenerator) = length(a.generator) - -direction(a::GeometricRayGenerator) = direction(a.generator) -position(a::GeometricRayGenerator) = position(a.generator) -uvec(a::GeometricRayGenerator) = uvec(a.generator) -vvec(a::GeometricRayGenerator) = vvec(a.generator) - - -""" - gendirection(o::GeometricRayGenerator{T}, n::Int) -> SVector{3,T} - -Generate directions for rays based on the type of the generator, e.g., randomly within a cone or collimated. `n` is the index of the point being generated, starting from 0. -This has little meaning for random generators, but is important for `GridSource`. -""" -gendirection(a::RandomSource, ::Int) = randinsolidangle(a.generator.direction, a.generator.uvec, a.generator.vvec, a.θmax) -gendirection(a::CollimatedSource, ::Int) = a.generator.direction -function gendirection(a::GridSource{T}, n::Int) where {T<:Real} - # distributing evenly across the area of the rectangle which subtends the given angle (*not* evenly across the angle range) - dindex = mod(n, a.nraysu * a.nraysv) - v = a.nraysv == 1 ? zero(T) : 2 * Int(floor(dindex / a.nraysu)) / (a.nraysv - 1) - 1.0 - u = a.nraysu == 1 ? zero(T) : 2 * mod(dindex, a.nraysu) / (a.nraysu - 1) - 1.0 - θu = atan(u * tan(a.halfangleu) / 2) * a.halfangleu - θv = atan(v * tan(a.halfanglev) / 2) * a.halfanglev - dir = cos(θv) * (cos(θu) * direction(a) + sin(θu) * uvec(a)) + sin(θv) * vvec(a) - return dir -end - -""" - generateray(o::GeometricRayGenerator{T}, n::Int) -> Ray{T,3} - -Generate geometric rays distributed according to the type of the generator. `n` is the index of the point being generated, starting from 0. -This has little meaning for random generators, but is important for `GridSource`, for example. -""" -function generateray(a::GeometricRayGenerator{T,O}, n::Int) where {T<:Real,O} - origin = genorigin(a.generator, n)::SVector{3,T} - direction = gendirection(a, n)::SVector{3,T} - return Ray(origin, direction) -end - -origingen(a::GeometricRayGenerator) = a.generator - -################################################################ - -""" - UniformOpticalSource{T,O,P} <: OpticalRayGenerator{T} - -Source of OpticalRays with uniform power. - -```julia -UniformOpticalSource(generator::GeometricRayGenerator, centralwavelength, power = 1.0; sourcenum = 0) -``` -""" -struct UniformOpticalSource{T,O,P<:GeometricRayGenerator{T,O}} <: OpticalRayGenerator{T} - generator::P - power::T - centralwavelength::T - sourcenum::Int - - function UniformOpticalSource(generator::P, centralwavelength::Unitful.Length, power::T = one(T); sourcenum::Int = 0) where {T<:Real,O<:RayOriginGenerator{T},P<:GeometricRayGenerator{T,O}} - λ = Unitful.ustrip(Unitful.u"μm", centralwavelength, sourcenum) - new{T,O,P}(generator, power, λ) - end - - function UniformOpticalSource(generator::P, centralwavelength::T, power::T = one(T); sourcenum::Int = 0) where {T<:Real,O<:RayOriginGenerator{T},P<:GeometricRayGenerator{T,O}} - new{T,O,P}(generator, power, centralwavelength, sourcenum) - end -end - -""" - CosineOpticalSource{T,O,P} <: OpticalRayGenerator{T} - -Source of OpticalRays with power defined by: -``I(\\theta) \\approx I_0(\\cos\\theta)^C`` -Where ``\\theta`` is the angle of the ray to the central direction of the source, and ``C`` is the `cosineexp` parameter. - -```julia -CosineOpticalSource(generator::GeometricRayGenerator, cosineexp, centralwavelength, power = 1.0; sourcenum = 0) -``` -""" -struct CosineOpticalSource{T,O,P<:GeometricRayGenerator{T,O}} <: OpticalRayGenerator{T} - generator::P - power::T - centralwavelength::T - cosineexp::T - sourcenum::Int - - function CosineOpticalSource(generator::P, cosineexp::T, centralwavelength::Unitful.Length, power::T = one(T); sourcenum::Int = 0) where {T<:Real,O<:RayOriginGenerator{T},P<:GeometricRayGenerator{T,O}} - λ = Unitful.ustrip(Unitful.u"μm", centralwavelength) - new{T,O,P}(generator, power, λ, cosineexp, sourcenum) - end - - function CosineOpticalSource(generator::P, cosineexp::T, centralwavelength::T, power::T = one(T); sourcenum::Int = 0) where {T<:Real,O<:RayOriginGenerator{T},P<:GeometricRayGenerator{T,O}} - new{T,O,P}(generator, power, centralwavelength, cosineexp, sourcenum) - end -end - -""" - GaussianOpticalSource{T,O,P} <: OpticalRayGenerator{T} - -Source of OpticalRays with power defined by: -``I(\\theta) \\approx I_0e^{-(G_ul^2 + G_vm^2)}`` -Where ``l`` and ``m`` are the direction cosines in the u and v directions to the central direction of the source, and ``G_u`` and ``G_v`` and the `gaussianu` and `gaussianv` parameters. - -```julia -GaussianOpticalSource(generator::GeometricRayGenerator, gaussianu, gaussianv, centralwavelength, power = 1.0; sourcenum = 0) -``` -""" -struct GaussianOpticalSource{T,P<:GeometricRayGenerator{T}} <: OpticalRayGenerator{T} - generator::P - power::T - centralwavelength::T - gaussianu::T - gaussianv::T - sourcenum::Int - - function GaussianOpticalSource(generator::P, gaussianu::T, gaussianv::T, centralwavelength::Unitful.Length, power::T = one(T); sourcenum::Int = 0) where {T<:Real,P<:GeometricRayGenerator{T}} - λ = Unitful.ustrip(Unitful.u"μm", centralwavelength) - new{T,P}(generator, power, λ, gaussianu, gaussianv, sourcenum) - end - - function GaussianOpticalSource(generator::P, gaussianu::T, gaussianv::T, centralwavelength::T, power::T = one(T); sourcenum::Int = 0) where {T<:Real,P<:GeometricRayGenerator{T}} - new{T,P}(generator, power, centralwavelength, gaussianu, gaussianv, sourcenum) - end -end - -export UniformOpticalSource, CosineOpticalSource, GaussianOpticalSource - -function generateray(a::GaussianOpticalSource{T,P}, n::Int) where {T<:Real,P} - r = generateray(a.generator, n)::Ray{T,3} - l = dot(direction(r), uvec(a.generator)) - m = dot(direction(r), vvec(a.generator)) - power = a.power * exp(-(a.gaussianu * l^2 + a.gaussianv * m^2)) - return OpticalRay(r, power, a.centralwavelength, sourcenum = a.sourcenum) -end - -function generateray(a::CosineOpticalSource{T,P}, n::Int) where {T<:Real,P} - r = generateray(a.generator, n)::Ray{T,3} - cosanglebetween = dot(direction(r), direction(a.generator)) - power = a.power * cosanglebetween^a.cosineexp - return OpticalRay(r, power, a.centralwavelength, sourcenum = a.sourcenum) -end - -""" - generateray(o::OpticalRayGenerator{T}, n::Int) -> OpticalRay{T,3} - -Generate optical rays distributed according to the type of the generator. `n` is the index of the point being generated, starting from 0. -This has little meaning for random generators, but is important for generators using `GridSource` or `GridRectOriginPoints`, for example. -""" -function generateray(a::UniformOpticalSource{T,P}, n::Int) where {T<:Real,P} - r = generateray(a.generator, n)::Ray{T,3} - return OpticalRay(r, one(T), a.centralwavelength, sourcenum = a.sourcenum) -end - -Base.length(a::OpticalRayGenerator) = length(a.generator) -origingen(a::OpticalRayGenerator) = origingen(a.generator) -direction(a::OpticalRayGenerator) = direction(a.generator) -position(a::OpticalRayGenerator) = position(a.generator) -uvec(a::OpticalRayGenerator) = uvec(a.generator) -vvec(a::OpticalRayGenerator) = vvec(a.generator) - -################################################################################################################################ - -""" - PixelSource{T,C} <: RayGenerator{T} - -Ray generator which encapsulates a number of subpixels, rays are generated in each subpixel in an interleaved manner. -The subpixels must be positioned correctly relative to each other when input to the constructor. - -All subpixels must be coplanar, the orientation of the pixel and any display made using the pixel is taken from the first subpixel. -All subpixels must have the same number of rays (if we make subpixels be handled sequentially we wouldn't need this). -All subpixels must be of the same type. - -```julia -PixelSource(subpixels::Vector{OpticalRayGenerator}; colormap = nothing, position = (0, 0, 0)) -PixelSource(subpixels::Vararg{OpticalRayGenerator}; colormap = nothing, position = (0, 0, 0)) -``` -""" -struct PixelSource{T,P<:OpticalRayGenerator{T},C} <: OpticalRayGenerator{T} - subpixels::Vector{P} - colormap::SVector{C,Int} - position::SVector{3,T} - - function PixelSource(subpixels::Vararg{P}; colormap = nothing, position::SVector{3,T} = SVector{3,T}(0, 0, 0)) where {T<:Real,P<:OpticalRayGenerator{T}} - PixelSource(collect(subpixels), colormap = colormap, position = position) - end - - function PixelSource(subpixels::Vector{P}; colormap = nothing, position::SVector{3,T} = SVector{3,T}(0, 0, 0)) where {T<:Real,P<:OpticalRayGenerator{T}} - C = length(subpixels) - # we need to map from subpixel index to color in image (e.g. for RGB pentile where there are multiple subpixels for one color) - if (colormap === nothing) - colormap = SVector{C,Int}(1:C) - end - for s in subpixels[1:end] - @assert uvec(s) == uvec(subpixels[1]) && vvec(s) == vvec(subpixels[1]) - @assert length(s) == length(subpixels[1]) - end - new{T,P,C}(subpixels, colormap, position) - end -end -export PixelSource - -""" - generateray(a::PixelSource{T}, n::Int) -> OpticalRay{T,3} - -Generates optical rays from all subpixels in the pixel. One ray is generated from each subpixel sequentially before looping back to the start. -""" -function generateray(a::PixelSource{T,P,C}, n::Int) where {T<:Real,P,C} - subpixelindex = mod(n, C) + 1 - subpixeliter = Int(floor(n / C)) - ray = generateray(a.subpixels[subpixelindex], subpixeliter)::OpticalRay{T,3} - return OpticalRay(origin(ray) + a.position, direction(ray), power(ray), wavelength(ray), sourcenum = sourcenum(ray)) -end - -uvec(a::PixelSource) = uvec(a.subpixels[1]) -vvec(a::PixelSource) = vvec(a.subpixels[1]) -colorindex(a::PixelSource{T,P,C}, i::Int) where {T<:Real,P,C} = a.colormap[mod(i, C) + 1] -Base.length(a::PixelSource) = sum(length.(a.subpixels)) - - -""" - OpticalSourceArray{T} <: RayGenerator{T} - -Generates rays from an array of the given source at the specified locations. - -```julia -OpticalSourceArray(generator::OpticalRayGenerator, positions::Vector{SVector{3,T}}) -``` -""" -struct OpticalSourceArray{T,S<:OpticalRayGenerator{T}} <: OpticalRayGenerator{T} - generator::S - positions::Vector{SVector{3,T}} - - function OpticalSourceArray(generator::S, positions::Vector{SVector{3,T}}) where {T<:Real,S<:OpticalRayGenerator{T}} - new{T,S}(generator, positions) - end -end -export OpticalSourceArray - -Base.copy(a::OpticalSourceArray) = OpticalSourceArray(copy(a.generator), copy(a.positions)) -Base.length(a::OpticalSourceArray) = length(a.generator) * length(a.positions) -Base.show(io::IO, a::OpticalSourceArray) = print(io, "OpticalSourceArray($(a.generator), $(length(a.positions)) positions)") - -""" - generateray(a::OpticalSourceArray{T}, n::Int) -> OpticalRay{T,3} - -Generates optical rays from all generators in the array. One ray is generated from each element sequentially before looping back to the start of the array. -""" -function generateray(a::OpticalSourceArray{T,S}, n::Int) where {T<:Real,S} - position = mod(n, length(a.positions)) + 1 - positioniter = Int(floor(n / length(a.positions))) - ray = generateray(a.generator::S, positioniter)::OpticalRay{T,3} - return OpticalRay(origin(ray) + a.positions[position], direction(ray), power(ray), wavelength(ray), sourcenum = sourcenum(ray)) -end - - -""" - BasicDisplayPanel{T,C} <: RayGenerator{T} - -Ray generator representing a simple panel display. The panel is flat and pixles are on a regular rectangular grid. Each pixel corresponds to one image pixel. - -```julia -BasicDisplayPanel(pixel::PixelSource, pitchx, pitchy, image, position = (0, 0, 0)) -BasicDisplayPanel(generator::OpticalSourceArray{T,PixelSource}, pixelvals::Vector{Union{T,Vector{T}}}) -``` -""" -struct BasicDisplayPanel{T,P<:PixelSource{T}} <: OpticalRayGenerator{T} - generator::OpticalSourceArray{T,P} - pixelvals::Vector{Union{T,Vector{T}}} - - function BasicDisplayPanel(pixel::P, pitchx::T, pitchy::T, image::Array, position::SVector{3,T} = SVector{3,T}(0, 0, 0), gamma::T = 2.2) where {T<:Real,P<:PixelSource{T}} - @assert pitchx > 0 && pitchy > 0 - h, w = size(image) - positions = Vector{SVector{3,T}}(undef, 0) - pixelvals = [] - for colindex in 1:w - for rowindex in 1:h - if size(channelview(image))[1] == h - k = T(channelview(image)[rowindex, colindex])^gamma - else - k = T.(channelview(image)[:, rowindex, colindex]) .^ gamma - end - if sum(k) > 0.001 - o = position + (colindex - (w + 1) / 2) * pitchx * uvec(pixel) + (rowindex - (h + 1) / 2) * pitchy * vvec(pixel) - push!(positions, o) - push!(pixelvals, k) - end - end - end - new{T,P}(OpticalSourceArray(pixel, positions), pixelvals) - end - - function BasicDisplayPanel(generator::OpticalSourceArray{T,P}, pixelvals::Vector{Union{T,Vector{T}}}) where {T<:Real,P<:PixelSource{T}} - new{T,P}(generator, pixelvals) - end -end - -export BasicDisplayPanel - -Base.copy(a::BasicDisplayPanel) = BasicDisplayPanel(copy(a.generator), copy(a.pixelvals)) -Base.length(a::BasicDisplayPanel) = length(a.generator) -Base.show(io::IO, a::BasicDisplayPanel) = print(io, "BasicDisplayPanel($(a.generator))") - -""" - generateray(a::BasicDisplayPanel{T}, n::Int) -> OpticalRay{T,3} - -Generates optical rays from all pixels in the display. One ray is generated from each pixel sequentially before looping back to the start of the display. -""" -function generateray(a::BasicDisplayPanel{T,P}, n::Int) where {T<:Real,P} - r = generateray(a.generator, n)::OpticalRay{T,3} - position = mod(n, length(a.pixelvals)) + 1 - positioniter = Int(floor(n / length(a.pixelvals))) - k = a.pixelvals[position] - if length(k) == 1 - pow = power(r) * k - else - pow = power(r) * k[colorindex(a.generator.generator::P, positioniter)] - end - return OpticalRay(ray(r), pow, wavelength(r), sourcenum = sourcenum(r)) -end - -uvec(a::BasicDisplayPanel) = uvec(a.generator) -vvec(a::BasicDisplayPanel) = vvec(a.generator) - -""" - OpticalSourceGroup{T} <: RayGenerator{T} - -Wrapper to group a number of separate optical sources into a single iterable object. -If the sources of the same type then performance will be improved. - -```julia -OpticalSourceGroup(generators::Vector{<:OpticalRayGenerator}) -OpticalSourceGroup(generators::Vararg{OpticalRayGenerator}) -``` -""" -abstract type OpticalSourceGroup{T} <: OpticalRayGenerator{T} end - -struct OpticalSourceGroupG{T} <: OpticalSourceGroup{T} - generators::Vector{OpticalRayGenerator{T}} - rayspergen::Vector{Int} -end - -struct OpticalSourceGroupS{T,S<:OpticalRayGenerator{T}} <: OpticalSourceGroup{T} - generators::Vector{S} - rayspergen::Vector{Int} -end - -Base.show(io::IO, ::Type{<:OpticalSourceGroup}) = print(io, "OpticalSourceGroup") -Base.show(io::IO, a::OpticalSourceGroupS{T}) where {T<:Real} = print(io, "OpticalSourceGroupS{$T}($(length(a.generators)), $(a.rayspergen))") -Base.show(io::IO, a::OpticalSourceGroupG{T}) where {T<:Real} = print(io, "OpticalSourceGroupG{$T}($(length(a.generators)), $(a.rayspergen))") - -OpticalSourceGroup(generators::Vararg{OpticalRayGenerator{T}}) where {T<:Real} = OpticalSourceGroup(collect(generators)) -function OpticalSourceGroup(generators::Vector{<:OpticalRayGenerator{T}}) where {T<:Real} - if length(generators) == 1 - return generators[1] - end - S = typeof(generators[1]) - for a in generators[2:end] - if typeof(a) != S - return OpticalSourceGroupG{T}(generators, length.(generators)) - end - end - OpticalSourceGroupS{T,S}(generators, length.(generators)) -end -export OpticalSourceGroup - -Base.copy(a::OpticalSourceGroup) = OpticalSourceGroup(a.generators) -Base.length(a::OpticalSourceGroup) = sum(a.rayspergen) - -""" - generateray(a::OpticalSourceGroup{T}, n::Int) -> OpticalRay{T,3} - -Generate optical rays for each source in the group. All rays are generated for the first source, then all for the second source and so on as `n` increases. -""" -function generateray(a::OpticalSourceGroup{T}, n::Int) where {T<:Real} - t = 0 - for (i, k) in enumerate(a.rayspergen) - if n < t + k - return generateray(a.generators[i], n - t)::OpticalRay{T,3} - break - end - t += k - end - return nothing -end - -""" - RayListSource{T} <: OpticalRayGenerator{T} - -Ray generator constructed manually from a list of rays which just outputs those rays in order. - -```julia -RayListSource(rays::Vararg{OpticalRay}) -RayListSource(rays::Vector{OpticalRay}) -``` -""" -struct RayListSource{T} <: OpticalRayGenerator{T} - rays::Vector{OpticalRay{T,3}} - RayListSource(rays::Vararg{OpticalRay{T,3}}) where {T<:Real} = new{T}(collect(rays)) - RayListSource(rays::Vector{OpticalRay{T,3}}) where {T<:Real} = new{T}(rays) -end -export RayListSource - -Base.length(a::RayListSource) = length(a.rays) -origingen(::RayListSource) = nothing - -function generateray(a::RayListSource, n::Int) - return a.rays[n + 1] -end diff --git a/src/Optical/Emitters/Directions.jl b/src/Optical/Emitters/Directions.jl index 583a98313..9502b8fb0 100644 --- a/src/Optical/Emitters/Directions.jl +++ b/src/Optical/Emitters/Directions.jl @@ -31,6 +31,10 @@ Constant(::Type{T} = Float64) where {T<:Real} struct Constant{T} <: AbstractDirectionDistribution{T} direction::Vec3{T} + function Constant(dirx::T,diry::T,dirz::T) where{T<:Real} + return new{T}(Vec3(dirx,diry,dirz)) + end + function Constant(direction::Vec3{T}) where {T<:Real} return new{T}(direction) end diff --git a/src/Optical/Optical.jl b/src/Optical/Optical.jl index c6576874d..397b5769a 100644 --- a/src/Optical/Optical.jl +++ b/src/Optical/Optical.jl @@ -5,7 +5,7 @@ # OpticalInterface.jl is included in Geometry.jl so is omitted here include("OpticalRay.jl") -include("Emitter.jl") +include("RayGenerator.jl") include("Emitters/Emitters.jl") include("Fresnel.jl") include("Paraxial.jl") diff --git a/src/Optical/RayGenerator.jl b/src/Optical/RayGenerator.jl new file mode 100644 index 000000000..d2771da02 --- /dev/null +++ b/src/Optical/RayGenerator.jl @@ -0,0 +1,20 @@ +# MIT license +# Copyright (c) Microsoft Corporation. All rights reserved. +# See LICENSE in the project root for full license information. + +abstract type AbstractRayGenerator{T<:Real} end + +""" + GeometricRayGenerator{T,O<:RayOriginGenerator{T}} <: AbstractRayGenerator{T} + +Generates geometric [`Ray`](@ref)s according to the specific implementation of the subclass. +""" +abstract type GeometricRayGenerator{T} <: AbstractRayGenerator{T} end + +""" + OpticalRayGenerator{T} <: AbstractRayGenerator{T} + +Generates [`OpticalRay`](@ref)s according to the specific implementation of the subclass. +""" +abstract type OpticalRayGenerator{T} <: AbstractRayGenerator{T} end +export AbstractRayGenerator, GeometricRayGenerator, OpticalRayGenerator diff --git a/src/Vis/Visualization.jl b/src/Vis/Visualization.jl index 01de86f3a..50a13f811 100644 --- a/src/Vis/Visualization.jl +++ b/src/Vis/Visualization.jl @@ -3,7 +3,7 @@ # See LICENSE in the project root for full license information. using ..OpticSim -using ..OpticSim: euclideancontrolpoints, evalcsg, vertex, makiemesh, detector, centroid, origingen, lower, upper, intervals, α +using ..OpticSim: euclideancontrolpoints, evalcsg, vertex, makiemesh, detector, centroid, lower, upper, intervals, α using ..OpticSim.Geometry using Unitful @@ -236,6 +236,7 @@ function save(path::String) AbstractPlotting.resize!(current_main_scene, size) display(current_main_scene) end +function save(::Nothing) end ############################################################################# @@ -434,7 +435,7 @@ onlydetectorrays(system::Q, tracevalue::LensTrace{T,3}) where {T<:Real,Q<:Abstra ## RAY GEN """ - drawtracerays(system::Q; raygenerator::S = UniformOpticalSource(CollimatedSource(GridRectOriginPoints(5, 5, 5.0, 5.0, position = SVector(0.0, 0.0, 10.0))), 0.55), test::Bool = false, trackallrays::Bool = false, colorbysourcenum::Bool = false, colorbynhits::Bool = false, rayfilter::Union{Nothing,Function} = onlydetectorrays, kwargs...) + drawtracerays(system::Q; raygenerator::S = Source(transform = Transform.translation(0.0,0.0,10.0), origins = Origins.RectGrid(10.0,10.0,25,25),directions = Constant(0.0,0.0,-1.0)), test::Bool = false, trackallrays::Bool = false, colorbysourcenum::Bool = false, colorbynhits::Bool = false, rayfilter::Union{Nothing,Function} = onlydetectorrays, kwargs...) Displays a model of the optical system. `raygenerator` is an iterator that generates rays. @@ -445,7 +446,7 @@ By default only ray paths that eventually intersect the detector surface are dis Also `drawtracerays!` to add to an existing scene, with `drawsys` and `drawgen` to specify whether `system` and `raygenerator` should be drawn respectively. """ -function drawtracerays(system::Q; raygenerator::S = UniformOpticalSource(CollimatedSource(GridRectOriginPoints(5, 5, 5.0, 5.0, position = SVector(0.0, 0.0, 10.0))), 0.55), test::Bool = false, trackallrays::Bool = false, colorbysourcenum::Bool = false, colorbynhits::Bool = false, rayfilter::Union{Nothing,Function} = onlydetectorrays, verbose::Bool = false, resolution::Tuple{Int,Int} = (1000, 1000), kwargs...) where {T<:Real,Q<:AbstractOpticalSystem{T},S<:AbstractRayGenerator{T}} +function drawtracerays(system::Q; raygenerator::S = Source(transform = translation(0.0,0.0,10.0), origins = Origins.RectGrid(10.0,10.0,25,25),directions = Constant(0.0,0.0,-1.0)), test::Bool = false, trackallrays::Bool = false, colorbysourcenum::Bool = false, colorbynhits::Bool = false, rayfilter::Union{Nothing,Function} = onlydetectorrays, verbose::Bool = false, resolution::Tuple{Int,Int} = (1000, 1000), kwargs...) where {T<:Real,Q<:AbstractOpticalSystem{T},S<:AbstractRayGenerator{T}} verbose && println("Drawing System...") s, ls = Vis.scene(resolution) @@ -456,7 +457,7 @@ end drawtracerays!(system::Q; kwargs...) where {T<:Real,Q<:AbstractOpticalSystem{T}} = drawtracerays!(current_3d_scene, system; kwargs...) -function drawtracerays!(scene::MakieLayout.LScene, system::Q; raygenerator::S = UniformOpticalSource(CollimatedSource(GridRectOriginPoints(25, 25, 5.0, 5.0, position = SVector(0.0, 0.0, 10.0))), 0.55), test::Bool = false, trackallrays::Bool = false, colorbysourcenum::Bool = false, colorbynhits::Bool = false, rayfilter::Union{Nothing,Function} = onlydetectorrays, verbose::Bool = false, drawsys::Bool = false, drawgen::Bool = false, kwargs...) where {T<:Real,Q<:AbstractOpticalSystem{T},S<:AbstractRayGenerator{T}} +function drawtracerays!(scene::MakieLayout.LScene, system::Q; raygenerator::S = Source(transform = translation(0.0,0.0,10.0), origins = Origins.RectGrid(10.0,10.0,25,25),directions = Constant(0.0,0.0,-1.0)), test::Bool = false, trackallrays::Bool = false, colorbysourcenum::Bool = false, colorbynhits::Bool = false, rayfilter::Union{Nothing,Function} = onlydetectorrays, verbose::Bool = false, drawsys::Bool = false, drawgen::Bool = false, kwargs...) where {T<:Real,Q<:AbstractOpticalSystem{T},S<:AbstractRayGenerator{T}} raylines = Vector{LensTrace{T,3}}(undef, 0) drawgen && draw!(scene, raygenerator, norays = true; kwargs...) @@ -494,11 +495,11 @@ function drawtracerays!(scene::MakieLayout.LScene, system::Q; raygenerator::S = end """ - drawtraceimage(system::Q; raygenerator::S = UniformOpticalSource(CollimatedSource(GridRectOriginPoints(25, 25, 5.0, 5.0, position = SVector(0.0, 0.0, 10.0))), 0.55), test::Bool = false) + drawtraceimage(system::Q; raygenerator::S = Source(transform = translation(0.0,0.0,10.0), origins = Origins.RectGrid(10.0,10.0,25,25),directions = Constant(0.0,0.0,-1.0)), test::Bool = false) Traces rays from `raygenerator` through `system` and shows and returns the detector image. `verbose` will print progress updates. """ -function drawtraceimage(system::Q; raygenerator::S = UniformOpticalSource(CollimatedSource(GridRectOriginPoints(25, 25, 5.0, 5.0, position = SVector(0.0, 0.0, 10.0))), 0.55), test::Bool = false, verbose::Bool = false) where {T<:Real,Q<:AbstractOpticalSystem{T},S<:AbstractRayGenerator{T}} +function drawtraceimage(system::Q; raygenerator::S = Source(transform = translation(0.0,0.0,10.0), origins = Origins.RectGrid(10.0,10.0,25,25),directions = Constant(0.0,0.0,-1.0)), test::Bool = false, verbose::Bool = false) where {T<:Real,Q<:AbstractOpticalSystem{T},S<:AbstractRayGenerator{T}} resetdetector!(system) start_time = time() for (i, r) in enumerate(raygenerator) @@ -516,80 +517,6 @@ function drawtraceimage(system::Q; raygenerator::S = UniformOpticalSource(Collim return detectorimage(system) end -""" - draw!(scene::MakieLayout.LScene, origingenerator::RayOriginGenerator; kwargs...) - -Draw the surface representing `origingenerator`. -""" -function draw!(scene::MakieLayout.LScene, origingenerator::Union{RandomRectOriginPoints{T},GridRectOriginPoints{T}}; position::SVector{3,T} = SVector{3,T}(0, 0, 0), color = :white, kwargs...) where {T<:Real} - draw!(scene, Rectangle(Plane(origingenerator.direction, origingenerator.position + position), origingenerator.halfsizeu, origingenerator.halfsizev, origingenerator.uvec, origingenerator.vvec); color = color) -end - -function draw!(scene::MakieLayout.LScene, origingenerator::Union{HexapolarOriginPoints{T},RandomEllipseOriginPoints{T}}; position::SVector{3,T} = SVector{3,T}(0, 0, 0), color = :white, kwargs...) where {T<:Real} - draw!(scene, Ellipse(Plane(origingenerator.direction, origingenerator.position + position), origingenerator.halfsizeu, origingenerator.halfsizev, origingenerator.uvec, origingenerator.vvec); color = color) -end - -function draw!(scene::MakieLayout.LScene, origingenerator::OriginPoint{T}; position::SVector{3,T} = SVector{3,T}(0, 0, 0), color = :white, kwargs...) where {T<:Real} - draw!(scene, origingenerator.position + position; color = color) -end - -""" - draw!(scene::MakieLayout.LScene, raygen::OpticalRayGenerator, norays::Bool = false; kwargs...) - -Draw the surface representing `raygen`, as well as some sample [`OpticalRay`](@ref)s eminating from it (providing `norays` is false). -""" -function draw!(scene::MakieLayout.LScene, raygen::OpticalRayGenerator{T}; position::SVector{3,T} = SVector{3,T}(0, 0, 0), norays::Bool = false, kwargs...) where {T<:Real} - if !norays - for r in raygen - draw!(scene, OpticalRay(origin(r) + position, direction(r), power(r), wavelength(r), opl = pathlength(r), nhits = nhits(r), sourcenum = sourcenum(r)); kwargs...) - end - end - if hasproperty(raygen, :sourcenum) - col = indexedcolor(raygen.sourcenum) - else - col = :white - end - if !(nothing === origingen(raygen)) - draw!(scene, origingen(raygen), position = position, color = col) - end -end - -""" - draw!(scene::MakieLayout.LScene, raygen::GeometricRayGenerator, norays::Bool = false; kwargs...) - -Draw the surface representing `raygen`, as well as some sample [`Ray`](@ref)s eminating from it (providing `norays` is false). -""" -function draw!(scene::MakieLayout.LScene, raygen::GeometricRayGenerator{T}; position::SVector{3,T} = SVector{3,T}(0, 0, 0), norays::Bool = false, kwargs...) where {T<:Real} - if !norays - for r in raygen - draw!(scene, Ray(origin(r) + position, direction(r)); kwargs...) - end - end - draw!(scene, origingen(raygen), position = position, color = :white) -end - -function draw!(scene::MakieLayout.LScene, pixel::PixelSource{T,C}; kwargs...) where {T<:Real,C} - for (i, raygen) in enumerate(pixel.subpixels) - draw!(scene, raygen; kwargs...) - end -end - -function draw!(scene::MakieLayout.LScene, display::BasicDisplayPanel{T,C}; kwargs...) where {T<:Real,C} - draw!(scene, display.generator; kwargs...) -end - -function draw!(scene::MakieLayout.LScene, arr::OpticalSourceArray{T}; position::SVector{3,T} = SVector{3,T}(0, 0, 0), kwargs...) where {T<:Real} - for pos in arr.positions - draw!(scene, arr.generator; kwargs..., position = pos + position) - end -end - -function draw!(scene::MakieLayout.LScene, arr::OpticalSourceGroup{T}; kwargs...) where {T<:Real} - for gen in arr.generators - draw!(scene, gen; kwargs...) - end -end - ## RAYS, LINES AND POINTS """ diff --git a/test/testsets/JuliaLang.jl b/test/testsets/JuliaLang.jl index d7a851d81..6b2c2d7f5 100644 --- a/test/testsets/JuliaLang.jl +++ b/test/testsets/JuliaLang.jl @@ -21,9 +21,6 @@ and pop it from the set methods_to_ignore = Dict( OpticSim => VERSION >= v"1.6.0-DEV" ? [ (OpticSim.LensAssembly, Tuple{Vararg{Union{CSGTree{T},LensAssembly{T},OpticSim.Surface{T}},N} where N} where {T<:Real}), - (OpticSim.RayListSource, Tuple{Vararg{OpticalRay{T,3},N} where N} where {T<:Real}), - (OpticSim.OpticalSourceGroup, Tuple{Vararg{OpticalRayGenerator{T},N} where N} where {T<:Real}), - (OpticSim.PixelSource, Tuple{Vararg{P,N} where N} where {P<:OpticalRayGenerator{T}} where {T<:Real}), (OpticSim.MultiHologramInterface, Tuple{Vararg{HologramInterface{T},N}} where {N} where {T<:Real}), ] : [], OpticSim.Zernike => [], diff --git a/test/testsets/OpticalSystem.jl b/test/testsets/OpticalSystem.jl index 36644310c..0a1f69225 100644 --- a/test/testsets/OpticalSystem.jl +++ b/test/testsets/OpticalSystem.jl @@ -5,8 +5,8 @@ @testset "OpticalSystem" begin @testset "Single threaded trace makes sure function executes properly" begin conv = Examples.doubleconvex() - rays = RayListSource([OpticalRay([0.0,0.0,0.0],[0.0,0.0,1.0],1.0,.78) for _ in 1:100]) - trace(conv,rays) + rays = Emitters.Sources.CompositeSource(translation(-unitZ3()), repeat([Emitters.Sources.Source()], 100)) + trace(conv, rays) @test true #just want to verify that the trace function executed properly end end