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

325 lines
11 KiB
Python

# Copyright (c) 2011-2022, Manfred Moitzi
# License: MIT License
from __future__ import annotations
from typing import TYPE_CHECKING, Iterator, Iterable, Union, cast, Optional
from collections import Counter, OrderedDict
import logging
from ezdxf.lldxf.const import DXFStructureError, DXF2004, DXF2000, DXFKeyError
from ezdxf.entities.dxfclass import DXFClass
from ezdxf.entities.dxfentity import DXFEntity, DXFTagStorage
if TYPE_CHECKING:
from ezdxf.document import Drawing
from ezdxf.lldxf.tagwriter import AbstractTagWriter
logger = logging.getLogger("ezdxf")
# name: cpp_class_name (2), app_name (3), flags(90), was_a_proxy (280),
# is_an_entity (281)
# Multiple entries for 'name' are possible and supported, ClassSection stores
# entries with key: (name, cpp_class_name).
# 0 <ctrl> CLASS
# 1 <str> MPOLYGON
# 2 <str> AcDbMPolygon
# 3 <str> "AcMPolygonObj15|Version(1.0.0.0) Product Desc: Object enabler for the AcDbMPolyg ... odesk.com"
# 90 <int> 3071, b101111111111
# 280 <int> 0
# 281 <int> 1
CLASS_DEFINITIONS = {
"ACDBDICTIONARYWDFLT": [
"AcDbDictionaryWithDefault",
"ObjectDBX Classes",
0,
0,
0,
],
"SUN": ["AcDbSun", "SCENEOE", 1153, 0, 0],
"DICTIONARYVAR": ["AcDbDictionaryVar", "ObjectDBX Classes", 0, 0, 0],
"TABLESTYLE": ["AcDbTableStyle", "ObjectDBX Classes", 4095, 0, 0],
"MATERIAL": ["AcDbMaterial", "ObjectDBX Classes", 1153, 0, 0],
"VISUALSTYLE": ["AcDbVisualStyle", "ObjectDBX Classes", 4095, 0, 0],
"SCALE": ["AcDbScale", "ObjectDBX Classes", 1153, 0, 0],
"MLEADERSTYLE": ["AcDbMLeaderStyle", "ACDB_MLEADERSTYLE_CLASS", 4095, 0, 0],
"MLEADER": ["AcDbMLeader", "ACDB_MLEADER_CLASS", 3071, 0, 1],
"MPOLYGON": ["AcDbMPolygon", "AcMPolygonObj15", 1025, 0, 1],
"CELLSTYLEMAP": ["AcDbCellStyleMap", "ObjectDBX Classes", 1152, 0, 0],
"EXACXREFPANELOBJECT": ["ExAcXREFPanelObject", "EXAC_ESW", 1025, 0, 0],
"NPOCOLLECTION": [
"AcDbImpNonPersistentObjectsCollection",
"ObjectDBX Classes",
1153,
0,
0,
],
"LAYER_INDEX": ["AcDbLayerIndex", "ObjectDBX Classes", 0, 0, 0],
"SPATIAL_INDEX": ["AcDbSpatialIndex", "ObjectDBX Classes", 0, 0, 0],
"IDBUFFER": ["AcDbIdBuffer", "ObjectDBX Classes", 0, 0, 0],
"DIMASSOC": ["AcDbDimAssoc", "AcDbDimAssoc", 0, 0, 0],
"ACDBSECTIONVIEWSTYLE": [
"AcDbSectionViewStyle",
"ObjectDBX Classes",
1025,
0,
0,
],
"ACDBDETAILVIEWSTYLE": [
"AcDbDetailViewStyle",
"ObjectDBX Classes",
1025,
0,
0,
],
"IMAGEDEF": ["AcDbRasterImageDef", "ISM", 0, 0, 0],
"RASTERVARIABLES": ["AcDbRasterVariables", "ISM", 0, 0, 0],
"IMAGEDEF_REACTOR": ["AcDbRasterImageDefReactor", "ISM", 1, 0, 0],
"IMAGE": ["AcDbRasterImage", "ISM", 2175, 0, 1],
"PDFDEFINITION": ["AcDbPdfDefinition", "ObjectDBX Classes", 1153, 0, 0],
"PDFUNDERLAY": ["AcDbPdfReference", "ObjectDBX Classes", 4095, 0, 1],
"DWFDEFINITION": ["AcDbDwfDefinition", "ObjectDBX Classes", 1153, 0, 0],
"DWFUNDERLAY": ["AcDbDwfReference", "ObjectDBX Classes", 1153, 0, 1],
"DGNDEFINITION": ["AcDbDgnDefinition", "ObjectDBX Classes", 1153, 0, 0],
"DGNUNDERLAY": ["AcDbDgnReference", "ObjectDBX Classes", 1153, 0, 1],
"MENTALRAYRENDERSETTINGS": [
"AcDbMentalRayRenderSettings",
"SCENEOE",
1024,
0,
0,
],
"ACDBPLACEHOLDER": ["AcDbPlaceHolder", "ObjectDBX Classes", 0, 0, 0],
"LAYOUT": ["AcDbLayout", "ObjectDBX Classes", 0, 0, 0],
"SURFACE": ["AcDbSurface", "ObjectDBX Classes", 4095, 0, 1],
"EXTRUDEDSURFACE": ["AcDbExtrudedSurface", "ObjectDBX Classes", 4095, 0, 1],
"LOFTEDSURFACE": ["AcDbLoftedSurface", "ObjectDBX Classes", 0, 0, 1],
"REVOLVEDSURFACE": ["AcDbRevolvedSurface", "ObjectDBX Classes", 0, 0, 1],
"SWEPTSURFACE": ["AcDbSweptSurface", "ObjectDBX Classes", 0, 0, 1],
"PLANESURFACE": ["AcDbPlaneSurface", "ObjectDBX Classes", 4095, 0, 1],
"NURBSSURFACE": ["AcDbNurbSurface", "ObjectDBX Classes", 4095, 0, 1],
"ACDBASSOCEXTRUDEDSURFACEACTIONBODY": [
"AcDbAssocExtrudedSurfaceActionBody",
"ObjectDBX Classes",
1025,
0,
0,
],
"ACDBASSOCLOFTEDSURFACEACTIONBODY": [
"AcDbAssocLoftedSurfaceActionBody",
"ObjectDBX Classes",
1025,
0,
0,
],
"ACDBASSOCREVOLVEDSURFACEACTIONBODY": [
"AcDbAssocRevolvedSurfaceActionBody",
"ObjectDBX Classes",
1025,
0,
0,
],
"ACDBASSOCSWEPTSURFACEACTIONBODY": [
"AcDbAssocSweptSurfaceActionBody",
"ObjectDBX Classes",
1025,
0,
0,
],
"HELIX": ["AcDbHelix", "ObjectDBX Classes", 4095, 0, 1],
"WIPEOUT": ["AcDbWipeout", "WipeOut", 127, 0, 1],
"WIPEOUTVARIABLES": ["AcDbWipeoutVariables", "WipeOut", 0, 0, 0],
"FIELDLIST": ["AcDbFieldList", "ObjectDBX Classes", 1152, 0, 0],
"GEODATA": ["AcDbGeoData", "ObjectDBX Classes", 4095, 0, 0],
"SORTENTSTABLE": ["AcDbSortentsTable", "ObjectDBX Classes", 0, 0, 0],
"ACAD_TABLE": ["AcDbTable", "ObjectDBX Classes", 1025, 0, 1],
"ARC_DIMENSION": ["AcDbArcDimension", "ObjectDBX Classes", 1025, 0, 1],
"LARGE_RADIAL_DIMENSION": [
"AcDbRadialDimensionLarge",
"ObjectDBX Classes",
1025,
0,
1,
],
}
REQ_R2000 = [
"ACDBDICTIONARYWDFLT",
"SUN",
"VISUALSTYLE",
"MATERIAL",
"SCALE",
"TABLESTYLE",
"MLEADERSTYLE",
"DICTIONARYVAR",
"CELLSTYLEMAP",
"MENTALRAYRENDERSETTINGS",
"ACDBDETAILVIEWSTYLE",
"ACDBSECTIONVIEWSTYLE",
"RASTERVARIABLES",
"ACDBPLACEHOLDER",
"LAYOUT",
]
REQ_R2004 = [
"ACDBDICTIONARYWDFLT",
"SUN",
"VISUALSTYLE",
"MATERIAL",
"SCALE",
"TABLESTYLE",
"MLEADERSTYLE",
"DICTIONARYVAR",
"CELLSTYLEMAP",
"MENTALRAYRENDERSETTINGS",
"ACDBDETAILVIEWSTYLE",
"ACDBSECTIONVIEWSTYLE",
"RASTERVARIABLES",
]
REQUIRED_CLASSES = {
DXF2000: REQ_R2000,
DXF2004: REQ_R2004,
}
class ClassesSection:
def __init__(
self,
doc: Optional[Drawing] = None,
entities: Optional[Iterable[DXFEntity]] = None,
):
# Multiple entries for 'name' possible -> key is (name, cpp_class_name)
# DXFClasses are not stored in the entities database, because CLASS has
# no handle.
self.classes: dict[tuple[str, str], DXFClass] = OrderedDict()
self.doc = doc
if entities is not None:
self.load(iter(entities))
def __iter__(self) -> Iterator[DXFClass]:
return (cls for cls in self.classes.values())
def load(self, entities: Iterator[DXFEntity]) -> None:
section_head = cast(DXFTagStorage, next(entities))
if section_head.dxftype() != "SECTION" or section_head.base_class[1] != (
2,
"CLASSES",
):
raise DXFStructureError("Critical structure error in CLASSES section.")
for cls_entity in entities:
if isinstance(cls_entity, DXFClass):
self.register(cls_entity)
else:
logger.warning(
f"Ignored invalid DXF entity type '{cls_entity.dxftype()}'"
f" in section CLASSES."
)
def register(
self, classes: Optional[Union[DXFClass, Iterable[DXFClass]]] = None
) -> None:
if classes is None:
return
if isinstance(classes, DXFClass):
classes = (classes,)
for dxfclass in classes:
key = dxfclass.key
if key not in self.classes:
self.classes[key] = dxfclass
def add_class(self, name: str):
"""Register a known class by `name`."""
if name not in CLASS_DEFINITIONS:
return
cls_data = CLASS_DEFINITIONS[name]
cls = DXFClass.new(doc=self.doc)
cpp, app, flags, proxy, entity = cls_data
cls.update_dxf_attribs(
{
"name": name,
"cpp_class_name": cpp,
"app_name": app,
"flags": flags,
"was_a_proxy": proxy,
"is_an_entity": entity,
}
)
self.register(cls)
def get(self, name: str) -> DXFClass:
"""Returns the first class matching `name`.
Storage key is the ``(name, cpp_class_name)`` tuple, because there are
some classes with the same :attr:`name` but different
:attr:`cpp_class_names`.
"""
for cls in self.classes.values():
if cls.dxf.name == name:
return cls
raise DXFKeyError(name)
def add_required_classes(self, dxfversion: str) -> None:
"""Add all required CLASS definitions for the specified DXF version."""
names = REQUIRED_CLASSES.get(dxfversion, REQ_R2004)
for name in names:
self.add_class(name)
if self.doc is None: # testing environment SUT
return
dxf_types_in_use = self.doc.entitydb.dxf_types_in_use()
if "IMAGE" in dxf_types_in_use:
self.add_class("IMAGE")
self.add_class("IMAGEDEF")
self.add_class("IMAGEDEF_REACTOR")
if "PDFUNDERLAY" in dxf_types_in_use:
self.add_class("PDFDEFINITION")
self.add_class("PDFUNDERLAY")
if "DWFUNDERLAY" in dxf_types_in_use:
self.add_class("DWFDEFINITION")
self.add_class("DWFUNDERLAY")
if "DGNUNDERLAY" in dxf_types_in_use:
self.add_class("DGNDEFINITION")
self.add_class("DGNUNDERLAY")
if "EXTRUDEDSURFACE" in dxf_types_in_use:
self.add_class("EXTRUDEDSURFACE")
self.add_class("ACDBASSOCEXTRUDEDSURFACEACTIONBODY")
if "LOFTEDSURFACE" in dxf_types_in_use:
self.add_class("LOFTEDSURFACE")
self.add_class("ACDBASSOCLOFTEDSURFACEACTIONBODY")
if "REVOLVEDSURFACE" in dxf_types_in_use:
self.add_class("REVOLVEDSURFACE")
self.add_class("ACDBASSOCREVOLVEDSURFACEACTIONBODY")
if "SWEPTSURFACE" in dxf_types_in_use:
self.add_class("SWEPTSURFACE")
self.add_class("ACDBASSOCSWEPTSURFACEACTIONBODY")
for dxftype in dxf_types_in_use:
self.add_class(dxftype)
def export_dxf(self, tagwriter: AbstractTagWriter) -> None:
"""Export DXF tags. (internal API)"""
tagwriter.write_str(" 0\nSECTION\n 2\nCLASSES\n")
for dxfclass in self.classes.values():
dxfclass.export_dxf(tagwriter)
tagwriter.write_str(" 0\nENDSEC\n")
def update_instance_counters(self) -> None:
"""Update CLASS instance counter for all registered classes, requires
DXF R2004+.
"""
assert self.doc is not None
if self.doc.dxfversion < DXF2004:
return # instance counter not supported
counter: dict[str, int] = Counter()
# count all entities in the entity database
for entity in self.doc.entitydb.values():
counter[entity.dxftype()] += 1
for dxfclass in self.classes.values():
dxfclass.dxf.instance_count = counter[dxfclass.dxf.name]