Beispiel #1
0
 def circular_arc(self, data: bytes):
     bs = ByteStream(data)
     attribs = self._build_dxf_attribs()
     attribs['center'] = Vec3(bs.read_vertex())
     attribs['radius'] = bs.read_float()
     normal = Vec3(bs.read_vertex())
     if normal != (0, 0, 1):
         logger.debug('ProxyGraphic: unsupported 3D ARC.')
     start_vec = Vec3(bs.read_vertex())
     sweep_angle = bs.read_float()
     arc_type = bs.read_struct('L')[0]
     # just do 2D for now
     start_angle = start_vec.angle_deg
     end_angle = start_angle + math.degrees(sweep_angle)
     attribs['start_angle'] = start_angle
     attribs['end_angle'] = end_angle
     return self._factory('ARC', dxfattribs=attribs)
Beispiel #2
0
def rytz_axis_construction(d1: Vec3, d2: Vec3) -> Tuple[Vec3, Vec3, float]:
    """The Rytz’s axis construction is a basic method of descriptive Geometry
    to find the axes, the semi-major axis and semi-minor axis, starting from two
    conjugated half-diameters.

    Source: `Wikipedia <https://en.m.wikipedia.org/wiki/Rytz%27s_construction>`_

    Given conjugated diameter `d1` is the vector from center C to point P and
    the given conjugated diameter `d2` is the vector from center C to point Q.
    Center of ellipse is always ``(0, 0, 0)``. This algorithm works for
    2D/3D vectors.

    Args:
        d1: conjugated semi-major axis as :class:`Vec3`
        d2: conjugated semi-minor axis as :class:`Vec3`

    Returns:
         Tuple of (major axis, minor axis, ratio)

    """
    Q = Vec3(d1)  # vector CQ
    # calculate vector CP', location P'
    if math.isclose(d1.z, 0, abs_tol=1e-9) and math.isclose(
            d2.z, 0, abs_tol=1e-9):
        # Vec3.orthogonal() works only for vectors in the xy-plane!
        P1 = Vec3(d2).orthogonal(ccw=False)
    else:
        extrusion = d1.cross(d2)
        P1 = extrusion.cross(d2).normalize(d2.magnitude)

    D = P1.lerp(Q)  # vector CD, location D, midpoint of P'Q
    radius = D.magnitude
    radius_vector = (Q - P1).normalize(radius)  # direction vector P'Q
    A = D - radius_vector  # vector CA, location A
    B = D + radius_vector  # vector CB, location B
    if A.isclose(NULLVEC) or B.isclose(NULLVEC):
        raise ArithmeticError("Conjugated axis required, invalid source data.")
    major_axis_length = (A - Q).magnitude
    minor_axis_length = (B - Q).magnitude
    if math.isclose(major_axis_length, 0.0) or math.isclose(
            minor_axis_length, 0.0):
        raise ArithmeticError("Conjugated axis required, invalid source data.")
    ratio = minor_axis_length / major_axis_length
    major_axis = B.normalize(major_axis_length)
    minor_axis = A.normalize(minor_axis_length)
    return major_axis, minor_axis, ratio
Beispiel #3
0
def test_fmt_mapping():
    from ezdxf.math import Vec3
    d = {'a': 1, 'b': 'str', 'c': Vec3(), 'd': 'xxx "yyy" \'zzz\''}
    r = list(_fmt_mapping(d))
    assert r[0] == "'a': 1,"
    assert r[1] == "'b': \"str\","
    assert r[2] == "'c': (0.0, 0.0, 0.0),"
    assert r[3] == "'d': \"xxx \\\"yyy\\\" 'zzz'\","
