refactor: excel parse
This commit is contained in:
@@ -0,0 +1,224 @@
|
||||
# Copyright (c) 2022-2024, Manfred Moitzi
|
||||
# License: MIT License
|
||||
from __future__ import annotations
|
||||
from typing import TypeVar, Generic, TYPE_CHECKING, Optional
|
||||
from abc import ABC, abstractmethod
|
||||
from .const import NULL_PTR_NAME, MIN_EXPORT_VERSION
|
||||
from .hdr import AcisHeader
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .entities import AcisEntity
|
||||
from ezdxf.math import Vec3
|
||||
|
||||
|
||||
T = TypeVar("T", bound="AbstractEntity")
|
||||
|
||||
|
||||
class AbstractEntity(ABC):
|
||||
"""Unified query interface for SAT and SAB data."""
|
||||
|
||||
name: str
|
||||
id: int = -1
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.name}"
|
||||
|
||||
@property
|
||||
def is_null_ptr(self) -> bool:
|
||||
"""Returns ``True`` if this entity is the ``NULL_PTR`` entity."""
|
||||
return self.name == NULL_PTR_NAME
|
||||
|
||||
|
||||
class DataLoader(ABC):
|
||||
"""Data loading interface to create high level AcisEntity data from low
|
||||
level AbstractEntity representation.
|
||||
|
||||
"""
|
||||
|
||||
version: int = MIN_EXPORT_VERSION
|
||||
|
||||
@abstractmethod
|
||||
def has_data(self) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def read_int(self, skip_sat: Optional[int] = None) -> int:
|
||||
"""There are sometimes additional int values in SAB files which are
|
||||
not present in SAT files, maybe reference counters e.g. vertex, coedge.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def read_double(self) -> float:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def read_interval(self) -> float:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def read_vec3(self) -> tuple[float, float, float]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def read_bool(self, true: str, false: str) -> bool:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def read_str(self) -> str:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def read_ptr(self) -> AbstractEntity:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def read_transform(self) -> list[float]:
|
||||
pass
|
||||
|
||||
|
||||
class DataExporter(ABC):
|
||||
version: int = MIN_EXPORT_VERSION
|
||||
|
||||
@abstractmethod
|
||||
def write_int(self, value: int, skip_sat=False) -> None:
|
||||
"""There are sometimes additional int values in SAB files which are
|
||||
not present in SAT files, maybe reference counters e.g. vertex, coedge.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def write_double(self, value: float) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def write_interval(self, value: float) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def write_loc_vec3(self, value: Vec3) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def write_dir_vec3(self, value: Vec3) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def write_bool(self, value: bool, true: str, false: str) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def write_str(self, value: str) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def write_literal_str(self, value: str) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def write_ptr(self, entity: AcisEntity) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def write_transform(self, data: list[str]) -> None:
|
||||
pass
|
||||
|
||||
|
||||
class AbstractBuilder(Generic[T]):
|
||||
header: AcisHeader
|
||||
bodies: list[T]
|
||||
entities: list[T]
|
||||
|
||||
def reorder_records(self) -> None:
|
||||
if len(self.entities) == 0:
|
||||
return
|
||||
header: list[T] = []
|
||||
entities: list[T] = []
|
||||
for e in self.entities:
|
||||
if e.name == "body":
|
||||
header.append(e)
|
||||
elif e.name == "asmheader":
|
||||
header.insert(0, e) # has to be the first record
|
||||
else:
|
||||
entities.append(e)
|
||||
self.entities = header + entities
|
||||
|
||||
def reset_ids(self, start: int = 0) -> None:
|
||||
for num, entity in enumerate(self.entities, start=start):
|
||||
entity.id = num
|
||||
|
||||
def clear_ids(self) -> None:
|
||||
for entity in self.entities:
|
||||
entity.id = -1
|
||||
|
||||
|
||||
class EntityExporter(Generic[T]):
|
||||
def __init__(self, header: AcisHeader):
|
||||
self.header = header
|
||||
self.version = header.version
|
||||
self._exported_entities: dict[int, T] = {}
|
||||
if self.header.has_asm_header:
|
||||
self.export(self.header.asm_header())
|
||||
|
||||
def export_records(self) -> list[T]:
|
||||
return list(self._exported_entities.values())
|
||||
|
||||
@abstractmethod
|
||||
def make_record(self, entity: AcisEntity) -> T:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def make_data_exporter(self, record: T) -> DataExporter:
|
||||
pass
|
||||
|
||||
def get_record(self, entity: AcisEntity) -> T:
|
||||
assert not entity.is_none
|
||||
return self._exported_entities[id(entity)]
|
||||
|
||||
def export(self, entity: AcisEntity):
|
||||
if entity.is_none:
|
||||
raise TypeError("invalid NONE_REF entity given")
|
||||
self._make_all_records(entity)
|
||||
self._export_data(entity)
|
||||
|
||||
def _has_record(self, entity: AcisEntity) -> bool:
|
||||
return id(entity) in self._exported_entities
|
||||
|
||||
def _add_record(self, entity: AcisEntity, record: T) -> None:
|
||||
assert not entity.is_none
|
||||
self._exported_entities[id(entity)] = record
|
||||
|
||||
def _make_all_records(self, entity: AcisEntity):
|
||||
def add(e: AcisEntity) -> bool:
|
||||
if not e.is_none and not self._has_record(e):
|
||||
self._add_record(e, self.make_record(e))
|
||||
return True
|
||||
return False
|
||||
|
||||
entities = [entity]
|
||||
while entities:
|
||||
next_entity = entities.pop(0)
|
||||
add(next_entity)
|
||||
for sub_entity in next_entity.entities():
|
||||
if add(sub_entity):
|
||||
entities.append(sub_entity)
|
||||
|
||||
def _export_data(self, entity: AcisEntity):
|
||||
def _export_record(e: AcisEntity):
|
||||
if id(e) not in done:
|
||||
done.add(id(e))
|
||||
record = self.get_record(e)
|
||||
if not e.attributes.is_none:
|
||||
record.attributes = self.get_record(e.attributes) # type: ignore
|
||||
e.export(self.make_data_exporter(record))
|
||||
return True
|
||||
return False
|
||||
|
||||
entities = [entity]
|
||||
done: set[int] = set()
|
||||
while entities:
|
||||
next_entity = entities.pop(0)
|
||||
_export_record(next_entity)
|
||||
for sub_entity in next_entity.entities():
|
||||
if _export_record(sub_entity):
|
||||
entities.append(sub_entity)
|
||||
Reference in New Issue
Block a user