forked from bartvdbraak/blender
330efd46fa
faster dict/list operations. use layer flags ratehr then lists.
1326 lines
40 KiB
Python
1326 lines
40 KiB
Python
"""This module provides wrapper objects for dxf entities.
|
|
|
|
The wrappers expect a "dxf object" as input. The dxf object is
|
|
an object with a type and a data attribute. Type is a lowercase
|
|
string matching the 0 code of a dxf entity. Data is a list containing
|
|
dxf objects or lists of [code, data] pairs.
|
|
|
|
This module is not general, and is only for dxf import.
|
|
"""
|
|
|
|
# --------------------------------------------------------------------------
|
|
# DXF Import Objects v0.8 by Ed Blake (AKA Kitsu)
|
|
# --------------------------------------------------------------------------
|
|
# ***** BEGIN GPL LICENSE BLOCK *****
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software Foundation,
|
|
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
#
|
|
# ***** END GPL LICENCE BLOCK *****
|
|
# --------------------------------------------------------------------------
|
|
from math import *
|
|
|
|
|
|
# from Stani's dxf writer v1.1 (c)www.stani.be (GPL)
|
|
#---color values
|
|
BYBLOCK=0
|
|
BYLAYER=256
|
|
|
|
#---block-type flags (bit coded values, may be combined):
|
|
ANONYMOUS =1 # This is an anonymous block generated by hatching, associative dimensioning, other internal operations, or an application
|
|
NON_CONSTANT_ATTRIBUTES =2 # This block has non-constant attribute definitions (this bit is not set if the block has any attribute definitions that are constant, or has no attribute definitions at all)
|
|
XREF =4 # This block is an external reference (xref)
|
|
XREF_OVERLAY =8 # This block is an xref overlay
|
|
EXTERNAL =16 # This block is externally dependent
|
|
RESOLVED =32 # This is a resolved external reference, or dependent of an external reference (ignored on input)
|
|
REFERENCED =64 # This definition is a referenced external reference (ignored on input)
|
|
|
|
#---mtext flags
|
|
#attachment point
|
|
TOP_LEFT = 1
|
|
TOP_CENTER = 2
|
|
TOP_RIGHT = 3
|
|
MIDDLE_LEFT = 4
|
|
MIDDLE_CENTER = 5
|
|
MIDDLE_RIGHT = 6
|
|
BOTTOM_LEFT = 7
|
|
BOTTOM_CENTER = 8
|
|
BOTTOM_RIGHT = 9
|
|
#drawing direction
|
|
LEFT_RIGHT = 1
|
|
TOP_BOTTOM = 3
|
|
BY_STYLE = 5 #the flow direction is inherited from the associated text style
|
|
#line spacing style (optional):
|
|
AT_LEAST = 1 #taller characters will override
|
|
EXACT = 2 #taller characters will not override
|
|
|
|
#---polyline flags
|
|
CLOSED =1 # This is a closed polyline (or a polygon mesh closed in the M direction)
|
|
CURVE_FIT =2 # Curve-fit vertices have been added
|
|
SPLINE_FIT =4 # Spline-fit vertices have been added
|
|
POLYLINE_3D =8 # This is a 3D polyline
|
|
POLYGON_MESH =16 # This is a 3D polygon mesh
|
|
CLOSED_N =32 # The polygon mesh is closed in the N direction
|
|
POLYFACE_MESH =64 # The polyline is a polyface mesh
|
|
CONTINOUS_LINETYPE_PATTERN =128 # The linetype pattern is generated continuously around the vertices of this polyline
|
|
|
|
#---text flags
|
|
#horizontal
|
|
LEFT = 0
|
|
CENTER = 1
|
|
RIGHT = 2
|
|
ALIGNED = 3 #if vertical alignment = 0
|
|
MIDDLE = 4 #if vertical alignment = 0
|
|
FIT = 5 #if vertical alignment = 0
|
|
#vertical
|
|
BASELINE = 0
|
|
BOTTOM = 1
|
|
MIDDLE = 2
|
|
TOP = 3
|
|
class Object:
|
|
"""Empty container class for dxf objects"""
|
|
|
|
def __init__(self, _type=''):
|
|
"""_type expects a string value."""
|
|
self.type = _type
|
|
self.name = ''
|
|
self.data = []
|
|
|
|
def __str__(self):
|
|
if self.name:
|
|
return self.name
|
|
else:
|
|
return self.type
|
|
|
|
def __repr__(self):
|
|
return str(self.data)
|
|
|
|
def get_type(self, kind=''):
|
|
"""Despite the name, this method actually returns all objects of type 'kind' from self.data."""
|
|
if type:
|
|
objects = []
|
|
for item in self.data:
|
|
if type(item) != list and item.type == kind:
|
|
# we want this type of object
|
|
objects.append(item)
|
|
elif type(item) == list and item[0] == kind:
|
|
# we want this type of data
|
|
objects.append(item[1])
|
|
return objects
|
|
|
|
|
|
class Layer:
|
|
"""Class for objects representing dxf layers."""
|
|
|
|
def __init__(self, obj):
|
|
"""Expects an entity object of type line as input."""
|
|
self.type = obj.type
|
|
self.data = obj.data[:]
|
|
|
|
self.name = obj.get_type(2)[0]
|
|
self.color = obj.get_type(62)[0]
|
|
self.flags = obj.get_type(70)[0]
|
|
self.frozen = self.flags&1
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
return "%s: name - %s, color - %s" %(self.__class__.__name__, self.name, self.color)
|
|
|
|
|
|
|
|
class Line:
|
|
"""Class for objects representing dxf lines."""
|
|
|
|
def __init__(self, obj):
|
|
"""Expects an entity object of type line as input."""
|
|
if not obj.type == 'line':
|
|
raise TypeError, "Wrong type %s for line object!" %obj.type
|
|
self.type = obj.type
|
|
self.data = obj.data[:]
|
|
|
|
self.space = obj.get_type(67)
|
|
if self.space:
|
|
self.space = self.space[0]
|
|
else:
|
|
self.space = 0
|
|
|
|
self.color_index = obj.get_type(62)
|
|
if self.color_index:
|
|
self.color_index = self.color_index[0]
|
|
else:
|
|
self.color_index = BYLAYER
|
|
|
|
discard, self.layer, discard_index = get_layer(obj.data)
|
|
del obj.data[discard_index]
|
|
|
|
self.points = self.get_points(obj.data)
|
|
|
|
|
|
|
|
|
|
def get_points(self, data):
|
|
"""Gets start and end points for a line type object.
|
|
|
|
Lines have a fixed number of points (two) and fixed codes for each value.
|
|
"""
|
|
|
|
# start x, y, z and end x, y, z = 0
|
|
sx, sy, sz, ex, ey, ez = 0, 0, 0, 0, 0, 0
|
|
for item in data:
|
|
if item[0] == 10: # 10 = x
|
|
sx = item[1]
|
|
elif item[0] == 20: # 20 = y
|
|
sy = item[1]
|
|
elif item[0] == 30: # 30 = z
|
|
sz = item[1]
|
|
elif item[0] == 11: # 11 = x
|
|
ex = item[1]
|
|
elif item[0] == 21: # 21 = y
|
|
ey = item[1]
|
|
elif item[0] == 31: # 31 = z
|
|
ez = item[1]
|
|
return [[sx, sy, sz], [ex, ey, ez]]
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
return "%s: layer - %s, points - %s" %(self.__class__.__name__, self.layer, self.points)
|
|
|
|
|
|
|
|
class LWpolyline:
|
|
"""Class for objects representing dxf LWpolylines."""
|
|
|
|
def __init__(self, obj):
|
|
"""Expects an entity object of type lwpolyline as input."""
|
|
if not obj.type == 'lwpolyline':
|
|
raise TypeError, "Wrong type %s for polyline object!" %obj.type
|
|
self.type = obj.type
|
|
self.data = obj.data[:]
|
|
|
|
# required data
|
|
self.num_points = obj.get_type(90)[0]
|
|
|
|
# optional data (with defaults)
|
|
self.space = obj.get_type(67)
|
|
if self.space:
|
|
self.space = self.space[0]
|
|
else:
|
|
self.space = 0
|
|
|
|
self.color_index = obj.get_type(62)
|
|
if self.color_index:
|
|
self.color_index = self.color_index[0]
|
|
else:
|
|
self.color_index = BYLAYER
|
|
|
|
self.elevation = obj.get_type(38)
|
|
if self.elevation:
|
|
self.elevation = self.elevation[0]
|
|
else:
|
|
self.elevation = 0
|
|
|
|
self.flags = obj.get_type(70)
|
|
if self.flags:
|
|
self.flags = self.flags[0]
|
|
else:
|
|
self.flags = 0
|
|
|
|
self.closed = self.flags&1 # byte coded, 1 = closed, 128 = plinegen
|
|
discard, self.layer, discard_index = get_layer(obj.data)
|
|
del obj.data[discard_index]
|
|
self.points = self.get_points(obj.data)
|
|
self.extrusion = self.get_extrusion(obj.data)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_points(self, data):
|
|
"""Gets points for a polyline type object.
|
|
|
|
Polylines have no fixed number of verts, and
|
|
each vert can have a number of properties.
|
|
Verts should be coded as
|
|
10:xvalue
|
|
20:yvalue
|
|
40:startwidth or 0
|
|
41:endwidth or 0
|
|
42:bulge or 0
|
|
for each vert
|
|
"""
|
|
num = self.num_points
|
|
point = None
|
|
points = []
|
|
for item in data:
|
|
if item[0] == 10: # 10 = x
|
|
if point:
|
|
points.append(point)
|
|
point = Vertex()
|
|
point.x = item[1]
|
|
elif item[0] == 20: # 20 = y
|
|
point.y = item[1]
|
|
elif item[0] == 40: # 40 = start width
|
|
point.swidth = item[1]
|
|
elif item[0] == 41: # 41 = end width
|
|
point.ewidth = item[1]
|
|
elif item[0] == 42: # 42 = bulge
|
|
point.bulge = item[1]
|
|
points.append(point)
|
|
return points
|
|
|
|
|
|
def get_extrusion(self, data):
|
|
"""Find the axis of extrusion.
|
|
|
|
Used to get the objects Object Coordinate System (ocs).
|
|
"""
|
|
vec = [0,0,1]
|
|
for item in data:
|
|
if item[0] == 210: # 210 = x
|
|
vec[0] = item[1]
|
|
elif item[0] == 220: # 220 = y
|
|
vec[1] = item[1]
|
|
elif item[0] == 230: # 230 = z
|
|
vec[2] = item[1]
|
|
return vec
|
|
|
|
|
|
def __repr__(self):
|
|
return "%s: layer - %s, points - %s" %(self.__class__.__name__, self.layer, self.points)
|
|
|
|
|
|
|
|
class Polyline:
|
|
"""Class for objects representing dxf LWpolylines."""
|
|
|
|
def __init__(self, obj):
|
|
"""Expects an entity object of type polyline as input."""
|
|
if not obj.type == 'polyline':
|
|
raise TypeError, "Wrong type %s for polyline object!" %obj.type
|
|
self.type = obj.type
|
|
self.data = obj.data[:]
|
|
self.points = []
|
|
|
|
# optional data (with defaults)
|
|
self.space = obj.get_type(67)
|
|
if self.space:
|
|
self.space = self.space[0]
|
|
else:
|
|
self.space = 0
|
|
|
|
self.color_index = obj.get_type(62)
|
|
if self.color_index:
|
|
self.color_index = self.color_index[0]
|
|
else:
|
|
self.color_index = BYLAYER
|
|
|
|
self.elevation = obj.get_type(30)
|
|
if self.elevation:
|
|
self.elevation = self.elevation[0]
|
|
else:
|
|
self.elevation = 0
|
|
|
|
self.flags = obj.get_type(70)
|
|
if self.flags:
|
|
self.flags = self.flags[0]
|
|
else:
|
|
self.flags = 0
|
|
|
|
self.closed = self.flags&1 # byte coded, 1 = closed, 128 = plinegen
|
|
|
|
discard, self.layer, discard_index = get_layer(obj.data)
|
|
del obj.data[discard_index]
|
|
self.extrusion = self.get_extrusion(obj.data)
|
|
|
|
|
|
|
|
|
|
|
|
def get_extrusion(self, data):
|
|
"""Find the axis of extrusion.
|
|
|
|
Used to get the objects Object Coordinate System (ocs).
|
|
"""
|
|
vec = [0,0,1]
|
|
for item in data:
|
|
if item[0] == 210: # 210 = x
|
|
vec[0] = item[1]
|
|
elif item[0] == 220: # 220 = y
|
|
vec[1] = item[1]
|
|
elif item[0] == 230: # 230 = z
|
|
vec[2] = item[1]
|
|
return vec
|
|
|
|
|
|
def __repr__(self):
|
|
return "%s: layer - %s, points - %s" %(self.__class__.__name__, self.layer, self.points)
|
|
|
|
|
|
|
|
class Vertex(object):
|
|
"""Generic vertex object used by polylines (and maybe others)."""
|
|
|
|
def __init__(self, obj=None):
|
|
"""Initializes vertex data.
|
|
|
|
The optional obj arg is an entity object of type vertex.
|
|
"""
|
|
self.loc = [0,0,0]
|
|
self.bulge = 0
|
|
self.swidth = 0
|
|
self.ewidth = 0
|
|
self.flags = 0
|
|
|
|
if obj is not None:
|
|
if not obj.type == 'vertex':
|
|
raise TypeError, "Wrong type %s for vertex object!" %obj.type
|
|
self.type = obj.type
|
|
self.data = obj.data[:]
|
|
|
|
self.get_props(obj.data)
|
|
|
|
|
|
def get_props(self, data):
|
|
"""Gets coords for a vertex type object.
|
|
|
|
Each vert can have a number of properties.
|
|
Verts should be coded as
|
|
10:xvalue
|
|
20:yvalue
|
|
40:startwidth or 0
|
|
41:endwidth or 0
|
|
42:bulge or 0
|
|
"""
|
|
for item in data:
|
|
if item[0] == 10: # 10 = x
|
|
self.x = item[1]
|
|
elif item[0] == 20: # 20 = y
|
|
self.y = item[1]
|
|
elif item[0] == 30: # 30 = z
|
|
self.z = item[1]
|
|
elif item[0] == 40: # 40 = start width
|
|
self.swidth = item[1]
|
|
elif item[0] == 41: # 41 = end width
|
|
self.ewidth = item[1]
|
|
elif item[0] == 42: # 42 = bulge
|
|
self.bulge = item[1]
|
|
elif item[0] == 70: # 70 = vert flags
|
|
self.flags = item[1]
|
|
|
|
|
|
def __len__(self):
|
|
return 3
|
|
|
|
|
|
def __getitem__(self, key):
|
|
return self.loc[key]
|
|
|
|
|
|
def __setitem__(self, key, value):
|
|
if key in [0,1,2]:
|
|
self.loc[key]
|
|
|
|
|
|
def __iter__(self):
|
|
return self.loc.__iter__()
|
|
|
|
|
|
def __str__(self):
|
|
return str(self.loc)
|
|
|
|
|
|
def __repr__(self):
|
|
return "Vertex %s, swidth=%s, ewidth=%s, bulge=%s" %(self.loc, self.swidth, self.ewidth, self.bulge)
|
|
|
|
|
|
def getx(self):
|
|
return self.loc[0]
|
|
|
|
def setx(self, value):
|
|
self.loc[0] = value
|
|
|
|
x = property(getx, setx)
|
|
|
|
|
|
def gety(self):
|
|
return self.loc[1]
|
|
|
|
def sety(self, value):
|
|
self.loc[1] = value
|
|
|
|
y = property(gety, sety)
|
|
|
|
|
|
def getz(self):
|
|
return self.loc[2]
|
|
|
|
def setz(self, value):
|
|
self.loc[2] = value
|
|
|
|
z = property(getz, setz)
|
|
|
|
|
|
|
|
class Text:
|
|
"""Class for objects representing dxf Text."""
|
|
|
|
def __init__(self, obj):
|
|
"""Expects an entity object of type text as input."""
|
|
if not obj.type == 'text':
|
|
raise TypeError, "Wrong type %s for text object!" %obj.type
|
|
self.type = obj.type
|
|
self.data = obj.data[:]
|
|
|
|
# required data
|
|
self.height = obj.get_type(40)[0]
|
|
self.value = obj.get_type(1)[0] # The text string value
|
|
|
|
# optional data (with defaults)
|
|
self.space = obj.get_type(67)
|
|
if self.space:
|
|
self.space = self.space[0]
|
|
else:
|
|
self.space = 0
|
|
|
|
self.color_index = obj.get_type(62)
|
|
if self.color_index:
|
|
self.color_index = self.color_index[0]
|
|
else:
|
|
self.color_index = BYLAYER
|
|
|
|
self.rotation = obj.get_type(50) # radians?
|
|
if not self.rotation:
|
|
self.rotation = 0
|
|
else:
|
|
self.rotation = self.rotation[0]
|
|
|
|
self.width_factor = obj.get_type(41) # Scaling factor along local x axis
|
|
if not self.width_factor:
|
|
self.width_factor = 1
|
|
else:
|
|
self.width_factor = self.width_factor[0]
|
|
|
|
self.oblique = obj.get_type(51) # skew in degrees -90 <= oblique <= 90
|
|
if not self.oblique:
|
|
self.oblique = 0
|
|
else:
|
|
self.oblique = self.oblique[0]
|
|
|
|
self.halignment = obj.get_type(72) # horiz. alignment
|
|
if not self.halignment: # 0=left, 1=center, 2=right, 3=aligned, 4=middle, 5=fit
|
|
self.halignment = 0
|
|
else:
|
|
self.halignment = self.halignment[0]
|
|
|
|
self.valignment = obj.get_type(73) # vert. alignment
|
|
if not self.valignment: # 0=baseline, 1=bottom, 2=middle, 3=top
|
|
self.valignment = 0
|
|
else:
|
|
self.valignment = self.valignment[0]
|
|
|
|
discard, self.layer, discard_index = get_layer(obj.data)
|
|
del obj.data[discard_index]
|
|
self.loc = self.get_loc(obj.data, self.halignment, self.valignment)
|
|
self.extrusion = self.get_extrusion(obj.data)
|
|
|
|
|
|
|
|
|
|
def get_loc(self, data, halign, valign):
|
|
"""Gets adjusted location for text type objects.
|
|
|
|
If group 72 and/or 73 values are nonzero then the first alignment point values
|
|
are ignored and AutoCAD calculates new values based on the second alignment
|
|
point and the length and height of the text string itself (after applying the
|
|
text style). If the 72 and 73 values are zero or missing, then the second
|
|
alignment point is meaningless.
|
|
|
|
I don't know how to calc text size...
|
|
"""
|
|
# bottom left x, y, z and justification x, y, z = 0
|
|
x, y, z, jx, jy, jz = 0, 0, 0, 0, 0, 0
|
|
for item in data:
|
|
if item[0] == 10: # 10 = x
|
|
x = item[1]
|
|
elif item[0] == 20: # 20 = y
|
|
y = item[1]
|
|
elif item[0] == 30: # 30 = z
|
|
z = item[1]
|
|
elif item[0] == 11: # 11 = x
|
|
jx = item[1]
|
|
elif item[0] == 21: # 21 = y
|
|
jy = item[1]
|
|
elif item[0] == 31: # 31 = z
|
|
jz = item[1]
|
|
|
|
if halign or valign:
|
|
x, y, z = jx, jy, jz
|
|
return [x, y, z]
|
|
|
|
def get_extrusion(self, data):
|
|
"""Find the axis of extrusion.
|
|
|
|
Used to get the objects Object Coordinate System (ocs).
|
|
"""
|
|
vec = [0,0,1]
|
|
for item in data:
|
|
if item[0] == 210: # 210 = x
|
|
vec[0] = item[1]
|
|
elif item[0] == 220: # 220 = y
|
|
vec[1] = item[1]
|
|
elif item[0] == 230: # 230 = z
|
|
vec[2] = item[1]
|
|
return vec
|
|
|
|
|
|
def __repr__(self):
|
|
return "%s: layer - %s, value - %s" %(self.__class__.__name__, self.layer, self.value)
|
|
|
|
|
|
|
|
class Mtext:
|
|
"""Class for objects representing dxf Mtext."""
|
|
|
|
def __init__(self, obj):
|
|
"""Expects an entity object of type mtext as input."""
|
|
if not obj.type == 'mtext':
|
|
raise TypeError, "Wrong type %s for mtext object!" %obj.type
|
|
self.type = obj.type
|
|
self.data = obj.data[:]
|
|
|
|
# required data
|
|
self.height = obj.get_type(40)[0]
|
|
self.width = obj.get_type(41)[0]
|
|
self.alignment = obj.get_type(71)[0] # alignment 1=TL, 2=TC, 3=TR, 4=ML, 5=MC, 6=MR, 7=BL, 8=BC, 9=BR
|
|
self.value = self.get_text(obj.data) # The text string value
|
|
|
|
# optional data (with defaults)
|
|
self.space = obj.get_type(67)
|
|
if self.space:
|
|
self.space = self.space[0]
|
|
else:
|
|
self.space = 0
|
|
|
|
self.color_index = obj.get_type(62)
|
|
if self.color_index:
|
|
self.color_index = self.color_index[0]
|
|
else:
|
|
self.color_index = BYLAYER
|
|
|
|
self.rotation = obj.get_type(50) # radians
|
|
if not self.rotation:
|
|
self.rotation = 0
|
|
else:
|
|
self.rotation = self.rotation[0]
|
|
|
|
self.width_factor = obj.get_type(42) # Scaling factor along local x axis
|
|
if not self.width_factor:
|
|
self.width_factor = 1
|
|
else:
|
|
self.width_factor = self.width_factor[0]
|
|
|
|
self.line_space = obj.get_type(44) # percentage of default
|
|
if not self.line_space:
|
|
self.line_space = 1
|
|
else:
|
|
self.line_space = self.line_space[0]
|
|
|
|
discard, self.layer, discard_index = get_layer(obj.data)
|
|
del obj.data[discard_index]
|
|
self.loc = self.get_loc(obj.data)
|
|
self.extrusion = self.get_extrusion(obj.data)
|
|
|
|
|
|
|
|
|
|
|
|
def get_text(self, data):
|
|
"""Reconstructs mtext data from dxf codes."""
|
|
primary = ''
|
|
secondary = []
|
|
for item in data:
|
|
if item[0] == 1: # There should be only one primary...
|
|
primary = item[1]
|
|
elif item[0] == 3: # There may be any number of extra strings (in order)
|
|
secondary.append(item[1])
|
|
if not primary:
|
|
#raise ValueError, "Empty Mtext Object!"
|
|
string = "Empty Mtext Object!"
|
|
if not secondary:
|
|
string = primary.replace(r'\P', '\n')
|
|
else:
|
|
string = ''.join(secondary)+primary
|
|
string = string.replace(r'\P', '\n')
|
|
return string
|
|
def get_loc(self, data):
|
|
"""Gets location for a mtext type objects.
|
|
|
|
Mtext objects have only one point indicating location.
|
|
"""
|
|
loc = [0,0,0]
|
|
for item in data:
|
|
if item[0] == 10: # 10 = x
|
|
loc[0] = item[1]
|
|
elif item[0] == 20: # 20 = y
|
|
loc[1] = item[1]
|
|
elif item[0] == 30: # 30 = z
|
|
loc[2] = item[1]
|
|
return loc
|
|
|
|
|
|
|
|
|
|
def get_extrusion(self, data):
|
|
"""Find the axis of extrusion.
|
|
|
|
Used to get the objects Object Coordinate System (ocs).
|
|
"""
|
|
vec = [0,0,1]
|
|
for item in data:
|
|
if item[0] == 210: # 210 = x
|
|
vec[0] = item[1]
|
|
elif item[0] == 220: # 220 = y
|
|
vec[1] = item[1]
|
|
elif item[0] == 230: # 230 = z
|
|
vec[2] = item[1]
|
|
return vec
|
|
|
|
|
|
def __repr__(self):
|
|
return "%s: layer - %s, value - %s" %(self.__class__.__name__, self.layer, self.value)
|
|
|
|
|
|
|
|
class Circle:
|
|
"""Class for objects representing dxf Circles."""
|
|
|
|
def __init__(self, obj):
|
|
"""Expects an entity object of type circle as input."""
|
|
if not obj.type == 'circle':
|
|
raise TypeError, "Wrong type %s for circle object!" %obj.type
|
|
self.type = obj.type
|
|
self.data = obj.data[:]
|
|
|
|
# required data
|
|
self.radius = obj.get_type(40)[0]
|
|
|
|
# optional data (with defaults)
|
|
self.space = obj.get_type(67)
|
|
if self.space:
|
|
self.space = self.space[0]
|
|
else:
|
|
self.space = 0
|
|
|
|
self.color_index = obj.get_type(62)
|
|
if self.color_index:
|
|
self.color_index = self.color_index[0]
|
|
else:
|
|
self.color_index = BYLAYER
|
|
|
|
discard, self.layer, discard_index = get_layer(obj.data)
|
|
del obj.data[discard_index]
|
|
self.loc = self.get_loc(obj.data)
|
|
self.extrusion = self.get_extrusion(obj.data)
|
|
|
|
|
|
|
|
|
|
|
|
def get_loc(self, data):
|
|
"""Gets the center location for circle type objects.
|
|
|
|
Circles have a single coord location.
|
|
"""
|
|
loc = [0, 0, 0]
|
|
for item in data:
|
|
if item[0] == 10: # 10 = x
|
|
loc[0] = item[1]
|
|
elif item[0] == 20: # 20 = y
|
|
loc[1] = item[1]
|
|
elif item[0] == 30: # 30 = z
|
|
loc[2] = item[1]
|
|
return loc
|
|
|
|
|
|
|
|
def get_extrusion(self, data):
|
|
"""Find the axis of extrusion.
|
|
|
|
Used to get the objects Object Coordinate System (ocs).
|
|
"""
|
|
vec = [0,0,1]
|
|
for item in data:
|
|
if item[0] == 210: # 210 = x
|
|
vec[0] = item[1]
|
|
elif item[0] == 220: # 220 = y
|
|
vec[1] = item[1]
|
|
elif item[0] == 230: # 230 = z
|
|
vec[2] = item[1]
|
|
return vec
|
|
|
|
|
|
def __repr__(self):
|
|
return "%s: layer - %s, radius - %s" %(self.__class__.__name__, self.layer, self.radius)
|
|
|
|
|
|
|
|
class Arc:
|
|
"""Class for objects representing dxf arcs."""
|
|
|
|
def __init__(self, obj):
|
|
"""Expects an entity object of type arc as input."""
|
|
if not obj.type == 'arc':
|
|
raise TypeError, "Wrong type %s for arc object!" %obj.type
|
|
self.type = obj.type
|
|
self.data = obj.data[:]
|
|
|
|
# required data
|
|
self.radius = obj.get_type(40)[0]
|
|
self.start_angle = obj.get_type(50)[0]
|
|
self.end_angle = obj.get_type(51)[0]
|
|
|
|
# optional data (with defaults)
|
|
self.space = obj.get_type(67)
|
|
if self.space:
|
|
self.space = self.space[0]
|
|
else:
|
|
self.space = 0
|
|
|
|
self.color_index = obj.get_type(62)
|
|
if self.color_index:
|
|
self.color_index = self.color_index[0]
|
|
else:
|
|
self.color_index = BYLAYER
|
|
|
|
discard, self.layer, discard_index = get_layer(obj.data)
|
|
del obj.data[discard_index]
|
|
self.loc = self.get_loc(obj.data)
|
|
self.extrusion = self.get_extrusion(obj.data)
|
|
|
|
|
|
|
|
|
|
|
|
def get_loc(self, data):
|
|
"""Gets the center location for arc type objects.
|
|
|
|
Arcs have a single coord location.
|
|
"""
|
|
loc = [0, 0, 0]
|
|
for item in data:
|
|
if item[0] == 10: # 10 = x
|
|
loc[0] = item[1]
|
|
elif item[0] == 20: # 20 = y
|
|
loc[1] = item[1]
|
|
elif item[0] == 30: # 30 = z
|
|
loc[2] = item[1]
|
|
return loc
|
|
|
|
|
|
|
|
def get_extrusion(self, data):
|
|
"""Find the axis of extrusion.
|
|
|
|
Used to get the objects Object Coordinate System (ocs).
|
|
"""
|
|
vec = [0,0,1]
|
|
for item in data:
|
|
if item[0] == 210: # 210 = x
|
|
vec[0] = item[1]
|
|
elif item[0] == 220: # 220 = y
|
|
vec[1] = item[1]
|
|
elif item[0] == 230: # 230 = z
|
|
vec[2] = item[1]
|
|
return vec
|
|
|
|
|
|
def __repr__(self):
|
|
return "%s: layer - %s, radius - %s" %(self.__class__.__name__, self.layer, self.radius)
|
|
|
|
|
|
|
|
class BlockRecord:
|
|
"""Class for objects representing dxf block_records."""
|
|
|
|
def __init__(self, obj):
|
|
"""Expects an entity object of type block_record as input."""
|
|
if not obj.type == 'block_record':
|
|
raise TypeError, "Wrong type %s for block_record object!" %obj.type
|
|
self.type = obj.type
|
|
self.data = obj.data[:]
|
|
|
|
# required data
|
|
self.name = obj.get_type(2)[0]
|
|
|
|
# optional data (with defaults)
|
|
self.insertion_units = obj.get_type(70)
|
|
if not self.insertion_units:
|
|
self.insertion_units = None
|
|
else:
|
|
self.insertion_units = self.insertion_units[0]
|
|
|
|
self.insert_units = obj.get_type(1070)
|
|
if not self.insert_units:
|
|
self.insert_units = None
|
|
else:
|
|
self.insert_units = self.insert_units[0]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
return "%s: name - %s, insert units - %s" %(self.__class__.__name__, self.name, self.insertion_units)
|
|
|
|
|
|
|
|
|
|
class Block:
|
|
"""Class for objects representing dxf blocks."""
|
|
|
|
def __init__(self, obj):
|
|
"""Expects an entity object of type block as input."""
|
|
if not obj.type == 'block':
|
|
raise TypeError, "Wrong type %s for block object!" %obj.type
|
|
self.type = obj.type
|
|
self.data = obj.data[:]
|
|
|
|
# required data
|
|
self.flags = obj.get_type(70)[0]
|
|
self.entities = Object('block_contents')
|
|
self.entities.data = objectify([ent for ent in obj.data if type(ent) != list])
|
|
|
|
# optional data (with defaults)
|
|
self.name = obj.get_type(3)
|
|
if self.name:
|
|
self.name = self.name[0]
|
|
else:
|
|
self.name = ''
|
|
|
|
self.path = obj.get_type(1)
|
|
if self.path:
|
|
self.path = self.path[0]
|
|
else:
|
|
self.path = ''
|
|
|
|
self.discription = obj.get_type(4)
|
|
if self.discription:
|
|
self.discription = self.discription[0]
|
|
else:
|
|
self.discription = ''
|
|
|
|
discard, self.layer, discard_index = get_layer(obj.data)
|
|
del obj.data[discard_index]
|
|
self.loc = self.get_loc(obj.data)
|
|
|
|
|
|
|
|
|
|
|
|
def get_loc(self, data):
|
|
"""Gets the insert point of the block."""
|
|
loc = [0, 0, 0]
|
|
for item in data:
|
|
if type(item) != list:
|
|
continue
|
|
if item[0] == 10: # 10 = x
|
|
loc[0] = item[1]
|
|
elif item[0] == 20: # 20 = y
|
|
loc[1] = item[1]
|
|
elif item[0] == 30: # 30 = z
|
|
loc[2] = item[1]
|
|
return loc
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
return "%s: name - %s, description - %s, xref-path - %s" %(self.__class__.__name__, self.name, self.discription, self.path)
|
|
|
|
|
|
|
|
|
|
class Insert:
|
|
"""Class for objects representing dxf inserts."""
|
|
|
|
def __init__(self, obj):
|
|
"""Expects an entity object of type insert as input."""
|
|
if not obj.type == 'insert':
|
|
raise TypeError, "Wrong type %s for insert object!" %obj.type
|
|
self.type = obj.type
|
|
self.data = obj.data[:]
|
|
|
|
# required data
|
|
self.block = obj.get_type(2)[0]
|
|
|
|
# optional data (with defaults)
|
|
self.rotation = obj.get_type(50)
|
|
if self.rotation:
|
|
self.rotation = self.rotation[0]
|
|
else:
|
|
self.rotation = 0
|
|
|
|
self.space = obj.get_type(67)
|
|
if self.space:
|
|
self.space = self.space[0]
|
|
else:
|
|
self.space = 0
|
|
|
|
self.color_index = obj.get_type(62)
|
|
if self.color_index:
|
|
self.color_index = self.color_index[0]
|
|
else:
|
|
self.color_index = BYLAYER
|
|
|
|
discard, self.layer, discard_index = get_layer(obj.data)
|
|
del obj.data[discard_index]
|
|
self.loc = self.get_loc(obj.data)
|
|
self.scale = self.get_scale(obj.data)
|
|
self.rows, self.columns = self.get_array(obj.data)
|
|
self.extrusion = self.get_extrusion(obj.data)
|
|
|
|
|
|
|
|
|
|
|
|
def get_loc(self, data):
|
|
"""Gets the center location for circle type objects.
|
|
|
|
Circles have a single coord location.
|
|
"""
|
|
loc = [0, 0, 0]
|
|
for item in data:
|
|
if item[0] == 10: # 10 = x
|
|
loc[0] = item[1]
|
|
elif item[0] == 20: # 20 = y
|
|
loc[1] = item[1]
|
|
elif item[0] == 30: # 30 = z
|
|
loc[2] = item[1]
|
|
return loc
|
|
|
|
|
|
|
|
def get_scale(self, data):
|
|
"""Gets the x/y/z scale factor for the block.
|
|
"""
|
|
scale = [1, 1, 1]
|
|
for item in data:
|
|
if item[0] == 41: # 41 = x scale
|
|
scale[0] = item[1]
|
|
elif item[0] == 42: # 42 = y scale
|
|
scale[1] = item[1]
|
|
elif item[0] == 43: # 43 = z scale
|
|
scale[2] = item[1]
|
|
return scale
|
|
|
|
|
|
|
|
def get_array(self, data):
|
|
"""Returns the pair (row number, row spacing), (column number, column spacing)."""
|
|
columns = 1
|
|
rows = 1
|
|
cspace = 0
|
|
rspace = 0
|
|
for item in data:
|
|
if item[0] == 70: # 70 = columns
|
|
columns = item[1]
|
|
elif item[0] == 71: # 71 = rows
|
|
rows = item[1]
|
|
if item[0] == 44: # 44 = columns
|
|
cspace = item[1]
|
|
elif item[0] == 45: # 45 = rows
|
|
rspace = item[1]
|
|
return (rows, rspace), (columns, cspace)
|
|
|
|
|
|
|
|
def get_extrusion(self, data):
|
|
"""Find the axis of extrusion.
|
|
|
|
Used to get the objects Object Coordinate System (ocs).
|
|
"""
|
|
vec = [0,0,1]
|
|
for item in data:
|
|
if item[0] == 210: # 210 = x
|
|
vec[0] = item[1]
|
|
elif item[0] == 220: # 220 = y
|
|
vec[1] = item[1]
|
|
elif item[0] == 230: # 230 = z
|
|
vec[2] = item[1]
|
|
return vec
|
|
|
|
|
|
def __repr__(self):
|
|
return "%s: layer - %s, block - %s" %(self.__class__.__name__, self.layer, self.block)
|
|
|
|
|
|
|
|
|
|
class Ellipse:
|
|
"""Class for objects representing dxf ellipses."""
|
|
|
|
def __init__(self, obj):
|
|
"""Expects an entity object of type ellipse as input."""
|
|
if not obj.type == 'ellipse':
|
|
raise TypeError, "Wrong type %s for ellipse object!" %obj.type
|
|
self.type = obj.type
|
|
self.data = obj.data[:]
|
|
|
|
# required data
|
|
self.ratio = obj.get_type(40)[0]
|
|
self.start_angle = obj.get_type(41)[0]
|
|
self.end_angle = obj.get_type(42)[0]
|
|
|
|
# optional data (with defaults)
|
|
self.space = obj.get_type(67)
|
|
if self.space:
|
|
self.space = self.space[0]
|
|
else:
|
|
self.space = 0
|
|
|
|
self.color_index = obj.get_type(62)
|
|
if self.color_index:
|
|
self.color_index = self.color_index[0]
|
|
else:
|
|
self.color_index = BYLAYER
|
|
|
|
discard, self.layer, discard_index = get_layer(obj.data)
|
|
del obj.data[discard_index]
|
|
self.loc = self.get_loc(obj.data)
|
|
self.major = self.get_major(obj.data)
|
|
self.extrusion = self.get_extrusion(obj.data)
|
|
self.radius = sqrt(self.major[0]**2 + self.major[0]**2 + self.major[0]**2)
|
|
|
|
|
|
|
|
|
|
def get_loc(self, data):
|
|
"""Gets the center location for arc type objects.
|
|
|
|
Arcs have a single coord location.
|
|
"""
|
|
loc = [0, 0, 0]
|
|
for item in data:
|
|
if item[0] == 10: # 10 = x
|
|
loc[0] = item[1]
|
|
elif item[0] == 20: # 20 = y
|
|
loc[1] = item[1]
|
|
elif item[0] == 30: # 30 = z
|
|
loc[2] = item[1]
|
|
return loc
|
|
|
|
|
|
|
|
def get_major(self, data):
|
|
"""Gets the major axis for ellipse type objects.
|
|
|
|
The ellipse major axis defines the rotation of the ellipse and its radius.
|
|
"""
|
|
loc = [0, 0, 0]
|
|
for item in data:
|
|
if item[0] == 11: # 11 = x
|
|
loc[0] = item[1]
|
|
elif item[0] == 21: # 21 = y
|
|
loc[1] = item[1]
|
|
elif item[0] == 31: # 31 = z
|
|
loc[2] = item[1]
|
|
return loc
|
|
|
|
|
|
|
|
def get_extrusion(self, data):
|
|
"""Find the axis of extrusion.
|
|
|
|
Used to get the objects Object Coordinate System (ocs).
|
|
"""
|
|
vec = [0,0,1]
|
|
for item in data:
|
|
if item[0] == 210: # 210 = x
|
|
vec[0] = item[1]
|
|
elif item[0] == 220: # 220 = y
|
|
vec[1] = item[1]
|
|
elif item[0] == 230: # 230 = z
|
|
vec[2] = item[1]
|
|
return vec
|
|
|
|
|
|
def __repr__(self):
|
|
return "%s: layer - %s, radius - %s" %(self.__class__.__name__, self.layer, self.radius)
|
|
|
|
|
|
|
|
class Face:
|
|
"""Class for objects representing dxf 3d faces."""
|
|
|
|
def __init__(self, obj):
|
|
"""Expects an entity object of type 3dfaceplot as input."""
|
|
if not obj.type == '3dface':
|
|
raise TypeError, "Wrong type %s for 3dface object!" %obj.type
|
|
self.type = obj.type
|
|
self.data = obj.data[:]
|
|
|
|
# optional data (with defaults)
|
|
self.space = obj.get_type(67)
|
|
if self.space:
|
|
self.space = self.space[0]
|
|
else:
|
|
self.space = 0
|
|
|
|
self.color_index = obj.get_type(62)
|
|
if self.color_index:
|
|
self.color_index = self.color_index[0]
|
|
else:
|
|
self.color_index = BYLAYER
|
|
|
|
discard, self.layer, discard_index = get_layer(obj.data)
|
|
del obj.data[discard_index]
|
|
self.points = self.get_points(obj.data)
|
|
|
|
|
|
|
|
|
|
def get_points(self, data):
|
|
"""Gets 3-4 points for a 3d face type object.
|
|
|
|
Faces have three or optionally four verts.
|
|
"""
|
|
|
|
a = [0, 0, 0]
|
|
b = [0, 0, 0]
|
|
c = [0, 0, 0]
|
|
d = False
|
|
for item in data:
|
|
# ----------- a -------------
|
|
if item[0] == 10: # 10 = x
|
|
a[0] = item[1]
|
|
elif item[0] == 20: # 20 = y
|
|
a[1] = item[1]
|
|
elif item[0] == 30: # 30 = z
|
|
a[2] = item[1]
|
|
# ----------- b -------------
|
|
elif item[0] == 11: # 11 = x
|
|
b[0] = item[1]
|
|
elif item[0] == 21: # 21 = y
|
|
b[1] = item[1]
|
|
elif item[0] == 31: # 31 = z
|
|
b[2] = item[1]
|
|
# ----------- c -------------
|
|
elif item[0] == 12: # 12 = x
|
|
c[0] = item[1]
|
|
elif item[0] == 22: # 22 = y
|
|
c[1] = item[1]
|
|
elif item[0] == 32: # 32 = z
|
|
c[2] = item[1]
|
|
# ----------- d -------------
|
|
elif item[0] == 13: # 13 = x
|
|
d = [0, 0, 0]
|
|
d[0] = item[1]
|
|
elif item[0] == 23: # 23 = y
|
|
d[1] = item[1]
|
|
elif item[0] == 33: # 33 = z
|
|
d[2] = item[1]
|
|
out = [a,b,c]
|
|
if d:
|
|
out.append(d)
|
|
return out
|
|
|
|
|
|
def __repr__(self):
|
|
return "%s: layer - %s, points - %s" %(self.__class__.__name__, self.layer, self.points)
|
|
|
|
|
|
def get_name(data):
|
|
"""Get the name of an object from its object data.
|
|
|
|
Returns a pair of (data_item, name) where data_item is the list entry where the name was found
|
|
(the data_item can be used to remove the entry from the object data). Be sure to check
|
|
name not None before using the returned values!
|
|
"""
|
|
value = None
|
|
for i, item in enumerate(data):
|
|
if item[0] == 2:
|
|
value = item[1]
|
|
break
|
|
return item, value, i
|
|
|
|
def get_layer(data):
|
|
"""Expects object data as input.
|
|
|
|
Returns (entry, layer_name, entry_index) where entry is the data item that provided the layer name.
|
|
"""
|
|
value = None
|
|
for i, item in enumerate(data):
|
|
if item[0] == 8:
|
|
value = item[1]
|
|
break
|
|
return item, value, i
|
|
|
|
|
|
# type to object map
|
|
type_map = {
|
|
'line':Line,
|
|
'lwpolyline':LWpolyline,
|
|
'text':Text,
|
|
'mtext':Mtext,
|
|
'circle':Circle,
|
|
'arc':Arc,
|
|
'layer':Layer,
|
|
'block_record':BlockRecord,
|
|
'block':Block,
|
|
'insert':Insert,
|
|
'ellipse':Ellipse,
|
|
'3dface':Face
|
|
}
|
|
|
|
def objectify(data):
|
|
"""Expects a section type object's data as input.
|
|
|
|
Maps object data to the correct object type.
|
|
"""
|
|
objects = [] # colector for finished objects
|
|
known_types = type_map.keys() # so we don't have to call foo.keys() every iteration
|
|
index = 0
|
|
while index < len(data):
|
|
item = data[index]
|
|
if type(item) != list and item.type in known_types:
|
|
# proccess the object and append the resulting object
|
|
objects.append(type_map[item.type](item))
|
|
elif type(item) != list and item.type == 'table':
|
|
item.data = objectify(item.data) # tables have sub-objects
|
|
objects.append(item)
|
|
elif type(item) != list and item.type == 'polyline':
|
|
pline = Polyline(item)
|
|
while 1:
|
|
index += 1
|
|
item = data[index]
|
|
if item.type == 'vertex':
|
|
v = Vertex(item)
|
|
pline.points.append(v)
|
|
elif item.type == 'seqend':
|
|
break
|
|
else:
|
|
print "Error: non-vertex found before seqend!"
|
|
index -= 1
|
|
break
|
|
objects.append(pline)
|
|
else:
|
|
# we will just let the data pass un-harrased
|
|
objects.append(item)
|
|
index += 1
|
|
return objects
|
|
if __name__ == "__main__":
|
|
print "No example yet!" |