Skip to content

feat(geometry): add arc segment support to PolySlab geometry#3220

Open
weiliangjin2021 wants to merge 1 commit intodevelopfrom
weiliang/FXC-1637-polyslab-with-bulge-factor-for-vertices-support-for-arcs
Open

feat(geometry): add arc segment support to PolySlab geometry#3220
weiliangjin2021 wants to merge 1 commit intodevelopfrom
weiliang/FXC-1637-polyslab-with-bulge-factor-for-vertices-support-for-arcs

Conversation

@weiliangjin2021
Copy link
Collaborator

@weiliangjin2021 weiliangjin2021 commented Jan 29, 2026

Note

Medium Risk
Changes core geometry computations (inside, intersections, bounds, area/perimeter) and adds new validation constraints, which could subtly affect existing workflows; functionality is gated behind bulges but touches commonly used code paths.

Overview
Adds first-class arc-edge support to PolySlab via an optional bulges array (bulge = tan(θ/4)), including canonicalization of vertices/bulges, arc discretization for Shapely operations, and arc-aware bounds, inside/intersection geometry, and area/perimeter calculations (affecting volume/surface area).

Introduces new validation rules for bulges (length match, finite values, no self-intersecting arc polygons, and rejecting zero-length edges with nonzero bulge) and temporarily restricts arc polyslabs to vertical sidewalls with no dilation; adjoint derivative computation now explicitly raises NotImplementedError when arcs are present. Schemas and tests are updated to serialize/validate bulges across simulation types and cover the new behaviors.

Written by Cursor Bugbot for commit dd4cec3. This will update automatically on new commits. Configure here.

@weiliangjin2021 weiliangjin2021 added the awaiting backend not to be merged as backend is not finalized label Jan 29, 2026
@weiliangjin2021 weiliangjin2021 force-pushed the weiliang/FXC-1637-polyslab-with-bulge-factor-for-vertices-support-for-arcs branch from c3f10b5 to fe09c6f Compare February 6, 2026 18:31
Add bulge/arc support
- Finite bulge validation (reject inf/nan)
- Synchronized vertex+bulge canonicalization (winding reversal,
  zero-length edge handling)
- Adjoint guard for non-zero bulge polyslabs
@weiliangjin2021 weiliangjin2021 force-pushed the weiliang/FXC-1637-polyslab-with-bulge-factor-for-vertices-support-for-arcs branch from fe09c6f to dd4cec3 Compare February 6, 2026 18:42
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

return [self.make_shapely_polygon(self.reference_polygon)]
# Use discretized polygon for arc segments
poly_vertices = self._discretized_reference_polygon
return [self.make_shapely_polygon(poly_vertices)]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side intersections not updated for arc segments

Medium Severity

_intersections_side uses self.reference_polygon (straight-edged vertices) for the vertical sidewall case, while inside and _intersections_normal were both updated to use self._discretized_reference_polygon. This means side-view cross-sections of a PolySlab with arc segments will show straight edges instead of curves, producing incorrect geometry in visualizations and any downstream computations relying on intersections_2d with a non-normal axis.

Additional Locations (2)

Fix in Cursor Fix in Web

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

Diff Coverage

Diff: origin/develop...HEAD, staged and unstaged changes

  • tidy3d/components/geometry/polyslab.py (94.2%): Missing lines 1283,1291,1302,1367,1453,1512,1541,1950-1951,1969-1972

Summary

  • Total: 226 lines
  • Missing: 13 lines
  • Coverage: 94%

tidy3d/components/geometry/polyslab.py

Lines 1279-1287

  1279             chord_vec = p1 - p0
  1280             chord_length = np.linalg.norm(chord_vec)
  1281 
  1282             if math.isclose(chord_length, 0):
! 1283                 continue
  1284 
  1285             # θ = 4 * atan(|bulge|)
  1286             theta = 4.0 * np.arctan(abs(b))

Lines 1287-1295

  1287 
  1288             # Radius: r = chord / (2 * sin(θ/2))
  1289             half_theta = theta / 2.0
  1290             if math.isclose(half_theta, 0) or math.isclose(np.sin(half_theta), 0):
! 1291                 continue
  1292             radius = chord_length / (2.0 * np.sin(half_theta))
  1293 
  1294             # Circular segment area: A = r² * (θ - sin(θ)) / 2
  1295             segment_area = (radius**2) * (theta - np.sin(theta)) / 2.0

Lines 1298-1306

  1298             # negative bulge subtracts area
  1299             if b > 0:
  1300                 base_area += segment_area
  1301             else:
! 1302                 base_area -= segment_area
  1303 
  1304         return base_area
  1305 
  1306     @staticmethod

Lines 1363-1371

  1363         chord_vec = p1 - p0
  1364         chord_length = np.linalg.norm(chord_vec)
  1365 
  1366         if math.isclose(chord_length, 0):
! 1367             raise SetupError("Arc endpoints are coincident; cannot define arc.")
  1368 
  1369         if math.isclose(bulge, 0):
  1370             # Degenerate case: straight line, no arc
  1371             return {

Lines 1449-1457

  1449             Shape (num_points, 2).
  1450         """
  1451         if math.isclose(bulge, 0):
  1452             # Straight line: just return endpoint
! 1453             return np.array([p1])
  1454 
  1455         arc = PolySlab._arc_from_bulge(p0, p1, bulge)
  1456         center = arc["center"]
  1457         radius = arc["radius"]

Lines 1508-1516

  1508 
  1509         # r = chord / (2 * sin(θ/2))
  1510         half_theta = theta / 2.0
  1511         if math.isclose(half_theta, 0):
! 1512             return chord_length
  1513         radius = chord_length / (2.0 * np.sin(half_theta))
  1514 
  1515         # arc_length = r * |θ|
  1516         return radius * theta

Lines 1537-1545

  1537         p1 = np.asarray(p1, dtype=float)
  1538 
  1539         if math.isclose(bulge, 0):
  1540             # Straight line
! 1541             return np.minimum(p0, p1), np.maximum(p0, p1)
  1542 
  1543         # Use discretized arc points to compute bounds (robust approach)
  1544         arc_points = PolySlab._discretize_arc(p0, p1, bulge)
  1545         all_points = np.vstack([p0, arc_points])

Lines 1946-1955

  1946         length = z_max - z_min
  1947 
  1948         # Use arc-aware area calculation when arc segments present
  1949         if self._has_arc_segments:
! 1950             top_area = abs(self._area_with_arcs(self.top_polygon, self._bulges))
! 1951             base_area = abs(self._area_with_arcs(self.base_polygon, self._bulges))
  1952         else:
  1953             top_area = abs(self._area(self.top_polygon))
  1954             base_area = abs(self._area(self.base_polygon))

Lines 1965-1976

  1965         base_polygon = self.base_polygon
  1966 
  1967         # Use arc-aware calculations when arc segments present
  1968         if self._has_arc_segments:
! 1969             top_area = abs(self._area_with_arcs(top_polygon, self._bulges))
! 1970             base_area = abs(self._area_with_arcs(base_polygon, self._bulges))
! 1971             top_perim = self._perimeter_with_arcs(top_polygon, self._bulges)
! 1972             base_perim = self._perimeter_with_arcs(base_polygon, self._bulges)
  1973         else:
  1974             top_area = abs(self._area(top_polygon))
  1975             base_area = abs(self._area(base_polygon))
  1976             top_perim = self._perimeter(top_polygon)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting backend not to be merged as backend is not finalized

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant