예제 #1
0
    def draw_solid_entity(self, entity: DXFGraphic,
                          properties: Properties) -> None:
        if isinstance(entity, Face3d):
            dxf = entity.dxf
            try:
                # this implementation supports all features of example file:
                # examples_dxf/3dface.dxf without changing the behavior of
                # Face3d.wcs_vertices() which removes the last vertex if
                # duplicated.
                points = [dxf.vtx0, dxf.vtx1, dxf.vtx2, dxf.vtx3, dxf.vtx0]
            except AttributeError:
                # all 4 vertices are required, otherwise the entity is invalid
                # for AutoCAD
                self.skip_entity(entity, "missing required vertex attribute")
                return
            edge_visibility = entity.get_edges_visibility()
            if all(edge_visibility):
                self.out.draw_path(from_vertices(points), properties)
            else:
                for a, b, visible in zip(points, points[1:], edge_visibility):
                    if visible:
                        self.out.draw_line(a, b, properties)

        elif isinstance(entity, Solid):
            # set solid fill type for SOLID and TRACE
            properties.filling = Filling()
            self.out.draw_filled_polygon(entity.wcs_vertices(close=False),
                                         properties)

        else:
            raise TypeError(
                "API error, requires a SOLID, TRACE or 3DFACE entity")
def test_backend_default_draw_path():
    backend = BasicBackend()
    path = from_vertices([(0, 0), (1, 0), (2, 0)])
    backend.draw_path(path, Properties())
    result = backend.collector
    assert len(result) == 2
    assert result[0][0] == "line"
예제 #3
0
    def _convert_entity(self):
        """ Calculates the rough border path for a single line text.

        Calculation is based on a mono-spaced font and therefore the border
        path is just an educated guess.

        Vertical text generation and oblique angle is ignored.

        """
        def get_text_rotation() -> float:
            if alignment in ('FIT', 'ALIGNED') and not p1.isclose(p2):
                return (p2 - p1).angle
            else:
                return math.degrees(text.dxf.rotation)

        def get_insert() -> Vec3:
            if alignment == 'LEFT':
                return p1
            elif alignment in ('FIT', 'ALIGNED'):
                return p1.lerp(p2, factor=0.5)
            else:
                return p2

        text = cast('Text', self.entity)
        if text.dxftype() == 'ATTDEF':
            # ATTDEF outside of a BLOCK renders the tag rather than the value
            content = text.dxf.tag
        else:
            content = text.dxf.text

        content = plain_text(content)
        if len(content) == 0:
            # empty path - does not render any vertices!
            self._path = Path()
            return

        p1: Vec3 = text.dxf.insert
        p2: Vec3 = text.dxf.align_point
        font = fonts.make_font(get_font_name(text), text.dxf.height,
                               text.dxf.width)
        text_line = TextLine(content, font)
        alignment: str = text.get_align()
        if text.dxf.halign > 2:  # ALIGNED=3, MIDDLE=4, FIT=5
            text_line.stretch(alignment, p1, p2)
        halign, valign = unified_alignment(text)
        corner_vertices = text_line.corner_vertices(get_insert(),
                                                    halign, valign,
                                                    get_text_rotation())

        ocs = text.ocs()
        self._path = from_vertices(
            ocs.points_to_wcs(corner_vertices),
            close=True,
        )
예제 #4
0
 def draw_solid_entity(self, entity: DXFGraphic,
                       properties: Properties) -> None:
     assert isinstance(entity, (Solid, Face3d)), \
         "API error, requires a SOLID, TRACE or 3DFACE entity"
     dxf, dxftype = entity.dxf, entity.dxftype()
     points = entity.wcs_vertices()
     if dxftype == '3DFACE':
         self.out.draw_path(from_vertices(points, close=True), properties)
     else:
         # set solid fill type for SOLID and TRACE
         properties.filling = Filling()
         self.out.draw_filled_polygon(points, properties)
