solid3d.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. # Created: 24.05.2015
  2. # Copyright (c) 2015-2018, Manfred Moitzi
  3. # License: MIT License
  4. from typing import Iterable
  5. from contextlib import contextmanager
  6. from ezdxf.lldxf.types import DXFTag
  7. from ezdxf.lldxf.extendedtags import ExtendedTags
  8. from ezdxf.lldxf.attributes import DXFAttr, DXFAttributes, DefSubclass
  9. from ezdxf.tools import crypt
  10. from .graphics import none_subclass, entity_subclass, ModernGraphicEntity
  11. _BODY_TPL = """0
  12. BODY
  13. 5
  14. 0
  15. 330
  16. 0
  17. 100
  18. AcDbEntity
  19. 8
  20. 0
  21. 100
  22. AcDbModelerGeometry
  23. 70
  24. 1
  25. """
  26. modeler_geometry_subclass = DefSubclass('AcDbModelerGeometry', {
  27. 'version': DXFAttr(70, default=1),
  28. })
  29. def convert_tags_to_text_lines(line_tags: Iterable[DXFTag]) -> Iterable[str]:
  30. """
  31. Args:
  32. line_tags: tags with code 1 or 3, tag with code 3 is the tail of previous line with more than 255 chars.
  33. Returns: yield strings
  34. """
  35. line_tags = iter(line_tags)
  36. try:
  37. line = next(line_tags).value # raises StopIteration
  38. except StopIteration:
  39. return
  40. while True:
  41. try:
  42. tag = next(line_tags)
  43. except StopIteration:
  44. if line:
  45. yield line
  46. return
  47. if tag.code == 3:
  48. line += tag.value
  49. continue
  50. yield line
  51. line = tag.value
  52. def convert_text_lines_to_tags(text_lines: Iterable[str]) -> Iterable[DXFTag]:
  53. for line in text_lines:
  54. yield DXFTag(1, line[:255])
  55. if len(line) > 255:
  56. yield DXFTag(3, line[255:]) # tail (max. 255 chars), what if line > 510 chars???
  57. class Body(ModernGraphicEntity):
  58. __slots__ = ()
  59. TEMPLATE = ExtendedTags.from_text(_BODY_TPL)
  60. DXFATTRIBS = DXFAttributes(none_subclass, entity_subclass, modeler_geometry_subclass)
  61. def get_acis_data(self) -> Iterable[str]:
  62. modeler_geometry = self.tags.subclasses[2]
  63. text_lines = convert_tags_to_text_lines(tag for tag in modeler_geometry if tag.code in (1, 3))
  64. return crypt.decode(text_lines)
  65. def set_acis_data(self, text_lines: Iterable[str]) -> None:
  66. def cleanup(lines):
  67. for line in lines:
  68. yield line.rstrip().replace('\n', '')
  69. modeler_geometry = self.tags.subclasses[2]
  70. # remove existing text
  71. modeler_geometry[:] = (tag for tag in modeler_geometry if tag.code not in (1, 3))
  72. modeler_geometry.extend(convert_text_lines_to_tags(crypt.encode(cleanup(text_lines))))
  73. @contextmanager
  74. def edit_data(self) -> 'ModelerGeometryData':
  75. data = ModelerGeometryData(self)
  76. yield data
  77. self.set_acis_data(data.text_lines)
  78. class ModelerGeometryData:
  79. def __init__(self, body: 'Body'):
  80. self.text_lines = list(body.get_acis_data())
  81. def __str__(self) -> str:
  82. return "\n".join(self.text_lines)
  83. def set_text(self, text: str, sep: str = '\n') -> None:
  84. self.text_lines = text.split(sep)
  85. class Region(Body):
  86. __slots__ = ()
  87. TEMPLATE = ExtendedTags.from_text(_BODY_TPL.replace('BODY', 'REGION'))
  88. _3DSOLID_TPL = """0
  89. 3DSOLID
  90. 5
  91. 0
  92. 330
  93. 0
  94. 100
  95. AcDbEntity
  96. 8
  97. 0
  98. 100
  99. AcDbModelerGeometry
  100. 70
  101. 1
  102. 100
  103. AcDb3dSolid
  104. 350
  105. 0
  106. """
  107. class Solid3d(Body):
  108. __slots__ = ()
  109. TEMPLATE = ExtendedTags.from_text(_3DSOLID_TPL)
  110. DXFATTRIBS = DXFAttributes(
  111. none_subclass,
  112. entity_subclass,
  113. modeler_geometry_subclass,
  114. DefSubclass('AcDb3dSolid', {'history': DXFAttr(350, default=0)})
  115. )