def __delitem__(self, handle: str) -> None: """ Delete entity by `handle`. Removes entity only from database, does not destroy the entity. """ if self.locked: raise DXFInternalEzdxfError('Locked entity database.') del self._database[handle]
def register_entity(cls): name = cls.DXFTYPE if name in ENTITY_CLASSES: raise DXFInternalEzdxfError( f'Double registration for DXF type {name}.') ENTITY_CLASSES[name] = cls return cls
def dim_style(self): if self.drawing is not None: dim_style_name = self.dxf.dimstyle # raises ValueError if not exists, but all used dim styles should exists! return self.drawing.dimstyles.get(dim_style_name) else: raise DXFInternalEzdxfError('Dimension.drawing attribute not initialized.')
def get_dim_style(self) -> 'DimStyle': """ Returns the associated :class:`DimStyle` entity. """ if self.doc is None: raise DXFInternalEzdxfError('Dimension.drawing attribute not initialized.') dim_style_name = self.dxf.dimstyle # raises ValueError if not exists, but all used dim styles should exists! return self.doc.dimstyles.get(dim_style_name)
def active_layout(self) -> Paperspace: """ Returns the active paperspace layout. """ for layout in self: if layout.is_active_paperspace: return cast(Paperspace, layout) raise DXFInternalEzdxfError('No active paperspace layout found.')
def export_entity(self, tagwriter: 'TagWriter') -> None: super().export_entity(tagwriter) # AcDbEntity export is done by parent class if tagwriter.dxfversion == DXF12: raise DXFInternalEzdxfError('Exporting BLOCK_RECORDS for DXF R12.') tagwriter.write_tag2(SUBCLASS_MARKER, acdb_symbol_table_record.name) tagwriter.write_tag2(SUBCLASS_MARKER, acdb_blockrec.name) self.dxf.export_dxf_attribs(tagwriter, ['name', 'layout', 'units', 'explode', 'scale'])
def active_layout(self) -> 'Layout': """ Returns active paper space layout. """ for layout in self: if layout.block_record_name.upper() == '*PAPER_SPACE': return layout raise DXFInternalEzdxfError('No active paper space found.')
def __setitem__(self, handle: str, entity: DXFEntity) -> None: """ Set `entity` for `handle`. """ assert isinstance(handle, str), type(handle) assert isinstance(entity, DXFEntity), type(entity) if self.locked: raise DXFInternalEzdxfError('Locked entity database.') if handle == '0' or not is_valid_handle(handle): raise ValueError(f'Invalid handle {handle}.') self._database[handle] = entity
def set_acad_dstyle(self, data: dict) -> None: if self.drawing is None: raise DXFInternalEzdxfError( 'Dimension.drawing attribute not initialized.') # replace virtual 'dimtxsty' attribute by 'dimtxsty_handle' if _DIMTXSTY in data: dimtxsty = data.pop(_DIMTXSTY) txtstyle = self.drawing.styles.get(dimtxsty) data['dimtxsty_handle'] = txtstyle.dxf.handle super().set_acad_dstyle(data)
def __setitem__(self, handle: str, entity: DXFEntity) -> None: """Set `entity` for `handle`.""" assert isinstance(handle, str), type(handle) assert isinstance(entity, DXFEntity), type(entity) assert entity.is_alive, "Can not store destroyed entity." if self.locked: raise DXFInternalEzdxfError("Locked entity database.") if handle == "0" or not is_valid_handle(handle): raise ValueError(f"Invalid handle {handle}.") self._database[handle] = entity
def export_entity(self, tagwriter: "TagWriter") -> None: super().export_entity(tagwriter) if tagwriter.dxfversion == DXF12: raise DXFInternalEzdxfError("Exporting BLOCK_RECORDS for DXF R12.") tagwriter.write_tag2(SUBCLASS_MARKER, acdb_symbol_table_record.name) tagwriter.write_tag2(SUBCLASS_MARKER, acdb_blockrec.name) self.dxf.export_dxf_attribs( tagwriter, [ "name", "layout", "units", "explode", "scale", ], )
def set_acad_dstyle(self, data: dict) -> None: """ Set XDATA section ACAD:DSTYLE, to override DIMSTYLE attributes for this DIMENSION entity. Args: data: ``dict`` with DIMSTYLE attribute names as keys. (internal API) """ if self.doc is None: raise DXFInternalEzdxfError( 'Dimension.doc attribute not initialized.') # ezdxf uses internally only resource names for arrows, line types and # text styles, but DXF 2000 and later requires handles for these # resources: actual_dxfversion = self.doc.dxfversion data = self.dim_style_attr_names_to_handles(data, actual_dxfversion) tags = [] dim_style_attributes = self.dim_style_attributes() for key, value in data.items(): if key not in dim_style_attributes: # Ignore unknown attributes logging.debug(f'Ignore unknown DIMSTYLE attribute: "{key}"') continue dxf_attr = dim_style_attributes.get(key) if dxf_attr and dxf_attr.code > 0: # skip internal and virtual tags if dxf_attr.dxfversion > actual_dxfversion: logging.debug( f'Unsupported DIMSTYLE attribute "{key}" for ' f'DXF version {self.doc.acad_release}') continue code = dxf_attr.code tags.append((1070, code)) if code == 5: # DimStyle 'dimblk' has group code 5 but is not a handle tags.append((1000, value)) else: tags.append((get_xcode_for(code), value)) if len(tags): self.set_xdata_list('ACAD', 'DSTYLE', tags)
def export_dxf(self, tagwriter: 'TagWriter') -> None: if self.dxf.handle is None: raise DXFInternalEzdxfError( 'TABLE needs a handle, maybe loaded from DXF R12 without handle!' ) # 1. tag: (0, DXFTYPE) tagwriter.write_tag2(STRUCTURE_MARKER, self.DXFTYPE) tagwriter.write_tag2(2, self.dxf.name) if tagwriter.dxfversion >= DXF2000: tagwriter.write_tag2(5, self.dxf.handle) if self.extension_dict: self.extension_dict.export_dxf(tagwriter) tagwriter.write_tag2(OWNER_CODE, self.dxf.owner) tagwriter.write_tag2(SUBCLASS_MARKER, acdb_symbol_table.name) tagwriter.write_tag2(70, self.dxf.count) if self.dxf.name == 'DIMSTYLE': # the one exception - typical Autodesk tagwriter.write_tag2(SUBCLASS_MARKER, 'AcDbDimStyleTable') else: # DXF R12 # TABLE does not need a handle at all tagwriter.write_tag2(70, self.dxf.count)
def frozen_layers(self) -> List[str]: """ Set/get frozen layers as list of layer names. """ type_ = self._frozen_layers_content_type if type_ == 'names': return self._frozen_layers bag: List[str] = [] entitydb = self.doc.entitydb if type_ == 'handles': for handle in self._frozen_layers: try: bag.append(entitydb[handle].dxf.name) except KeyError: # ignore non-existing layers pass else: raise DXFInternalEzdxfError( f'invalid frozen_layer_content_type: "{type_}"') self._frozen_layers = bag self._frozen_layers_content_type = 'names' return bag
def __init__( self, dimension: "Dimension", ucs: "UCS" = None, override: "DimStyleOverride" = None, ): super().__init__(dimension, ucs, override) dimtype = self.dimension.dimtype measurement = self.measurement if dimtype == 3: self.is_diameter_dim = True elif dimtype == 4: self.is_radius_dim = True else: raise DXFInternalEzdxfError(f"Invalid dimension type {dimtype}") self.center: Vec2 = self._center() # override in diameter dimension self.point_on_circle: Vec2 = Vec2(self.dimension.dxf.defpoint4) # modify parameters for special scenarios if measurement.user_location is None: if ( measurement.text_is_inside and measurement.text_inside_horizontal and measurement.text_movement_rule == 1 ): # move text, add leader # use algorithm for user define dimension line location measurement.user_location = self.center.lerp( self.point_on_circle ) measurement.text_valign = 0 # text vertical centered direction = self.point_on_circle - self.center self.dim_line_vec = direction.normalize() self.dim_line_angle = self.dim_line_vec.angle_deg self.radius = direction.magnitude # get_measurement() works for radius and diameter dimension measurement.update(self.dimension.get_measurement()) self.outside_default_distance = self.radius + 2 * self.arrows.arrow_size self.outside_default_defpoint = self.center + ( self.dim_line_vec * self.outside_default_distance ) self.outside_text_force_dimline = bool(self.dim_style.get("dimtofl", 1)) # final dimension text (without limits or tolerance) # default location is outside, if not forced to be inside measurement.text_is_outside = not measurement.force_text_inside # text_outside: user defined location, overrides default location if measurement.user_location is not None: measurement.text_is_outside = self.is_location_outside( measurement.user_location ) self._total_text_width: float = 0.0 if measurement.text: # text width and required space self._total_text_width = self.total_text_width() if self.tol.has_limits: # limits show the upper and lower limit of the measurement as # stacked values and with the size of tolerances self.tol.update_limits(measurement.value) # default rotation is angle of dimension line, from center to point on circle. rotation = self.dim_line_angle if measurement.text_is_outside and measurement.text_outside_horizontal: rotation = 0.0 elif measurement.text_is_inside and measurement.text_inside_horizontal: rotation = 0.0 # final absolute text rotation (x-axis=0) measurement.text_rotation = normalize_text_angle( rotation, fix_upside_down=True ) # final text location measurement.text_location = self.get_text_location() self.geometry.set_text_box(self.init_text_box()) # write final text location into DIMENSION entity if measurement.user_location: self.dimension.dxf.text_midpoint = measurement.user_location # default locations elif ( measurement.text_is_outside and measurement.text_outside_horizontal ): self.dimension.dxf.text_midpoint = self.outside_default_defpoint else: self.dimension.dxf.text_midpoint = measurement.text_location
def __init__(self, dimension: 'Dimension', ucs: 'UCS' = None, override: 'DimStyleOverride' = None): super().__init__(dimension, ucs, override) dimtype = self.dimension.dimtype if dimtype == 3: self.is_diameter_dim = True self.text_prefix = 'Ø' elif dimtype == 4: self.is_radius_dim = True self.text_prefix = 'R' else: raise DXFInternalEzdxfError(f'Invalid dimension type {dimtype}') self.center = self._center() # override in diameter dimension self.point_on_circle = Vec2(self.dimension.dxf.defpoint4) # modify parameters for special scenarios if self.user_location is None: # default location if self.text_inside and self.text_inside_horizontal and self.text_movement_rule == 1: # move text, add leader # use algorithm for user define dimension line location self.user_location = self.center.lerp(self.point_on_circle) self.text_valign = 0 # text vertical centered direction = self.point_on_circle - self.center self.dim_line_vec = direction.normalize() self.dim_line_angle = self.dim_line_vec.angle_deg self.radius = direction.magnitude # get_measurement() works for radius and diameter dimension self.measurement = self.dimension.get_measurement() self.outside_default_distance = self.radius + 2 * self.arrow_size self.outside_default_defpoint = self.center + ( self.dim_line_vec * self.outside_default_distance) self.outside_text_force_dimline = self.dim_style.get('dimtofl', 1) # final dimension text (without limits or tolerance) self.text = self.text_override( self.measurement * self.dim_measurement_factor) # type: str # default location is outside, if not forced to be inside self.text_outside = not self.force_text_inside # text_outside: user defined location, overrides default location if self.user_location is not None: self.text_outside = self.is_location_outside(self.user_location) if self.text: # text width and required space self.dim_text_width = self.text_width(self.text) # type: float if self.dim_tolerance: self.dim_text_width += self.tol_text_width elif self.dim_limits: # limits show the upper and lower limit of the measurement as stacked values # and with the size of tolerances measurement = self.measurement * self.dim_measurement_factor self.measurement_upper_limit = measurement + self.tol_maximum self.measurement_lower_limit = measurement - self.tol_minimum self.tol_text_upper = self.format_tolerance_text( self.measurement_upper_limit) # Only the lower limit has a text prefix self.tol_text_lower = self.text_prefix + self.format_tolerance_text( self.measurement_lower_limit) self.tol_text_width = self.tolerance_text_width( max(len(self.tol_text_upper), len(self.tol_text_lower))) # only limits are displayed so: self.dim_text_width = self.tol_text_width # default rotation is angle of dimension line, from center to point on circle. rotation = self.dim_line_angle if self.text_outside and self.text_outside_horizontal: rotation = 0 elif self.text_inside and self.text_inside_horizontal: rotation = 0 # final absolute text rotation (x-axis=0) self.text_rotation = normalize_text_angle(rotation, fix_upside_down=True) # final text location self.text_location = self.get_text_location() # type: Vec2 self.text_box = TextBox(center=self.text_location, width=self.dim_text_width, height=self.text_height, angle=self.text_rotation, gap=self.text_gap * .75) # write final text location into DIMENSION entity if self.user_location: self.dimension.dxf.text_midpoint = self.user_location # default locations elif self.text_outside and self.text_outside_horizontal: self.dimension.dxf.text_midpoint = self.outside_default_defpoint else: self.dimension.dxf.text_midpoint = self.text_location