예제 #5
0
 def draw_solid_entity(self, entity: DXFGraphic,
                       properties: Properties) -> None:
     # Handles SOLID, TRACE and 3DFACE
     dxf, dxftype = entity.dxf, entity.dxftype()
     points = get_tri_or_quad_points(entity,
                                     adjust_order=dxftype != '3DFACE')
     # TRACE is an OCS entity
     if dxftype == 'TRACE' and dxf.hasattr('extrusion'):
         ocs = entity.ocs()
         points = list(ocs.points_to_wcs(points))
     if dxftype == '3DFACE':
         self.out.draw_path(from_vertices(points, close=True), properties)
     else:
         # Set default SOLID filling for SOLID and TRACE
         properties.filling = Filling()
         self.out.draw_filled_polygon(points, properties)
예제 #6
0
 def draw_mesh_builder_entity(self, builder: MeshBuilder,
                              properties: Properties) -> None:
     for face in builder.faces_as_vertices():
         self.out.draw_path(from_vertices(face, close=True),
                            properties=properties)
예제 #7
0
 def test_only_vertices(self):
     p = from_vertices([(1, 0), (2, 0), (3, 1)])
     result = list(to_bsplines_and_vertices(p))
     assert len(result) == 1, "expected one list of vertices"
     assert len(result[0]) == 3, "expected 3 vertices"
예제 #8
0
    def _convert_entity(self):
        """Calculates the rough border path for a MTEXT entity.

        Calculation is based on a mono-spaced font and therefore the border
        path is just an educated guess.

        Most special features of MTEXT is not supported.

        """
        def get_content() -> List[str]:
            text = mtext.plain_text(split=False)
            return text_wrap(text, box_width, font.text_width)  # type: ignore

        def get_max_str() -> str:
            return max(content, key=lambda s: len(s))

        def get_rect_width() -> float:
            if box_width:
                return box_width
            s = get_max_str()
            if len(s) == 0:
                s = " "
            return font.text_width(s)

        def get_rect_height() -> float:
            line_height = font.measurements.total_height
            cap_height = font.measurements.cap_height
            # Line spacing factor: Percentage of default (3-on-5) line
            # spacing to be applied.

            # thx to mbway: multiple of cap_height between the baseline of the
            # previous line and the baseline of the next line
            # 3-on-5 line spacing = 5/3 = 1.67
            line_spacing = cap_height * mtext.dxf.line_spacing_factor * 1.67
            spacing = line_spacing - line_height
            line_count = len(content)
            return line_height * line_count + spacing * (line_count - 1)

        def get_ucs() -> UCS:
            """Create local coordinate system:
            origin = insertion point
            z-axis = extrusion vector
            x-axis = text_direction or text rotation, text rotation requires
                extrusion vector == (0, 0, 1) or treatment like an OCS?

            """
            origin = mtext.dxf.insert
            z_axis = mtext.dxf.extrusion  # default is Z_AXIS
            x_axis = X_AXIS
            if mtext.dxf.hasattr("text_direction"):
                x_axis = mtext.dxf.text_direction
            elif mtext.dxf.hasattr("rotation"):
                # TODO: what if extrusion vector is not (0, 0, 1)
                x_axis = Vec3.from_deg_angle(mtext.dxf.rotation)
                z_axis = Z_AXIS
            return UCS(origin=origin, ux=x_axis, uz=z_axis)

        def get_shift_factors():
            halign, valign = unified_alignment(mtext)
            shift_x = 0
            shift_y = 0
            if halign == const.CENTER:
                shift_x = -0.5
            elif halign == const.RIGHT:
                shift_x = -1.0
            if valign == const.MIDDLE:
                shift_y = 0.5
            elif valign == const.BOTTOM:
                shift_y = 1.0
            return shift_x, shift_y

        def get_corner_vertices() -> Iterable[Vec3]:
            """Create corner vertices in the local working plan, where
            the insertion point is the origin.
            """
            if columns:
                rect_width = columns.total_width
                rect_height = columns.total_height
                # TODO: this works only for reliable sources like AutoCAD,
                #  BricsCAD and ezdxf! So far no known column support from
                #  other DXF exporters.
            else:
                rect_width = mtext.dxf.get("rect_width", get_rect_width())
                rect_height = mtext.dxf.get("rect_height", get_rect_height())
            # TOP LEFT alignment:
            vertices = [
                Vec3(0, 0),
                Vec3(rect_width, 0),
                Vec3(rect_width, -rect_height),
                Vec3(0, -rect_height),
            ]
            sx, sy = get_shift_factors()
            shift = Vec3(sx * rect_width, sy * rect_height)
            return (v + shift for v in vertices)

        mtext: "MText" = cast("MText", self.entity)
        columns = mtext.columns
        if columns is None:
            box_width = mtext.dxf.get("width", 0)
            font = fonts.make_font(get_font_name(mtext), mtext.dxf.char_height,
                                   1.0)
            content: List[str] = get_content()
            if len(content) == 0:
                # empty path - does not render any vertices!
                self._path = Path()
                return
        ucs = get_ucs()
        corner_vertices = get_corner_vertices()
        self._path = from_vertices(
            ucs.points_to_wcs(corner_vertices),
            close=True,
        )
