refactor: excel parse
This commit is contained in:
@@ -0,0 +1,279 @@
|
||||
# Copyright (c) 2019-2022 Manfred Moitzi
|
||||
# License: MIT License
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
from ezdxf.lldxf import validator
|
||||
from ezdxf.lldxf.attributes import (
|
||||
DXFAttr,
|
||||
DXFAttributes,
|
||||
DefSubclass,
|
||||
XType,
|
||||
RETURN_DEFAULT,
|
||||
group_code_mapping,
|
||||
merge_group_code_mappings,
|
||||
)
|
||||
from ezdxf.lldxf.const import DXF12, SUBCLASS_MARKER, VERTEXNAMES
|
||||
from ezdxf.math import Matrix44, Z_AXIS, NULLVEC, Vec3
|
||||
from ezdxf.math.transformtools import OCSTransform
|
||||
from .dxfentity import base_class, SubclassProcessor
|
||||
from .dxfgfx import (
|
||||
DXFGraphic,
|
||||
acdb_entity,
|
||||
elevation_to_z_axis,
|
||||
acdb_entity_group_codes,
|
||||
)
|
||||
from .factory import register_entity
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ezdxf.lldxf.tagwriter import AbstractTagWriter
|
||||
from ezdxf.entities import DXFNamespace
|
||||
|
||||
__all__ = ["Solid", "Trace", "Face3d"]
|
||||
|
||||
acdb_trace = DefSubclass(
|
||||
"AcDbTrace",
|
||||
{
|
||||
# IMPORTANT: all 4 vertices have to be present in the DXF file,
|
||||
# otherwise AutoCAD shows a DXF structure error and does not load the
|
||||
# file! (SOLID, TRACE and 3DFACE)
|
||||
# 1. corner Solid WCS; Trace OCS
|
||||
"vtx0": DXFAttr(10, xtype=XType.point3d, default=NULLVEC),
|
||||
# 2. corner Solid WCS; Trace OCS
|
||||
"vtx1": DXFAttr(11, xtype=XType.point3d, default=NULLVEC),
|
||||
# 3. corner Solid WCS; Trace OCS
|
||||
"vtx2": DXFAttr(12, xtype=XType.point3d, default=NULLVEC),
|
||||
# 4. corner Solid WCS; Trace OCS:
|
||||
# If only three corners are entered to define the SOLID, then the fourth
|
||||
# corner coordinate is the same as the third.
|
||||
"vtx3": DXFAttr(13, xtype=XType.point3d, default=NULLVEC),
|
||||
# IMPORTANT: for TRACE and SOLID the last two vertices are in reversed
|
||||
# order: a square has the vertex order 0-1-3-2
|
||||
# Elevation is a legacy feature from R11 and prior, do not use this
|
||||
# attribute, store the entity elevation in the z-axis of the vertices.
|
||||
# ezdxf does not export the elevation attribute!
|
||||
"elevation": DXFAttr(38, default=0, optional=True),
|
||||
# Thickness could be negative:
|
||||
"thickness": DXFAttr(39, default=0, optional=True),
|
||||
"extrusion": DXFAttr(
|
||||
210,
|
||||
xtype=XType.point3d,
|
||||
default=Z_AXIS,
|
||||
optional=True,
|
||||
validator=validator.is_not_null_vector,
|
||||
fixer=RETURN_DEFAULT,
|
||||
),
|
||||
},
|
||||
)
|
||||
acdb_trace_group_codes = group_code_mapping(acdb_trace)
|
||||
merged_trace_group_codes = merge_group_code_mappings(
|
||||
acdb_entity_group_codes, acdb_trace_group_codes # type: ignore
|
||||
)
|
||||
|
||||
|
||||
class _Base(DXFGraphic):
|
||||
def __getitem__(self, num):
|
||||
return self.dxf.get(VERTEXNAMES[num])
|
||||
|
||||
def __setitem__(self, num, value):
|
||||
return self.dxf.set(VERTEXNAMES[num], value)
|
||||
|
||||
|
||||
@register_entity
|
||||
class Solid(_Base):
|
||||
"""DXF SHAPE entity"""
|
||||
|
||||
DXFTYPE = "SOLID"
|
||||
DXFATTRIBS = DXFAttributes(base_class, acdb_entity, acdb_trace)
|
||||
|
||||
def load_dxf_attribs(
|
||||
self, processor: Optional[SubclassProcessor] = None
|
||||
) -> DXFNamespace:
|
||||
"""Loading interface. (internal API)"""
|
||||
# bypass DXFGraphic, loading proxy graphic is skipped!
|
||||
dxf = super(DXFGraphic, self).load_dxf_attribs(processor)
|
||||
if processor:
|
||||
processor.simple_dxfattribs_loader(dxf, merged_trace_group_codes)
|
||||
if processor.r12:
|
||||
# Transform elevation attribute from R11 to z-axis values:
|
||||
elevation_to_z_axis(dxf, VERTEXNAMES)
|
||||
return dxf
|
||||
|
||||
def export_entity(self, tagwriter: AbstractTagWriter) -> None:
|
||||
"""Export entity specific data as DXF tags. (internal API)"""
|
||||
super().export_entity(tagwriter)
|
||||
if tagwriter.dxfversion > DXF12:
|
||||
tagwriter.write_tag2(SUBCLASS_MARKER, acdb_trace.name)
|
||||
if not self.dxf.hasattr("vtx3"):
|
||||
self.dxf.vtx3 = self.dxf.vtx2
|
||||
self.dxf.export_dxf_attribs(
|
||||
tagwriter,
|
||||
[
|
||||
"vtx0",
|
||||
"vtx1",
|
||||
"vtx2",
|
||||
"vtx3",
|
||||
"thickness",
|
||||
"extrusion",
|
||||
],
|
||||
)
|
||||
|
||||
def transform(self, m: Matrix44) -> Solid:
|
||||
"""Transform the SOLID/TRACE entity by transformation matrix `m` inplace."""
|
||||
# SOLID and TRACE are OCS entities.
|
||||
dxf = self.dxf
|
||||
ocs = OCSTransform(self.dxf.extrusion, m)
|
||||
for name in VERTEXNAMES:
|
||||
if dxf.hasattr(name):
|
||||
dxf.set(name, ocs.transform_vertex(dxf.get(name)))
|
||||
if dxf.hasattr("thickness"):
|
||||
dxf.thickness = ocs.transform_thickness(dxf.thickness)
|
||||
dxf.extrusion = ocs.new_extrusion
|
||||
self.post_transform(m)
|
||||
return self
|
||||
|
||||
def wcs_vertices(self, close: bool = False) -> list[Vec3]:
|
||||
"""Returns WCS vertices in correct order,
|
||||
if argument `close` is ``True``, last vertex == first vertex.
|
||||
Does **not** return the duplicated last vertex if the entity represents
|
||||
a triangle.
|
||||
|
||||
"""
|
||||
ocs = self.ocs()
|
||||
return list(ocs.points_to_wcs(self.vertices(close)))
|
||||
|
||||
def vertices(self, close: bool = False) -> list[Vec3]:
|
||||
"""Returns OCS vertices in correct order,
|
||||
if argument `close` is ``True``, last vertex == first vertex.
|
||||
Does **not** return the duplicated last vertex if the entity represents
|
||||
a triangle.
|
||||
|
||||
"""
|
||||
dxf = self.dxf
|
||||
vertices: list[Vec3] = [dxf.vtx0, dxf.vtx1, dxf.vtx2]
|
||||
if dxf.vtx3 != dxf.vtx2: # face is not a triangle
|
||||
vertices.append(dxf.vtx3)
|
||||
|
||||
# adjust weird vertex order of SOLID and TRACE:
|
||||
# 0, 1, 2, 3 -> 0, 1, 3, 2
|
||||
if len(vertices) > 3:
|
||||
vertices[2], vertices[3] = vertices[3], vertices[2]
|
||||
|
||||
if close and not vertices[0].isclose(vertices[-1]):
|
||||
vertices.append(vertices[0])
|
||||
return vertices
|
||||
|
||||
|
||||
@register_entity
|
||||
class Trace(Solid):
|
||||
"""DXF TRACE entity"""
|
||||
|
||||
DXFTYPE = "TRACE"
|
||||
|
||||
|
||||
acdb_face = DefSubclass(
|
||||
"AcDbFace",
|
||||
{
|
||||
# 1. corner WCS:
|
||||
"vtx0": DXFAttr(10, xtype=XType.point3d, default=NULLVEC),
|
||||
# 2. corner WCS:
|
||||
"vtx1": DXFAttr(11, xtype=XType.point3d, default=NULLVEC),
|
||||
# 3. corner WCS:
|
||||
"vtx2": DXFAttr(12, xtype=XType.point3d, default=NULLVEC),
|
||||
# 4. corner WCS:
|
||||
# If only three corners are entered to define the SOLID, then the fourth
|
||||
# corner coordinate is the same as the third.
|
||||
"vtx3": DXFAttr(13, xtype=XType.point3d, default=NULLVEC),
|
||||
# invisible:
|
||||
# 1 = First edge is invisible
|
||||
# 2 = Second edge is invisible
|
||||
# 4 = Third edge is invisible
|
||||
# 8 = Fourth edge is invisible
|
||||
"invisible_edges": DXFAttr(70, default=0, optional=True),
|
||||
},
|
||||
)
|
||||
acdb_face_group_codes = group_code_mapping(acdb_face)
|
||||
merged_face_group_codes = merge_group_code_mappings(
|
||||
acdb_entity_group_codes, acdb_face_group_codes # type: ignore
|
||||
)
|
||||
|
||||
|
||||
@register_entity
|
||||
class Face3d(_Base):
|
||||
"""DXF 3DFACE entity"""
|
||||
|
||||
# IMPORTANT: for 3DFACE the last two vertices are in regular order:
|
||||
# a square has the vertex order 0-1-2-3
|
||||
|
||||
DXFTYPE = "3DFACE"
|
||||
DXFATTRIBS = DXFAttributes(base_class, acdb_entity, acdb_face)
|
||||
|
||||
def is_invisible_edge(self, num: int) -> bool:
|
||||
"""Returns True if edge `num` is an invisible edge."""
|
||||
if num < 0 or num > 4:
|
||||
raise ValueError(f"invalid edge: {num}")
|
||||
return bool(self.dxf.invisible_edges & (1 << num))
|
||||
|
||||
def set_edge_visibility(self, num: int, visible: bool = False) -> None:
|
||||
"""Set visibility of edge `num`, status `True` for visible, status
|
||||
`False` for invisible.
|
||||
"""
|
||||
if num < 0 or num >= 4:
|
||||
raise ValueError(f"invalid edge: {num}")
|
||||
if not visible:
|
||||
self.dxf.invisible_edges = self.dxf.invisible_edges | (1 << num)
|
||||
else:
|
||||
self.dxf.invisible_edges = self.dxf.invisible_edges & ~(1 << num)
|
||||
|
||||
def get_edges_visibility(self) -> list[bool]:
|
||||
# if the face is a triangle, a fourth visibility flag
|
||||
# may be present but is ignored
|
||||
return [not self.is_invisible_edge(i) for i in range(4)]
|
||||
|
||||
def load_dxf_attribs(
|
||||
self, processor: Optional[SubclassProcessor] = None
|
||||
) -> DXFNamespace:
|
||||
"""Loading interface. (internal API)"""
|
||||
# bypass DXFGraphic, loading proxy graphic is skipped!
|
||||
dxf = super(DXFGraphic, self).load_dxf_attribs(processor)
|
||||
if processor:
|
||||
processor.simple_dxfattribs_loader(dxf, merged_face_group_codes)
|
||||
return dxf
|
||||
|
||||
def export_entity(self, tagwriter: AbstractTagWriter) -> None:
|
||||
super().export_entity(tagwriter)
|
||||
if tagwriter.dxfversion > DXF12:
|
||||
tagwriter.write_tag2(SUBCLASS_MARKER, acdb_face.name)
|
||||
if not self.dxf.hasattr("vtx3"):
|
||||
self.dxf.vtx3 = self.dxf.vtx2
|
||||
self.dxf.export_dxf_attribs(
|
||||
tagwriter, ["vtx0", "vtx1", "vtx2", "vtx3", "invisible_edges"]
|
||||
)
|
||||
|
||||
def transform(self, m: Matrix44) -> Face3d:
|
||||
"""Transform the 3DFACE entity by transformation matrix `m` inplace."""
|
||||
dxf = self.dxf
|
||||
# 3DFACE is a real 3d entity
|
||||
dxf.vtx0, dxf.vtx1, dxf.vtx2, dxf.vtx3 = m.transform_vertices(
|
||||
(dxf.vtx0, dxf.vtx1, dxf.vtx2, dxf.vtx3)
|
||||
)
|
||||
self.post_transform(m)
|
||||
return self
|
||||
|
||||
def wcs_vertices(self, close: bool = False) -> list[Vec3]:
|
||||
"""Returns WCS vertices, if argument `close` is ``True``,
|
||||
the first vertex is also returned as closing last vertex.
|
||||
|
||||
Returns 4 vertices when `close` is ``False`` and 5 vertices when `close` is
|
||||
``True``. Some edges may have zero-length. This is a compatibility interface
|
||||
to SOLID and TRACE. The 3DFACE entity is already defined by WCS vertices.
|
||||
"""
|
||||
dxf = self.dxf
|
||||
vertices: list[Vec3] = [dxf.vtx0, dxf.vtx1, dxf.vtx2]
|
||||
vtx3 = dxf.get("vtx3")
|
||||
if (
|
||||
isinstance(vtx3, Vec3) and vtx3 != dxf.vtx2
|
||||
): # face is not a triangle
|
||||
vertices.append(vtx3)
|
||||
if close:
|
||||
vertices.append(vertices[0])
|
||||
return vertices
|
||||
Reference in New Issue
Block a user