Exemplo n.º 1
0
    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)
Exemplo n.º 2
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 = Path.from_vertices(
            ocs.points_to_wcs(corner_vertices),
            close=True,
        )
Exemplo n.º 3
0
Arquivo: geo.py Projeto: kloppen/ezdxf
def mapping(entity: DXFGraphic,
            distance: float = MAX_FLATTENING_DISTANCE,
            force_line_string: bool = False) -> Dict:
    """ Create the compiled ``__geo_interface__`` mapping as :class:`dict`
    for the given DXF `entity`, all coordinates are :class:`Vector` objects and
    represents "Polygon" always as tuple (exterior, holes) even without holes.


    Internal API - result is **not** a valid ``_geo_interface__`` mapping!

    Args:
        entity: DXF entity
        distance: maximum flattening distance for curve approximations
        force_line_string: by default this function returns Polygon objects for
            closed geometries like CIRCLE, SOLID, closed POLYLINE and so on,
            by setting argument `force_line_string` to ``True``, this entities
            will be returned as LineString objects.

    """

    dxftype = entity.dxftype()
    if dxftype == 'POINT':
        return {TYPE: POINT, COORDINATES: entity.dxf.location}
    elif dxftype == 'LINE':
        return line_string_mapping([entity.dxf.start, entity.dxf.end])
    elif dxftype == 'POLYLINE':
        entity = cast('Polyline', entity)
        if entity.is_3d_polyline or entity.is_2d_polyline:
            # May contain arcs as bulge values:
            path = Path.from_polyline(entity)
            points = list(path.flattening(distance))
            return _line_string_or_polygon_mapping(points, force_line_string)
        else:
            raise TypeError('Polymesh and Polyface not supported.')
    elif dxftype == 'LWPOLYLINE':
        # May contain arcs as bulge values:
        path = Path.from_lwpolyline(cast('LWPolyline', entity))
        points = list(path.flattening(distance))
        return _line_string_or_polygon_mapping(points, force_line_string)
    elif dxftype in {'CIRCLE', 'ARC', 'ELLIPSE', 'SPLINE'}:
        return _line_string_or_polygon_mapping(
            list(entity.flattening(distance)), force_line_string)
    elif dxftype in {'SOLID', 'TRACE', '3DFACE'}:
        return _line_string_or_polygon_mapping(entity.wcs_vertices(close=True),
                                               force_line_string)
    elif dxftype == 'HATCH':
        return _hatch_as_polygon(entity, distance, force_line_string)
    else:
        raise TypeError(dxftype)
Exemplo n.º 4
0
Arquivo: geo.py Projeto: kloppen/ezdxf
def _boundaries_to_polygons(boundaries, ocs, elevation):
    paths = (Path.from_hatch_boundary_path(boundary, ocs, elevation)
             for boundary in boundaries)
    for polygon in nesting.fast_bbox_detection(paths):
        exterior = polygon[0]
        # only take exterior path of level 1 holes, nested holes are ignored
        yield exterior, [hole[0] for hole in polygon[1:]]
Exemplo n.º 5
0
def test_end_points(ellipse):
    p = Path.from_ellipse(ellipse)

    assert ellipse.start_point.isclose(p.start)
    assert ellipse.end_point.isclose(p.end)

    # end point locations measured in BricsCAD:
    assert ellipse.start_point.isclose((2191.3054, -1300.8375), abs_tol=1e-4)
    assert ellipse.end_point.isclose((2609.7870, -1520.6677), abs_tol=1e-4)
Exemplo n.º 6
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)
Exemplo n.º 7
0
def _hatch_primitives(
        hatch: 'Hatch',
        max_flattening_distance=None) -> Iterable[AbstractPrimitive]:
    """ Yield all HATCH boundary paths as separated Path() objects. """
    ocs = hatch.ocs()
    elevation = hatch.dxf.elevation.z
    for boundary in hatch.paths:
        yield PathPrimitive(
            Path.from_hatch_boundary_path(boundary, ocs, elevation), hatch,
            max_flattening_distance)
Exemplo n.º 8
0
    def path(self) -> Optional[Path]:
        """ Create path representation on demand.

        :class:`Path` can not represent a point, a :class:`Path` with only a
        start point yields not vertices!

        """
        if self._path is None:
            self._path = Path(self.entity.dxf.location)
        return self._path