Beispiel #4
0
def test_arc_params_issue_708(arc_params):
    cpts = list(arc_params(-2.498091544796509, -0.6435011087932844))
    assert cpts[0] == (Vec3(-0.8, -0.6, 0.0),
                       Vec3(-0.6111456180001683, -0.8518058426664423,
                            0.0), Vec3(-0.3147573033330529, -1.0, 0.0),
                       Vec3(6.123233995736766e-17, -1.0, 0.0))
    assert cpts[1] == (Vec3(6.123233995736766e-17, -1.0,
                            0.0), Vec3(0.314757303333053, -1.0, 0.0),
                       Vec3(0.6111456180001683, -0.8518058426664423,
                            0.0), Vec3(0.8, -0.5999999999999999, 0.0))
Beispiel #5
0
def test_arbitrary_ucs():
    origin = Vec3(3, 3, 3)
    ux = Vec3(1, 2, 0)
    def_point_in_xy_plane = Vec3(3, 10, 4)
    uz = ux.cross(def_point_in_xy_plane - origin)
    ucs = UCS(origin=origin, ux=ux, uz=uz)
    m = Matrix44.ucs(ucs.ux, ucs.uy, ucs.uz, ucs.origin)
    def_point_in_ucs = ucs.from_wcs(def_point_in_xy_plane)

    assert ucs.ux == m.ux
    assert ucs.uy == m.uy
    assert ucs.uz == m.uz
    assert ucs.origin == m.origin

    assert def_point_in_ucs == m.ucs_vertex_from_wcs(def_point_in_xy_plane)
    assert def_point_in_ucs.z == 0
    assert ucs.to_wcs(def_point_in_ucs).isclose(def_point_in_xy_plane)
    assert ucs.is_cartesian is True
Beispiel #6
0
def test_matrix44_to_wcs():
    ocs = OCS(EXTRUSION)
    matrix = Matrix44.ucs(ocs.ux, ocs.uy, ocs.uz)
    assert is_close_points(
        matrix.ocs_to_wcs(
            Vec3(9.41378764657076, 13.15481838975576, 0.8689258932616031)),
        (-9.56460754, 8.44764172, 9.97894327),
        places=6,
    )
Beispiel #7
0
def rotate(vertices: Iterable['Vertex'],
           angle: 0.,
           deg: bool = True) -> Iterable[Vec3]:
    """
    Rotate `vertices` about to z-axis at to origin (0, 0), faster than a Matrix44 transformation.

    Args:
        vertices: iterable of vertices
        angle: rotation angle
        deg: True if angle in degrees, False if angle in radians

    Returns: yields transformed vertices

    """
    if deg:
        return (Vec3(v).rotate_deg(angle) for v in vertices)
    else:
        return (Vec3(v).rotate(angle) for v in vertices)
Beispiel #8
0
def test_fmt_mapping():
    from ezdxf.math import Vec3

    d = {"a": 1, "b": "str", "c": Vec3(), "d": "xxx \"yyy\" 'zzz'"}
    r = list(_fmt_mapping(d))
    assert r[0] == "'a': 1,"
    assert r[1] == "'b': \"str\","
    assert r[2] == "'c': (0.0, 0.0, 0.0),"
    assert r[3] == "'d': \"xxx \\\"yyy\\\" 'zzz'\","
Beispiel #9
0
def _update_location_from_mtext(text: Text, mtext: MText) -> None:
    # TEXT is an OCS entity, MTEXT is a WCS entity
    dxf = text.dxf
    insert = Vec3(mtext.dxf.insert)
    extrusion = Vec3(mtext.dxf.extrusion)
    text_direction = mtext.get_text_direction()
    if extrusion.isclose(Z_AXIS):  # most common case
        dxf.rotation = text_direction.angle_deg
    else:
        ocs = OCS(extrusion)
        insert = ocs.from_wcs(insert)
        dxf.extrusion = extrusion.normalize()
        dxf.rotation = ocs.from_wcs(text_direction).angle_deg  # type: ignore

    dxf.insert = insert
    dxf.align_point = insert  # the same point for all MTEXT alignments!
    dxf.halign, dxf.valign = MAP_MTEXT_ALIGN_TO_FLAGS.get(
        mtext.dxf.attachment_point, (TextHAlign.LEFT, TextVAlign.TOP))
