There are a number of powerful visualization tools available, we primarily rely on 3D visualization of systems using Makie.

There are a number of helper methods, as well as the ability to draw objects, surfaces, points, rays and more individually. For example, looking at rays passing through a system in 3D and 2D:

Vis.drawtracerays(Examples.cooketriplet(), trackallrays=true, test=true, numdivisions=100)
Vis.drawtracerays(Examples.cooketriplet(), trackallrays=true, test=true, numdivisions=100)"assets/vis_ex_3d.png") # hide
Vis.drawtracerays(Examples.cooketriplet(), trackallrays=true, test=true, numdivisions=100, drawsys=true, resolution = (1000, 700))
Vis.make2dy()"assets/vis_ex_2d.png"); nothing #hide

3D visualization example 2D visualization example

And the image on the detector for a trace of a system:

Vis.drawtraceimage(Examples.cooketriplet(), test=true)
im = Vis.drawtraceimage(Examples.cooketriplet(Float64, 400), test=true)

detector image example

Basic Drawing

These methods are all you need to build up a visualization piece by piece. For example:

obj = (Sphere(0.5) ∩ Plane(0.0, 1.0, 0.0, 0.0, 0.1, 0.0))()
ray1 = Ray([0.0, -0.1, 1.0], [0.0, 0.0, -1.0])
ray2 = Ray([0.8, 0.0, 0.0], [-1.0, 0.0, 0.0])
Vis.draw!(ray1, rayscale=0.2)
Vis.draw!(ray2, rayscale=0.2, color=:blue)
Vis.draw!(surfaceintersection(obj, ray1), color=:red)
Vis.draw!(surfaceintersection(obj, ray2), color=:green)"assets/vis_ex_3d_parts.png"); nothing # hide

basic drawing example

scene(resolution = (1000, 1000))

Create a new Makie scene with the given resolution including control buttons.

draw(ob; resolution = (1000, 1000), kwargs...)

Draw an object in a new scene. kwargs depends on the object type.


draw the ClusterWithProperties at coordinates specified by latticecoordinateoffset

draw!([scene = currentscene], ob; kwargs...)

Draw an object in an existing scene. kwargs depends on the object type.


Helper Methods

These are the helper methods to provide common visualizations more easily, as used above. Another example:

Vis.surfacesag(AcceleratedParametricSurface(TestData.zernikesurface2()), (256, 256), (1.55, 1.55))
p = Vis.surfacesag(AcceleratedParametricSurface(TestData.zernikesurface2()), (256, 256), (1.55, 1.55))

surface sag example

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. If trackallrays is true then ray paths from the emitter will be displayed otherwise just the final rays that intersect the image detector will be shown, not the entire ray path. colorbysourcenum and colorbynhits will color rays accordingly, otherwise rays will be colored according to their wavelength.

By default only ray paths that eventually intersect the detector surface are displayed. If you want to display all ray paths set rayfilter = nothing.

Also drawtracerays! to add to an existing scene, with drawsys and drawgen to specify whether system and raygenerator should be drawn respectively.

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.

spotdiag(sys::CSGOpticalSystem{T}, raygenerator::OpticalRayGenerator{T}; size = (500, 500), kwargs...)

Plot a spot diagram for an arbitrary CSGOpticalSystem and OpticalRayGenerator. All rays from raygenerator will be traced through sys and their intersection location on the detector plotted.

Also spotdiag! of the same arguments to add to an existing plot.

surfacesag(object::Union{CSGTree{T},Surface{T}}, resolution::Tuple{Int,Int}, halfsizes::Tuple{T,T}; offset::T = T(10), position::SVector{3,T} = SVector{3,T}(0.0, 0.0, 10.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))

Calculates and displays the surface sag of an arbitrary Surface or CSGTree.

Rays are shot in a grid of size defined by resolution across a arectangular area defined by halfsizes. This rectangle is centred at postion with normal along direction and rotation defined by rotationvec. offset is subtracted from the sag measurements to provide values relative to the appropriate zero level.

eyebox_eval_eye(assembly::LensAssembly{T}, raygen::OpticalRayGenerator{T}, eye_rotation_x::T, eye_rotation_y::T, sample_points_x::Int, sample_points_y::Int; pupil_radius::T = T(2.0), resolution::Int = 512, eye_transform::Transform{T} = identitytransform(T))

Visualise the images formed when tracing assembly with a human eye for an evenly sampled sample_points_x × sample_points_y grid in the angular range of eyeball rotations -eye_rotation_x:eye_rotation_x and -eye_rotation_y:eye_rotation_y in each dimension respectively. resolution is the size of the detector image (necessarily square).

The eye must be positioned appropriately relative to the system using eye_transform, this should transform the eye to the correct position and orientation when at 0 rotation in both dimensions. By default the eye is directed along the positive z-axis with the vertex of the cornea at the origin.

The result is displayed as a 4D image - the image seen by the eye is shown in 2D as normal with sliders to vary eye rotation in x and y. The idea being that the whole image should be visible for all rotations in the range.

eyebox_eval_planar(assembly::LensAssembly{T}, raygen::OpticalRayGenerator{T}, eyebox::Rectangle{T}, sample_points_x::Int, sample_points_y::Int, vsize::T; pupil_radius::T = T(2.0), resolution::Int = 512)

