2. How LensLab Does Ray Tracing



2.1 How Rays Accumulate Information

2.2 Built-in Ray Sources

2.3 Sequential Tracing and Light Path

2.3.1 Light Source Ordering Example
2.3.2 Component Ordering Example

2.4 Nonsequential Tracing and Resonate

2.4.1 Three-mirror Resonator
2.4.2 A Subtle Substitute for Resonate

2.5 Rays and Surfaces

2.5.1 How Rayica Generates Rays

2.5.2 Component Surfaces

Faces and Edges
Rolling Your Own Edge

2.6 Special Considerations with PropagateSystem

2.6.1 QuickSurfaceSort
2.6.2 Confine
2.6.3 Unconfine


Certain details of LensLab's ray tracing methods commend themselves to study.  By knowing which methods to employ when, you can optimize both your time and LensLab's results.  By knowing how LensLab performs ray-tracing, you can avoid various pitfalls associated with specific types of optical systems.

A large number of optical systems do not demand such fine-tuning.  LensLab handles them well using default methods.  Other optical systems, however, require more careful thought and some knowledge of LensLab's internal workings.

Most optical systems are sequential in nature.  An entrance aperture accepts incoming light, which then propagates from one element to the next, finally emerging from an exit aperture or terminating at an image plane.  A distinguishing feature of such systems is that a given ray intercepts a given surface only once.  All that need concern you about these systems is the order in which components are listed.  This order should match the intended light path of the system.

Other systems do not behave in this straightforward manner.  A ray can intercept the same surface more than once.  For example, a laser cavity involves two opposing mirrors.  The same ray may bounce between the mirrors many times before emerging from the laser.  In an optical fiber, the same ray may bounce off the inner surface many times before finally emerging from the end.

These systems require special handling.  LensLab makes a fundamental distinction between sequential and nonsequential systems.  This chapter discusses how and when LensLab invokes nonsequential ray-tracing.

Sometimes a component presents many faces to the external environment, such that light can enter from any of several bounding surfaces; only one of these surface illuminations has any bearing on the analysis.  Light entering through other surfaces is irrelevant to the design.  This situation also benefits from a knowledge of LensLab's ray-tracing details.  You can restrict LensLab's analysis to the entrance surface of interest.

None of these scenarios presents any difficulty to LensLab, but all of them require a measure of forethought tied to an understanding of the LensLab features that handle them.

In LensLab, Ray and Component are used to store information about an optical system.

Component represents an optical component by housing "genetic" information including: the shapes of the surfaces in the component, how the component is graphically rendered, and how rays get traced through the component. Ray holds the memory of the ray trace by holding information about a particular surface intersection point with a particular ray.  As a single ray propagates through the list of components, a new Ray is created each time a surface is touched by the ray. Eventually a single ray propagating through a list of components will create as many Ray objects as there are intersecting surfaces. When the ray tracing has finished, the returned list of created Ray objects describes all of the intersection points along every ray-tracing path.

If a ray intersects a surface, then the surface uses and modifies the Ray object's contents depending on the surface's characteristics. In this world of Ray and Component objects, the Ray objects are passive, acting as vehicles carrying information between the different component surfaces. The Component objects are fixed in space, but have the ability to read information from incoming Ray objects and create new information for outgoing Ray objects. Component objects have the ability to create new information, destroy old information, and modify existing information, including their own structure.

There are several other chapters that discuss important aspects about ray tracing. Chapter 9 discusses the parameters contained in Ray expressions. Chapter 10 explains the genetics housed in Component expressions. Chapter 5 shows how these component genetics can be built-up using "genetic building block" functions.

Go to list of topics

Loading LensLab

Make sure that the LensLab package is located either in the home directory, or on a directory path recognized by Mathematica for packages. The LensLab package is named LensLab.m and located in the LensLab directory, and the LensLab package is loaded with the following expression.



LensLab version 1.5 is now loaded.

This loading process should only take a few seconds. In addition to being loaded as a package, the LensLab.m file is formatted as a Mathematica notebook. The LensLab source code is made accessible so that you can develop new functions of your own by studying LensLab's built-in functions. This is particularly helpful when you wish to model new component ideas in LensLab. However, you should receive permission from Optica Software before distributing any user-created functions that are derived from the LensLab source code. The unauthorized distribution of LensLab-derived code may be a LensLab license agreement violation or a copyright infringement.

Go to list of topics

2.1 How Rays Accumulate Information

With each new surface encounter, new Ray objects are created. These Ray objects are used in upcoming surface encounters, while the "replaced" Ray objects get stored and reported at the end of the ray-tracing operation.

