diff --git a/docs/assets/tech_drawing.svg b/docs/assets/tech_drawing.svg
index 7279403..5038000 100644
--- a/docs/assets/tech_drawing.svg
+++ b/docs/assets/tech_drawing.svg
@@ -52,12 +52,12 @@
-
+
-
+
@@ -68,8 +68,8 @@
-
+
@@ -85,8 +85,8 @@
-
+
@@ -103,8 +103,8 @@
-
+
@@ -121,8 +121,8 @@
-
+
@@ -133,8 +133,8 @@
-
+
\ No newline at end of file
diff --git a/docs/objects_2d.py b/docs/objects_2d.py
index ac1a8b6..8901e0c 100644
--- a/docs/objects_2d.py
+++ b/docs/objects_2d.py
@@ -290,10 +290,13 @@ svg.add_shape(tech_drawing.sketch)
svg.write("assets/tech_drawing.svg")
# [ArrowHead]
-arrow_head = ArrowHead(10)
-s = 100 / max(*arrow_head.bounding_box().size)
+arrow_head_types = [HeadType.CURVED, HeadType.STRAIGHT, HeadType.FILLETED]
+arrow_heads = [ArrowHead(50, a_type) for a_type in arrow_head_types]
+s = 100 / max(*arrow_heads[0].bounding_box().size)
svg = ExportSVG(scale=s)
-svg.add_shape(arrow_head)
+for i, arrow_head in enumerate(arrow_heads):
+ svg.add_shape(arrow_head.moved(Location((0, -i * 40))))
+ svg.add_shape(Text(arrow_head_types[i].name, 5).moved(Location((-25, -i * 40))))
svg.write("assets/arrow_head.svg")
# [Arrow]
diff --git a/src/build123d/build_common.py b/src/build123d/build_common.py
index 9764135..dfe0853 100644
--- a/src/build123d/build_common.py
+++ b/src/build123d/build_common.py
@@ -103,7 +103,7 @@ LB = 453.59237 * G
operations_apply_to = {
"add": ["BuildPart", "BuildSketch", "BuildLine"],
"bounding_box": ["BuildPart", "BuildSketch", "BuildLine"],
- "chamfer": ["BuildPart", "BuildSketch"],
+ "chamfer": ["BuildPart", "BuildSketch", "BuildLine"],
"extrude": ["BuildPart"],
"fillet": ["BuildPart", "BuildSketch", "BuildLine"],
"loft": ["BuildPart"],
diff --git a/src/build123d/drafting.py b/src/build123d/drafting.py
index 2a141ee..4eff61d 100644
--- a/src/build123d/drafting.py
+++ b/src/build123d/drafting.py
@@ -135,7 +135,7 @@ class Arrow(BaseSketchObject):
shaft_path = shaft_path.trim(0.0, 1.0 - trim_amount)
# Create a perpendicular line to sweep the tail path
- shaft_pen = shaft_path.perpendicular_line(shaft_width)
+ shaft_pen = shaft_path.perpendicular_line(shaft_width, 0)
shaft = sweep(shaft_pen, shaft_path, mode=Mode.PRIVATE)
arrow = arrow_head.fuse(shaft).clean()
@@ -284,6 +284,8 @@ class Draft:
processed_path = Wire.make_polygon(pnts, close=False)
else:
raise ValueError("Unsupported patch descriptor")
+ # processed_path = Plane.XY.to_local_coords(processed_path)
+
return processed_path
def _label_to_str(
@@ -547,7 +549,7 @@ class ExtensionLine(BaseSketchObject):
# Build the extension line sketch
e_lines = []
for extension_line in extension_lines:
- line_pen = extension_line.perpendicular_line(draft.line_width)
+ line_pen = extension_line.perpendicular_line(draft.line_width, 0)
e_line_shape = sweep(line_pen, extension_line, mode=Mode.PRIVATE)
e_lines.append(e_line_shape)
d_line = DimensionLine(
diff --git a/src/build123d/operations_generic.py b/src/build123d/operations_generic.py
index 3a66d93..f5b599b 100644
--- a/src/build123d/operations_generic.py
+++ b/src/build123d/operations_generic.py
@@ -274,6 +274,7 @@ def chamfer(
ValueError: objects must be Vertices
"""
context: Builder = Builder._get_context("chamfer")
+ length2 = length if length2 is None else length2
if (objects is None and context is None) or (
objects is None and context is not None and context._obj is None
@@ -309,14 +310,13 @@ def chamfer(
target = (
Sketch(target.wrapped) if isinstance(target, BaseSketchObject) else target
)
-
if not all([isinstance(obj, Vertex) for obj in object_list]):
raise ValueError("2D chamfer operation takes only Vertices")
new_faces = []
for face in target.faces():
vertices_in_face = [v for v in face.vertices() if v in object_list]
if vertices_in_face:
- new_faces.append(face.chamfer_2d(length, vertices_in_face))
+ new_faces.append(face.chamfer_2d(length, length2, vertices_in_face))
else:
new_faces.append(face)
new_sketch = Sketch(Compound.make_compound(new_faces).wrapped)
@@ -325,6 +325,28 @@ def chamfer(
context._add_to_context(new_sketch, mode=Mode.REPLACE)
return new_sketch
+ elif target._dim == 1:
+ target = (
+ Wire(target.wrapped)
+ if isinstance(target, BaseLineObject)
+ else target.wires()[0]
+ )
+ if not all([isinstance(obj, Vertex) for obj in object_list]):
+ raise ValueError("1D fillet operation takes only Vertices")
+ # Remove any end vertices as these can't be filleted
+ if not target.is_closed():
+ object_list = filter(
+ lambda v: not (
+ (Vector(*v.to_tuple()) - target.position_at(0)).length == 0
+ or (Vector(*v.to_tuple()) - target.position_at(1)).length == 0
+ ),
+ object_list,
+ )
+ new_wire = target.chamfer_2d(length, length2, object_list)
+ if context is not None:
+ context._add_to_context(new_wire, mode=Mode.REPLACE)
+ return new_wire
+
def fillet(
objects: Union[ChamferFilletType, Iterable[ChamferFilletType]],
diff --git a/src/build123d/operations_sketch.py b/src/build123d/operations_sketch.py
index 750057c..6f466bc 100644
--- a/src/build123d/operations_sketch.py
+++ b/src/build123d/operations_sketch.py
@@ -140,7 +140,7 @@ def trace(
new_faces = []
for edge in trace_edges:
- trace_pen = edge.perpendicular_line(line_width)
+ trace_pen = edge.perpendicular_line(line_width, 0)
new_faces.extend(Face.sweep(trace_pen, edge).faces())
if context is not None:
context._add_to_context(*new_faces, mode=mode)
diff --git a/src/build123d/topology.py b/src/build123d/topology.py
index 70d2bc7..0ca4bef 100644
--- a/src/build123d/topology.py
+++ b/src/build123d/topology.py
@@ -835,20 +835,25 @@ class Mixin1D:
else:
return offset_wire
- def perpendicular_line(self, length: float, plane: Plane = Plane.XY) -> Edge:
+ def perpendicular_line(
+ self, length: float, u_value: float, plane: Plane = Plane.XY
+ ) -> Edge:
"""perpendicular_line
Create a line on the given plane perpendicular to and centered on beginning of self
Args:
length (float): line length
+ u_value (float): position along line between 0.0 and 1.0
plane (Plane, optional): plane containing perpendicular line. Defaults to Plane.XY.
Returns:
Edge: perpendicular line
"""
- start = self.position_at(0)
- local_plane = Plane(origin=start, x_dir=self.tangent_at(0), z_dir=plane.z_dir)
+ start = self.position_at(u_value)
+ local_plane = Plane(
+ origin=start, x_dir=self.tangent_at(u_value), z_dir=plane.z_dir
+ )
line = Edge.make_line(
start + local_plane.y_dir * length / 2,
start - local_plane.y_dir * length / 2,
@@ -5136,11 +5141,14 @@ class Face(Shape):
return self.__class__(fillet_builder.Shape())
- def chamfer_2d(self, distance: float, vertices: Iterable[Vertex]) -> Face:
+ def chamfer_2d(
+ self, distance: float, distance2: float, vertices: Iterable[Vertex]
+ ) -> Face:
"""Apply 2D chamfer to a face
Args:
distance: float:
+ distance2: float:
vertices: Iterable[Vertex]:
Returns:
@@ -5161,7 +5169,7 @@ class Face(Shape):
TopoDS.Edge_s(edge1.wrapped),
TopoDS.Edge_s(edge2.wrapped),
distance,
- distance,
+ distance2,
)
chamfer_builder.Build()
@@ -6629,19 +6637,26 @@ class Wire(Shape, Mixin1D):
"""
return Face.make_from_wires(self).fillet_2d(radius, vertices).outer_wire()
- def chamfer_2d(self, distance: float, vertices: Iterable[Vertex]) -> Wire:
+ def chamfer_2d(
+ self, distance: float, distance2: float, vertices: Iterable[Vertex]
+ ) -> Wire:
"""chamfer_2d
Apply 2D chamfer to a wire
Args:
distance (float): chamfer length
+ distance2 (float): chamfer length
vertices (Iterable[Vertex]): vertices to chamfer
Returns:
Wire: chamfered wire
"""
- return Face.make_from_wires(self).chamfer_2d(distance, vertices).outer_wire()
+ return (
+ Face.make_from_wires(self)
+ .chamfer_2d(distance, distance2, vertices)
+ .outer_wire()
+ )
@classmethod
def make_rect(
diff --git a/tests/test_direct_api.py b/tests/test_direct_api.py
index b3a65b1..3beb4f6 100644
--- a/tests/test_direct_api.py
+++ b/tests/test_direct_api.py
@@ -3039,7 +3039,7 @@ class TestWire(DirectApiTestCase):
def test_chamfer_2d(self):
square = Wire.make_rect(1, 1)
- squaroid = square.chamfer_2d(0.1, square.vertices())
+ squaroid = square.chamfer_2d(0.1, 0.1, square.vertices())
self.assertAlmostEqual(
squaroid.length, 4 * (1 - 2 * 0.1 + 0.1 * math.sqrt(2)), 5
)