Skip to content

Commit

Permalink
Added optika.apertures.OctagonalAperture.
Browse files Browse the repository at this point in the history
  • Loading branch information
byrdie committed Oct 7, 2023
1 parent de3578b commit 9f50267
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 0 deletions.
41 changes: 41 additions & 0 deletions optika/_tests/test_apertures.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,44 @@ def test_half_width(self, a: optika.apertures.RectangularAperture):
)
assert isinstance(a.half_width, types_valid)
assert np.all(a.half_width >= 0)


class AbstractTestAbstractRegularPolygonalAperture(
AbstractTestAbstractPolygonalAperture,
):

def test_radius(self, a: optika.apertures.AbstractRegularPolygonalAperture):
assert isinstance(na.as_named_array(a.radius), na.AbstractScalar)

def test_num_vertices(self, a: optika.apertures.AbstractRegularPolygonalAperture):
assert isinstance(a.num_vertices, int)


class AbstractTestAbstractOctagonalAperture(
AbstractTestAbstractRegularPolygonalAperture,
):
pass


@pytest.mark.parametrize(
argnames="a",
argvalues=[
optika.apertures.OctagonalAperture(
radius=radius,
samples_wire=21,
active=active,
inverted=inverted,
transformation=transformation,
kwargs_plot=kwargs_plot,
)
for radius in radius_parameterization
for active in active_parameterization
for inverted in inverted_parameterization
for transformation in transform_parameterization
for kwargs_plot in test_plotting.kwargs_plot_parameterization
],
)
class TestOctagonalAperture(
AbstractTestAbstractOctagonalAperture,
):
pass
96 changes: 96 additions & 0 deletions optika/apertures.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
__all__ = [
"AbstractAperture",
"CircularAperture",
"AbstractRegularPolygonalAperture",
"AbstractOctagonalAperture",
"OctagonalAperture",
]


Expand Down Expand Up @@ -292,6 +295,37 @@ class AbstractPolygonalAperture(
Base class for any type of polygonal aperture
"""

def __call__(
self,
position: na.AbstractCartesian3dVectorArray,
) -> na.AbstractScalar:
vertices = self.vertices
active = self.active
inverted = self.inverted
if self.transformation is not None:
position = self.transformation.inverse(position)

shape = na.shape_broadcasted(vertices, active, inverted, position)

vertices = na.broadcast_to(vertices, shape)
active = na.broadcast_to(active, shape)
inverted = na.broadcast_to(inverted, shape)
position = na.broadcast_to(position, shape)

result = False
for v in range(vertices.shape["vertex"]):
vert_j = vertices[dict(vertex=v - 1)]
vert_i = vertices[dict(vertex=v)]
slope = (vert_j.y - vert_i.y) / (vert_j.x - vert_i.x)
condition_1 = (vert_i.y > position.y) != (vert_j.y > position.y)
condition_2 = position.x < ((position.y - vert_i.y) / slope + vert_i.x)
result = result ^ (condition_1 & condition_2)

result[inverted] = ~result[inverted]
result[~active] = True

return result

@property
def bound_lower(self) -> na.AbstractCartesian3dVectorArray:
return self.vertices.min(axis="vertex")
Expand Down Expand Up @@ -468,3 +502,65 @@ def vertices(self):
if self.transformation is not None:
result = self.transformation(result)
return result


@dataclasses.dataclass(eq=False, repr=False)
class AbstractRegularPolygonalAperture(
AbstractPolygonalAperture,
):
@property
@abc.abstractmethod
def radius(self) -> na.ScalarLike:
"""
the radial distance from the origin to each vertex
"""

@property
@abc.abstractmethod
def num_vertices(self) -> int:
"""
Number of vertices in this polygon
"""

@property
def vertices(self) -> na.AbstractCartesian3dVectorArray:
radius = self.radius
unit = na.unit(radius)
angle = na.linspace(
start=0 * u.deg,
stop=360 * u.deg,
axis="vertex",
num=self.num_vertices,
endpoint=False,
)
result = na.Cartesian3dVectorArray(
x=radius * np.cos(angle).value,
y=radius * np.sin(angle).value,
z=0,
)
if unit is not None:
result.z = result.z * unit
if self.transformation is not None:
result = self.transformation(result)
return result


@dataclasses.dataclass(eq=False, repr=False)
class AbstractOctagonalAperture(
AbstractRegularPolygonalAperture,
):
@property
def num_vertices(self) -> int:
return 8


@dataclasses.dataclass(eq=False, repr=False)
class OctagonalAperture(
AbstractOctagonalAperture,
):
radius: float | u.Quantity | na.AbstractScalar = 0 * u.mm
samples_wire: int = 101
active: bool | na.AbstractScalar = True
inverted: bool | na.AbstractScalar = False
transformation: None | na.transformations.AbstractTransformation = None
kwargs_plot: None | dict = None

0 comments on commit 9f50267

Please sign in to comment.