In Ray, ray-tracing information is stored as rules. As shown below, each Rule is displayed by an arrow pointing from a label element to an information element. These rules provide a convenient notation for labeling and identifying specific pieces of information within the larger Ray body. Because rules contain labels, the list position of the rule is not important for its identification. As each new Ray replaces an older Ray at a surface point, rules are added and removed. Before propagating through any optics, we examine a typical Ray using CreateRay in combination with InputForm.




Ray[OpticalMedium -> Air, RayEnd -> {0., 0., 0.}, RayLineRGB -> {0.03874742970479889, 0.9161617552424037, 0.03984721226918141}, RayPointRGB -> {0., 0., 0.},
RayStart -> {0., 0., 0.}, RayTilt -> {1., 0., 0.}, RefractiveIndex -> 1.000877]

Here we have used InputForm to examine the Ray object's contents and CreateRay to initialize the Ray object's parameters. Next we can use Length to find the number of elements contained in Ray.





Let us trace a single ray through a simple lens element and look at the output to see how the last Ray is different from the initial Ray shown above.


opticalsystem = DrawSystem[



OpticalSystem[{Ray, Ray, Ray}, {PlanoConvexLens, Boundary}, -OpticsGraphics-]

We see that the output from DrawSystem is a list of objects headed by OpticalSystem, that includes Ray objects, Component objects, and "-OpticsGraphics-" which holds component rendering information.  For our purposes, from this output, we are only interested in examining the last Ray given and nothing else. We use RaySelect to extract Ray objects.

RaySelect[objectset, selectionproperties] takes an object set carrying Ray objects, and returns a filtered list of Ray objects that shares the common set of traits specified in selectionproperties.

With RaySelect, we need a selection property to choose a particular Ray. In this case we want to examine the last Ray generated, so we use IntersectionNumber -> 3. IntersectionNumber is further discussed in Section 9.3.


InputForm[RaySelect[opticalsystem,IntersectionNumber -> 3]]


{Ray[ComponentIncrement -> Automatic, ComponentNumber -> 2, ConfinedNumber -> 6, ConfinedPosition -> 1, GenerationNumber -> 3, Intensity -> 100.,
  InternalDirectionChange -> False, IntersectionNumber -> 3, OffAxis -> {0., 0.}, OpticalLength -> 209.14368897094312, OpticalMedium -> Air,
  RayEnd -> {200., 29.734726731199345, 0.}, RayLength -> 83.56970706111035, RayLineRGB -> {0.03874742970479889, 0.9161617552424037, 0.03984721226918141},
  RayPointRGB -> {0., 0., 0.}, RayStart -> {120.66130167367106, 3.480833950059889, 0.}, RayTilt -> {0.9493715021438631, 0.3141556157656661, 0}, RefractiveIndex -> 1.000877,
  SurfaceBoundary -> {200, 200}, SurfaceCoordinates -> {-29.73472673119934, 0}, SurfaceIncrement -> 1, SurfaceNormalMatrix -> {{-1., 0., 0.}, {0., -1., 0.}, {0., 0., 1.}},
  SurfaceNumber -> 6, UnconfinedIncrement -> 1, UnconfinedPath -> {1, 2, 3, 4, 5, 6}, UnconfinedPosition -> 6]}

Note that the above Ray object contains IntersectionNumber -> 3 as a parameter as required by our chosen selection property. Let's check the length of this Ray.





So we notice that Ray grew from seven rule parameters at the beginning to twenty-five parameters at the end, demonstrating how Ray objects accumulate additional information from the different surfaces they are propagated through. As shown in Section 1.10, you can examine any of these ray parameters for their values using ReadRays.

Go to list of topics

2.2 Built-in Ray Sources

You can trace a single ray through the system by including Ray[] within the list of objects evaluated by DrawSystem. In addition, LensLab has seven built-in ray source functions.

CircleOfRays[radius, options] creates a set of rays, starting in the y-z plane, equally distributed on the surface of a cylinder placed symmetrically about the positive x axis.

ConeOfRays[conicangle, options] creates a set of rays, starting at the origin, equally distributed on the surface of a cone placed symmetrically about the positive x axis.

GridOfRays[size, options] creates a rectangular or circular grid of rays, starting in the y-z plane, distributed at a grid of intervals about the positive x axis.

LineOfRays[linewidth, options] creates a set of rays, starting at the y axis, lying in the horizontal plane, equally distributed within the specified linewidth, and directed down the positive x axis.

PointOfRays[startingpoint, pupilsize, options] creates a set of rays distributed within a funnel-shaped volume that is oriented down the positive x axis.

RainbowOfRays[{minwavelength, maxwavelength}, options] creates a set of overlapping rays equally distributed over the specified range of wavelengths given in microns, starting at the origin and directed down the positive x axis.

WedgeOfRays[wedgeangle, options] creates a set of rays, starting at the origin, equally distributed across a horizontal wedge, and fanned symmetrically about the positive x axis.

Definitions of ray source functions.

By using one of the Move commands, ray sources can be easily repositioned. In addition to the built-in source functions, more complex ray sources can be easily created by listing together several of the built-in sources in combination with Move. You can learn more about defining new ray-source functions by examining the built-in definitions in the "Source Functions" section of the LensLab package.

Go to list of topics

2.3 Sequential Tracing and Light Path

Sequential ray-tracing is the default mode in LensLab.  The defining characteristic of this mode is that LensLab determines the order of surface intersections in advance, by inspection of input, without respect to physical geometry.  The system listing given by the user input fixes the order of intersection tests.  A given element may only transmit rays to components following it in the list.  LensLab checks the possible intersections in order, and does not backtrack.  Sequential ray-tracing is the most efficient mode of operation for this very reason.  Speed is the primary reason for using it.

Go to list of topics

2.3.1 Light Source Ordering Example

Sequential tracing demands that your system enumeration matches the light path through the system.  You must order the elements of your system in correct sequence.  Otherwise you will not obtain a correct trace.

Here is a correct system with two sources and two lenses.  The system elements appear in proper order for sequential ray tracing.  The sources precede the lenses, and the lenses appear in light-path order. Each beam refracts through each lens.


greenSource = Move[WedgeOfRays[15, NumberOfRays7, RayLineRGB {0, 1, 0}], {-50, ... ens[80, 50, 10], 50], Boundary[100] }, PlotTypeTopView] ;


