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"""
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,

View file

@ -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

View file

@ -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"""

View file

@ -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]):

View file

@ -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,