Skip to content

Live rendering ​

render! generates new plots on every call. For animations and interactive applications, where components are moved and the system is re-solved many times per second, use live_render! instead. It returns a handle that re-synchronizes the existing plots with the current simulation state via update_render!:

julia
using GLMakie, BeamletOptics

fig = Figure()
ax = LScene(fig[1, 1])

hsys = live_render!(ax, system)   # geometry is generated once
hbeam = live_render!(ax, beam)    # all ray segments bundled into a single plot

zrotate3d!(mirror, 1e-3)
solve_system!(system, beam)
update_render!(hsys)              # moved components: only their model transformation changes
update_render!(hbeam)             # beam path: point buffer is replaced in place
  • Objects and systems: the geometry is rendered once with render!. Kinematic changes (translate3d!, rotate3d!, ...) are then applied as a rigid model transformation of the existing plots, which costs microseconds regardless of the mesh resolution.

  • Rays, beams and beam groups: all segments are drawn by a single linesegments plot, so the number of plots does not grow with the number of rays. The number of segments may change between updates, e.g. when a component is moved out of the beam path.

  • Gaussian beamlets: the 1/e² envelope of all segments is merged into a single mesh. This applies to GaussianBeamlets, AstigmaticGaussianBeamlets and groups of astigmatic beamlets, of which every render_every-th beamlet is rendered.

live_render! draws the same plots as render!, see Rays and beams and Gaussian beamlets; it only keeps the data they are drawn from. Use remove_render! to delete the plots of a handle.

Render handle protocol ​

Every handle returned by live_render! is a BeamletOptics.AbstractRenderHandle. Packages built on BeamletOptics, e.g. BeamletOpticsGUI, use the handles through a protocol instead of the concrete handle types of the Makie extension. There are three abstract handle types: BeamletOptics.AbstractObjectRenderHandle for a component, BeamletOptics.AbstractSystemRenderHandle for a system and BeamletOptics.AbstractBeamRenderHandle for a ray, beam or beam group. They share the accessors below.

BeamletOptics.rendered Function

The object, system or beam that the render handle h shows, see AbstractRenderHandle.

source
BeamletOptics.render_plots Function
julia
render_plots(h::AbstractRenderHandle) -> AbstractVector

The plots of the render handle h; of a system handle, the plots of all its object handles. The plots of an object handle may be replaced by update_render!, hence do not keep them.

source

A system handle groups the handles of its objects. It can be searched and changed at runtime:

BeamletOptics.render_children Function

The object handles of the system handle h, one per rendered object (the objects of groups, not the groups). Change them via push! and delete! of h, not via the returned vector.

source
BeamletOptics.render_parent Function

The object group of the system of h that holds obj (an object or a group), or nothing if obj is at the top level or not in h.

source
Base.push! Method

Adds the object handle oh at the top level of the system handle h, e.g. of an object added to the scene after live_render!, such that update_render! and pick_object of h include it.

source
Base.delete! Method

Removes the object handle oh from the system handle h. Its plots stay in the axis, delete them via remove_render!(oh).

source

push! adds an object handle at the top level, delete! removes it from the system handle without deleting its plots (use remove_render! for that). update_render!, remove_render! and pick_object work on any system handle through these accessors, so a handle type of your own only needs to implement them.

An object that is added to a system at runtime, see Changing a system, is rendered into the handle of the system, and removed from it again, by:

BeamletOptics.live_render! Method

Live-renders obj into the axis of the system handle h and adds it to h, e.g. an object that was added to the system via push!(system, obj) after h was created. An object group is rendered per object, with its hierarchy known to render_parent, like the groups of live_render!(ax, system). Returns the new object handles, one per rendered object. The system of h is not changed. An obj that h already renders throws an ArgumentError.

Keyword arguments are passed on as for render!. Implemented by the handle returned by live_render!(ax, system). A system handle that does not implement it throws an ArgumentError: render obj via live_render!(ax, obj) and add its handle via push!(h, oh) instead.

source
BeamletOptics.remove_render! Method

Removes obj from the system handle h: deletes the plots of obj (of all objects of an object group) from the axis and removes their object handles from h, e.g. after delete!(system, obj). The system of h is not changed. Nothing happens for an obj that h does not render. An object within a group can not be removed on its own and throws an ArgumentError: remove the group instead.

source
julia
push!(system, lens)
live_render!(hsys, lens)     # draws the lens, hsys now updates and picks it
delete!(system, lens)
remove_render!(hsys, lens)   # deletes its plots and its handle

Beam handles report how they were drawn:

BeamletOptics.render_settings Function

The settings with which the beam handle h draws its beam, at least flen (length of a final ray without intersection [m]) and render_every (every how many beams of a beam group are drawn, 1 for other beams), e.g. to find the drawn segments of a beam.

source

Overlays that are not part of a component, e.g. markers, are drawn with the function form of live_render!, which returns a handle for the plots draw creates and moves them together with x:

BeamletOptics.live_render! Method
julia
live_render!(draw, axis, x) -> AbstractObjectRenderHandle

Live-renders x via the function draw: draw() plots x in its current pose into the axis, and the returned handle moves these plots with x like those of an object, see live_render!, e.g. a marker of a thing that render! does not draw. x has a position and an orientation (or a direction), e.g. a source or an own movable type (see kinematic_trait_of):

julia
h = live_render!(ax, src) do
    scatter!(ax, [Point3f(position(src))]; color = :orange)
end

If no suitable backend is loaded, a MissingBackendError will be thrown.

source

pick_object finds the object that owns a picked plot. By default all plots of an object select it; a method of pickable_plots restricts this, e.g. to exclude an outline. The colors of the material classes of the active look are available via look_colors:

BeamletOptics.pickable_plots Function
julia
pickable_plots(x, plots) -> AbstractVector

The plots among the plots of x that select x in pick_object, by default all. Add a method for the type of x to exclude plots, e.g. the outline of a marker, whose clicks should reach what lies behind it.

source
BeamletOptics.look_colors Function
julia
look_colors() -> Dict{Symbol, RGBf}

The colors of the material classes (:refractive, :reflective, :coating, :polarizer, :detector, :mechanics, :interface) of the active look, see set_render_look, e.g. to recolor the rendered objects of a class for a dark background.

Needs the Makie extension, i.e. a loaded Makie backend.

source