Exemple #1
0
 def draw_filled_paths(
     self,
     paths: Iterable[Path],
     holes: Iterable[Path],
     properties: Properties,
 ) -> None:
     paths = transform_paths(paths, self._scaling_matrix)
     holes = transform_paths(holes, self._scaling_matrix)
     self._backend.draw_filled_paths(paths, holes, properties)
Exemple #2
0
 def test_one_path_line_to(self):
     path = Path()
     path.line_to((1, 0))
     result = transform_paths([path], Matrix44())
     path0 = result[0]
     assert path0[0].type == Command.LINE_TO
     assert path0.start == (0, 0)
     assert path0.end == (1, 0)
Exemple #3
0
 def test_one_path_curve4_to(self):
     path = Path()
     path.curve4_to((2, 0), (0, 1), (2, 1))
     result = transform_paths([path], Matrix44())
     path0 = result[0]
     assert path0[0].type == Command.CURVE4_TO
     assert len(path0[0]) == 3
     assert path0.start == (0, 0)
     assert path0.end == (2, 0)
Exemple #4
0
 def test_multi_path_objects(self):
     path = Path()
     path.line_to((1, 0, 0))
     path.move_to((2, 0, 0))
     paths = transform_paths([path], Matrix44.translate(0, 1, 0))
     assert len(paths) == 1
     path2 = paths[0]
     assert path2.start.isclose((0, 1, 0))
     assert len(path2) == 2
     assert path2.end.isclose((2, 1, 0))
     assert path2.has_sub_paths is True
Exemple #5
0
    def test_one_path_multiple_command(self):
        path = Path()
        path.line_to((1, 0))
        path.curve3_to((2, 0), (2.5, 1))
        path.curve4_to((3, 0), (2, 1), (3, 1))
        result = transform_paths([path], Matrix44())

        path0 = result[0]
        assert path0[0].type == Command.LINE_TO
        assert path0[1].type == Command.CURVE3_TO
        assert path0[2].type == Command.CURVE4_TO
        assert path0.start == (0, 0)
        assert path0.end == (3, 0)
Exemple #6
0
    def test_two_paths_one_command(self):
        path_a = Path()
        path_a.line_to((1, 0))
        path_b = Path((2, 0))
        path_b.line_to((3, 0))
        result = transform_paths([path_a, path_b], Matrix44())

        path0 = result[0]
        assert path0[0].type == Command.LINE_TO
        assert path0.start == (0, 0)
        assert path0.end == (1, 0)

        path1 = result[1]
        assert path1[0].type == Command.LINE_TO
        assert path1.start == (2, 0)
        assert path1.end == (3, 0)
Exemple #7
0
def make_paths_from_str(s: str,
                        font: fonts.FontFace,
                        size: float = 1.0,
                        align: str = 'LEFT',
                        length: float = 0,
                        m: Matrix44 = None) -> List[Path]:
    """ Convert a single line string `s` into a list of
    :class:`~ezdxf.path.Path` objects. All paths are returned in a single
    list. The text `size` is the height of the uppercase letter "X" (cap height).
    The paths are aligned about the insertion point at (0, 0).
    BASELINE means the bottom of the letter "X".

    Args:
         s: text to convert
         font: font face definition
         size: text size (cap height) in drawing units
         align: alignment as string, default is "LEFT"
         length: target length for the "ALIGNED" and "FIT" alignments
         m: transformation :class:`~ezdxf.math.Matrix44`

    """
    if len(s) == 0:
        return []
    font_properties, font_measurements = _get_font_data(font)
    scaled_size = size / font_measurements.cap_height
    scaled_fm = font_measurements.scale_from_baseline(scaled_size)
    paths = _str_to_paths(s, font_properties, scaled_size)
    bbox = path.bbox(paths, precise=False)
    halign, valign = const.TEXT_ALIGN_FLAGS[align.upper()]
    matrix = get_alignment_transformation(scaled_fm, bbox, halign, valign)

    stretch_x = 1.0
    stretch_y = 1.0
    if align == 'ALIGNED':
        stretch_x = length / bbox.size.x
        stretch_y = stretch_x
    elif align == 'FIT':
        stretch_x = length / bbox.size.x
    if stretch_x != 1.0:
        matrix *= Matrix44.scale(stretch_x, stretch_y, 1.0)
    if m is not None:
        matrix *= m
    return list(path.transform_paths(paths, matrix))
