graphicsfactory.py 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095
  1. # Created: 10.03.2013
  2. # Copyright (c) 2013-2018, Manfred Moitzi
  3. # License: MIT License
  4. from typing import TYPE_CHECKING, Iterable, Sequence, Union, Dict, Tuple, cast
  5. import math
  6. import logging
  7. from ezdxf.lldxf import const
  8. from ezdxf.lldxf.const import DXFValueError, DXFVersionError
  9. from ezdxf.math import Vector
  10. from ezdxf.math import bspline_control_frame, bspline_control_frame_approx
  11. from ezdxf.render.arrows import ARROWS
  12. from ezdxf.dimstyleoverride import DimStyleOverride
  13. from ezdxf.render.dimension import multi_point_linear_dimension
  14. logger = logging.getLogger('ezdxf')
  15. if TYPE_CHECKING: # import forward references
  16. from ezdxf.eztypes import DXFFactoryType, DXFEntity, Spline, Text, ImageDef, Image, Line, Point, Circle, Arc, Shape
  17. from ezdxf.eztypes import Solid, Trace, Face, Insert, Attrib, Polyline, Polyface, Polymesh, UnderlayDef, Underlay
  18. from ezdxf.eztypes import Hatch, Mesh, LWPolyline, Ellipse, MText, Ray, XLine, Dimension, DimStyleOverride
  19. from ezdxf.eztypes import Solid3d, Region, Body, Surface, RevolvedSurface, ExtrudedSurface, SweptSurface, \
  20. LoftedSurface
  21. from ezdxf.eztypes import UCS, GenericLayoutType, Vertex
  22. def copy_attribs(dxfattribs: dict = None) -> dict:
  23. return dict(dxfattribs or {})
  24. class GraphicsFactory:
  25. """ Abstract base class for BaseLayout() """
  26. def __init__(self, dxffactory: 'DXFFactoryType'):
  27. self._dxffactory = dxffactory
  28. @property
  29. def dxfversion(self) -> str:
  30. return self._dxffactory.dxfversion
  31. def build_and_add_entity(self, type_: str, dxfattribs: dict):
  32. raise NotImplementedError("Abstract method call.")
  33. def add_point(self, location: 'Vertex', dxfattribs: dict = None) -> 'Point':
  34. """
  35. Add a :class:`Point` element at `location`.
  36. Args:
  37. location: 2D/3D point in :ref:`WCS`
  38. dxfattribs (dist): additional DXF attributes for :class:`Point` entity
  39. Returns: :class:`Point`
  40. """
  41. dxfattribs = copy_attribs(dxfattribs)
  42. dxfattribs['location'] = location
  43. return self.build_and_add_entity('POINT', dxfattribs)
  44. def add_line(self, start: 'Vertex', end: 'Vertex', dxfattribs: dict = None) -> 'Line':
  45. """
  46. Add a :class:`Line` element from `start` to `end`.
  47. Args:
  48. start: 2D/3D point in :ref:`WCS`
  49. end: 2D/3D point in :ref:`WCS`
  50. dxfattribs (dict): additional DXF attributes for :class:`Line` entity
  51. Returns: :class:`Line`
  52. """
  53. dxfattribs = copy_attribs(dxfattribs)
  54. dxfattribs['start'] = start
  55. dxfattribs['end'] = end
  56. return self.build_and_add_entity('LINE', dxfattribs)
  57. def add_circle(self, center: 'Vertex', radius: float, dxfattribs: dict = None) -> 'Circle':
  58. """
  59. Add a :class:`Circle` element. This is an 2D element, which can be placed in space by using :ref:`OCS`.
  60. Args:
  61. center: 2D/3D point in :ref:`WCS`
  62. radius: circle radius
  63. dxfattribs (dcit): additional DXF attributes for :class:`Circle` entity
  64. Returns: :class:`Circle`
  65. """
  66. dxfattribs = copy_attribs(dxfattribs)
  67. dxfattribs['center'] = center
  68. dxfattribs['radius'] = radius
  69. return self.build_and_add_entity('CIRCLE', dxfattribs)
  70. def add_ellipse(self, center: 'Vertex', major_axis: 'Vertex' = (1, 0, 0), ratio: float = 1, start_param: float = 0,
  71. end_param: float = 2*math.pi, dxfattribs: dict = None) -> 'Ellipse':
  72. """
  73. Add an :class:`Ellipse` element, `ratio` is the ratio of minor axis to major axis, `start_param` and `end_param`
  74. defines start and end point of the ellipse, a full ellipse goes from 0 to 2*pi. The ellipse goes from start to
  75. end param in `counter clockwise` direction.
  76. Args:
  77. center: center of ellipse as 2D/3D point in :ref:`WCS`
  78. major_axis: major axis as vector (x, y, z)
  79. ratio: ratio of minor axis to major axis
  80. start_param: start of ellipse curve
  81. end_param: end param of ellipse curve
  82. dxfattribs (dict): additional DXF attributes for :class:`Ellipse` entity
  83. Returns: :class:`Ellipse`
  84. """
  85. if self.dxfversion < 'AC1015':
  86. raise DXFVersionError('ELLIPSE requires DXF version R2000+')
  87. if ratio > 1.:
  88. raise DXFValueError("Parameter 'ratio' has to be <= 1.0")
  89. dxfattribs = copy_attribs(dxfattribs)
  90. dxfattribs['center'] = center
  91. dxfattribs['major_axis'] = major_axis
  92. dxfattribs['ratio'] = ratio
  93. dxfattribs['start_param'] = start_param
  94. dxfattribs['end_param'] = end_param
  95. return self.build_and_add_entity('ELLIPSE', dxfattribs)
  96. def add_arc(self, center: 'Vertex', radius: float, start_angle: float, end_angle: float,
  97. is_counter_clockwise: bool = True, dxfattribs: dict = None) -> 'Arc':
  98. """
  99. Add an :class:`Arc` element. The arc goes from `start_angle` to `end_angle` in counter clockwise
  100. direction by default, set parameter `is_counter_clockwise` to False for clockwise orientation.
  101. Args:
  102. center: center of arc as 2D/3D point in :ref:`WCS`
  103. radius: arc radius
  104. start_angle: start angle in degrees
  105. end_angle: end angle in degrees
  106. is_counter_clockwise: False for clockwise orientation
  107. dxfattribs (dict): additional DXF attributes for :class:`Arc` entity
  108. Returns: :class:`Arc`
  109. """
  110. dxfattribs = copy_attribs(dxfattribs)
  111. dxfattribs['center'] = center
  112. dxfattribs['radius'] = radius
  113. if is_counter_clockwise:
  114. dxfattribs['start_angle'] = start_angle
  115. dxfattribs['end_angle'] = end_angle
  116. else:
  117. dxfattribs['start_angle'] = end_angle
  118. dxfattribs['end_angle'] = start_angle
  119. return self.build_and_add_entity('ARC', dxfattribs)
  120. def add_solid(self, points: Iterable['Vertex'], dxfattribs: dict = None) -> 'Solid':
  121. """
  122. Add a :class:`Solid` element, `points` is an iterable of 3 or 4 points.
  123. Args:
  124. points: iterable of 3 or 4 2D/3D points in :ref:`WCS`
  125. dxfattribs (dict): additional DXF attributes for :class:`Solid` entity
  126. Returns: :class:`Solid`
  127. """
  128. return cast('Solid', self._add_quadrilateral('SOLID', points, dxfattribs))
  129. def add_trace(self, points: Iterable['Vertex'], dxfattribs: dict = None) -> 'Trace':
  130. """
  131. Add a :class:`Trace` element, `points` is an iterable of 3 or 4 points.
  132. Args:
  133. points: iterable of 3 or 4 2D/3D points in :ref:`WCS`
  134. dxfattribs (dict): additional DXF attributes for :class:`Trace` entity
  135. Returns: :class:`Trace`
  136. """
  137. return cast('Trace', self._add_quadrilateral('TRACE', points, dxfattribs))
  138. def add_3dface(self, points: Iterable['Vertex'], dxfattribs: dict = None) -> 'Face':
  139. """
  140. Add a :class:`3DFace` element, `points` is an iterable 3 or 4 2D/3D points.
  141. Args:
  142. points: iterable of 3 or 4 2D/3D points in :ref:`WCS`
  143. dxfattribs (dict): additional DXF attributes for :class:`3DFace` entity
  144. Returns: :class:`3DFace`
  145. """
  146. return cast('Face', self._add_quadrilateral('3DFACE', points, dxfattribs))
  147. def add_text(self, text: str, dxfattribs: dict = None) -> 'Text':
  148. """
  149. Add a :class:`Text` element, see also :class:`Style`.
  150. Args:
  151. text: content string
  152. dxfattribs (dict): additional DXF attributes for :class:`Text` entity
  153. Returns: :class:`Text`
  154. """
  155. dxfattribs = copy_attribs(dxfattribs)
  156. dxfattribs['text'] = text
  157. dxfattribs.setdefault('insert', (0, 0))
  158. return self.build_and_add_entity('TEXT', dxfattribs)
  159. def add_blockref(self, name: str, insert: 'Vertex', dxfattribs: dict = None) -> 'Insert':
  160. """
  161. Add an :class:`Insert` element.
  162. Args:
  163. name: block name
  164. insert: insert location as 2D/3D point in :ref:`WCS`
  165. dxfattribs (dict): additional DXF attributes for :class:`Insert` entity
  166. Returns: :class:`Insert`
  167. """
  168. dxfattribs = copy_attribs(dxfattribs)
  169. dxfattribs['name'] = name
  170. dxfattribs['insert'] = insert
  171. blockref = self.build_and_add_entity('INSERT', dxfattribs)
  172. return blockref
  173. def add_auto_blockref(self, name: str, insert: 'Vertex', values: Dict[str, str], dxfattribs: dict = None) \
  174. -> 'Insert':
  175. """
  176. Add an :class:`Insert` element. This method adds for each :class:`Attdef` entity, defined in the block
  177. definition, automatically an :class:`Attrib` entity to the block reference and set tag/value DXF attributes of
  178. the Attrib entities. The `values` dict defines the tag/value attributes as key=tag, value=tag value as string.
  179. The Attrib entities are placed relative to the insert point, which is equal to the block base point.
  180. Args:
  181. name: block name
  182. insert: insert location as 2D/3D point in :ref:`WCS`
  183. values (dict): :class:`Attrib` tag values as key=tag, value=tag value pairs
  184. dxfattribs (dict): additional DXF attributes for :class:`Insert` entity
  185. Returns: :class:`Insert`
  186. """
  187. def get_dxfattribs(attdef) -> dict:
  188. dxfattribs = attdef.dxfattribs()
  189. dxfattribs.pop('prompt', None)
  190. dxfattribs.pop('handle', None)
  191. return dxfattribs
  192. def unpack(dxfattribs) -> Tuple[str, str, 'Vertex']:
  193. tag = dxfattribs.pop('tag')
  194. text = values.get(tag, "")
  195. insert = dxfattribs.pop('insert')
  196. return tag, text, insert
  197. def autofill(blockref, blockdef) -> None:
  198. # ATTRIBs are placed relative to the base point
  199. for attdef in blockdef.attdefs():
  200. dxfattribs = get_dxfattribs(attdef)
  201. tag, text, insert = unpack(dxfattribs)
  202. blockref.add_attrib(tag, text, insert, dxfattribs)
  203. dxfattribs = copy_attribs(dxfattribs)
  204. autoblock = self._dxffactory.blocks.new_anonymous_block()
  205. blockref = autoblock.add_blockref(name, (0, 0))
  206. blockdef = self._dxffactory.blocks[name]
  207. autofill(blockref, blockdef)
  208. return self.add_blockref(autoblock.name, insert, dxfattribs)
  209. def add_attrib(self, tag: str, text: str, insert: 'Vertex' = (0, 0), dxfattribs: dict = None) -> 'Attrib':
  210. """
  211. Add an :class:`Attrib` as stand alone DXF entity.
  212. Args:
  213. tag: tag name as string
  214. text: tag value as string
  215. insert: insert location as 2D/3D point in :ref:`WCS`
  216. dxfattribs (dict): additional DXF attributes for :class:`Attrib` entity
  217. Returns: :class:`Attrib`
  218. """
  219. dxfattribs = copy_attribs(dxfattribs)
  220. dxfattribs['tag'] = tag
  221. dxfattribs['text'] = text
  222. dxfattribs['insert'] = insert
  223. return self.build_and_add_entity('ATTRIB', dxfattribs)
  224. def add_polyline2d(self, points: Iterable['Vertex'], dxfattribs: dict = None) -> 'Polyline':
  225. """
  226. Add a 2D :class:`Polyline` entity.
  227. Args:
  228. points: iterable of 2D points in :ref:`WCS`
  229. dxfattribs (dict): additional DXF attributes for :class:`Polyline` entity
  230. Returns: :class:`Polyline`
  231. """
  232. dxfattribs = copy_attribs(dxfattribs)
  233. closed = dxfattribs.pop('closed', False)
  234. polyline = self.build_and_add_entity('POLYLINE', dxfattribs)
  235. polyline.close(closed)
  236. polyline.append_vertices(points)
  237. return polyline
  238. def add_polyline3d(self, points: Iterable['Vertex'], dxfattribs: dict = None) -> 'Polyline':
  239. """
  240. Add a 3D :class:`Polyline` entity.
  241. Args:
  242. points: iterable of 3D points in :ref:`WCS`
  243. dxfattribs (dict): additional DXF attributes for :class:`Polyline` entity
  244. Returns: :class:`Polyline`
  245. """
  246. dxfattribs = copy_attribs(dxfattribs)
  247. dxfattribs['flags'] = dxfattribs.get('flags', 0) | const.POLYLINE_3D_POLYLINE
  248. return self.add_polyline2d(points, dxfattribs)
  249. def add_polymesh(self, size: Tuple[int, int] = (3, 3), dxfattribs: dict = None) -> 'Polymesh':
  250. """
  251. Add a :class:`Polymesh` entity, which is represented as :class:`Polyline` entity in the DXF file.
  252. A polymesh is a grid of `mcount` x `ncount` vertices and every vertex has its own xyz-coordinates.
  253. Args:
  254. size: 2-tuple (`mcount`, `ncount`)
  255. dxfattribs (dict): additional DXF attributes for :class:`Polyline` entity
  256. Returns: :class:`Polymesh`
  257. """
  258. dxfattribs = copy_attribs(dxfattribs)
  259. dxfattribs['flags'] = dxfattribs.get('flags', 0) | const.POLYLINE_3D_POLYMESH
  260. m_size = max(size[0], 2)
  261. n_size = max(size[1], 2)
  262. dxfattribs['m_count'] = m_size
  263. dxfattribs['n_count'] = n_size
  264. m_close = dxfattribs.pop('m_close', False)
  265. n_close = dxfattribs.pop('n_close', False)
  266. polymesh = self.build_and_add_entity('POLYLINE', dxfattribs)
  267. points = [(0, 0, 0)] * (m_size * n_size)
  268. polymesh.append_vertices(points) # init mesh vertices
  269. polymesh.close(m_close, n_close)
  270. return polymesh.cast()
  271. def add_polyface(self, dxfattribs: dict = None) -> 'Polyface':
  272. """
  273. Add a :class:`Polyface` entity, which is represented as :class:`Polyline` entity in the DXF file.
  274. Args:
  275. dxfattribs (dict): additional DXF attributes for :class:`Polyline` entity
  276. Returns: :class:`Polyface`
  277. """
  278. dxfattribs = copy_attribs(dxfattribs)
  279. dxfattribs['flags'] = dxfattribs.get('flags', 0) | const.POLYLINE_POLYFACE
  280. m_close = dxfattribs.pop('m_close', False)
  281. n_close = dxfattribs.pop('n_close', False)
  282. polyface = self.build_and_add_entity('POLYLINE', dxfattribs)
  283. polyface.close(m_close, n_close)
  284. return polyface.cast()
  285. def _add_quadrilateral(self, type_: str, points: Iterable['Vertex'], dxfattribs: dict = None) -> 'DXFEntity':
  286. dxfattribs = copy_attribs(dxfattribs)
  287. entity = self.build_and_add_entity(type_, dxfattribs)
  288. for x, point in enumerate(self._four_points(points)):
  289. entity[x] = point
  290. return entity
  291. @staticmethod
  292. def _four_points(points: Iterable['Vertex']) -> Iterable['Vertex']:
  293. vertices = list(points)
  294. if len(vertices) not in (3, 4):
  295. raise DXFValueError('3 or 4 points required.')
  296. for vertex in vertices:
  297. yield vertex
  298. if len(vertices) == 3:
  299. yield vertices[-1] # last again
  300. def add_shape(self, name: str, insert: 'Vertex' = (0, 0), size: float = 1.0, dxfattribs: dict = None) -> 'Shape':
  301. """
  302. Add a :class:`Shape` reference to a external stored shape.
  303. Args:
  304. name: shape name as string
  305. insert: insert location as 2D/3D point in :ref:`WCS`
  306. size: size factor
  307. dxfattribs (dict): additional DXF attributes for :class:`Shape` entity
  308. Returns: :class:`Shape`
  309. """
  310. dxfattribs = copy_attribs(dxfattribs)
  311. dxfattribs['name'] = name
  312. dxfattribs['insert'] = insert
  313. dxfattribs['size'] = size
  314. return self.build_and_add_entity('SHAPE', dxfattribs)
  315. # new entities in DXF AC1015 (R2000)
  316. def add_lwpolyline(self, points: Iterable['Vertex'], format: str = 'xyseb', dxfattribs: dict = None) -> 'LWPolyline':
  317. """
  318. Add a 2D polyline as :class:`LWPolyline` entity. A points are defined as (x, y, [start_width, [end_width,
  319. [bulge]]]) tuples, but order can be redefined by the `format` argument. Set start_width, end_width to 0 to be
  320. ignored (x, y, 0, 0, bulge).
  321. The :class:`LWPolyline` is defined as a single DXF entity and needs less disk space and memory than a
  322. :class:`Polyline` entity. (requires DXF R2000+)
  323. Args:
  324. points: iterable of (x, y, [start_width, [end_width, [bulge]]]) tuples
  325. format: user defined point format, default is "xyseb"
  326. dxfattribs (dict): additional DXF attributes for :class:`LWPolyline` entity
  327. Returns: :class:`LWPolyline`
  328. """
  329. if self.dxfversion < 'AC1015':
  330. raise DXFVersionError('LWPOLYLINE requires DXF version R2000+')
  331. dxfattribs = copy_attribs(dxfattribs)
  332. closed = dxfattribs.pop('closed', False)
  333. lwpolyline = self.build_and_add_entity('LWPOLYLINE', dxfattribs)
  334. lwpolyline.set_points(points, format=format)
  335. lwpolyline.closed = closed
  336. return lwpolyline
  337. def add_mtext(self, text: str, dxfattribs: dict = None) -> 'MText':
  338. """
  339. Add a multiline text element with automatic text wrapping at boundaries as :class:`MText` entity. (requires
  340. DXF R2000+)
  341. Args:
  342. text: content string
  343. dxfattribs (dict): additional DXF attributes for :class:`MText` entity
  344. Returns: :class:`MText`
  345. """
  346. if self.dxfversion < 'AC1015':
  347. raise DXFVersionError('MTEXT requires DXF version R2000+')
  348. dxfattribs = copy_attribs(dxfattribs)
  349. mtext = self.build_and_add_entity('MTEXT', dxfattribs)
  350. mtext.set_text(text)
  351. return mtext
  352. def add_ray(self, start: 'Vertex', unit_vector: 'Vertex', dxfattribs: dict = None) -> 'Ray':
  353. """
  354. Add a :class:`Ray` that begins at `start` point and continues to infinity (construction line).
  355. (requires DXF R2000+)
  356. Args:
  357. start: location 3D point in :ref:`WCS`
  358. unit_vector: 3D vector (x, y, z)
  359. dxfattribs (dict): additional DXF attributes for :class:`Ray` entity
  360. Returns: :class:`Ray`
  361. """
  362. if self.dxfversion < 'AC1015':
  363. raise DXFVersionError('RAY requires DXF version R2000+')
  364. dxfattribs = copy_attribs(dxfattribs)
  365. dxfattribs['start'] = start
  366. dxfattribs['unit_vector'] = unit_vector
  367. return self.build_and_add_entity('RAY', dxfattribs)
  368. def add_xline(self, start: 'Vertex', unit_vector: 'Vertex', dxfattribs: dict = None) -> 'XLine':
  369. """
  370. Add an infinity :class:`XLine` (construction line).
  371. (requires DXF R2000+)
  372. Args:
  373. start: location 3D point in :ref:`WCS`
  374. unit_vector: 3D vector (x, y, z)
  375. dxfattribs (dict): additional DXF attributes for :class:`XLine` entity
  376. Returns: :class:`XLine`
  377. """
  378. if self.dxfversion < 'AC1015':
  379. raise DXFVersionError('XLINE requires DXF version R2000+')
  380. dxfattribs = copy_attribs(dxfattribs)
  381. dxfattribs['start'] = start
  382. dxfattribs['unit_vector'] = unit_vector
  383. return self.build_and_add_entity('XLINE', dxfattribs)
  384. def add_spline(self, fit_points: Iterable['Vertex'] = None, degree: int = 3, dxfattribs: dict = None) -> 'Spline':
  385. """
  386. Add a B-spline defined by fit points, the control points and knot values are created by the CAD application,
  387. therefore it is not predictable how the rendered spline will look like, because for every set of fit points
  388. exists an infinite set of B-splines. If fit_points is None, an 'empty' spline will be created, all data has to
  389. be set by the user. (requires DXF R2000+)
  390. AutoCAD creates a spline through fit points by a proprietary algorithm. `ezdxf` can not reproduce the control
  391. point calculation.
  392. Args:
  393. fit_points: iterable of fit points as (x, y[, z]) in :ref:`WCS`, if None -> user defined spline
  394. degree: degree fo B-spline
  395. dxfattribs (dict): additional DXF attributes for :class:`Spline` entity
  396. Returns: :class:`Spline`
  397. """
  398. if self.dxfversion < 'AC1015':
  399. raise DXFVersionError('SPLINE requires DXF version R2000+')
  400. dxfattribs = copy_attribs(dxfattribs)
  401. dxfattribs['degree'] = degree
  402. spline = self.build_and_add_entity('SPLINE', dxfattribs)
  403. if fit_points is not None:
  404. spline.set_fit_points(list(fit_points))
  405. return spline
  406. def add_spline_control_frame(self, fit_points: Iterable['Vertex'], degree: int = 3, method: str = 'distance',
  407. power: float = .5, dxfattribs: dict = None) -> 'Spline':
  408. """
  409. Create and add B-spline control frame from fit points.
  410. 1. method = "uniform", creates a uniform t vector, [0...1] equally spaced
  411. 2. method = "distance", creates a t vector with values proportional to the fit point distances
  412. 3. method = "centripetal", creates a t vector with values proportional to the fit point distances^power
  413. None of this methods matches the spline created from fit points by AutoCAD.
  414. Args:
  415. fit_points: iterable of fit points as (x, y[, z]) in :ref:`WCS`
  416. degree: degree of B-spline
  417. method: calculation method for parameter vector t
  418. power: power for centripetal method
  419. dxfattribs (dict): additional DXF attributes for :class:`Spline` entity
  420. Returns: :class:`Spline`
  421. """
  422. bspline = bspline_control_frame(fit_points, degree=degree, method=method, power=power)
  423. return self.add_open_spline(
  424. control_points=bspline.control_points,
  425. degree=bspline.degree,
  426. knots=bspline.knot_values(),
  427. dxfattribs=dxfattribs,
  428. )
  429. def add_spline_approx(self, fit_points: Iterable['Vertex'], count: int, degree: int = 3, method: str = 'distance',
  430. power: float = .5, dxfattribs: dict = None) -> 'Spline':
  431. """
  432. Approximate B-spline by a reduced count of control points, given are the fit points and the degree of the B-spline.
  433. 1. method = 'uniform', creates a uniform t vector, [0...1] equally spaced
  434. 2. method = 'distance', creates a t vector with values proportional to the fit point distances
  435. 3. method = 'centripetal', creates a t vector with values proportional to the fit point distances^power
  436. (requires DXF R2000+)
  437. Args:
  438. fit_points: all fit points of B-spline
  439. count: count of designated control points
  440. degree: degree of B-spline
  441. method: calculation method for parameter vector t
  442. power: power for centripetal method
  443. dxfattribs (dict): additional DXF attributes for :class:`Spline` entity
  444. Returns: :class:`Spline`
  445. """
  446. bspline = bspline_control_frame_approx(fit_points, count, degree=degree, method=method, power=power)
  447. return self.add_open_spline(
  448. control_points=bspline.control_points,
  449. degree=bspline.degree,
  450. dxfattribs=dxfattribs,
  451. )
  452. def add_open_spline(self, control_points: Iterable['Vertex'], degree: int = 3, knots: Iterable[float] = None,
  453. dxfattribs: dict = None) -> 'Spline':
  454. """
  455. Add an open uniform :class:`Spline` defined by `control_points`. (requires DXF R2000+)
  456. Open uniform B-splines start and end at your first and last control point.
  457. Args:
  458. control_points: iterable of 3D points in :ref:`WCS`
  459. degree: degree of B-spline
  460. knots: knot values as iterable of floats
  461. dxfattribs (dict): additional DXF attributes for :class:`Spline` entity
  462. Returns: :class:`Spline`
  463. """
  464. spline = self.add_spline(dxfattribs=dxfattribs)
  465. spline.set_open_uniform(list(control_points), degree)
  466. if knots is not None:
  467. spline.set_knot_values(list(knots))
  468. return spline
  469. def add_closed_spline(self, control_points: Iterable['Vertex'], degree: int = 3, knots: Iterable[float] = None,
  470. dxfattribs: dict = None) -> 'Spline':
  471. """
  472. Add a closed uniform :class:`Spline` defined by `control_points`. (requires DXF R2000+)
  473. Closed uniform B-splines is a closed curve start and end at the first control point.
  474. Args:
  475. control_points: iterable of 3D points in :ref:`WCS`
  476. degree: degree of B-spline
  477. knots: knot values as iterable of floats
  478. dxfattribs (dict): additional DXF attributes for :class:`Spline` entity
  479. Returns: :class:`Spline`
  480. """
  481. spline = self.add_spline(dxfattribs=dxfattribs)
  482. spline.set_periodic(list(control_points), degree)
  483. if knots is not None:
  484. spline.set_knot_values(list(knots))
  485. return spline
  486. def add_rational_spline(self, control_points: Iterable['Vertex'], weights: Sequence[float], degree: int = 3,
  487. knots: Iterable[float] = None, dxfattribs: dict = None) -> 'Spline':
  488. """
  489. Add an open rational uniform :class:`Spline` defined by `control_points`. (requires DXF R2000+)
  490. `weights` has to be an iterable of floats, which defines the influence of the associated control point to the
  491. shape of the B-spline, therefore for each control point is one weight value required.
  492. Open rational uniform B-splines start and end at the first and last control point.
  493. Args:
  494. control_points: iterable of 3D points in :ref:`WCS`
  495. weights: weight values as iterable of floats
  496. degree: degree of B-spline
  497. knots: knot values as iterable of floats
  498. dxfattribs (dict): additional DXF attributes for :class:`Spline` entity
  499. Returns: :class:`Spline`
  500. """
  501. spline = self.add_spline(dxfattribs=dxfattribs)
  502. spline.set_open_rational(list(control_points), weights, degree)
  503. if knots is not None:
  504. spline.set_knot_values(list(knots))
  505. return spline
  506. def add_closed_rational_spline(self, control_points: Iterable['Vertex'], weights: Sequence[float], degree: int = 3,
  507. knots: Iterable[float] = None, dxfattribs: dict = None) -> 'Spline':
  508. """
  509. Add a closed rational uniform :class:`Spline` defined by `control_points`. (requires DXF R2000+)
  510. `weights` has to be an iterable of floats, which defines the influence of the associated control point to the
  511. shape of the B-spline, therefore for each control point is one weight value required.
  512. Closed rational uniform B-splines start and end at the first control point.
  513. Args:
  514. control_points: iterable of 3D points in :ref:`WCS`
  515. weights: weight values as iterable of floats
  516. degree: degree of B-spline
  517. knots: knot values as iterable of floats
  518. dxfattribs (dict): additional DXF attributes for :class:`Spline` entity
  519. Returns: :class:`Spline`
  520. """
  521. spline = self.add_spline(dxfattribs=dxfattribs)
  522. spline.set_periodic_rational(list(control_points), weights, degree)
  523. if knots is not None:
  524. spline.set_knot_values(list(knots))
  525. return spline
  526. def add_body(self, acis_data: str = None, dxfattribs: dict = None) -> 'Body':
  527. """
  528. Add a :class:`Body` entity. (requires DXF R2000+)
  529. Args:
  530. acis_data: ACIS data as iterable of text lines as strings, no interpretation by ezdxf possible
  531. dxfattribs (dict): additional DXF attributes for :class:`Body` entity
  532. Returns: :class:`Body`
  533. """
  534. return self._add_acis_entiy('BODY', acis_data, dxfattribs)
  535. def add_region(self, acis_data: str = None, dxfattribs: dict = None) -> 'Region':
  536. """
  537. Add a :class:`Region` entity. (requires DXF R2000+)
  538. Args:
  539. acis_data: ACIS data as iterable of text lines as strings, no interpretation by ezdxf possible
  540. dxfattribs (dict): additional DXF attributes for :class:`Region` entity
  541. Returns: :class:`Region`
  542. """
  543. return cast('Region', self._add_acis_entiy('REGION', acis_data, dxfattribs))
  544. def add_3dsolid(self, acis_data: str = None, dxfattribs: dict = None) -> 'Solid3d':
  545. """
  546. Add a :class:`3DSolid` entity. (requires DXF R2000+)
  547. Args:
  548. acis_data: ACIS data as iterable of text lines as strings, no interpretation by ezdxf possible
  549. dxfattribs (dict): additional DXF attributes for :class:`3DSolid` entity
  550. Returns: :class:`3DSolid`
  551. """
  552. return cast('Solid3d', self._add_acis_entiy('3DSOLID', acis_data, dxfattribs))
  553. def add_surface(self, acis_data: str = None, dxfattribs: dict = None) -> 'Surface':
  554. """
  555. Add a :class:`Surface` entity. (requires DXF R2007+)
  556. Args:
  557. acis_data: ACIS data as iterable of text lines as strings, no interpretation by ezdxf possible
  558. dxfattribs (dict): additional DXF attributes for :class:`Surface` entity
  559. Returns: :class:`Surface`
  560. """
  561. if self.dxfversion < 'AC1021':
  562. raise DXFVersionError('SURFACE requires DXF version R2007+')
  563. return cast('Surface', self._add_acis_entiy('SURFACE', acis_data, dxfattribs))
  564. def add_extruded_surface(self, acis_data: str = None, dxfattribs: dict = None) -> 'ExtrudedSurface':
  565. """
  566. Add a :class:`ExtrudedSurface` entity. (requires DXF R2007+)
  567. Args:
  568. acis_data: ACIS data as iterable of text lines as strings, no interpretation by ezdxf possible
  569. dxfattribs (dict): additional DXF attributes for :class:`ExtrudedSurface` entity
  570. Returns: :class:`ExtrudedSurface`
  571. """
  572. if self.dxfversion < 'AC1021':
  573. raise DXFVersionError('EXTRUDEDSURFACE requires DXF version R2007+')
  574. return cast('ExtrudedSurface', self._add_acis_entiy('EXTRUDEDSURFACE', acis_data, dxfattribs))
  575. def add_lofted_surface(self, acis_data: str = None, dxfattribs: dict = None) -> 'LoftedSurface':
  576. """
  577. Add a :class:`LoftedSurface` entity. (requires DXF R2007+)
  578. Args:
  579. acis_data: ACIS data as iterable of text lines as strings, no interpretation by ezdxf possible
  580. dxfattribs (dict): additional DXF attributes for :class:`LoftedSurface` entity
  581. Returns: :class:`LoftedSurface`
  582. """
  583. if self.dxfversion < 'AC1021':
  584. raise DXFVersionError('LOFTEDSURFACE requires DXF version R2007+')
  585. return cast('LoftedSurface', self._add_acis_entiy('LOFTEDSURFACE', acis_data, dxfattribs))
  586. def add_revolved_surface(self, acis_data: str = None, dxfattribs: dict = None) -> 'RevolvedSurface':
  587. """
  588. Add a :class:`RevolvedSurface` entity. (requires DXF R2007+)
  589. Args:
  590. acis_data: ACIS data as iterable of text lines as strings, no interpretation by ezdxf possible
  591. dxfattribs (dict): additional DXF attributes for :class:`RevolvedSurface` entity
  592. Returns: :class:`RevolvedSurface`
  593. """
  594. if self.dxfversion < 'AC1021':
  595. raise DXFVersionError('REVOLVEDSURFACE requires DXF version R2007+')
  596. return cast('RevolvedSurface', self._add_acis_entiy('REVOLVEDSURFACE', acis_data, dxfattribs))
  597. def add_swept_surface(self, acis_data: str = None, dxfattribs: dict = None) -> 'SweptSurface':
  598. """
  599. Add a :class:`SweptSurface` entity. (requires DXF R2007+)
  600. Args:
  601. acis_data: ACIS data as iterable of text lines as strings, no interpretation by ezdxf possible
  602. dxfattribs (dict): additional DXF attributes for :class:`SweptSurface` entity
  603. Returns: :class:`SweptSurface`
  604. """
  605. if self.dxfversion < 'AC1021':
  606. raise DXFVersionError('SWEPT requires DXF version R2007+')
  607. return cast('SweptSurface', self._add_acis_entiy('SWEPTSURFACE', acis_data, dxfattribs))
  608. def _add_acis_entiy(self, name, acis_data: str, dxfattribs: dict) -> 'Body':
  609. if self.dxfversion < 'AC1015':
  610. raise DXFVersionError('{} requires DXF version R2000+'.format(name))
  611. dxfattribs = copy_attribs(dxfattribs)
  612. entity = self.build_and_add_entity(name, dxfattribs)
  613. if acis_data is not None:
  614. entity.set_acis_data(acis_data)
  615. return entity
  616. def add_hatch(self, color: int = 7, dxfattribs: dict = None) -> 'Hatch':
  617. """
  618. Add a :class:`Hatch` entity. (requires DXF R2007+)
  619. Args:
  620. color: ACI (AutoCAD Color Index), default is 7 (black/white).
  621. dxfattribs (dict): additional DXF attributes for :class:`Hatch` entity
  622. Returns: :class:`Hatch`
  623. """
  624. if self.dxfversion < 'AC1015':
  625. raise DXFVersionError('HATCH requires DXF version R2000+')
  626. dxfattribs = copy_attribs(dxfattribs)
  627. dxfattribs['solid_fill'] = 1
  628. dxfattribs['color'] = color
  629. dxfattribs['pattern_name'] = 'SOLID'
  630. return self.build_and_add_entity('HATCH', dxfattribs)
  631. def add_mesh(self, dxfattribs: dict = None) -> 'Mesh':
  632. """
  633. Add a :class:`Mesh` entity. (requires DXF R2007+)
  634. Args:
  635. dxfattribs (dict): additional DXF attributes for :class:`Mesh` entity
  636. Returns: :class:`Mesh`
  637. """
  638. if self.dxfversion < 'AC1015':
  639. raise DXFVersionError('MESH requires DXF version R2000+')
  640. dxfattribs = copy_attribs(dxfattribs)
  641. return self.build_and_add_entity('MESH', dxfattribs)
  642. def add_image(self, image_def: 'ImageDef', insert: 'Vertex', size_in_units: Tuple[float, float], rotation: float = 0.,
  643. dxfattribs: dict = None) -> 'Image':
  644. """
  645. Add an :class:`Image` entity. Create :class:`ImageDef` by the :class:`Drawing` factory function
  646. :meth:`~Drawing.add_image_def`, see :ref:`tut_image`. (requires DXF R2000+)
  647. Args:
  648. image_def: required image definition as :class:`ImageDef`
  649. insert: insertion point as 3D point in :ref:`WCS`
  650. size_in_units: size as (x, y) tuple in drawing units
  651. rotation: rotation angle around the z-axis in degrees
  652. dxfattribs (dict): additional DXF attributes for :class:`Image` entity
  653. Returns: :class:`Image`
  654. """
  655. def to_vector(units_per_pixel, angle_in_rad):
  656. x = math.cos(angle_in_rad) * units_per_pixel
  657. y = math.sin(angle_in_rad) * units_per_pixel
  658. return round(x, 6), round(y, 6), 0 # supports only images in the xy-plane
  659. if self.dxfversion < 'AC1015':
  660. raise DXFVersionError('IMAGE requires DXF version R2000+')
  661. dxfattribs = copy_attribs(dxfattribs)
  662. x_pixels, y_pixels = image_def.dxf.image_size
  663. x_units, y_units = size_in_units
  664. x_units_per_pixel = x_units / x_pixels
  665. y_units_per_pixel = y_units / y_pixels
  666. x_angle_rad = math.radians(rotation)
  667. y_angle_rad = x_angle_rad + (math.pi / 2.)
  668. dxfattribs['insert'] = Vector(insert)
  669. dxfattribs['u_pixel'] = to_vector(x_units_per_pixel, x_angle_rad)
  670. dxfattribs['v_pixel'] = to_vector(y_units_per_pixel, y_angle_rad)
  671. dxfattribs['image_def'] = image_def.dxf.handle
  672. dxfattribs['image_size'] = image_def.dxf.image_size
  673. image = self.build_and_add_entity('IMAGE', dxfattribs)
  674. if self.drawing is not None:
  675. image_def_reactor = self.drawing.objects.add_image_def_reactor(image.dxf.handle)
  676. reactor_handle = image_def_reactor.dxf.handle
  677. image.dxf.image_def_reactor = reactor_handle
  678. image_def.append_reactor_handle(reactor_handle)
  679. return image
  680. def add_underlay(self, underlay_def: 'UnderlayDef', insert: 'Vertex' = (0, 0, 0),
  681. scale: Tuple[float, float, float] = (1, 1, 1),
  682. rotation: float = 0., dxfattribs: dict = None) -> 'Underlay':
  683. """
  684. Add an :class:`Underlay` entity. Create :class:`UnderlayDef` by the :class:`Drawing` factory function
  685. :meth:`~Drawing.add_underlay_def`, see :ref:`tut_underlay`. (requires DXF R2000+)
  686. Args:
  687. underlay_def: required underlay definition as :class:`UnderlayDef`
  688. insert: insertion point as 3D point in :ref:`WCS`
  689. scale: underlay scaling factor as (x, y, z) tuple
  690. rotation: rotation angle around the z-axis in degrees
  691. dxfattribs (dict): additional DXF attributes for :class:`Underlay` entity
  692. Returns: :class:`Underlay`
  693. """
  694. if self.dxfversion < 'AC1015':
  695. raise DXFVersionError('UNDERLAY requires DXF version R2000+')
  696. dxfattribs = copy_attribs(dxfattribs)
  697. dxfattribs['insert'] = insert
  698. dxfattribs['underlay_def'] = underlay_def.dxf.handle
  699. dxfattribs['rotation'] = rotation
  700. underlay = self.build_and_add_entity(underlay_def.entity_name, dxfattribs)
  701. underlay.scale = scale
  702. underlay_def.append_reactor_handle(underlay.dxf.handle)
  703. return underlay
  704. def add_linear_dim(self,
  705. base: 'Vertex',
  706. p1: 'Vertex',
  707. p2: 'Vertex',
  708. location: 'Vertex' = None,
  709. text: str = "<>",
  710. angle: float = 0, # 0=horizontal, 90=vertical, else=rotated
  711. text_rotation: float = None,
  712. dimstyle: str = 'EZDXF',
  713. override: dict = None,
  714. dxfattribs: dict = None) -> DimStyleOverride:
  715. """
  716. Add horizontal, vertical and rotated dimension line. If an :class:`UCS` is used for dimension line rendering,
  717. all point definitions in UCS coordinates, translation into :ref:`WCS` and :ref:`OCS` is done by the rendering
  718. function. Manual set extrusion vector will be replaced by OCS defined by UCS or (0, 0, 1) if no UCS is used.
  719. This method returns a :class:`DimStyleOverride` object, to create the necessary dimension geometry, you have to
  720. call :meth:`DimStyleOverride.render` manually, this two step process allows additional processing steps on the
  721. :class:`Dimension` entity between creation and rendering.
  722. Args:
  723. base: location of dimension line, any point on the dimension line or its extension will do (in UCS)
  724. p1: measurement point 1 and start point of extension line 1 (in UCS)
  725. p2: measurement point 2 and start point of extension line 2 (in UCS)
  726. location: user defined location for text mid point (in UCS)
  727. text: None or "<>" the measurement is drawn as text, " " (one space) suppresses the dimension text,
  728. everything else `text` is drawn as dimension text
  729. dimstyle: dimension style name (:class:`DimStyle` table entry), default is "EZDXF"
  730. angle: angle from ucs/wcs x-axis to dimension line in degrees
  731. text_rotation: rotation angle of the dimension text away from its default orientation
  732. (the direction of the dimension line) in degrees
  733. override: :class:`DimStyleOverride` attributes
  734. dxfattribs: DXF attributes for :class:`Dimension` entity
  735. Returns: :class:`DimStyleOverride`
  736. """
  737. type_ = {'dimtype': const.DIM_LINEAR | const.DIM_BLOCK_EXCLUSIVE}
  738. dimline = cast('Dimension', self.build_and_add_entity('DIMENSION', dxfattribs=type_).cast())
  739. dxfattribs = copy_attribs(dxfattribs)
  740. dxfattribs['dimstyle'] = dimstyle
  741. dxfattribs['defpoint'] = Vector(base)
  742. dxfattribs['text'] = text
  743. dxfattribs['defpoint2'] = Vector(p1)
  744. dxfattribs['defpoint3'] = Vector(p2)
  745. dxfattribs['angle'] = float(angle)
  746. # text_rotation ALWAYS overrides implicit angles as absolute angle (0 is horizontal)!
  747. if text_rotation is not None:
  748. dxfattribs['text_rotation'] = float(text_rotation)
  749. dimline.update_dxf_attribs(dxfattribs)
  750. style = DimStyleOverride(dimline, override=override)
  751. if location is not None:
  752. style.set_location(location, leader=False, relative=False)
  753. return style
  754. def add_multi_point_linear_dim(self,
  755. base: 'Vertex',
  756. points: Iterable['Vertex'],
  757. angle: float = 0,
  758. ucs: 'UCS' = None,
  759. avoid_double_rendering: bool = True,
  760. dimstyle: str = 'EZDXF',
  761. override: dict = None,
  762. dxfattribs: dict = None,
  763. discard=False) -> None:
  764. """
  765. Add multiple linear dimensions for iterable `points`. If an :class:`UCS` is used for dimension line
  766. rendering, all point definitions in UCS coordinates, translation into :ref:`WCS` and :ref:`OCS` is done by the
  767. rendering function. Manual set extrusion vector will be replaced by OCS defined by UCS or (0, 0, 1) if no UCS
  768. is used.
  769. This method sets many design decisions by itself, the necessary geometry will be generated automatically, no
  770. required nor possible :meth:`render` call. This method is easy to use but you get what you get.
  771. Args:
  772. base: location of dimension line, any point on the dimension line or its extension will do (in UCS)
  773. points: iterable of measurement points (in UCS)
  774. angle: angle from ucs/wcs x-axis to dimension line in degrees (0=horizontal, 90=vertical)
  775. ucs: user defined coordinate system
  776. avoid_double_rendering: suppresses the first extension line and the first arrow if possible for continued
  777. dimension entities
  778. dimstyle: dimension style name (DimStyle table entry), default is "EZDXF"
  779. override: :class:`DimStyleOverride` attributes
  780. dxfattribs: DXF attributes for :class:`Dimension` entity
  781. discard: discard rendering result for friendly CAD applications like BricsCAD to get a native and likely
  782. better rendering result. (does not work with AutoCAD)
  783. """
  784. multi_point_linear_dimension(
  785. cast('GenericLayoutType', self),
  786. base=base,
  787. points=points,
  788. angle=angle,
  789. ucs=ucs,
  790. avoid_double_rendering=avoid_double_rendering,
  791. dimstyle=dimstyle,
  792. override=override,
  793. dxfattribs=dxfattribs,
  794. discard=discard,
  795. )
  796. def add_aligned_dim(self,
  797. p1: 'Vertex',
  798. p2: 'Vertex',
  799. distance: float,
  800. text: str = "<>",
  801. dimstyle: str = 'EZDXF',
  802. override: dict = None,
  803. dxfattribs: dict = None) -> DimStyleOverride:
  804. """
  805. Add linear dimension aligned with measurement points `p1` and `p2`. If an :class:`UCS` is used for dimension
  806. line rendering, all point definitions in UCS coordinates, translation into :ref:`WCS` and :ref:`OCS` is
  807. done by the rendering function. Manual set extrusion vector will be replaced by OCS defined by UCS or (0, 0, 1)
  808. if no UCS is used.
  809. This method returns a :class:`DimStyleOverride` object, to create the necessary dimension geometry, you have to
  810. call :meth:`DimStyleOverride.render` manually, this two step process allows additional processing steps on the
  811. :class:`Dimension` entity between creation and rendering.
  812. Args:
  813. p1: measurement point 1 and start point of extension line 1 (in UCS)
  814. p2: measurement point 2 and start point of extension line 2 (in UCS)
  815. distance: distance of dimension line from measurement points
  816. text: None or "<>" the measurement is drawn as text, " " (one space) suppresses the dimension text,
  817. everything else `text` is drawn as dimension text
  818. dimstyle: dimension style name (:class:`DimStyle` table entry), default is "EZDXF"
  819. override: :class:`DimStyleOverride` attributes
  820. dxfattribs: DXF attributes for :class:`Dimension` entity
  821. Returns: :class:`DimStyleOverride`
  822. """
  823. p1 = Vector(p1)
  824. p2 = Vector(p2)
  825. direction = p2 - p1
  826. angle = direction.angle_deg
  827. base = direction.orthogonal().normalize(distance)
  828. return self.add_linear_dim(
  829. base=base,
  830. p1=p1,
  831. p2=p2,
  832. dimstyle=dimstyle,
  833. text=text,
  834. angle=angle,
  835. override=override,
  836. dxfattribs=dxfattribs,
  837. )
  838. def add_angular_dim(self, override: dict = None, dxfattribs: dict = None) -> DimStyleOverride:
  839. type_ = {'dimtype': const.DIM_ANGULAR | const.DIM_BLOCK_EXCLUSIVE}
  840. dimline = cast('Dimension', self.build_and_add_entity('DIMENSION', dxfattribs=type_).cast())
  841. dxfattribs = copy_attribs(dxfattribs)
  842. dimline.update_dxf_attribs(dxfattribs)
  843. style = DimStyleOverride(dimline, override=override)
  844. return style
  845. def add_diameter_dim(self, override: dict = None, dxfattribs: dict = None) -> DimStyleOverride:
  846. type_ = {'dimtype': const.DIM_DIAMETER | const.DIM_BLOCK_EXCLUSIVE}
  847. dimline = cast('Dimension', self.build_and_add_entity('DIMENSION', dxfattribs=type_).cast())
  848. dxfattribs = copy_attribs(dxfattribs)
  849. dimline.update_dxf_attribs(dxfattribs)
  850. style = DimStyleOverride(dimline, override=override)
  851. return style
  852. def add_radius_dim(self, override: dict = None, dxfattribs: dict = None) -> DimStyleOverride:
  853. type_ = {'dimtype': const.DIM_RADIUS | const.DIM_BLOCK_EXCLUSIVE}
  854. dimline = cast('Dimension', self.build_and_add_entity('DIMENSION', dxfattribs=type_).cast())
  855. dxfattribs = copy_attribs(dxfattribs)
  856. dimline.update_dxf_attribs(dxfattribs)
  857. style = DimStyleOverride(dimline, override=override)
  858. return style
  859. def add_angular_3p_dim(self, override: dict = None, dxfattribs: dict = None) -> DimStyleOverride:
  860. type_ = {'dimtype': const.DIM_ANGULAR_3P | const.DIM_BLOCK_EXCLUSIVE}
  861. dimline = cast('Dimension', self.build_and_add_entity('DIMENSION', dxfattribs=type_).cast())
  862. dxfattribs = copy_attribs(dxfattribs)
  863. dimline.update_dxf_attribs(dxfattribs)
  864. style = DimStyleOverride(dimline, override=override)
  865. return style
  866. def add_ordinate_dim(self, override: dict = None, dxfattribs: dict = None) -> DimStyleOverride:
  867. type_ = {'dimtype': const.DIM_ORDINATE | const.DIM_BLOCK_EXCLUSIVE}
  868. dimline = cast('Dimension', self.build_and_add_entity('DIMENSION', dxfattribs=type_).cast())
  869. dxfattribs = copy_attribs(dxfattribs)
  870. dimline.update_dxf_attribs(dxfattribs)
  871. style = DimStyleOverride(dimline, override=override)
  872. return style
  873. def add_arrow(self, name: str, insert: 'Vertex', size: float = 1., rotation: float = 0,
  874. dxfattribs: dict = None) -> Vector:
  875. return ARROWS.render_arrow(self, name=name, insert=insert, size=size, rotation=rotation, dxfattribs=dxfattribs)
  876. def add_arrow_blockref(self, name: str, insert: 'Vertex', size: float = 1., rotation: float = 0,
  877. dxfattribs: dict = None) -> Vector:
  878. return ARROWS.insert_arrow(self, name=name, insert=insert, size=size, rotation=rotation, dxfattribs=dxfattribs)