def _virtual_polyline_entities(points, elevation: float, extrusion: Vec3, dxfattribs: dict, doc) -> Iterable[Union["Line", "Arc"]]: ocs = OCS(extrusion) if extrusion else OCS() prev_point = None prev_bulge = None for x, y, bulge in points: point = Vec3(x, y, elevation) if prev_point is None: prev_point = point prev_bulge = bulge continue attribs = dict(dxfattribs) if prev_bulge != 0: center, start_angle, end_angle, radius = bulge_to_arc( prev_point, point, prev_bulge) if radius > 0: attribs["center"] = Vec3(center.x, center.y, elevation) attribs["radius"] = radius attribs["start_angle"] = math.degrees(start_angle) attribs["end_angle"] = math.degrees(end_angle) if extrusion: attribs["extrusion"] = extrusion yield factory.new(dxftype="ARC", dxfattribs=attribs, doc=doc) else: attribs["start"] = ocs.to_wcs(prev_point) attribs["end"] = ocs.to_wcs(point) yield factory.new(dxftype="LINE", dxfattribs=attribs, doc=doc) prev_point = point prev_bulge = bulge
def _virtual_polyline_entities(points, elevation: float, extrusion: Vector, dxfattribs: dict, doc) -> Iterable[Union['Line', 'Arc']]: ocs = OCS(extrusion) if extrusion else OCS() prev_point = None prev_bulge = None for x, y, bulge in points: point = Vector(x, y, elevation) if prev_point is None: prev_point = point prev_bulge = bulge continue attribs = dict(dxfattribs) if prev_bulge != 0: center, start_angle, end_angle, radius = bulge_to_arc( prev_point, point, prev_bulge) attribs['center'] = Vector(center.x, center.y, elevation) attribs['radius'] = radius attribs['start_angle'] = math.degrees(start_angle) attribs['end_angle'] = math.degrees(end_angle) if extrusion: attribs['extrusion'] = extrusion yield factory.new(dxftype='ARC', dxfattribs=attribs, doc=doc) else: attribs['start'] = ocs.to_wcs(prev_point) attribs['end'] = ocs.to_wcs(point) yield factory.new(dxftype='LINE', dxfattribs=attribs, doc=doc) prev_point = point prev_bulge = bulge
def test_bulge_to_arc(): center, start_angle, end_angle, radius = bulge_to_arc(start_point=(0, 0), end_point=(1, 0), bulge=-1) assert center.isclose((.5, 0., 0.)) assert math.isclose(start_angle, 0, abs_tol=1e-15) assert math.isclose(end_angle, math.pi) assert radius == .5
def bulge_to(p1: Vec3, p2: Vec3, bulge: float): if p1.isclose(p2): return center, start_angle, end_angle, radius = bulge_to_arc(p1, p2, bulge) ellipse = ConstructionEllipse.from_arc( center, radius, Z_AXIS, math.degrees(start_angle), math.degrees(end_angle), ) curves = list(cubic_bezier_from_ellipse(ellipse)) if curves[0].control_points[0].isclose(p2): curves = _reverse_bezier_curves(curves) self.add_curves(curves)
def bulge_to(p1: Vec3, p2: Vec3, bulge: float): if p1.isclose(p2, rel_tol=IS_CLOSE_TOL, abs_tol=0): return center, start_angle, end_angle, radius = bulge_to_arc(p1, p2, bulge) ellipse = ConstructionEllipse.from_arc( center, radius, Z_AXIS, math.degrees(start_angle), math.degrees(end_angle), ) curves = list(cubic_bezier_from_ellipse(ellipse)) curve0 = curves[0] cp0 = curve0.control_points[0] if cp0.isclose(p2, rel_tol=IS_CLOSE_TOL, abs_tol=0): curves = reverse_bezier_curves(curves) add_bezier4p(path, curves)
def _edges(points) -> Iterable[Union[LineEdge, ArcEdge]]: prev_point = None prev_bulge = None for x, y, bulge in points: point = Vec3(x, y) if prev_point is None: prev_point = point prev_bulge = bulge continue if prev_bulge != 0: arc = ArcEdge() # bulge_to_arc returns always counter-clockwise oriented # start- and end angles: ( arc.center, start_angle, end_angle, arc.radius, ) = bulge_to_arc(prev_point, point, prev_bulge) chk_point = arc.center + Vec2.from_angle( start_angle, arc.radius ) arc.ccw = chk_point.isclose(prev_point, abs_tol=1e-9) arc.start_angle = math.degrees(start_angle) % 360.0 arc.end_angle = math.degrees(end_angle) % 360.0 if math.isclose( arc.start_angle, arc.end_angle ) and math.isclose(arc.start_angle, 0): arc.end_angle = 360.0 yield arc else: line = LineEdge() line.start = (prev_point.x, prev_point.y) line.end = (point.x, point.y) yield line prev_point = point prev_bulge = bulge
def from_polyline(cls, polyline: 'DXFGraphic', segments: int = 64) -> 'TraceBuilder': """ Create a complete trace from a LWPOLYLINE or a 2D POLYLINE entity, the trace consist of multiple sub-traces if :term:`bulge` values are present. Args: polyline: :class:`~ezdxf.entities.LWPolyline` or 2D :class:`~ezdxf.entities.Polyline` segments: count of segments for bulge approximation, given count is for a full circle, partial arcs have proportional less segments, but at least 3 """ dxftype = polyline.dxftype() if dxftype == 'LWPOLYLINE': polyline = cast('LWPOLYLINE', polyline) const_width = polyline.dxf.const_width points = [] for x, y, start_width, end_width, bulge in polyline.lwpoints: location = Vec2(x, y) if const_width: # This is AutoCAD behavior, BricsCAD uses const width # only for missing width values. start_width = const_width end_width = const_width points.append((location, start_width, end_width, bulge)) closed = polyline.closed elif dxftype == 'POLYLINE': polyline = cast('POLYLINE', polyline) if not polyline.is_2d_polyline: raise TypeError('2D POLYLINE required') closed = polyline.is_closed default_start_width = polyline.dxf.default_start_width default_end_width = polyline.dxf.default_end_width points = [] for vertex in polyline.vertices: location = Vec2(vertex.dxf.location) if vertex.dxf.hasattr('start_width'): start_width = vertex.dxf.start_width else: start_width = default_start_width if vertex.dxf.hasattr('end_width'): end_width = vertex.dxf.end_width else: end_width = default_end_width bulge = vertex.dxf.bulge points.append((location, start_width, end_width, bulge)) else: raise TypeError(f'Invalid DXF type {dxftype}') if closed and not points[0][0].isclose(points[-1][0]): # close polyline explicit points.append(points[0]) trace = cls() store_bulge = None store_start_width = None store_end_width = None store_point = None linear_trace = LinearTrace() for point, start_width, end_width, bulge in points: if store_bulge: center, start_angle, end_angle, radius = bulge_to_arc( store_point, point, store_bulge) if radius > 0: arc = ConstructionArc( center, radius, math.degrees(start_angle), math.degrees(end_angle), is_counter_clockwise=True, ) if arc.start_point.isclose(point): sw = store_end_width ew = store_start_width else: ew = store_end_width sw = store_start_width trace.append(CurvedTrace.from_arc(arc, sw, ew, segments)) store_bulge = None if bulge != 0: # arc from prev_point to point if linear_trace.is_started: linear_trace.add_station(point, start_width, end_width) trace.append(linear_trace) linear_trace = LinearTrace() store_bulge = bulge store_start_width = start_width store_end_width = end_width store_point = point continue linear_trace.add_station(point, start_width, end_width) if linear_trace.is_started: trace.append(linear_trace) if closed and len(trace) > 1: # This is required for traces with multiple paths to create the correct # miter at the closing point. (only linear to linear trace). trace.close() return trace