Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions optika/_tests/test_mixins.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pytest
import abc
import dataclasses
import pathlib
import numpy as np
import matplotlib.axes
import matplotlib.pyplot as plt
Expand Down Expand Up @@ -227,3 +228,46 @@ class TestRollable(
AbstractTestRollable,
):
pass


class AbstractTestDxfWritable(
abc.ABC,
):

@pytest.mark.parametrize(
argnames="file",
argvalues=[
pathlib.Path("test_dwg.dxf"),
],
)
@pytest.mark.parametrize(
argnames="unit",
argvalues=[
u.mm,
],
)
@pytest.mark.parametrize(
argnames="transformation",
argvalues=[
None,
na.transformations.Cartesian3dRotationY(23 * u.deg),
],
)
def test_to_dxf(
self,
a: optika.mixins.DxfWritable,
file: pathlib.Path,
unit: u.Unit,
transformation: None | na.transformations.AbstractTransformation,
):
a.to_dxf(
file=file,
unit=unit,
transformation=transformation,
)

assert file.is_file()

assert file.stat().st_size > 0

file.unlink()
2 changes: 2 additions & 0 deletions optika/_tests/test_surfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@
sag=optika.sags.SphericalSag(radius=1000 * u.mm),
material=optika.materials.Mirror(),
aperture=optika.apertures.RectangularAperture(half_width=10 * u.mm),
aperture_mechanical=optika.apertures.RectangularAperture(11 * u.mm),
transformation=na.transformations.Cartesian3dTranslation(z=100 * u.mm),
rulings=optika.rulings.Rulings(spacing=1 * u.um, diffraction_order=1),
),
]


class AbstractTestAbstractSurface(
test_mixins.AbstractTestDxfWritable,
test_mixins.AbstractTestPlottable,
test_mixins.AbstractTestPrintable,
test_mixins.AbstractTestTransformable,
Expand Down
11 changes: 10 additions & 1 deletion optika/_tests/test_systems.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def test_image(


class AbstractTestAbstractSequentialSystem(
test_mixins.AbstractTestDxfWritable,
AbstractTestAbstractSystem,
):
def test_object(self, a: optika.systems.AbstractSequentialSystem):
Expand Down Expand Up @@ -249,6 +250,13 @@ def test_spot_diagram(self, a: optika.systems.AbstractSequentialSystem):
),
]

_transformations = [
None,
None,
na.transformations.Cartesian3dTranslation(x=100 * u.mm),
na.transformations.Cartesian3dRotationZ(23 * u.deg),
]

_surfaces = [
optika.surfaces.Surface(
name="mirror",
Expand Down Expand Up @@ -295,8 +303,9 @@ def test_spot_diagram(self, a: optika.systems.AbstractSequentialSystem):
surfaces=_surfaces,
sensor=_sensor,
grid_input=_grid_input,
transformation=transform,
)
for obj in _objects
for obj, transform in zip(_objects, _transformations)
],
)
class TestSequentialSystem(AbstractTestAbstractSequentialSystem):
Expand Down
54 changes: 54 additions & 0 deletions optika/apertures/_apertures.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import astropy.units as u
import named_arrays as na
import optika
from ezdxf.addons.r12writer import R12FastStreamWriter

