idbuffer.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. # Created: 12.04.2018
  2. # Copyright (c) 2018, Manfred Moitzi
  3. # License: MIT-License
  4. from typing import TYPE_CHECKING, Iterable, Union, Any, cast
  5. import array
  6. from ezdxf.lldxf.types import DXFTag
  7. from ezdxf.lldxf.packedtags import PackedTags, replace_tags
  8. from ezdxf.lldxf import loader
  9. from .dxfobjects import DXFObject, DefSubclass, DXFAttributes, none_subclass, ExtendedTags
  10. if TYPE_CHECKING:
  11. from ezdxf.eztypes import Tags
  12. def convert(values: Iterable[Union[str, int]]) -> Iterable[int]:
  13. for value in values:
  14. yield int(value, 16) if isinstance(value, str) else value
  15. def new_array(values: Iterable[Union[str, int]] = None) -> array.array:
  16. a = array.array('Q')
  17. if values is not None:
  18. a.extend(convert(values))
  19. return a
  20. class PackedHandles(PackedTags):
  21. code = -330 # compatible with DXFTag.code
  22. __slots__ = ('value',)
  23. def __init__(self, handles: Iterable[str] = None):
  24. # compatible with DXFTag.value
  25. self.value = new_array(handles) # type: array.array
  26. def __len__(self) -> int:
  27. return len(self.value)
  28. def __getitem__(self, item: Union[slice, int]) -> Union[str, Iterable[str]]:
  29. if isinstance(item, slice):
  30. return ['%X' % v for v in self.value[item]]
  31. else:
  32. return '%X' % self.value[item]
  33. def __setitem__(self, item: int, value: Any) -> None:
  34. if isinstance(value, (tuple, list)):
  35. value = new_array(value)
  36. else:
  37. value = int(value, 16)
  38. self.value[item] = value
  39. def __delitem__(self, item: int) -> None:
  40. del self.value[item]
  41. def __iadd__(self, value: str) -> 'PackedHandles':
  42. self.append(value)
  43. return self
  44. def __eq__(self, other: 'PackedHandles') -> bool:
  45. return self.value == new_array(other)
  46. def append(self, handle: str) -> None:
  47. self.value.append(int(handle, 16))
  48. def extend(self, handles: Iterable[str]) -> None:
  49. self.value.extend(convert(handles))
  50. def dxftags(self) -> Iterable[DXFTag]:
  51. for handle in self.value:
  52. yield DXFTag(330, "%X" % handle)
  53. def clone(self) -> 'PackedHandles':
  54. return self.__class__(handles=self.value)
  55. def clear(self) -> None:
  56. del self.value[:]
  57. @loader.register('IDBUFFER', legacy=False)
  58. def tag_processor(tags: ExtendedTags):
  59. subclass = tags.get_subclass('AcDbIdBuffer')
  60. id_buffer = PackedHandles(handles=(tag.value for tag in subclass[1:]))
  61. replace_tags(subclass, codes=(330,), packed_data=id_buffer)
  62. return tags
  63. _IDBUFFER_TPL = """0
  64. IDBUFFER
  65. 5
  66. 0
  67. 102
  68. {ACAD_REACTORS
  69. 330
  70. 0
  71. 102
  72. }
  73. 330
  74. 0
  75. 100
  76. AcDbIdBuffer
  77. """
  78. class IDBuffer(DXFObject):
  79. __slots__ = ('_cached_handles',)
  80. TEMPLATE = tag_processor(ExtendedTags.from_text(_IDBUFFER_TPL))
  81. DXFATTRIBS = DXFAttributes(none_subclass, DefSubclass('AcDbIdBuffer', {}))
  82. @property
  83. def buffer_subclass(self) -> 'Tags':
  84. return self.tags.subclasses[1] # 2nd subclass
  85. @property
  86. def handles(self) -> PackedHandles:
  87. try:
  88. return self._cached_handles
  89. except AttributeError:
  90. self._cached_handles = cast(PackedHandles, self.buffer_subclass.get_first_tag(PackedHandles.code))
  91. return self._cached_handles
  92. @handles.setter
  93. def handles(self, items: Iterable[str]) -> None:
  94. self.handles[:] = list(items)