def add_dim(msp, x, y, override, dimstyle='EZ_RADIUS'):
    center = Vec3(x, y)
    msp.add_circle(center, radius=RADIUS)
    dim = msp.add_radius_dim(center=center,
                             radius=RADIUS,
                             angle=45,
                             dimstyle=dimstyle,
                             override=override)
    dim.render()
Beispiel #11
0
def export_dxf(layout: "GenericLayoutType",
               bins: List[Bin],
               offset: Vertex = (1, 0, 0)) -> None:
    from ezdxf import colors

    offset_vec = Vec3(offset)
    start = Vec3()
    index = 0
    rgb = (colors.RED, colors.GREEN, colors.BLUE, colors.MAGENTA, colors.CYAN)
    for box in bins:
        m = Matrix44.translate(start.x, start.y, start.z)
        _add_frame(layout, box, "FRAME", m)
        for item in box.items:
            _add_mesh(layout, item, "ITEMS", rgb[index], m)
            index += 1
            if index >= len(rgb):
                index = 0
        start += offset_vec
Beispiel #12
0
    def translate(self, dx: float, dy: float, dz: float) -> 'XLine':
        """ Optimized XLINE/RAY translation about `dx` in x-axis, `dy` in
        y-axis and `dz` in z-axis, returns `self` (floating interface).

        .. versionadded:: 0.13

        """
        self.dxf.start = Vec3(dx, dy, dz) + self.dxf.start
        return self
Beispiel #13
0
 def polygons_wcs(self, ocs: OCS,
                  elevation: float) -> Iterable[Sequence[Vec3]]:
     """Yields for each sub-trace a single polygon as sequence of
     :class:`~ezdxf.math.Vec3` objects in :ref:`WCS`.
     """
     for trace in self._traces:
         yield tuple(
             ocs.points_to_wcs(
                 Vec3(v.x, v.y, elevation) for v in trace.polygon()))
Beispiel #14
0
 def transform_vertices(self,
                        vectors: Iterable['Vertex']) -> Iterable[Vec3]:
     """ Returns an iterable of transformed vertices. """
     m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15 = self.matrix
     for vector in vectors:
         x, y, z = vector
         yield Vec3(x * m0 + y * m4 + z * m8 + m12,
                    x * m1 + y * m5 + z * m9 + m13,
                    x * m2 + y * m6 + z * m10 + m14)
Beispiel #15
0
    def translate(self, dx: float, dy: float, dz: float) -> 'Line':
        """ Optimized LINE translation about `dx` in x-axis, `dy` in y-axis and
        `dz` in z-axis.

        """
        vec = Vec3(dx, dy, dz)
        self.dxf.start = vec + self.dxf.start
        self.dxf.end = vec + self.dxf.end
        return self
Beispiel #16
0
    def translate(self, dx: float, dy: float, dz: float) -> 'Ellipse':
        """ Optimized ELLIPSE translation about `dx` in x-axis, `dy` in y-axis
        and `dz` in z-axis, returns `self` (floating interface).

        .. versionadded:: 0.13

        """
        self.dxf.center = Vec3(dx, dy, dz) + self.dxf.center
        return self
Beispiel #17
0
 def test_audit_invalid_major_axis(self, msp):
     ellipse = msp.add_ellipse((0, 0), (1, 0))
     # can only happen for loaded DXF files
     ellipse.dxf.__dict__["major_axis"] = Vec3(0, 0, 0)  # hack
     auditor = Auditor(ellipse.doc)
     ellipse.audit(auditor)
     auditor.empty_trashcan()
     assert len(auditor.fixes) == 1
     assert ellipse.is_alive is False, "invalid ellipse should be deleted"
