def dxftags(self) -> Tags: def flag(flag): return 1 if self.dxf.flags & flag else 0 dxf = self.dxf tags = [ DXFTag(1001, 'ACAD'), DXFTag(1000, 'MVIEW'), DXFTag(1002, '{'), DXFTag(1070, 16), # extended data version, always 16 for R11/12 DXFVertex(1010, dxf.view_target_point), DXFVertex(1010, dxf.view_direction_vector), DXFTag(1040, dxf.view_twist_angle), DXFTag(1040, dxf.view_height), DXFTag(1040, dxf.view_center_point[0]), DXFTag( 1040, dxf.view_center_point[1], ), DXFTag(1040, dxf.perspective_lens_length), DXFTag(1040, dxf.front_clip_plane_z_value), DXFTag(1040, dxf.back_clip_plane_z_value), DXFTag(1070, dxf.render_mode), DXFTag(1070, dxf.circle_zoom), DXFTag(1070, flag(const.VSF_FAST_ZOOM)), DXFTag(1070, dxf.ucs_icon), DXFTag(1070, flag(const.VSF_SNAP_MODE)), DXFTag(1070, flag(const.VSF_GRID_MODE)), DXFTag(1070, flag(const.VSF_ISOMETRIC_SNAP_STYLE)), DXFTag(1070, 0), # snap isopair ??? DXFTag(1040, dxf.snap_angle), DXFTag(1040, dxf.snap_base_point[0]), DXFTag(1040, dxf.snap_base_point[1]), DXFTag(1040, dxf.snap_spacing[0]), DXFTag(1040, dxf.snap_spacing[1]), DXFTag(1040, dxf.grid_spacing[0]), DXFTag(1040, dxf.grid_spacing[1]), DXFTag(1070, flag(const.VSF_HIDE_PLOT_MODE)), DXFTag(1002, '{'), # start frozen layer list ] tags.extend( DXFTag(1003, layer_name) for layer_name in self.frozen_layers) tags.extend([ DXFTag(1002, '}'), # end of frozen layer list DXFTag(1002, '}'), # MVIEW ]) return Tags(tags)
def round_tags(tags: Tags, ndigits: int) -> Iterator[DXFTag]: for tag in tags: if isinstance(tag, DXFVertex): yield DXFVertex(tag.code, (round(d, ndigits) for d in tag.value)) elif isinstance(tag.value, float): yield DXFTag(tag.code, round(tag.value, ndigits)) # type: ignore else: yield tag
def dxftags(self): return [ DXFTag(72, 2), # edge type DXFVertex(10, self.center), DXFTag(40, self.radius), DXFTag(50, self.start_angle), DXFTag(51, self.end_angle), DXFTag(73, self.is_counter_clockwise), ]
def dxftags(self): tags = [ DXFTag(72, 4), # edge type DXFTag(94, int(self.degree)), DXFTag(73, int(self.rational)), DXFTag(74, int(self.periodic)), DXFTag(95, len(self.knot_values)), # number of knots DXFTag(96, len(self.control_points)), # number of control points ] # build knot values list # knot values have to be present and valid, otherwise AutoCAD crashes if len(self.knot_values): tags.extend( [DXFTag(40, float(value)) for value in self.knot_values]) else: raise DXFValueError("SplineEdge: missing required knot values") # build control points # control points have to be present and valid, otherwise AutoCAD crashes tags.extend([ DXFVertex(10, (float(value[0]), float(value[1]))) for value in self.control_points ]) # build weights list, optional tags.extend([DXFTag(42, float(value)) for value in self.weights]) # build fit points # fit points have to be present and valid, otherwise AutoCAD crashes # edit 2016-12-20: this is not true - there are cases with no fit points and without crashing AutoCAD tags.append(DXFTag(97, len(self.fit_points))) tags.extend([ DXFVertex(11, (float(value[0]), float(value[1]))) for value in self.fit_points ]) tags.append( DXFVertex( 12, (float(self.start_tangent[0]), float(self.start_tangent[1])))) tags.append( DXFVertex( 13, (float(self.end_tangent[0]), float(self.end_tangent[1])))) return tags
def dxftags(self) -> Iterable[DXFTag]: for point in self: x, y, start_width, end_width, bulge = point yield DXFVertex(self.VERTEX_CODE, (x, y)) if start_width: yield DXFTag(self.START_WIDTH_CODE, start_width) if end_width: yield DXFTag(self.END_WIDTH_CODE, end_width) if bulge: yield DXFTag(self.BULGE_CODE, bulge)
def set_mesh_data(self, vertices=None, faces=None): if vertices is None: vertices = [] else: vertices = list(vertices) if faces is None: faces = [] else: faces = list(faces) self._remove_mesh_data() geodata = self.AcDbGeoData geodata.append(DXFTag(93, len(vertices))) for source, target in vertices: geodata.append(DXFVertex(13, (source[0], source[1]))) geodata.append(DXFVertex(14, (target[0], target[1]))) geodata.append(DXFTag(96, len(faces))) for face in faces: geodata.extend( DXFTag(code, index) for code, index in zip((97, 98, 99), face))
def set_seed_points(self, points: Sequence[Tuple[float, float]]) -> None: if len(points) < 1: raise DXFValueError("Param points should be a collection of 2D points and requires at least one point.") hatch_tags = self.AcDbHatch first_seed_point_index = self._get_seed_point_index(hatch_tags) existing_seed_points = hatch_tags.collect_consecutive_tags([10], start=first_seed_point_index) # don't rely on 'Number of seed points' new_seed_points = [DXFVertex(10, (point[0], point[1])) for point in points] # only use x and y coordinate, self.dxf.n_seed_points = len(new_seed_points) # set new count of seed points # replace existing seed points hatch_tags[first_seed_point_index: first_seed_point_index + len(existing_seed_points)] = new_seed_points
def dxftags(self) -> Iterable[DXFTag]: for point in self: x, y, start_width, end_width, bulge = point yield DXFVertex(self.VERTEX_CODE, (x, y)) if start_width or end_width: # Export always start- and end width together, # required for BricsCAD but not AutoCAD! yield DXFTag(self.START_WIDTH_CODE, start_width) yield DXFTag(self.END_WIDTH_CODE, end_width) if bulge: yield DXFTag(self.BULGE_CODE, bulge)
def tag_compiler(tags: Tags) -> Iterable[DXFTag]: """Special tag compiler for the DXF browser. This compiler should never fail and always return printable tags: - invalid point coordinates are returned as float("nan") - invalid ints are returned as string - invalid floats are returned as string """ def to_float(v: str) -> float: try: return float(v) except ValueError: return float("NaN") count = len(tags) index = 0 while index < count: code, value = tags[index] if code in POINT_CODES: try: y_code, y_value = tags[index + 1] except IndexError: # x-coord as last tag yield DXFTag(code, to_float(value)) return if y_code != code + 10: # not an y-coord? yield DXFTag(code, to_float(value)) # x-coord as single tag index += 1 continue try: z_code, z_value = tags[index + 2] except IndexError: # no z-coord exist z_code = 0 z_value = 0 point: Sequence[float] if z_code == code + 20: # is a z-coord? point = (to_float(value), to_float(y_value), to_float(z_value)) index += 3 else: # a valid 2d point(x, y) point = (to_float(value), to_float(y_value)) index += 2 yield DXFVertex(code, point) else: # a single tag try: if code == 0: value = value.strip() yield DXFTag(code, TYPE_TABLE.get(code, str)(value)) except ValueError: yield DXFTag(code, str(value)) # just as string index += 1
def dxftags(self) -> List[DXFTag]: has_bulge = self.has_bulge() vtags = [] for x, y, bulge in self.vertices: vtags.append(DXFVertex(10, (float(x), float(y)))) if has_bulge: vtags.append(DXFTag(42, float(bulge))) tags = [ DXFTag(92, int(self.path_type_flags)), DXFTag(72, int(has_bulge)), DXFTag(73, int(self.is_closed)), DXFTag(93, len(self.vertices)), ] tags.extend(vtags) tags.extend(build_source_boundary_object_tags(self.source_boundary_objects)) return tags
def test_init(): v = DXFVertex(10, (1, 2, 3)) assert v.value == (1., 2., 3.)
def test_dxf_string(): v = DXFVertex(10, (1, 2, 3)) assert v.dxfstr() == " 10\n1.0\n 20\n2.0\n 30\n3.0\n"
def test_dxf_tags(): v = DXFVertex(10, (1, 2, 3)) tags = tuple(v.dxftags()) assert tags[0] == (10, 1.) assert tags[1] == (20, 2.) assert tags[2] == (30, 3.)
def test_clone(): v = DXFVertex(10, (1, 2, 3)) v2 = v.clone() assert v2.code == v.code assert v2.value == v.value
def test_xdata_string(): v = DXFVertex(1011, (1, 2, 3)) assert v.dxfstr() == "1011\n1.0\n1021\n2.0\n1031\n3.0\n"
def byte_tag_compiler(tags: Iterable[DXFTag], encoding=const.DEFAULT_ENCODING) -> Iterable[DXFTag]: """ Compiles DXF tag values imported by bytes_loader() into Python types. Raises DXFStructureError() for invalid float values and invalid coordinate values. Expects DXF coordinates written in x, y[, z] order, see function :func:`safe_tag_loader` for usage with applied repair filters. Args: tags: DXF tag generator, yielding tag values as bytes like bytes_loader() encoding: text encoding Raises: DXFStructureError: Found invalid DXF tag or unexpected coordinate order. """ def error_msg(tag): code = tag.code value = tag.value.decode(encoding) return f'Invalid tag ({code}, "{value}") near line: {line}.' tags = iter(tags) undo_tag = None line = 0 while True: try: if undo_tag is not None: x = undo_tag undo_tag = None else: x = next(tags) line += 2 code = x.code if code in POINT_CODES: y = next(tags) # y coordinate is mandatory line += 2 # e.g. y-code for x-code=10 is 20 if y.code != code + 10: raise const.DXFStructureError( f"Missing required y-coordinate near line: {line}.") # optional z coordinate z = next(tags) line += 2 try: # is it a z-coordinate like (30, 0.0) for base x-code=10 if z.code == code + 20: point = (float(x.value), float(y.value), float(z.value)) else: point = (float(x.value), float(y.value)) undo_tag = z except ValueError: raise const.DXFStructureError( f'Invalid floating point values near line: {line}.') yield DXFVertex(code, point) elif code in BINARY_DATA: # maybe pre compiled in low level tagger (binary DXF) if isinstance(x, DXFBinaryTag): tag = x else: try: tag = DXFBinaryTag.from_string(code, x.value) except ValueError: raise const.DXFStructureError( f'Invalid binary data near line: {line}.') yield tag else: # just a single tag type_ = TYPE_TABLE.get(code, str) value: bytes = x.value if type_ is str: if code == 0: # remove white space from structure tags value = x.value.strip() yield DXFTag(code, value.decode(encoding)) else: try: # fast path for int and float yield DXFTag(code, type_(value)) except ValueError: # slow path - ProE stores int values as floats :(( if type_ is int: try: yield DXFTag(code, int(float(x.value))) except ValueError: raise const.DXFStructureError(error_msg(x)) else: raise const.DXFStructureError(error_msg(x)) except StopIteration: return
def dxftags(self): return [ DXFTag(72, 1), # edge type DXFVertex(10, self.start), DXFVertex(11, self.end), ]
def test_write_point_tag(): s, t = setup_stream() t.write_tag(DXFVertex(10, (7., 8., 9.))) result = s.getvalue() assert result == ' 10\n7.0\n 20\n8.0\n 30\n9.0\n'
def _set_path_tags(self, vertices): boundary = [DXFVertex(11, value) for value in vertices] subclasstags = Tags(tag for tag in self.tags.subclasses[2] if tag.code != 11) # filter out existing path tags subclasstags.extend(boundary) self.tags.subclasses[2] = subclasstags
def test_strtag2_vector(): assert ' 10\n1.0\n 20\n2.0\n 30\n3.0\n' == DXFVertex(10, Vec3(1, 2, 3)).dxfstr() assert ' 10\n1.0\n 20\n2.0\n 30\n3.0\n' == DXFVertex(10, Vec3((1, 2, 3))).dxfstr() assert ' 10\n1.0\n 20\n2.0\n 30\n0.0\n' == DXFVertex(10, Vec3(1, 2)).dxfstr() assert ' 10\n1.0\n 20\n2.0\n 30\n0.0\n' == DXFVertex(10, Vec3((1, 2))).dxfstr()
def byte_tag_compiler( tags: Iterable[DXFTag], encoding=const.DEFAULT_ENCODING, messages: List = None, errors: str = 'surrogateescape', ) -> Iterable[DXFTag]: """ Compiles DXF tag values imported by bytes_loader() into Python types. Raises DXFStructureError() for invalid float values and invalid coordinate values. Expects DXF coordinates written in x, y[, z] order, see function :func:`safe_tag_loader` for usage with applied repair filters. Args: tags: DXF tag generator, yielding tag values as bytes like bytes_loader() encoding: text encoding messages: list to store error messages errors: specify decoding error handler - "surrogateescape" to preserve possible binary data (default) - "ignore" to use the replacement char U+FFFD "\ufffd" for invalid data - "strict" to raise an :class:`UnicodeDecodeError` exception for invalid data Raises: DXFStructureError: Found invalid DXF tag or unexpected coordinate order. """ def error_msg(tag): code = tag.code value = tag.value.decode(encoding) return f'Invalid tag ({code}, "{value}") near line: {line}.' if messages is None: messages = [] tags = iter(tags) undo_tag = None line = 0 while True: try: if undo_tag is not None: x = undo_tag undo_tag = None else: x = next(tags) line += 2 code = x.code if code in POINT_CODES: y = next(tags) # y coordinate is mandatory line += 2 # e.g. y-code for x-code=10 is 20 if y.code != code + 10: raise const.DXFStructureError( f"Missing required y-coordinate near line: {line}.") # optional z coordinate z = next(tags) line += 2 try: # is it a z-coordinate like (30, 0.0) for base x-code=10 if z.code == code + 20: try: point = (float(x.value), float(y.value), float(z.value)) except ValueError: # search for any float values point = (_search_float(x.value), _search_float(y.value), _search_float(z.value)) else: try: point = (float(x.value), float(y.value)) except ValueError: # seach for any float values point = ( _search_float(x.value), _search_float(y.value), ) undo_tag = z except ValueError: raise const.DXFStructureError( f'Invalid floating point values near line: {line}.') yield DXFVertex(code, point) elif code in BINARY_DATA: # maybe pre compiled in low level tagger (binary DXF) if isinstance(x, DXFBinaryTag): tag = x else: try: tag = DXFBinaryTag.from_string(code, x.value) except ValueError: raise const.DXFStructureError( f'Invalid binary data near line: {line}.') yield tag else: # just a single tag type_ = TYPE_TABLE.get(code, str) value: bytes = x.value if type_ is str: if code == 0: # remove white space from structure tags value = x.value.strip().upper() try: # 2 stages to document decoding errors str_ = value.decode(encoding, errors='strict') except UnicodeDecodeError: str_ = value.decode(encoding, errors=errors) messages.append( (AuditError.DECODING_ERROR, f'Fixed unicode decoding error near line {line}')) # Convert DXF unicode notation "\U+xxxx" to unicode, # but exclude structure tags (code>0): if code and has_dxf_unicode(str_): str_ = decode_dxf_unicode(str_) yield DXFTag(code, str_) else: try: # fast path for int and float yield DXFTag(code, type_(value)) except ValueError: # slow path - e.g. ProE stores int values as floats :(( if type_ is int: try: yield DXFTag(code, _search_int(x.value)) except ValueError: raise const.DXFStructureError(error_msg(x)) elif type_ is float: try: yield DXFTag(code, _search_float(x.value)) except ValueError: raise const.DXFStructureError(error_msg(x)) else: raise const.DXFStructureError(error_msg(x)) except StopIteration: return
def test_write_point_tag(self, t): t.write_tag(DXFVertex(10, (7., 8., 9.))) assert t.tags[0] == (10, 7.) assert t.tags[1] == (20, 8.) assert t.tags[2] == (30, 9.)
def _set_path_tags(self, vertices: Sequence[Tuple[float, float]]): boundary = [DXFVertex(14, value) for value in vertices] subclasstags = Tags(tag for tag in self.tags.subclasses[2] if tag.code != 14) subclasstags.extend(boundary) self.tags.subclasses[2] = subclasstags self.dxf.count_boundary_points = len(vertices)