Skip to content

Commit 5542816

Browse files
2D fillet chamfer (#683)
* Implement 2D chamfers and fillets * Implement 2D chamfer/fillet tests Co-authored-by: Marcus Boyd <[email protected]>
1 parent f0308ce commit 5542816

File tree

2 files changed

+125
-1
lines changed

2 files changed

+125
-1
lines changed

cadquery/occ_impl/shapes.py

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
List,
88
Sequence,
99
Iterator,
10+
Dict,
1011
Any,
1112
overload,
1213
TypeVar,
@@ -136,7 +137,11 @@
136137
BRepOffsetAPI_MakeOffset,
137138
)
138139

139-
from OCP.BRepFilletAPI import BRepFilletAPI_MakeChamfer, BRepFilletAPI_MakeFillet
140+
from OCP.BRepFilletAPI import (
141+
BRepFilletAPI_MakeChamfer,
142+
BRepFilletAPI_MakeFillet,
143+
BRepFilletAPI_MakeFillet2d,
144+
)
140145

141146
from OCP.TopTools import TopTools_IndexedDataMapOfShapeListOfShape, TopTools_ListOfShape
142147

@@ -680,6 +685,28 @@ def _entities(self, topo_type: Shapes) -> List[TopoDS_Shape]:
680685

681686
return list(out.values())
682687

688+
def _entitiesFrom(
689+
self, child_type: Shapes, parent_type: Shapes
690+
) -> Dict["Shape", List["Shape"]]:
691+
692+
res = TopTools_IndexedDataMapOfShapeListOfShape()
693+
694+
TopTools_IndexedDataMapOfShapeListOfShape()
695+
TopExp.MapShapesAndAncestors_s(
696+
self.wrapped,
697+
inverse_shape_LUT[child_type],
698+
inverse_shape_LUT[parent_type],
699+
res,
700+
)
701+
702+
out: Dict[Shape, List[Shape]] = {}
703+
for i in range(1, res.Extent() + 1):
704+
out[Shape.cast(res.FindKey(i))] = [
705+
Shape.cast(el) for el in res.FindFromIndex(i)
706+
]
707+
708+
return out
709+
683710
def Vertices(self) -> List["Vertex"]:
684711
"""
685712
:returns: All the vertices in this Shape
@@ -904,6 +931,9 @@ def moved(self, loc: Location) -> "Shape":
904931
def __hash__(self) -> int:
905932
return self.hashCode()
906933

934+
def __eq__(self, other) -> bool:
935+
return self.isSame(other)
936+
907937
def _bool_op(
908938
self,
909939
args: Iterable["Shape"],
@@ -1778,6 +1808,24 @@ def offset2D(
17781808

17791809
return rv
17801810

1811+
def fillet2D(self, radius: float, vertices: Iterable[Vertex]) -> "Wire":
1812+
"""
1813+
Apply 2D fillet to a wire
1814+
"""
1815+
1816+
f = Face.makeFromWires(self)
1817+
1818+
return f.fillet2D(radius, vertices).outerWire()
1819+
1820+
def chamfer2D(self, d: float, vertices: Iterable[Vertex]) -> "Wire":
1821+
"""
1822+
Apply 2D chamfer to a wire
1823+
"""
1824+
1825+
f = Face.makeFromWires(self)
1826+
1827+
return f.chamfer2D(d, vertices).outerWire()
1828+
17811829

17821830
class Face(Shape):
17831831
"""
@@ -1974,6 +2022,43 @@ def makeFromWires(
19742022

19752023
return cls(face).fix()
19762024

2025+
def fillet2D(self, radius: float, vertices: Iterable[Vertex]) -> "Face":
2026+
"""
2027+
Apply 2D fillet to a face
2028+
"""
2029+
2030+
fillet_builder = BRepFilletAPI_MakeFillet2d(self.wrapped)
2031+
2032+
for v in vertices:
2033+
fillet_builder.AddFillet(v.wrapped, radius)
2034+
2035+
fillet_builder.Build()
2036+
2037+
return self.__class__(fillet_builder.Shape())
2038+
2039+
def chamfer2D(self, d: float, vertices: Iterable[Vertex]) -> "Face":
2040+
"""
2041+
Apply 2D chamfer to a face
2042+
"""
2043+
2044+
chamfer_builder = BRepFilletAPI_MakeFillet2d(self.wrapped)
2045+
edge_map = self._entitiesFrom("Vertex", "Edge")
2046+
2047+
for v in vertices:
2048+
edges = edge_map[v]
2049+
if len(edges) < 2:
2050+
raise ValueError("Cannot chamfer at this location")
2051+
2052+
e1, e2 = edges
2053+
2054+
chamfer_builder.AddChamfer(
2055+
TopoDS.Edge_s(e1.wrapped), TopoDS.Edge_s(e2.wrapped), d, d
2056+
)
2057+
2058+
chamfer_builder.Build()
2059+
2060+
return self.__class__(chamfer_builder.Shape()).fix()
2061+
19772062

19782063
class Shell(Shape):
19792064
"""

tests/test_cadquery.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4327,3 +4327,42 @@ def testCompSolid(self):
43274327

43284328
self.assertEqual(len(result.CompSolids()), 1)
43294329
self.assertEqual(len(result.Solids()), 4)
4330+
4331+
def test2Dfillet(self):
4332+
4333+
r = Workplane().rect(1, 2).wires().val()
4334+
f = Face.makeFromWires(r)
4335+
verts = r.Vertices()
4336+
4337+
self.assertEqual(len(f.fillet2D(0.5, verts).Vertices()), 6)
4338+
self.assertEqual(len(r.fillet2D(0.5, verts).Vertices()), 6)
4339+
self.assertEqual(len(r.fillet2D(0.25, verts).Vertices()), 8)
4340+
4341+
# Test fillet2D with open wire and single vertex
4342+
w0 = Workplane().hLine(1).vLine(1).wire()
4343+
w0_verts = w0.vertices(">X and <Y").vals()
4344+
unfilleted_wire0 = w0.val()
4345+
filleted_wire0 = unfilleted_wire0.fillet2D(0.5, w0_verts)
4346+
4347+
self.assertEqual(len(filleted_wire0.Vertices()), 4)
4348+
4349+
# the filleted wire is shorter than the original
4350+
self.assertGreater(unfilleted_wire0.Length() - filleted_wire0.Length(), 0.1)
4351+
4352+
def test2Dchamfer(self):
4353+
4354+
r = Workplane().rect(1, 2).wires().val()
4355+
f = Face.makeFromWires(r)
4356+
verts = r.Vertices()
4357+
4358+
self.assertEqual(len(f.chamfer2D(0.5, verts).Vertices()), 6)
4359+
self.assertEqual(len(r.chamfer2D(0.5, verts).Vertices()), 6)
4360+
self.assertEqual(len(r.chamfer2D(0.25, verts).Vertices()), 8)
4361+
4362+
r = Workplane().hLine(1).vLine(1).wire().val()
4363+
vs = r.Vertices()
4364+
4365+
self.assertEqual(len(r.chamfer2D(0.25, [vs[1]]).Vertices()), 4)
4366+
4367+
with raises(ValueError):
4368+
r.chamfer2D(0.25, [vs[0]])

0 commit comments

Comments
 (0)