build123d icon indicating copy to clipboard operation
build123d copied to clipboard

ExtensionLine / DimensionLine complain that start > end

Open jdegenstein opened this issue 1 year ago • 4 comments

This code is adapted from docs/objects_2d.py:

std = Draft()

# [ExtensionLine]
with BuildSketch() as e_line:
    with BuildLine():
        l1 = Polyline((20, 40), (-40, 40), (-40, -40), (20, -40))
        RadiusArc(l1 @ 0, l1 @ 1, 50)
    make_face()
    edg = e_line.edges().sort_by(Axis.X)[0]
    ExtensionLine(border=edg, offset=10, draft=std)
    outside_curve = e_line.edges().sort_by(Axis.X)[-1]
    ExtensionLine(border=outside_curve, offset=10, label_angle=True, draft=std)

# [TechnicalDrawing]
with BuildSketch() as tech_drawing:
    with Locations((0, 20)):
        add(e_line)
    TechnicalDrawing()

Returns:

ValueError                                Traceback (most recent call last)
     24     ExtensionLine(border=edg, offset=10, draft=std)
     25     outside_curve = e_line.edges().sort_by(Axis.X)[-1]
---> 26     ExtensionLine(border=outside_curve, offset=10, label_angle=True, draft=std)
     28 # [TechnicalDrawing]
     29 with BuildSketch() as tech_drawing:

File c:\Users\someuser\.conda\envs\ocp_vscode\Lib\site-packages\build123d\drafting.py:562, in ExtensionLine.__init__(self, border, offset, draft, sketch, label, arrows, tolerance, label_angle, project_line, mode)
    560     e_line_shape = sweep(line_pen, extension_line, mode=Mode.PRIVATE)
    561     e_lines.append(e_line_shape)
--> 562 d_line = DimensionLine(
    563     dimension_path,
    564     draft,
    565     sketch,
    566     label,
    567     arrows,
    568     tolerance,
    569     label_angle,
    570     mode=Mode.PRIVATE,
    571 )
    572 self.dimension = d_line.dimension  #: length of the dimension
    574 e_line_sketch = Sketch(children=e_lines + d_line.faces())

File c:\Users\someuser\.conda\envs\ocp_vscode\Lib\site-packages\build123d\drafting.py:410, in DimensionLine.__init__(self, path, draft, sketch, label, arrows, tolerance, label_angle, mode)
    406 if label_length + arrows.count(True) * draft.arrow_length < path_length:
    407     shaft_length = (path_length - label_length) / 2 - draft.pad_around_text
    408     shaft_pair = [
    409         path_obj.trim(0.0, shaft_length / path_length),
--> 410         path_obj.trim(1.0 - shaft_length / path_length, 1.0),
    411     ]
    412 else:
    413     shaft_length = 2 * draft.arrow_length

File c:\Users\someuser\.conda\envs\ocp_vscode\Lib\site-packages\build123d\topology.py:7785, in Wire.trim(self, start, end)
   7783     u_start = edge.param_at_point(trim_start_point)
   7784     u_end = edge.param_at_point(trim_end_point)
-> 7785     edge = edge.trim(u_start, u_end)
   7786 elif contains_start:
   7787     u_value = edge.param_at_point(trim_start_point)

File c:\Users\someuser\.conda\envs\ocp_vscode\Lib\site-packages\build123d\topology.py:4609, in Edge.trim(self, start, end)
   4594 """trim
   4595 
   4596 Create a new edge by keeping only the section between start and end.
   (...)
   4606     Edge: trimmed edge
   4607 """
   4608 if start >= end:
-> 4609     raise ValueError(f"start ({start}) must be less than end ({end})")
   4611 new_curve = BRep_Tool.Curve_s(
   4612     copy.deepcopy(self).wrapped, self.param_at(0), self.param_at(1)
   4613 )
   4614 parm_start = self.param_at(start)

ValueError: start (0.8165271836783728) must be less than end (0.0)

I have also observed some non-repeatable sporadic behavior in which this fails on the first call to ExtensionLine (in the same way as above). Perhaps the line is reversed at some point?

jdegenstein avatar May 08 '24 14:05 jdegenstein

