From 726a72a20b4dbea511d0eb7c970fae3f64cb099c Mon Sep 17 00:00:00 2001 From: gumyr Date: Wed, 3 Dec 2025 11:35:20 -0500 Subject: [PATCH] Eliminating copying exploration methods in higher order classes --- src/build123d/topology/composite.py | 2 +- src/build123d/topology/one_d.py | 83 ++++++++++++++++------------ src/build123d/topology/shape_core.py | 39 ++++++++----- src/build123d/topology/three_d.py | 22 ++------ src/build123d/topology/two_d.py | 29 ++++------ 5 files changed, 93 insertions(+), 82 deletions(-) diff --git a/src/build123d/topology/composite.py b/src/build123d/topology/composite.py index 7299e32..365808d 100644 --- a/src/build123d/topology/composite.py +++ b/src/build123d/topology/composite.py @@ -600,7 +600,7 @@ class Compound(Mixin3D[TopoDS_Compound]): """Return the Compound""" shape_list = self.compounds() entity_count = len(shape_list) - if entity_count != 1: + if entity_count > 1: warnings.warn( f"Found {entity_count} compounds, returning first", stacklevel=2, diff --git a/src/build123d/topology/one_d.py b/src/build123d/topology/one_d.py index 2b9a211..d2a4913 100644 --- a/src/build123d/topology/one_d.py +++ b/src/build123d/topology/one_d.py @@ -556,7 +556,7 @@ class Mixin1D(Shape[TOPODS]): A curvature comb is a set of short line segments (“teeth”) erected perpendicular to the curve that visualize the signed curvature κ(u). - Tooth length is proportional to \|κ\| and the direction encodes the sign + Tooth length is proportional to |κ| and the direction encodes the sign (left normal for κ>0, right normal for κ<0). This is useful for inspecting fairness and continuity (C0/C1/C2) of edges and wires. @@ -684,30 +684,30 @@ class Mixin1D(Shape[TOPODS]): return derivative - def edge(self) -> Edge | None: - """Return the Edge""" - return Shape.get_single_shape(self, "Edge") + # def edge(self) -> Edge | None: + # """Return the Edge""" + # return Shape.get_single_shape(self, "Edge") - def edges(self) -> ShapeList[Edge]: - """edges - all the edges in this Shape""" - if isinstance(self, Wire) and self.wrapped is not None: - # The WireExplorer is a tool to explore the edges of a wire in a connection order. - explorer = BRepTools_WireExplorer(self.wrapped) + # def edges(self) -> ShapeList[Edge]: + # """edges - all the edges in this Shape""" + # if isinstance(self, Wire) and self.wrapped is not None: + # # The WireExplorer is a tool to explore the edges of a wire in a connection order. + # explorer = BRepTools_WireExplorer(self.wrapped) - edge_list: ShapeList[Edge] = ShapeList() - while explorer.More(): - next_edge = Edge(explorer.Current()) - next_edge.topo_parent = ( - self if self.topo_parent is None else self.topo_parent - ) - edge_list.append(next_edge) - explorer.Next() - return edge_list + # edge_list: ShapeList[Edge] = ShapeList() + # while explorer.More(): + # next_edge = Edge(explorer.Current()) + # next_edge.topo_parent = ( + # self if self.topo_parent is None else self.topo_parent + # ) + # edge_list.append(next_edge) + # explorer.Next() + # return edge_list - edge_list = Shape.get_shape_list(self, "Edge") - return edge_list.filter_by( - lambda e: BRep_Tool.Degenerated_s(e.wrapped), reverse=True - ) + # edge_list = Shape.get_shape_list(self, "Edge") + # return edge_list.filter_by( + # lambda e: BRep_Tool.Degenerated_s(e.wrapped), reverse=True + # ) def end_point(self) -> Vector: """The end point of this edge. @@ -1431,21 +1431,21 @@ class Mixin1D(Shape[TOPODS]): """ return self.derivative_at(position, 1, position_mode).normalized() - def vertex(self) -> Vertex | None: - """Return the Vertex""" - return Shape.get_single_shape(self, "Vertex") + # def vertex(self) -> Vertex | None: + # """Return the Vertex""" + # return Shape.get_single_shape(self, "Vertex") - def vertices(self) -> ShapeList[Vertex]: - """vertices - all the vertices in this Shape""" - return Shape.get_shape_list(self, "Vertex") + # def vertices(self) -> ShapeList[Vertex]: + # """vertices - all the vertices in this Shape""" + # return Shape.get_shape_list(self, "Vertex") - def wire(self) -> Wire | None: - """Return the Wire""" - return Shape.get_single_shape(self, "Wire") + # def wire(self) -> Wire | None: + # """Return the Wire""" + # return Shape.get_single_shape(self, "Wire") - def wires(self) -> ShapeList[Wire]: - """wires - all the wires in this Shape""" - return Shape.get_shape_list(self, "Wire") + # def wires(self) -> ShapeList[Wire]: + # """wires - all the wires in this Shape""" + # return Shape.get_shape_list(self, "Wire") class Edge(Mixin1D[TopoDS_Edge]): @@ -3561,6 +3561,21 @@ class Wire(Mixin1D[TopoDS_Wire]): return return_value + def edges(self) -> ShapeList[Edge]: + """edges - all the edges in this Shape""" + # The WireExplorer is a tool to explore the edges of a wire in a connection order. + explorer = BRepTools_WireExplorer(self.wrapped) + + edge_list: ShapeList[Edge] = ShapeList() + while explorer.More(): + next_edge = Edge(explorer.Current()) + next_edge.topo_parent = ( + self if self.topo_parent is None else self.topo_parent + ) + edge_list.append(next_edge) + explorer.Next() + return edge_list + def fillet_2d(self, radius: float, vertices: Iterable[Vertex]) -> Wire: """fillet_2d diff --git a/src/build123d/topology/shape_core.py b/src/build123d/topology/shape_core.py index 3858e4a..6e84222 100644 --- a/src/build123d/topology/shape_core.py +++ b/src/build123d/topology/shape_core.py @@ -101,7 +101,7 @@ from OCP.BRepGProp import BRepGProp, BRepGProp_Face from OCP.BRepIntCurveSurface import BRepIntCurveSurface_Inter from OCP.BRepMesh import BRepMesh_IncrementalMesh from OCP.BRepPrimAPI import BRepPrimAPI_MakeHalfSpace -from OCP.BRepTools import BRepTools +from OCP.BRepTools import BRepTools, BRepTools_WireExplorer from OCP.gce import gce_MakeLin from OCP.Geom import Geom_Line from OCP.GeomAPI import GeomAPI_ProjectPointOnSurf @@ -839,7 +839,9 @@ class Shape(NodeMixin, Generic[TOPODS]): with a warning if count != 1.""" shape_list = Shape.get_shape_list(shape, entity_type) entity_count = len(shape_list) - if entity_count != 1: + if entity_count == 0: + return None + elif entity_count > 1: warnings.warn( f"Found {entity_count} {entity_type.lower()}s, returning first", stacklevel=3, @@ -1185,13 +1187,14 @@ class Shape(NodeMixin, Generic[TOPODS]): def edge(self) -> Edge | None: """Return the Edge""" - return None - - # Note all sub-classes have vertices and vertex methods + return Shape.get_single_shape(self, "Edge") def edges(self) -> ShapeList[Edge]: """edges - all the edges in this Shape - subclasses may override""" - return ShapeList() + edge_list = Shape.get_shape_list(self, "Edge") + return edge_list.filter_by( + lambda e: BRep_Tool.Degenerated_s(e.wrapped), reverse=True + ) def entities(self, topo_type: Shapes) -> list[TopoDS_Shape]: """Return all of the TopoDS sub entities of the given type""" @@ -1201,11 +1204,11 @@ class Shape(NodeMixin, Generic[TOPODS]): def face(self) -> Face | None: """Return the Face""" - return None + return Shape.get_single_shape(self, "Face") def faces(self) -> ShapeList[Face]: """faces - all the faces in this Shape""" - return ShapeList() + return Shape.get_shape_list(self, "Face") def faces_intersected_by_axis( self, @@ -1724,11 +1727,11 @@ class Shape(NodeMixin, Generic[TOPODS]): def shell(self) -> Shell | None: """Return the Shell""" - return None + return Shape.get_single_shape(self, "Shell") def shells(self) -> ShapeList[Shell]: """shells - all the shells in this Shape""" - return ShapeList() + return Shape.get_shape_list(self, "Shell") def show_topology( self, @@ -1782,11 +1785,11 @@ class Shape(NodeMixin, Generic[TOPODS]): def solid(self) -> Solid | None: """Return the Solid""" - return None + return Shape.get_single_shape(self, "Solid") def solids(self) -> ShapeList[Solid]: """solids - all the solids in this Shape""" - return ShapeList() + return Shape.get_shape_list(self, "Solid") @overload def split( @@ -2235,11 +2238,11 @@ class Shape(NodeMixin, Generic[TOPODS]): def wire(self) -> Wire | None: """Return the Wire""" - return None + return Shape.get_single_shape(self, "Wire") def wires(self) -> ShapeList[Wire]: """wires - all the wires in this Shape""" - return ShapeList() + return Shape.get_shape_list(self, "Wire") def _apply_transform(self, transformation: gp_Trsf) -> Self: """Private Apply Transform @@ -2395,6 +2398,14 @@ class Shape(NodeMixin, Generic[TOPODS]): return shape_to_html(self)._repr_html_() + def vertex(self) -> Vertex | None: + """Return the Vertex""" + return Shape.get_single_shape(self, "Vertex") + + def vertices(self) -> ShapeList[Vertex]: + """vertices - all the vertices in this Shape""" + return Shape.get_shape_list(self, "Vertex") + class Comparable(ABC): """Abstract base class that requires comparison methods""" diff --git a/src/build123d/topology/three_d.py b/src/build123d/topology/three_d.py index c9ce928..e6a7c38 100644 --- a/src/build123d/topology/three_d.py +++ b/src/build123d/topology/three_d.py @@ -144,16 +144,6 @@ class Mixin3D(Shape[TOPODS]): project_to_viewport = Mixin1D.project_to_viewport find_intersection_points = Mixin2D.find_intersection_points - vertices = Mixin1D.vertices - vertex = Mixin1D.vertex - edges = Mixin1D.edges - edge = Mixin1D.edge - wires = Mixin1D.wires - wire = Mixin1D.wire - faces = Mixin2D.faces - face = Mixin2D.face - shells = Mixin2D.shells - shell = Mixin2D.shell # ---- Properties ---- @property @@ -725,13 +715,13 @@ class Mixin3D(Shape[TOPODS]): return offset_solid - def solid(self) -> Solid | None: - """Return the Solid""" - return Shape.get_single_shape(self, "Solid") + # def solid(self) -> Solid | None: + # """Return the Solid""" + # return Shape.get_single_shape(self, "Solid") - def solids(self) -> ShapeList[Solid]: - """solids - all the solids in this Shape""" - return Shape.get_shape_list(self, "Solid") + # def solids(self) -> ShapeList[Solid]: + # """solids - all the solids in this Shape""" + # return Shape.get_shape_list(self, "Solid") class Solid(Mixin3D[TopoDS_Solid]): diff --git a/src/build123d/topology/two_d.py b/src/build123d/topology/two_d.py index 8a5879d..5c3e35c 100644 --- a/src/build123d/topology/two_d.py +++ b/src/build123d/topology/two_d.py @@ -174,11 +174,6 @@ class Mixin2D(ABC, Shape[TOPODS]): project_to_viewport = Mixin1D.project_to_viewport - vertices = Mixin1D.vertices - vertex = Mixin1D.vertex - edges = Mixin1D.edges - edge = Mixin1D.edge - wires = Mixin1D.wires # ---- Properties ---- @property @@ -226,13 +221,13 @@ class Mixin2D(ABC, Shape[TOPODS]): return new_surface - def face(self) -> Face | None: - """Return the Face""" - return Shape.get_single_shape(self, "Face") + # def face(self) -> Face | None: + # """Return the Face""" + # return Shape.get_single_shape(self, "Face") - def faces(self) -> ShapeList[Face]: - """faces - all the faces in this Shape""" - return Shape.get_shape_list(self, "Face") + # def faces(self) -> ShapeList[Face]: + # """faces - all the faces in this Shape""" + # return Shape.get_shape_list(self, "Face") def find_intersection_points( self, other: Axis, tolerance: float = TOLERANCE @@ -412,13 +407,13 @@ class Mixin2D(ABC, Shape[TOPODS]): """Return a copy of self moved along the normal by amount""" return copy.deepcopy(self).moved(Location(self.normal_at() * amount)) - def shell(self) -> Shell | None: - """Return the Shell""" - return Shape.get_single_shape(self, "Shell") + # def shell(self) -> Shell | None: + # """Return the Shell""" + # return Shape.get_single_shape(self, "Shell") - def shells(self) -> ShapeList[Shell]: - """shells - all the shells in this Shape""" - return Shape.get_shape_list(self, "Shell") + # def shells(self) -> ShapeList[Shell]: + # """shells - all the shells in this Shape""" + # return Shape.get_shape_list(self, "Shell") def _wrap_edge( self,