Skip to content

Commit 63c8cc1

Browse files
Polygon vertices cleanup and more robust corner containtment check
1 parent 11ae8a3 commit 63c8cc1

File tree

4 files changed

+15
-4
lines changed

4 files changed

+15
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2525
- Bug in `CoaxialLumpedPort` where source injection is off when the `normal_axis` is not `z`.
2626
- Validation of `freqs` in the `ComponentModeler` and `TerminalComponentModeler`.
2727
- Calculation of voltage and current in the `WavePort`, when one type of path integral is supplied and the transmission line mode is lossy.
28+
- Polygon vertices cleanup in `ClipOperation.intersections_plane`.
2829

2930
## [2.9.0] - 2025-08-04
3031

tests/test_components/test_geometry.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1288,8 +1288,7 @@ def test_cleanup_shapely_object():
12881288
# Test using a non-empty exterior polygon (big_square_5x5)
12891289
orig_polygon = shapely.Polygon(exterior_coords, interior_coords_list)
12901290
new_polygon = cleanup_shapely_object(orig_polygon, tolerance_ratio=1e-12)
1291-
# Delete any nearby or overlapping vertices (cleanup_shapely_object() does not do this).
1292-
new_polygon = shapely.simplify(new_polygon, tolerance=1e-10)
1291+
# Delete any nearby or overlapping vertices (cleanup_shapely_object() now does this).
12931292
# Now `new_polygon` should only contain the coordinates of the square (with a duplicate at end).
12941293
assert len(new_polygon.exterior.coords) == 5 # squares have 4 vertices but shapely adds 1
12951294
assert len(new_polygon.interiors) == 1 # only the "triangle_empty_tails" interior hole survives
@@ -1298,5 +1297,4 @@ def test_cleanup_shapely_object():
12981297
exterior_coords = triangle_collinear # has zero area
12991298
orig_polygon = shapely.Polygon(exterior_coords)
13001299
new_polygon = cleanup_shapely_object(orig_polygon, tolerance_ratio=1e-12)
1301-
new_polygon = shapely.simplify(new_polygon, tolerance=1e-10)
13021300
assert len(new_polygon.exterior.coords) == 0 # empty / collinear polygons should get deleted

tidy3d/components/geometry/base.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666

6767
POLY_GRID_SIZE = 1e-12
6868
POLY_TOLERANCE_RATIO = 1e-12
69+
POLY_DISTANCE_TOLERANCE = 8e-12
6970

7071

7172
_shapely_operations = {
@@ -3397,6 +3398,9 @@ def cleanup_shapely_object(obj: Shapely, tolerance_ratio: float = POLY_TOLERANCE
33973398
cap_style="square",
33983399
quad_segs=3,
33993400
)
3401+
# Clean vertices of very close distances created during the erosion/dilation process.
3402+
# The distance value is heuristic.
3403+
cleaned_obj = cleaned_obj.simplify(POLY_DISTANCE_TOLERANCE, preserve_topology=True)
34003404
# Revert to the original scale and position.
34013405
rescaled_clean_obj = shapely.affinity.affine_transform(
34023406
cleaned_obj,

tidy3d/components/grid/grid_spec.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
from tidy3d.components.base import Tidy3dBaseModel, cached_property, skip_if_fields_missing
1212
from tidy3d.components.geometry.base import Box, ClipOperation
13+
from tidy3d.components.geometry.utils_2d import increment_float
1314
from tidy3d.components.lumped_element import LumpedElementType
1415
from tidy3d.components.source.utils import SourceType
1516
from tidy3d.components.structure import MeshOverrideStructure, Structure, StructureType
@@ -1323,6 +1324,13 @@ def _is_inplane_bounded(self) -> bool:
13231324
self.size[(self.axis + 2) % 3]
13241325
)
13251326

1327+
@cached_property
1328+
def _slightly_enlarged_box(self) -> Box:
1329+
"""Slightly enlarged box for robust point containment querying."""
1330+
# increase size slightly
1331+
size = [increment_float(orig_length, 1) for orig_length in self.size]
1332+
return Box(center=self.center, size=size)
1333+
13261334
def _unpop_axis(self, ax_coord: float, plane_coord: Any) -> CoordinateOptional:
13271335
"""Combine coordinate along axis with identical coordinates on the plane tangential to the axis.
13281336
@@ -1411,7 +1419,7 @@ def _inplane_inside(self, point: ArrayFloat2D) -> bool:
14111419
point_3d = self.unpop_axis(
14121420
ax_coord=self.center[self.axis], plane_coords=point, axis=self.axis
14131421
)
1414-
return self.inside(point_3d[0], point_3d[1], point_3d[2])
1422+
return self._slightly_enlarged_box.inside(point_3d[0], point_3d[1], point_3d[2])
14151423

14161424
def _corners_and_convexity_2d(
14171425
self, structure_list: list[Structure], ravel: bool

0 commit comments

Comments
 (0)