def draw_elliptic_arc_entity(self, entity: DXFGraphic, properties: Properties) -> None: dxftype = entity.dxftype() if NULLVEC.isclose(entity.dxf.extrusion): self.skip_entity( f'Invalid extrusion (0, 0, 0) in entity: {str(entity)}') return if dxftype == 'CIRCLE': if entity.dxf.radius <= 0: self.skip_entity(f'Invalid radius in entity: {str(entity)}') return path = Path.from_circle(cast('Circle', entity)) elif dxftype == 'ARC': if entity.dxf.radius <= 0: self.skip_entity(f'Invalid radius in entity: {str(entity)}') return path = Path.from_arc(cast('Arc', entity)) elif dxftype == 'ELLIPSE': if NULLVEC.isclose(entity.dxf.major_axis): self.skip_entity( f'Invalid major axis (0, 0, 0) in entity: {str(entity)}') return path = Path.from_ellipse(cast('Ellipse', entity)) else: # API usage error raise TypeError(dxftype) self.out.draw_path(path, properties)
def audit(self, auditor: 'Auditor') -> None: """ Validity check. """ def reset_mline_style(name='Standard'): auditor.fixed_error( code=AuditError.RESET_MLINE_STYLE, message=f'Reset MLINESTYLE to "{name}" in {str(self)}.', dxf_entity=self, ) self.dxf.style_name = name style = doc.mline_styles.get(name) self.dxf.style_handle = style.dxf.handle super().audit(auditor) doc = auditor.doc if doc is None: return # Audit associated MLINESTYLE name and handle: style = doc.entitydb.get(self.dxf.style_handle) if style is None: # handle is invalid, get style by name style = doc.mline_styles.get(self.dxf.style_name, None) if style is None: reset_mline_style() else: # fix MLINESTYLE handle: auditor.fixed_error( code=AuditError.INVALID_MLINESTYLE_HANDLE, message=f'Fixed invalid style handle in {str(self)}.', dxf_entity=self, ) self.dxf.style_handle = style.dxf.handle else: # update MLINESTYLE name silently self.dxf.style_name = style.dxf.name # Get current (maybe fixed) MLINESTYLE: style = self.style # Update style element count silently: element_count = len(style.elements) self.dxf.style_element_count = element_count # Audit vertices: for vertex in self.vertices: if NULLVEC.isclose(vertex.line_direction): break if NULLVEC.isclose(vertex.miter_direction): break if len(vertex.line_params) != element_count: break # Ignore fill parameters. else: # no break return # Invalid vertices found: auditor.fixed_error( code=AuditError.INVALID_MLINE_VERTEX, message=f'Execute geometry update for {str(self)}.', dxf_entity=self, ) self.update_geometry()
def load_spline_data(self, tags) -> Iterable: """Load and set spline data (fit points, control points, weights, knots) and remove invalid start- and end tangents. Yields the remaining unprocessed tags. """ control_points = [] fit_points = [] knots = [] weights = [] for tag in tags: code, value = tag if code == 10: control_points.append(value) elif code == 11: fit_points.append(value) elif code == 40: knots.append(value) elif code == 41: weights.append(value) elif code in (12, 13) and NULLVEC.isclose(value): # Tangent values equal to (0, 0, 0) are invalid and ignored at # the loading stage! pass else: yield tag self.control_points = control_points self.fit_points = fit_points self.knots = knots self.weights = weights
def from_arc(cls, center: 'Vertex' = NULLVEC, radius: float = 1, extrusion: 'Vertex' = Z_AXIS, start_angle: float = 0, end_angle: float = 360, ccw: bool = True) -> 'ConstructionEllipse': """ Returns :class:`ConstructionEllipse` from arc or circle. Arc and Circle parameters defined in OCS. Args: center: center in OCS radius: arc or circle radius extrusion: OCS extrusion vector start_angle: start angle in degrees end_angle: end angle in degrees ccw: arc curve goes counter clockwise from start to end if ``True`` """ radius = abs(radius) if NULLVEC.isclose(extrusion): raise ValueError(f'Invalid extrusion: {str(extrusion)}') ratio = 1.0 ocs = OCS(extrusion) center = ocs.to_wcs(center) # Major axis along the OCS x-axis. major_axis = ocs.to_wcs(Vec3(radius, 0, 0)) # No further adjustment of start- and end angle required. start_param = math.radians(start_angle) end_param = math.radians(end_angle) return cls(center, major_axis, extrusion, ratio, start_param, end_param, bool(ccw))
def check_extrusion_vector(self, entity: 'DXFEntity') -> None: if NULLVEC.isclose(entity.dxf.extrusion): entity.dxf.discard('extrusion') self.fixed_error( code=AuditError.INVALID_EXTRUSION_VECTOR, message=f'Fixed extrusion vector for entity: {str(self)}.', dxf_entity=entity, )
def remove_invalid_data(spline_tags: 'Tags') -> None: # The loading function use the regular validator/fixer # infrastructure and I don't want to ignore (0, 0, 0) as tangent values # silently, so I remove invalid start- and end tangent values at the # DXF loadings stage: codes = [ code for code, value in spline_tags if code in (12, 13) and NULLVEC.isclose(value) ] if codes: spline_tags.remove_tags(codes=codes)
def audit(self, auditor: 'Auditor') -> None: """ Validity check. """ super().audit(auditor) if self.dxf.hasattr('major_axis') and NULLVEC.isclose( self.dxf.major_axis): auditor.fixed_error( code=AuditError.INVALID_MAJOR_AXIS, message=f'Deleted entity {str(self)} with invalid major axis.', dxf_entity=self, ) if self.doc and self.doc.entitydb: self.entitydb.trash(self.dxf.handle) else: self.destroy()
def is_not_null_vector(v) -> bool: return not NULLVEC.isclose(v)
def from_hatch_edge_path(edges: 'EdgePath', ocs: OCS = None, elevation: float = 0) -> 'Path': """ Returns a :class:`Path` object from a :class:`~ezdxf.entities.Hatch` edge path. """ def add_line_edge(edge): start = wcs(edge.start) end = wcs(edge.end) if len(path): if path.end.isclose(start): # path-end -> line-end path.line_to(end) elif path.end.isclose(end): # path-end (==line-end) -> line-start path.line_to(start) else: # path-end -> edge-start -> edge-end path.line_to(start) path.line_to(end) else: # start path path.start = start path.line_to(end) def add_arc_edge(edge): x, y, *_ = edge.center # from_arc() requires OCS data: ellipse = ConstructionEllipse.from_arc( center=(x, y, elevation), radius=edge.radius, extrusion=extrusion, start_angle=edge.start_angle, end_angle=edge.end_angle, ) tools.add_ellipse(path, ellipse, reset=not bool(path)) def add_ellipse_edge(edge): ocs_ellipse = edge.construction_tool() # ConstructionEllipse has WCS representation: ellipse = ConstructionEllipse( center=wcs(ocs_ellipse.center.replace(z=elevation)), major_axis=wcs(ocs_ellipse.major_axis), ratio=ocs_ellipse.ratio, extrusion=extrusion, start_param=ocs_ellipse.start_param, end_param=ocs_ellipse.end_param, ) tools.add_ellipse(path, ellipse, reset=not bool(path)) def add_spline_edge(edge): control_points = [wcs(p) for p in edge.control_points] if len(control_points) == 0: fit_points = [wcs(p) for p in edge.fit_points] if len(fit_points): bspline = from_fit_points(edge, fit_points) else: # No control points and no fit points: # DXF structure error return else: bspline = from_control_points(edge, control_points) tools.add_spline(path, bspline, reset=not bool(path)) def from_fit_points(edge, fit_points): tangents = None if edge.start_tangent and edge.end_tangent: tangents = (wcs(edge.start_tangent), wcs(edge.end_tangent)) return global_bspline_interpolation( fit_points, degree=edge.degree, tangents=tangents, ) def from_control_points(edge, control_points): return BSpline(control_points=control_points, order=edge.degree + 1, knots=edge.knot_values, weights=edge.weights if edge.weights else None) def wcs(vertex): if ocs and ocs.transform: return ocs.to_wcs((vertex.x, vertex.y, elevation)) else: return Vec3(vertex) extrusion = ocs.uz if ocs else Z_AXIS path = Path() for edge in edges: if edge.EDGE_TYPE == "LineEdge": add_line_edge(edge) elif edge.EDGE_TYPE == "ArcEdge": if not math.isclose(edge.radius, 0): add_arc_edge(edge) elif edge.EDGE_TYPE == "EllipseEdge": if not NULLVEC.isclose(edge.major_axis): add_ellipse_edge(edge) elif edge.EDGE_TYPE == "SplineEdge": add_spline_edge(edge) return path