Visualise the images formed when tracing assembly for multiple pupil positions within a planar eyebox. Any angles which are present in a circle radius pupil_radius around each sampling point on an even sample_points_x × sample_points_y grid on the eyebox are added to the sub-image at that grid point.

A paraxial lens focal length 1mm is placed at the eyebox and a detector of size vsize × vsizemm placed 1mm behind it. The normal of the detector rectangle should point towards the system (and away from the fake detector). Any rays which miss the detector are ignored.

The pupil is always fully contained in the eyebox, i.e., the extreme sample position in u would be eyebox.halfsizeu - pupil_radius, for example.

The result is displayed as a 4D image - each sub-image is shown as normal with sliders to vary eye rotation in x and y. The idea being that the whole FoV should be visible for all rotations in the range.


Complete Drawing Functions

As mentioned above, Vis.draw! can be used to draw a large variety of objects, each with their own additional arguments. Here is a full list of the available drawing function and their associated options.

draw!([scene = currentscene], ob; kwargs...)

Draw an object in an existing scene. kwargs depends on the object type.

draw!(scene::Makie.LScene, surf::Surface{T}; numdivisions = 20, normals = false, normalcolor = :blue, kwargs...)

Transforms surf into a mesh using makemesh and draws the result. normals of the surface can be drawn at evenly sampled points with provided normalcolor. numdivisions determines the resolution with which the mesh is triangulated. kwargs is passed on to the TriangleMesh drawing function.

draw!(scene::Makie.LScene, tmesh::TriangleMesh{T}; linewidth = 3, shaded = true, wireframe = false, color = :orange, normals = false, normalcolor = :blue, transparency = false, kwargs...)

Draw a TriangleMesh, optionially with a visible wireframe. kwargs are passed on to Makie.mesh.

draw!(scene::Makie.LScene, meshes::Vararg{S}; colors::Bool = false, kwargs...) where {T<:Real,S<:Union{TriangleMesh{T},Surface{T}}}

Draw a series of TriangleMesh or Surface objects, if colors is true then each mesh will be colored automatically with a diverse series of colors. kwargs are is passed on to the drawing function for each element.

draw!(scene::Makie.LScene, csg::Union{CSGTree,CSGGenerator}; numdivisions::Int = 20, kwargs...)

Convert a CSG object (CSGTree or CSGGenerator) to a mesh using makemesh with resolution set by numdivisions and draw the resulting TriangleMesh.

draw!(scene::Makie.LScene, bbox::BoundingBox{T}; kwargs...)

Draw a BoundingBox as a wireframe, ie series of lines.

draw!(scene::Makie.LScene, ass::LensAssembly; kwargs...)

Draw each element in a LensAssembly, with each element automatically colored differently.

draw!(scene::Makie.LScene, sys::AbstractOpticalSystem; kwargs...)

Draw each element in the lens assembly of an AbstractOpticalSystem, with each element automatically colored differently, as well as the detector of the system.

draw!(scene::Makie.LScene, rays::AbstractVector{<:AbstractRay{T,N}}; kwargs...)

Draw a vector of Ray or OpticalRay objects.

draw!(scene::Makie.LScene, traces::AbstractVector{LensTrace{T,N}}; kwargs...)

Draw a vector of LensTrace objects.

draw!(scene::Makie.LScene, trace::LensTrace{T,N}; colorbysourcenum::Bool = false, colorbynhits::Bool = false, kwargs...)

Draw a LensTrace as a line which can be colored automatically by its sourcenum or nhits attributes. The alpha is determined by the power attribute of trace.

draw!(scene::Makie.LScene, ray::OpticalRay{T,N}; colorbysourcenum::Bool = false, colorbynhits::Bool = false, kwargs...)

Draw an OpticalRay which can be colored automatically by its sourcenum or nhits attributes. The alpha of the ray is determined by the power attribute of ray. kwargs are passed to draw!(scene, ray::Ray).

draw!(scene::Makie.LScene, ray::Ray{T,N}; color = :yellow, rayscale = 1.0, kwargs...)

Draw a Ray in a given color optionally scaling the size using rayscale. kwargs are passed to Makie.arrows.

draw!(scene::Makie.LScene, du::DisjointUnion{T}; kwargs...)

Draw each Interval in a DisjointUnion.

draw!(scene::Makie.LScene, intervals::AbstractVector{Interval{T}}; kwargs...)

Draw a vector of Intervals.

draw!(scene::Makie.LScene, interval::Interval{T}; kwargs...)

Draw an Interval as a line with circles at each Intersection point.

draw!(scene::Makie.LScene, intersection::Intersection; normal::Bool = false, kwargs...)

Draw an Intersection as a circle, optionally showing the surface normal at the point.

draw!(scene::Makie.LScene, lines::AbstractVector{Tuple{AbstractVector{T},AbstractVector{T}}}; kwargs...)

Draw a vector of lines.

draw!(scene::Makie.LScene, line::Tuple{AbstractVector{T},AbstractVector{T}}; color = :yellow, kwargs...)

Draw a line between two points, kwargs are passed to Makie.linesegments.

draw!(s::Makie.LScene, point::AbstractVector{T}; kwargs...)

Draw a single point, kwargs are passed to draw!(scene, points::AbstractVector{AbstractVector{T}}).

draw!(scene::Makie.LScene, points::AbstractVector{AbstractVector{T}}; markersize = 20, color = :black, kwargs...)

Draw a vector of points. kwargs are passed to Makie.scatter.