image.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. # Created: 07.03.2016
  2. # Copyright (c) 2016-2018, Manfred Moitzi
  3. # License: MIT License
  4. from typing import Iterable, Tuple, Sequence, List, TYPE_CHECKING, cast
  5. from ezdxf.dxfentity import DXFEntity
  6. from ezdxf.lldxf.attributes import DXFAttr, DXFAttributes, DefSubclass, XType
  7. from ezdxf.lldxf.types import DXFVertex
  8. from ezdxf.lldxf.tags import Tags
  9. from ezdxf.lldxf.extendedtags import ExtendedTags
  10. from ezdxf.math import Vector
  11. from .graphics import none_subclass, entity_subclass, ModernGraphicEntity
  12. if TYPE_CHECKING:
  13. from ezdxf.eztypes import Vertex
  14. _IMAGE_CLS = """0
  15. CLASS
  16. 1
  17. IMAGE
  18. 2
  19. AcDbRasterImage
  20. 3
  21. ISM
  22. 90
  23. 2175
  24. 91
  25. 0
  26. 280
  27. 0
  28. 281
  29. 1
  30. """
  31. _IMAGE_TPL = """0
  32. IMAGE
  33. 5
  34. 0
  35. 330
  36. 0
  37. 100
  38. AcDbEntity
  39. 8
  40. 0
  41. 100
  42. AcDbRasterImage
  43. 90
  44. 0
  45. 10
  46. 0.0
  47. 20
  48. 0.0
  49. 30
  50. 0.0
  51. 11
  52. 0.0
  53. 21
  54. 0.0
  55. 31
  56. 0.0
  57. 12
  58. 0.0
  59. 22
  60. 0.0
  61. 32
  62. 0.0
  63. 13
  64. 640
  65. 23
  66. 320
  67. 340
  68. 0
  69. 70
  70. 3
  71. 280
  72. 0
  73. 281
  74. 50
  75. 282
  76. 50
  77. 283
  78. 0
  79. 360
  80. 0
  81. 71
  82. 1
  83. 91
  84. 2
  85. """
  86. image_subclass = DefSubclass('AcDbRasterImage', {
  87. 'insert': DXFAttr(10, xtype=XType.point3d),
  88. 'u_pixel': DXFAttr(11, xtype=XType.point3d), # U-vector of a single pixel (points along the visual bottom of the image, starting at the insertion point)
  89. 'v_pixel': DXFAttr(12, xtype=XType.point3d), # V2-vector of a single pixel (points along the visual left side of the image, starting at the insertion point)
  90. 'image_size': DXFAttr(13, xtype=XType.point2d), # Image size in pixels
  91. 'image_def': DXFAttr(340), # Hard reference to image def object
  92. 'flags': DXFAttr(70, default=3), # Image display properties:
  93. # 1 = Show image
  94. # 2 = Show image when not aligned with screen
  95. # 4 = Use clipping boundary
  96. # 8 = Transparency is on
  97. 'clipping': DXFAttr(280, default=0), # Clipping state: 0 = Off; 1 = On
  98. 'brightness': DXFAttr(281, default=50), # Brightness value (0-100; default = 50)
  99. 'contrast': DXFAttr(282, default=50), # Contrast value (0-100; default = 50)
  100. 'fade': DXFAttr(283, default=0), # Fade value (0-100; default = 0)
  101. 'image_def_reactor': DXFAttr(360), # Hard reference to image def reactor object, not required by AutoCAD
  102. 'clipping_boundary_type': DXFAttr(71, default=1), # Clipping boundary type. 1 = Rectangular; 2 = Polygonal
  103. 'count_boundary_points': DXFAttr(91), # Number of clip boundary vertices that follow
  104. 'clip_mode': DXFAttr(290, dxfversion='AC1024'), # 0 = outside, 1 = inside mode
  105. # boundary path coordinates are pixel coordinates NOT drawing units
  106. })
  107. class Image(ModernGraphicEntity):
  108. __slots__ = ()
  109. TEMPLATE = ExtendedTags.from_text(_IMAGE_TPL)
  110. CLASS = ExtendedTags.from_text(_IMAGE_CLS)
  111. DXFATTRIBS = DXFAttributes(none_subclass, entity_subclass, image_subclass)
  112. # flags for IMAGE
  113. SHOW_IMAGE = 1
  114. SHOW_IMAGE_WHEN_NOT_ALIGNED = 2
  115. USE_CLIPPING_BOUNDARY = 4
  116. USE_TRANSPARENCY = 8
  117. def post_new_hook(self) -> None:
  118. self.reset_boundary_path()
  119. def set_boundary_path(self, vertices: Iterable[Tuple[float, float]]) -> None:
  120. vertices = list(vertices)
  121. if len(vertices) > 2 and vertices[-1] != vertices[0]:
  122. vertices.append(vertices[0]) # close path, else AutoCAD crashes
  123. self._set_path_tags(vertices)
  124. self.set_flag_state(self.USE_CLIPPING_BOUNDARY, state=True)
  125. self.dxf.clipping = 1
  126. self.dxf.clipping_boundary_type = 1 if len(vertices) < 3 else 2
  127. def _set_path_tags(self, vertices: Sequence[Tuple[float, float]]):
  128. boundary = [DXFVertex(14, value) for value in vertices]
  129. subclasstags = Tags(tag for tag in self.tags.subclasses[2] if tag.code != 14)
  130. subclasstags.extend(boundary)
  131. self.tags.subclasses[2] = subclasstags
  132. self.dxf.count_boundary_points = len(vertices)
  133. def reset_boundary_path(self) -> None:
  134. lower_left_corner = (-.5, -.5)
  135. upper_right_corner = Vector(self.dxf.image_size) + lower_left_corner
  136. self._set_path_tags([lower_left_corner, upper_right_corner[:2]])
  137. self.set_flag_state(Image.USE_CLIPPING_BOUNDARY, state=False)
  138. self.dxf.clipping = 0
  139. self.dxf.clipping_boundary_type = 1
  140. def get_boundary_path(self) -> List['Vertex']:
  141. image_subclass = self.tags.subclasses[2]
  142. return [tag.value for tag in image_subclass if tag.code == 14]
  143. def get_image_def(self) -> 'ImageDef':
  144. return cast('ImageDef', self.dxffactory.wrap_handle(self.dxf.image_def))
  145. def destroy(self) -> None:
  146. super(Image, self).destroy()
  147. # remove rectors
  148. image_def = self.get_image_def()
  149. reactor_handle = self.get_dxf_attrib('image_def_reactor', None)
  150. if reactor_handle is None:
  151. return
  152. image_def.remove_reactor_handle(reactor_handle)
  153. if self.drawing is not None:
  154. reactor = self.dxffactory.wrap_handle(reactor_handle)
  155. self.drawing.objects.delete_entity(reactor)
  156. _IMAGE_DEF_CLS = """0
  157. CLASS
  158. 1
  159. IMAGEDEF
  160. 2
  161. AcDbRasterImageDef
  162. 3
  163. ISM
  164. 90
  165. 0
  166. 91
  167. 0
  168. 280
  169. 0
  170. 281
  171. 0
  172. """
  173. _IMAGE_DEF_TPL = """0
  174. IMAGEDEF
  175. 5
  176. 0
  177. 330
  178. 0
  179. 102
  180. {ACAD_REACTORS
  181. 102
  182. }
  183. 100
  184. AcDbRasterImageDef
  185. 90
  186. 0
  187. 1
  188. path/filename.jpg
  189. 10
  190. 640
  191. 20
  192. 480
  193. 11
  194. 0.01
  195. 21
  196. 0.01
  197. 280
  198. 1
  199. 281
  200. 0
  201. """
  202. image_def_subclass = DefSubclass('AcDbRasterImageDef', {
  203. 'class_version': DXFAttr(90), # class version
  204. 'filename': DXFAttr(1), # File name of image
  205. 'image_size': DXFAttr(10, xtype=XType.point2d), # image size in pixels
  206. 'pixel_size': DXFAttr(11, xtype=XType.point2d), # Default size of one pixel in AutoCAD units
  207. 'loaded': DXFAttr(280, default=1),
  208. 'resolution_units': DXFAttr(281, default=0), # Resolution units. 0 = No units; 2 = Centimeters; 5 = Inch
  209. })
  210. # IMAGEDEF - requires entry in objects table ACAD_IMAGE_DICT, ACAD_IMAGE_DICT exists not by default
  211. class ImageDef(DXFEntity):
  212. TEMPLATE = ExtendedTags.from_text(_IMAGE_DEF_TPL)
  213. CLASS = ExtendedTags.from_text(_IMAGE_DEF_CLS)
  214. DXFATTRIBS = DXFAttributes(none_subclass, image_def_subclass)
  215. _IMAGE_DEF_REACTOR_CLS = """0
  216. CLASS
  217. 1
  218. IMAGEDEF_REACTOR
  219. 2
  220. AcDbRasterImageDefReactor
  221. 3
  222. ISM
  223. 90
  224. 1
  225. 91
  226. 0
  227. 280
  228. 0
  229. 281
  230. 0
  231. """
  232. _IMAGE_DEF_REACTOR_TPL = """0
  233. IMAGEDEF_REACTOR
  234. 5
  235. 0
  236. 330
  237. 0
  238. 100
  239. AcDbRasterImageDefReactor
  240. 90
  241. 2
  242. 330
  243. 0
  244. """
  245. # IMAGEDEF_REACTOR is not required by AutoCAD
  246. # owner -> IMAGE
  247. class ImageDefReactor(DXFEntity):
  248. TEMPLATE = ExtendedTags.from_text(_IMAGE_DEF_REACTOR_TPL)
  249. CLASS = ExtendedTags.from_text(_IMAGE_DEF_REACTOR_CLS)
  250. DXFATTRIBS = DXFAttributes(none_subclass, DefSubclass('AcDbRasterImageDef', {
  251. 'image': DXFAttr(330), # handle to image
  252. }))
  253. _RASTER_VARIABLES_CLS = """0
  254. CLASS
  255. 1
  256. RASTERVARIABLES
  257. 2
  258. AcDbRasterVariables
  259. 3
  260. ISM
  261. 90
  262. 0
  263. 91
  264. 0
  265. 280
  266. 0
  267. 281
  268. 0
  269. """
  270. _RASTER_VARIABLES_TPL = """0
  271. RASTERVARIABLES
  272. 5
  273. 0
  274. 102
  275. {ACAD_REACTORS
  276. 330
  277. 0
  278. 102
  279. }
  280. 330
  281. 0
  282. 100
  283. AcDbRasterVariables
  284. 90
  285. 0
  286. 70
  287. 0
  288. 71
  289. 1
  290. 72
  291. 3
  292. """
  293. class RasterVariables(DXFEntity):
  294. TEMPLATE = ExtendedTags.from_text(_RASTER_VARIABLES_TPL)
  295. CLASS = ExtendedTags.from_text(_RASTER_VARIABLES_CLS)
  296. DXFATTRIBS = DXFAttributes(
  297. none_subclass,
  298. DefSubclass('AcDbRasterVariables', {
  299. 'version': DXFAttr(90, default=0),
  300. 'frame': DXFAttr(70, default=0), # 0 = no frame; 1= show frame
  301. 'quality': DXFAttr(71, default=1), # 0=draft; 1=high
  302. 'units': DXFAttr(72, default=3), # 0 = None; 1 = mm; 2 = cm 3 = m; 4 = km; 5 = in 6 = ft; 7 = yd; 8 = mi
  303. }),
  304. )