160 lines
5.4 KiB
Python
160 lines
5.4 KiB
Python
import pytest
|
|
|
|
from inire.geometry.components import Bend90, SBend, Straight
|
|
from inire.geometry.primitives import Port, rotate_port, translate_port
|
|
|
|
|
|
def test_straight_generation() -> None:
|
|
start = Port(0, 0, 0)
|
|
length = 10.0
|
|
width = 2.0
|
|
result = Straight.generate(start, length, width, snap_size=1.0)
|
|
|
|
assert result.end_port.x == 10.0
|
|
assert result.end_port.y == 0.0
|
|
assert result.end_port.orientation == 0.0
|
|
assert len(result.geometry) == 1
|
|
|
|
# Bounds of the polygon
|
|
minx, miny, maxx, maxy = result.geometry[0].bounds
|
|
assert minx == 0.0
|
|
assert maxx == 10.0
|
|
assert miny == -1.0
|
|
assert maxy == 1.0
|
|
|
|
|
|
def test_bend90_generation() -> None:
|
|
start = Port(0, 0, 0)
|
|
radius = 10.0
|
|
width = 2.0
|
|
|
|
# CW bend
|
|
result_cw = Bend90.generate(start, radius, width, direction="CW", snap_size=1.0)
|
|
assert result_cw.end_port.x == 10.0
|
|
assert result_cw.end_port.y == -10.0
|
|
assert result_cw.end_port.orientation == 270.0
|
|
|
|
# CCW bend
|
|
result_ccw = Bend90.generate(start, radius, width, direction="CCW", snap_size=1.0)
|
|
assert result_ccw.end_port.x == 10.0
|
|
assert result_ccw.end_port.y == 10.0
|
|
assert result_ccw.end_port.orientation == 90.0
|
|
|
|
|
|
def test_sbend_generation() -> None:
|
|
start = Port(0, 0, 0)
|
|
offset = 5.0
|
|
radius = 10.0
|
|
width = 2.0
|
|
|
|
result = SBend.generate(start, offset, radius, width, snap_size=1.0)
|
|
assert result.end_port.y == 5.0
|
|
assert result.end_port.orientation == 0.0
|
|
assert len(result.geometry) == 2 # Optimization: returns individual arcs
|
|
|
|
# Verify failure for large offset
|
|
with pytest.raises(ValueError, match=r"SBend offset .* must be less than 2\*radius"):
|
|
SBend.generate(start, 25.0, 10.0, 2.0)
|
|
|
|
|
|
def test_bend_collision_models() -> None:
|
|
start = Port(0, 0, 0)
|
|
radius = 10.0
|
|
width = 2.0
|
|
|
|
# 1. BBox model
|
|
res_bbox = Bend90.generate(start, radius, width, direction="CCW", collision_type="bbox", snap_size=1.0)
|
|
# Arc CCW R=10 from (0,0,0) ends at (10,10,90).
|
|
# Waveguide width is 2.0, so bbox will be slightly larger than (0,0,10,10)
|
|
minx, miny, maxx, maxy = res_bbox.geometry[0].bounds
|
|
assert minx <= 0.0 + 1e-6
|
|
assert maxx >= 10.0 - 1e-6
|
|
assert miny <= 0.0 + 1e-6
|
|
assert maxy >= 10.0 - 1e-6
|
|
|
|
# 2. Clipped BBox model
|
|
res_clipped = Bend90.generate(start, radius, width, direction="CCW", collision_type="clipped_bbox", clip_margin=1.0, snap_size=1.0)
|
|
# Area should be less than full bbox
|
|
assert res_clipped.geometry[0].area < res_bbox.geometry[0].area
|
|
|
|
|
|
def test_sbend_collision_models() -> None:
|
|
start = Port(0, 0, 0)
|
|
offset = 5.0
|
|
radius = 10.0
|
|
width = 2.0
|
|
|
|
res_bbox = SBend.generate(start, offset, radius, width, collision_type="bbox", snap_size=1.0)
|
|
# Geometry should be a list of individual bbox polygons for each arc
|
|
assert len(res_bbox.geometry) == 2
|
|
|
|
res_arc = SBend.generate(start, offset, radius, width, collision_type="arc", snap_size=1.0)
|
|
area_bbox = sum(p.area for p in res_bbox.geometry)
|
|
area_arc = sum(p.area for p in res_arc.geometry)
|
|
assert area_bbox > area_arc
|
|
|
|
|
|
def test_sbend_continuity() -> None:
|
|
# Verify SBend endpoints and continuity math
|
|
start = Port(10, 20, 90) # Starting facing up
|
|
offset = 4.0
|
|
radius = 20.0
|
|
width = 1.0
|
|
|
|
# We use snap_size=1.0 so that (10-offset) = 6.0 is EXACTLY hit.
|
|
res = SBend.generate(start, offset, radius, width, snap_size=1.0)
|
|
|
|
# Target orientation should be same as start
|
|
assert abs(res.end_port.orientation - 90.0) < 1e-6
|
|
|
|
# For a port at 90 deg, +offset is a shift in -x direction
|
|
assert abs(res.end_port.x - (10.0 - offset)) < 1e-6
|
|
|
|
# Geometry should be a list of valid polygons
|
|
assert len(res.geometry) == 2
|
|
for p in res.geometry:
|
|
assert p.is_valid
|
|
|
|
|
|
def test_arc_sagitta_precision() -> None:
|
|
# Verify that requested sagitta actually controls segment count
|
|
start = Port(0, 0, 0)
|
|
radius = 100.0 # Large radius to make sagitta significant
|
|
width = 2.0
|
|
|
|
# Coarse: 1um sagitta
|
|
res_coarse = Bend90.generate(start, radius, width, sagitta=1.0)
|
|
# Fine: 0.01um (10nm) sagitta
|
|
res_fine = Bend90.generate(start, radius, width, sagitta=0.01)
|
|
|
|
# Number of segments should be significantly higher for fine
|
|
# Exterior points = (segments + 1) * 2
|
|
pts_coarse = len(res_coarse.geometry[0].exterior.coords)
|
|
pts_fine = len(res_fine.geometry[0].exterior.coords)
|
|
|
|
assert pts_fine > pts_coarse * 2
|
|
|
|
|
|
def test_component_transform_invariance() -> None:
|
|
# Verify that generating at (0,0) then transforming
|
|
# is same as generating at the transformed port.
|
|
start0 = Port(0, 0, 0)
|
|
radius = 10.0
|
|
width = 2.0
|
|
|
|
res0 = Bend90.generate(start0, radius, width, direction="CCW", snap_size=1.0)
|
|
|
|
# Transform: Translate (10, 10) then Rotate 90
|
|
dx, dy = 10.0, 5.0
|
|
angle = 90.0
|
|
|
|
# 1. Transform the generated geometry
|
|
p_end_transformed = rotate_port(translate_port(res0.end_port, dx, dy), angle)
|
|
|
|
# 2. Generate at transformed start
|
|
start_transformed = rotate_port(translate_port(start0, dx, dy), angle)
|
|
res_transformed = Bend90.generate(start_transformed, radius, width, direction="CCW", snap_size=1.0)
|
|
|
|
assert abs(res_transformed.end_port.x - p_end_transformed.x) < 1e-6
|
|
assert abs(res_transformed.end_port.y - p_end_transformed.y) < 1e-6
|
|
assert abs(res_transformed.end_port.orientation - p_end_transformed.orientation) < 1e-6
|