Sequential tracing works correctly when the system list is properly ordered.

A seemingly minor alteration to the previous input will cause the sequential trace to fail.  If redSource follows L1 in the system enumeration, then redSource rays will ignore L1.  Only components listed after redSource can affect its rays.  You are not changing physical geometry here, merely reordering input.  The system list contains exactly the same elements, all in exactly the same geometric positions, the order of the Mathematica input has changed.


greenSource = Move[WedgeOfRays[15, NumberOfRays7, RayLineRGB {0, 1, 0}], {-50, ... ens[80, 50, 10], 50], Boundary[100] }, PlotTypeTopView] ;


L1 now fails to refract the lower beam from redSource because the system list does not follow light-path order.

The lower beam from redSource acts as though L1 does not exist.  The beam refracts only through lens L2.  The lenses have not moved, and the beam's point of origin has not changed, but its computed light path has changed.  This trace is therefore incorrect.

Go to list of topics

2.3.2 Component Ordering Example

Components too must appear in proper order for sequential ray tracing.  The system below has one source and four components, including an invisible Boundary.


source = Move[WedgeOfRays[15, NumberOfRays7], -50] ; lens = BiConvexLens[60, 50, 14] ; ... 2371;prism, mirror, boundary}, PlotTypeTopView] ;


The prism functions correctly when the system list order mimics the light path.

Pay particular attention to the prism.  The only system elements that can transmit rays to the prism are those preceding it in the system list.  Observe what happens when you list mirror before prism.  The same elements still precede prism, but with the addition of mirror to the set.


DrawSystem[ {source, lens, mirror, (* moved ! *)prism, boundary}, PlotTypeTopView] ;


Putting mirror ahead of prism causes the sequential trace to fail.  The prism does not refract the beam.

The prism no longer affects the rays.  The trace would be identical if the prism was removed altogether.  The same elements still precede prism in the system enumeration, but the trace has failed.  Why?

The trouble arises from sequential processing.  In this kind of tracing, LensLab considers the system enumeration as a checklist of candidates for intersection or ray generation.  There is no backtracking.  Once LensLab reaches a certain point in the checklist, it does not return to previous items.

In this case the source is the first element, so LensLab generates its rays.  LensLab is then finished with source and checks it off the list.  The candidates are now, in order, {lens, mirror, prism, boundary}.  LensLab checks for intersections with lens, finds them, and checks lens off the list.  Now the remaining candidates are, in order, {mirror, prism, boundary}.  LensLab checks for intersections with mirror, finds them, and checks mirror off the list.  LensLab fails here to consider prism as a candidate.  This failure is an artifact of sequential processing.  From a processing viewpoint, the mirror is first in line, and therefore has priority.

