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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/bpymodules/dxfColorMap.py282
-rw-r--r--release/scripts/bpymodules/dxfImportObjects.py1326
-rw-r--r--release/scripts/bpymodules/dxfReader.py323
-rw-r--r--release/scripts/import_dxf.py1005
4 files changed, 2936 insertions, 0 deletions
diff --git a/release/scripts/bpymodules/dxfColorMap.py b/release/scripts/bpymodules/dxfColorMap.py
new file mode 100644
index 00000000000..66c0bd4e9a2
--- /dev/null
+++ b/release/scripts/bpymodules/dxfColorMap.py
@@ -0,0 +1,282 @@
+# dictionary mapping AutoCAD color indexes with Blender colors
+
+# --------------------------------------------------------------------------
+# color_map.py Final 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 *****
+# --------------------------------------------------------------------------
+
+color_map = {
+ 0:[0.0, 0.0, 0.0],
+ 1:[0.99609375, 0.0, 0.0],
+ 2:[0.99609375, 0.99609375, 0.0],
+ 3:[0.0, 0.99609375, 0.0],
+ 4:[0.0, 0.99609375, 0.99609375],
+ 5:[0.0, 0.0, 0.99609375],
+ 6:[0.99609375, 0.0, 0.99609375],
+ 7:[0.99609375, 0.99609375, 0.99609375],
+ 8:[0.25390625, 0.25390625, 0.25390625],
+ 9:[0.5, 0.5, 0.5],
+ 10:[0.99609375, 0.0, 0.0],
+ 11:[0.99609375, 0.6640625, 0.6640625],
+ 12:[0.73828125, 0.0, 0.0],
+ 13:[0.73828125, 0.4921875, 0.4921875],
+ 14:[0.50390625, 0.0, 0.0],
+ 15:[0.50390625, 0.3359375, 0.3359375],
+ 16:[0.40625, 0.0, 0.0],
+ 17:[0.40625, 0.26953125, 0.26953125],
+ 18:[0.30859375, 0.0, 0.0],
+ 19:[0.30859375, 0.20703125, 0.20703125],
+ 20:[0.99609375, 0.24609375, 0.0],
+ 21:[0.99609375, 0.74609375, 0.6640625],
+ 22:[0.73828125, 0.1796875, 0.0],
+ 23:[0.73828125, 0.55078125, 0.4921875],
+ 24:[0.50390625, 0.12109375, 0.0],
+ 25:[0.50390625, 0.375, 0.3359375],
+ 26:[0.40625, 0.09765625, 0.0],
+ 27:[0.40625, 0.3046875, 0.26953125],
+ 28:[0.30859375, 0.07421875, 0.0],
+ 29:[0.30859375, 0.23046875, 0.20703125],
+ 30:[0.99609375, 0.49609375, 0.0],
+ 31:[0.99609375, 0.828125, 0.6640625],
+ 32:[0.73828125, 0.3671875, 0.0],
+ 33:[0.73828125, 0.61328125, 0.4921875],
+ 34:[0.50390625, 0.25, 0.0],
+ 35:[0.50390625, 0.41796875, 0.3359375],
+ 36:[0.40625, 0.203125, 0.0],
+ 37:[0.40625, 0.3359375, 0.26953125],
+ 38:[0.30859375, 0.15234375, 0.0],
+ 39:[0.30859375, 0.2578125, 0.20703125],
+ 40:[0.99609375, 0.74609375, 0.0],
+ 41:[0.99609375, 0.9140625, 0.6640625],
+ 42:[0.73828125, 0.55078125, 0.0],
+ 43:[0.73828125, 0.67578125, 0.4921875],
+ 44:[0.50390625, 0.375, 0.0],
+ 45:[0.50390625, 0.4609375, 0.3359375],
+ 46:[0.40625, 0.3046875, 0.0],
+ 47:[0.40625, 0.37109375, 0.26953125],
+ 48:[0.30859375, 0.23046875, 0.0],
+ 49:[0.30859375, 0.28515625, 0.20703125],
+ 50:[0.99609375, 0.99609375, 0.0],
+ 51:[0.99609375, 0.99609375, 0.6640625],
+ 52:[0.73828125, 0.73828125, 0.0],
+ 53:[0.73828125, 0.73828125, 0.4921875],
+ 54:[0.50390625, 0.50390625, 0.0],
+ 55:[0.50390625, 0.50390625, 0.3359375],
+ 56:[0.40625, 0.40625, 0.0],
+ 57:[0.40625, 0.40625, 0.26953125],
+ 58:[0.30859375, 0.30859375, 0.0],
+ 59:[0.30859375, 0.30859375, 0.20703125],
+ 60:[0.74609375, 0.99609375, 0.0],
+ 61:[0.9140625, 0.99609375, 0.6640625],
+ 62:[0.55078125, 0.73828125, 0.0],
+ 63:[0.67578125, 0.73828125, 0.4921875],
+ 64:[0.375, 0.50390625, 0.0],
+ 65:[0.4609375, 0.50390625, 0.3359375],
+ 66:[0.3046875, 0.40625, 0.0],
+ 67:[0.37109375, 0.40625, 0.26953125],
+ 68:[0.23046875, 0.30859375, 0.0],
+ 69:[0.28515625, 0.30859375, 0.20703125],
+ 70:[0.49609375, 0.99609375, 0.0],
+ 71:[0.828125, 0.99609375, 0.6640625],
+ 72:[0.3671875, 0.73828125, 0.0],
+ 73:[0.61328125, 0.73828125, 0.4921875],
+ 74:[0.25, 0.50390625, 0.0],
+ 75:[0.41796875, 0.50390625, 0.3359375],
+ 76:[0.203125, 0.40625, 0.0],
+ 77:[0.3359375, 0.40625, 0.26953125],
+ 78:[0.15234375, 0.30859375, 0.0],
+ 79:[0.2578125, 0.30859375, 0.20703125],
+ 80:[0.24609375, 0.99609375, 0.0],
+ 81:[0.74609375, 0.99609375, 0.6640625],
+ 82:[0.1796875, 0.73828125, 0.0],
+ 83:[0.55078125, 0.73828125, 0.4921875],
+ 84:[0.12109375, 0.50390625, 0.0],
+ 85:[0.375, 0.50390625, 0.3359375],
+ 86:[0.09765625, 0.40625, 0.0],
+ 87:[0.3046875, 0.40625, 0.26953125],
+ 88:[0.07421875, 0.30859375, 0.0],
+ 89:[0.23046875, 0.30859375, 0.20703125],
+ 90:[0.0, 0.99609375, 0.0],
+ 91:[0.6640625, 0.99609375, 0.6640625],
+ 92:[0.0, 0.73828125, 0.0],
+ 93:[0.4921875, 0.73828125, 0.4921875],
+ 94:[0.0, 0.50390625, 0.0],
+ 95:[0.3359375, 0.50390625, 0.3359375],
+ 96:[0.0, 0.40625, 0.0],
+ 97:[0.26953125, 0.40625, 0.26953125],
+ 98:[0.0, 0.30859375, 0.0],
+ 99:[0.20703125, 0.30859375, 0.20703125],
+ 100:[0.0, 0.99609375, 0.24609375],
+ 101:[0.6640625, 0.99609375, 0.74609375],
+ 102:[0.0, 0.73828125, 0.1796875],
+ 103:[0.4921875, 0.73828125, 0.55078125],
+ 104:[0.0, 0.50390625, 0.12109375],
+ 105:[0.3359375, 0.50390625, 0.375],
+ 106:[0.0, 0.40625, 0.09765625],
+ 107:[0.26953125, 0.40625, 0.3046875],
+ 108:[0.0, 0.30859375, 0.07421875],
+ 109:[0.20703125, 0.30859375, 0.23046875],
+ 110:[0.0, 0.99609375, 0.49609375],
+ 111:[0.6640625, 0.99609375, 0.828125],
+ 112:[0.0, 0.73828125, 0.3671875],
+ 113:[0.4921875, 0.73828125, 0.61328125],
+ 114:[0.0, 0.50390625, 0.25],
+ 115:[0.3359375, 0.50390625, 0.41796875],
+ 116:[0.0, 0.40625, 0.203125],
+ 117:[0.26953125, 0.40625, 0.3359375],
+ 118:[0.0, 0.30859375, 0.15234375],
+ 119:[0.20703125, 0.30859375, 0.2578125],
+ 120:[0.0, 0.99609375, 0.74609375],
+ 121:[0.6640625, 0.99609375, 0.9140625],
+ 122:[0.0, 0.73828125, 0.55078125],
+ 123:[0.4921875, 0.73828125, 0.67578125],
+ 124:[0.0, 0.50390625, 0.375],
+ 125:[0.3359375, 0.50390625, 0.4609375],
+ 126:[0.0, 0.40625, 0.3046875],
+ 127:[0.26953125, 0.40625, 0.37109375],
+ 128:[0.0, 0.30859375, 0.23046875],
+ 129:[0.20703125, 0.30859375, 0.28515625],
+ 130:[0.0, 0.99609375, 0.99609375],
+ 131:[0.6640625, 0.99609375, 0.99609375],
+ 132:[0.0, 0.73828125, 0.73828125],
+ 133:[0.4921875, 0.73828125, 0.73828125],
+ 134:[0.0, 0.50390625, 0.50390625],
+ 135:[0.3359375, 0.50390625, 0.50390625],
+ 136:[0.0, 0.40625, 0.40625],
+ 137:[0.26953125, 0.40625, 0.40625],
+ 138:[0.0, 0.30859375, 0.30859375],
+ 139:[0.20703125, 0.30859375, 0.30859375],
+ 140:[0.0, 0.74609375, 0.99609375],
+ 141:[0.6640625, 0.9140625, 0.99609375],
+ 142:[0.0, 0.55078125, 0.73828125],
+ 143:[0.4921875, 0.67578125, 0.73828125],
+ 144:[0.0, 0.375, 0.50390625],
+ 145:[0.3359375, 0.4609375, 0.50390625],
+ 146:[0.0, 0.3046875, 0.40625],
+ 147:[0.26953125, 0.37109375, 0.40625],
+ 148:[0.0, 0.23046875, 0.30859375],
+ 149:[0.20703125, 0.28515625, 0.30859375],
+ 150:[0.0, 0.49609375, 0.99609375],
+ 151:[0.6640625, 0.828125, 0.99609375],
+ 152:[0.0, 0.3671875, 0.73828125],
+ 153:[0.4921875, 0.61328125, 0.73828125],
+ 154:[0.0, 0.25, 0.50390625],
+ 155:[0.3359375, 0.41796875, 0.50390625],
+ 156:[0.0, 0.203125, 0.40625],
+ 157:[0.26953125, 0.3359375, 0.40625],
+ 158:[0.0, 0.15234375, 0.30859375],
+ 159:[0.20703125, 0.2578125, 0.30859375],
+ 160:[0.0, 0.24609375, 0.99609375],
+ 161:[0.6640625, 0.74609375, 0.99609375],
+ 162:[0.0, 0.1796875, 0.73828125],
+ 163:[0.4921875, 0.55078125, 0.73828125],
+ 164:[0.0, 0.12109375, 0.50390625],
+ 165:[0.3359375, 0.375, 0.50390625],
+ 166:[0.0, 0.09765625, 0.40625],
+ 167:[0.26953125, 0.3046875, 0.40625],
+ 168:[0.0, 0.07421875, 0.30859375],
+ 169:[0.20703125, 0.23046875, 0.30859375],
+ 170:[0.0, 0.0, 0.99609375],
+ 171:[0.6640625, 0.6640625, 0.99609375],
+ 172:[0.0, 0.0, 0.73828125],
+ 173:[0.4921875, 0.4921875, 0.73828125],
+ 174:[0.0, 0.0, 0.50390625],
+ 175:[0.3359375, 0.3359375, 0.50390625],
+ 176:[0.0, 0.0, 0.40625],
+ 177:[0.26953125, 0.26953125, 0.40625],
+ 178:[0.0, 0.0, 0.30859375],
+ 179:[0.20703125, 0.20703125, 0.30859375],
+ 180:[0.24609375, 0.0, 0.99609375],
+ 181:[0.74609375, 0.6640625, 0.99609375],
+ 182:[0.1796875, 0.0, 0.73828125],
+ 183:[0.55078125, 0.4921875, 0.73828125],
+ 184:[0.12109375, 0.0, 0.50390625],
+ 185:[0.375, 0.3359375, 0.50390625],
+ 186:[0.09765625, 0.0, 0.40625],
+ 187:[0.3046875, 0.26953125, 0.40625],
+ 188:[0.07421875, 0.0, 0.30859375],
+ 189:[0.23046875, 0.20703125, 0.30859375],
+ 190:[0.49609375, 0.0, 0.99609375],
+ 191:[0.828125, 0.6640625, 0.99609375],
+ 192:[0.3671875, 0.0, 0.73828125],
+ 193:[0.61328125, 0.4921875, 0.73828125],
+ 194:[0.25, 0.0, 0.50390625],
+ 195:[0.41796875, 0.3359375, 0.50390625],
+ 196:[0.203125, 0.0, 0.40625],
+ 197:[0.3359375, 0.26953125, 0.40625],
+ 198:[0.15234375, 0.0, 0.30859375],
+ 199:[0.2578125, 0.20703125, 0.30859375],
+ 200:[0.74609375, 0.0, 0.99609375],
+ 201:[0.9140625, 0.6640625, 0.99609375],
+ 202:[0.55078125, 0.0, 0.73828125],
+ 203:[0.67578125, 0.4921875, 0.73828125],
+ 204:[0.375, 0.0, 0.50390625],
+ 205:[0.4609375, 0.3359375, 0.50390625],
+ 206:[0.3046875, 0.0, 0.40625],
+ 207:[0.37109375, 0.26953125, 0.40625],
+ 208:[0.23046875, 0.0, 0.30859375],
+ 209:[0.28515625, 0.20703125, 0.30859375],
+ 210:[0.99609375, 0.0, 0.99609375],
+ 211:[0.99609375, 0.6640625, 0.99609375],
+ 212:[0.73828125, 0.0, 0.73828125],
+ 213:[0.73828125, 0.4921875, 0.73828125],
+ 214:[0.50390625, 0.0, 0.50390625],
+ 215:[0.50390625, 0.3359375, 0.50390625],
+ 216:[0.40625, 0.0, 0.40625],
+ 217:[0.40625, 0.26953125, 0.40625],
+ 218:[0.30859375, 0.0, 0.30859375],
+ 219:[0.30859375, 0.20703125, 0.30859375],
+ 220:[0.99609375, 0.0, 0.74609375],
+ 221:[0.99609375, 0.6640625, 0.9140625],
+ 222:[0.73828125, 0.0, 0.55078125],
+ 223:[0.73828125, 0.4921875, 0.67578125],
+ 224:[0.50390625, 0.0, 0.375],
+ 225:[0.50390625, 0.3359375, 0.4609375],
+ 226:[0.40625, 0.0, 0.3046875],
+ 227:[0.40625, 0.26953125, 0.37109375],
+ 228:[0.30859375, 0.0, 0.23046875],
+ 229:[0.30859375, 0.20703125, 0.28515625],
+ 230:[0.99609375, 0.0, 0.49609375],
+ 231:[0.99609375, 0.6640625, 0.828125],
+ 232:[0.73828125, 0.0, 0.3671875],
+ 233:[0.73828125, 0.4921875, 0.61328125],
+ 234:[0.50390625, 0.0, 0.25],
+ 235:[0.50390625, 0.3359375, 0.41796875],
+ 236:[0.40625, 0.0, 0.203125],
+ 237:[0.40625, 0.26953125, 0.3359375],
+ 238:[0.30859375, 0.0, 0.15234375],
+ 239:[0.30859375, 0.20703125, 0.2578125],
+ 240:[0.99609375, 0.0, 0.24609375],
+ 241:[0.99609375, 0.6640625, 0.74609375],
+ 242:[0.73828125, 0.0, 0.1796875],
+ 243:[0.73828125, 0.4921875, 0.55078125],
+ 244:[0.50390625, 0.0, 0.12109375],
+ 245:[0.50390625, 0.3359375, 0.375],
+ 246:[0.40625, 0.0, 0.09765625],
+ 247:[0.40625, 0.26953125, 0.3046875],
+ 248:[0.30859375, 0.0, 0.07421875],
+ 249:[0.30859375, 0.20703125, 0.23046875],
+ 250:[0.19921875, 0.19921875, 0.19921875],
+ 251:[0.3125, 0.3125, 0.3125],
+ 252:[0.41015625, 0.41015625, 0.41015625],
+ 253:[0.5078125, 0.5078125, 0.5078125],
+ 254:[0.7421875, 0.7421875, 0.7421875],
+ 255:[0.99609375, 0.99609375, 0.99609375],
+}
diff --git a/release/scripts/bpymodules/dxfImportObjects.py b/release/scripts/bpymodules/dxfImportObjects.py
new file mode 100644
index 00000000000..b78e91e8428
--- /dev/null
+++ b/release/scripts/bpymodules/dxfImportObjects.py
@@ -0,0 +1,1326 @@
+"""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 = get_layer(obj.data)
+ obj.data.remove(discard)
+ 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 = get_layer(obj.data)
+ obj.data.remove(discard)
+ 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 = get_layer(obj.data)
+ obj.data.remove(discard)
+ 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 = get_layer(obj.data)
+ obj.data.remove(discard)
+ 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 = get_layer(obj.data)
+ obj.data.remove(discard)
+ 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 = get_layer(obj.data)
+ obj.data.remove(discard)
+ 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 = get_layer(obj.data)
+ obj.data.remove(discard)
+ 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 = get_layer(obj.data)
+ obj.data.remove(discard)
+ 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 = get_layer(obj.data)
+ obj.data.remove(discard)
+ 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 = get_layer(obj.data)
+ obj.data.remove(discard)
+ 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 = get_layer(obj.data)
+ obj.data.remove(discard)
+ 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 item in data:
+ if item[0] == 2:
+ value = item[1]
+ break
+ return item, value
+
+def get_layer(data):
+ """Expects object data as input.
+
+ Returns (entry, layer_name) where entry is the data item that provided the layer name.
+ """
+ value = None
+ for item in data:
+ if item[0] == 8:
+ value = item[1]
+ break
+ return item, value
+
+
+# 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!" \ No newline at end of file
diff --git a/release/scripts/bpymodules/dxfReader.py b/release/scripts/bpymodules/dxfReader.py
new file mode 100644
index 00000000000..8d122173b82
--- /dev/null
+++ b/release/scripts/bpymodules/dxfReader.py
@@ -0,0 +1,323 @@
+"""This module provides a function for reading dxf files and parsing them into a useful tree of objects and data.
+
+ The convert function is called by the readDXF fuction to convert dxf strings into the correct data based
+ on their type code. readDXF expects a (full path) file name as input.
+"""
+
+# --------------------------------------------------------------------------
+# DXF Reader 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 dxfImportObjects import *
+
+class InitializationError(Exception): pass
+
+class StateMachine:
+ """(finite) State Machine from the great David Mertz's great Charming Python article."""
+
+ def __init__(self):
+ self.handlers = []
+ self.startState = None
+ self.endStates = []
+
+ def add_state(self, handler, end_state=0):
+ """All states and handlers are functions which return
+ a state and a cargo."""
+ self.handlers.append(handler)
+ if end_state:
+ self.endStates.append(handler)
+ def set_start(self, handler):
+ """Sets the starting handler function."""
+ self.startState = handler
+
+
+ def run(self, cargo=None):
+ if not self.startState:
+ raise InitializationError,\
+ "must call .set_start() before .run()"
+ if not self.endStates:
+ raise InitializationError, \
+ "at least one state must be an end_state"
+ handler = self.startState
+ while 1:
+ (newState, cargo) = handler(cargo)
+ #print cargo
+ if newState in self.endStates:
+ return newState(cargo)
+ #break
+ elif newState not in self.handlers:
+ raise RuntimeError, "Invalid target %s" % newState
+ else:
+ handler = newState
+
+def convert(code, value):
+ """Convert a string to the correct Python type based on its dxf code.
+ code types:
+ ints = 60-79, 170-179, 270-289, 370-389, 400-409, 1060-1070
+ longs = 90-99, 420-429, 440-459, 1071
+ floats = 10-39, 40-59, 110-139, 140-149, 210-239, 460-469, 1010-1059
+ hex = 105, 310-379, 390-399
+ strings = 0-9, 100, 102, 300-309, 410-419, 430-439, 470-479, 999, 1000-1009
+ """
+ if 59 < code < 80 or 169 < code < 180 or 269 < code < 290 or 369 < code < 390 or 399 < code < 410 or 1059 < code < 1071:
+ value = int(value)
+ elif 89 < code < 100 or 419 < code < 430 or 439 < code < 460 or code == 1071:
+ value = long(value)
+ elif 9 < code < 60 or 109 < code < 150 or 209 < code < 240 or 459 < code < 470 or 1009 < code < 1060:
+ value = float(value)
+ elif code == 105 or 309 < code < 380 or 389 < code < 400:
+ value = int(value, 16) # should be left as string?
+ else: # it's already a string so do nothing
+ pass
+ return value
+
+
+def findObject(infile, kind=''):
+ """Finds the next occurance of an object."""
+ obj = False
+ while 1:
+ line = infile.readline()
+ if not line: # readline returns '' at eof
+ return False
+ if not obj: # We're still looking for our object code
+ if line.lower().strip() == '0':
+ obj = True # found it
+ else: # we are in an object definition
+ if kind: # if we're looking for a particular kind
+ if line.lower().strip() == kind:
+ obj = Object(line.lower().strip())
+ break
+ else: # otherwise take anything non-numeric
+ if line.lower().strip() not in string.digits:
+ obj = Object(line.lower().strip())
+ break
+ obj = False # whether we found one or not it's time to start over
+ return obj
+
+def handleObject(infile):
+ """Add data to an object until end of object is found."""
+ line = infile.readline()
+ if line.lower().strip() == 'section':
+ return 'section' # this would be a problem
+ elif line.lower().strip() == 'endsec':
+ return 'endsec' # this means we are done with a section
+ else: # add data to the object until we find a new object
+ obj = Object(line.lower().strip())
+ obj.name = obj.type
+ done = False
+ data = []
+ while not done:
+ line = infile.readline()
+ if not data:
+ if line.lower().strip() == '0':
+ #we've found an object, time to return
+ return obj
+ else:
+ # first part is always an int
+ data.append(int(line.lower().strip()))
+ else:
+ data.append(convert(data[0], line.strip()))
+ obj.data.append(data)
+ data = []
+
+def handleTable(table, infile):
+ """Special handler for dealing with nested table objects."""
+ item, name = get_name(table.data)
+ if name: # We should always find a name
+ table.data.remove(item)
+ table.name = name.lower()
+ # This next bit is from handleObject
+ # handleObject should be generalized to work with any section like object
+ while 1:
+ obj = handleObject(infile)
+ if obj.type == 'table':
+ print "Warning: previous table not closed!"
+ return table
+ elif obj.type == 'endtab':
+ return table # this means we are done with the table
+ else: # add objects to the table until one of the above is found
+ table.data.append(obj)
+
+
+
+
+def handleBlock(block, infile):
+ """Special handler for dealing with nested table objects."""
+ item, name = get_name(block.data)
+ if name: # We should always find a name
+ block.data.remove(item)
+ block.name = name.lower()
+ # This next bit is from handleObject
+ # handleObject should be generalized to work with any section like object
+ while 1:
+ obj = handleObject(infile)
+ if obj.type == 'block':
+ print "Warning: previous block not closed!"
+ return block
+ elif obj.type == 'endblk':
+ return block # this means we are done with the table
+ else: # add objects to the table until one of the above is found
+ block.data.append(obj)
+
+
+
+
+"""These are the states/functions used in the State Machine.
+states:
+ start - find first section
+ start_section - add data, find first object
+ object - add obj-data, watch for next obj (called directly by start_section)
+ end_section - look for next section or eof
+ end - return results
+"""
+
+def start(cargo):
+ """Expects the infile as cargo, initializes the cargo."""
+ #print "Entering start state!"
+ infile = cargo
+ drawing = Object('drawing')
+ section = findObject(infile, 'section')
+ if section:
+ return start_section, (infile, drawing, section)
+ else:
+ return error, (infile, "Failed to find any sections!")
+
+def start_section(cargo):
+ """Expects [infile, drawing, section] as cargo, builds a nested section object."""
+ #print "Entering start_section state!"
+ infile = cargo[0]
+ drawing = cargo[1]
+ section = cargo[2]
+ # read each line, if it is an object declaration go to object mode
+ # otherwise create a [index, data] pair and add it to the sections data.
+ done = False
+ data = []
+ while not done:
+ line = infile.readline()
+
+ if not data: # if we haven't found a dxf code yet
+ if line.lower().strip() == '0':
+ # we've found an object
+ while 1: # no way out unless we find an end section or a new section
+ obj = handleObject(infile)
+ if obj == 'section': # shouldn't happen
+ print "Warning: failed to close previous section!"
+ return end_section, (infile, drawing)
+ elif obj == 'endsec': # This section is over, look for the next
+ drawing.data.append(section)
+ return end_section, (infile, drawing)
+ elif obj.type == 'table': # tables are collections of data
+ obj = handleTable(obj, infile) # we need to find all there contents
+ section.data.append(obj) # before moving on
+ elif obj.type == 'block': # the same is true of blocks
+ obj = handleBlock(obj, infile) # we need to find all there contents
+ section.data.append(obj) # before moving on
+ else: # found another sub-object
+ section.data.append(obj)
+ else:
+ data.append(int(line.lower().strip()))
+ else: # we have our code, now we just need to convert the data and add it to our list.
+ data.append(convert(data[0], line.strip()))
+ section.data.append(data)
+ data = []
+def end_section(cargo):
+ """Expects (infile, drawing) as cargo, searches for next section."""
+ #print "Entering end_section state!"
+ infile = cargo[0]
+ drawing = cargo[1]
+ section = findObject(infile, 'section')
+ if section:
+ return start_section, (infile, drawing, section)
+ else:
+ return end, (infile, drawing)
+
+def end(cargo):
+ """Expects (infile, drawing) as cargo, called when eof has been reached."""
+ #print "Entering end state!"
+ infile = cargo[0]
+ drawing = cargo[1]
+ #infile.close()
+ return drawing
+
+def error(cargo):
+ """Expects a (infile, string) as cargo, called when there is an error during processing."""
+ #print "Entering error state!"
+ infile = cargo[0]
+ err = cargo[1]
+ infile.close()
+ print "There has been an error:"
+ print err
+ return False
+
+def readDXF(filename):
+ """Given a file name try to read it as a dxf file.
+
+ Output is an object with the following structure
+ drawing
+ header
+ header data
+ classes
+ class data
+ tables
+ table data
+ blocks
+ block data
+ entities
+ entity data
+ objects
+ object data
+ where foo data is a list of sub-objects. True object data
+ is of the form [code, data].
+"""
+ infile = open(filename)
+
+ sm = StateMachine()
+ sm.add_state(error, True)
+ sm.add_state(end, True)
+ sm.add_state(start_section)
+ sm.add_state(end_section)
+ sm.add_state(start)
+ sm.set_start(start)
+ try:
+ drawing = sm.run(infile)
+ if drawing:
+ drawing.name = filename
+ for obj in drawing.data:
+ item, name = get_name(obj.data)
+ if name:
+ obj.data.remove(item)
+ obj.name = name.lower()
+ setattr(drawing, name.lower(), obj)
+ # Call the objectify function from dxfImportObjects to cast
+ # raw objects into the right types of object
+ obj.data = objectify(obj.data)
+ #print obj.name
+ finally:
+ infile.close()
+ return drawing
+if __name__ == "__main__":
+ filename = r".\examples\block-test.dxf"
+ drawing = readDXF(filename)
+ for item in drawing.entities.data:
+ print item
+
+
diff --git a/release/scripts/import_dxf.py b/release/scripts/import_dxf.py
new file mode 100644
index 00000000000..15bee5b3905
--- /dev/null
+++ b/release/scripts/import_dxf.py
@@ -0,0 +1,1005 @@
+#!BPY
+
+# """
+# Name: 'Drawing eXchange Format (.dxf)'
+# Blender: 243
+# Group: 'Import'
+# Tooltip: 'Import DXF file.'
+# """
+__author__ = 'Kitsu (Ed Blake)'
+__version__ = '0.8 1/2007'
+__url__ = ["elysiun.com", "BlenderArtists.org"]
+__email__ = ["Kitsune_e@yahoo.com"]
+__bpydoc__ = """\
+This is a Blender import script for dxf files.
+
+This script imports the dxf Geometery from dxf versions 2007 and earlier.
+
+Supported:<br>
+ At this time only mesh based imports are supported.<br>
+ Future support for all curve import is planned.<br>
+ <br>
+Currently Supported DXF Ojects:<br>
+ Lines<br>
+ LightWeight polylines<br>
+ True polylines<br>
+ Text<br>
+ Mtext<br>
+ Circles<br>
+ Arcs<br>
+ Ellipses<br>
+ Blocks<br>
+ 3Dfaces<br>
+
+Known issues:<br>
+ Does not convert perfectly between Object Coordinate System (OCS)
+ and World Coordinate System (WCS). Only rudimentary support for
+ true polylines have been implimented - splines/fitted curves/
+ 3d plines/polymeshes are not supported.
+ No support for most 3d entities. Doesn't support the new style object
+ visability. There are problems importing some curves/arcs/circles.
+
+Notes:<br>
+ This is primarally a 2d drawing release. Currently only support for
+ 3d faces has been added.
+ Blocks are created on layer 19 then referenced at each insert point. The
+ insert point is designated with a small 3d crosshair. This handle does not render.
+
+"""
+
+# --------------------------------------------------------------------------
+# DXF Import 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 *****
+# --------------------------------------------------------------------------
+
+import Blender
+from Blender import *
+Sys = sys
+try:
+ from dxfReader import readDXF
+except ImportError:
+ import sys
+ curdir = Sys.dirname(Blender.Get('filename'))
+ sys.path.append(curdir)
+from dxfReader import readDXF
+from dxfColorMap import color_map
+from math import *
+
+try:
+ import os
+ if os.name:# != 'mac':
+ import psyco
+ psyco.log()
+ psyco.full(memory=100)
+ psyco.profile(0.05, memory=100)
+ psyco.profile(0.2)
+except ImportError:
+ pass
+
+SCENE = Scene.GetCurrent()
+WORLDX = Mathutils.Vector((1,0,0))
+AUTO = BezTriple.HandleTypes.AUTO
+BYLAYER=256
+
+class Layer:
+ """Dummy layer object."""
+ def __init__(self, name, color, frozen):
+ self.name = name
+ self.color = color
+ self.frozen = frozen
+
+
+class MatColors:
+ """A smart container for color based materials.
+
+ This class is a wrapper around a dictionary mapping color indicies to materials.
+ When called with a color index it returns a material corrisponding to that index.
+ Behind the scenes it checks if that index is in its keys, and if not it creates
+ a new material. It then adds the new index:material pair to its dict and returns
+ the material.
+ """
+
+ def __init__(self, map):
+ """Expects a dictionary mapping layer names to color idices."""
+ self.map = map
+ self.colors = {}
+
+
+ def __call__(self, color=None):
+ """Return the material associated with color.
+
+ If a layer name is provided the color of that layer is used.
+ """
+ if not color:
+ color = 0
+ if type(color) == str: # Layer name
+ try:
+ color = self.map[color].color # color = layer_map[name].color
+ except KeyError:
+ layer = Layer(name=color, color=0, frozen=False)
+ self.map[color] = layer
+ color = 0
+ color = abs(color)
+ if color not in self.colors.keys():
+ self.add(color)
+ return self.colors[color]
+
+
+
+
+ def add(self, color):
+ """Create a new material using the provided color index."""
+ global color_map
+ mat = Material.New('ColorIndex-%s' %color)
+ mat.setRGBCol(color_map[color])
+ mat.setMode("Shadeless", "Wire")
+ self.colors[color] = mat
+
+
+
+
+class Blocks:
+ """A smart container for blocks.
+
+ This class is a wrapper around a dictionary mapping block names to Blender data blocks.
+ When called with a name string it returns a block corrisponding to that name.
+ Behind the scenes it checks if that name is in its keys, and if not it creates
+ a new data block. It then adds the new name:block pair to its dict and returns
+ the block.
+ """
+
+ def __init__(self, map, settings):
+ """Expects a dictionary mapping block names to block objects."""
+ self.map = map
+ self.settings = settings
+ self.blocks = {}
+
+
+ def __call__(self, name=None):
+ """Return the data block associated with name.
+
+ If no name is provided return self.blocks.
+ """
+ if not name:
+ return self.blocks
+ if name not in self.blocks.keys():
+ self.add(name)
+ return self.blocks[name]
+
+
+
+ def add(self, name):
+ """Create a new block group for the block with name."""
+ optimization = self.settings.optimization
+ group = Group.New(name)
+ block = self.map[name]
+ if optimization <= 1:
+ print "\nDrawing %s block entities..." %name
+ drawEntities(block.entities, self.settings, group)
+ if optimization <= 1:
+ print "Done!"
+ self.blocks[name] = group
+
+
+
+
+
+class Settings:
+ """A container for all the import settings and objects used by the draw functions.
+
+ This is like a collection of globally accessable persistant properties and functions.
+ """
+ # Optimization constants
+ MIN = 0
+ MID = 1
+ MAX = 2
+
+ def __init__(self, drawing, curves):
+ """Given the drawing initialize all the important settings used by the draw functions."""
+ self.curves = curves
+ self.layers = True
+ self.blocks = True
+ self.optimization = self.getOpt()
+
+ # First sort out all the sections
+ sections = dict([(item.name, item) for item in drawing.data])
+
+ # The header section may be omited
+ if self.optimization <= self.MID:
+ if 'header' in sections.keys():
+ print "Found header!"
+ else:
+ print "File contains no header!"
+
+ # The tables section may be partialy or completely missing.
+ if 'tables' in sections.keys():
+ if self.optimization <= self.MID:
+ print "Found tables!"
+ tables = dict([(item.name, item) for item in sections["tables"].data])
+ if 'layer' in tables.keys():
+ if self.optimization <= self.MID:
+ print "Found layers!"
+ # Read the layers table and get the layer colors
+ self.colors = getLayers(drawing)
+ else:
+ if self.optimization <= self.MID:
+ print "File contains no layers table!"
+ self.layers = False
+ self.colors = MatColors({})
+ else:
+ if self.optimization <= self.MID:
+ print "File contains no tables!"
+ print "File contains no layers table!"
+ self.layers = False
+ self.colors = MatColors({})
+
+ # The blocks section may be omited
+ if 'blocks' in sections.keys():
+ if self.optimization <= self.MID:
+ print "Found blocks!"
+ # Read the block definitions and build our block object
+ self.blocks = getBlocks(drawing, self)
+ else:
+ if self.optimization <= self.MID:
+ print "File contains no blocks!"
+ self.blocks = False
+
+
+ def getOpt(self):
+ """Ask the user for update optimization level."""
+ Window.WaitCursor(False)
+
+ retval = Draw.PupIntInput('optimization: ', 1, 0, 2)
+ print "Setting optimization level %s!" %retval
+
+ Window.WaitCursor(True)
+ return retval
+
+
+ def isOff(self, name):
+ """Given a layer name look up the layer object and return its visable status."""
+ # colors are negative if layer is off
+ try:
+ layer = self.colors.map[name]
+ except KeyError:
+ return False
+
+ if layer.frozen or layer.color < 0:
+ return True
+ else:
+ return False
+
+
+
+
+class Drawer:
+ """Super 'function' for all the entitiy drawing functions.
+
+ The code for the drawing functions was very repetitive, each differing
+ by only a few lines at most. So here is a callable class with methods
+ for each part of the import proccess.
+ """
+
+ def __init__(self, block=False):
+ self.block = block
+
+
+
+ def __call__(self, entities, settings, group=None):
+ """Call with a list of entities and a settings object to generate Blender geometry."""
+ if entities and settings.optimization <= settings.MID:
+ print "Drawing %ss..." %entities[0].type,
+
+ if self.block:
+ # create one 'handle' data block to use with all blocks
+ handle = Mesh.New('insert')
+ handle.verts.extend(
+ [(-0.01,0,0),
+ (0.01,0,0),
+ (0,-0.01,0),
+ (0,0.01,0),
+ (0,0,-0.01),
+ (0,0,0.01)]
+ )
+ handle.edges.extend([(0,1),(2,3),(4,5)])
+
+ # For now we only want model-space objects
+ entities = [entity for entity in entities if entity.space == 0]
+
+ if group:
+ block_def = True
+ else:
+ block_def = False
+
+ for entity in entities:
+ if settings.optimization <= settings.MID:
+ print '\b.',
+ # First get the layer group
+ if not block_def:
+ group = self.getGroup('layer %s' %entity.layer) # add overhead just to make things a little cleaner
+
+ if not self.block:
+ ob = self.draw(entity, settings.curves)
+ else:
+ ob = self.draw(entity, handle, settings)
+
+ self.setColor(entity, ob, settings)
+ # Link it to the scene and add it to the correct group
+ SCENE.link(ob)
+ self.setGroup(group, ob)
+
+ # Set the visability
+ if settings.isOff(entity.layer):
+ ob.layers = [20]
+ elif block_def:
+ ob.layers = [19]
+ else:
+ ob.layers = [i+1 for i in range(20)]
+
+ # # Set the visability
+ # if settings.isOff(entity.layer) or block_def:
+ # ob.restrictDisplay = True
+ # ob.restrictRender = True
+
+ if settings.optimization == settings.MIN:
+ # I know it's slow to have Blender redraw after each entity type is drawn
+ # But is it really slower than the progress bar?
+ Blender.Redraw()
+ if entities and settings.optimization <= settings.MID:
+ print "\nFinished drawing %ss!" %entities[0].type
+ def getGroup(self, name):
+ """Returns a Blender group object."""
+ try:
+ group = Group.Get(name)
+ except: # What is the exception?
+ group = Group.New(name)
+ return group
+ def draw(self, entity):
+ """Dummy method to be over written in subclasses."""
+ pass
+
+
+ def setColor(self, entity, ob, settings):
+ # Set the color
+ if entity.color_index == BYLAYER:
+ mat = settings.colors(entity.layer)
+ else:
+ mat = settings.colors(entity.color_index)
+ try:
+ ob.setMaterials([mat])
+ except ValueError:
+ print "material error - %s!" %mat
+ ob.colbits = 0x01 # Set OB materials.
+ def setGroup(self, group, it):
+ try:
+ group.objects.link(it)
+ except:
+ group.objects.append(it)
+
+
+def main(filename):
+ editmode = Window.EditMode() # are we in edit mode? If so ...
+ if editmode: Window.EditMode(0) # leave edit mode before
+ Window.WaitCursor(True) # Let the user know we are thinking
+
+ try:
+ if not filename:
+ print "DXF import: error, no file selected. Attempting to load default file."
+ try:
+ filename = Sys.expandpath(r".\examples\big-test.dxf")
+ except IOError:
+ print "DXF import: error finding default test file, exiting..."
+ return None
+ if filename:
+ drawing = readDXF(filename)
+ drawDrawing(drawing)
+ finally:
+ # restore state even if things didn't work
+ Window.WaitCursor(False)
+ if editmode: Window.EditMode(1) # and put things back how we fond them
+
+def getOCS(az):
+ """An implimentation of the Arbitrary Axis Algorithm."""
+ # world x, y, and z axis
+ wx = WORLDX
+ wy = Mathutils.Vector((0,1,0))
+ wz = Mathutils.Vector((0,0,1))
+
+ #decide if we need to transform our coords
+ if az[0] == 0 and az[1] == 0:
+ return False
+ # elif abs(az[0]) < 0.0001 or abs(az[1]) < 0.0001:
+ # return False
+ az = Mathutils.Vector(az)
+
+ cap = 0.015625 # square polar cap value (1/64.0)
+ if abs(az.x) < cap and abs(az.y) < cap:
+ ax = Mathutils.CrossVecs(wy, az)
+ else:
+ ax = Mathutils.CrossVecs(wz, az)
+ ax = ax.normalize()
+ ay = Mathutils.CrossVecs(az, ax)
+ ay = ay.normalize()
+ return ax, ay, az
+
+def transform(normal, obj):
+ """Use the calculated ocs to determine the objects location/orientation in space.
+
+ Quote from dxf docs:
+ The elevation value stored with an entity and output in DXF files is a sum
+ of the Z-coordinate difference between the UCS XY plane and the OCS XY
+ plane, and the elevation value that the user specified at the time the entity
+ was drawn.
+ """
+ ocs = getOCS(normal)
+ if ocs:
+ #print ocs
+ x, y, z = ocs
+ x = x.resize4D()
+ y = y.resize4D()
+ z = -z.resize4D()
+ x.w = 0
+ y.w = 0
+ z.w = 0
+ o = Mathutils.Vector(obj.loc)
+ o = o.resize4D()
+ mat = Mathutils.Matrix(x, y, z, o)
+ obj.setMatrix(mat)
+
+def getLayers(drawing):
+ """Build a dictionary of name:color pairs for the given drawing."""
+ tables = drawing.tables
+ for table in tables.data:
+ if table.name == 'layer':
+ layers = table
+ break
+ map = {}
+ for item in layers.data:
+ if type(item) != list and item.type == 'layer':
+ map[item.name] = item
+ colors = MatColors(map)
+ return colors
+def getBlocks(drawing, settings):
+ """Build a dictionary of name:block pairs for the given drawing."""
+ map = {}
+ for item in drawing.blocks.data:
+ if type(item) != list and item.type == 'block':
+ try:
+ map[item.name] = item
+ except KeyError:
+ # annon block
+ print "Cannot map %s - %s!" %(item.name, item)
+ blocks = Blocks(map, settings)
+ return blocks
+def drawDrawing(drawing):
+ """Given a drawing object recreate the drawing in Blender."""
+ print "Getting settings..."
+ # The settings object controls how dxf entities are drawn
+ settings = Settings(drawing, curves=False)
+
+ if settings.optimization <= settings.MID:
+ print "Drawings entities..."
+ # Draw all the know entity types in the current scene
+ drawEntities(drawing.entities, settings)
+
+ # Set the visable layers
+ SCENE.setLayers([i+1 for i in range(18)])
+ Blender.Redraw(-1)
+ if settings.optimization <= settings.MID:
+ print "Done!"
+def drawEntities(entities, settings, group=None):
+ """Draw every kind of thing in the entity list.
+
+ If provided 'group' is the Blender group new entities are to be added to.
+ """
+ for _type, drawer in type_map.items():
+ # for each known type get a list of that type and call the associated draw function
+ drawer(entities.get_type(_type), settings, group)
+
+
+drawLines = Drawer()
+def drawLine(line, curves=False):
+ """Do all the specific things needed to import lines into Blender."""
+ # Generate the geometery
+ points = line.points
+ edges = [[0, 1]]
+
+ me = Mesh.New('line') # create a new mesh
+
+ me.verts.extend(points) # add vertices to mesh
+ me.edges.extend(edges) # add edges to the mesh
+
+ # Now Create an object
+ ob = Object.New('Mesh', 'line') # link mesh to an object
+ ob.link(me)
+
+ return ob
+drawLines.draw = drawLine
+
+
+drawLWpolylines = Drawer()
+def drawLWpolyline(pline, curves=False):
+ """Do all the specific things needed to import plines into Blender."""
+ # Generate the geometery
+ points = []
+ for i in range(len(pline.points)):
+ point = pline.points[i]
+ if not point.bulge:
+ points.append(point.loc)
+ elif point.bulge and i < len(pline.points)-1:# > 0:
+ center, radius, start, end = solveBulge(point, pline.points[i+1])
+ #print center, radius, start, end
+ verts, nosense = drawArc(center, radius, start, end)
+ verts.pop(0) # remove first
+ verts.pop() #remove last
+ if point.bulge >= 0:
+ verts.reverse()
+ points.extend(verts)
+ edges = [[num, num+1] for num in range(len(points)-1)]
+ if pline.closed:
+ edges.append([len(pline.points)-1, 0])
+
+ me = Mesh.New('lwpline') # create a new mesh
+
+ me.verts.extend(points) # add vertices to mesh
+ me.edges.extend(edges) # add edges to the mesh
+
+ # Now Create an object
+ ob = Object.New('Mesh', 'lwpline') # link mesh to an object
+ ob.link(me)
+ transform(pline.extrusion, ob)
+ ob.LocZ = pline.elevation
+
+ return ob
+drawLWpolylines.draw = drawLWpolyline
+
+drawPolylines = Drawer()
+def drawPolyline(pline, curves=False):
+ """Do all the specific things needed to import plines into Blender."""
+ # Generate the geometery
+ points = []
+ for i in range(len(pline.points)):
+ point = pline.points[i]
+ if not point.bulge:
+ points.append(point.loc)
+ elif point.bulge and i < len(pline.points)-1:# > 0:
+ center, radius, start, end = solveBulge(point, pline.points[i+1])
+ #print center, radius, start, end
+ verts, nosense = drawArc(center, radius, start, end)
+ verts.pop(0) # remove first
+ verts.pop() #remove last
+ if point.bulge >= 0:
+ verts.reverse()
+ points.extend(verts)
+ edges = [[num, num+1] for num in range(len(points)-1)]
+ if pline.closed:
+ edges.append([len(pline.points)-1, 0])
+
+ me = Mesh.New('pline') # create a new mesh
+
+ me.verts.extend(points) # add vertices to mesh
+ me.edges.extend(edges) # add edges to the mesh
+
+ # Now Create an object
+ ob = Object.New('Mesh', 'pline') # link mesh to an object
+ ob.link(me)
+ transform(pline.extrusion, ob)
+ ob.LocZ = pline.elevation
+
+ return ob
+drawPolylines.draw = drawPolyline
+
+
+def solveBulge(p1, p2):
+ """return the center, radius, start angle, and end angle given two points.
+
+ Needs to take into account bulge sign.
+ negative = clockwise
+ positive = counter-clockwise
+
+ to find center given two points, and arc angle
+ calculate radius
+ Cord = sqrt(start^2 + end^2)
+ S = (bulge*Cord)/2
+ radius = ((Cord/2)^2+S^2)/2*S
+ angle of arc = 4*atan( bulge )
+ angle from p1 to center is (180-angle)/2
+ get vector pointing from p1 to p2 (p2 - p1)
+ normalize it and multiply by radius
+ rotate around p1 by angle to center point to center.
+
+ start angle = angle between (center - p1) and worldX
+ end angle = start angle + angle of arc
+ """
+ bulge = p1.bulge
+ p2 = Mathutils.Vector(p2.loc)
+ p1 = Mathutils.Vector(p1.loc)
+ cord = p2 - p1 # vector from p1 to p2
+ clength = cord.length
+ s = (bulge * clength)/2 # sagitta (height)
+ radius = abs(((clength/2)**2 + s**2)/(2*s)) # magic formula
+ angle = abs(degrees(4*atan(bulge))) # theta (included angle)
+ delta = (180 - angle)/2 # the angle from cord to center
+ if bulge > 0:
+ delta = -delta
+ radial = cord.normalize() * radius # a radius length vector aligned with cord
+ rmat = Mathutils.RotationMatrix(delta, 3, 'Z')
+ center = p1 + (rmat * radial) # rotate radial by delta degrees, then add to p1 to find center
+ if bulge < 0:
+ sv = (p1 - center) # start from point 2
+ else:
+ sv = (p2 - center) # start from point 1
+ start = Mathutils.AngleBetweenVecs(sv, WORLDX) # start angle is the angle between the first leg of the section and the x axis
+ # The next bit is my cludge to figure out if start should be negative
+ rmat = Mathutils.RotationMatrix(start, 3, 'Z')
+ rstart = rmat * sv
+ if Mathutils.AngleBetweenVecs(rstart, WORLDX) < start:
+ start = -start
+ # the end angle is just 'angle' more than start angle
+ end = start + angle
+ return list(center), radius, start, end
+drawTexts = Drawer()
+def drawText(text, curves=False):
+ """Do all the specific things needed to import texts into Blender."""
+ # Generate the geometery
+ txt = Text3d.New("text")
+ txt.setSize(1)
+ txt.setShear(text.oblique/90)
+ txt.setExtrudeDepth(0.5)
+ if text.halignment == 0:
+ align = Text3d.LEFT
+ elif text.halignment == 1:
+ align = Text3d.MIDDLE
+ elif text.halignment == 2:
+ align = Text3d.RIGHT
+ elif text.halignment == 3:
+ align = Text3d.FLUSH
+ else:
+ align = Text3d.MIDDLE
+ txt.setAlignment(align)
+ txt.setText(text.value)
+
+ # Now Create an object
+ ob = Object.New('Text', 'text') # link mesh to an object
+ ob.link(txt)
+
+ transform(text.extrusion, ob)
+
+ # move the object center to the text location
+ ob.loc = tuple(text.loc)
+ # scale it to the text size
+ ob.SizeX = text.height*text.width_factor
+ ob.SizeY = text.height
+ ob.SizeZ = text.height
+ # and rotate it around z
+ ob.RotZ = radians(text.rotation)
+
+ return ob
+drawTexts.draw = drawText
+
+drawMtexts = Drawer()
+def drawMtext(text, curves=False):
+ """Do all the specific things needed to import mtexts into Blender."""
+ # Generate the geometery
+ txt = Text3d.New("mtext")
+ txt.setSize(1)
+ # Blender doesn't give access to its text object width currently
+ # only to the text3d's curve width...
+ #txt.setWidth(text.width/10)
+ txt.setLineSeparation(text.line_space)
+ txt.setExtrudeDepth(0.5)
+ txt.setText(text.value)
+
+ # Now Create an object
+ ob = Object.New('Text', 'mtext') # link mesh to an object
+ ob.link(txt)
+
+ transform(text.extrusion, ob)
+
+ # move the object center to the text location
+ ob.loc = tuple(text.loc)
+ # scale it to the text size
+ ob.SizeX = text.height*text.width_factor
+ ob.SizeY = text.height
+ ob.SizeZ = text.height
+ # and rotate it around z
+ ob.RotZ = radians(text.rotation)
+
+ return ob
+drawMtexts.draw = drawMtext
+
+
+
+drawCircles = Drawer()
+def drawCircle(circle, curves=False):
+ """Do all the specific things needed to import circles into Blender."""
+ # Generate the geometery
+ # Now Create an object
+ if curves:
+ ob = drawCurveCircle(circle)
+ else:
+ center = circle.loc
+ radius = circle.radius
+
+ circ = 2 * pi * radius
+ if circ < 65: # if circumfrance is too small
+ verts = 32 # set a fixed number of 32 verts
+ else:
+ verts = circ/.5 # figure out how many verts we need
+ if verts > 100: # Blender only accepts values
+ verts = 100 # [3:100]
+
+ c = Mesh.Primitives.Circle(int(verts), radius*2)
+
+ ob = Object.New('Mesh', 'circle')
+ ob.link(c) # link curve data with this object
+
+ ob.loc = tuple(center)
+ transform(circle.extrusion, ob)
+
+ return ob
+drawCircles.draw = drawCircle
+
+drawArcs = Drawer()
+def drawArc(arc, curves=False):
+ """Do all the specific things needed to import arcs into Blender."""
+ # Generate the geometery
+ # Now Create an object
+ if curves:
+ ob = drawCurveArc(arc)
+ else:
+ center = arc.loc
+ radius = arc.radius
+ start = arc.start_angle
+ end = arc.end_angle
+ verts, edges = drawArc(None, radius, start, end)
+
+ a = Mesh.New('arc')
+
+ a.verts.extend(verts) # add vertices to mesh
+ a.edges.extend(edges) # add edges to the mesh
+
+ ob = Object.New('Mesh', 'arc')
+ ob.link(a) # link curve data with this object
+ ob.loc = tuple(center)
+ ob.RotX = radians(180)
+
+ transform(arc.extrusion, ob)
+ ob.size = (1,1,1)
+
+ return ob
+drawArcs.draw = drawArc
+
+
+def drawArc(center, radius, start, end, step=0.5):
+ """Draw a mesh arc with the given parameters."""
+ # center is currently set by object
+
+ # if start > end:
+ # start = start - 360
+ # if end > 360:
+ # end = end%360
+ startmatrix = Mathutils.RotationMatrix(start, 3, "Z")
+ startpoint = startmatrix * Mathutils.Vector((radius, 0, 0))
+ endmatrix = Mathutils.RotationMatrix(end, 3, "Z")
+ endpoint = endmatrix * Mathutils.Vector((radius, 0, 0))
+ points = [startpoint]
+
+ if end < start:
+ end +=360
+
+ delta = end - start
+ length = radians(delta) * radius
+ if radius < step*10: # if circumfrance is too small
+ pieces = int(delta/10) # set a fixed step of 10 degrees
+ else:
+ pieces = int(length/step) # figure out how many pieces we need for our arc
+ if pieces == 0: # stupid way to avoid a div by zero error
+ pieces = 1 # what would be a smarter way to fix this?
+ step = delta/pieces # set step so pieces * step = degrees in arc
+
+ stepmatrix = Mathutils.RotationMatrix(step, 3, "Z")
+ point = Mathutils.Vector(startpoint)
+ for i in range(int(pieces)):
+ point = stepmatrix * point
+ points.append(point)
+ points.append(endpoint)
+
+ if center:
+ points = [[point[0]+center[0], point[1]+center[1], point[2]+center[2]] for point in points]
+ edges = [[num, num+1] for num in range(len(points)-1)]
+
+ return points, edges
+drawEllipses = Drawer()
+def drawEllipse(ellipse, curves=False):
+ """Do all the specific things needed to import ellipses into Blender."""
+ # Generate the geometery
+ # Now Create an object
+ if curves:
+ ob = drawCurveArc(ellipse)
+ else:
+ major = Mathutils.Vector(ellipse.major)
+ delta = Mathutils.AngleBetweenVecs(major, WORLDX)
+ center = ellipse.loc
+ radius = major.length
+ start = degrees(ellipse.start_angle)
+ end = degrees(ellipse.end_angle)
+ verts, edges = drawArc(None, radius, start, end)
+
+ e = Mesh.New('ellipse')
+
+ e.verts.extend(verts) # add vertices to mesh
+ e.edges.extend(edges) # add edges to the mesh
+
+
+ ob = Object.New('Mesh', 'arc')
+ ob.link(e) # link curve data with this object
+ ob.loc = tuple(center)
+ ob.SizeY = ellipse.ratio
+ #ob.RotZ = radians(delta)
+ ob.RotX = radians(180)
+
+
+ transform(ellipse.extrusion, ob)
+ ob.RotZ = radians(delta)
+
+ return ob
+drawEllipses.draw = drawEllipse
+drawBlocks = Drawer(True)
+def drawBlock(insert, handle, settings):
+ """recursivly draw block objects.
+
+ Blocks are made of three objects:
+ the block_record in the tables section
+ the block in the blocks section
+ the insert object in the entities section
+
+ block_records give the insert units, blocks provide the objects drawn in the
+ block, and the insert object gives the location/scale/rotation of the block
+ instances. To draw a block you must first get a group with all the
+ blocks entities drawn in it, then scale the entities to match the world
+ units, then dupligroup that data to an object matching each insert object."""
+ if settings.blocks:
+ # get our block group
+ block = settings.blocks(insert.block)
+
+ # Now Create an object
+ ob = Object.New('Mesh', insert.block)
+ ob.link(handle) # Give the object a handle
+ ob.DupGroup = block
+ ob.enableDupGroup = True
+ else:
+ ob = Object.New('Mesh')
+
+ ob.loc = tuple(insert.loc)
+ transform(insert.extrusion, ob)
+ ob.RotZ += radians(insert.rotation)
+ ob.size = tuple(insert.scale)
+
+ return ob
+drawBlocks.draw = drawBlock
+
+
+drawFaces = Drawer()
+def drawFace(face, curves=False):
+ """Do all the specific things needed to import 3d faces into Blender."""
+ # Generate the geometery
+ points = face.points
+ if len(face.points) > 3:
+ faces = [[0, 1, 2, 3]]
+ else:
+ faces = [[0, 1, 2]]
+
+ me = Mesh.New('line') # create a new mesh
+
+ me.verts.extend(points) # add vertices to mesh
+ me.faces.extend(faces) # add faces to the mesh
+
+ # Now Create an object
+ ob = Object.New('Mesh', '3dface') # link mesh to an object
+ ob.link(me)
+
+ return ob
+drawFaces.draw = drawFace
+# Here are some alternate drawing functions for creating curve geometery.
+
+def drawCurveCircle(circle):
+ """Given a dxf circle object return a blender circle object using curves."""
+ c = Curve.New('circle') # create new curve data
+
+ center = circle.loc
+ radius = circle.radius
+
+ p1 = (0, -radius, 0)
+ p2 = (radius, 0, 0)
+ p3 = (0, radius, 0)
+ p4 = (-radius, 0, 0)
+
+ p1 = BezTriple.New(p1)
+ p2 = BezTriple.New(p2)
+ p3 = BezTriple.New(p3)
+ p4 = BezTriple.New(p4)
+
+ curve = c.appendNurb(p1)
+ curve.append(p2)
+ curve.append(p3)
+ curve.append(p4)
+ for point in curve:
+ point.handleTypes = [AUTO, AUTO]
+ curve.flagU = 1 # Set curve cyclic
+ c.update()
+
+ ob = Object.New('Curve', 'circle') # make curve object
+ return ob
+
+def drawCurveArc(arc):
+ """Given a dxf circle object return a blender circle object using curves."""
+ if start > end:
+ start = start - 360
+ startmatrix = Mathutils.RotationMatrix(start, 3, "Z")
+ startpoint = startmatrix * Mathutils.Vector((radius, 0, 0))
+ endmatrix = Mathutils.RotationMatrix(end, 3, "Z")
+ endpoint = endmatrix * Mathutils.Vector((radius, 0, 0))
+ # Note: handles must be tangent to arc and of correct length...
+
+ a = Curve.New('arc') # create new curve data
+
+ center = circle.loc
+ radius = circle.radius
+
+ p1 = (0, -radius, 0)
+ p2 = (radius, 0, 0)
+ p3 = (0, radius, 0)
+ p4 = (-radius, 0, 0)
+
+ p1 = BezTriple.New(p1)
+ p2 = BezTriple.New(p2)
+ p3 = BezTriple.New(p3)
+ p4 = BezTriple.New(p4)
+
+ curve = a.appendNurb(p1)
+ curve.append(p2)
+ curve.append(p3)
+ curve.append(p4)
+ for point in curve:
+ point.handleTypes = [AUTO, AUTO]
+ curve.flagU = 1 # Set curve cyclic
+ a.update()
+
+ ob = Object.New('Curve', 'arc') # make curve object
+ return ob
+
+
+type_map = {
+ 'line':drawLines,
+ 'lwpolyline':drawLWpolylines,
+ 'polyline':drawPolylines,
+ 'text':drawTexts,
+ 'mtext':drawMtexts,
+ 'circle':drawCircles,
+ 'arc':drawArcs,
+ 'ellipse':drawEllipses,
+ 'insert':drawBlocks,
+ '3dface':drawFaces
+}
+
+
+if __name__ == "__main__":
+ Window.FileSelector(main, 'Import a DXF file', '*.dxf')