Ray Tracing

Tilting & De-centering Components

Complex ray tracing through misaligned or deliberately folded optics.

BeginnerRay TracingNumPy backend8 min read

Introduction

This tutorial shows how to tilt and de-center surfaces in a lens. We will use a spot diagram to show how ray intersection on the image plane are impacted by tilt/decentering.

Core concepts used

rx=np.radians(theta)
Rotates the surface (or group) around the X-axis. Rotations are always in radians.
dy=value
Translates (de-centers) the surface vertex along the Y-axis relative to the optical axis.
analysis.SpotDiagram(lens)
Calculates the geometric distribution of rays on the image plane.

Step-by-step build

1

Set up and examine the baseline aligned lens

Import the required libraries, build a simple singlet with all surfaces perfectly aligned, draw the layout, and view the baseline spot diagram.

python
import numpy as np

from optiland import analysis, optic

lens = optic.Optic()

# add surfaces
lens.add_surface(index=0, radius=np.inf, thickness=np.inf)
lens.add_surface(index=1, thickness=7, radius=19.93, is_stop=True, material="N-SF11")
lens.add_surface(index=2, thickness=21.48)
lens.add_surface(index=3)

# add aperture
lens.set_aperture(aperture_type="EPD", value=25.4)

# add field
lens.set_field_type(field_type="angle")
lens.add_field(y=0)

# add wavelength
lens.add_wavelength(value=0.587, is_primary=True)

lens.draw(num_rays=10)

spot = analysis.SpotDiagram(lens)
spot.view()
Baseline aligned singlet layoutBaseline spot diagram
2

Tilt the first surface by 5° and observe spot degradation

Now, let's tilt the first surface by 5 degrees and redraw the lens.

python
lens = optic.Optic()

# add surfaces
lens.add_surface(index=0, radius=np.inf, thickness=np.inf)

# == WE ADD THE TILT TO THIS SURFACE ===============
lens.add_surface(
 index=1,
 thickness=7,
 radius=19.93,
 is_stop=True,
 material="N-SF11",
 rx=np.radians(5.0),
)
# ==================================================

lens.add_surface(index=2, thickness=21.48)
lens.add_surface(index=3)

# add aperture
lens.set_aperture(aperture_type="EPD", value=25.4)

# add field
lens.set_field_type(field_type="angle")
lens.add_field(y=0)

# add wavelength
lens.add_wavelength(value=0.587, is_primary=True)

lens.draw(num_rays=10)

spot = analysis.SpotDiagram(lens)
spot.view()
StepStep
3

Decenter the first surface by 1 mm and compare

Let's decenter the first surface of the lens.

python
lens = optic.Optic()

# add surfaces
lens.add_surface(index=0, radius=np.inf, thickness=np.inf)

# == WE DECENTER THIS SURFACE =====================
lens.add_surface(
 index=1,
 thickness=7,
 radius=19.93,
 is_stop=True,
 material="N-SF11",
 dy=1.0,  # 1 mm decenter
)
# ==================================================

lens.add_surface(index=2, thickness=21.48)

lens.add_surface(index=3)

# add aperture
lens.set_aperture(aperture_type="EPD", value=25.4)

# add field
lens.set_field_type(field_type="angle")
lens.add_field(y=0)

# add wavelength
lens.add_wavelength(value=0.587, is_primary=True)

lens.draw(num_rays=10)

spot = analysis.SpotDiagram(lens)
spot.view()
StepStep
Show full code listing
python
import numpy as np

from optiland import analysis, optic

lens = optic.Optic()

# add surfaces
lens.add_surface(index=0, radius=np.inf, thickness=np.inf)
lens.add_surface(index=1, thickness=7, radius=19.93, is_stop=True, material="N-SF11")
lens.add_surface(index=2, thickness=21.48)
lens.add_surface(index=3)

# add aperture
lens.set_aperture(aperture_type="EPD", value=25.4)

# add field
lens.set_field_type(field_type="angle")
lens.add_field(y=0)

# add wavelength
lens.add_wavelength(value=0.587, is_primary=True)

lens.draw(num_rays=10)

spot = analysis.SpotDiagram(lens)
spot.view()

lens = optic.Optic()

# add surfaces
lens.add_surface(index=0, radius=np.inf, thickness=np.inf)

# == WE ADD THE TILT TO THIS SURFACE ===============
lens.add_surface(
  index=1,
  thickness=7,
  radius=19.93,
  is_stop=True,
  material="N-SF11",
  rx=np.radians(5.0),
)
# ==================================================

lens.add_surface(index=2, thickness=21.48)
lens.add_surface(index=3)

# add aperture
lens.set_aperture(aperture_type="EPD", value=25.4)

# add field
lens.set_field_type(field_type="angle")
lens.add_field(y=0)

# add wavelength
lens.add_wavelength(value=0.587, is_primary=True)

lens.draw(num_rays=10)

spot = analysis.SpotDiagram(lens)
spot.view()

lens = optic.Optic()

# add surfaces
lens.add_surface(index=0, radius=np.inf, thickness=np.inf)

# == WE DECENTER THIS SURFACE =====================
lens.add_surface(
  index=1,
  thickness=7,
  radius=19.93,
  is_stop=True,
  material="N-SF11",
  dy=1.0,  # 1 mm decenter
)
# ==================================================

lens.add_surface(index=2, thickness=21.48)

lens.add_surface(index=3)

# add aperture
lens.set_aperture(aperture_type="EPD", value=25.4)

# add field
lens.set_field_type(field_type="angle")
lens.add_field(y=0)

# add wavelength
lens.add_wavelength(value=0.587, is_primary=True)

lens.draw(num_rays=10)

spot = analysis.SpotDiagram(lens)
spot.view()

Conclusions

Tilting and de-centering surfaces is a powerful way to model both manufacturing errors and intentional folded-optic designs. In this tutorial you:

  • Built a baseline singlet with all surfaces perfectly aligned and established a reference spot diagram.
  • Applied a 5° rotation (rx=np.radians(5.0)) to a single surface and observed how the degradation of the on-axis spot.
  • Applied a 1 mm decenter (dy=1.0) to the same surface and compared the resulting off-axis asymmetry with the tilt case.
  • Used SpotDiagram consistently to quantify image-quality changes after each perturbation.

These same techniques apply to tolerance analysis, fold-mirror systems, and any design where component misalignment must be simulated explicitly.

Next tutorials