Now the rays have physically bypassed prism and bounced off mirror.  LensLab has only {prism, boundary} remaining as candidates to the list.  The rays from mirror do not intersect prism, so LensLab checks prism off the list.  This action leaves only {boundary} on the list.  The rays from mirror do indeed intersect boundary.  LensLab finds their intersection and checks boundary off the list.  Now the checklist is empty, so LensLab terminates the trace.  (You generally always want a Boundary object as the last element in your system list.)

There are two ways to repair this failed trace.  One is to correct the system order to follow the physical light path.  Yet in many systems, the light path is more complicated than this simple example suggests.  A more general solution is LensLab's Resonate function.  Resonate instructs LensLab to perform nonsequential ray tracing for the set of components that are supplied as arguments.  In this mode, all surfaces are candidates for intersection at all times.

We wrap Resonate around the system components without changing their order.  Resonate accepts a list of components as its argument.  The argument here is the list {lens, mirror, prism}.


DrawSystem[ {source, Resonate[{lens, mirror, prism}, "system"], boundary}, PlotTypeTopView] ;


The LensLab function Resonate enforces nonsequential ray tracing.  The trace is now correct.

Components inside the list argument to Resonate may appear in any order.  The Resonate function effectively becomes just another Component object in your system list.  The trace is sequential before and after the Resonate component.  Inside the Resonate component, it is nonsequential.

Go to list of topics

2.4 Nonsequential Tracing and Resonate

Sequential ray-tracing cannot handle all possible systems.  An important category of systems not amenable to sequential processing is that of resonating cavities.  A general resonating cavity is any component or system that confines light rays temporarily or indefinitely, not transmitting them directly.  This definition encompasses laser cavities, optical fibers, integrating spheres, and so on.  Such components and systems cause light to wander within their boundaries prior to escape.

More specifically, from the viewpoint of LensLab, a resonating cavity is any component or system for which it is possible that the same ray may hit the same surface (a) more than once or (b) from more than one other surface.  Proper analysis of a resonating cavity requires nonsequential ray tracing.  You instruct LensLab to apply nonsequential methods by means of the Resonate function.  This function creates a new Component object out of the constituents of the cavity system.

Go to list of topics

2.4.1 Three-mirror Resonator

The following mirror cavity demonstrates the need for the Resonate function.  First we trace the system without calling Resonate.  The first attempt will be a sequential ray trace.  The source is a SingleRay.  Note that the mirrors have no thickness.  In LensLab terms, each of them is a single surface, not a double surface.


M1 = MoveReflected[ThinSphericalMirror[-300, 100], {300, 0}, {150, 300}, {0, 0}] ; M2 = MoveRe ... 62371;M2, M3, boundary}] ; ShowSystem[ringSys, PlotTypeTopView] ;



Three mirrors in a ring arrangement constitute a resonating cavity.  The trace fails in this case because Resonate was not used.

Instead of circulating between the mirrors, the ray terminated at the Boundary.  Not instructed otherwise, LensLab assumed that the trace was sequential.  The splitter was the first element encountered.  One transmissive and one reflective ray were created at the splitter.  LensLab then searched the system list for candidate components which might intersect these secondary rays.  The only component appearing in order after M3 was Boundary.  LensLab therefore intersected the two secondary rays with Boundary and terminated the trace.  Physically, the reflected secondary ray should have struck M1.  However owing to its position in the system list, M1 was no longer a candidate for intersection.

A natural temptation would be to reorder the system list to place M1 in sequence after splitter.


ringSys = DrawSystem[{source, M3, M1, M2, boundary}] ; ShowSystem[ringSys, PlotTypeTopView] ;



The sequential ray trace does a little better with a revised system order.  However this trace is still incorrect because there should be a second encounter with the beam splitter.

The reordering has helped somewhat.  The ray correctly bounces off all three components.  The problem is that the ray should encounter the beam splitter a second time after its reflection from M2.  However it does not because this is still a sequential trace, and LensLab considers the trace complete once the system list is exhausted.  After M2, all that is left in the system list is boundary.  LensLab therefore intercepts the ray from M2 at the boundary and terminates the trace.

What you need is a way to ask LensLab to trace nonsequentially.  That is, LensLab should not ignore a component just because it was already hit once or appears in a certain input order.  The Resonate function causes LensLab to treat its arguments as a single Component with the property of a resonating cavity.


