Files
stepanalyser/.venv/lib/python3.12/site-packages/ezdxf/render/polyline.py
Christian Anetzberger a197de9456 initial
2026-01-22 20:23:51 +01:00

249 lines
8.3 KiB
Python

# Copyright (c) 2020-2022, Manfred Moitzi
# License: MIT License
from __future__ import annotations
from typing import TYPE_CHECKING, Iterable, Union
import logging
import math
from ezdxf.entities import factory
from ezdxf.lldxf.const import VERTEXNAMES
from ezdxf.math import Vec3, bulge_to_arc, OCS
if TYPE_CHECKING:
from ezdxf.entities import LWPolyline, Polyline, Line, Arc, Face3d, Polymesh
logger = logging.getLogger("ezdxf")
def virtual_lwpolyline_entities(
lwpolyline: LWPolyline,
) -> Iterable[Union[Line, Arc]]:
"""Yields 'virtual' entities of LWPOLYLINE as LINE or ARC objects.
These entities are located at the original positions, but are not stored in
the entity database, have no handle and are not assigned to any layout.
(internal API)
"""
assert lwpolyline.dxftype() == "LWPOLYLINE"
points = lwpolyline.get_points("xyb")
if len(points) < 2:
return
if lwpolyline.closed:
points.append(points[0])
yield from _virtual_polyline_entities(
points=points,
elevation=lwpolyline.dxf.elevation,
extrusion=lwpolyline.dxf.get("extrusion", None),
dxfattribs=lwpolyline.graphic_properties(),
doc=lwpolyline.doc,
)
def virtual_polyline_entities(
polyline: Polyline,
) -> Iterable[Union[Line, Arc, Face3d]]:
"""Yields 'virtual' entities of POLYLINE as LINE, ARC or 3DFACE objects.
These entities are located at the original positions, but are not stored in
the entity database, have no handle and are not assigned to any layout.
(internal API)
"""
assert polyline.dxftype() == "POLYLINE"
if polyline.is_2d_polyline:
return virtual_polyline2d_entities(polyline)
elif polyline.is_3d_polyline:
return virtual_polyline3d_entities(polyline)
elif polyline.is_polygon_mesh:
return virtual_polymesh_entities(polyline)
elif polyline.is_poly_face_mesh:
return virtual_polyface_entities(polyline)
return []
def virtual_polyline2d_entities(
polyline: Polyline,
) -> Iterable[Union[Line, Arc]]:
"""Yields 'virtual' entities of 2D POLYLINE as LINE or ARC objects.
These entities are located at the original positions, but are not stored in
the entity database, have no handle and are not assigned to any layout.
(internal API)
"""
assert polyline.dxftype() == "POLYLINE"
assert polyline.is_2d_polyline
if len(polyline.vertices) < 2:
return
points = [
(v.dxf.location.x, v.dxf.location.y, v.dxf.bulge)
for v in polyline.vertices
]
if polyline.is_closed:
points.append(points[0])
yield from _virtual_polyline_entities(
points=points,
elevation=Vec3(polyline.dxf.get("elevation", (0, 0, 0))).z,
extrusion=polyline.dxf.get("extrusion", None),
dxfattribs=polyline.graphic_properties(),
doc=polyline.doc,
)
def _virtual_polyline_entities(
points, elevation: float, extrusion: Vec3, dxfattribs: dict, doc
) -> Iterable[Union[Line, Arc]]:
ocs = OCS(extrusion) if extrusion else OCS()
prev_point = None
prev_bulge = None
for x, y, bulge in points:
point = Vec3(x, y, elevation)
if prev_point is None:
prev_point = point
prev_bulge = bulge
continue
attribs = dict(dxfattribs)
if prev_bulge != 0:
center, start_angle, end_angle, radius = bulge_to_arc(
prev_point, point, prev_bulge
)
if radius > 0:
attribs["center"] = Vec3(center.x, center.y, elevation)
attribs["radius"] = radius
attribs["start_angle"] = math.degrees(start_angle)
attribs["end_angle"] = math.degrees(end_angle)
if extrusion:
attribs["extrusion"] = extrusion
yield factory.new(dxftype="ARC", dxfattribs=attribs, doc=doc)
else:
attribs["start"] = ocs.to_wcs(prev_point)
attribs["end"] = ocs.to_wcs(point)
yield factory.new(dxftype="LINE", dxfattribs=attribs, doc=doc)
prev_point = point
prev_bulge = bulge
def virtual_polyline3d_entities(polyline: Polyline) -> Iterable[Line]:
"""Yields 'virtual' entities of 3D POLYLINE as LINE objects.
This entities are located at the original positions, but are not stored in
the entity database, have no handle and are not assigned to any layout.
(internal API)
"""
assert polyline.dxftype() == "POLYLINE"
assert polyline.is_3d_polyline
if len(polyline.vertices) < 2:
return
doc = polyline.doc
vertices = polyline.vertices
dxfattribs = polyline.graphic_properties()
start = -1 if polyline.is_closed else 0
for index in range(start, len(vertices) - 1):
dxfattribs["start"] = vertices[index].dxf.location
dxfattribs["end"] = vertices[index + 1].dxf.location
yield factory.new(dxftype="LINE", dxfattribs=dxfattribs, doc=doc) # type: ignore
def virtual_polymesh_entities(polyline: Polyline) -> Iterable[Face3d]:
"""Yields 'virtual' entities of POLYMESH as 3DFACE objects.
This entities are located at the original positions, but are not stored in
the entity database, have no handle and are not assigned to any layout.
(internal API)
"""
polymesh: "Polymesh" = polyline # type: ignore
assert polymesh.dxftype() == "POLYLINE"
assert polymesh.is_polygon_mesh
doc = polymesh.doc
mesh = polymesh.get_mesh_vertex_cache()
dxfattribs = polymesh.graphic_properties()
m_count = polymesh.dxf.m_count
n_count = polymesh.dxf.n_count
m_range = m_count - int(not polymesh.is_m_closed)
n_range = n_count - int(not polymesh.is_n_closed)
for m in range(m_range):
for n in range(n_range):
next_m = (m + 1) % m_count
next_n = (n + 1) % n_count
dxfattribs["vtx0"] = mesh[m, n]
dxfattribs["vtx1"] = mesh[next_m, n]
dxfattribs["vtx2"] = mesh[next_m, next_n]
dxfattribs["vtx3"] = mesh[m, next_n]
yield factory.new(dxftype="3DFACE", dxfattribs=dxfattribs, doc=doc) # type: ignore
def virtual_polyface_entities(polyline: Polyline) -> Iterable[Face3d]:
"""Yields 'virtual' entities of POLYFACE as 3DFACE objects.
This entities are located at the original positions, but are not stored in
the entity database, have no handle and are not assigned to any layout.
(internal API)
"""
assert polyline.dxftype() == "POLYLINE"
assert polyline.is_poly_face_mesh
doc = polyline.doc
vertices = polyline.vertices
base_attribs = polyline.graphic_properties()
face_records = (v for v in vertices if v.is_face_record)
for face in face_records:
# check if vtx0, vtx1 and vtx2 exist
for name in VERTEXNAMES[:-1]:
if not face.dxf.hasattr(name):
logger.info(
f"skipped face {str(face)} with less than 3 vertices"
f"in PolyFaceMesh(#{str(polyline.dxf.handle)})"
)
continue
# Alternate solutions: return a face with less than 3 vertices
# as LINE (breaks the method signature) or as degenerated 3DFACE
# (vtx0, vtx1, vtx1, vtx1)
face3d_attribs = dict(base_attribs)
face3d_attribs.update(face.graphic_properties())
invisible = 0
pos = 1
indices = (
(face.dxf.get(name), name)
for name in VERTEXNAMES
if face.dxf.hasattr(name)
)
for index, name in indices:
# vertex indices are 1-based, negative indices indicate invisible edges
if index < 0:
index = abs(index)
invisible += pos
# python list `vertices` is 0-based
face3d_attribs[name] = vertices[index - 1].dxf.location
# vertex index bit encoded: 1=0b0001, 2=0b0010, 3=0b0100, 4=0b1000
pos <<= 1
if "vtx3" not in face3d_attribs:
# A triangle face ends with two identical vertices vtx2 and vtx3.
# This is a requirement defined by AutoCAD.
face3d_attribs["vtx3"] = face3d_attribs["vtx2"]
face3d_attribs["invisible"] = invisible
yield factory.new(dxftype="3DFACE", dxfattribs=face3d_attribs, doc=doc) # type: ignore