123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095 |
- # Created: 10.03.2013
- # Copyright (c) 2013-2018, Manfred Moitzi
- # License: MIT License
- from typing import TYPE_CHECKING, Iterable, Sequence, Union, Dict, Tuple, cast
- import math
- import logging
- from ezdxf.lldxf import const
- from ezdxf.lldxf.const import DXFValueError, DXFVersionError
- from ezdxf.math import Vector
- from ezdxf.math import bspline_control_frame, bspline_control_frame_approx
- from ezdxf.render.arrows import ARROWS
- from ezdxf.dimstyleoverride import DimStyleOverride
- from ezdxf.render.dimension import multi_point_linear_dimension
- logger = logging.getLogger('ezdxf')
- if TYPE_CHECKING: # import forward references
- from ezdxf.eztypes import DXFFactoryType, DXFEntity, Spline, Text, ImageDef, Image, Line, Point, Circle, Arc, Shape
- from ezdxf.eztypes import Solid, Trace, Face, Insert, Attrib, Polyline, Polyface, Polymesh, UnderlayDef, Underlay
- from ezdxf.eztypes import Hatch, Mesh, LWPolyline, Ellipse, MText, Ray, XLine, Dimension, DimStyleOverride
- from ezdxf.eztypes import Solid3d, Region, Body, Surface, RevolvedSurface, ExtrudedSurface, SweptSurface, \
- LoftedSurface
- from ezdxf.eztypes import UCS, GenericLayoutType, Vertex
- def copy_attribs(dxfattribs: dict = None) -> dict:
- return dict(dxfattribs or {})
- class GraphicsFactory:
- """ Abstract base class for BaseLayout() """
- def __init__(self, dxffactory: 'DXFFactoryType'):
- self._dxffactory = dxffactory
- @property
- def dxfversion(self) -> str:
- return self._dxffactory.dxfversion
- def build_and_add_entity(self, type_: str, dxfattribs: dict):
- raise NotImplementedError("Abstract method call.")
- def add_point(self, location: 'Vertex', dxfattribs: dict = None) -> 'Point':
- """
- Add a :class:`Point` element at `location`.
- Args:
- location: 2D/3D point in :ref:`WCS`
- dxfattribs (dist): additional DXF attributes for :class:`Point` entity
- Returns: :class:`Point`
- """
- dxfattribs = copy_attribs(dxfattribs)
- dxfattribs['location'] = location
- return self.build_and_add_entity('POINT', dxfattribs)
- def add_line(self, start: 'Vertex', end: 'Vertex', dxfattribs: dict = None) -> 'Line':
- """
- Add a :class:`Line` element from `start` to `end`.
- Args:
- start: 2D/3D point in :ref:`WCS`
- end: 2D/3D point in :ref:`WCS`
- dxfattribs (dict): additional DXF attributes for :class:`Line` entity
- Returns: :class:`Line`
- """
- dxfattribs = copy_attribs(dxfattribs)
- dxfattribs['start'] = start
- dxfattribs['end'] = end
- return self.build_and_add_entity('LINE', dxfattribs)
- def add_circle(self, center: 'Vertex', radius: float, dxfattribs: dict = None) -> 'Circle':
- """
- Add a :class:`Circle` element. This is an 2D element, which can be placed in space by using :ref:`OCS`.
- Args:
- center: 2D/3D point in :ref:`WCS`
- radius: circle radius
- dxfattribs (dcit): additional DXF attributes for :class:`Circle` entity
- Returns: :class:`Circle`
- """
- dxfattribs = copy_attribs(dxfattribs)
- dxfattribs['center'] = center
- dxfattribs['radius'] = radius
- return self.build_and_add_entity('CIRCLE', dxfattribs)
- def add_ellipse(self, center: 'Vertex', major_axis: 'Vertex' = (1, 0, 0), ratio: float = 1, start_param: float = 0,
- end_param: float = 2*math.pi, dxfattribs: dict = None) -> 'Ellipse':
- """
- Add an :class:`Ellipse` element, `ratio` is the ratio of minor axis to major axis, `start_param` and `end_param`
- defines start and end point of the ellipse, a full ellipse goes from 0 to 2*pi. The ellipse goes from start to
- end param in `counter clockwise` direction.
- Args:
- center: center of ellipse as 2D/3D point in :ref:`WCS`
- major_axis: major axis as vector (x, y, z)
- ratio: ratio of minor axis to major axis
- start_param: start of ellipse curve
- end_param: end param of ellipse curve
- dxfattribs (dict): additional DXF attributes for :class:`Ellipse` entity
- Returns: :class:`Ellipse`
- """
- if self.dxfversion < 'AC1015':
- raise DXFVersionError('ELLIPSE requires DXF version R2000+')
- if ratio > 1.:
- raise DXFValueError("Parameter 'ratio' has to be <= 1.0")
- dxfattribs = copy_attribs(dxfattribs)
- dxfattribs['center'] = center
- dxfattribs['major_axis'] = major_axis
- dxfattribs['ratio'] = ratio
- dxfattribs['start_param'] = start_param
- dxfattribs['end_param'] = end_param
- return self.build_and_add_entity('ELLIPSE', dxfattribs)
- def add_arc(self, center: 'Vertex', radius: float, start_angle: float, end_angle: float,
- is_counter_clockwise: bool = True, dxfattribs: dict = None) -> 'Arc':
- """
- Add an :class:`Arc` element. The arc goes from `start_angle` to `end_angle` in counter clockwise
- direction by default, set parameter `is_counter_clockwise` to False for clockwise orientation.
- Args:
- center: center of arc as 2D/3D point in :ref:`WCS`
- radius: arc radius
- start_angle: start angle in degrees
- end_angle: end angle in degrees
- is_counter_clockwise: False for clockwise orientation
- dxfattribs (dict): additional DXF attributes for :class:`Arc` entity
- Returns: :class:`Arc`
- """
- dxfattribs = copy_attribs(dxfattribs)
- dxfattribs['center'] = center
- dxfattribs['radius'] = radius
- if is_counter_clockwise:
- dxfattribs['start_angle'] = start_angle
- dxfattribs['end_angle'] = end_angle
- else:
- dxfattribs['start_angle'] = end_angle
- dxfattribs['end_angle'] = start_angle
- return self.build_and_add_entity('ARC', dxfattribs)
- def add_solid(self, points: Iterable['Vertex'], dxfattribs: dict = None) -> 'Solid':
- """
- Add a :class:`Solid` element, `points` is an iterable of 3 or 4 points.
- Args:
- points: iterable of 3 or 4 2D/3D points in :ref:`WCS`
- dxfattribs (dict): additional DXF attributes for :class:`Solid` entity
- Returns: :class:`Solid`
- """
- return cast('Solid', self._add_quadrilateral('SOLID', points, dxfattribs))
- def add_trace(self, points: Iterable['Vertex'], dxfattribs: dict = None) -> 'Trace':
- """
- Add a :class:`Trace` element, `points` is an iterable of 3 or 4 points.
- Args:
- points: iterable of 3 or 4 2D/3D points in :ref:`WCS`
- dxfattribs (dict): additional DXF attributes for :class:`Trace` entity
- Returns: :class:`Trace`
- """
- return cast('Trace', self._add_quadrilateral('TRACE', points, dxfattribs))
- def add_3dface(self, points: Iterable['Vertex'], dxfattribs: dict = None) -> 'Face':
- """
- Add a :class:`3DFace` element, `points` is an iterable 3 or 4 2D/3D points.
- Args:
- points: iterable of 3 or 4 2D/3D points in :ref:`WCS`
- dxfattribs (dict): additional DXF attributes for :class:`3DFace` entity
- Returns: :class:`3DFace`
- """
- return cast('Face', self._add_quadrilateral('3DFACE', points, dxfattribs))
- def add_text(self, text: str, dxfattribs: dict = None) -> 'Text':
- """
- Add a :class:`Text` element, see also :class:`Style`.
- Args:
- text: content string
- dxfattribs (dict): additional DXF attributes for :class:`Text` entity
- Returns: :class:`Text`
- """
- dxfattribs = copy_attribs(dxfattribs)
- dxfattribs['text'] = text
- dxfattribs.setdefault('insert', (0, 0))
- return self.build_and_add_entity('TEXT', dxfattribs)
- def add_blockref(self, name: str, insert: 'Vertex', dxfattribs: dict = None) -> 'Insert':
- """
- Add an :class:`Insert` element.
- Args:
- name: block name
- insert: insert location as 2D/3D point in :ref:`WCS`
- dxfattribs (dict): additional DXF attributes for :class:`Insert` entity
- Returns: :class:`Insert`
- """
- dxfattribs = copy_attribs(dxfattribs)
- dxfattribs['name'] = name
- dxfattribs['insert'] = insert
- blockref = self.build_and_add_entity('INSERT', dxfattribs)
- return blockref
- def add_auto_blockref(self, name: str, insert: 'Vertex', values: Dict[str, str], dxfattribs: dict = None) \
- -> 'Insert':
- """
- Add an :class:`Insert` element. This method adds for each :class:`Attdef` entity, defined in the block
- definition, automatically an :class:`Attrib` entity to the block reference and set tag/value DXF attributes of
- the Attrib entities. The `values` dict defines the tag/value attributes as key=tag, value=tag value as string.
- The Attrib entities are placed relative to the insert point, which is equal to the block base point.
- Args:
- name: block name
- insert: insert location as 2D/3D point in :ref:`WCS`
- values (dict): :class:`Attrib` tag values as key=tag, value=tag value pairs
- dxfattribs (dict): additional DXF attributes for :class:`Insert` entity
- Returns: :class:`Insert`
- """
- def get_dxfattribs(attdef) -> dict:
- dxfattribs = attdef.dxfattribs()
- dxfattribs.pop('prompt', None)
- dxfattribs.pop('handle', None)
- return dxfattribs
- def unpack(dxfattribs) -> Tuple[str, str, 'Vertex']:
- tag = dxfattribs.pop('tag')
- text = values.get(tag, "")
- insert = dxfattribs.pop('insert')
- return tag, text, insert
- def autofill(blockref, blockdef) -> None:
- # ATTRIBs are placed relative to the base point
- for attdef in blockdef.attdefs():
- dxfattribs = get_dxfattribs(attdef)
- tag, text, insert = unpack(dxfattribs)
- blockref.add_attrib(tag, text, insert, dxfattribs)
- dxfattribs = copy_attribs(dxfattribs)
- autoblock = self._dxffactory.blocks.new_anonymous_block()
- blockref = autoblock.add_blockref(name, (0, 0))
- blockdef = self._dxffactory.blocks[name]
- autofill(blockref, blockdef)
- return self.add_blockref(autoblock.name, insert, dxfattribs)
- def add_attrib(self, tag: str, text: str, insert: 'Vertex' = (0, 0), dxfattribs: dict = None) -> 'Attrib':
- """
- Add an :class:`Attrib` as stand alone DXF entity.
- Args:
- tag: tag name as string
- text: tag value as string
- insert: insert location as 2D/3D point in :ref:`WCS`
- dxfattribs (dict): additional DXF attributes for :class:`Attrib` entity
- Returns: :class:`Attrib`
- """
- dxfattribs = copy_attribs(dxfattribs)
- dxfattribs['tag'] = tag
- dxfattribs['text'] = text
- dxfattribs['insert'] = insert
- return self.build_and_add_entity('ATTRIB', dxfattribs)
- def add_polyline2d(self, points: Iterable['Vertex'], dxfattribs: dict = None) -> 'Polyline':
- """
- Add a 2D :class:`Polyline` entity.
- Args:
- points: iterable of 2D points in :ref:`WCS`
- dxfattribs (dict): additional DXF attributes for :class:`Polyline` entity
- Returns: :class:`Polyline`
- """
- dxfattribs = copy_attribs(dxfattribs)
- closed = dxfattribs.pop('closed', False)
- polyline = self.build_and_add_entity('POLYLINE', dxfattribs)
- polyline.close(closed)
- polyline.append_vertices(points)
- return polyline
- def add_polyline3d(self, points: Iterable['Vertex'], dxfattribs: dict = None) -> 'Polyline':
- """
- Add a 3D :class:`Polyline` entity.
- Args:
- points: iterable of 3D points in :ref:`WCS`
- dxfattribs (dict): additional DXF attributes for :class:`Polyline` entity
- Returns: :class:`Polyline`
- """
- dxfattribs = copy_attribs(dxfattribs)
- dxfattribs['flags'] = dxfattribs.get('flags', 0) | const.POLYLINE_3D_POLYLINE
- return self.add_polyline2d(points, dxfattribs)
- def add_polymesh(self, size: Tuple[int, int] = (3, 3), dxfattribs: dict = None) -> 'Polymesh':
- """
- Add a :class:`Polymesh` entity, which is represented as :class:`Polyline` entity in the DXF file.
- A polymesh is a grid of `mcount` x `ncount` vertices and every vertex has its own xyz-coordinates.
- Args:
- size: 2-tuple (`mcount`, `ncount`)
- dxfattribs (dict): additional DXF attributes for :class:`Polyline` entity
- Returns: :class:`Polymesh`
- """
- dxfattribs = copy_attribs(dxfattribs)
- dxfattribs['flags'] = dxfattribs.get('flags', 0) | const.POLYLINE_3D_POLYMESH
- m_size = max(size[0], 2)
- n_size = max(size[1], 2)
- dxfattribs['m_count'] = m_size
- dxfattribs['n_count'] = n_size
- m_close = dxfattribs.pop('m_close', False)
- n_close = dxfattribs.pop('n_close', False)
- polymesh = self.build_and_add_entity('POLYLINE', dxfattribs)
- points = [(0, 0, 0)] * (m_size * n_size)
- polymesh.append_vertices(points) # init mesh vertices
- polymesh.close(m_close, n_close)
- return polymesh.cast()
- def add_polyface(self, dxfattribs: dict = None) -> 'Polyface':
- """
- Add a :class:`Polyface` entity, which is represented as :class:`Polyline` entity in the DXF file.
- Args:
- dxfattribs (dict): additional DXF attributes for :class:`Polyline` entity
- Returns: :class:`Polyface`
- """
- dxfattribs = copy_attribs(dxfattribs)
- dxfattribs['flags'] = dxfattribs.get('flags', 0) | const.POLYLINE_POLYFACE
- m_close = dxfattribs.pop('m_close', False)
- n_close = dxfattribs.pop('n_close', False)
- polyface = self.build_and_add_entity('POLYLINE', dxfattribs)
- polyface.close(m_close, n_close)
- return polyface.cast()
- def _add_quadrilateral(self, type_: str, points: Iterable['Vertex'], dxfattribs: dict = None) -> 'DXFEntity':
- dxfattribs = copy_attribs(dxfattribs)
- entity = self.build_and_add_entity(type_, dxfattribs)
- for x, point in enumerate(self._four_points(points)):
- entity[x] = point
- return entity
- @staticmethod
- def _four_points(points: Iterable['Vertex']) -> Iterable['Vertex']:
- vertices = list(points)
- if len(vertices) not in (3, 4):
- raise DXFValueError('3 or 4 points required.')
- for vertex in vertices:
- yield vertex
- if len(vertices) == 3:
- yield vertices[-1] # last again
- def add_shape(self, name: str, insert: 'Vertex' = (0, 0), size: float = 1.0, dxfattribs: dict = None) -> 'Shape':
- """
- Add a :class:`Shape` reference to a external stored shape.
- Args:
- name: shape name as string
- insert: insert location as 2D/3D point in :ref:`WCS`
- size: size factor
- dxfattribs (dict): additional DXF attributes for :class:`Shape` entity
- Returns: :class:`Shape`
- """
- dxfattribs = copy_attribs(dxfattribs)
- dxfattribs['name'] = name
- dxfattribs['insert'] = insert
- dxfattribs['size'] = size
- return self.build_and_add_entity('SHAPE', dxfattribs)
- # new entities in DXF AC1015 (R2000)
- def add_lwpolyline(self, points: Iterable['Vertex'], format: str = 'xyseb', dxfattribs: dict = None) -> 'LWPolyline':
- """
- Add a 2D polyline as :class:`LWPolyline` entity. A points are defined as (x, y, [start_width, [end_width,
- [bulge]]]) tuples, but order can be redefined by the `format` argument. Set start_width, end_width to 0 to be
- ignored (x, y, 0, 0, bulge).
- The :class:`LWPolyline` is defined as a single DXF entity and needs less disk space and memory than a
- :class:`Polyline` entity. (requires DXF R2000+)
- Args:
- points: iterable of (x, y, [start_width, [end_width, [bulge]]]) tuples
- format: user defined point format, default is "xyseb"
- dxfattribs (dict): additional DXF attributes for :class:`LWPolyline` entity
- Returns: :class:`LWPolyline`
- """
- if self.dxfversion < 'AC1015':
- raise DXFVersionError('LWPOLYLINE requires DXF version R2000+')
- dxfattribs = copy_attribs(dxfattribs)
- closed = dxfattribs.pop('closed', False)
- lwpolyline = self.build_and_add_entity('LWPOLYLINE', dxfattribs)
- lwpolyline.set_points(points, format=format)
- lwpolyline.closed = closed
- return lwpolyline
- def add_mtext(self, text: str, dxfattribs: dict = None) -> 'MText':
- """
- Add a multiline text element with automatic text wrapping at boundaries as :class:`MText` entity. (requires
- DXF R2000+)
- Args:
- text: content string
- dxfattribs (dict): additional DXF attributes for :class:`MText` entity
- Returns: :class:`MText`
- """
- if self.dxfversion < 'AC1015':
- raise DXFVersionError('MTEXT requires DXF version R2000+')
- dxfattribs = copy_attribs(dxfattribs)
- mtext = self.build_and_add_entity('MTEXT', dxfattribs)
- mtext.set_text(text)
- return mtext
- def add_ray(self, start: 'Vertex', unit_vector: 'Vertex', dxfattribs: dict = None) -> 'Ray':
- """
- Add a :class:`Ray` that begins at `start` point and continues to infinity (construction line).
- (requires DXF R2000+)
- Args:
- start: location 3D point in :ref:`WCS`
- unit_vector: 3D vector (x, y, z)
- dxfattribs (dict): additional DXF attributes for :class:`Ray` entity
- Returns: :class:`Ray`
- """
- if self.dxfversion < 'AC1015':
- raise DXFVersionError('RAY requires DXF version R2000+')
- dxfattribs = copy_attribs(dxfattribs)
- dxfattribs['start'] = start
- dxfattribs['unit_vector'] = unit_vector
- return self.build_and_add_entity('RAY', dxfattribs)
- def add_xline(self, start: 'Vertex', unit_vector: 'Vertex', dxfattribs: dict = None) -> 'XLine':
- """
- Add an infinity :class:`XLine` (construction line).
- (requires DXF R2000+)
- Args:
- start: location 3D point in :ref:`WCS`
- unit_vector: 3D vector (x, y, z)
- dxfattribs (dict): additional DXF attributes for :class:`XLine` entity
- Returns: :class:`XLine`
- """
- if self.dxfversion < 'AC1015':
- raise DXFVersionError('XLINE requires DXF version R2000+')
- dxfattribs = copy_attribs(dxfattribs)
- dxfattribs['start'] = start
- dxfattribs['unit_vector'] = unit_vector
- return self.build_and_add_entity('XLINE', dxfattribs)
- def add_spline(self, fit_points: Iterable['Vertex'] = None, degree: int = 3, dxfattribs: dict = None) -> 'Spline':
- """
- Add a B-spline defined by fit points, the control points and knot values are created by the CAD application,
- therefore it is not predictable how the rendered spline will look like, because for every set of fit points
- exists an infinite set of B-splines. If fit_points is None, an 'empty' spline will be created, all data has to
- be set by the user. (requires DXF R2000+)
- AutoCAD creates a spline through fit points by a proprietary algorithm. `ezdxf` can not reproduce the control
- point calculation.
- Args:
- fit_points: iterable of fit points as (x, y[, z]) in :ref:`WCS`, if None -> user defined spline
- degree: degree fo B-spline
- dxfattribs (dict): additional DXF attributes for :class:`Spline` entity
- Returns: :class:`Spline`
- """
- if self.dxfversion < 'AC1015':
- raise DXFVersionError('SPLINE requires DXF version R2000+')
- dxfattribs = copy_attribs(dxfattribs)
- dxfattribs['degree'] = degree
- spline = self.build_and_add_entity('SPLINE', dxfattribs)
- if fit_points is not None:
- spline.set_fit_points(list(fit_points))
- return spline
- def add_spline_control_frame(self, fit_points: Iterable['Vertex'], degree: int = 3, method: str = 'distance',
- power: float = .5, dxfattribs: dict = None) -> 'Spline':
- """
- Create and add B-spline control frame from fit points.
- 1. method = "uniform", creates a uniform t vector, [0...1] equally spaced
- 2. method = "distance", creates a t vector with values proportional to the fit point distances
- 3. method = "centripetal", creates a t vector with values proportional to the fit point distances^power
- None of this methods matches the spline created from fit points by AutoCAD.
- Args:
- fit_points: iterable of fit points as (x, y[, z]) in :ref:`WCS`
- degree: degree of B-spline
- method: calculation method for parameter vector t
- power: power for centripetal method
- dxfattribs (dict): additional DXF attributes for :class:`Spline` entity
- Returns: :class:`Spline`
- """
- bspline = bspline_control_frame(fit_points, degree=degree, method=method, power=power)
- return self.add_open_spline(
- control_points=bspline.control_points,
- degree=bspline.degree,
- knots=bspline.knot_values(),
- dxfattribs=dxfattribs,
- )
- def add_spline_approx(self, fit_points: Iterable['Vertex'], count: int, degree: int = 3, method: str = 'distance',
- power: float = .5, dxfattribs: dict = None) -> 'Spline':
- """
- Approximate B-spline by a reduced count of control points, given are the fit points and the degree of the B-spline.
- 1. method = 'uniform', creates a uniform t vector, [0...1] equally spaced
- 2. method = 'distance', creates a t vector with values proportional to the fit point distances
- 3. method = 'centripetal', creates a t vector with values proportional to the fit point distances^power
- (requires DXF R2000+)
- Args:
- fit_points: all fit points of B-spline
- count: count of designated control points
- degree: degree of B-spline
- method: calculation method for parameter vector t
- power: power for centripetal method
- dxfattribs (dict): additional DXF attributes for :class:`Spline` entity
- Returns: :class:`Spline`
- """
- bspline = bspline_control_frame_approx(fit_points, count, degree=degree, method=method, power=power)
- return self.add_open_spline(
- control_points=bspline.control_points,
- degree=bspline.degree,
- dxfattribs=dxfattribs,
- )
- def add_open_spline(self, control_points: Iterable['Vertex'], degree: int = 3, knots: Iterable[float] = None,
- dxfattribs: dict = None) -> 'Spline':
- """
- Add an open uniform :class:`Spline` defined by `control_points`. (requires DXF R2000+)
- Open uniform B-splines start and end at your first and last control point.
- Args:
- control_points: iterable of 3D points in :ref:`WCS`
- degree: degree of B-spline
- knots: knot values as iterable of floats
- dxfattribs (dict): additional DXF attributes for :class:`Spline` entity
- Returns: :class:`Spline`
- """
- spline = self.add_spline(dxfattribs=dxfattribs)
- spline.set_open_uniform(list(control_points), degree)
- if knots is not None:
- spline.set_knot_values(list(knots))
- return spline
- def add_closed_spline(self, control_points: Iterable['Vertex'], degree: int = 3, knots: Iterable[float] = None,
- dxfattribs: dict = None) -> 'Spline':
- """
- Add a closed uniform :class:`Spline` defined by `control_points`. (requires DXF R2000+)
- Closed uniform B-splines is a closed curve start and end at the first control point.
- Args:
- control_points: iterable of 3D points in :ref:`WCS`
- degree: degree of B-spline
- knots: knot values as iterable of floats
- dxfattribs (dict): additional DXF attributes for :class:`Spline` entity
- Returns: :class:`Spline`
- """
- spline = self.add_spline(dxfattribs=dxfattribs)
- spline.set_periodic(list(control_points), degree)
- if knots is not None:
- spline.set_knot_values(list(knots))
- return spline
- def add_rational_spline(self, control_points: Iterable['Vertex'], weights: Sequence[float], degree: int = 3,
- knots: Iterable[float] = None, dxfattribs: dict = None) -> 'Spline':
- """
- Add an open rational uniform :class:`Spline` defined by `control_points`. (requires DXF R2000+)
- `weights` has to be an iterable of floats, which defines the influence of the associated control point to the
- shape of the B-spline, therefore for each control point is one weight value required.
- Open rational uniform B-splines start and end at the first and last control point.
- Args:
- control_points: iterable of 3D points in :ref:`WCS`
- weights: weight values as iterable of floats
- degree: degree of B-spline
- knots: knot values as iterable of floats
- dxfattribs (dict): additional DXF attributes for :class:`Spline` entity
- Returns: :class:`Spline`
- """
- spline = self.add_spline(dxfattribs=dxfattribs)
- spline.set_open_rational(list(control_points), weights, degree)
- if knots is not None:
- spline.set_knot_values(list(knots))
- return spline
- def add_closed_rational_spline(self, control_points: Iterable['Vertex'], weights: Sequence[float], degree: int = 3,
- knots: Iterable[float] = None, dxfattribs: dict = None) -> 'Spline':
- """
- Add a closed rational uniform :class:`Spline` defined by `control_points`. (requires DXF R2000+)
- `weights` has to be an iterable of floats, which defines the influence of the associated control point to the
- shape of the B-spline, therefore for each control point is one weight value required.
- Closed rational uniform B-splines start and end at the first control point.
- Args:
- control_points: iterable of 3D points in :ref:`WCS`
- weights: weight values as iterable of floats
- degree: degree of B-spline
- knots: knot values as iterable of floats
- dxfattribs (dict): additional DXF attributes for :class:`Spline` entity
- Returns: :class:`Spline`
- """
- spline = self.add_spline(dxfattribs=dxfattribs)
- spline.set_periodic_rational(list(control_points), weights, degree)
- if knots is not None:
- spline.set_knot_values(list(knots))
- return spline
- def add_body(self, acis_data: str = None, dxfattribs: dict = None) -> 'Body':
- """
- Add a :class:`Body` entity. (requires DXF R2000+)
- Args:
- acis_data: ACIS data as iterable of text lines as strings, no interpretation by ezdxf possible
- dxfattribs (dict): additional DXF attributes for :class:`Body` entity
- Returns: :class:`Body`
- """
- return self._add_acis_entiy('BODY', acis_data, dxfattribs)
- def add_region(self, acis_data: str = None, dxfattribs: dict = None) -> 'Region':
- """
- Add a :class:`Region` entity. (requires DXF R2000+)
- Args:
- acis_data: ACIS data as iterable of text lines as strings, no interpretation by ezdxf possible
- dxfattribs (dict): additional DXF attributes for :class:`Region` entity
- Returns: :class:`Region`
- """
- return cast('Region', self._add_acis_entiy('REGION', acis_data, dxfattribs))
- def add_3dsolid(self, acis_data: str = None, dxfattribs: dict = None) -> 'Solid3d':
- """
- Add a :class:`3DSolid` entity. (requires DXF R2000+)
- Args:
- acis_data: ACIS data as iterable of text lines as strings, no interpretation by ezdxf possible
- dxfattribs (dict): additional DXF attributes for :class:`3DSolid` entity
- Returns: :class:`3DSolid`
- """
- return cast('Solid3d', self._add_acis_entiy('3DSOLID', acis_data, dxfattribs))
- def add_surface(self, acis_data: str = None, dxfattribs: dict = None) -> 'Surface':
- """
- Add a :class:`Surface` entity. (requires DXF R2007+)
- Args:
- acis_data: ACIS data as iterable of text lines as strings, no interpretation by ezdxf possible
- dxfattribs (dict): additional DXF attributes for :class:`Surface` entity
- Returns: :class:`Surface`
- """
- if self.dxfversion < 'AC1021':
- raise DXFVersionError('SURFACE requires DXF version R2007+')
- return cast('Surface', self._add_acis_entiy('SURFACE', acis_data, dxfattribs))
- def add_extruded_surface(self, acis_data: str = None, dxfattribs: dict = None) -> 'ExtrudedSurface':
- """
- Add a :class:`ExtrudedSurface` entity. (requires DXF R2007+)
- Args:
- acis_data: ACIS data as iterable of text lines as strings, no interpretation by ezdxf possible
- dxfattribs (dict): additional DXF attributes for :class:`ExtrudedSurface` entity
- Returns: :class:`ExtrudedSurface`
- """
- if self.dxfversion < 'AC1021':
- raise DXFVersionError('EXTRUDEDSURFACE requires DXF version R2007+')
- return cast('ExtrudedSurface', self._add_acis_entiy('EXTRUDEDSURFACE', acis_data, dxfattribs))
- def add_lofted_surface(self, acis_data: str = None, dxfattribs: dict = None) -> 'LoftedSurface':
- """
- Add a :class:`LoftedSurface` entity. (requires DXF R2007+)
- Args:
- acis_data: ACIS data as iterable of text lines as strings, no interpretation by ezdxf possible
- dxfattribs (dict): additional DXF attributes for :class:`LoftedSurface` entity
- Returns: :class:`LoftedSurface`
- """
- if self.dxfversion < 'AC1021':
- raise DXFVersionError('LOFTEDSURFACE requires DXF version R2007+')
- return cast('LoftedSurface', self._add_acis_entiy('LOFTEDSURFACE', acis_data, dxfattribs))
- def add_revolved_surface(self, acis_data: str = None, dxfattribs: dict = None) -> 'RevolvedSurface':
- """
- Add a :class:`RevolvedSurface` entity. (requires DXF R2007+)
- Args:
- acis_data: ACIS data as iterable of text lines as strings, no interpretation by ezdxf possible
- dxfattribs (dict): additional DXF attributes for :class:`RevolvedSurface` entity
- Returns: :class:`RevolvedSurface`
- """
- if self.dxfversion < 'AC1021':
- raise DXFVersionError('REVOLVEDSURFACE requires DXF version R2007+')
- return cast('RevolvedSurface', self._add_acis_entiy('REVOLVEDSURFACE', acis_data, dxfattribs))
- def add_swept_surface(self, acis_data: str = None, dxfattribs: dict = None) -> 'SweptSurface':
- """
- Add a :class:`SweptSurface` entity. (requires DXF R2007+)
- Args:
- acis_data: ACIS data as iterable of text lines as strings, no interpretation by ezdxf possible
- dxfattribs (dict): additional DXF attributes for :class:`SweptSurface` entity
- Returns: :class:`SweptSurface`
- """
- if self.dxfversion < 'AC1021':
- raise DXFVersionError('SWEPT requires DXF version R2007+')
- return cast('SweptSurface', self._add_acis_entiy('SWEPTSURFACE', acis_data, dxfattribs))
- def _add_acis_entiy(self, name, acis_data: str, dxfattribs: dict) -> 'Body':
- if self.dxfversion < 'AC1015':
- raise DXFVersionError('{} requires DXF version R2000+'.format(name))
- dxfattribs = copy_attribs(dxfattribs)
- entity = self.build_and_add_entity(name, dxfattribs)
- if acis_data is not None:
- entity.set_acis_data(acis_data)
- return entity
- def add_hatch(self, color: int = 7, dxfattribs: dict = None) -> 'Hatch':
- """
- Add a :class:`Hatch` entity. (requires DXF R2007+)
- Args:
- color: ACI (AutoCAD Color Index), default is 7 (black/white).
- dxfattribs (dict): additional DXF attributes for :class:`Hatch` entity
- Returns: :class:`Hatch`
- """
- if self.dxfversion < 'AC1015':
- raise DXFVersionError('HATCH requires DXF version R2000+')
- dxfattribs = copy_attribs(dxfattribs)
- dxfattribs['solid_fill'] = 1
- dxfattribs['color'] = color
- dxfattribs['pattern_name'] = 'SOLID'
- return self.build_and_add_entity('HATCH', dxfattribs)
- def add_mesh(self, dxfattribs: dict = None) -> 'Mesh':
- """
- Add a :class:`Mesh` entity. (requires DXF R2007+)
- Args:
- dxfattribs (dict): additional DXF attributes for :class:`Mesh` entity
- Returns: :class:`Mesh`
- """
- if self.dxfversion < 'AC1015':
- raise DXFVersionError('MESH requires DXF version R2000+')
- dxfattribs = copy_attribs(dxfattribs)
- return self.build_and_add_entity('MESH', dxfattribs)
- def add_image(self, image_def: 'ImageDef', insert: 'Vertex', size_in_units: Tuple[float, float], rotation: float = 0.,
- dxfattribs: dict = None) -> 'Image':
- """
- Add an :class:`Image` entity. Create :class:`ImageDef` by the :class:`Drawing` factory function
- :meth:`~Drawing.add_image_def`, see :ref:`tut_image`. (requires DXF R2000+)
- Args:
- image_def: required image definition as :class:`ImageDef`
- insert: insertion point as 3D point in :ref:`WCS`
- size_in_units: size as (x, y) tuple in drawing units
- rotation: rotation angle around the z-axis in degrees
- dxfattribs (dict): additional DXF attributes for :class:`Image` entity
- Returns: :class:`Image`
- """
- def to_vector(units_per_pixel, angle_in_rad):
- x = math.cos(angle_in_rad) * units_per_pixel
- y = math.sin(angle_in_rad) * units_per_pixel
- return round(x, 6), round(y, 6), 0 # supports only images in the xy-plane
- if self.dxfversion < 'AC1015':
- raise DXFVersionError('IMAGE requires DXF version R2000+')
- dxfattribs = copy_attribs(dxfattribs)
- x_pixels, y_pixels = image_def.dxf.image_size
- x_units, y_units = size_in_units
- x_units_per_pixel = x_units / x_pixels
- y_units_per_pixel = y_units / y_pixels
- x_angle_rad = math.radians(rotation)
- y_angle_rad = x_angle_rad + (math.pi / 2.)
- dxfattribs['insert'] = Vector(insert)
- dxfattribs['u_pixel'] = to_vector(x_units_per_pixel, x_angle_rad)
- dxfattribs['v_pixel'] = to_vector(y_units_per_pixel, y_angle_rad)
- dxfattribs['image_def'] = image_def.dxf.handle
- dxfattribs['image_size'] = image_def.dxf.image_size
- image = self.build_and_add_entity('IMAGE', dxfattribs)
- if self.drawing is not None:
- image_def_reactor = self.drawing.objects.add_image_def_reactor(image.dxf.handle)
- reactor_handle = image_def_reactor.dxf.handle
- image.dxf.image_def_reactor = reactor_handle
- image_def.append_reactor_handle(reactor_handle)
- return image
- def add_underlay(self, underlay_def: 'UnderlayDef', insert: 'Vertex' = (0, 0, 0),
- scale: Tuple[float, float, float] = (1, 1, 1),
- rotation: float = 0., dxfattribs: dict = None) -> 'Underlay':
- """
- Add an :class:`Underlay` entity. Create :class:`UnderlayDef` by the :class:`Drawing` factory function
- :meth:`~Drawing.add_underlay_def`, see :ref:`tut_underlay`. (requires DXF R2000+)
- Args:
- underlay_def: required underlay definition as :class:`UnderlayDef`
- insert: insertion point as 3D point in :ref:`WCS`
- scale: underlay scaling factor as (x, y, z) tuple
- rotation: rotation angle around the z-axis in degrees
- dxfattribs (dict): additional DXF attributes for :class:`Underlay` entity
- Returns: :class:`Underlay`
- """
- if self.dxfversion < 'AC1015':
- raise DXFVersionError('UNDERLAY requires DXF version R2000+')
- dxfattribs = copy_attribs(dxfattribs)
- dxfattribs['insert'] = insert
- dxfattribs['underlay_def'] = underlay_def.dxf.handle
- dxfattribs['rotation'] = rotation
- underlay = self.build_and_add_entity(underlay_def.entity_name, dxfattribs)
- underlay.scale = scale
- underlay_def.append_reactor_handle(underlay.dxf.handle)
- return underlay
- def add_linear_dim(self,
- base: 'Vertex',
- p1: 'Vertex',
- p2: 'Vertex',
- location: 'Vertex' = None,
- text: str = "<>",
- angle: float = 0, # 0=horizontal, 90=vertical, else=rotated
- text_rotation: float = None,
- dimstyle: str = 'EZDXF',
- override: dict = None,
- dxfattribs: dict = None) -> DimStyleOverride:
- """
- Add horizontal, vertical and rotated dimension line. If an :class:`UCS` is used for dimension line rendering,
- all point definitions in UCS coordinates, translation into :ref:`WCS` and :ref:`OCS` is done by the rendering
- function. Manual set extrusion vector will be replaced by OCS defined by UCS or (0, 0, 1) if no UCS is used.
- This method returns a :class:`DimStyleOverride` object, to create the necessary dimension geometry, you have to
- call :meth:`DimStyleOverride.render` manually, this two step process allows additional processing steps on the
- :class:`Dimension` entity between creation and rendering.
- Args:
- base: location of dimension line, any point on the dimension line or its extension will do (in UCS)
- p1: measurement point 1 and start point of extension line 1 (in UCS)
- p2: measurement point 2 and start point of extension line 2 (in UCS)
- location: user defined location for text mid point (in UCS)
- text: None or "<>" the measurement is drawn as text, " " (one space) suppresses the dimension text,
- everything else `text` is drawn as dimension text
- dimstyle: dimension style name (:class:`DimStyle` table entry), default is "EZDXF"
- angle: angle from ucs/wcs x-axis to dimension line in degrees
- text_rotation: rotation angle of the dimension text away from its default orientation
- (the direction of the dimension line) in degrees
- override: :class:`DimStyleOverride` attributes
- dxfattribs: DXF attributes for :class:`Dimension` entity
- Returns: :class:`DimStyleOverride`
- """
- type_ = {'dimtype': const.DIM_LINEAR | const.DIM_BLOCK_EXCLUSIVE}
- dimline = cast('Dimension', self.build_and_add_entity('DIMENSION', dxfattribs=type_).cast())
- dxfattribs = copy_attribs(dxfattribs)
- dxfattribs['dimstyle'] = dimstyle
- dxfattribs['defpoint'] = Vector(base)
- dxfattribs['text'] = text
- dxfattribs['defpoint2'] = Vector(p1)
- dxfattribs['defpoint3'] = Vector(p2)
- dxfattribs['angle'] = float(angle)
- # text_rotation ALWAYS overrides implicit angles as absolute angle (0 is horizontal)!
- if text_rotation is not None:
- dxfattribs['text_rotation'] = float(text_rotation)
- dimline.update_dxf_attribs(dxfattribs)
- style = DimStyleOverride(dimline, override=override)
- if location is not None:
- style.set_location(location, leader=False, relative=False)
- return style
- def add_multi_point_linear_dim(self,
- base: 'Vertex',
- points: Iterable['Vertex'],
- angle: float = 0,
- ucs: 'UCS' = None,
- avoid_double_rendering: bool = True,
- dimstyle: str = 'EZDXF',
- override: dict = None,
- dxfattribs: dict = None,
- discard=False) -> None:
- """
- Add multiple linear dimensions for iterable `points`. If an :class:`UCS` is used for dimension line
- rendering, all point definitions in UCS coordinates, translation into :ref:`WCS` and :ref:`OCS` is done by the
- rendering function. Manual set extrusion vector will be replaced by OCS defined by UCS or (0, 0, 1) if no UCS
- is used.
- This method sets many design decisions by itself, the necessary geometry will be generated automatically, no
- required nor possible :meth:`render` call. This method is easy to use but you get what you get.
- Args:
- base: location of dimension line, any point on the dimension line or its extension will do (in UCS)
- points: iterable of measurement points (in UCS)
- angle: angle from ucs/wcs x-axis to dimension line in degrees (0=horizontal, 90=vertical)
- ucs: user defined coordinate system
- avoid_double_rendering: suppresses the first extension line and the first arrow if possible for continued
- dimension entities
- dimstyle: dimension style name (DimStyle table entry), default is "EZDXF"
- override: :class:`DimStyleOverride` attributes
- dxfattribs: DXF attributes for :class:`Dimension` entity
- discard: discard rendering result for friendly CAD applications like BricsCAD to get a native and likely
- better rendering result. (does not work with AutoCAD)
- """
- multi_point_linear_dimension(
- cast('GenericLayoutType', self),
- base=base,
- points=points,
- angle=angle,
- ucs=ucs,
- avoid_double_rendering=avoid_double_rendering,
- dimstyle=dimstyle,
- override=override,
- dxfattribs=dxfattribs,
- discard=discard,
- )
- def add_aligned_dim(self,
- p1: 'Vertex',
- p2: 'Vertex',
- distance: float,
- text: str = "<>",
- dimstyle: str = 'EZDXF',
- override: dict = None,
- dxfattribs: dict = None) -> DimStyleOverride:
- """
- Add linear dimension aligned with measurement points `p1` and `p2`. If an :class:`UCS` is used for dimension
- line rendering, all point definitions in UCS coordinates, translation into :ref:`WCS` and :ref:`OCS` is
- done by the rendering function. Manual set extrusion vector will be replaced by OCS defined by UCS or (0, 0, 1)
- if no UCS is used.
- This method returns a :class:`DimStyleOverride` object, to create the necessary dimension geometry, you have to
- call :meth:`DimStyleOverride.render` manually, this two step process allows additional processing steps on the
- :class:`Dimension` entity between creation and rendering.
- Args:
- p1: measurement point 1 and start point of extension line 1 (in UCS)
- p2: measurement point 2 and start point of extension line 2 (in UCS)
- distance: distance of dimension line from measurement points
- text: None or "<>" the measurement is drawn as text, " " (one space) suppresses the dimension text,
- everything else `text` is drawn as dimension text
- dimstyle: dimension style name (:class:`DimStyle` table entry), default is "EZDXF"
- override: :class:`DimStyleOverride` attributes
- dxfattribs: DXF attributes for :class:`Dimension` entity
- Returns: :class:`DimStyleOverride`
- """
- p1 = Vector(p1)
- p2 = Vector(p2)
- direction = p2 - p1
- angle = direction.angle_deg
- base = direction.orthogonal().normalize(distance)
- return self.add_linear_dim(
- base=base,
- p1=p1,
- p2=p2,
- dimstyle=dimstyle,
- text=text,
- angle=angle,
- override=override,
- dxfattribs=dxfattribs,
- )
- def add_angular_dim(self, override: dict = None, dxfattribs: dict = None) -> DimStyleOverride:
- type_ = {'dimtype': const.DIM_ANGULAR | const.DIM_BLOCK_EXCLUSIVE}
- dimline = cast('Dimension', self.build_and_add_entity('DIMENSION', dxfattribs=type_).cast())
- dxfattribs = copy_attribs(dxfattribs)
- dimline.update_dxf_attribs(dxfattribs)
- style = DimStyleOverride(dimline, override=override)
- return style
- def add_diameter_dim(self, override: dict = None, dxfattribs: dict = None) -> DimStyleOverride:
- type_ = {'dimtype': const.DIM_DIAMETER | const.DIM_BLOCK_EXCLUSIVE}
- dimline = cast('Dimension', self.build_and_add_entity('DIMENSION', dxfattribs=type_).cast())
- dxfattribs = copy_attribs(dxfattribs)
- dimline.update_dxf_attribs(dxfattribs)
- style = DimStyleOverride(dimline, override=override)
- return style
- def add_radius_dim(self, override: dict = None, dxfattribs: dict = None) -> DimStyleOverride:
- type_ = {'dimtype': const.DIM_RADIUS | const.DIM_BLOCK_EXCLUSIVE}
- dimline = cast('Dimension', self.build_and_add_entity('DIMENSION', dxfattribs=type_).cast())
- dxfattribs = copy_attribs(dxfattribs)
- dimline.update_dxf_attribs(dxfattribs)
- style = DimStyleOverride(dimline, override=override)
- return style
- def add_angular_3p_dim(self, override: dict = None, dxfattribs: dict = None) -> DimStyleOverride:
- type_ = {'dimtype': const.DIM_ANGULAR_3P | const.DIM_BLOCK_EXCLUSIVE}
- dimline = cast('Dimension', self.build_and_add_entity('DIMENSION', dxfattribs=type_).cast())
- dxfattribs = copy_attribs(dxfattribs)
- dimline.update_dxf_attribs(dxfattribs)
- style = DimStyleOverride(dimline, override=override)
- return style
- def add_ordinate_dim(self, override: dict = None, dxfattribs: dict = None) -> DimStyleOverride:
- type_ = {'dimtype': const.DIM_ORDINATE | const.DIM_BLOCK_EXCLUSIVE}
- dimline = cast('Dimension', self.build_and_add_entity('DIMENSION', dxfattribs=type_).cast())
- dxfattribs = copy_attribs(dxfattribs)
- dimline.update_dxf_attribs(dxfattribs)
- style = DimStyleOverride(dimline, override=override)
- return style
- def add_arrow(self, name: str, insert: 'Vertex', size: float = 1., rotation: float = 0,
- dxfattribs: dict = None) -> Vector:
- return ARROWS.render_arrow(self, name=name, insert=insert, size=size, rotation=rotation, dxfattribs=dxfattribs)
- def add_arrow_blockref(self, name: str, insert: 'Vertex', size: float = 1., rotation: float = 0,
- dxfattribs: dict = None) -> Vector:
- return ARROWS.insert_arrow(self, name=name, insert=insert, size=size, rotation=rotation, dxfattribs=dxfattribs)
|