Eliminating copying exploration methods in higher order classes
Some checks are pending
benchmarks / benchmarks (macos-14, 3.12) (push) Waiting to run
benchmarks / benchmarks (macos-15-intel, 3.12) (push) Waiting to run
benchmarks / benchmarks (ubuntu-latest, 3.12) (push) Waiting to run
benchmarks / benchmarks (windows-latest, 3.12) (push) Waiting to run
Upload coverage reports to Codecov / run (push) Waiting to run
pylint / lint (3.10) (push) Waiting to run
Run type checker / typecheck (3.10) (push) Waiting to run
Run type checker / typecheck (3.13) (push) Waiting to run
Wheel building and publishing / Build wheel on ubuntu-latest (push) Waiting to run
Wheel building and publishing / upload_pypi (push) Blocked by required conditions
tests / tests (macos-14, 3.10) (push) Waiting to run
tests / tests (macos-14, 3.13) (push) Waiting to run
tests / tests (macos-15-intel, 3.10) (push) Waiting to run
tests / tests (macos-15-intel, 3.13) (push) Waiting to run
tests / tests (ubuntu-latest, 3.10) (push) Waiting to run
tests / tests (ubuntu-latest, 3.13) (push) Waiting to run
tests / tests (windows-latest, 3.10) (push) Waiting to run
tests / tests (windows-latest, 3.13) (push) Waiting to run

This commit is contained in:
gumyr 2025-12-03 11:35:20 -05:00
parent 3871345dcd
commit 726a72a20b
5 changed files with 93 additions and 82 deletions

View file