ringSys = DrawSystem[{source, Resonate[{M3, M1, M2> ...  "cavity"], boundary}] ; ShowSystem[ringSys, PlotTypeTopView] ;



Now we have a true resonating cavity by means of Resonate.  This trace is correct.

Resonate properly handles this cavity.  Rays circulate within the cavity and occasionally make their way out through the beam splitter.

Any time you ask LensLab to trace a resonating system, there is a question of when to terminate the trace.  In principle it could go on forever.  LensLab creates a fresh Ray object at each new intersection.  Some of the Ray objects do leave the cavity through the splitter.  However, many more are simultaneously created as reflected rays.  These could potentially circulate endlessly.

There are two options used by LensLab to regulate such traces: GenerationLimit and ThresholdIntensity.

One way that LensLab terminates such a trace is by restricting the number of Ray creation events.  The maximum number is given by GenerationLimit, an option to DrawSystem.  The default value of the limit is moderately large, but it can be set to any value.


ringSys = DrawSystem[{source, Resonate[{M3, M1, M2> ... 1;boundary}, GenerationLimit  7] ; ShowSystem[ringSys, PlotTypeTopView] ;



The effect of lowering the ray GenerationLimit for the resonating cavity.

A second way that LensLab terminates such a trace is by monitoring the Intensity values of the circulating rays.  The minimum Intensity value permitted for a Ray is given by ThresholdIntensity, also an option to DrawSystem.  For lossy systems, such as this cavity example, this provides a more natural ending to the trace.

Go to list of topics

2.4.2 A Subtle Substitute for Resonate

Sometimes you need Resonate even for mundane geometries.  If the light path backtracks on itself, you generally need Resonate.  However a subtle trick can allow you to trace some of these systems sequentially.  This trick accomplishes the same results as Resonate but without the speed penalty.  It requires careful analysis of the light path.  The idea is to include the same component multiple times in your system list.

Here is a system of one source and three components traced sequentially (without Resonate).  Sequential tracing cannot correctly analyze this system as given.


source = Move[LineOfRays[45, NumberOfRays6], 5] ; lens = Move[BiConvexLens[100, 50, 10 ... urce, lens, mirror, boundary}, PlotTypeTopView] ;


Even a simple geometry may demand nonsequential ray tracing.  The reflected rays in this sequential trace do not refract in a backward direction.

The trace has failed.  The reflected rays should have refracted through the lens a second time after reversing direction at the mirror.  Instead, the rays ignored the lens after the reflection.  We now call Resonate to advise LensLab that the system includes a nonsequential section.  This will repair the trace, but at the cost of slower execution associated with nonsequential tracing.


DrawSystem[ {source, Resonate[{lens, mirror}, "system"], boundary}, PlotTypeTopView] ;


Nonsequential tracing with Resonate handles this case properly.  Nonsequential tracing, however, incurs a speed penalty.

Resonate is generally safe under all circumstances; it has correctly traced this system.  However, it has also increased the execution time.  With a little care, we can attain the same results using sequential ray-tracing.  The light path crosses the lens two times.  Therefore, sequential tracing will work if we enter the lens two times in the system list.  One lens entry comes before mirror, and the other comes after mirror.


DrawSystem[ {source, lens, mirror, lens, boundary}, PlotTypeTopView] ;


For speed optimization, you can trace resonating systems sequentially if you pay careful attention to the light path and system list.

This trick is rather subtle and not recommended until you are well acquainted with LensLab.  It requires a careful, accurate assessment of the light path for your system, and fastidious attention to the order of your system list.  By contrast, Resonate accepts components in any order.

Go to list of topics

2.5 Rays and Surfaces

LensLab records information about ray traces by means of Ray objects.  LensLab creates these objects dynamically to monitor the progress of the trace.  When the trace is complete, Ray objects serve as data repositories for ReadRays, the main function for system examination.

Surfaces constitute a major topic in LensLab.  Certain issues surrounding component surfaces may influence your ray-tracing results.  Moreover, you can customize LensLab's behavior with various surface-related function calls.

Go to list of topics

2.5.1 How LensLab Generates Rays

Any ray trace spawns a certain number of Ray objects.  Each Source generates its particular set of Ray objects.  These propagate to the surfaces of various Component objects in the system.  At every intersection of a Ray with a Component, LensLab determines what action the Component has on the light.  Common interactions include reflection, refraction, beam splitting, and absorption.

LensLab embeds intercept data within the Ray object.  This data includes angle of incidence and point of intercept.  However, recording the entire optical action of a Component necessitates more software activity than a mere alteration of one Ray.  A solitary Ray cannot embody all of the relevant information.  Among other issues, Ray can only record a single straight path, not a complete, piecewise-linear path.  LensLab therefore creates multiple Ray objects to capture the total optical effect.

When Ray intersects a PlanoConvexLens, for example, LensLab registers the intercept location inside Ray, then creates another Ray to follow a refracted (but straight) path through the lens.  This second Ray encounters the back surface of the lens, where it refracts in turn.  LensLab registers its intercept location and creates a third Ray that leaves the lens.  So three distinct Ray objects record the overall light path.  This case is illustrated below.


LensLab creates Ray objects 2 and 3 to record the optical effect of lens L on Ray number 1.

Two Rays were created where one existed before.

When Ray intercepts a Mirror, the result is similar.  LensLab registers the intercept and creates a reflected Ray to follow the new light path.  There are two Ray objects in this case.  A half-silvered mirror generates both a reflected and a transmitted Ray.

Go to list of topics

2.5.2 Component Surfaces

A primary task of Component objects is to maintain information about surfaces.  In general, you may view Component as a set of surfaces.  Component can have anywhere from one to dozens of surfaces.  The surfaces may be physically internal or external to the component.  Each surface has a parametric surface description, a surface normal function, and various other properties.

Surfaces within Component are numbered.  The number of a surface is called its SurfaceNumber.  There is no special meaning to surface numbers beyond their mutual uniqueness within Component.  Most LensLab components have a canonical surface ordering which assumes some default placement of the component.  This numbering system is merely convention; it has no effect on ray tracing.  Rays may enter through any external surface, whatever its number.

The reason to be concerned about surface numbers is for extraction of post-trace data.  When Ray intersects a surface, LensLab records certain identifiers into Ray that pertain to the intersection.  These include ComponentNumber, for the particular component within the system, and SurfaceNumber, for the particular surface within the component.  You often use these numbers in conjunction with ReadRays to isolate specific intercepts for examination.

Go to list of topics

Faces and Edges

Visualizing Component objects as organized collections of surfaces, instead of contiguous solids, more directly captures LensLab's behavior.  Think of multiple surfaces floating in space and you have the idea.  LensLab never defines the volume occupied by a component, but only selected faces.  The selected faces are those important for optical design.

This convention can break down; it fails only under peculiar conditions that seldom occur in practice.  Component edges typically matter very little.  Lenses are not designed to pass light through their edges, but through their faces.  LensLab properties of lens edges are mostly irrelevant to optical design work.  For this reason, most built-in LensLab components lack edge definitions.  Thus a PlanoConvexLens has only two surfaces, one planar and one convex; LensLab omits the lens circumference.  As far as LensLab is concerned, a PlanoConvexLens is a pair of infinitely thin surfaces with open space between them.

You may construct a highly contrived optical system to demonstrate how this convention works, and where it breaks down.  Following is a PlanoConvexLens turned by 90 degrees.  This rotation orients the faces away from the optic axis.  Two light sources illuminate the lens edge-on, down the optic axis: one exactly, the other through the curved face.  The light shining exactly edge-on passes straight through, unaffected, between the two faces of the lens.


redlight = Move[LineOfRays[14, NumberOfRays10, RayLineRGB  {1, 0, 0}], {.1, -3 ... rue, ,,  , AxesLabel  {"x", "y", "z"}}], , ]}], ;}]


