Source code for manim_physics.optics.lenses

"""Lenses for refracting Rays.
"""
from __future__ import annotations
from typing import Iterable, Tuple

from manim import config
from manim.constants import LEFT, RIGHT
from manim.mobject.geometry.arc import Circle
from manim.mobject.geometry.boolean_ops import Difference, Intersection
from manim.mobject.geometry.polygram import Square
from manim.mobject.types.vectorized_mobject import VMobject, VectorizedPoint
import numpy as np
from shapely import geometry as gm


__all__ = ["Lens"]


try:
    # For manim < 0.15.0
    from manim.mobject.opengl_compatibility import ConvertToOpenGL
except ModuleNotFoundError:
    # For manim >= 0.15.0
    from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL


[docs]def intersection(vmob1: VMobject, vmob2: VMobject) -> Iterable[Iterable[float]]: """intersection points of 2 curves""" a = gm.LineString(vmob1.points) b = gm.LineString(vmob2.points) intersects: gm.GeometryCollection = a.intersection(b) try: # for intersections > 1 return np.array( [[[x, y, z] for x, y, z in m.coords][0] for m in intersects.geoms] ) except: # else return np.array([[x, y, z] for x, y, z in intersects.coords])
[docs]def snell(i_ang: float, n: float) -> float: """accepts radians, returns radians""" return np.arcsin(np.sin(i_ang) / n)
[docs]def antisnell(r_ang: float, n: float) -> float: """accepts radians, returns radians""" return np.arcsin(np.sin(r_ang) * n)
[docs]class Lens(VMobject, metaclass=ConvertToOpenGL): def __init__(self, f: float, d: float, n: float = 1.52, **kwargs) -> None: """A lens. Commonly used with :class:`~Ray` . Parameters ---------- f Focal length. This does not correspond correctly to the point of focus (Known issue). Positive f returns a convex lens, negative for concave. d Lens thickness n Refractive index. By default, glass. kwargs Additional parameters to be passed to :class:`~VMobject` . """ super().__init__(**kwargs) self.f = f f *= 50 / 7 * f if f > 0 else -50 / 7 * f # this is odd, but it works if f > 0: r = ((n - 1) ** 2 * f * d / n) ** 0.5 else: r = ((n - 1) ** 2 * -f * d / n) ** 0.5 self.d = d self.n = n self.r = r if f > 0: self.set_points( Intersection( a := Circle(r).shift(RIGHT * (r - d / 2)), b := Circle(r).shift(LEFT * (r - d / 2)), ) .insert_n_curves(50) .points ) else: self.set_points( Difference( Difference( Square(2 * 0.7 * r), a := Circle(r).shift(LEFT * (r + d / 2)), ), b := Circle(r).shift(RIGHT * (r + d / 2)), ) .insert_n_curves(50) .points ) self.add(VectorizedPoint(a.get_center()), VectorizedPoint(b.get_center())) @property def C(self) -> Tuple[Iterable[float]]: """Returns a tuple of two points corresponding to the centers of curvature.""" i = 0 i += 1 if config.renderer != "opengl" else 0 return self[i].points[0], self[i + 1].points[0] # why is this confusing