Skip to content

Commit cad7472

Browse files
Krandeclaude
andcommitted
feat(cad): adacpp builds CurveBoundedPlane; tessellate_shape backend-aware
- AdacppBackend.build() dispatches su.CurveBoundedPlane (plate/beam shell representation) to adacpp.cad.build_planar_face — native, no pythonocc. - tessellate_shape() now dispatches on handle type itself: pythonocc TopoDS → rich ShapeTesselator; other handles → active backend's tessellate verb + computed normals. Fixes every tessellation entry (occ_geom_to_poly_mesh, shape_to_tri_mesh, BatchTessellator) under adacpp; removes the now-redundant _triangulate helper. OccBackend unchanged (655 pass), ruff clean. Under adacpp, CurveBoundedPlane build-failures resolved (remaining shell failures are the FEM-meshing TopExp path, next). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 3432326 commit cad7472

2 files changed

Lines changed: 29 additions & 23 deletions

File tree

src/ada/cad/__init__.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ def build(self, geometry: "Geometry") -> ShapeHandle:
9393
# NotImplementedError rather than borrowing pythonocc. End goal: full
9494
# parity with OccBackend. See dap plan/v3 Phase 7.
9595
import ada.geom.solids as so
96+
import ada.geom.surfaces as su
9697

9798
g = geometry.geometry
9899

@@ -114,8 +115,6 @@ def _axis(d, default):
114115
p = g.position
115116
shape = self._cad.build_cone(list(p.location), _axis(p.axis, (0, 0, 1)), g.bottom_radius, g.height)
116117
elif isinstance(g, so.ExtrudedAreaSolid):
117-
import ada.geom.surfaces as su
118-
119118
area = g.swept_area
120119
if not isinstance(area, su.ArbitraryProfileDef) or area.profile_type != su.ProfileType.AREA:
121120
raise NotImplementedError(
@@ -129,6 +128,21 @@ def _axis(d, default):
129128
outer, inners, self._xyz(p.location),
130129
_axis(p.axis, (0, 0, 1)), _axis(p.ref_direction, (1, 0, 0)), g.depth,
131130
)
131+
elif isinstance(g, su.CurveBoundedPlane):
132+
import ada.geom.curves as cu
133+
134+
if not isinstance(g.outer_boundary, cu.IndexedPolyCurve):
135+
raise NotImplementedError(
136+
f"AdacppBackend.build: CurveBoundedPlane outer_boundary "
137+
f"{type(g.outer_boundary).__name__!r} not yet ported to adacpp."
138+
)
139+
outer = self._encode_curve(g.outer_boundary)
140+
inners = [self._encode_curve(c) for c in g.inner_boundaries]
141+
pos = g.basis_surface.position
142+
shape = self._cad.build_planar_face(
143+
outer, inners, self._xyz(pos.location),
144+
_axis(pos.axis, (0, 0, 1)), _axis(pos.ref_direction, (1, 0, 0)),
145+
)
132146
else:
133147
raise NotImplementedError(
134148
f"AdacppBackend.build: ada.geom type {type(g).__name__!r} is not yet ported to "

src/ada/occ/tessellating.py

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,18 @@ def tessellate_advanced_face(face: TopoDS_Shape, linear_deflection=0.1, angular_
8282

8383

8484
def tessellate_shape(shape: TopoDS_Shape, quality=1.0, render_edges=False, parallel=True) -> TriangleMesh:
85+
# Backend dispatch: a pythonocc TopoDS uses the rich ShapeTesselator
86+
# (normals + edges). Any other handle (e.g. an adacpp ShapeHandle) is
87+
# tessellated through the active backend's tessellate verb (adacpp's native
88+
# ShapeTesselator port) and adapted to a TriangleMesh with computed normals.
89+
if not isinstance(shape, TopoDS_Shape):
90+
from ada.cad import active_backend
91+
92+
mesh = active_backend().tessellate(shape)
93+
positions = np.ascontiguousarray(mesh.positions, dtype="float32")
94+
faces = np.ascontiguousarray(mesh.indices, dtype="uint32")
95+
return TriangleMesh(positions, faces, None, _vertex_normals(positions, faces))
96+
8597
# first, compute the tesselation
8698
try:
8799
tess = ShapeTesselator(shape)
@@ -161,7 +173,7 @@ def tessellate_occ_geom(
161173
tess_shape = tessellate_edges(occ_geom)
162174
indices = tess_shape.indices
163175
else:
164-
tess_shape = self._triangulate(occ_geom)
176+
tess_shape = tessellate_shape(occ_geom, self.quality, self.render_edges, self.parallel)
165177
indices = tess_shape.faces
166178

167179
mat_id = self.material_store.get(geom_color, None)
@@ -180,26 +192,6 @@ def tessellate_occ_geom(
180192
geom_ref,
181193
)
182194

183-
def _triangulate(self, occ_geom) -> TriangleMesh:
184-
"""Triangle-mesh a body. pythonocc TopoDS handles use the rich
185-
ShapeTesselator (normals + edges). Any other handle (e.g. an adacpp
186-
ShapeHandle) is tessellated through the active backend's tessellate
187-
verb and adapted to a TriangleMesh — keeping the tessellation path
188-
backend-independent. Normals are computed from the triangles since the
189-
lean backend Mesh carries only positions + indices."""
190-
from OCC.Core.TopoDS import TopoDS_Shape
191-
192-
if isinstance(occ_geom, TopoDS_Shape):
193-
return tessellate_shape(occ_geom, self.quality, self.render_edges, self.parallel)
194-
195-
from ada.cad import active_backend
196-
197-
mesh = active_backend().tessellate(occ_geom)
198-
positions = np.ascontiguousarray(mesh.positions, dtype="float32")
199-
faces = np.ascontiguousarray(mesh.indices, dtype="uint32")
200-
normals = _vertex_normals(positions, faces)
201-
return TriangleMesh(positions, faces, None, normals)
202-
203195
def tessellate_geom(
204196
self,
205197
geom: Geometry,

0 commit comments

Comments
 (0)