@ -600,7 +600,7 @@ class Compound(Mixin3D[TopoDS_Compound]):
"""Return the Compound""" """Return the Compound"""
shape_list = self.compounds() shape_list = self.compounds()
entity_count = len(shape_list) entity_count = len(shape_list)
if entity_count != 1: if entity_count > 1:
warnings.warn( warnings.warn(
f"Found {entity_count} compounds, returning first", f"Found {entity_count} compounds, returning first",
stacklevel=2, stacklevel=2,

View file

@ -556,7 +556,7 @@ class Mixin1D(Shape[TOPODS]):
A curvature comb is a set of short line segments (teeth) erected A curvature comb is a set of short line segments (teeth) erected
perpendicular to the curve that visualize the signed curvature κ(u). 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 (left normal for κ>0, right normal for κ<0). This is useful for inspecting
fairness and continuity (C0/C1/C2) of edges and wires. fairness and continuity (C0/C1/C2) of edges and wires.
@ -684,30 +684,30 @@ class Mixin1D(Shape[TOPODS]):
return derivative return derivative
def edge(self) -> Edge | None: # def edge(self) -> Edge | None:
"""Return the Edge""" # """Return the Edge"""
return Shape.get_single_shape(self, "Edge") # return Shape.get_single_shape(self, "Edge")
def edges(self) -> ShapeList[Edge]: # def edges(self) -> ShapeList[Edge]:
"""edges - all the edges in this Shape""" # """edges - all the edges in this Shape"""
if isinstance(self, Wire) and self.wrapped is not None: # 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. # # The WireExplorer is a tool to explore the edges of a wire in a connection order.
explorer = BRepTools_WireExplorer(self.wrapped) # explorer = BRepTools_WireExplorer(self.wrapped)
edge_list: ShapeList[Edge] = ShapeList() # edge_list: ShapeList[Edge] = ShapeList()
while explorer.More(): # while explorer.More():
next_edge = Edge(explorer.Current()) # next_edge = Edge(explorer.Current())
next_edge.topo_parent = ( # next_edge.topo_parent = (
self if self.topo_parent is None else self.topo_parent # self if self.topo_parent is None else self.topo_parent
) # )
edge_list.append(next_edge) # edge_list.append(next_edge)
explorer.Next() # explorer.Next()
return edge_list # return edge_list
edge_list = Shape.get_shape_list(self, "Edge") # edge_list = Shape.get_shape_list(self, "Edge")
return edge_list.filter_by( # return edge_list.filter_by(
lambda e: BRep_Tool.Degenerated_s(e.wrapped), reverse=True # lambda e: BRep_Tool.Degenerated_s(e.wrapped), reverse=True
) # )
def end_point(self) -> Vector: def end_point(self) -> Vector:
"""The end point of this edge. """The end point of this edge.
@ -1431,21 +1431,21 @@ class Mixin1D(Shape[TOPODS]):
""" """
return self.derivative_at(position, 1, position_mode).normalized() return self.derivative_at(position, 1, position_mode).normalized()
def vertex(self) -> Vertex | None: # def vertex(self) -> Vertex | None:
"""Return the Vertex""" # """Return the Vertex"""
return Shape.get_single_shape(self, "Vertex") # return Shape.get_single_shape(self, "Vertex")
def vertices(self) -> ShapeList[Vertex]: # def vertices(self) -> ShapeList[Vertex]:
"""vertices - all the vertices in this Shape""" # """vertices - all the vertices in this Shape"""
return Shape.get_shape_list(self, "Vertex") # return Shape.get_shape_list(self, "Vertex")
def wire(self) -> Wire | None: # def wire(self) -> Wire | None:
"""Return the Wire""" # """Return the Wire"""
return Shape.get_single_shape(self, "Wire") # return Shape.get_single_shape(self, "Wire")
def wires(self) -> ShapeList[Wire]: # def wires(self) -> ShapeList[Wire]:
"""wires - all the wires in this Shape""" # """wires - all the wires in this Shape"""
return Shape.get_shape_list(self, "Wire") # return Shape.get_shape_list(self, "Wire")
class Edge(Mixin1D[TopoDS_Edge]): class Edge(Mixin1D[TopoDS_Edge]):
@ -3561,6 +3561,21 @@ class Wire(Mixin1D[TopoDS_Wire]):
return return_value 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: def fillet_2d(self, radius: float, vertices: Iterable[Vertex]) -> Wire:
"""fillet_2d """fillet_2d

View file

@ -101,7 +101,7 @@ from OCP.BRepGProp import BRepGProp, BRepGProp_Face
from OCP.BRepIntCurveSurface import BRepIntCurveSurface_Inter from OCP.BRepIntCurveSurface import BRepIntCurveSurface_Inter
from OCP.BRepMesh import BRepMesh_IncrementalMesh from OCP.BRepMesh import BRepMesh_IncrementalMesh
from OCP.BRepPrimAPI import BRepPrimAPI_MakeHalfSpace 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.gce import gce_MakeLin
from OCP.Geom import Geom_Line from OCP.Geom import Geom_Line
from OCP.GeomAPI import GeomAPI_ProjectPointOnSurf from OCP.GeomAPI import GeomAPI_ProjectPointOnSurf
@ -839,7 +839,9 @@ class Shape(NodeMixin, Generic[TOPODS]):
with a warning if count != 1.""" with a warning if count != 1."""
shape_list = Shape.get_shape_list(shape, entity_type) shape_list = Shape.get_shape_list(shape, entity_type)
entity_count = len(shape_list) entity_count = len(shape_list)
if entity_count != 1: if entity_count == 0:
return None
elif entity_count > 1:
warnings.warn( warnings.warn(
f"Found {entity_count} {entity_type.lower()}s, returning first", f"Found {entity_count} {entity_type.lower()}s, returning first",
stacklevel=3, stacklevel=3,
@ -1185,13 +1187,14 @@ class Shape(NodeMixin, Generic[TOPODS]):
def edge(self) -> Edge | None: def edge(self) -> Edge | None:
"""Return the Edge""" """Return the Edge"""
return None return Shape.get_single_shape(self, "Edge")
# Note all sub-classes have vertices and vertex methods
def edges(self) -> ShapeList[Edge]: def edges(self) -> ShapeList[Edge]:
"""edges - all the edges in this Shape - subclasses may override""" """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]: def entities(self, topo_type: Shapes) -> list[TopoDS_Shape]:
"""Return all of the TopoDS sub entities of the given type""" """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: def face(self) -> Face | None:
"""Return the Face""" """Return the Face"""
return None return Shape.get_single_shape(self, "Face")
def faces(self) -> ShapeList[Face]: def faces(self) -> ShapeList[Face]:
"""faces - all the faces in this Shape""" """faces - all the faces in this Shape"""
return ShapeList() return Shape.get_shape_list(self, "Face")
def faces_intersected_by_axis( def faces_intersected_by_axis(
self, self,
@ -1724,11 +1727,11 @@ class Shape(NodeMixin, Generic[TOPODS]):
def shell(self) -> Shell | None: def shell(self) -> Shell | None:
"""Return the Shell""" """Return the Shell"""
return None return Shape.get_single_shape(self, "Shell")
def shells(self) -> ShapeList[Shell]: def shells(self) -> ShapeList[Shell]:
"""shells - all the shells in this Shape""" """shells - all the shells in this Shape"""
return ShapeList() return Shape.get_shape_list(self, "Shell")
def show_topology( def show_topology(
self, self,
@ -1782,11 +1785,11 @@ class Shape(NodeMixin, Generic[TOPODS]):
def solid(self) -> Solid | None: def solid(self) -> Solid | None:
"""Return the Solid""" """Return the Solid"""
return None return Shape.get_single_shape(self, "Solid")
def solids(self) -> ShapeList[Solid]: def solids(self) -> ShapeList[Solid]:
"""solids - all the solids in this Shape""" """solids - all the solids in this Shape"""
return ShapeList() return Shape.get_shape_list(self, "Solid")
@overload @overload
def split( def split(
@ -2235,11 +2238,11 @@ class Shape(NodeMixin, Generic[TOPODS]):
def wire(self) -> Wire | None: def wire(self) -> Wire | None:
"""Return the Wire""" """Return the Wire"""
return None return Shape.get_single_shape(self, "Wire")
def wires(self) -> ShapeList[Wire]: def wires(self) -> ShapeList[Wire]:
"""wires - all the wires in this Shape""" """wires - all the wires in this Shape"""
return ShapeList() return Shape.get_shape_list(self, "Wire")
def _apply_transform(self, transformation: gp_Trsf) -> Self: def _apply_transform(self, transformation: gp_Trsf) -> Self:
"""Private Apply Transform """Private Apply Transform
@ -2395,6 +2398,14 @@ class Shape(NodeMixin, Generic[TOPODS]):
return shape_to_html(self)._repr_html_() 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): class Comparable(ABC):
"""Abstract base class that requires comparison methods""" """Abstract base class that requires comparison methods"""

View file

@ -144,16 +144,6 @@ class Mixin3D(Shape[TOPODS]):
project_to_viewport = Mixin1D.project_to_viewport project_to_viewport = Mixin1D.project_to_viewport
find_intersection_points = Mixin2D.find_intersection_points 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 ---- # ---- Properties ----
@property @property
@ -725,13 +715,13 @@ class Mixin3D(Shape[TOPODS]):
return offset_solid return offset_solid
def solid(self) -> Solid | None: # def solid(self) -> Solid | None:
"""Return the Solid""" # """Return the Solid"""
return Shape.get_single_shape(self, "Solid") # return Shape.get_single_shape(self, "Solid")
def solids(self) -> ShapeList[Solid]: # def solids(self) -> ShapeList[Solid]:
"""solids - all the solids in this Shape""" # """solids - all the solids in this Shape"""
return Shape.get_shape_list(self, "Solid") # return Shape.get_shape_list(self, "Solid")
class Solid(Mixin3D[TopoDS_Solid]): class Solid(Mixin3D[TopoDS_Solid]):

View file

@ -174,11 +174,6 @@ class Mixin2D(ABC, Shape[TOPODS]):
project_to_viewport = Mixin1D.project_to_viewport project_to_viewport = Mixin1D.project_to_viewport
vertices = Mixin1D.vertices
vertex = Mixin1D.vertex
edges = Mixin1D.edges
edge = Mixin1D.edge
wires = Mixin1D.wires
# ---- Properties ---- # ---- Properties ----
@property @property
@ -226,13 +221,13 @@ class Mixin2D(ABC, Shape[TOPODS]):
return new_surface return new_surface
def face(self) -> Face | None: # def face(self) -> Face | None:
"""Return the Face""" # """Return the Face"""
return Shape.get_single_shape(self, "Face") # return Shape.get_single_shape(self, "Face")
def faces(self) -> ShapeList[Face]: # def faces(self) -> ShapeList[Face]:
"""faces - all the faces in this Shape""" # """faces - all the faces in this Shape"""
return Shape.get_shape_list(self, "Face") # return Shape.get_shape_list(self, "Face")
def find_intersection_points( def find_intersection_points(
self, other: Axis, tolerance: float = TOLERANCE 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 a copy of self moved along the normal by amount"""
return copy.deepcopy(self).moved(Location(self.normal_at() * amount)) return copy.deepcopy(self).moved(Location(self.normal_at() * amount))
def shell(self) -> Shell | None: # def shell(self) -> Shell | None:
"""Return the Shell""" # """Return the Shell"""
return Shape.get_single_shape(self, "Shell") # return Shape.get_single_shape(self, "Shell")
def shells(self) -> ShapeList[Shell]: # def shells(self) -> ShapeList[Shell]:
"""shells - all the shells in this Shape""" # """shells - all the shells in this Shape"""
return Shape.get_shape_list(self, "Shell") # return Shape.get_shape_list(self, "Shell")
def _wrap_edge( def _wrap_edge(
self, self,