Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
(which we are looking for) will end at y= plus/minus diameter/2 at the
aperture stop. We return the largest angle first, for
convenience.
"""
(stopPosition, stopDiameter) = self.apertureStop()
transferMatrixToApertureStop = self.transferMatrix(upTo=stopPosition)
A = transferMatrixToApertureStop.A
B = transferMatrixToApertureStop.B
thetaUp = (stopDiameter / 2.0 - A * y) / B
thetaDown = (-stopDiameter / 2.0 - A * y) / B
if thetaDown > thetaUp:
(thetaUp, thetaDown) = (thetaDown, thetaUp)
return (Ray(y=y, theta=thetaUp), Ray(y=y, theta=thetaDown))
def trace(self, ray):
"""Returns a list of rays (i.e. a ray trace) for the input ray through the matrix.
Because we want to manage blockage by apertures, we need to perform a two-step process
for elements that have a finite, non-null length: where is the ray blocked exactly?
It can be blocked at the entrance, at the exit, or anywhere in between.
The aperture diameter for a finite-length element is constant across the length
of the element. We therefore check before entering the element and after having
propagated through the element. For now, this will suffice.
If the length is null, the ray is traced in a single step
"""
rayTrace = []
if isinstance(ray, Ray):
if self.L > 0:
if abs(ray.y) > self.apertureDiameter / 2:
ray.isBlocked = True
rayTrace.append(ray)
rayTrace.append(self*ray)
return rayTrace
Strategy: we take a ray height and divide by real aperture
diameter at that position. Some elements may have a finite length
(e.g., Space() or ThickLens()), so we always calculate the ratio
before propagating inside the element and after having propoagated
through the element. The position where the absolute value of the
ratio is maximum is the aperture stop.
If there are no elements of finite diameter (i.e. all
optical elements are infinite in diameters), then
there is no aperture stop in the system and the size
of the aperture stop is infinite.
"""
if not self.hasFiniteApertureDiameter():
return (None, float('+Inf'))
else:
ray = Ray(y=0, theta=0.1) # Any ray angle will do
rayTrace = self.trace(ray)
maxRatio = 0.0
apertureStopPosition = 0
apertureStopDiameter = float("+Inf")
for ray in rayTrace:
ratio = abs(ray.y / ray.apertureDiameter)
if ratio > maxRatio:
apertureStopPosition = ray.z
apertureStopDiameter = ray.apertureDiameter
maxRatio = ratio
return (apertureStopPosition, apertureStopDiameter)
def fan(y, radianMin, radianMax, N):
"""A list of rays spanning from radianMin to radianMax to be used
with Matrix.trace() or Matrix.traceMany()
"""
if N >= 2:
deltaRadian = float(radianMax - radianMin) / (N - 1)
elif N == 1:
deltaRadian = 0.0
else:
raise ValueError("N must be 1 or larger.")
rays = []
for i in range(N):
theta = radianMin + float(i) * deltaRadian
rays.append(Ray(y, theta, z=0))
return rays
else:
raise ValueError("N must be 1 or larger.")
if M >= 2:
deltaHeight = float(yMax - yMin) / (M - 1)
elif M == 1:
deltaHeight = 0.0
else:
raise ValueError("M must be 1 or larger.")
rays = []
for j in range(M):
for i in range(N):
theta = radianMin + float(i) * deltaRadian
y = yMin + float(j) * deltaHeight
rays.append(Ray(y, theta, z=0))
return rays
def mul_ray(self, rightSideRay):
""" Multiplication of a ray by a matrix. New position of
ray is updated by the physical length of the matrix.
If the ray is beyond the aperture diameter it is labelled
as "isBlocked = True" but can still propagate.
"""
outputRay = Ray()
outputRay.y = self.A * rightSideRay.y + self.B * rightSideRay.theta
outputRay.theta = self.C * rightSideRay.y + self.D * rightSideRay.theta
outputRay.z = self.L + rightSideRay.z
outputRay.apertureDiameter = self.apertureDiameter
if abs(outputRay.y) > abs(self.apertureDiameter / 2.0):
outputRay.isBlocked = True
else:
outputRay.isBlocked = rightSideRay.isBlocked
return outputRay
obj = Objective(f=10, NA=0.8, focusToFocusLength=60, backAperture=18, workingDistance=2, label="Objective")
print("Focal distances: ", obj.focalDistances())
print("Position of PP1 and PP2: ", obj.principalPlanePositions(z=0))
print("Focal spots positions: ", obj.focusPositions(z=0))
print("Distance between entrance and exit planes: ", obj.L)
path = ImagingPath()
path.fanAngle = 0.0
path.fanNumber = 1
path.rayNumber = 15
path.objectHeight = 10.0
path.label = "Demo #14 Path with generic objective"
path.append(Space(180))
path.append(obj)
path.append(Space(10))
path.display()
'''
path = LaserPath()
path.label = "Demo #18: Laser beam and vendor lenses"
path.append(Space(d=50))
path.append(thorlabs.AC254_050_A())
path.append(Space(d=50))
path.append(thorlabs.AC254_050_A())
path.append(Space(d=150))
path.append(eo.PN_33_921())
path.append(Space(d=50))
path.append(eo.PN_88_593())
path.append(Space(d=180))
path.append(olympus.LUMPlanFL40X())
path.append(Space(d=10))
path.display()
from raytracing import ImagingPath, Space, Lens, Aperture
'''
Demo #9 - Infinite telecentric 4f telescope
'''
path = ImagingPath()
path.label = "Demo #9: Infinite telecentric 4f telescope"
path.append(Space(d=5))
path.append(Lens(f=5))
path.append(Space(d=10))
path.append(Lens(f=5))
path.append(Space(d=5))
path.display()
from raytracing import ImagingPath, Space, ThickLens
'''
Demo #11 - Thick diverging lens
'''
path = ImagingPath()
path.label = "Demo #11: Thick diverging lens"
path.objectHeight = 20
path.append(Space(d=50))
path.append(ThickLens(R1=-20, R2=20, n=1.55, thickness=10, diameter=25, label='Lens'))
path.append(Space(d=50))
path.display()