Exemple #8
0
def make_paths_from_entity(entity: AnyText) -> List[Path]:
    """ Convert text content from DXF entities TEXT and ATTRIB into a
    list of :class:`~ezdxf.path.Path` objects. All paths are returned in a
    single list.
    The paths are located at the location of the source entity.

    """

    check_entity_type(entity)
    fonts.load()
    text = entity.plain_text()
    paths = make_paths_from_str(
        text,
        fonts.get_font_face(entity.font_name()),
        size=entity.dxf.height,  # cap height in drawing units
        align=entity.get_align(),
        length=entity.fit_length(),
    )
    m = entity.wcs_transformation_matrix()
    return path.transform_paths(paths, m)
Exemple #9
0
    def test_two_paths_multiple_commands(self):
        path_a = Path()
        path_a.line_to((1, 0))
        path_a.curve3_to((2, 0), (2.5, 1))
        path_a.curve4_to((3, 0), (2, 1), (3, 1))

        path_b = path_a.transform(Matrix44.translate(4, 0, 0))
        result = transform_paths([path_a, path_b], Matrix44())

        path0 = result[0]
        assert path0[0].type == Command.LINE_TO
        assert path0[1].type == Command.CURVE3_TO
        assert path0[2].type == Command.CURVE4_TO
        assert path0.start == (0, 0)
        assert path0.end == (3, 0)

        path1 = result[1]
        assert path1[0].type == Command.LINE_TO
        assert path1[1].type == Command.CURVE3_TO
        assert path1[2].type == Command.CURVE4_TO
        assert path1.start == (4, 0)
        assert path1.end == (7, 0)
Exemple #10
0
def make_paths_from_str(s: str,
                        font: fonts.FontFace,
                        size: float = 1.0,
                        align: str = 'LEFT',
                        length: float = 0,
                        m: Matrix44 = None) -> List[Path]:
    """ Convert a single line string `s` into a list of
    :class:`~ezdxf.path.Path` objects. All paths are returned in a single
    list. The text `size` is the height of the uppercase letter "X" (cap height).
    The paths are aligned about the insertion point at (0, 0).
    BASELINE means the bottom of the letter "X".

    Args:
         s: text to convert
         font: font face definition as :class:`~ezdxf.tools.fonts.FontFace` object
         size: text size (cap height) in drawing units
         align: alignment as string, default is "LEFT"
         length: target length for the "ALIGNED" and "FIT" alignments
         m: transformation :class:`~ezdxf.math.Matrix44`

    """
    if len(s) == 0:
        return []
    font_properties, font_measurements = _get_font_data(font)
    # scale font rendering units to drawing units:
    render_size = size / font_measurements.cap_height
    paths = _str_to_paths(s, font_properties, render_size)
    bbox = path.bbox(paths, flatten=False)

    # Text is rendered in drawing units,
    # therefore do alignment in drawing units:
    draw_units_fm = font_measurements.scale_from_baseline(size)
    matrix = alignment_transformation(draw_units_fm, bbox, align, length)
    if m is not None:
        matrix *= m
    return list(path.transform_paths(paths, matrix))
# create the target box:
msp.add_lwpolyline([(0, 0), (sx, 0), (sx, sy), (0, sy)],
                   close=True,
                   dxfattribs={'color': 1})

# convert text string into path objects:
text_as_paths = text2path.make_paths_from_str("Squeeze Me", ff)

