Convert intersect to use _bool_op and split Wire after intersect

This commit is contained in:
Jonathan Wagenet 2025-11-14 13:26:17 -05:00
parent 3877fd5876
commit 68f6ef2125
2 changed files with 78 additions and 98 deletions

View file

@ -52,12 +52,11 @@ license:
from __future__ import annotations from __future__ import annotations
import copy import copy
import numpy as np
import warnings import warnings
from collections.abc import Iterable from collections.abc import Iterable, Sequence
from itertools import combinations from itertools import combinations
from math import atan2, ceil, copysign, cos, floor, inf, isclose, pi, radians from math import atan2, ceil, copysign, cos, floor, inf, isclose, pi, radians
from typing import TYPE_CHECKING, Literal, TypeAlias, overload from typing import TYPE_CHECKING, Literal, overload
from typing import cast as tcast from typing import cast as tcast
import numpy as np import numpy as np
@ -729,122 +728,103 @@ class Mixin1D(Shape):
def to_vertex(objs: Iterable) -> ShapeList: def to_vertex(objs: Iterable) -> ShapeList:
return ShapeList([Vertex(v) if isinstance(v, Vector) else v for v in objs]) return ShapeList([Vertex(v) if isinstance(v, Vector) else v for v in objs])
common_set: ShapeList[Vertex | Edge] = ShapeList(self.edges()) def bool_op(
target: ShapeList | Shape | Plane args: Sequence,
tools: Sequence,
operation: BRepAlgoAPI_Section | BRepAlgoAPI_Common,
) -> ShapeList:
# Wrap Shape._bool_op for corrected output
intersections: Shape | ShapeList = Shape()._bool_op(args, tools, operation)
if isinstance(intersections, ShapeList):
return intersections or ShapeList()
if isinstance(intersections, Shape) and not intersections.is_null:
return ShapeList([intersections])
return ShapeList()
def filter_shapes_by_order(shapes: ShapeList, orders: list) -> ShapeList:
# Remove lower order shapes from list which *appear* to be part of
# a higher order shape using a lazy distance check
# (sufficient for vertices, may be an issue for higher orders)
order_groups = []
for order in orders:
order_groups.append(
ShapeList([s for s in shapes if isinstance(s, order)])
)
filtered_shapes = order_groups[-1]
for i in range(len(order_groups) - 1):
los = order_groups[i]
his: list = sum(order_groups[i + 1 :], [])
filtered_shapes.extend(
ShapeList(
lo
for lo in los
if all(lo.distance_to(hi) > TOLERANCE for hi in his)
)
)
return filtered_shapes
common_set: ShapeList[Vertex | Edge | Wire] = ShapeList([self])
target: Shape | Plane
for other in to_intersect: for other in to_intersect:
# Conform target type # Conform target type
# Vertices need to be Vector for set()
match other: match other:
case Axis(): case Axis():
target = ShapeList([Edge(other)]) # BRepAlgoAPI_Section seems happier if Edge isnt infinite
bbox = self.bounding_box()
dist = self.distance_to(other.position)
dist = dist if dist >= 1 else 1
target = Edge.make_line(
other.position - other.direction * bbox.diagonal * dist,
other.position + other.direction * bbox.diagonal * dist,
)
case Plane(): case Plane():
target = other target = other
case Vector(): case Vector():
target = Vertex(other) target = Vertex(other)
case Location(): case Location():
target = Vertex(other.position) target = Vertex(other.position)
case Edge():
target = ShapeList([other])
case Wire():
target = ShapeList(other.edges())
case _ if issubclass(type(other), Shape): case _ if issubclass(type(other), Shape):
target = other target = other
case _: case _:
raise ValueError(f"Unsupported type to_intersect: {type(other)}") raise ValueError(f"Unsupported type to_intersect: {type(other)}")
# Find common matches # Find common matches
common: list[Vector | Edge] = [] common: list[Vertex | Edge | Wire] = []
result: ShapeList | Shape | None result: ShapeList | None
for obj in common_set: for obj in common_set:
match (obj, target): match (obj, target):
case obj, Shape() as target: case (_, Plane()):
# Find Shape with Edge/Wire target = Shape(BRepBuilderAPI_MakeFace(other.wrapped).Face())
if isinstance(target, Vertex): operation = BRepAlgoAPI_Section()
result = Shape.intersect(obj, target) result = bool_op((obj,), (target,), operation)
else: operation = BRepAlgoAPI_Common()
result = target.intersect(obj) result.extend(bool_op((obj,), (target,), operation))
if result: case (_, Vertex() | Edge() | Wire()):
if not isinstance(result, list): operation = BRepAlgoAPI_Section()
result = ShapeList([result]) section = bool_op((obj,), (target,), operation)
common.extend(to_vector(result)) result = section
if not section:
operation = BRepAlgoAPI_Common()
result.extend(bool_op((obj,), (target,), operation))
case Vertex() as obj, target: case _ if issubclass(type(target), Shape):
if not isinstance(target, ShapeList): result = target.intersect(obj)
target = ShapeList([target])
for tar in target: if result:
if isinstance(tar, Edge): common.extend(result)
result = Shape.intersect(obj, tar)
else:
result = obj.intersect(tar)
if result:
if not isinstance(result, list):
result = ShapeList([result])
common.extend(to_vector(result))
case Edge() as obj, ShapeList() as targets:
# Find any edge / edge intersection points
for tar in targets:
# Find crossing points
try:
intersection_points = obj.find_intersection_points(tar)
common.extend(intersection_points)
except ValueError:
pass
# Find common end points
obj_end_points = set(Vector(v) for v in obj.vertices())
tar_end_points = set(Vector(v) for v in tar.vertices())
points = set.intersection(obj_end_points, tar_end_points)
common.extend(points)
# Find Edge/Edge overlaps
result = obj._bool_op(
(obj,), targets, BRepAlgoAPI_Common()
).edges()
common.extend(result if isinstance(result, list) else [result])
case Edge() as obj, Plane() as plane:
# Find any edge / plane intersection points & edges
# Find point intersections
if obj.wrapped is None:
continue
geom_line = BRep_Tool.Curve_s(
obj.wrapped, obj.param_at(0), obj.param_at(1)
)
geom_plane = Geom_Plane(plane.local_coord_system)
intersection_calculator = GeomAPI_IntCS(geom_line, geom_plane)
plane_intersection_points: list[Vector] = []
if intersection_calculator.IsDone():
plane_intersection_points = [
Vector(intersection_calculator.Point(i + 1))
for i in range(intersection_calculator.NbPoints())
]
common.extend(plane_intersection_points)
# Find edge intersections
if all(
plane.contains(v)
for v in obj.positions(i / 7 for i in range(8))
): # is a 2D edge
common.append(obj)
if common: if common:
common_set = to_vertex(set(common)) common_set = ShapeList()
# Remove Vertex intersections coincident to Edge intersections for shape in common:
vts = common_set.vertices() if isinstance(shape, Wire):
eds = common_set.edges() common_set.extend(shape.edges())
if vts and eds: else:
filtered_vts = ShapeList( common_set.append(shape)
[ common_set = to_vertex(set(to_vector(common_set)))
v common_set = filter_shapes_by_order(common_set, [Vertex, Edge])
for v in vts
if all(v.distance_to(e) > TOLERANCE for e in eds)
]
)
common_set = filtered_vts + eds
else: else:
return None return None