Running the exact same code, I get a different error. MacOS and build123d 0.7.0:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[61], line 12
     10     #ExtensionLine(border=edg, offset=10, draft=std)
     11     outside_curve = e_line.edges().sort_by(Axis.X)[-1]
---> 12     ExtensionLine(border=outside_curve, offset=10, label_angle=True, draft=std)
     14 # [TechnicalDrawing]
     15 with BuildSketch() as tech_drawing:

File ~/miniconda3/envs/default/lib/python3.12/site-packages/build123d/drafting.py:562, in ExtensionLine.__init__(self, border, offset, draft, sketch, label, arrows, tolerance, label_angle, project_line, mode)
    560     e_line_shape = sweep(line_pen, extension_line, mode=Mode.PRIVATE)
    561     e_lines.append(e_line_shape)
--> 562 d_line = DimensionLine(
    563     dimension_path,
    564     draft,
    565     sketch,
    566     label,
    567     arrows,
    568     tolerance,
    569     label_angle,
    570     mode=Mode.PRIVATE,
    571 )
    572 self.dimension = d_line.dimension  #: length of the dimension
    574 e_line_sketch = Sketch(children=e_lines + d_line.faces())

File ~/miniconda3/envs/default/lib/python3.12/site-packages/build123d/drafting.py:409, in DimensionLine.__init__(self, path, draft, sketch, label, arrows, tolerance, label_angle, mode)
    406 if label_length + arrows.count(True) * draft.arrow_length < path_length:
    407     shaft_length = (path_length - label_length) / 2 - draft.pad_around_text
    408     shaft_pair = [
--> 409         path_obj.trim(0.0, shaft_length / path_length),
    410         path_obj.trim(1.0 - shaft_length / path_length, 1.0),
    411     ]
    412 else:
    413     shaft_length = 2 * draft.arrow_length

File ~/miniconda3/envs/default/lib/python3.12/site-packages/build123d/topology.py:8020, in Wire.trim(self, start, end)
   8017         if v_edge != 1:
   8018             new_edges.append(e.trim(v_edge, 1))
-> 8020 return Wire(new_edges)

File ~/miniconda3/envs/default/lib/python3.12/site-packages/build123d/topology.py:7819, in Wire.__init__(self, *args, **kwargs)
   7817     obj = wire.wrapped
   7818 elif edges:
-> 7819     obj = Wire._make_wire(edges, False if sequenced is None else sequenced)
   7821 super().__init__(
   7822     obj=obj,
   7823     label="" if label is None else label,
   7824     color=color,
   7825     parent=parent,
   7826 )

File ~/miniconda3/envs/default/lib/python3.12/site-packages/build123d/topology.py:8115, in Wire._make_wire(cls, edges, sequenced)
   8113         raise RuntimeError("Wire is empty")
   8114     elif wire_builder.Error() == BRepBuilderAPI_DisconnectedWire:
-> 8115         raise ValueError("Edges are disconnected")
   8117 return wire_builder.Wire()

ValueError: Edges are disconnected

Changing the problematic code from ExtensionLine to DimensionLine works, but doesn’t generate the desired output, of course:

DimensionLine(path=outside_curve, label_angle=True, draft=std)

tshead avatar Sep 24 '24 06:09 tshead

@tshead I can confirm that the error message has changed. I am seeing the same error message as you but I am running a slightly newer development version 0.7.1.dev4+g043629e. There have been some changes to edges/wire trim since this issue was created so I am not surprised the error has changed a bit.

jdegenstein avatar Sep 24 '24 15:09 jdegenstein

Alright, doing some more investigation, an internal object after being offset is producing a wire with three edges from a wire with one edge. The trim function is having trouble processing the three edge wire, after looking I have determined that running Wire.clean() will reduce the numbers of edges back down to one at which point ExtensionLine will run just fine. I am not sure this is the best general solution and I suspect that the issue may actually lie in the offset_2d method.

jdegenstein avatar Sep 24 '24 18:09 jdegenstein

@jdegenstein I appreciate you looking into this, but I’m not following you on how to use Wire.clean() in this case - could you expound on that a bit?

Many thanks, Tim

tshead avatar Sep 25 '24 02:09 tshead