__all__ = [
"AbstractAperture",
Expand All @@ -28,6 +29,7 @@

@dataclasses.dataclass(eq=False, repr=False)
class AbstractAperture(
optika.mixins.DxfWritable,
optika.mixins.Printable,
optika.mixins.Plottable,
optika.mixins.Transformable,
Expand Down Expand Up @@ -166,6 +168,58 @@ def plot(
**kwargs,
)

def _write_to_dxf(
self,
dxf: R12FastStreamWriter,
unit: u.Unit,
transformation: None | na.transformations.AbstractTransformation = None,
sag: None | optika.sags.AbstractSag = None,
**kwargs,
) -> None:

super()._write_to_dxf(
dxf=dxf,
unit=unit,
transformation=transformation,
)

wire = self.wire()

wire = wire.broadcast_to(wire.shape)

unit_wire = na.unit_normalized(wire)
if not unit_wire.is_equivalent(unit):
return

if sag is not None:
wire.z = sag(wire)

if transformation is not None:
wire = transformation(wire)

wire = na.nominal(wire.broadcasted)

x = na.as_named_array(wire.x)
y = na.as_named_array(wire.y)
z = na.as_named_array(wire.z)

for index in wire.ndindex(axis_ignored="wire"):

vertices = np.stack(
arrays=[
x[index].ndarray,
y[index].ndarray,
z[index].ndarray,
],
axis=~0,
)

vertices = vertices.to_value(unit)

dxf.add_polyline(
vertices=vertices,
)


@dataclasses.dataclass(eq=False, repr=False)
class CircularAperture(
Expand Down
1 change: 1 addition & 0 deletions optika/apertures/_apertures_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@


class AbstractTestAbstractAperture(
test_mixins.AbstractTestDxfWritable,
test_mixins.AbstractTestPrintable,
test_mixins.AbstractTestPlottable,
test_mixins.AbstractTestTransformable,
Expand Down
45 changes: 45 additions & 0 deletions optika/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@
from typing import Any
import abc
import dataclasses
import pathlib
import numpy as np
import numpy.typing as npt
import matplotlib.axes
import astropy.units as u
import named_arrays as na
import ezdxf.addons
from ezdxf.addons.r12writer import R12FastStreamWriter

__all__ = [
"Shaped",
Expand Down Expand Up @@ -243,3 +246,45 @@ def transformation(self) -> na.transformations.AbstractTransformation:
return super().transformation @ na.transformations.Cartesian3dRotationZ(
angle=self.roll
)


@dataclasses.dataclass(eq=False, repr=False)
class DxfWritable(abc.ABC):

def to_dxf(
self,
file: pathlib.Path,
unit: u.Unit,
transformation: None | na.transformations.AbstractTransformation = None,
):

with ezdxf.addons.r12writer(file) as dxf:
self._write_to_dxf(
dxf=dxf,
unit=unit,
transformation=transformation,
)

@abc.abstractmethod
def _write_to_dxf(
self,
dxf: R12FastStreamWriter,
unit: u.Unit,
transformation: None | na.transformations.AbstractTransformation = None,
**kwargs,
) -> None:
"""
Write a representation of this object to a DXF file.

Parameters
----------
dxf
The stream representing the open DXF file.
unit
The length units to use for this file.
transformation
An additional transformation to apply to the coordinate system
before writing to the DXF file.
kwargs
Additional keyword arguments passed to subclass implementations.
"""
53 changes: 53 additions & 0 deletions optika/rays/_ray_vectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import numpy as np
import astropy.units as u
import named_arrays as na
from ezdxf.addons.r12writer import R12FastStreamWriter
from .. import mixins

__all__ = [
"AbstractRayVectorArray",
Expand All @@ -22,6 +24,7 @@

@dataclasses.dataclass(eq=False, repr=False)
class AbstractRayVectorArray(
mixins.DxfWritable,
na.AbstractSpectralPositionalVectorArray,
):
"""An interface describing an ensemble of lights rays."""
Expand Down Expand Up @@ -166,6 +169,56 @@ def __array_ufunc__(
if method == "__call__":
return self.__array_add__(*inputs, **kwargs)

def _write_to_dxf(
self,
dxf: R12FastStreamWriter,
unit: u.Unit,
transformation: None | na.transformations.AbstractTransformation = None,
axis: None | str = None,
**kwargs,
) -> None:

if axis is None: # pragma: nocover
raise ValueError("`axis` cannot be None.")

super()._write_to_dxf(
dxf=dxf,
unit=unit,
transformation=transformation,
)

mask = self.unvignetted[{axis: ~0}]

position = self.position

position = position[mask]

if transformation is not None:
position = transformation(position)

position = na.nominal(position.broadcasted)

x = na.as_named_array(position.x)
y = na.as_named_array(position.y)
z = na.as_named_array(position.z)

for index in x.ndindex(axis_ignored=axis):

vertices = np.stack(
arrays=[
x[index].ndarray,
y[index].ndarray,
z[index].ndarray,
],
axis=~0,
)

vertices = vertices.to_value(unit)

dxf.add_polyline(
vertices=vertices,
)


@dataclasses.dataclass(eq=False, repr=False)
class RayVectorArray(
Expand Down
39 changes: 39 additions & 0 deletions optika/surfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
import dataclasses
import numpy.typing as npt
import matplotlib.axes
from astropy import units as u
import named_arrays as na
import optika
from ezdxf.addons.r12writer import R12FastStreamWriter

__all__ = [
"AbstractSurface",
Expand Down Expand Up @@ -41,6 +43,7 @@

@dataclasses.dataclass(eq=False, repr=False)
class AbstractSurface(
optika.mixins.DxfWritable,
optika.mixins.Plottable,
optika.mixins.Printable,
optika.mixins.Transformable,
Expand Down Expand Up @@ -240,6 +243,42 @@ def plot(

return result

def _write_to_dxf(
self,
dxf: R12FastStreamWriter,
unit: u.Unit,
transformation: None | na.transformations.AbstractTransformation = None,
**kwargs,
) -> None:

if self.transformation is not None:
if transformation is not None:
transformation = transformation @ self.transformation
else:
transformation = self.transformation

super()._write_to_dxf(
dxf=dxf,
unit=unit,
transformation=transformation,
)

if self.aperture is not None:
self.aperture._write_to_dxf(
dxf=dxf,
unit=unit,
transformation=transformation,
sag=self.sag,
)

if self.aperture_mechanical is not None:
self.aperture_mechanical._write_to_dxf(
dxf=dxf,
unit=unit,
transformation=transformation,
sag=self.sag,
)


@dataclasses.dataclass(eq=False, repr=False)
class Surface(
Expand Down
Loading
Loading