예제 #9
0
    def _convert_entity(self):
        """Calculates the rough border path for a single line text.

        Calculation is based on a mono-spaced font and therefore the border
        path is just an educated guess.

        Vertical text generation and oblique angle is ignored.

        """
        def text_rotation():
            if fit_or_aligned and not p1.isclose(p2):
                return (p2 - p1).angle
            else:
                return math.radians(text.dxf.rotation)

        def location():
            if fit_or_aligned:
                return p1.lerp(p2, factor=0.5)
            return p1

        text = cast("Text", self.entity)
        if text.dxftype() == "ATTDEF":
            # ATTDEF outside of a BLOCK renders the tag rather than the value
            content = text.dxf.tag
        else:
            content = text.dxf.text

        content = plain_text(content)
        if len(content) == 0:
            # empty path - does not render any vertices!
            self._path = Path()
            return

        font = fonts.make_font(get_font_name(text), text.dxf.height,
                               text.dxf.width)
        text_line = TextLine(content, font)
        alignment, p1, p2 = text.get_placement()
        if p2 is None:
            p2 = p1
        fit_or_aligned = (alignment == TextEntityAlignment.FIT
                          or alignment == TextEntityAlignment.ALIGNED)
        if text.dxf.halign > 2:  # ALIGNED=3, MIDDLE=4, FIT=5
            text_line.stretch(alignment, p1, p2)
        halign, valign = unified_alignment(text)
        mirror_x = -1 if text.is_backward else 1
        mirror_y = -1 if text.is_upside_down else 1
        oblique: float = math.radians(text.dxf.oblique)
        corner_vertices = text_line.corner_vertices(
            location(),
            halign,
            valign,
            angle=text_rotation(),
            scale=(mirror_x, mirror_y),
            oblique=oblique,
        )

        ocs = text.ocs()
        self._path = from_vertices(
            ocs.points_to_wcs(corner_vertices),
            close=True,
        )
예제 #10
0
#  Copyright (c) 2020, Manfred Moitzi
#  License: MIT License

import pytest
from ezdxf.render.forms import square, translate
from ezdxf.path import Path, nesting, from_vertices

EXTERIOR = list(translate(square(10), (-5, -5)))
EXT1_PATH = from_vertices(EXTERIOR)
EXT2_PATH = from_vertices(translate(EXTERIOR, (11, 0)))

CENTER_HOLE1 = list(translate(square(8), (-4, -4)))
CH1_PATH = from_vertices(CENTER_HOLE1)

CENTER_HOLE2 = list(translate(square(6), (-3, -3)))
CH2_PATH = from_vertices(CENTER_HOLE2)

LEFT_HOLE = list(translate(square(2.1), (-3, -1)))
LH_PATH = from_vertices(LEFT_HOLE)

RIGHT_HOLE = list(translate(square(2.0), (3, -1)))
RH_PATH = from_vertices(RIGHT_HOLE)

DETECTION_DATA = [
    pytest.param(
        # Each polygon is a list of paths
        [EXT1_PATH],
        [[EXT1_PATH]],
        id="1 path",
    ),
    pytest.param(