Removed target from fillet/chamfer, fixed fillet/chamfer bug with base objects

This commit is contained in:
Roger Maitland 2023-03-26 09:16:00 -04:00
parent 82898b4a39
commit e74e496b43
4 changed files with 58 additions and 21 deletions

1
.gitignore vendored
View file

@ -19,6 +19,7 @@ docs/_build/
*.step
*.STEP
*.stl
*.svg
#mypy cache
.mypy_cache

View file

@ -44,6 +44,8 @@ from build123d.geometry import (
RotationLike,
Vector,
)
from build123d.objects_part import BasePartObject
from build123d.objects_sketch import BaseSketchObject
from build123d.topology import (
Compound,
Curve,
@ -220,7 +222,6 @@ def chamfer(
*objects: Union[Edge, Vertex],
length: float,
length2: float = None,
target: Union[Face, Sketch, Solid, Part] = None,
) -> Union[Sketch, Part]:
"""Generic Operation: chamfer
@ -232,13 +233,11 @@ def chamfer(
objects (Union[Edge,Vertex]): sequence of edges or vertices to chamfer
length (float): chamfer size
length2 (float, optional): asymmetric chamfer size. Defaults to None.
target (Union[Face, Sketch, Solid, Part], optional): object to chamfer. Defaults to None.
Raises:
ValueError: no objects provided
ValueError: objects must be Edges
ValueError: objects must be Vertices
ValueError: missing target object
"""
context: Builder = Builder._get_context("chamfer")
validate_inputs(context, "chamfer", objects)
@ -248,12 +247,18 @@ def chamfer(
):
raise ValueError("No objects provided")
if target is None:
if context is None:
raise ValueError("A target object must be provided")
objects = list(objects)
if context is not None:
target = context._obj
else:
target = objects[0].topo_parent
if target is None:
raise ValueError("Nothing to chamfer")
if target._dim == 3:
# Convert BasePartObject into Part so casting into Part during construction works
target = Part(target.wrapped) if isinstance(target, BasePartObject) else target
if not all([isinstance(obj, Edge) for obj in objects]):
raise ValueError("3D chamfer operation takes only Edges")
new_part = target.chamfer(length, length2, list(objects))
@ -263,6 +268,11 @@ def chamfer(
return Part(Compound.make_compound([new_part]).wrapped)
elif target._dim == 2:
# Convert BaseSketchObject into Sketch so casting into Sketch during construction works
target = (
Sketch(target.wrapped) if isinstance(target, BaseSketchObject) else target
)
if not all([isinstance(obj, Vertex) for obj in objects]):
raise ValueError("2D chamfer operation takes only Vertices")
new_faces = []
@ -282,7 +292,6 @@ def chamfer(
def fillet(
*objects: Union[Edge, Vertex],
radius: float,
target: Union[Face, Sketch, Solid, Part] = None,
) -> Union[Sketch, Part]:
"""Generic Operation: fillet
@ -295,9 +304,10 @@ def fillet(
radius (float): fillet size - must be less than 1/2 local width
Raises:
ValueError: no objects provided
ValueError: objects must be Edges
ValueError: objects must be Vertices
ValueError: missing target object
ValueError: nothing to fillet
"""
context: Builder = Builder._get_context("fillet")
validate_inputs(context, "fillet", objects)
@ -307,12 +317,18 @@ def fillet(
):
raise ValueError("No objects provided")
if target is None:
if context is None:
raise ValueError("A target object must be provided")
objects = list(objects)
if context is not None:
target = context._obj
else:
target = objects[0].topo_parent
if target is None:
raise ValueError("Nothing to fillet")
if target._dim == 3:
# Convert BasePartObject in Part so casting into Part during construction works
target = Part(target.wrapped) if isinstance(target, BasePartObject) else target
if not all([isinstance(obj, Edge) for obj in objects]):
raise ValueError("3D fillet operation takes only Edges")
new_part = target.fillet(radius, list(objects))
@ -322,6 +338,11 @@ def fillet(
return Part(Compound.make_compound([new_part]).wrapped)
elif target._dim == 2:
# Convert BaseSketchObject into Sketch so casting into Sketch during construction works
target = (
Sketch(target.wrapped) if isinstance(target, BaseSketchObject) else target
)
if not all([isinstance(obj, Vertex) for obj in objects]):
raise ValueError("2D fillet operation takes only Vertices")
new_faces = []

View file

@ -723,7 +723,7 @@ class Mixin1D:
class Mixin3D:
"""Additional methods to add to 3D Shape classes"""
def fillet(self, radius: float, edge_list: Iterable[Edge]):
def fillet(self, radius: float, edge_list: Iterable[Edge]) -> Self:
"""Fillet
Fillets the specified edges of this solid.
@ -814,7 +814,7 @@ class Mixin3D:
def chamfer(
self, length: float, length2: Optional[float], edge_list: Iterable[Edge]
):
) -> Self:
"""Chamfer
Chamfers the specified edges of this solid.
@ -1130,6 +1130,9 @@ class Shape(NodeMixin):
if isinstance(self, Face):
self.created_on: Plane = None
# Extracted objects like Vertices and Edges may need to know where they came from
self.topo_parent: Shape = None
@property
def location(self) -> Location:
"""Get this Shape's Location"""
@ -1871,17 +1874,25 @@ class Shape(NodeMixin):
def vertices(self) -> ShapeList[Vertex]:
"""vertices - all the vertices in this Shape"""
return ShapeList([Vertex(downcast(i)) for i in self._entities(Vertex.__name__)])
vertex_list = ShapeList(
[Vertex(downcast(i)) for i in self._entities(Vertex.__name__)]
)
for vertex in vertex_list:
vertex.topo_parent = self
return vertex_list
def edges(self) -> ShapeList[Edge]:
"""edges - all the edges in this Shape"""
return ShapeList(
edge_list = ShapeList(
[
Edge(i)
for i in self._entities(Edge.__name__)
if not BRep_Tool.Degenerated_s(TopoDS.Edge_s(i))
]
)
for edge in edge_list:
edge.topo_parent = self
return edge_list
def compounds(self) -> ShapeList[Compound]:
"""compounds - all the compounds in this Shape"""
@ -1898,7 +1909,10 @@ class Shape(NodeMixin):
def faces(self) -> ShapeList[Face]:
"""faces - all the faces in this Shape"""
return ShapeList([Face(i) for i in self._entities(Face.__name__)])
face_list = ShapeList([Face(i) for i in self._entities(Face.__name__)])
for face in face_list:
face.topo_parent = self
return face_list
def shells(self) -> ShapeList[Shell]:
"""shells - all the shells in this Shape"""

View file

@ -630,28 +630,29 @@ class LocationTests(unittest.TestCase):
class OperationsTests(unittest.TestCase):
def test_fillet_3d(self):
b = Box(1, 2, 3)
c = fillet(*b.edges(), radius=0.2, target=b)
c = fillet(*b.edges(), radius=0.2)
self.assertAlmostEqual(b.volume, 6.0, 6)
self.assertAlmostEqual(c.volume, 5.804696, 4)
def test_fillet_2d(self):
r = Rectangle(1, 2)
c = fillet(*r.vertices(), radius=0.2, target=r)
c = fillet(*r.vertices(), radius=0.2)
self.assertAlmostEqual(r.area, 2.0, 6)
self.assertAlmostEqual(c.area, 1.965663, 4)
def test_chamfer_3d(self):
b = Box(1, 2, 3)
c = chamfer(*b.edges(), length=0.2, target=b)
c = chamfer(*b.edges(), length=0.2)
self.assertAlmostEqual(b.volume, 6.0, 6)
self.assertAlmostEqual(c.volume, 5.804696, 4)
# self.assertAlmostEqual(c.volume, 5.804696, 4)
self.assertAlmostEqual(c.volume, 5.56266, 4)
def test_chamfer_2d(self):
r = Rectangle(1, 2)
c = chamfer(*r.vertices(), length=0.2, target=r)
c = chamfer(*r.vertices(), length=0.2)
self.assertAlmostEqual(r.area, 2.0, 6)
self.assertAlmostEqual(c.area, 1.92, 4)