View file

@ -296,7 +296,7 @@ sl1 = Box(2, 2, 2).solid()
sl2 = Pos(Z=5) * Box(2, 2, 2).solid() sl2 = Pos(Z=5) * Box(2, 2, 2).solid()
sl3 = Cylinder(2, 1).solid() - Cylinder(1.5, 1).solid() sl3 = Cylinder(2, 1).solid() - Cylinder(1.5, 1).solid()
wi7 = Wire([l1 := sl3.faces().sort_by(Axis.Z)[-1].edge().trim(.3, .4), wi7 = Wire([l1 := sl3.faces().sort_by(Axis.Z)[-1].edges()[0].trim(.3, .4),
l2 := l1.trim(2, 3), l2 := l1.trim(2, 3),
RadiusArc(l1 @ 1, l2 @ 0, 1, short_sagitta=False) RadiusArc(l1 @ 1, l2 @ 0, 1, short_sagitta=False)
]) ])
@ -430,7 +430,7 @@ freecad_matrix = [
Case(vert, e1, [Vertex], "vertical, ellipse, tangent", None), Case(vert, e1, [Vertex], "vertical, ellipse, tangent", None),
Case(horz, e1, [Vertex], "horizontal, ellipse, tangent", None), Case(horz, e1, [Vertex], "horizontal, ellipse, tangent", None),
Case(c1, c2, [Vertex, Vertex], "circle, skew, intersect", "Should return 2 Vertices"), Case(c1, c2, [Vertex, Vertex], "circle, skew, intersect", None),
Case(c1, horz, [Vertex], "circle, horiz, tangent", None), Case(c1, horz, [Vertex], "circle, horiz, tangent", None),
Case(c2, horz, [Vertex], "circle, horiz, tangent", None), Case(c2, horz, [Vertex], "circle, horiz, tangent", None),
Case(c1, vert, [Vertex], "circle, vert, tangent", None), Case(c1, vert, [Vertex], "circle, vert, tangent", None),