Exemplo n.º 9
0
 def draw_solid_entity(self, entity: DXFGraphic) -> None:
     # Handles SOLID, TRACE and 3DFACE
     dxf, dxftype = entity.dxf, entity.dxftype()
     properties = self._resolve_properties(entity)
     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(Path.from_vertices(points, close=True), properties)
     else:  # SOLID, TRACE
         self.out.draw_filled_polygon(points, properties)
Exemplo n.º 10
0
def linedrawer(painter, scaler, boardlen, boardwid):
    # Right now this is disabled as I am just testing the Hatch drawing
    """for e in msp:
        print(e.dxftype())"""
    """for e in msp.query("CIRCLE"):
        ocs = e.ocs()
        radius = e.dxf.radius
        wcs_center = ocs.to_wcs(e.dxf.center)
        painter.drawEllipse(wcs_center[0] * scaler, wcs_center[1] * scaler, (radius / 2) * scaler,
                            (radius / 2) * scaler)"""

    for e in msp.query('LINE'):
        if e.dxf.layer in render_layers:
            start = e.dxf.start
            end = e.dxf.end
            startx = start[0] * scaler
            starty = start[1] * scaler
            endx = end[0] * scaler
            endy = end[1] * scaler
            # Right now this is disabled as I am just testing the Hatch drawing
            # painter.drawLine(startx, starty, endx, endy)
    for hatch in msp:
        try:
            if hatch.dxftype() == "HATCH" and hatch.dxf.layer in render_layers:
                ocs = hatch.ocs()
                for p in hatch.paths:
                    # For each path of a hatch
                    if p.PATH_TYPE == 'EdgePath':
                        path = Path.from_hatch_edge_path(p, ocs, 0)
                        for counter, vector in enumerate(path.approximate()):
                            if counter == 0:
                                oldvector = vector
                            sxs = vector[0] * scaler
                            sys = (boardlen - vector[1]) * scaler
                            sxe = oldvector[0] * scaler
                            sye = (boardlen - oldvector[1]) * scaler
                            oldvector = vector
                            painter.drawLine(sxs, sys, sxe, sye)
        except:
            print("Error!")
            continue
    for region in msp:
        try:
            if region.dxftype(
            ) == "REGION" and region.dxf.layer in render_layers:
                print("REGION found")
        except:
            print("Error!")
            continue
Exemplo n.º 11
0
#  Copyright (c) 2020, Manfred Moitzi
#  License: MIT License
import ezdxf
from ezdxf.render import Path

doc = ezdxf.new()
msp = doc.modelspace()

ellipse = msp.add_ellipse(
    center=(1999.488177113287, -1598.02265357955, 0.0),
    major_axis=(629.968069297, 0.0, 0.0),
    ratio=0.495263197,
    start_param=-1.261396328799999,
    end_param=-0.2505454928,
    dxfattribs={
        'layer': "0",
        'linetype': "Continuous",
        'color': 3,
        'extrusion': (0.0, 0.0, -1.0),
    },
)

p = Path.from_ellipse(ellipse)
msp.add_lwpolyline(p.approximate(),
                   dxfattribs={
                       'layer': 'PathRendering',
                       'color': 1,
                   })
doc.set_modelspace_vport(500, (2400, -1400))
doc.saveas('path_rendering.dxf')
Exemplo n.º 12
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)

        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.
            """
            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)
        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 = Path.from_vertices(
            ucs.points_to_wcs(corner_vertices),
            close=True,
        )
Exemplo n.º 13
0
Arquivo: geo.py Projeto: kloppen/ezdxf
 def boundary_to_vertices(boundary) -> List[Vector]:
     path = Path.from_hatch_boundary_path(boundary, ocs, elevation)
     return path_to_vertices(path)
Exemplo n.º 14
0
#  Copyright (c) 2020, Manfred Moitzi
#  License: MIT License

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

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

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

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

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

RIGHT_HOLE = list(translate(square(2.0), (3, -1)))
RH_PATH = 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(
        # returns the path sorted by area, and reversed if equal sized
        [EXT1_PATH, EXT2_PATH], [[EXT2_PATH], [EXT1_PATH]],
Exemplo n.º 15
0
 def to_path(p):
     path = Path.from_hatch_boundary_path(p, ocs, elevation)
     path.close()
     return path
Exemplo n.º 16
0
 def draw_spline_entity(self, entity: DXFGraphic) -> None:
     properties = self._resolve_properties(entity)
     path = Path.from_spline(cast(Spline, entity))
     self.out.draw_path(path, properties)