The standard PlanoConvexLens has no optical edge.  The edge-on (green) light travels between the faces without refraction.  The slightly off-sides light does refract, because it interacts with one of the faces.

LensLab's graphical display indicates an edge for aesthetic reasons only.  From a ray-tracing standpoint, this PlanoConvexLens is a pair of refractive surfaces floating in space; it has no edge at all.  This fact is proven by the trace above.  Only those rays striking a lens face experience refraction.  The remaining rays pass between the two  faces without disturbance.

Whenever edges concern your system, you should carefully explore the implications of LensLab's default treatment.

Go to list of topics

Rolling Your Own Edge

In some rare cases, you may need to create modified components with proper optical edges.  LensLab allows such definitions.  They require some facility with surface data  and a bit of creativity.  This section demonstrates how to accomplish the edge-completion task for PlanoConvexLens above.  This topic is somewhat advanced and may be skipped without penalty.

We can mate the two faces of PlanoConvexLens by means of CylindricalLens, which is shaped and placed in a highly unusual configuration.  The CylindricalLens will constitute the edge of the PlanoConvexLens.  In other words, the two main faces of CylindricalLens will form a single contiguous edge for PlanoConvexLens.  The result will be a completely closed Component that properly represents the full physical behavior of a plano-convex lens.

To produce this modified component, you must obtain the parameters of CylindricalLens.  The cylindrical lens will be rotated 90 degrees relative to the plano-convex lens.  Each face of CylindricalLens will cover a 180-degree arc.  The two radii of curvature will be identical but of opposite polarities, indicating opposite directions of curvature.  The curvature radius is one-half the aperture diameter of PlanoConvexLens, or 50/2.