Beispiel #18
0
    def translate(self, dx: float, dy: float, dz: float) -> 'Point':
        """ Optimized POINT translation about `dx` in x-axis, `dy` in y-axis and
        `dz` in z-axis.

        .. versionadded:: 0.13

        """
        self.dxf.location = Vec3(dx, dy, dz) + self.dxf.location
        return self
Beispiel #19
0
    def baseline_vertices(self, insert: Vec3, halign: int = 0, valign: int = 0,
                          angle: float = 0) -> List[Vec3]:
        """ Returns the left and the right baseline vertex of the text line.

        Args:
            insert: insertion point
            halign: horizontal alignment left=0, center=1, right=2
            valign: vertical alignment baseline=0, bottom=1, middle=2, top=3
            angle: text rotation in radians

        """
        fm = self.font_measurements()
        vertices = [
            Vec3(0, fm.baseline),
            Vec3(self.width, fm.baseline),
        ]
        shift = self._shift_vector(halign, valign, fm)
        return _transform(vertices, insert, shift, angle)
Beispiel #20
0
    def _create_linked_columns(self) -> None:
        """ Create linked MTEXT columns for DXF versions before R2018. """
        # creates virtual MTEXT entities
        dxf = self.dxf
        attribs = self.dxfattribs(drop={'handle', 'owner'})
        doc = self.doc
        cols = self._columns

        insert = dxf.get('insert', Vec3())
        default_direction = Vec3.from_deg_angle(dxf.get('rotation', 0))
        text_direction = Vec3(dxf.get('text_direction', default_direction))
        offset = text_direction.normalize(cols.width + cols.gutter_width)
        linked_columns = cols.linked_columns
        for _ in range(cols.count - 1):
            insert += offset
            column = MText.new(dxfattribs=attribs, doc=doc)
            column.dxf.insert = insert
            linked_columns.append(column)
Beispiel #21
0
 def translate(self, dx: float, dy: float, dz: float) -> "Point":
     """Optimized POINT translation about `dx` in x-axis, `dy` in y-axis and
     `dz` in z-axis.
     """
     self.dxf.location = Vec3(dx, dy, dz) + self.dxf.location
     # Avoid Matrix44 instantiation if not required:
     if self.is_post_transform_required:
         self.post_transform(Matrix44.translate(dx, dy, dz))
     return self
Beispiel #22
0
 def draw_text_entity(self, entity: DXFGraphic,
                      properties: Properties) -> None:
     # Draw embedded MTEXT entity as virtual MTEXT entity:
     if isinstance(entity, BaseAttrib) and entity.has_embedded_mtext_entity:
         self.draw_mtext_entity(entity.virtual_mtext_entity(), properties)
     elif is_spatial_text(Vec3(entity.dxf.extrusion)):
         self.draw_text_entity_3d(entity, properties)
     else:
         self.draw_text_entity_2d(entity, properties)
Beispiel #23
0
def closest_point(base: "Vertex", points: Iterable["Vertex"]) -> "Vec3":
    """Returns closest point to `base`.

    Args:
        base: base point as :class:`Vec3` compatible object
        points: iterable of points as :class:`Vec3` compatible object

    """
    base = Vec3(base)
    min_dist = None
    found = None
    for point in points:
        p = Vec3(point)
        dist = (base - p).magnitude
        if (min_dist is None) or (dist < min_dist):
            min_dist = dist
            found = p
    return found
Beispiel #24
0
 def add_dim(x, y, radius, dimtad):
     center = Vec3(x, y)
     msp.add_circle((x, y), radius=3)
     dim_location = center + Vec3.from_deg_angle(angle, radius)
     dim = msp.add_diameter_dim(center=(x, y), radius=3, location=dim_location, dimstyle='EZ_RADIUS',
                                override={
                                    'dimtad': dimtad,
                                })
     dim.render(discard=BRICSCAD)
