==dxf import script ==
script for DXF import by kitsu (Ed Blake) - provide superior import to our native DXF import for many files
This commit is contained in:
282
release/scripts/bpymodules/dxfColorMap.py
Normal file
282
release/scripts/bpymodules/dxfColorMap.py
Normal file
@@ -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],
|
||||
}
|
||||
1326
release/scripts/bpymodules/dxfImportObjects.py
Normal file
1326
release/scripts/bpymodules/dxfImportObjects.py
Normal file
@@ -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!"
|
||||
323
release/scripts/bpymodules/dxfReader.py
Normal file
323
release/scripts/bpymodules/dxfReader.py
Normal file
@@ -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
|
||||
|
||||
|
||||
1005
release/scripts/import_dxf.py
Normal file
1005
release/scripts/import_dxf.py
Normal file
@@ -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')
|
||||
Reference in New Issue
Block a user