def _convert_entity(self): e: 'LWPolyline' = cast('LWPolyline', self.entity) if e.has_width: # use a mesh representation: tb = TraceBuilder.from_polyline(e) mb = MeshVertexMerger() # merges coincident vertices for face in tb.faces(): mb.add_face(Vec3.generate(face)) self._mesh = MeshBuilder.from_builder(mb) else: # use a path representation to support bulges! self._path = make_path(e)
def filling() -> "Hatch": attribs = _dxfattribs(mline) attribs["color"] = style.dxf.fill_color attribs["elevation"] = Vec3(ocs.from_wcs(bottom_border[0])).replace( x=0, y=0) attribs["extrusion"] = mline.dxf.extrusion hatch = cast("Hatch", factory.new("HATCH", dxfattribs=attribs, doc=doc)) bulges: List[float] = [0.0] * (len(bottom_border) * 2) points = chain( Vec3.generate(ocs.points_from_wcs(bottom_border)), Vec3.generate(ocs.points_from_wcs(reversed(top_border))), ) if not closed: if style.get_flag_state(style.END_ROUND): bulges[len(bottom_border) - 1] = 1.0 if style.get_flag_state(style.START_ROUND): bulges[-1] = 1.0 lwpoints = ((v.x, v.y, bulge) for v, bulge in zip(points, bulges)) hatch.paths.add_polyline_path(lwpoints, is_closed=True) return hatch
def line(self, x1: float, y1: float, x2: float, y2: float, m: Matrix44 = None) -> None: points = [(x1, y1), (x2, y2)] if m is not None: p1, p2 = m.transform_vertices(points) else: p1, p2 = Vec3.generate(points) self.backend.draw_line(p1, p2, self.properties)
def test_to_splines_and_polylines(self, path): entities = list(to_splines_and_polylines([path])) assert len(entities) == 2 polyline = entities[0] spline = entities[1] assert polyline.dxftype() == "POLYLINE" assert spline.dxftype() == "SPLINE" assert polyline.vertices[0].dxf.location.isclose((0, 0)) assert polyline.vertices[1].dxf.location.isclose((4, 0)) assert close_vectors( Vec3.generate(spline.control_points), [(4, 0, 0), (3, 1, 1), (1, 1, 1), (0, 0, 0)], )
def scale( vertices: Iterable["Vertex"], scaling=(1.0, 1.0, 1.0) ) -> Iterable[Vec3]: """Scale `vertices` around the origin (0, 0), faster than a Matrix44 transformation. Args: vertices: iterable of vertices scaling: scale factors as tuple of floats for x-, y- and z-axis Returns: yields scaled vertices """ sx, sy, sz = scaling for v in Vec3.generate(vertices): yield Vec3(v.x * sx, v.y * sy, v.z * sz)
def test_to_edge_path_hatches(self, path): hatches = list(to_hatches(path, edge_path=True)) assert len(hatches) == 1 h0 = hatches[0] assert h0.dxftype() == "HATCH" assert len(h0.paths) == 1 edge_path = h0.paths[0] assert edge_path.type == BoundaryPathType.EDGE line, spline = edge_path.edges assert line.type == EdgeType.LINE assert line.start == (0, 0) assert line.end == (4, 0) assert spline.type == EdgeType.SPLINE assert close_vectors( Vec3.generate(spline.control_points), [(4, 0), (3, 1), (1, 1), (0, 0)], )
def transform(self, m: Matrix44) -> 'Bezier4P': """ General transformation interface, returns a new :class:`Bezier4p` curve and it is always a 3D curve. Args: m: 4x4 transformation matrix (:class:`ezdxf.math.Matrix44`) .. versionadded:: 0.14 """ if len(self._control_points[0]) == 2: defpoints = Vec3.generate(self._control_points) else: defpoints = self._control_points defpoints = tuple(m.transform_vertices(defpoints)) return Bezier4P(defpoints)
def test_to_edge_path_hatches(self, path): hatches = list(to_hatches(path, edge_path=True)) assert len(hatches) == 1 h0 = hatches[0] assert h0.dxftype() == 'HATCH' assert len(h0.paths) == 1 edge_path = h0.paths[0] assert edge_path.PATH_TYPE == 'EdgePath' line, spline = edge_path.edges assert line.EDGE_TYPE == 'LineEdge' assert line.start == (0, 0) assert line.end == (4, 0) assert spline.EDGE_TYPE == 'SplineEdge' assert close_vectors(Vec3.generate(spline.control_points), [(4, 0), (3, 1), (1, 1), (0, 0)])
def add_vertices(self, vertices: Iterable['Vertex']) -> Sequence[int]: """ Add new vertices to the mesh, each vertex is a ``(x, y, z)`` tuple or a :class:`~ezdxf.math.Vec3` object, returns the indices of the `vertices` added to the :attr:`vertices` list. e.g. adding 4 vertices to an empty mesh, returns the indices ``(0, 1, 2, 3)``, adding additional 4 vertices returns the indices ``(4, 5, 6, 7)``. Args: vertices: list of vertices, vertex as ``(x, y, z)`` tuple or :class:`~ezdxf.math.Vec3` objects Returns: tuple: indices of the `vertices` added to the :attr:`vertices` list """ start_index = len(self.vertices) self.vertices.extend(Vec3.generate(vertices)) return tuple(range(start_index, len(self.vertices)))
def corner_vertices( left: float, bottom: float, right: float, top: float, m: Matrix44 = None, ) -> Iterable[Vec3]: corners = [ # closed polygon: fist vertex == last vertex (left, top), (right, top), (right, bottom), (left, bottom), (left, top), ] if m is None: return Vec3.generate(corners) else: return m.transform_vertices(corners)
def draw_polyline_entity(self, entity: DXFGraphic, properties: Properties) -> None: dxftype = entity.dxftype() if dxftype == 'POLYLINE': e = cast(Polyface, entity) if e.is_polygon_mesh or e.is_poly_face_mesh: # draw 3D mesh or poly-face entity self.draw_mesh_builder_entity( MeshBuilder.from_polyface(e), properties, ) return entity = cast(Union[LWPolyline, Polyline], entity) is_lwpolyline = dxftype == 'LWPOLYLINE' if entity.has_width: # draw banded 2D polyline elevation = 0.0 ocs = entity.ocs() transform = ocs.transform if transform: if is_lwpolyline: # stored as float elevation = entity.dxf.elevation else: # stored as vector (0, 0, elevation) elevation = Vec3(entity.dxf.elevation).z trace = TraceBuilder.from_polyline( entity, segments=self.circle_approximation_count // 2 ) for polygon in trace.polygons(): # polygon is a sequence of Vec2() if transform: points = ocs.points_to_wcs( Vec3(v.x, v.y, elevation) for v in polygon ) else: points = Vec3.generate(polygon) # Set default SOLID filling for LWPOLYLINE properties.filling = Filling() self.out.draw_filled_polygon(points, properties) return path = Path.from_lwpolyline(entity) \ if is_lwpolyline else Path.from_polyline(entity) self.out.draw_path(path, properties)
def params_from_vertices(self, vertices: Iterable['Vertex']) -> Iterable[float]: """ Yields ellipse params for all given `vertices`. The vertex don't has to be exact on the ellipse curve or in the range from start- to end param or even in the ellipse plane. Param is calculated from the intersection point of the ray projected on the ellipse plane from the center of the ellipse through the vertex. .. warning:: An input for start- and end vertex at param 0 and 2*pi return unpredictable results because of floating point inaccuracy, sometimes 0 and sometimes 2*pi. """ x_axis = self.major_axis.normalize() y_axis = self.minor_axis.normalize() ratio = self.ratio center = self.center for v in Vec3.generate(vertices): v -= center yield math.atan2(y_axis.dot(v) / ratio, x_axis.dot(v)) % math.tau
def fit_points(self, points: Iterable["Vertex"]) -> None: self._fit_points: Vertices = cast( Vertices, VertexArray(chain.from_iterable(Vec3.generate(points))), )
def test_distance_point_line_3d(points, expected): p, a, b = Vec3.generate(points) assert distance_point_line_3d(p, a, b) == pytest.approx(expected)
def fit_points(self, points: Iterable['Vertex']) -> None: self._fit_points = VertexArray( chain.from_iterable(Vec3.generate(points)))