Beispiel #25
0
def test_transform_mtext_with_linked_columns():
    mtext = new_mtext_with_linked_columns(3)
    offset = Vec3(1, 2, 3)
    mtext2 = mtext.copy()
    mtext2.translate(*offset.xyz)
    assert mtext2.dxf.insert.isclose(mtext.dxf.insert + offset)
    for col1, col2 in zip(mtext.columns.linked_columns,
                          mtext2.columns.linked_columns):
        assert col2.dxf.insert.isclose(col1.dxf.insert + offset)
Beispiel #26
0
    def user_location_override(self, location: 'Vertex') -> None:
        """ Set text location by user, `location` is relative to the origin of
        the UCS defined in the :meth:`render` method or WCS if the `ucs`
        argument is ``None``.

        """
        self.dimension.set_flag_state(self.dimension.USER_LOCATION_OVERRIDE,
                                      state=True, name='dimtype')
        self.dimstyle_attribs['user_location'] = Vec3(location)
Beispiel #27
0
    def moveto(self, location: "Vertex") -> "UCS":
        """Place current UCS at new origin `location` and returns `self`.

        Args:
            location: new origin in WCS

        """
        self.origin = Vec3(location)
        return self
Beispiel #28
0
    def shift(self, delta: "Vertex") -> "UCS":
        """Shifts current UCS by `delta` vector and returns `self`.

        Args:
            delta: shifting vector

        """
        self.origin += Vec3(delta)
        return self
Beispiel #29
0
    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)
Beispiel #30
0
def parse(geo_mapping: Dict) -> Dict:
    """ Parse ``__geo_interface__`` convert all coordinates into
    :class:`Vec3` objects, Polygon['coordinates'] is always a
    tuple (exterior, holes), holes maybe an empty list.

    """
    geo_mapping = copy.deepcopy(geo_mapping)
    type_ = geo_mapping.get(TYPE)
    if type_ is None:
        raise ValueError(f'Required key "{TYPE}" not found.')

    if type_ == FEATURE_COLLECTION:
        # It is possible for this array to be empty.
        features = geo_mapping.get(FEATURES)
        if features:
            geo_mapping[FEATURES] = [parse(f) for f in features]
        else:
            raise ValueError(
                f'Missing key "{FEATURES}" in FeatureCollection.')
    elif type_ == GEOMETRY_COLLECTION:
        # It is possible for this array to be empty.
        geometries = geo_mapping.get(GEOMETRIES)
        if geometries:
            geo_mapping[GEOMETRIES] = [parse(g) for g in geometries]
        else:
            raise ValueError(
                f'Missing key "{GEOMETRIES}" in GeometryCollection.')
    elif type_ == FEATURE:
        # The value of the geometry member SHALL be either a Geometry object
        # or, in the case that the Feature is unlocated, a JSON null value.
        if GEOMETRY in geo_mapping:
            geometry = geo_mapping.get(GEOMETRY)
            geo_mapping[GEOMETRY] = parse(geometry) if geometry else None
        else:
            raise ValueError(
                f'Missing key "{GEOMETRY}" in Feature.')
    elif type_ in {POINT, LINE_STRING, POLYGON, MULTI_POINT,
                   MULTI_LINE_STRING, MULTI_POLYGON}:
        coordinates = geo_mapping.get(COORDINATES)
        if coordinates is None:
            raise ValueError(
                f'Missing key "{COORDINATES}" in {type_}.')
        if type_ == POINT:
            coordinates = Vec3(coordinates)
        elif type_ in (LINE_STRING, MULTI_POINT):
            coordinates = Vec3.list(coordinates)
        elif type_ == POLYGON:
            coordinates = _parse_polygon(coordinates)
        elif type_ == MULTI_LINE_STRING:
            coordinates = [Vec3.list(v) for v in coordinates]
        elif type_ == MULTI_POLYGON:
            coordinates = [_parse_polygon(v) for v in coordinates]
        geo_mapping[COORDINATES] = coordinates
    else:
        raise TypeError(f'Invalid type "{type_}".')
    return geo_mapping