If you view PlanoConvexLens edge-on, the outline of its edge is rectangular.  So the face-on aperture of CylindricalLens must also be rectangular.


sidelens = PlanoConvexLens[75, 50, 15, GraphicDesignWire] ;  RowBox[{RowBox[{D ...  Frame  True, ,, , PlotRange  {{-5, 35}, {-30, 30}}}], , ]}], ;}]


This side view of PlanoConvexLens reveals the rectangular aperture shape required for CylindricalLens, which will form its edge.

A list of two numbers specifies a rectangular aperture.  The height of the rectangle is the diameter of the PlanoConvexLens (50 units).  The width of the aperture is more difficult to ascertain.  It is the distance between the rim of surface 1 and the flat of surface 2.  Finding this distance requires some digging into the data of PlanoConvexLens.  The thickness of PlanoConvexLens is measured from the peak of its convex face to its planar face.  The width of the CylindricalLens aperture must equal this total thickness, less the thickness of the convex surface only.  Use SurfaceFunction of the convex face (surface 1) to compute the aperture width.


cylApertureHeight = 50 ; surfaceFunc = Part[SurfaceFunction /. (Surfaces/.lens), 1, 1] cylApertureWidth = 15 - surfaceFunc[0, 50/2]


RowBox[{RowBox[{RowBox[{-, RowBox[{RowBox[{(, RowBox[{RowBox[{1513.52, }], -, #1^2, -, #2^2}], )}], ^, (1/2)}]}], +, 38.904}], &}]



Resonate will now consolidate edgelens with the previous lens definition to create newlens as a single component.  edgelens is nudged into position by nested Move calls.


edgelens = CylindricalLens[25, -25, {cylApertureHeight, cylApertureWidth}, 50, SurfaceRenderin ... dgelens, -25],  {12, 0, 0}, {0, 0, 1} ] }, "newlens"] ;

A test of the new lens proves it out.  This optical system is identical to the previous case but has lens replaced by newlens.


RowBox[{RowBox[{DrawSystem, [, , RowBox[{RowBox[{{, , RowBox[{Boundary[100], , ... True, ,,  , AxesLabel  {"x", "y", "z"}}], , ]}], ;}]


This customized PlanoConvexLens has an edge.  The edge-on (green) light now refracts.

You must ensure that both the edge and the original component are made of the same material.  Since this material was unspecified in the present case, it defaulted to BK7 glass for both the plano-convex and the cylindrical lens.

Go to list of topics

2.6 Special Considerations with PropagateSystem

This section covers a number of advanced considerations with the use PropagateSystem and DrawSystem. These features were originally developed for the Optica package in the days when computers operated much more slowly. Although these features are also present in LensLab, they are no longer required for many present-day modelling applications and are only presented here for the sake of completeness. As such, this section may be skipped by most users without penalty.

Go to list of topics

2.6.1 QuickSurfaceSort

When propagating rays within a component, LensLab must decide in which order to check surfaces for intersection, and which of all possible intersections is correct.  This problem is similar to that of sorting components at the system level.  At the lower level of an isolated component, the internal surfaces must be sorted in some kind of priority order for intersection processing.

LensLab uses geometry to make intelligent guesses about which surfaces should be checked first.  The actual details of LensLab's workings in this regard are complex.  However, LensLab presents an option called QuickSurfaceSort that controls the procedure in a general sense.  This option applies to DrawSystem and PropagateSystem.  Note, however, that the QuickSurfaceSort option does not apply to TurboTrace and TurboPlot applications, since they do not directly employ QuickSurfaceSort at all (other for gaining scout trace information from PropagateSystem). In general, TurboTrace and TurboPlot always uses the QuickSurfaceSort->False setting.

In previous versions of LensLab, PropagateSystem used QuickSurfaceSort→True as its default setting in order to maximize its ray-trace speed. Since the advent of TurboTrace, however, speed considerations for PropagateSystem have been supplanted by the concern for robust, trouble-free use and QuickSurfaceSort→False is now the default setting. When you set QuickSurfaceSort→True, LensLab employs an efficient, but occasionally fallible, method for sorting surfaces within components.  The method compares center-points of surfaces to sort them from nearest to farthest.  Sometimes you must set QuickSurfaceSort→False to obtain a correct trace.  This setting causes LensLab to perform actual surface intersections to determine the sort order.  Naturally, this extra calculation incurs a speed penalty, but it is more robust and LensLab has available closed-form intersection formulas for certain built-in surfaces like spheres, planes, and conics.

The next example demonstrates how QuickSurfaceSort affects the outcome of a trace.  It is borrowed from the section on resonating cavities.  The difference here is that we give the mirrors thickness.  This change means that LensLab must sort the surfaces within each mirror component for purposes of intersection processing.  If a ray can intersect two surfaces, LensLab must decide which of the two it will strike.


source = Move[Ray[], {150, 20}] ; M1 = MoveReflected[SphericalMirror[300, 100, 10], {300, 0},  ... 2371;system, QuickSurfaceSort  True, PlotType  TopView] ;


With QuickSurfaceSort→True, the trace fails in this particular geometry.

The trace is wrong.  Evidently, LensLab checked for an intersection with the back surface of M2 before the front surface.  This outcome shows that LensLab's default surface processing failed for this particular geometry.

LensLab produced the wrong outcome in spite of Resonate.  The Resonate directive ensures that all surfaces will be checked; it does not affect the order in which they are checked.  To change this order, you must use QuickSurfaceSort.  When this option is False, LensLab uses a more exact, but slower, method of sorting surfaces inside components.


DrawSystem[system, QuickSurfaceSort  False, (* exact processing *)PlotType  TopView] ;


With QuickSurfaceSort→False, LensLab uses a more exact method of sorting surfaces within components.  This trace is correct.

It is always safe to set QuickSurfaceSort→False, but this setting, just like Resonate, incurs a speed penalty.

Go to list of topics

2.6.2 Confine

Components can receive light from any exposed surface.  Sometimes concern focuses exclusively upon light that enters through a particular surface.  This surface might be termed an entry port for the component.  This situation immediately suggests a ray-tracing optimization; LensLab should follow only those rays that enter through this surface, ignoring the rest.  The Confine function instructs LensLab to consider only specific surfaces as candidates for light entry into the component.  Please note, however, that the following discussions are not used in TurboTrace and TurboPlot, which do not directly employ confined port information at all.  As such, this section only applies to the use of DrawSystem and PropagateSystem.

Here is a custom function that creates a multi-sided prism component.  The output of this function is a set of adjoining LensSurface components consolidated by Resonate into a single, unified component.  As created, such a prism has no preferred entry port.


Clear[PolygonPrism] ; PolygonPrism[faces_, radius_, height_, options___] := Module[{halfangle, ... 1; {n, 1, faces}], top, bottom}], "system", options]] ;

This is the appearance of a 9-sided PolygonPrism.


DrawSystem[PolygonPrism[9, 50, 50, SurfaceRendering {Fill, Trace}, CrossRenderingEmpty]] ;


A custom prism.

The following trace shows how the prism refracts light with no entry port defined.  All exposed surfaces that happen to be facing the light will refract it.


light = Move[LineOfRays[100, NumberOfRays9], 5] ; prism = Move[PolygonPrism[9, 50, 50, ... ight, prism, boundary}, PlotTypeTopView, DefaultFont {"Courier", 18}] ;


The prism refracts indiscriminately with no privileged entry ports defined.

Suppose that surface 3 is an entry port; light impinging on other surfaces is considered irrelevant to the design.  Then you must notify LensLab to simplify the trace.  The prism is identical, but you Confine the light to enter through surface 3.


confinedprism = Confine[prism, {3}] ; DrawSystem[{light, confinedprism, boundary}, PlotTypeTopView, DefaultFont {"Courier", 18}] ;


Confine causes the prism to receive light only through designated surfaces.

Light that would normally refract through other surfaces skips past the prism altogether, terminating at the boundary.  Light allowed into the prism through the entry port (surface 3) reflects internally and eventually exits.  Once inside the prism, light can exit through any surface.

You may define more than one surface as an entry port.  Instead of a single number, use list notation, Confine[prism,{3,4,5}], for example.

Go to list of topics

2.6.3 Unconfine

Sometimes you must eliminate entry port restrictions.  The component will then receive light from all exposed surfaces.  The LensLab function Unconfine accomplishes this task; it effectively cancels the action of Confine.  For instance, you might Unconfine the confinedPrism that was defined above:


unconfinedprism = Unconfine[confinedprism] ; DrawSystem[{light, unconfinedprism, boundary}, PlotTypeTopView, DefaultFont {"Courier", 18}] ;


Unconfine reverses the action of Confine.  The prism no longer has preferred entry ports.

The unconfinedPrism behaves as the original prism.  Light enters through all exposed surfaces.

Go to list of topics

Created by Mathematica  (November 4, 2005)