# fit text paths into a given box size by scaling, does not move the path objects:
# uniform=True, keeps the text aspect ratio
# uniform=False, scales the text to touch all 4 sides of the box
final_paths = path.fit_paths_into_box(text_as_paths,
                                      size=(sx, sy, 0),
                                      uniform=False)

# mirror text along x-axis
final_paths = path.transform_paths(final_paths, Matrix44.scale(-1, 1, 1))

# move bottom/left corner to (0, 0) if required:
bbox = path.bbox(final_paths)
dx, dy, dz = -bbox.extmin
final_paths = path.transform_paths(final_paths, Matrix44.translate(dx, dy, dz))

path.render_lwpolylines(msp,
                        final_paths,
                        distance=0.01,
                        dxfattribs={'color': 2})

zoom.extents(msp)
doc.saveas(DIR / 'SqeezeMe.dxf')
Exemple #12
0
 def test_transformation_is_executed(self):
     # Real transformation is just tested once, because Matrix44
     # transformation is tested in 605:
     result = transform_paths([Path((1, 2, 3))],
                              Matrix44.translate(1, 1, 1))
     assert result[0].start == (2, 3, 4)
Exemple #13
0
 def test_start_point_only_paths(self):
     result = transform_paths([Path((1, 2, 3))], Matrix44())
     assert len(result) == 1
     assert len(result[0]) == 0
     assert result[0].start == (1, 2, 3)
Exemple #14
0
 def test_empty_paths(self):
     result = transform_paths([], Matrix44())
     assert len(result) == 0
Exemple #15
0
def make_paths_from_entity(entity: AnyText) -> List[Path]:
    """ Convert text content from DXF entities TEXT and ATTRIB into a
    list of :class:`~ezdxf.path.Path` objects. All paths are returned in a
    single list.
    The paths are located at the location of the source entity, but don't expect
    a 100% match compared to CAD applications.

    """

    def get_font_name():
        font_name = 'arial.ttf'
        style_name = entity.dxf.style
        if entity.doc:
            try:
                style = entity.doc.styles.get(style_name)
                font_name = style.dxf.font
            except ValueError:
                pass
        return font_name

    def get_transformation():
        """ Apply rotation, width factor, translation to the insertion point
        and if necessary transformation from OCS to WCS.
        """
        # TODO: text generation flags - mirror-x and mirror-y
        angle = math.radians(entity.dxf.rotation)
        width_factor = entity.dxf.width
        if align == 'LEFT':
            location = p1
        elif align in ('ALIGNED', 'FIT'):
            width_factor = 1.0  # text goes from p1 to p2, no stretching applied
            location = p1.lerp(p2, factor=0.5)
            angle = (p2 - p1).angle  # override stored angle
        else:
            location = p2
        m = Matrix44.chain(
            Matrix44.scale(width_factor, 1, 1),
            Matrix44.z_rotate(angle),
            Matrix44.translate(location.x, location.y, location.z),
        )
        ocs = entity.ocs()
        if ocs.transform:
            m *= ocs.matrix
        return m

    if not entity.dxftype() in ('TEXT', 'ATTRIB'):
        raise TypeError(f'unsupported entity type: {entity.dxftype()}')
    fonts.load()
    text = entity.plain_text()
    align = entity.get_align()
    p1 = Vec3(entity.dxf.insert)
    if entity.dxf.hasattr('align_point'):
        p2 = Vec3(entity.dxf.align_point)
    else:
        p2 = p1

    length = 0
    if align in ('FIT', 'ALIGNED'):
        # text is stretch between p1 and p2
        length = p1.distance(p2)
    paths = make_paths_from_str(
        text, fonts.get_font_face(get_font_name()),
        size=entity.dxf.height,  # cap height in drawing units
        align=align,
        length=length,
    )
    m = get_transformation()
    return path.transform_paths(paths, m)