699 lines
22 KiB
Python
699 lines
22 KiB
Python
"""
|
|
**Contains**
|
|
|
|
* refraction_angle
|
|
* fresnel_coefficients
|
|
* deviation
|
|
* brewster_angle
|
|
* critical_angle
|
|
* lens_makers_formula
|
|
* mirror_formula
|
|
* lens_formula
|
|
* hyperfocal_distance
|
|
* transverse_magnification
|
|
"""
|
|
|
|
__all__ = ['refraction_angle',
|
|
'deviation',
|
|
'fresnel_coefficients',
|
|
'brewster_angle',
|
|
'critical_angle',
|
|
'lens_makers_formula',
|
|
'mirror_formula',
|
|
'lens_formula',
|
|
'hyperfocal_distance',
|
|
'transverse_magnification'
|
|
]
|
|
|
|
from sympy.core.numbers import (Float, I, oo, pi, zoo)
|
|
from sympy.core.singleton import S
|
|
from sympy.core.symbol import Symbol
|
|
from sympy.core.sympify import sympify
|
|
from sympy.functions.elementary.miscellaneous import sqrt
|
|
from sympy.functions.elementary.trigonometric import (acos, asin, atan2, cos, sin, tan)
|
|
from sympy.matrices.dense import Matrix
|
|
from sympy.polys.polytools import cancel
|
|
from sympy.series.limits import Limit
|
|
from sympy.geometry.line import Ray3D
|
|
from sympy.geometry.util import intersection
|
|
from sympy.geometry.plane import Plane
|
|
from sympy.utilities.iterables import is_sequence
|
|
from .medium import Medium
|
|
|
|
|
|
def refractive_index_of_medium(medium):
|
|
"""
|
|
Helper function that returns refractive index, given a medium
|
|
"""
|
|
if isinstance(medium, Medium):
|
|
n = medium.refractive_index
|
|
else:
|
|
n = sympify(medium)
|
|
return n
|
|
|
|
|
|
def refraction_angle(incident, medium1, medium2, normal=None, plane=None):
|
|
"""
|
|
This function calculates transmitted vector after refraction at planar
|
|
surface. ``medium1`` and ``medium2`` can be ``Medium`` or any sympifiable object.
|
|
If ``incident`` is a number then treated as angle of incidence (in radians)
|
|
in which case refraction angle is returned.
|
|
|
|
If ``incident`` is an object of `Ray3D`, `normal` also has to be an instance
|
|
of `Ray3D` in order to get the output as a `Ray3D`. Please note that if
|
|
plane of separation is not provided and normal is an instance of `Ray3D`,
|
|
``normal`` will be assumed to be intersecting incident ray at the plane of
|
|
separation. This will not be the case when `normal` is a `Matrix` or
|
|
any other sequence.
|
|
If ``incident`` is an instance of `Ray3D` and `plane` has not been provided
|
|
and ``normal`` is not `Ray3D`, output will be a `Matrix`.
|
|
|
|
Parameters
|
|
==========
|
|
|
|
incident : Matrix, Ray3D, sequence or a number
|
|
Incident vector or angle of incidence
|
|
medium1 : sympy.physics.optics.medium.Medium or sympifiable
|
|
Medium 1 or its refractive index
|
|
medium2 : sympy.physics.optics.medium.Medium or sympifiable
|
|
Medium 2 or its refractive index
|
|
normal : Matrix, Ray3D, or sequence
|
|
Normal vector
|
|
plane : Plane
|
|
Plane of separation of the two media.
|
|
|
|
Returns
|
|
=======
|
|
|
|
Returns an angle of refraction or a refracted ray depending on inputs.
|
|
|
|
Examples
|
|
========
|
|
|
|
>>> from sympy.physics.optics import refraction_angle
|
|
>>> from sympy.geometry import Point3D, Ray3D, Plane
|
|
>>> from sympy.matrices import Matrix
|
|
>>> from sympy import symbols, pi
|
|
>>> n = Matrix([0, 0, 1])
|
|
>>> P = Plane(Point3D(0, 0, 0), normal_vector=[0, 0, 1])
|
|
>>> r1 = Ray3D(Point3D(-1, -1, 1), Point3D(0, 0, 0))
|
|
>>> refraction_angle(r1, 1, 1, n)
|
|
Matrix([
|
|
[ 1],
|
|
[ 1],
|
|
[-1]])
|
|
>>> refraction_angle(r1, 1, 1, plane=P)
|
|
Ray3D(Point3D(0, 0, 0), Point3D(1, 1, -1))
|
|
|
|
With different index of refraction of the two media
|
|
|
|
>>> n1, n2 = symbols('n1, n2')
|
|
>>> refraction_angle(r1, n1, n2, n)
|
|
Matrix([
|
|
[ n1/n2],
|
|
[ n1/n2],
|
|
[-sqrt(3)*sqrt(-2*n1**2/(3*n2**2) + 1)]])
|
|
>>> refraction_angle(r1, n1, n2, plane=P)
|
|
Ray3D(Point3D(0, 0, 0), Point3D(n1/n2, n1/n2, -sqrt(3)*sqrt(-2*n1**2/(3*n2**2) + 1)))
|
|
>>> round(refraction_angle(pi/6, 1.2, 1.5), 5)
|
|
0.41152
|
|
"""
|
|
|
|
n1 = refractive_index_of_medium(medium1)
|
|
n2 = refractive_index_of_medium(medium2)
|
|
|
|
# check if an incidence angle was supplied instead of a ray
|
|
try:
|
|
angle_of_incidence = float(incident)
|
|
except TypeError:
|
|
angle_of_incidence = None
|
|
|
|
try:
|
|
critical_angle_ = critical_angle(medium1, medium2)
|
|
except (ValueError, TypeError):
|
|
critical_angle_ = None
|
|
|
|
if angle_of_incidence is not None:
|
|
if normal is not None or plane is not None:
|
|
raise ValueError('Normal/plane not allowed if incident is an angle')
|
|
|
|
if not 0.0 <= angle_of_incidence < pi*0.5:
|
|
raise ValueError('Angle of incidence not in range [0:pi/2)')
|
|
|
|
if critical_angle_ and angle_of_incidence > critical_angle_:
|
|
raise ValueError('Ray undergoes total internal reflection')
|
|
return asin(n1*sin(angle_of_incidence)/n2)
|
|
|
|
# Treat the incident as ray below
|
|
# A flag to check whether to return Ray3D or not
|
|
return_ray = False
|
|
|
|
if plane is not None and normal is not None:
|
|
raise ValueError("Either plane or normal is acceptable.")
|
|
|
|
if not isinstance(incident, Matrix):
|
|
if is_sequence(incident):
|
|
_incident = Matrix(incident)
|
|
elif isinstance(incident, Ray3D):
|
|
_incident = Matrix(incident.direction_ratio)
|
|
else:
|
|
raise TypeError(
|
|
"incident should be a Matrix, Ray3D, or sequence")
|
|
else:
|
|
_incident = incident
|
|
|
|
# If plane is provided, get direction ratios of the normal
|
|
# to the plane from the plane else go with `normal` param.
|
|
if plane is not None:
|
|
if not isinstance(plane, Plane):
|
|
raise TypeError("plane should be an instance of geometry.plane.Plane")
|
|
# If we have the plane, we can get the intersection
|
|
# point of incident ray and the plane and thus return
|
|
# an instance of Ray3D.
|
|
if isinstance(incident, Ray3D):
|
|
return_ray = True
|
|
intersection_pt = plane.intersection(incident)[0]
|
|
_normal = Matrix(plane.normal_vector)
|
|
else:
|
|
if not isinstance(normal, Matrix):
|
|
if is_sequence(normal):
|
|
_normal = Matrix(normal)
|
|
elif isinstance(normal, Ray3D):
|
|
_normal = Matrix(normal.direction_ratio)
|
|
if isinstance(incident, Ray3D):
|
|
intersection_pt = intersection(incident, normal)
|
|
if len(intersection_pt) == 0:
|
|
raise ValueError(
|
|
"Normal isn't concurrent with the incident ray.")
|
|
else:
|
|
return_ray = True
|
|
intersection_pt = intersection_pt[0]
|
|
else:
|
|
raise TypeError(
|
|
"Normal should be a Matrix, Ray3D, or sequence")
|
|
else:
|
|
_normal = normal
|
|
|
|
eta = n1/n2 # Relative index of refraction
|
|
# Calculating magnitude of the vectors
|
|
mag_incident = sqrt(sum([i**2 for i in _incident]))
|
|
mag_normal = sqrt(sum([i**2 for i in _normal]))
|
|
# Converting vectors to unit vectors by dividing
|
|
# them with their magnitudes
|
|
_incident /= mag_incident
|
|
_normal /= mag_normal
|
|
c1 = -_incident.dot(_normal) # cos(angle_of_incidence)
|
|
cs2 = 1 - eta**2*(1 - c1**2) # cos(angle_of_refraction)**2
|
|
if cs2.is_negative: # This is the case of total internal reflection(TIR).
|
|
return S.Zero
|
|
drs = eta*_incident + (eta*c1 - sqrt(cs2))*_normal
|
|
# Multiplying unit vector by its magnitude
|
|
drs = drs*mag_incident
|
|
if not return_ray:
|
|
return drs
|
|
else:
|
|
return Ray3D(intersection_pt, direction_ratio=drs)
|
|
|
|
|
|
def fresnel_coefficients(angle_of_incidence, medium1, medium2):
|
|
"""
|
|
This function uses Fresnel equations to calculate reflection and
|
|
transmission coefficients. Those are obtained for both polarisations
|
|
when the electric field vector is in the plane of incidence (labelled 'p')
|
|
and when the electric field vector is perpendicular to the plane of
|
|
incidence (labelled 's'). There are four real coefficients unless the
|
|
incident ray reflects in total internal in which case there are two complex
|
|
ones. Angle of incidence is the angle between the incident ray and the
|
|
surface normal. ``medium1`` and ``medium2`` can be ``Medium`` or any
|
|
sympifiable object.
|
|
|
|
Parameters
|
|
==========
|
|
|
|
angle_of_incidence : sympifiable
|
|
|
|
medium1 : Medium or sympifiable
|
|
Medium 1 or its refractive index
|
|
|
|
medium2 : Medium or sympifiable
|
|
Medium 2 or its refractive index
|
|
|
|
Returns
|
|
=======
|
|
|
|
Returns a list with four real Fresnel coefficients:
|
|
[reflection p (TM), reflection s (TE),
|
|
transmission p (TM), transmission s (TE)]
|
|
If the ray is undergoes total internal reflection then returns a
|
|
list of two complex Fresnel coefficients:
|
|
[reflection p (TM), reflection s (TE)]
|
|
|
|
Examples
|
|
========
|
|
|
|
>>> from sympy.physics.optics import fresnel_coefficients
|
|
>>> fresnel_coefficients(0.3, 1, 2)
|
|
[0.317843553417859, -0.348645229818821,
|
|
0.658921776708929, 0.651354770181179]
|
|
>>> fresnel_coefficients(0.6, 2, 1)
|
|
[-0.235625382192159 - 0.971843958291041*I,
|
|
0.816477005968898 - 0.577377951366403*I]
|
|
|
|
References
|
|
==========
|
|
|
|
.. [1] https://en.wikipedia.org/wiki/Fresnel_equations
|
|
"""
|
|
if not 0 <= 2*angle_of_incidence < pi:
|
|
raise ValueError('Angle of incidence not in range [0:pi/2)')
|
|
|
|
n1 = refractive_index_of_medium(medium1)
|
|
n2 = refractive_index_of_medium(medium2)
|
|
|
|
angle_of_refraction = asin(n1*sin(angle_of_incidence)/n2)
|
|
try:
|
|
angle_of_total_internal_reflection_onset = critical_angle(n1, n2)
|
|
except ValueError:
|
|
angle_of_total_internal_reflection_onset = None
|
|
|
|
if angle_of_total_internal_reflection_onset is None or\
|
|
angle_of_total_internal_reflection_onset > angle_of_incidence:
|
|
R_s = -sin(angle_of_incidence - angle_of_refraction)\
|
|
/sin(angle_of_incidence + angle_of_refraction)
|
|
R_p = tan(angle_of_incidence - angle_of_refraction)\
|
|
/tan(angle_of_incidence + angle_of_refraction)
|
|
T_s = 2*sin(angle_of_refraction)*cos(angle_of_incidence)\
|
|
/sin(angle_of_incidence + angle_of_refraction)
|
|
T_p = 2*sin(angle_of_refraction)*cos(angle_of_incidence)\
|
|
/(sin(angle_of_incidence + angle_of_refraction)\
|
|
*cos(angle_of_incidence - angle_of_refraction))
|
|
return [R_p, R_s, T_p, T_s]
|
|
else:
|
|
n = n2/n1
|
|
R_s = cancel((cos(angle_of_incidence)-\
|
|
I*sqrt(sin(angle_of_incidence)**2 - n**2))\
|
|
/(cos(angle_of_incidence)+\
|
|
I*sqrt(sin(angle_of_incidence)**2 - n**2)))
|
|
R_p = cancel((n**2*cos(angle_of_incidence)-\
|
|
I*sqrt(sin(angle_of_incidence)**2 - n**2))\
|
|
/(n**2*cos(angle_of_incidence)+\
|
|
I*sqrt(sin(angle_of_incidence)**2 - n**2)))
|
|
return [R_p, R_s]
|
|
|
|
|
|
def deviation(incident, medium1, medium2, normal=None, plane=None):
|
|
"""
|
|
This function calculates the angle of deviation of a ray
|
|
due to refraction at planar surface.
|
|
|
|
Parameters
|
|
==========
|
|
|
|
incident : Matrix, Ray3D, sequence or float
|
|
Incident vector or angle of incidence
|
|
medium1 : sympy.physics.optics.medium.Medium or sympifiable
|
|
Medium 1 or its refractive index
|
|
medium2 : sympy.physics.optics.medium.Medium or sympifiable
|
|
Medium 2 or its refractive index
|
|
normal : Matrix, Ray3D, or sequence
|
|
Normal vector
|
|
plane : Plane
|
|
Plane of separation of the two media.
|
|
|
|
Returns angular deviation between incident and refracted rays
|
|
|
|
Examples
|
|
========
|
|
|
|
>>> from sympy.physics.optics import deviation
|
|
>>> from sympy.geometry import Point3D, Ray3D, Plane
|
|
>>> from sympy.matrices import Matrix
|
|
>>> from sympy import symbols
|
|
>>> n1, n2 = symbols('n1, n2')
|
|
>>> n = Matrix([0, 0, 1])
|
|
>>> P = Plane(Point3D(0, 0, 0), normal_vector=[0, 0, 1])
|
|
>>> r1 = Ray3D(Point3D(-1, -1, 1), Point3D(0, 0, 0))
|
|
>>> deviation(r1, 1, 1, n)
|
|
0
|
|
>>> deviation(r1, n1, n2, plane=P)
|
|
-acos(-sqrt(-2*n1**2/(3*n2**2) + 1)) + acos(-sqrt(3)/3)
|
|
>>> round(deviation(0.1, 1.2, 1.5), 5)
|
|
-0.02005
|
|
"""
|
|
refracted = refraction_angle(incident,
|
|
medium1,
|
|
medium2,
|
|
normal=normal,
|
|
plane=plane)
|
|
try:
|
|
angle_of_incidence = Float(incident)
|
|
except TypeError:
|
|
angle_of_incidence = None
|
|
|
|
if angle_of_incidence is not None:
|
|
return float(refracted) - angle_of_incidence
|
|
|
|
if refracted != 0:
|
|
if isinstance(refracted, Ray3D):
|
|
refracted = Matrix(refracted.direction_ratio)
|
|
|
|
if not isinstance(incident, Matrix):
|
|
if is_sequence(incident):
|
|
_incident = Matrix(incident)
|
|
elif isinstance(incident, Ray3D):
|
|
_incident = Matrix(incident.direction_ratio)
|
|
else:
|
|
raise TypeError(
|
|
"incident should be a Matrix, Ray3D, or sequence")
|
|
else:
|
|
_incident = incident
|
|
|
|
if plane is None:
|
|
if not isinstance(normal, Matrix):
|
|
if is_sequence(normal):
|
|
_normal = Matrix(normal)
|
|
elif isinstance(normal, Ray3D):
|
|
_normal = Matrix(normal.direction_ratio)
|
|
else:
|
|
raise TypeError(
|
|
"normal should be a Matrix, Ray3D, or sequence")
|
|
else:
|
|
_normal = normal
|
|
else:
|
|
_normal = Matrix(plane.normal_vector)
|
|
|
|
mag_incident = sqrt(sum([i**2 for i in _incident]))
|
|
mag_normal = sqrt(sum([i**2 for i in _normal]))
|
|
mag_refracted = sqrt(sum([i**2 for i in refracted]))
|
|
_incident /= mag_incident
|
|
_normal /= mag_normal
|
|
refracted /= mag_refracted
|
|
i = acos(_incident.dot(_normal))
|
|
r = acos(refracted.dot(_normal))
|
|
return i - r
|
|
|
|
|
|
def brewster_angle(medium1, medium2):
|
|
"""
|
|
This function calculates the Brewster's angle of incidence to Medium 2 from
|
|
Medium 1 in radians.
|
|
|
|
Parameters
|
|
==========
|
|
|
|
medium 1 : Medium or sympifiable
|
|
Refractive index of Medium 1
|
|
medium 2 : Medium or sympifiable
|
|
Refractive index of Medium 1
|
|
|
|
Examples
|
|
========
|
|
|
|
>>> from sympy.physics.optics import brewster_angle
|
|
>>> brewster_angle(1, 1.33)
|
|
0.926093295503462
|
|
|
|
"""
|
|
|
|
n1 = refractive_index_of_medium(medium1)
|
|
n2 = refractive_index_of_medium(medium2)
|
|
|
|
return atan2(n2, n1)
|
|
|
|
def critical_angle(medium1, medium2):
|
|
"""
|
|
This function calculates the critical angle of incidence (marking the onset
|
|
of total internal) to Medium 2 from Medium 1 in radians.
|
|
|
|
Parameters
|
|
==========
|
|
|
|
medium 1 : Medium or sympifiable
|
|
Refractive index of Medium 1.
|
|
medium 2 : Medium or sympifiable
|
|
Refractive index of Medium 1.
|
|
|
|
Examples
|
|
========
|
|
|
|
>>> from sympy.physics.optics import critical_angle
|
|
>>> critical_angle(1.33, 1)
|
|
0.850908514477849
|
|
|
|
"""
|
|
|
|
n1 = refractive_index_of_medium(medium1)
|
|
n2 = refractive_index_of_medium(medium2)
|
|
|
|
if n2 > n1:
|
|
raise ValueError('Total internal reflection impossible for n1 < n2')
|
|
else:
|
|
return asin(n2/n1)
|
|
|
|
|
|
|
|
def lens_makers_formula(n_lens, n_surr, r1, r2, d=0):
|
|
"""
|
|
This function calculates focal length of a lens.
|
|
It follows cartesian sign convention.
|
|
|
|
Parameters
|
|
==========
|
|
|
|
n_lens : Medium or sympifiable
|
|
Index of refraction of lens.
|
|
n_surr : Medium or sympifiable
|
|
Index of reflection of surrounding.
|
|
r1 : sympifiable
|
|
Radius of curvature of first surface.
|
|
r2 : sympifiable
|
|
Radius of curvature of second surface.
|
|
d : sympifiable, optional
|
|
Thickness of lens, default value is 0.
|
|
|
|
Examples
|
|
========
|
|
|
|
>>> from sympy.physics.optics import lens_makers_formula
|
|
>>> from sympy import S
|
|
>>> lens_makers_formula(1.33, 1, 10, -10)
|
|
15.1515151515151
|
|
>>> lens_makers_formula(1.2, 1, 10, S.Infinity)
|
|
50.0000000000000
|
|
>>> lens_makers_formula(1.33, 1, 10, -10, d=1)
|
|
15.3418463277618
|
|
|
|
"""
|
|
|
|
if isinstance(n_lens, Medium):
|
|
n_lens = n_lens.refractive_index
|
|
else:
|
|
n_lens = sympify(n_lens)
|
|
if isinstance(n_surr, Medium):
|
|
n_surr = n_surr.refractive_index
|
|
else:
|
|
n_surr = sympify(n_surr)
|
|
d = sympify(d)
|
|
|
|
focal_length = 1/((n_lens - n_surr) / n_surr*(1/r1 - 1/r2 + (((n_lens - n_surr) * d) / (n_lens * r1 * r2))))
|
|
|
|
if focal_length == zoo:
|
|
return S.Infinity
|
|
return focal_length
|
|
|
|
|
|
def mirror_formula(focal_length=None, u=None, v=None):
|
|
"""
|
|
This function provides one of the three parameters
|
|
when two of them are supplied.
|
|
This is valid only for paraxial rays.
|
|
|
|
Parameters
|
|
==========
|
|
|
|
focal_length : sympifiable
|
|
Focal length of the mirror.
|
|
u : sympifiable
|
|
Distance of object from the pole on
|
|
the principal axis.
|
|
v : sympifiable
|
|
Distance of the image from the pole
|
|
on the principal axis.
|
|
|
|
Examples
|
|
========
|
|
|
|
>>> from sympy.physics.optics import mirror_formula
|
|
>>> from sympy.abc import f, u, v
|
|
>>> mirror_formula(focal_length=f, u=u)
|
|
f*u/(-f + u)
|
|
>>> mirror_formula(focal_length=f, v=v)
|
|
f*v/(-f + v)
|
|
>>> mirror_formula(u=u, v=v)
|
|
u*v/(u + v)
|
|
|
|
"""
|
|
if focal_length and u and v:
|
|
raise ValueError("Please provide only two parameters")
|
|
|
|
focal_length = sympify(focal_length)
|
|
u = sympify(u)
|
|
v = sympify(v)
|
|
if u is oo:
|
|
_u = Symbol('u')
|
|
if v is oo:
|
|
_v = Symbol('v')
|
|
if focal_length is oo:
|
|
_f = Symbol('f')
|
|
if focal_length is None:
|
|
if u is oo and v is oo:
|
|
return Limit(Limit(_v*_u/(_v + _u), _u, oo), _v, oo).doit()
|
|
if u is oo:
|
|
return Limit(v*_u/(v + _u), _u, oo).doit()
|
|
if v is oo:
|
|
return Limit(_v*u/(_v + u), _v, oo).doit()
|
|
return v*u/(v + u)
|
|
if u is None:
|
|
if v is oo and focal_length is oo:
|
|
return Limit(Limit(_v*_f/(_v - _f), _v, oo), _f, oo).doit()
|
|
if v is oo:
|
|
return Limit(_v*focal_length/(_v - focal_length), _v, oo).doit()
|
|
if focal_length is oo:
|
|
return Limit(v*_f/(v - _f), _f, oo).doit()
|
|
return v*focal_length/(v - focal_length)
|
|
if v is None:
|
|
if u is oo and focal_length is oo:
|
|
return Limit(Limit(_u*_f/(_u - _f), _u, oo), _f, oo).doit()
|
|
if u is oo:
|
|
return Limit(_u*focal_length/(_u - focal_length), _u, oo).doit()
|
|
if focal_length is oo:
|
|
return Limit(u*_f/(u - _f), _f, oo).doit()
|
|
return u*focal_length/(u - focal_length)
|
|
|
|
|
|
def lens_formula(focal_length=None, u=None, v=None):
|
|
"""
|
|
This function provides one of the three parameters
|
|
when two of them are supplied.
|
|
This is valid only for paraxial rays.
|
|
|
|
Parameters
|
|
==========
|
|
|
|
focal_length : sympifiable
|
|
Focal length of the mirror.
|
|
u : sympifiable
|
|
Distance of object from the optical center on
|
|
the principal axis.
|
|
v : sympifiable
|
|
Distance of the image from the optical center
|
|
on the principal axis.
|
|
|
|
Examples
|
|
========
|
|
|
|
>>> from sympy.physics.optics import lens_formula
|
|
>>> from sympy.abc import f, u, v
|
|
>>> lens_formula(focal_length=f, u=u)
|
|
f*u/(f + u)
|
|
>>> lens_formula(focal_length=f, v=v)
|
|
f*v/(f - v)
|
|
>>> lens_formula(u=u, v=v)
|
|
u*v/(u - v)
|
|
|
|
"""
|
|
if focal_length and u and v:
|
|
raise ValueError("Please provide only two parameters")
|
|
|
|
focal_length = sympify(focal_length)
|
|
u = sympify(u)
|
|
v = sympify(v)
|
|
if u is oo:
|
|
_u = Symbol('u')
|
|
if v is oo:
|
|
_v = Symbol('v')
|
|
if focal_length is oo:
|
|
_f = Symbol('f')
|
|
if focal_length is None:
|
|
if u is oo and v is oo:
|
|
return Limit(Limit(_v*_u/(_u - _v), _u, oo), _v, oo).doit()
|
|
if u is oo:
|
|
return Limit(v*_u/(_u - v), _u, oo).doit()
|
|
if v is oo:
|
|
return Limit(_v*u/(u - _v), _v, oo).doit()
|
|
return v*u/(u - v)
|
|
if u is None:
|
|
if v is oo and focal_length is oo:
|
|
return Limit(Limit(_v*_f/(_f - _v), _v, oo), _f, oo).doit()
|
|
if v is oo:
|
|
return Limit(_v*focal_length/(focal_length - _v), _v, oo).doit()
|
|
if focal_length is oo:
|
|
return Limit(v*_f/(_f - v), _f, oo).doit()
|
|
return v*focal_length/(focal_length - v)
|
|
if v is None:
|
|
if u is oo and focal_length is oo:
|
|
return Limit(Limit(_u*_f/(_u + _f), _u, oo), _f, oo).doit()
|
|
if u is oo:
|
|
return Limit(_u*focal_length/(_u + focal_length), _u, oo).doit()
|
|
if focal_length is oo:
|
|
return Limit(u*_f/(u + _f), _f, oo).doit()
|
|
return u*focal_length/(u + focal_length)
|
|
|
|
def hyperfocal_distance(f, N, c):
|
|
"""
|
|
|
|
Parameters
|
|
==========
|
|
|
|
f: sympifiable
|
|
Focal length of a given lens.
|
|
|
|
N: sympifiable
|
|
F-number of a given lens.
|
|
|
|
c: sympifiable
|
|
Circle of Confusion (CoC) of a given image format.
|
|
|
|
Example
|
|
=======
|
|
|
|
>>> from sympy.physics.optics import hyperfocal_distance
|
|
>>> round(hyperfocal_distance(f = 0.5, N = 8, c = 0.0033), 2)
|
|
9.47
|
|
"""
|
|
|
|
f = sympify(f)
|
|
N = sympify(N)
|
|
c = sympify(c)
|
|
|
|
return (1/(N * c))*(f**2)
|
|
|
|
def transverse_magnification(si, so):
|
|
"""
|
|
|
|
Calculates the transverse magnification, which is the ratio of the
|
|
image size to the object size.
|
|
|
|
Parameters
|
|
==========
|
|
|
|
so: sympifiable
|
|
Lens-object distance.
|
|
|
|
si: sympifiable
|
|
Lens-image distance.
|
|
|
|
Example
|
|
=======
|
|
|
|
>>> from sympy.physics.optics import transverse_magnification
|
|
>>> transverse_magnification(30, 15)
|
|
-2
|
|
|
|
"""
|
|
|
|
si = sympify(si)
|
|
so = sympify(so)
|
|
|
|
return (-(si/so))
|