Source code for visualization.info.lens_info_viewer

"""Lens Info Viewer Module

Provides a class for viewing detailed information about an optical system.
This module contains the `LensInfoViewer` class, which is designed to
present a tabular summary of the properties of each surface in an optical
system. This includes surface type, radius, thickness, material, conic
constant, and semi-aperture.

Kramer Harrison, 2024

re-worked by Manuel Fragata Mendes, june 2025
"""

from __future__ import annotations

import pandas as pd

import optiland.backend as be
from optiland.geometries import (
    ChebyshevPolynomialGeometry,
    EvenAsphere,
    OddAsphere,
    PolynomialGeometry,
    ZernikePolynomialGeometry,
)
from optiland.physical_apertures import RadialAperture
from optiland.visualization.base import BaseViewer
from optiland.visualization.info.material_formatter import MaterialFormatter


[docs] class LensInfoViewer(BaseViewer): """A class for viewing information about a lens. Args: optic (Optic): The optic object containing the lens information. Attributes: optic (Optic): The optic object containing the lens information. Methods: view(): Prints the lens information in a tabular format. """ def __init__(self, optic): self.optic = optic
[docs] def view(self): """Prints the lens information in a tabular format. The lens information includes the surface type, radius, thickness, material, conic, and semi-aperture of each surface. """ self.optic.updater.update_paraxial() surf_type = self._get_surface_types() comments = self._get_comments() radii = be.to_numpy(self.optic.surfaces.radii) thicknesses = self._get_thicknesses() conic = be.to_numpy(self.optic.surfaces.conic) semi_aperture = self._get_semi_apertures() mat = self._get_materials() self.optic.updater.update_paraxial() df = pd.DataFrame( { "Type": surf_type, "Comment": comments, "Radius": radii, "Thickness": thicknesses, "Material": mat, "Conic": conic, "Semi-aperture": semi_aperture, }, ) print(df.to_markdown(headers="keys", tablefmt="fancy_outline")) # Aspheric coefficients rows, headers = self._get_aspheric_coefficients() df = pd.DataFrame(rows, columns=headers) print(df.to_markdown(index=False, tablefmt="fancy_outline", floatfmt=".4g"))
def _get_surface_types(self): """Extracts and formats the surface types.""" surf_type = [] for surf in self.optic.surfaces: g = surf.geometry # Check if __str__ method exists if type(g).__dict__.get("__str__"): surf_type.append(str(surf.geometry)) else: raise ValueError("Unknown surface type") if surf.is_stop: surf_type[-1] = "Stop - " + surf_type[-1] return surf_type def _get_comments(self): """Extracts comments for each surface.""" return [surf.comment for surf in self.optic.surfaces] def _get_thicknesses(self): """Calculates thicknesses between surfaces.""" thicknesses = be.diff( be.ravel(self.optic.surfaces.positions), append=be.array([be.nan]) ) return be.to_numpy(thicknesses) def _get_semi_apertures(self): """Extracts semi-aperture values for each surface.""" semi_apertures = [] for surf in self.optic.surfaces: if isinstance(surf.aperture, RadialAperture): semi_apertures.append(be.to_numpy(surf.aperture.r_max)) else: semi_apertures.append(be.to_numpy(surf.semi_aperture)) return semi_apertures def _get_materials(self): """Determines the material for each surface.""" return [MaterialFormatter.format(surf) for surf in self.optic.surfaces] def _get_aspheric_coefficients(self): """Extracts the aspheric coefficients for each surface.""" valid_geometry_types = ( EvenAsphere, OddAsphere, PolynomialGeometry, ZernikePolynomialGeometry, ChebyshevPolynomialGeometry, ) surface_coeffs = [] for i, surface in enumerate(self.optic.surfaces): if isinstance(surface.geometry, valid_geometry_types): coefficients = list(surface.geometry.coefficients) if coefficients: rows = [f"Surface {i}"] + coefficients surface_coeffs.append(rows) if surface_coeffs: max_coef_num = len(max(surface_coeffs, key=len)) headers = ["Surface"] + [f"c{i}" for i in range(max_coef_num - 1)] return surface_coeffs, headers else: return None, None