Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Treyer <treyer@arch.ethz.ch>2016-08-18 01:13:05 +0300
committerLukas Treyer <treyer@arch.ethz.ch>2016-08-20 16:11:27 +0300
commitc6af87952d78261c7bc87f730f1ad395145a0610 (patch)
tree9b9471809b1b68d219b0336d30701d0435da4b31 /io_import_dxf
parentc342d0c69bc59c00309aa078c905237651b8376f (diff)
related to the latest file in T48806 I found a small bug, which is fixed now. Also dxfgrabber seemed to be incomplete in the last commit.
Diffstat (limited to 'io_import_dxf')
-rw-r--r--io_import_dxf/dxfgrabber/dxfentities.py1264
-rw-r--r--io_import_dxf/dxfgrabber/dxfobjects.py99
-rw-r--r--io_import_dxf/dxfimport/is_.py2
3 files changed, 1365 insertions, 0 deletions
diff --git a/io_import_dxf/dxfgrabber/dxfentities.py b/io_import_dxf/dxfgrabber/dxfentities.py
new file mode 100644
index 00000000..5c83a882
--- /dev/null
+++ b/io_import_dxf/dxfgrabber/dxfentities.py
@@ -0,0 +1,1264 @@
+# encoding: utf-8
+# Purpose: entity classes, new implementation without dxf12/dxf13 layer
+# Created: 17.04.2016
+# Copyright (C) 2016, Manfred Moitzi
+# License: MIT License
+from __future__ import unicode_literals
+__author__ = "mozman <mozman@gmx.at>"
+
+import math
+
+from . import const
+from .color import TrueColor
+from .styles import default_text_style
+from .decode import decode
+
+SPECIAL_CHARS = {
+ 'd': '°'
+}
+
+
+basic_attribs = {
+ 5: 'handle',
+ 6: 'linetype',
+ 8: 'layer',
+ 39: 'thickness',
+ 48: 'ltscale',
+ 62: 'color',
+ 67: 'paperspace',
+ 210: 'extrusion',
+ 284: 'shadow_mode',
+ 330: 'owner',
+ 370: 'line_weight',
+ 410: 'layout_tab_name',
+}
+
+
+class DXFEntity(object):
+ def __init__(self):
+ self.dxftype = 'ENTITY'
+ self.handle = None
+ self.owner = None
+ self.paperspace = None
+ self.layer = '0'
+ self.linetype = None
+ self.thickness = 0.0
+ self.extrusion = None
+ self.ltscale = 1.0
+ self.line_weight = 0
+ self.invisible = 0
+ self.color = const.BYLAYER
+ self.true_color = None
+ self.transparency = None
+ self.shadow_mode = None
+ self.layout_tab_name = None
+
+ def setup_attributes(self, tags):
+ self.dxftype = tags.get_type()
+ for code, value in tags.plain_tags():
+ if code in basic_attribs:
+ self.__setattr__(basic_attribs[code], value)
+ elif code == 420:
+ self.true_color = TrueColor(value)
+ elif code == 440:
+ self.transparency = 1. - float(value & 0xFF) / 255.
+ else:
+ yield code, value # chain of generators
+
+ def set_default_extrusion(self): # call only for 2d entities with extrusion vector
+ if self.extrusion is None:
+ self.extrusion = (0., 0., 1.)
+
+ def __str__(self):
+ return "{} [{}]".format(self.dxftype, self.handle)
+
+
+class Point(DXFEntity):
+ def __init__(self):
+ super(Point, self).__init__()
+ self.point = (0, 0, 0)
+
+ def setup_attributes(self, tags):
+ for code, value in super(Point, self).setup_attributes(tags):
+ if code == 10:
+ self.point = value
+ else:
+ yield code, value # chain of generators
+ self.set_default_extrusion()
+
+
+class Line(DXFEntity):
+ def __init__(self):
+ super(Line, self).__init__()
+ self.start = (0, 0, 0)
+ self.end = (0, 0, 0)
+
+ def setup_attributes(self, tags):
+ for code, value in super(Line, self).setup_attributes(tags):
+ if code == 10:
+ self.start = value
+ elif code == 11:
+ self.end = value
+ else:
+ yield code, value # chain of generators
+
+
+class Circle(DXFEntity):
+ def __init__(self):
+ super(Circle, self).__init__()
+ self.center = (0, 0, 0)
+ self.radius = 1.0
+
+ def setup_attributes(self, tags):
+ for code, value in super(Circle, self).setup_attributes(tags):
+ if code == 10:
+ self.center = value
+ elif code == 40:
+ self.radius = value
+ else:
+ yield code, value # chain of generators
+ self.set_default_extrusion()
+
+
+class Arc(Circle):
+ def __init__(self):
+ super(Arc, self).__init__()
+ self.start_angle = 0.
+ self.end_angle = 360.
+
+ def setup_attributes(self, tags):
+ for code, value in super(Arc, self).setup_attributes(tags):
+ if code == 50:
+ self.start_angle = value
+ elif code == 51:
+ self.end_angle = value
+ else:
+ yield code, value # chain of generators
+ self.set_default_extrusion()
+
+TRACE_CODES = frozenset((10, 11, 12, 13))
+
+
+class Trace(DXFEntity):
+ def __init__(self):
+ super(Trace, self).__init__()
+ self.points = []
+
+ def setup_attributes(self, tags):
+ for code, value in super(Trace, self).setup_attributes(tags):
+ if code in TRACE_CODES:
+ self.points.append(value)
+ else:
+ yield code, value # chain of generators
+ self.set_default_extrusion()
+
+
+Solid = Trace
+
+
+class Face(Trace):
+ def __init__(self):
+ super(Face, self).__init__()
+ self.points = []
+ self.invisible_edge = 0
+
+ def setup_attributes(self, tags):
+ for code, value in super(Face, self).setup_attributes(tags):
+ if code == 70:
+ self.invisible_edge = value
+ else:
+ yield code, value # chain of generators
+ self.set_default_extrusion()
+
+ def is_edge_invisible(self, edge):
+ # edges 0 .. 3
+ return bool(self.invisible_edge & (1 << edge))
+
+
+class Text(DXFEntity):
+ def __init__(self):
+ super(Text, self).__init__()
+ self.insert = (0., 0.)
+ self.height = 1.0
+ self.text = ""
+ self.rotation = 0.
+ self.oblique = 0.
+ self.style = "STANDARD"
+ self.width = 1.
+ self.is_backwards = False
+ self.is_upside_down = False
+ self.halign = 0
+ self.valign = 0
+ self.align_point = None
+ self.font = ""
+ self.big_font = ""
+
+ def setup_attributes(self, tags):
+ for code, value in super(Text, self).setup_attributes(tags):
+ if code == 10:
+ self.insert = value
+ elif code == 11:
+ self.align_point = value
+ elif code == 1:
+ self.text = value
+ elif code == 7:
+ self.style = value
+ elif code == 40:
+ self.height = value
+ elif code == 41:
+ self.width = value
+ elif code == 50:
+ self.rotation = value
+ elif code == 51:
+ self.oblique = value
+ elif code == 71:
+ self.is_backwards = bool(value & 2)
+ self.is_upside_down = bool(value & 4)
+ elif code == 72:
+ self.halign = value
+ elif code == 73:
+ self.valign = value
+ else:
+ yield code, value # chain of generators
+ self.set_default_extrusion()
+
+ def resolve_text_style(self, text_styles):
+ style = text_styles.get(self.style, None)
+ if style is None:
+ style = default_text_style
+ if self.height == 0:
+ self.height = style.height
+ if self.width == 0:
+ self.width = style.width
+ if self.oblique is None:
+ self.oblique = style.oblique
+ if self.is_backwards is None:
+ self.is_backwards = style.is_backwards
+ if self.is_upside_down is None:
+ self.is_upside_down = style.is_upside_down
+ if self.font is None:
+ self.font = style.font
+ if self.big_font is None:
+ self.big_font = style.big_font
+
+ def plain_text(self):
+ chars = []
+ raw_chars = list(reversed(self.text)) # text splitted into chars, in reversed order for efficient pop()
+ while len(raw_chars):
+ char = raw_chars.pop()
+ if char == '%': # formatting codes and special characters
+ if len(raw_chars) and raw_chars[-1] == '%':
+ raw_chars.pop() # '%'
+ if len(raw_chars):
+ special_char = raw_chars.pop() # command char
+ chars.append(SPECIAL_CHARS.get(special_char, ""))
+ else: # char is just a single '%'
+ chars.append(char)
+ else: # char is what it is, a character
+ chars.append(char)
+ return "".join(chars)
+
+
+class Attrib(Text):
+ def __init__(self):
+ super(Attrib, self).__init__()
+ self.field_length = 0
+ self.tag = ""
+
+ def setup_attributes(self, tags):
+ for code, value in super(Attrib, self).setup_attributes(tags):
+ if code == 2:
+ self.tag = value
+ elif code == 73:
+ self.field_length = value
+ else:
+ yield code, value
+
+
+class Insert(DXFEntity):
+ def __init__(self):
+ super(Insert, self).__init__()
+ self.name = ""
+ self.insert = (0., 0., 0.)
+ self.rotation = 0.
+ self.scale = (1., 1., 1.)
+ self.row_count = 1
+ self.row_spacing = 0.
+ self.col_count = 1
+ self.col_spacing = 0.
+ self.attribsfollow = False
+ self.attribs = []
+
+ def setup_attributes(self, tags):
+ xscale = 1.
+ yscale = 1.
+ zscale = 1.
+ for code, value in super(Insert, self).setup_attributes(tags):
+ if code == 2:
+ self.name = value
+ elif code == 10:
+ self.insert = value
+ elif code == 41:
+ xscale = value
+ elif code == 42:
+ yscale = value
+ elif code == 43:
+ zscale = value
+ elif code == 44:
+ self.col_spacing = value
+ elif code == 45:
+ self.row_spacing = value
+ elif code == 50:
+ self.rotation = value
+ elif code == 66:
+ self.attribsfollow = bool(value)
+ elif code == 70:
+ self.col_count = value
+ elif code == 71:
+ self.row_count = value
+ else:
+ yield code, value # chain of generators
+ self.scale = (xscale, yscale, zscale)
+ self.set_default_extrusion()
+
+ def find_attrib(self, attrib_tag):
+ for attrib in self.attribs:
+ if attrib.tag == attrib_tag:
+ return attrib
+ return None
+
+ def append_data(self, attribs):
+ self.attribs = attribs
+
+
+class Polyline(DXFEntity):
+ LINE_TYPES = frozenset(('spline2d', 'polyline2d', 'polyline3d'))
+
+ def __init__(self):
+ super(Polyline, self).__init__()
+ self.vertices = [] # set in append data
+ self.points = [] # set in append data
+ self.control_points = [] # set in append data
+ self.width = [] # set in append data
+ self.bulge = [] # set in append data
+ self.tangents = [] # set in append data
+ self.flags = 0
+ self.mode = 'polyline2d'
+ self.mcount = 0
+ self.ncount = 0
+ self.default_start_width = 0.
+ self.default_end_width = 0.
+ self.is_mclosed = False
+ self.is_nclosed = False
+ self.is_closed = False
+ self.elevation = (0., 0., 0.)
+ self.m_smooth_density = 0.
+ self.n_smooth_density = 0.
+ self.smooth_type = 0
+ self.spline_type = None
+
+ def setup_attributes(self, tags):
+ def get_mode():
+ flags = self.flags
+ if flags & const.POLYLINE_SPLINE_FIT_VERTICES_ADDED:
+ return 'spline2d'
+ elif flags & const.POLYLINE_3D_POLYLINE:
+ return 'polyline3d'
+ elif flags & const.POLYLINE_3D_POLYMESH:
+ return 'polymesh'
+ elif flags & const.POLYLINE_POLYFACE:
+ return 'polyface'
+ else:
+ return 'polyline2d'
+
+ for code, value in super(Polyline, self).setup_attributes(tags):
+ if code == 10:
+ self.elevation = value
+ elif code == 40:
+ self.default_start_width = value
+ elif code == 41:
+ self.default_end_width = value
+ elif code == 70:
+ self.flags = value
+ elif code == 71:
+ self.mcount = value
+ elif code == 72:
+ self.ncount = value
+ elif code == 73:
+ self.m_smooth_density = value
+ elif code == 73:
+ self.n_smooth_density = value
+ elif code == 75:
+ self.smooth_type = value
+ else:
+ yield code, value # chain of generators
+
+ self.mode = get_mode()
+ if self.mode == 'spline2d':
+ if self.smooth_type == const.POLYMESH_CUBIC_BSPLINE:
+ self.spline_type = 'cubic_bspline'
+ elif self.smooth_type == const.POLYMESH_QUADRIC_BSPLINE:
+ self.spline_type = 'quadratic_bspline'
+ elif self.smooth_type == const.POLYMESH_BEZIER_SURFACE:
+ self.spline_type = 'bezier_curve' # is this a valid spline type for DXF12?
+ self.is_mclosed = bool(self.flags & const.POLYLINE_MESH_CLOSED_M_DIRECTION)
+ self.is_nclosed = bool(self.flags & const.POLYLINE_MESH_CLOSED_N_DIRECTION)
+ self.is_closed = self.is_mclosed
+ self.set_default_extrusion()
+
+ def __len__(self):
+ return len(self.vertices)
+
+ def __getitem__(self, item):
+ return self.vertices[item]
+
+ def __iter__(self):
+ return iter(self.vertices)
+
+ def append_data(self, vertices):
+ def default_width(start_width, end_width):
+ if start_width == 0.:
+ start_width = self.default_start_width
+ if end_width == 0.:
+ end_width = self.default_end_width
+ return start_width, end_width
+
+ self.vertices = vertices
+ if self.mode in Polyline.LINE_TYPES:
+ for vertex in self.vertices:
+ if vertex.flags & const.VTX_SPLINE_FRAME_CONTROL_POINT:
+ self.control_points.append(vertex.location)
+ else:
+ self.points.append(vertex.location)
+ self.width.append(default_width(vertex.start_width, vertex.end_width))
+ self.bulge.append(vertex.bulge)
+ self.tangents.append(vertex.tangent if vertex.flags & const.VTX_CURVE_FIT_TANGENT else None)
+
+ def cast(self):
+ if self.mode == 'polyface':
+ return PolyFace(self)
+ elif self.mode == 'polymesh':
+ return PolyMesh(self)
+ else:
+ return self
+
+
+class SubFace(object):
+ def __init__(self, face_record, vertices):
+ self._vertices = vertices
+ self.face_record = face_record
+
+ def __len__(self):
+ return len(self.face_record.vtx)
+
+ def __getitem__(self, item):
+ return self._vertices[self._vertex_index(item)]
+
+ def __iter__(self):
+ return (self._vertices[index].location for index in self.indices())
+
+ def _vertex_index(self, pos):
+ return abs(self.face_record.vtx[pos]) - 1
+
+ def indices(self):
+ return tuple(abs(i)-1 for i in self.face_record.vtx if i != 0)
+
+ def is_edge_visible(self, pos):
+ return self.face_record.vtx[pos] > 0
+
+
+class PolyShape(object):
+ def __init__(self, polyline, dxftype):
+ # copy all dxf attributes from polyline
+ for key, value in polyline.__dict__.items():
+ self.__dict__[key] = value
+ self.dxftype = dxftype
+
+ def __str__(self):
+ return "{} [{}]".format(self.dxftype, self.handle)
+
+
+class PolyFace(PolyShape):
+ def __init__(self, polyline):
+ VERTEX_FLAGS = const.VTX_3D_POLYFACE_MESH_VERTEX + const.VTX_3D_POLYGON_MESH_VERTEX
+
+ def is_vertex(flags):
+ return flags & VERTEX_FLAGS == VERTEX_FLAGS
+
+ super(PolyFace, self).__init__(polyline, 'POLYFACE')
+ vertices = []
+ face_records = []
+ for vertex in polyline.vertices:
+ (vertices if is_vertex(vertex.flags) else face_records).append(vertex)
+
+ self._face_records = face_records
+
+ def __getitem__(self, item):
+ return SubFace(self._face_records[item], self.vertices)
+
+ def __len__(self):
+ return len(self._face_records)
+
+ def __iter__(self):
+ return (SubFace(f, self.vertices) for f in self._face_records)
+
+
+class PolyMesh(PolyShape):
+ def __init__(self, polyline):
+ super(PolyMesh, self).__init__(polyline, 'POLYMESH')
+
+ def __iter__(self):
+ return iter(self.vertices)
+
+ def get_location(self, pos):
+ return self.get_vertex(pos).location
+
+ def get_vertex(self, pos):
+ m, n = pos
+ if 0 <= m < self.mcount and 0 <= n < self.ncount:
+ pos = m * self.ncount + n
+ return self.vertices[pos]
+ else:
+ raise IndexError(repr(pos))
+
+
+class Vertex(DXFEntity):
+ def __init__(self):
+ super(Vertex, self).__init__()
+ self.location = (0., 0., 0.)
+ self.flags = 0
+ self.start_width = 0.
+ self.end_width = 0.
+ self.bulge = 0.
+ self.tangent = None
+ self.vtx = None
+
+ def setup_attributes(self, tags):
+ vtx0 = 0
+ vtx1 = 0
+ vtx2 = 0
+ vtx3 = 0
+ for code, value in super(Vertex, self).setup_attributes(tags):
+ if code == 10:
+ self.location = value
+ elif code == 40:
+ self.start_width = value
+ elif code == 41:
+ self.end_width = value
+ elif code == 42:
+ self.bulge = value
+ elif code == 50:
+ self.tangent = value
+ elif code == 70:
+ self.flags = value
+ elif code == 71:
+ vtx0 = value
+ elif code == 72:
+ vtx1 = value
+ elif code == 73:
+ vtx2 = value
+ elif code == 74:
+ vtx3 = value
+ else:
+ yield code, value # chain of generators
+ indices = (vtx0, vtx1, vtx2, vtx3)
+ if any(indices):
+ self.vtx = indices
+
+ def __getitem__(self, item):
+ return self.location[item]
+
+ def __iter__(self):
+ return iter(self.location)
+
+
+class Block(DXFEntity):
+ def __init__(self):
+ super(Block, self).__init__()
+ self.basepoint = (0, 0, 0)
+ self.name = ''
+ self.description = ''
+ self.flags = 0
+ self.xrefpath = ""
+ self._entities = []
+
+ def setup_attributes(self, tags):
+ for code, value in super(Block, self).setup_attributes(tags):
+ if code == 2:
+ self.name = value
+ elif code == 4:
+ self.description = value
+ elif code == 1:
+ self.xrefpath = value
+ elif code == 10:
+ self.basepoint = value
+ elif code == 70:
+ self.flags = value
+ else:
+ yield code, value # chain of generators
+
+ @property
+ def is_xref(self):
+ return bool(self.flags & const.BLK_XREF)
+
+ @property
+ def is_xref_overlay(self):
+ return bool(self.flags & const.BLK_XREF_OVERLAY)
+
+ @property
+ def is_anonymous(self):
+ return bool(self.flags & const.BLK_ANONYMOUS)
+
+ def set_entities(self, entities):
+ self._entities = entities
+
+ def __iter__(self):
+ return iter(self._entities)
+
+ def __getitem__(self, item):
+ return self._entities[item]
+
+ def __len__(self):
+ return len(self._entities)
+
+
+class LWPolyline(DXFEntity):
+ def __init__(self):
+ super(LWPolyline, self).__init__()
+ self.points = []
+ self.width = []
+ self.bulge = []
+ self.elevation = 0.
+ self.const_width = 0.
+ self.flags = 0
+
+ def setup_attributes(self, tags):
+ bulge, start_width, end_width = 0., 0., 0.
+ init = True
+
+ for code, value in super(LWPolyline, self).setup_attributes(tags):
+ if code == 10:
+ if not init:
+ self.bulge.append(bulge)
+ self.width.append((start_width, end_width))
+ bulge, start_width, end_width = 0., 0., 0.
+ self.points.append(value)
+ init = False
+ elif code == 40:
+ start_width = value
+ elif code == 41:
+ end_width = value
+ elif code == 42:
+ bulge = value
+ elif code == 38:
+ self.elevation = value
+ elif code == 39:
+ self.thickness = value
+ elif code == 43:
+ self.const_width = value
+ elif code == 70:
+ self.flags = value
+ elif code == 210:
+ self.extrusion = value
+ else:
+ yield code, value # chain of generators
+
+ # add values for the last point
+ self.bulge.append(bulge)
+ self.width.append((start_width, end_width))
+
+ if self.const_width != 0.:
+ self.width = []
+ self.set_default_extrusion()
+
+ @property
+ def is_closed(self):
+ return bool(self.flags & 1)
+
+ def __len__(self):
+ return len(self.points)
+
+ def __getitem__(self, item):
+ return self.points[item]
+
+ def __iter__(self):
+ return iter(self.points)
+
+
+class Ellipse(DXFEntity):
+ def __init__(self):
+ super(Ellipse, self).__init__()
+ self.center = (0., 0., 0.)
+ self.major_axis = (1., 0., 0.)
+ self.ratio = 1.0
+ self.start_param = 0.
+ self.end_param = 6.283185307179586
+
+ def setup_attributes(self, tags):
+ for code, value in super(Ellipse, self).setup_attributes(tags):
+ if code == 10:
+ self.center = value
+ elif code == 11:
+ self.major_axis = value
+ elif code == 40:
+ self.ratio = value
+ elif code == 41:
+ self.start_param = value
+ elif code == 42:
+ self.end_param = value
+ else:
+ yield code, value # chain of generators
+ self.set_default_extrusion()
+
+
+class Ray(DXFEntity):
+ def __init__(self):
+ super(Ray, self).__init__()
+ self.start = (0, 0, 0)
+ self.unit_vector = (1, 0, 0)
+
+ def setup_attributes(self, tags):
+ for code, value in super(Ray, self).setup_attributes(tags):
+ if code == 10:
+ self.start = value
+ elif code == 11:
+ self.unit_vector = value
+ else:
+ yield code, value # chain of generators
+
+
+def deg2vec(deg):
+ rad = float(deg) * math.pi / 180.0
+ return math.cos(rad), math.sin(rad), 0.
+
+
+def normalized(vector):
+ x, y, z = vector
+ m = (x**2 + y**2 + z**2)**0.5
+ return x/m, y/m, z/m
+
+##################################################
+# MTEXT inline codes
+# \L Start underline
+# \l Stop underline
+# \O Start overstrike
+# \o Stop overstrike
+# \K Start strike-through
+# \k Stop strike-through
+# \P New paragraph (new line)
+# \pxi Control codes for bullets, numbered paragraphs and columns
+# \X Paragraph wrap on the dimension line (only in dimensions)
+# \Q Slanting (obliquing) text by angle - e.g. \Q30;
+# \H Text height - e.g. \H3x;
+# \W Text width - e.g. \W0.8x;
+# \F Font selection
+#
+# e.g. \Fgdt;o - GDT-tolerance
+# e.g. \Fkroeger|b0|i0|c238|p10 - font Kroeger, non-bold, non-italic, codepage 238, pitch 10
+#
+# \S Stacking, fractions
+#
+# e.g. \SA^B:
+# A
+# B
+# e.g. \SX/Y:
+# X
+# -
+# Y
+# e.g. \S1#4:
+# 1/4
+#
+# \A Alignment
+#
+# \A0; = bottom
+# \A1; = center
+# \A2; = top
+#
+# \C Color change
+#
+# \C1; = red
+# \C2; = yellow
+# \C3; = green
+# \C4; = cyan
+# \C5; = blue
+# \C6; = magenta
+# \C7; = white
+#
+# \T Tracking, char.spacing - e.g. \T2;
+# \~ Non-wrapping space, hard space
+# {} Braces - define the text area influenced by the code
+# \ Escape character - e.g. \\ = "\", \{ = "{"
+#
+# Codes and braces can be nested up to 8 levels deep
+
+ESCAPED_CHARS = "\\{}"
+GROUP_CHARS = "{}"
+ONE_CHAR_COMMANDS = "PLlOoKkX"
+
+
+class MText(DXFEntity):
+ def __init__(self):
+ super(MText, self).__init__()
+ self.insert = (0., 0., 0.)
+ self.raw_text = ""
+ self.height = 0.
+ self.rect_width = None
+ self.horizontal_width = None
+ self.vertical_height = None
+ self.line_spacing = 1.
+ self.attachment_point = 1
+ self.style = 'STANDARD'
+ self.xdirection = (1., 0., 0.)
+ self.font = None
+ self.big_font = None
+
+ def setup_attributes(self, tags):
+ text = ""
+ lines = []
+ rotation = 0.
+ xdir = None
+ for code, value in super(MText, self).setup_attributes(tags):
+ if code == 10:
+ self.insert = value
+ elif code == 11:
+ xdir = value
+ elif code == 1:
+ text = value
+ elif code == 3:
+ lines.append(value)
+ elif code == 7:
+ self.style = value
+ elif code == 40:
+ self.height = value
+ elif code == 41:
+ self.rect_width = value
+ elif code == 42:
+ self.horizontal_width = value
+ elif code == 43:
+ self.vertical_height = value
+ elif code == 44:
+ self.line_spacing = value
+ elif code == 50:
+ rotation = value
+ elif code == 71:
+ self.attachment_point = value
+ else:
+ yield code, value # chain of generators
+
+ lines.append(text)
+ self.raw_text = "".join(lines)
+ if xdir is None:
+ xdir = deg2vec(rotation)
+ self.xdirection = normalized(xdir)
+ self.set_default_extrusion()
+
+ def lines(self):
+ return self.raw_text.split('\P')
+
+ def plain_text(self, split=False):
+ chars = []
+ raw_chars = list(reversed(self.raw_text)) # text splitted into chars, in reversed order for efficient pop()
+ while len(raw_chars):
+ char = raw_chars.pop()
+ if char == '\\': # is a formatting command
+ try:
+ char = raw_chars.pop()
+ except IndexError:
+ break # premature end of text - just ignore
+
+ if char in ESCAPED_CHARS: # \ { }
+ chars.append(char)
+ elif char in ONE_CHAR_COMMANDS:
+ if char == 'P': # new line
+ chars.append('\n')
+ # discard other commands
+ else: # more character commands are terminated by ';'
+ stacking = char == 'S' # stacking command surrounds user data
+ try:
+ while char != ';': # end of format marker
+ char = raw_chars.pop()
+ if stacking and char != ';':
+ chars.append(char) # append user data of stacking command
+ except IndexError:
+ break # premature end of text - just ignore
+ elif char in GROUP_CHARS: # { }
+ pass # discard group markers
+ elif char == '%': # special characters
+ if len(raw_chars) and raw_chars[-1] == '%':
+ raw_chars.pop() # discard next '%'
+ if len(raw_chars):
+ special_char = raw_chars.pop()
+ # replace or discard formatting code
+ chars.append(SPECIAL_CHARS.get(special_char, ""))
+ else: # char is just a single '%'
+ chars.append(char)
+ else: # char is what it is, a character
+ chars.append(char)
+
+ plain_text = "".join(chars)
+ return plain_text.split('\n') if split else plain_text
+
+ def resolve_text_style(self, text_styles):
+ style = text_styles.get(self.style, None)
+ if style is None:
+ style = default_text_style
+ if self.height == 0:
+ self.height = style.height
+ if self.font is None:
+ self.font = style.font
+ if self.big_font is None:
+ self.big_font = style.font
+
+
+class Light(DXFEntity):
+ def __init__(self):
+ super(Light, self).__init__()
+ self.version = 1
+ self.name = ""
+ self.light_type = 1 # distant = 1; point = 2; spot = 3
+ self.status = False # on/off ?
+ self.light_color = 0 # 0 is unset
+ self.true_color = None # None is unset
+ self.plot_glyph = 0
+ self.intensity = 0
+ self.position = (0., 0., 1.)
+ self.target = (0., 0., 0.)
+ self.attenuation_type = 0 # 0 = None; 1 = Inverse Linear; 2 = Inverse Square
+ self.use_attenuation_limits = False
+ self.attenuation_start_limit = 0
+ self.attenuation_end_limit = 0
+ self.hotspot_angle = 0
+ self.fall_off_angle = 0.
+ self.cast_shadows = False
+ self.shadow_type = 0 # 0 = Ray traced shadows; 1 = Shadow maps
+ self.shadow_map_size = 0
+ self.shadow_softness = 0
+
+ def setup_attributes(self, tags):
+ for code, value in super(Light, self).setup_attributes(tags):
+ if code == 1:
+ self.name = value
+ elif code == 10:
+ self.position = value
+ elif code == 11:
+ self.target = value
+ elif code == 40:
+ self.intensity = value
+ elif code == 41:
+ self.attenuation_start_limit = value
+ elif code == 42:
+ self.attenuation_end_limit = value
+ elif code == 50:
+ self.hotspot_angle = value
+ elif code == 51:
+ self.fall_off_angle = value
+ elif code == 63:
+ self.light_color = value
+ elif code == 70:
+ self.light_type = value
+ elif code == 72:
+ self.attenuation_type = value
+ elif code == 73:
+ self.shadow_type = value
+ elif code == 90:
+ self.version = value
+ elif code == 91:
+ self.shadow_map_size = value
+ elif code == 280:
+ self.shadow_softness = value
+ elif code == 290:
+ self.status = value
+ elif code == 291:
+ self.plot_glyph = value
+ elif code == 292:
+ self.use_attenuation_limits = value
+ elif code == 293:
+ self.cast_shadows = value
+ elif code == 421:
+ self.true_color = value
+ else:
+ yield code, value # chain of generators
+
+
+class Body(DXFEntity):
+ def __init__(self):
+ super(Body, self).__init__()
+ # need handle to get SAB data in DXF version AC1027 and later
+ self.version = 1
+ self.acis = []
+
+ def setup_attributes(self, tags):
+ sat = []
+ for code, value in super(Body, self).setup_attributes(tags):
+ if code == 70:
+ self.version = value
+ elif code in (1, 3):
+ sat.append(value)
+ else:
+ yield code, value # chain of generators
+ self.acis = decode(sat)
+
+ def set_sab_data(self, sab_data):
+ self.acis = sab_data
+
+ @property
+ def is_sat(self):
+ return isinstance(self.acis, list) # but could be an empty list
+
+ @property
+ def is_sab(self):
+ return not self.is_sat # has binary encoded ACIS data
+
+
+class Surface(Body):
+ def __init__(self):
+ super(Body, self).__init__()
+ self.u_isolines = 0
+ self.v_isolines = 0
+
+ def setup_attributes(self, tags):
+ for code, value in super(Surface, self).setup_attributes(tags):
+ if code == 71:
+ self.u_isolines = value
+ elif code == 72:
+ self.v_isolines = value
+ else:
+ yield code, value # chain of generators
+
+
+class Mesh(DXFEntity):
+ def __init__(self):
+ super(Mesh, self).__init__()
+ self.version = 2
+ self.blend_crease = False
+ self.subdivision_levels = 1
+ # rest are mostly positional tags
+ self.vertices = []
+ self.faces = []
+ self.edges = []
+ self.edge_crease_list = []
+
+ def setup_attributes(self, tags):
+ status = 0
+ count = 0
+ index_tags = []
+ for code, value in super(Mesh, self).setup_attributes(tags):
+ if code == 10:
+ self.vertices.append(value)
+ elif status == -1: # ignore overridden properties at the end of the mesh entity
+ pass # property override uses also group codes 90, 91, 92 but only at the end of the MESH entity
+ elif code == 71:
+ self.version = value
+ elif code == 72:
+ self.blend_crease = bool(value)
+ elif code == 91:
+ self.subdivision_levels = value
+ elif 92 <= code <= 95: # 92 = vertices, 93 = faces; 94 = edges 95 = edge creases
+ if code in (92, 95):
+ continue # ignore vertices and edge creases count
+ status = code
+ count = value
+ if status == 94: # edge count
+ count *= 2
+ elif code == 140:
+ self.edge_crease_list.append(value)
+ elif code == 90 and count > 0: # faces or edges
+ count -= 1
+ index_tags.append(value)
+ if count < 1:
+ if status == 93:
+ self.setup_faces(index_tags)
+ elif status == 94:
+ self.setup_edges(index_tags)
+ index_tags = []
+ elif code == 90: # count == 0; start of overridden properties (group code 90 after face or edge list)
+ status = -1
+ else:
+ yield code, value # chain of generators
+
+ def get_face(self, index):
+ return tuple(self.vertices[vertex_index] for vertex_index in self.faces[index])
+
+ def get_edge(self, index):
+ return tuple(self.vertices[vertex_index] for vertex_index in self.edges[index])
+
+ def setup_faces(self, tags):
+ face = []
+ count = 0
+ for value in tags:
+ if count == 0:
+ if len(face):
+ self.faces.append(tuple(face))
+ del face[:]
+ count = value
+ else:
+ count -= 1
+ face.append(value)
+
+ if len(face):
+ self.faces.append(tuple(face))
+
+ def setup_edges(self, tags):
+ self.edges = list(zip(tags[::2], tags[1::2]))
+
+
+class Spline(DXFEntity):
+ def __init__(self):
+ super(Spline, self).__init__()
+ self.normal_vector = None
+ self.flags = 0
+ self.degree = 3
+ self.start_tangent = None
+ self.end_tangent = None
+ self.knots = []
+ self.weights = []
+ self.tol_knot = .0000001
+ self.tol_control_point = .0000001
+ self.tol_fit_point = .0000000001
+ self.control_points = []
+ self.fit_points = []
+
+ def setup_attributes(self, tags):
+ subclass = 'AcDbSpline'
+ for code, value in super(Spline, self).setup_attributes(tags):
+ if subclass == 'AcDbHelix':
+ yield code, value # chain of generators
+ elif code == 10:
+ self.control_points.append(value)
+ elif code == 11:
+ self.fit_points.append(value)
+ elif code == 12:
+ self.start_tangent = value
+ elif code == 13:
+ self.end_tangent = value
+ elif code == 40:
+ self.knots.append(value)
+ elif code == 41:
+ self.weights.append(value)
+ elif code == 42:
+ self.tol_knot = value
+ elif code == 43:
+ self.tol_control_point = value
+ elif code == 44:
+ self.tol_fit_point = value
+ elif code == 70:
+ self.flags = value
+ elif code == 71:
+ self.degree = value
+ elif 72 <= code < 75:
+ pass # ignore knot-, control- and fit point count
+ elif code == 100:
+ subclass = value
+ self.normal_vector = self.extrusion
+ if len(self.weights) == 0:
+ self.weights = [1.0] * len(self.control_points)
+
+ @property
+ def is_closed(self):
+ return bool(self.flags & const.SPLINE_CLOSED)
+
+ @property
+ def is_periodic(self):
+ return bool(self.flags & const.SPLINE_PERIODIC)
+
+ @property
+ def is_rational(self):
+ return bool(self.flags & const.SPLINE_RATIONAL)
+
+ @property
+ def is_planar(self):
+ return bool(self.flags & const.SPLINE_PLANAR)
+
+ @property
+ def is_linear(self):
+ return bool(self.flags & const.SPLINE_LINEAR)
+
+
+class Helix(Spline):
+ def __init__(self):
+ super(Helix, self).__init__()
+ self.helix_version = (1, 1)
+ self.axis_base_point = None
+ self.start_point = None
+ self.axis_vector = None
+ self.radius = 0
+ self.turns = 0
+ self.turn_height = 0
+ self.handedness = 0 # 0 = left, 1 = right
+ self.constrain = 0
+ # 0 = Constrain turn height;
+ # 1 = Constrain turns;
+ # 2 = Constrain height
+
+ def setup_attributes(self, tags):
+ helix_major_version = 1
+ helix_maintainance_version = 1
+ for code, value in super(Helix, self).setup_attributes(tags):
+ if code == 10:
+ self.axis_base_point = value
+ elif code == 11:
+ self.start_point = value
+ elif code == 12:
+ self.axis_vector = value
+ elif code == 90:
+ helix_major_version = value
+ elif code == 91:
+ helix_maintainance_version = value
+ elif code == 91:
+ helix_maintainance_version = value
+ elif code == 40:
+ self.radius = value
+ elif code == 41:
+ self.turns = value
+ elif code == 42:
+ self.turn_height = value
+ elif code == 290:
+ self.handedness = value
+ elif code == 280:
+ self.constrain = value
+ else:
+ yield code, value # chain of generators
+ self.helix_version = (helix_major_version, helix_maintainance_version)
+
+
+EntityTable = {
+ 'LINE': Line,
+ 'POINT': Point,
+ 'CIRCLE': Circle,
+ 'ARC': Arc,
+ 'TRACE': Trace,
+ 'SOLID': Solid,
+ '3DFACE': Face,
+ 'TEXT': Text,
+ 'INSERT': Insert,
+ 'SEQEND': DXFEntity,
+ 'ATTRIB': Attrib,
+ 'ATTDEF': Attrib,
+ 'POLYLINE': Polyline,
+ 'VERTEX': Vertex,
+ 'BLOCK': Block,
+ 'ENDBLK': DXFEntity,
+ 'LWPOLYLINE': LWPolyline,
+ 'ELLIPSE': Ellipse,
+ 'RAY': Ray,
+ 'XLINE': Ray,
+ 'SPLINE': Spline,
+ 'HELIX': Helix,
+ 'MTEXT': MText,
+ 'MESH': Mesh,
+ 'LIGHT': Light,
+ 'BODY': Body,
+ 'REGION': Body,
+ '3DSOLID': Body,
+ 'SURFACE': Surface,
+ 'PLANESURFACE': Surface,
+}
+
+
+def entity_factory(tags):
+ dxftype = tags.get_type()
+ cls = EntityTable[dxftype] # get entity class or raise KeyError
+ entity = cls() # call constructor
+ list(entity.setup_attributes(tags)) # setup dxf attributes - chain of generators
+ return entity
+
diff --git a/io_import_dxf/dxfgrabber/dxfobjects.py b/io_import_dxf/dxfgrabber/dxfobjects.py
new file mode 100644
index 00000000..c7ee320b
--- /dev/null
+++ b/io_import_dxf/dxfgrabber/dxfobjects.py
@@ -0,0 +1,99 @@
+# encoding: utf-8
+# Purpose: objects classes
+# Created: 17.04.2016
+# Copyright (C) 2016, Manfred Moitzi
+# License: MIT License
+from __future__ import unicode_literals
+__author__ = "mozman <mozman@gmx.at>"
+
+from datetime import datetime
+from .juliandate import calendar_date
+
+
+class DXFObject(object):
+ def __init__(self):
+ self.dxftype = 'ENTITY'
+ self.handle = None
+ self.owner = None
+
+ def setup_attributes(self, tags):
+ self.dxftype = tags.get_type()
+ for code, value in tags.plain_tags():
+ if code == 5: # special case 105 handled by STYLE TABLE
+ self.handle = value
+ elif code == 330:
+ self.owner = value
+ else:
+ yield code, value # chain of generators
+
+
+def unpack_seconds(seconds):
+ seconds = int(seconds / 1000) # remove 1/1000 part
+ hours = int(seconds / 3600)
+ seconds = int(seconds % 3600)
+ minutes = int(seconds / 60)
+ seconds = int(seconds % 60)
+ return hours, minutes, seconds
+
+
+class Sun(DXFObject):
+ def __init__(self):
+ super(Sun, self).__init__()
+ self.version = 1
+ self.status = False
+ self.sun_color = None
+ self.intensity = 0.
+ self.shadows = False
+ self.date = datetime.now()
+ self.daylight_savings_time = False
+ self.shadow_type = 0
+ self.shadow_map_size = 0
+ self.shadow_softness = 0.
+
+ def setup_attributes(self, tags):
+ date = 0
+ time = 0
+ for code, value in super(Sun, self).setup_attributes(tags):
+ if code == 40:
+ self.intensity = value
+ elif code == 63:
+ self.sun_color = value
+ elif code == 70:
+ self.shadow_type = value
+ elif code == 71:
+ self.shadow_map_size = value
+ elif code == 90:
+ self.version = value
+ elif code == 91:
+ date = value
+ elif code == 92:
+ time = value
+ elif code == 280:
+ self.shadow_softness = value
+ elif code == 290:
+ self.status = bool(value)
+ elif code == 291:
+ self.shadows = bool(value)
+ elif code == 292:
+ self.daylight_savings_time = bool(value)
+ else:
+ yield code, value # chain of generators
+
+ if date > 0:
+ date = calendar_date(date)
+ else:
+ date = datetime.now()
+ hours, minutes, seconds = unpack_seconds(time)
+ self.date = datetime(date.year, date.month, date.day, hours, minutes, seconds)
+
+ObjectsTable = {
+ 'SUN': Sun,
+}
+
+
+def objects_factory(tags):
+ dxftype = tags.get_type()
+ cls = ObjectsTable.get(dxftype, DXFObject) # get object class
+ entity = cls() # call constructor
+ list(entity.setup_attributes(tags)) # setup dxf attributes - chain of generators
+ return entity
diff --git a/io_import_dxf/dxfimport/is_.py b/io_import_dxf/dxfimport/is_.py
index 6a8977a5..c6777530 100644
--- a/io_import_dxf/dxfimport/is_.py
+++ b/io_import_dxf/dxfimport/is_.py
@@ -140,5 +140,7 @@ def combined(typestr):
def extrusion(entity):
+ if entity.extrusion is None:
+ return False
return Vector(entity.extrusion) != Vector((0, 0, 1)) \
or (hasattr(entity, "elevation") and entity.elevation != 0) \ No newline at end of file