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:
Diffstat (limited to 'io_import_dxf/dxfgrabber/entities.py')
-rwxr-xr-xio_import_dxf/dxfgrabber/entities.py930
1 files changed, 930 insertions, 0 deletions
diff --git a/io_import_dxf/dxfgrabber/entities.py b/io_import_dxf/dxfgrabber/entities.py
new file mode 100755
index 00000000..937b05bd
--- /dev/null
+++ b/io_import_dxf/dxfgrabber/entities.py
@@ -0,0 +1,930 @@
+# encoding: utf-8
+# Purpose: entity classes
+# Created: 21.07.2012, parts taken from my ezdxf project
+# Copyright (C) 2012, Manfred Moitzi
+# License: MIT License
+from __future__ import unicode_literals
+__author__ = "mozman <mozman@gmx.at>"
+
+from . import dxf12, dxf13
+from . import const
+from .juliandate import calendar_date
+from datetime import datetime
+from .color import TrueColor
+import math
+
+from .styles import default_text_style
+
+SPECIAL_CHARS = {
+ 'd': '°'
+}
+
+
+class SeqEnd(object):
+ def __init__(self, wrapper):
+ self.dxftype = wrapper.dxftype()
+
+
+class Entity(SeqEnd):
+ def __init__(self, wrapper):
+ super(Entity, self).__init__(wrapper)
+ self.paperspace = bool(wrapper.paperspace())
+
+
+class Shape(Entity):
+ def __init__(self, wrapper):
+ super(Shape, self).__init__(wrapper)
+ get_dxf = wrapper.get_dxf_attrib
+ self.layer = get_dxf('layer', '0')
+ self.linetype = get_dxf('linetype', None) # None=BYLAYER
+ self.thickness = get_dxf('thickness', 0.0)
+ self.extrusion = get_dxf('extrusion', (0., 0., 1.))
+ self.ltscale = get_dxf('ltscale', 1.0)
+ self.invisible = get_dxf('invisible', 0) # 0=visible
+ self.color = get_dxf('color', const.BYLAYER) # 256=BYLAYER, 0=BYBLOCK
+ self.true_color = get_dxf('true_color', None) # 0x00RRGGBB
+ if self.true_color is not None:
+ self.true_color = TrueColor(self.true_color)
+ self.transparency = get_dxf('transparency', None) # 0x020000TT
+ if self.transparency is not None:
+ # 0.0 = opaque & 1.0 if fully transparent
+ self.transparency = 1. - float(self.transparency & 0xFF) / 255.
+ self.shadow_mode = get_dxf('shadow_mode', None)
+ # 0 = Casts and receives shadows
+ # 1 = Casts shadows
+ # 2 = Receives shadows
+ # 3 = Ignores shadows
+
+ # if adding additional DXF attributes, do it also for PolyShape
+
+
+class PolyShape(object):
+ """ Base class for Polyface and Polymesh, both are special cases of POLYLINE.
+ """
+ def __init__(self, polyline, dxftype):
+ self.dxftype = dxftype
+ self.paperspace = polyline.paperspace
+ self.layer = polyline.layer
+ self.linetype = polyline.linetype
+ self.ltscale = polyline.ltscale
+ self.invisible = polyline.invisible
+ self.color = polyline.color
+ self.true_color = polyline.true_color
+ self.transparency = polyline.transparency
+ self.shadow_mode = polyline.shadow_mode
+
+
+class Line(Shape):
+ def __init__(self, wrapper):
+ super(Line, self).__init__(wrapper)
+ self.start = wrapper.get_dxf_attrib('start')
+ self.end = wrapper.get_dxf_attrib('end')
+
+
+class Point(Shape):
+ def __init__(self, wrapper):
+ super(Point, self).__init__(wrapper)
+ self.point = wrapper.get_dxf_attrib('point')
+
+
+class Circle(Shape):
+ def __init__(self, wrapper):
+ super(Circle, self).__init__(wrapper)
+ self.center = wrapper.get_dxf_attrib('center')
+ self.radius = wrapper.get_dxf_attrib('radius')
+
+
+class Arc(Shape):
+ def __init__(self, wrapper):
+ super(Arc, self).__init__(wrapper)
+ get_dxf = wrapper.get_dxf_attrib
+ self.center = get_dxf('center')
+ self.radius = get_dxf('radius')
+ self.startangle = get_dxf('startangle')
+ self.endangle = get_dxf('endangle')
+
+
+class Trace(Shape):
+ def __init__(self, wrapper):
+ super(Trace, self).__init__(wrapper)
+ get_dxf = wrapper.get_dxf_attrib
+ self.points = [
+ get_dxf(vname) for vname in const.VERTEXNAMES
+ ]
+
+Solid = Trace
+
+
+class Face(Trace):
+ def __init__(self, wrapper):
+ super(Face, self).__init__(wrapper)
+ self.invisible_edge = wrapper.get_dxf_attrib('invisible_edge', 0)
+
+ def is_edge_invisible(self, edge):
+ # edges 0 .. 3
+ return bool(self.invisible_edge & (1 << edge))
+
+
+class Text(Shape):
+ def __init__(self, wrapper):
+ super(Text, self).__init__(wrapper)
+ get_dxf = wrapper.get_dxf_attrib
+ self.insert = get_dxf('insert')
+ self.text = get_dxf('text')
+ self.height = get_dxf('height', 0)
+ self.width = get_dxf('width', 0)
+ self.oblique = get_dxf('oblique', None)
+ self.rotation = get_dxf('rotation', 0.)
+ self.style = get_dxf('style', "")
+ self.halign = get_dxf('halign', 0)
+ self.valign = get_dxf('valign', 0)
+ self.alignpoint = get_dxf('alignpoint', None)
+ if get_dxf('textgenerationflag', None) is not None:
+ self.is_backwards = bool(get_dxf('textgenerationflag', 0) & 2)
+ self.is_upside_down = bool(get_dxf('textgenerationflag', 0) & 4)
+ else:
+ self.is_backwards = None
+ self.is_upside_down = None
+ self.font = ""
+ self.bigfont = ""
+
+ 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.bigfont is None:
+ self.bigfont = style.bigfont
+
+ 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 Insert(Shape):
+ def __init__(self, wrapper):
+ super(Insert, self).__init__(wrapper)
+ get_dxf = wrapper.get_dxf_attrib
+ self.name = get_dxf('name')
+ self.insert = get_dxf('insert')
+ self.rotation = get_dxf('rotation', 0.)
+ self.scale = get_dxf('xscale', 1.), get_dxf('yscale', 1.), get_dxf('zscale', 1.)
+ self.row_count = get_dxf('rowcount', 1)
+ self.row_spacing = get_dxf('rowspacing', 0.)
+ self.col_count = get_dxf('colcount', 1)
+ self.col_spacing = get_dxf('colspacing', 0.)
+ self.attribsfollow = bool(get_dxf('attribsfollow', 0))
+ self.attribs = []
+
+ 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 Attrib(Text): # also ATTDEF
+ def __init__(self, wrapper):
+ super(Attrib, self).__init__(wrapper)
+ self.tag = wrapper.get_dxf_attrib('tag')
+
+_LINE_TYPES = frozenset(('spline2d', 'polyline2d', 'polyline3d'))
+
+
+class Polyline(Shape):
+ def __init__(self, wrapper):
+ super(Polyline, self).__init__(wrapper)
+ self.vertices = [] # set in append data
+ self.points = [] # set in append data
+ self.controlpoints = [] # set in append data
+ self.width = [] # set in append data
+ self.bulge = [] # set in append data
+ self.tangents = [] # set in append data
+ self.flags = wrapper.flags
+ self.mode = wrapper.get_mode()
+ get_dxf = wrapper.get_dxf_attrib
+ self.mcount = get_dxf('mcount', 0)
+ self.ncount = get_dxf('ncount', 0)
+ self.default_start_width = get_dxf('defaultstartwidth', 0.)
+ self.default_end_width = get_dxf('defaultendwidth', 0.)
+ self.is_mclosed = wrapper.is_mclosed()
+ self.is_nclosed = wrapper.is_nclosed()
+ self.elevation = get_dxf('elevation', (0., 0., 0.))
+ self.m_smooth_density = get_dxf('msmoothdensity', 0.)
+ self.n_smooth_density = get_dxf('nsmoothdensity', 0.)
+ self.smooth_type = get_dxf('smoothtype', 0)
+ self.spline_type = None
+ 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?
+
+ def __len__(self):
+ return len(self.vertices)
+
+ def __getitem__(self, item):
+ return self.vertices[item]
+
+ def __iter__(self):
+ return iter(self.vertices)
+
+ @property
+ def is_closed(self):
+ return self.is_mclosed
+
+ @is_closed.setter
+ def is_closed(self, status):
+ self.is_mclosed = status
+
+ 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 _LINE_TYPES:
+ for vertex in self.vertices:
+ if vertex.flags & const.VTX_SPLINE_FRAME_CONTROL_POINT:
+ self.controlpoints.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 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.vertices = vertices
+ 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')
+ self.mcount = polyline.mcount
+ self.ncount = polyline.ncount
+ self.is_mclosed = polyline.is_mclosed
+ self.is_nclosed = polyline.is_nclosed
+ self._vertices = polyline.vertices
+ self.m_smooth_density = polyline.m_smooth_density
+ self.n_smooth_density = polyline.n_smooth_density
+ self.smooth_type = polyline.smooth_type
+
+ def __iter__(self):
+ return iter(self._vertices)
+
+ def get_location(self, pos):
+ return self.get_vertex(pos).location
+
+ def get_vertex(self, pos):
+ mcount = self.mcount
+ ncount = self.ncount
+ m, n = pos
+ if 0 <= m < mcount and 0 <= n < ncount:
+ pos = m * ncount + n
+ return self._vertices[pos]
+ else:
+ raise IndexError(repr(pos))
+
+
+class Vertex(Shape):
+ def __init__(self, wrapper):
+ super(Vertex, self).__init__(wrapper)
+ get_dxf = wrapper.get_dxf_attrib
+ self.location = get_dxf('location')
+ self.flags = get_dxf('flags', 0)
+ self.start_width = get_dxf('startwidth', 0)
+ self.end_width = get_dxf('endwidth', 0)
+ self.bulge = get_dxf('bulge', 0)
+ self.tangent = get_dxf('tangent', None)
+ self.vtx = self._get_vtx(wrapper)
+
+ def _get_vtx(self, wrapper):
+ vtx = []
+ get_dxf = wrapper.get_dxf_attrib
+ for vname in const.VERTEXNAMES:
+ try:
+ vtx.append(get_dxf(vname))
+ except ValueError:
+ pass
+ return tuple(vtx)
+
+
+class LWPolyline(Shape):
+ def __init__(self, wrapper):
+ super(LWPolyline, self).__init__(wrapper)
+ self.points, self.width, self.bulge = wrapper.data()
+ self.const_width = wrapper.get_dxf_attrib('const_width', 0)
+ self.is_closed = wrapper.is_closed()
+ self.elevation = wrapper.get_dxf_attrib('elevation', (0., 0., 0.))
+
+ def __len__(self):
+ return len(self.points)
+
+ def __getitem__(self, item):
+ return self.points[item]
+
+ def __iter__(self):
+ return iter(self.points)
+
+
+class Ellipse(Shape):
+ def __init__(self, wrapper):
+ super(Ellipse, self).__init__(wrapper)
+ get_dxf = wrapper.get_dxf_attrib
+ self.center = get_dxf('center')
+ self.majoraxis = get_dxf('majoraxis')
+ self.ratio = get_dxf('ratio', 1.0) # circle
+ self.startparam = get_dxf('startparam', 0.)
+ self.endparam = get_dxf('endparam', 6.283185307179586) # 2*pi
+
+
+class Ray(Shape):
+ def __init__(self, wrapper):
+ super(Ray, self).__init__(wrapper)
+ self.start = wrapper.get_dxf_attrib('start')
+ self.unitvector = wrapper.get_dxf_attrib('unitvector')
+
+XLine = Ray
+
+
+class Spline(Shape):
+ def __init__(self, wrapper):
+ super(Spline, self).__init__(wrapper)
+ get_dxf = wrapper.get_dxf_attrib
+ self.normalvector = get_dxf('normalvector', None)
+ self.flags = get_dxf('flags', 0)
+ self.degree = get_dxf('degree', 3)
+ self.starttangent = get_dxf('starttangent', None)
+ self.endtangent = get_dxf('endtangent', None)
+ self.knots = tuple(wrapper.knots())
+ self.weights = tuple(wrapper.weights())
+ self.tol_knot = get_dxf('knot_tolernace', .0000001)
+ self.tol_controlpoint = get_dxf('controlpoint_tolerance', .0000001)
+ self.tol_fitpoint = get_dxf('fitpoint_tolerance', .0000000001)
+ self.controlpoints = tuple(wrapper.controlpoints())
+ self.fitpoints = tuple(wrapper.fitpoints())
+ if len(self.weights) == 0:
+ self.weights = tuple([1.0] * len(self.controlpoints))
+
+ @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, wrapper):
+ super(Helix, self).__init__(wrapper)
+ get_dxf = wrapper.get_dxf_attrib
+ self.helix_version = (get_dxf('helix_major_version', 1),
+ get_dxf('helix_maintainance_version', 1))
+ self.axis_base_point = get_dxf('axis_base_point', None)
+ self.start_point = get_dxf('start_point', None)
+ self.axis_vector = get_dxf('axis_vector', None)
+ self.radius = get_dxf('radius', 0)
+ self.turns = get_dxf('turns', 0)
+ self.turn_height = get_dxf('turn_height', 0)
+ self.handedness = get_dxf('handedness', 0) # 0 = left, 1 = right
+ self.constrain = get_dxf('constrain', 0)
+ # 0 = Constrain turn height;
+ # 1 = Constrain turns;
+ # 2 = Constrain height
+
+
+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(Shape):
+ def __init__(self, wrapper):
+
+ super(MText, self).__init__(wrapper)
+ self.insert = wrapper.get_dxf_attrib('insert')
+ self.rawtext = wrapper.rawtext()
+ get_dxf = wrapper.get_dxf_attrib
+ self.height = get_dxf('height', 0)
+ self.rect_width = get_dxf('reference_rectangle_width', None)
+ self.horizontal_width = get_dxf('horizontal_width', None)
+ self.vertical_height = get_dxf('vertical_height', None)
+ self.linespacing = get_dxf('linespacing', 1.0)
+ self.attachmentpoint = get_dxf('attachmentpoint', 1)
+ self.style = get_dxf('style', 'STANDARD')
+ self.extrusion = get_dxf('extrusion', (0., 0., 1.))
+ try:
+ xdir = wrapper.get_dxf_attrib('xdirection')
+ except ValueError:
+ xdir = deg2vec(get_dxf('rotation', 0.0))
+ self.xdirection = normalized(xdir)
+ self.font = None
+ self.bigfont = None
+
+ def lines(self):
+ return self.rawtext.split('\P')
+
+ def plain_text(self, split=False):
+ chars = []
+ raw_chars = list(reversed(self.rawtext)) # 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.bigfont is None:
+ self.bigfont = style.font
+
+
+class Block(Shape):
+ def __init__(self, wrapper):
+ super(Block, self).__init__(wrapper)
+ self.basepoint = wrapper.get_dxf_attrib('basepoint')
+ self.name = wrapper.get_dxf_attrib('name')
+ self.flags = wrapper.get_dxf_attrib('flags', 0)
+ self.xrefpath = wrapper.get_dxf_attrib('xrefpath', "")
+ self._entities = list()
+
+ @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 BlockEnd(SeqEnd):
+ pass
+
+
+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(Entity):
+ def __init__(self, wrapper):
+ super(Sun, self).__init__(wrapper)
+ get_dxf = wrapper.get_dxf_attrib
+ self.version = get_dxf('version', 1)
+ self.status = bool(get_dxf('status', 0)) # on/off ?
+ self.sun_color = get_dxf('sun_color', None) # None is unset
+ self.intensity = get_dxf('intensity', 0)
+ self.shadows = bool(get_dxf('shadows', 0))
+ julian_date = get_dxf('date', 0.)
+ if julian_date > 0.:
+ date = calendar_date(julian_date)
+ else:
+ date = datetime.now()
+ hours, minutes, seconds = unpack_seconds(get_dxf('time', 0))
+ self.date = datetime(date.year, date.month, date.day, hours, minutes, seconds)
+ self.daylight_savings_time = bool(get_dxf('daylight_savings_time', 0))
+ self.shadow_type = get_dxf('shadows_type', 0)
+ self.shadow_map_size = get_dxf('shadow_map_size', 0)
+ self.shadow_softness = get_dxf('shadow_softness', 0)
+
+
+class Mesh(Shape):
+ def __init__(self, wrapper):
+ super(Mesh, self).__init__(wrapper)
+ get_dxf = wrapper.get_dxf_attrib
+ self.version = get_dxf('version', 2)
+ self.blend_crease = bool(get_dxf('blend_crease', 0))
+ self.subdivision_levels = get_dxf('subdivision_levels', 1)
+ # rest are mostly positional tags
+ self.vertices = []
+ self.faces = []
+ self.edges = []
+ self.edge_crease_list = []
+
+ subdmesh_tags = wrapper.tags.get_subclass('AcDbSubDMesh')
+ # for all blocks I ignore the count values, perhaps they are wrong,
+ # but I use the count tags as indicator for the begin of the list
+ try:
+ pos = subdmesh_tags.tag_index(92)
+ except ValueError: # no vertices???
+ return
+ else:
+ self.vertices = Mesh.get_vertices(subdmesh_tags, pos+1)
+ try:
+ pos = subdmesh_tags.tag_index(93)
+ except ValueError: # no faces???
+ pass
+ else:
+ self.faces = Mesh.get_faces(subdmesh_tags, pos+1)
+ try:
+ pos = subdmesh_tags.tag_index(94)
+ except ValueError: # no edges
+ pass
+ else:
+ self.edges = Mesh.get_edges(subdmesh_tags, pos+1)
+ try:
+ pos = subdmesh_tags.tag_index(95)
+ except ValueError: # no edges crease values
+ pass
+ else:
+ self.edge_crease_list = Mesh.get_edge_crease_list(subdmesh_tags, pos+1)
+
+ 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])
+
+ @staticmethod
+ def get_vertices(tags, pos):
+ vertices = []
+ itags = iter(tags[pos:])
+ while True:
+ try:
+ tag = next(itags)
+ except StopIteration: # premature end of tags, return what you got
+ break
+ if tag.code == 10:
+ vertices.append(tag.value)
+ else:
+ break
+ return vertices
+
+ @staticmethod
+ def get_faces(tags, pos):
+ faces = []
+ face = []
+ itags = iter(tags[pos:])
+ try:
+ while True:
+ tag = next(itags)
+ # loop until first tag.code != 90
+ if tag.code != 90:
+ break
+ count = tag.value # count of vertex indices
+ while count > 0:
+ tag = next(itags)
+ face.append(tag.value)
+ count -= 1
+ faces.append(tuple(face))
+ del face[:]
+ except StopIteration: # premature end of tags, return what you got
+ pass
+ return faces
+
+ @staticmethod
+ def get_edges(tags, pos):
+ edges = []
+ start_index = None
+ for index in Mesh.get_raw_list(tags, pos, code=90):
+ if start_index is None:
+ start_index = index
+ else:
+ edges.append((start_index, index))
+ start_index = None
+ return edges
+
+ @staticmethod
+ def get_edge_crease_list(tags, pos):
+ return Mesh.get_raw_list(tags, pos, code=140)
+
+ @staticmethod
+ def get_raw_list(tags, pos, code):
+ raw_list = []
+ itags = iter(tags[pos:])
+ while True:
+ try:
+ tag = next(itags)
+ except StopIteration:
+ break
+ if tag.code == code:
+ raw_list.append(tag.value)
+ else:
+ break
+ return raw_list
+
+
+class Light(Shape):
+ def __init__(self, wrapper):
+ super(Light, self).__init__(wrapper)
+ get_dxf = wrapper.get_dxf_attrib
+ self.version = get_dxf('version', 1)
+ self.name = get_dxf('name', "")
+ self.light_type = get_dxf('light_type', 1) # distant = 1; point = 2; spot = 3
+ self.status = bool(get_dxf('status', 0)) # on/off ?
+ self.light_color = get_dxf('light_color', None) # 0 is unset
+ self.true_color = get_dxf('true_color', None) # None is unset
+ self.plot_glyph = bool(get_dxf('plot_glyph', 0))
+ self.intensity = get_dxf('intensity', 0)
+ self.position = get_dxf('position', (0, 0, 1))
+ self.target = get_dxf('target', (0, 0, 0))
+ self.attenuation_type = get_dxf('attenuation_type', 0) # 0 = None; 1 = Inverse Linear; 2 = Inverse Square
+ self.use_attenuation_limits = bool(get_dxf('use_attenuation_limits', 0))
+ self.attenuation_start_limit = get_dxf('attenuation_start_limit', 0)
+ self.attenuation_end_limit = get_dxf('attenuation_end_limit', 0)
+ self.hotspot_angle = get_dxf('hotspot_angle', 0)
+ self.fall_off_angle = get_dxf('fall_off_angle', 0)
+ self.cast_shadows = bool(get_dxf('cast_shadows', 0))
+ self.shadow_type = get_dxf('shadow_type', 0) # 0 = Ray traced shadows; 1 = Shadow maps
+ self.shadow_map_size = get_dxf('shadow_map_size', 0)
+ self.shadow_softness = get_dxf('shadow_softness', 0)
+
+
+class Body(Shape):
+ def __init__(self, wrapper):
+ super(Body, self).__init__(wrapper)
+ # need handle to get SAB data in DXF version AC1027 and later
+ self.handle = wrapper.get_dxf_attrib('handle', None)
+ self.version = wrapper.get_dxf_attrib('version', 1)
+ self.acis = wrapper.get_acis_data()
+
+ 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
+
+Solid3d = Body
+# perhaps reading creation history is needed
+
+
+class Surface(Body):
+ def __init__(self, wrapper):
+ super(Surface, self).__init__(wrapper)
+ self.u_isolines = wrapper.get_dxf_attrib('u_isolines', 0)
+ self.v_isolines = wrapper.get_dxf_attrib('v_isolines', 0)
+
+
+EntityTable = {
+ 'LINE': (Line, dxf12.Line, dxf13.Line),
+ 'POINT': (Point, dxf12.Point, dxf13.Point),
+ 'CIRCLE': (Circle, dxf12.Circle, dxf13.Circle),
+ 'ARC': (Arc, dxf12.Arc, dxf13.Arc),
+ 'TRACE': (Trace, dxf12.Trace, dxf13.Trace),
+ 'SOLID': (Solid, dxf12.Solid, dxf13.Solid),
+ '3DFACE': (Face, dxf12.Face, dxf13.Face),
+ 'TEXT': (Text, dxf12.Text, dxf13.Text),
+ 'INSERT': (Insert, dxf12.Insert, dxf13.Insert),
+ 'SEQEND': (SeqEnd, dxf12.SeqEnd, dxf13.SeqEnd),
+ 'ATTRIB': (Attrib, dxf12.Attrib, dxf13.Attrib),
+ 'ATTDEF': (Attrib, dxf12.Attrib, dxf13.Attdef),
+ 'POLYLINE': (Polyline, dxf12.Polyline, dxf13.Polyline),
+ 'VERTEX': (Vertex, dxf12.Vertex, dxf13.Vertex),
+ 'BLOCK': (Block, dxf12.Block, dxf13.Block),
+ 'ENDBLK': (BlockEnd, dxf12.EndBlk, dxf13.EndBlk),
+ 'LWPOLYLINE': (LWPolyline, None, dxf13.LWPolyline),
+ 'ELLIPSE': (Ellipse, None, dxf13.Ellipse),
+ 'RAY': (Ray, None, dxf13.Ray),
+ 'XLINE': (XLine, None, dxf13.XLine),
+ 'SPLINE': (Spline, None, dxf13.Spline),
+ 'HELIX': (Helix, None, dxf13.Helix),
+ 'MTEXT': (MText, None, dxf13.MText),
+ 'SUN': (Sun, None, dxf13.Sun),
+ 'MESH': (Mesh, None, dxf13.Mesh),
+ 'LIGHT': (Light, None, dxf13.Light),
+ 'BODY': (Body, None, dxf13.Body),
+ 'REGION': (Body, None, dxf13.Body),
+ '3DSOLID': (Solid3d, None, dxf13.Solid3d),
+ 'SURFACE': (Surface, None, dxf13.Surface),
+ 'PLANESURFACE': (Surface, None, dxf13.Surface),
+}
+
+
+def entity_factory(tags, dxfversion):
+ dxftype = tags.get_type()
+ cls, dxf12wrapper, dxf13wrapper = EntityTable[dxftype]
+ wrapper = dxf12wrapper(tags) if dxfversion == "AC1009" else dxf13wrapper(tags)
+ wrapper.post_read_correction()
+ shape = cls(wrapper)
+ return shape
+
+