def set_extline_format(self, color: int = None, lineweight: int = None, extension: float = None, offset: float = None, fixed_length: float = None): """ Set common extension line attributes. Args: color: color index lineweight: line weight as int, 13 = 0.13mm, 200 = 2.00mm extension: extension length above dimension line offset: offset from measurement point fixed_length: set fixed length extension line, length below the dimension line """ if color is not None: self.dxf.dimclre = color if extension is not None: self.dxf.dimexe = extension if offset is not None: self.dxf.dimexo = offset try: if lineweight is not None: self.dxf.dimlwe = lineweight except const.DXFAttributeError: raise DXFVersionError('DIMLWE requires DXF R2000+') try: if fixed_length is not None: self.dxf.dimfxlon = 1 self.dxf.dimfxl = fixed_length except const.DXFAttributeError: raise DXFVersionError('DIMFXL requires DXF R2007+')
def set_text_align(self, halign: str = None, valign: str = None, vshift: float = None) -> None: """ Set measurement text alignment, `halign` defines the horizontal alignment (requires DXFR2000+), `valign` defines the vertical alignment, `above1` and `above2` means above extension line 1 or 2 and aligned with extension line. Args: halign: `left`, `right`, `center`, `above1`, `above2`, requires DXF R2000+ valign: `above`, `center`, `below` vshift: vertical text shift, if `valign` is `center`; >0 shift upward, <0 shift downwards """ if valign: valign = valign.lower() self.set_dxf_attrib('dimtad', DIMTAD[valign]) if valign == 'center' and vshift is not None: self.set_dxf_attrib('dimtvp', vshift) try: if halign: self.set_dxf_attrib('dimjust', DIMJUST[halign.lower()]) except const.DXFAttributeError: raise DXFVersionError('DIMJUST require DXF R2000+')
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_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_mtext(self, text, dxfattribs=None): 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_xline(self, start, unit_vector, dxfattribs=None): 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_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_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_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=7, dxfattribs=None): 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 delete_layout(self, name): if self.dxfversion > 'AC1009': if name not in self.layouts: raise DXFValueError("Layout '{}' does not exist.".format(name)) else: self.layouts.delete(name) else: raise DXFVersionError('delete_layout() not supported for DXF version R12.')
def new_layout(self, name, dxfattribs=None): if self.dxfversion > 'AC1009': if name in self.layouts: raise DXFValueError("Layout '{}' already exists.".format(name)) else: return self.layouts.new(name, dxfattribs) else: raise DXFVersionError('new_layout() not supported for DXF version R12.')
def add_lwpolyline(self, points, dxfattribs=None): 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) lwpolyline.closed = closed return lwpolyline
def _validate_dxf_version(self, version: str) -> str: version = version.upper() version = acad_release_to_dxf_version.get(version, version) # translates 'R12' -> 'AC1009' if version not in versions_supported_by_save: raise DXFVersionError(f'Unsupported DXF version "{version}".') if version == DXF12: if self._dxfversion > DXF12: logger.warning(f'Downgrade from DXF {self.acad_release} to R12 may create an invalid DXF file.') elif version < self._dxfversion: logger.info(f'Downgrade from DXF {self.acad_release} to {acad_release[version]} can cause lost of features.') return version
def set_dimline_format(self, color: int = None, linetype: str = None, lineweight: int = None, extension: float = None, disable1: bool = None, disable2: bool = None): """ Set dimension line properties Args: color: color index linetype: linetype as string, requires DXF R2007+ lineweight: line weight as int, 13 = 0.13mm, 200 = 2.00mm, requires DXF R2000+ extension: extension length disable1: True to suppress first part of dimension line, requires DXF R2000+ disable2: True to suppress second part of dimension line, requires DXF R2000+ """ if color is not None: self.dxf.dimclrd = color if extension is not None: self.dxf.dimdle = extension try: if lineweight is not None: self.dxf.dimlwd = lineweight if disable1 is not None: self.dxf.dimsd1 = disable1 if disable2 is not None: self.dxf.dimsd2 = disable2 except const.DXFAttributeError: raise DXFVersionError( 'DIMLWD, DIMSD1 and DIMSD2 requires DXF R2000+') try: if linetype is not None: self.dxf.dimltype = linetype except const.DXFAttributeError: raise DXFVersionError('DIMLTYPE requires DXF R2007+')
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_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_underlay_def(self, filename, format='ext', name=None): """ Add an underlay definition to the objects section. Args: format: file format as string pdf|dwf|dgn or ext=get format from filename extension name: underlay name, None for an auto-generated name """ if self.dxfversion < 'AC1015': raise DXFVersionError('The UNDERLAY entity needs at least DXF version R2000 or later.') if format == 'ext': format = filename[-3:] return self.objects.add_underlay_def(filename, format, name)
def new(dxfversion='AC1009'): from .lldxf.const import versions_supported_by_new, acad_release_to_dxf_version dxfversion = dxfversion.upper() dxfversion = acad_release_to_dxf_version.get(dxfversion, dxfversion) # translates 'R12' -> 'AC1009' if dxfversion not in versions_supported_by_new: raise DXFVersionError("Can not create DXF drawings, unsupported DXF version '{}'.".format(dxfversion)) finder = TemplateLoader(options.template_dir) stream = finder.getstream(dxfversion.upper()) try: dwg = Drawing.read(stream) finally: stream.close() dwg._setup_metadata() return dwg
def set_extline2(self, linetype: str = None, disable=False): """ Set extension line 2 attributes. Args: linetype: linetype for extension line 2, requires DXF R2007+ disable: disable extension line 2 if True """ if disable: self.dxf.dimse2 = 1 try: if linetype is not None: self.dxf.dimltex2 = linetype except const.DXFAttributeError: raise DXFVersionError('DIMLTEX2 requires DXF R2007+')
def add_underlay(self, underlay_def, insert=(0, 0, 0), scale=(1, 1, 1), rotation=0., dxfattribs=None): 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_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_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_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 set_text_format(self, prefix: str = '', postfix: str = '', rnd: float = None, dec: int = None, sep: str = None, leading_zeros: bool = True, trailing_zeros: bool = True): """ Set dimension text format, like prefix and postfix string, rounding rule and number of decimal places. Args: prefix: Dimension text prefix text as string postfix: Dimension text postfix text as string rnd: Rounds all dimensioning distances to the specified value, for instance, if DIMRND is set to 0.25, all distances round to the nearest 0.25 unit. If you set DIMRND to 1.0, all distances round to the nearest integer. dec: Sets the number of decimal places displayed for the primary units of a dimension. requires DXF R2000+ sep: "." or "," as decimal separator requires DXF R2000+ leading_zeros: suppress leading zeros for decimal dimensions if False trailing_zeros: suppress trailing zeros for decimal dimensions if False """ if prefix or postfix: self.dxf.dimpost = prefix + '<>' + postfix if rnd is not None: self.dxf.dimrnd = rnd # works only with decimal dimensions not inch and feet, US user set dimzin directly if leading_zeros is not None or trailing_zeros is not None: dimzin = 0 if leading_zeros is False: dimzin = const.DIMZIN_SUPPRESSES_LEADING_ZEROS if trailing_zeros is False: dimzin += const.DIMZIN_SUPPRESSES_TRAILING_ZEROS self.dxf.dimzin = dimzin try: if dec is not None: self.dxf.dimdec = dec if sep is not None: self.dxf.dimdsep = ord(sep) except const.DXFAttributeError: raise DXFVersionError('DIMDSEP and DIMDEC require DXF R2000+')
def __init__(self, dxfversion=DXF2013): self.entitydb = EntityDB() self.dxffactory = EntityFactory(self) self.tracker = Tracker() target_dxfversion = dxfversion.upper() self._dxfversion: str = acad_release_to_dxf_version.get( target_dxfversion, target_dxfversion) if self._dxfversion not in versions_supported_by_new: raise DXFVersionError( f'Unsupported DXF version "{self.dxfversion}".') # Store original dxf version if loaded (and maybe converted R13/14) from file. self._loaded_dxfversion: Optional[str] = None self.encoding: str = 'cp1252' # read/write self.filename: Optional[str] = None # named objects dictionary self.rootdict: 'Dictionary' = None # DXF sections self.header: HeaderSection = None self.classes: ClassesSection = None self.tables: TablesSection = None self.blocks: BlocksSection = None self.entities: EntitySection = None self.objects: ObjectsSection = None # DXF R2013 and later self.acdsdata: AcDsDataSection = None self.stored_sections = [] self.layouts: Layouts = None self.groups: GroupCollection = None self.materials: MaterialCollection = None self.mleader_styles: MLeaderStyleCollection = None self.mline_styles: MLineStyleCollection = None self._acad_compatible = True # will generated DXF file compatible with AutoCAD self._dimension_renderer = DimensionRenderer( ) # set DIMENSION rendering engine self._acad_incompatibility_reason = set( ) # avoid multiple warnings for same reason # Don't create any new entities here: # New created handles could collide with handles loaded from DXF file. assert len(self.entitydb) == 0
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_ellipse(self, center, major_axis=(1, 0, 0), ratio=1, start_param=0, end_param=6.283185307, dxfattribs=None): 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_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 set_gradient(self, color1: 'RGB' = (0, 0, 0), color2: 'RGB' = (255, 255, 255), rotation: float = 0., centered: float = 0., one_color: int = 0, tint: float = 0., name: str = 'LINEAR') -> None: if self.drawing is not None and self.drawing.dxfversion < 'AC1018': raise DXFVersionError( "Gradient support requires at least DXF version AC1018, this drawing is:%s" % self.drawing.dxfversion) gradient_data = GradientData() gradient_data.color1 = color1 gradient_data.color2 = color2 gradient_data.one_color = one_color gradient_data.rotation = rotation gradient_data.centered = centered gradient_data.tint = tint gradient_data.name = name self._set_gradient(gradient_data)