add missing flags for NLA strips
[blender.git] / release / scripts / bpymodules / dxfImportObjects.py
1 """This module provides wrapper objects for dxf entities.
2     
3     The wrappers expect a "dxf object" as input.  The dxf object is
4     an object with a type and a data attribute.  Type is a lowercase 
5     string matching the 0 code of a dxf entity.  Data is a list containing
6     dxf objects or lists of [code, data] pairs.
7     
8     This module is not general, and is only for dxf import.
9 """
10
11 # --------------------------------------------------------------------------
12 # DXF Import Objects v0.8 by Ed Blake (AKA Kitsu)
13 # --------------------------------------------------------------------------
14 # ***** BEGIN GPL LICENSE BLOCK *****
15 #
16 # This program is free software; you can redistribute it and/or
17 # modify it under the terms of the GNU General Public License
18 # as published by the Free Software Foundation; either version 2
19 # of the License, or (at your option) any later version.
20 #
21 # This program is distributed in the hope that it will be useful,
22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24 # GNU General Public License for more details.
25 #
26 # You should have received a copy of the GNU General Public License
27 # along with this program; if not, write to the Free Software Foundation,
28 # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
29 #
30 # ***** END GPL LICENCE BLOCK *****
31 # --------------------------------------------------------------------------
32 from math import *
33
34
35 # from Stani's dxf writer v1.1 (c)www.stani.be (GPL)
36 #---color values
37 BYBLOCK=0
38 BYLAYER=256
39
40 #---block-type flags (bit coded values, may be combined): 
41 ANONYMOUS               =1  # This is an anonymous block generated by hatching, associative dimensioning, other internal operations, or an application
42 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)
43 XREF                    =4  # This block is an external reference (xref)
44 XREF_OVERLAY            =8  # This block is an xref overlay 
45 EXTERNAL                =16 # This block is externally dependent
46 RESOLVED                =32 # This is a resolved external reference, or dependent of an external reference (ignored on input)
47 REFERENCED              =64 # This definition is a referenced external reference (ignored on input)
48
49 #---mtext flags
50 #attachment point
51 TOP_LEFT        = 1
52 TOP_CENTER      = 2
53 TOP_RIGHT       = 3
54 MIDDLE_LEFT     = 4
55 MIDDLE_CENTER   = 5
56 MIDDLE_RIGHT    = 6
57 BOTTOM_LEFT     = 7
58 BOTTOM_CENTER   = 8
59 BOTTOM_RIGHT    = 9
60 #drawing direction
61 LEFT_RIGHT      = 1
62 TOP_BOTTOM      = 3
63 BY_STYLE        = 5 #the flow direction is inherited from the associated text style
64 #line spacing style (optional): 
65 AT_LEAST        = 1 #taller characters will override
66 EXACT           = 2 #taller characters will not override
67
68 #---polyline flags
69 CLOSED                      =1      # This is a closed polyline (or a polygon mesh closed in the M direction)
70 CURVE_FIT                   =2      # Curve-fit vertices have been added
71 SPLINE_FIT                  =4      # Spline-fit vertices have been added
72 POLYLINE_3D                 =8      # This is a 3D polyline
73 POLYGON_MESH                =16     # This is a 3D polygon mesh
74 CLOSED_N                    =32     # The polygon mesh is closed in the N direction
75 POLYFACE_MESH               =64     # The polyline is a polyface mesh
76 CONTINOUS_LINETYPE_PATTERN  =128    # The linetype pattern is generated continuously around the vertices of this polyline
77
78 #---text flags
79 #horizontal
80 LEFT        = 0
81 CENTER      = 1
82 RIGHT       = 2
83 ALIGNED     = 3 #if vertical alignment = 0
84 MIDDLE      = 4 #if vertical alignment = 0
85 FIT         = 5 #if vertical alignment = 0
86 #vertical
87 BASELINE    = 0
88 BOTTOM      = 1
89 MIDDLE      = 2
90 TOP         = 3
91 class Object:
92     """Empty container class for dxf objects"""
93     
94     def __init__(self, _type=''):
95         """_type expects a string value."""
96         self.type = _type
97         self.name = ''
98         self.data = []
99     
100     def __str__(self):
101         if self.name:
102             return self.name
103         else:
104             return self.type
105     
106     def __repr__(self):
107         return str(self.data)
108     
109     def get_type(self, kind=''):
110         """Despite the name, this method actually returns all objects of type 'kind' from self.data."""
111         if type:
112             objects = []
113             for item in self.data:
114                 if type(item) != list and item.type == kind:
115                     # we want this type of object
116                     objects.append(item)
117                 elif type(item) == list and item[0] == kind:
118                     # we want this type of data
119                     objects.append(item[1])
120             return objects
121     
122
123 class Layer:
124     """Class for objects representing dxf layers."""
125     
126     def __init__(self, obj):
127         """Expects an entity object of type line as input."""
128         self.type = obj.type
129         self.data = obj.data[:]
130         
131         self.name = obj.get_type(2)[0]
132         self.color = obj.get_type(62)[0]
133         self.flags = obj.get_type(70)[0]
134         self.frozen = self.flags&1
135         
136     
137     
138     def __repr__(self):
139         return "%s: name - %s, color - %s" %(self.__class__.__name__, self.name, self.color)
140     
141
142
143 class Line:
144     """Class for objects representing dxf lines."""
145     
146     def __init__(self, obj):
147         """Expects an entity object of type line as input."""
148         if not obj.type == 'line':
149             raise TypeError, "Wrong type %s for line object!" %obj.type
150         self.type = obj.type
151         self.data = obj.data[:]
152         
153         self.space = obj.get_type(67)
154         if self.space:
155             self.space = self.space[0]
156         else:
157             self.space = 0
158         
159         self.color_index = obj.get_type(62)
160         if self.color_index:
161             self.color_index = self.color_index[0]
162         else:
163             self.color_index = BYLAYER
164         
165         discard, self.layer, discard_index = get_layer(obj.data)
166         del obj.data[discard_index]
167         
168         self.points = self.get_points(obj.data)
169     
170     
171     
172     
173     def get_points(self, data):
174         """Gets start and end points for a line type object.
175         
176         Lines have a fixed number of points (two) and fixed codes for each value.
177         """
178         
179         # start x, y, z and end x, y, z = 0
180         sx, sy, sz, ex, ey, ez = 0, 0, 0, 0, 0, 0
181         for item in data:
182             if item[0] == 10:   # 10 = x
183                 sx = item[1]
184             elif item[0] == 20: # 20 = y
185                 sy = item[1]
186             elif item[0] == 30: # 30 = z
187                 sz = item[1]
188             elif item[0] == 11: # 11 = x
189                 ex = item[1]
190             elif item[0] == 21: # 21 = y
191                 ey = item[1]
192             elif item[0] == 31: # 31 = z
193                 ez = item[1]
194         return [[sx, sy, sz], [ex, ey, ez]]
195     
196     
197     
198     def __repr__(self):
199         return "%s: layer - %s, points - %s" %(self.__class__.__name__, self.layer, self.points)
200     
201
202
203 class LWpolyline:
204     """Class for objects representing dxf LWpolylines."""
205     
206     def __init__(self, obj):
207         """Expects an entity object of type lwpolyline as input."""
208         if not obj.type == 'lwpolyline':
209             raise TypeError, "Wrong type %s for polyline object!" %obj.type
210         self.type = obj.type
211         self.data = obj.data[:]
212         
213         # required data
214         self.num_points = obj.get_type(90)[0]
215         
216         # optional data (with defaults)
217         self.space = obj.get_type(67)
218         if self.space:
219             self.space = self.space[0]
220         else:
221             self.space = 0
222             
223         self.color_index = obj.get_type(62)
224         if self.color_index:
225             self.color_index = self.color_index[0]
226         else:
227             self.color_index = BYLAYER
228             
229         self.elevation = obj.get_type(38)
230         if self.elevation:
231             self.elevation = self.elevation[0]
232         else:
233             self.elevation = 0
234             
235         self.flags = obj.get_type(70)
236         if self.flags:
237             self.flags = self.flags[0]
238         else:
239             self.flags = 0
240         
241         self.closed = self.flags&1 # byte coded, 1 = closed, 128 = plinegen
242         discard, self.layer, discard_index = get_layer(obj.data)
243         del obj.data[discard_index]
244         self.points = self.get_points(obj.data)
245         self.extrusion = self.get_extrusion(obj.data)
246     
247     
248     
249     
250     
251     
252     def get_points(self, data):
253         """Gets points for a polyline type object.
254         
255         Polylines have no fixed number of verts, and 
256         each vert can have a number of properties.
257         Verts should be coded as 
258         10:xvalue
259         20:yvalue
260         40:startwidth or 0
261         41:endwidth or 0
262         42:bulge or 0
263         for each vert
264         """
265         num = self.num_points
266         point = None
267         points = []
268         for item in data:
269             if item[0] == 10:   # 10 = x
270                 if point:
271                     points.append(point)
272                 point = Vertex()
273                 point.x = item[1]
274             elif item[0] == 20: # 20 = y
275                 point.y = item[1]
276             elif item[0] == 40: # 40 = start width
277                 point.swidth = item[1]
278             elif item[0] == 41: # 41 = end width
279                 point.ewidth = item[1]
280             elif item[0] == 42: # 42 = bulge
281                 point.bulge = item[1]
282         points.append(point)
283         return points
284     
285     
286     def get_extrusion(self, data):
287         """Find the axis of extrusion.
288         
289         Used to get the objects Object Coordinate System (ocs).
290         """
291         vec = [0,0,1]
292         for item in data:
293             if item[0] == 210:   # 210 = x
294                 vec[0] = item[1]
295             elif item[0] == 220: # 220 = y
296                 vec[1] = item[1]
297             elif item[0] == 230: # 230 = z
298                 vec[2] = item[1]
299         return vec
300     
301     
302     def __repr__(self):
303         return "%s: layer - %s, points - %s" %(self.__class__.__name__, self.layer, self.points)
304     
305
306
307 class Polyline:
308     """Class for objects representing dxf LWpolylines."""
309     
310     def __init__(self, obj):
311         """Expects an entity object of type polyline as input."""
312         if not obj.type == 'polyline':
313             raise TypeError, "Wrong type %s for polyline object!" %obj.type
314         self.type = obj.type
315         self.data = obj.data[:]
316         self.points = []
317         
318         # optional data (with defaults)
319         self.space = obj.get_type(67)
320         if self.space:
321             self.space = self.space[0]
322         else:
323             self.space = 0
324             
325         self.color_index = obj.get_type(62)
326         if self.color_index:
327             self.color_index = self.color_index[0]
328         else:
329             self.color_index = BYLAYER
330             
331         self.elevation = obj.get_type(30)
332         if self.elevation:
333             self.elevation = self.elevation[0]
334         else:
335             self.elevation = 0
336             
337         self.flags = obj.get_type(70)
338         if self.flags:
339             self.flags = self.flags[0]
340         else:
341             self.flags = 0
342         
343         self.closed = self.flags&1 # byte coded, 1 = closed, 128 = plinegen
344             
345         discard, self.layer, discard_index = get_layer(obj.data)
346         del obj.data[discard_index]
347         self.extrusion = self.get_extrusion(obj.data)
348     
349     
350     
351     
352     
353     def get_extrusion(self, data):
354         """Find the axis of extrusion.
355         
356         Used to get the objects Object Coordinate System (ocs).
357         """
358         vec = [0,0,1]
359         for item in data:
360             if item[0] == 210:   # 210 = x
361                 vec[0] = item[1]
362             elif item[0] == 220: # 220 = y
363                 vec[1] = item[1]
364             elif item[0] == 230: # 230 = z
365                 vec[2] = item[1]
366         return vec
367     
368     
369     def __repr__(self):
370         return "%s: layer - %s, points - %s" %(self.__class__.__name__, self.layer, self.points)
371     
372
373
374 class Vertex(object):
375     """Generic vertex object used by polylines (and maybe others)."""
376     
377     def __init__(self, obj=None):
378         """Initializes vertex data.
379         
380             The optional obj arg is an entity object of type vertex.
381         """
382         self.loc = [0,0,0]
383         self.bulge = 0
384         self.swidth = 0
385         self.ewidth = 0
386         self.flags = 0
387         
388         if obj is not None:
389             if not obj.type == 'vertex':
390                 raise TypeError, "Wrong type %s for vertex object!" %obj.type
391             self.type = obj.type
392             self.data = obj.data[:]
393             
394             self.get_props(obj.data)
395     
396     
397     def get_props(self, data):
398         """Gets coords for a vertex type object.
399         
400         Each vert can have a number of properties.
401         Verts should be coded as 
402         10:xvalue
403         20:yvalue
404         40:startwidth or 0
405         41:endwidth or 0
406         42:bulge or 0
407         """
408         for item in data:
409             if item[0] == 10:   # 10 = x
410                 self.x = item[1]
411             elif item[0] == 20: # 20 = y
412                 self.y = item[1]
413             elif item[0] == 30: # 30 = z
414                 self.z = item[1]
415             elif item[0] == 40: # 40 = start width
416                 self.swidth = item[1]
417             elif item[0] == 41: # 41 = end width
418                 self.ewidth = item[1]
419             elif item[0] == 42: # 42 = bulge
420                 self.bulge = item[1]
421             elif item[0] == 70: # 70 = vert flags
422                 self.flags = item[1]
423     
424     
425     def __len__(self):
426         return 3
427     
428     
429     def __getitem__(self, key):
430         return self.loc[key]
431     
432     
433     def __setitem__(self, key, value):
434         if key in [0,1,2]:
435             self.loc[key]
436     
437     
438     def __iter__(self):
439         return self.loc.__iter__()
440     
441     
442     def __str__(self):
443         return str(self.loc)
444     
445     
446     def __repr__(self):
447         return "Vertex %s, swidth=%s, ewidth=%s, bulge=%s" %(self.loc, self.swidth, self.ewidth, self.bulge)
448     
449     
450     def getx(self):
451         return self.loc[0]
452     
453     def setx(self, value):
454         self.loc[0] = value
455     
456     x = property(getx, setx)
457     
458     
459     def gety(self):
460         return self.loc[1]
461     
462     def sety(self, value):
463         self.loc[1] = value
464     
465     y = property(gety, sety)
466     
467     
468     def getz(self):
469         return self.loc[2]
470     
471     def setz(self, value):
472         self.loc[2] = value
473     
474     z = property(getz, setz)
475     
476
477
478 class Text:
479     """Class for objects representing dxf Text."""
480     
481     def __init__(self, obj):
482         """Expects an entity object of type text as input."""
483         if not obj.type == 'text':
484             raise TypeError, "Wrong type %s for text object!" %obj.type
485         self.type = obj.type
486         self.data = obj.data[:]
487         
488         # required data
489         self.height = obj.get_type(40)[0]
490         self.value = obj.get_type(1)[0] # The text string value
491         
492         # optional data (with defaults)
493         self.space = obj.get_type(67)
494         if self.space:
495             self.space = self.space[0]
496         else:
497             self.space = 0
498             
499         self.color_index = obj.get_type(62)
500         if self.color_index:
501             self.color_index = self.color_index[0]
502         else:
503             self.color_index = BYLAYER
504             
505         self.rotation = obj.get_type(50) # radians?
506         if not self.rotation:
507             self.rotation = 0
508         else:
509             self.rotation = self.rotation[0]
510         
511         self.width_factor = obj.get_type(41) # Scaling factor along local x axis
512         if not self.width_factor:
513             self.width_factor = 1
514         else:
515             self.width_factor = self.width_factor[0]
516         
517         self.oblique = obj.get_type(51) # skew in degrees -90 <= oblique <= 90
518         if not self.oblique:
519             self.oblique = 0
520         else:
521             self.oblique = self.oblique[0]
522         
523         self.halignment = obj.get_type(72) # horiz. alignment
524         if not self.halignment:  # 0=left, 1=center, 2=right, 3=aligned, 4=middle, 5=fit
525             self.halignment = 0
526         else:
527             self.halignment = self.halignment[0]
528             
529         self.valignment = obj.get_type(73) # vert. alignment
530         if not self.valignment:  # 0=baseline, 1=bottom, 2=middle, 3=top
531             self.valignment = 0
532         else:
533             self.valignment = self.valignment[0]
534             
535         discard, self.layer, discard_index = get_layer(obj.data)
536         del obj.data[discard_index]
537         self.loc = self.get_loc(obj.data, self.halignment, self.valignment)
538         self.extrusion = self.get_extrusion(obj.data)
539     
540     
541     
542     
543     def get_loc(self, data, halign, valign):
544         """Gets adjusted location for text type objects.
545         
546         If group 72 and/or 73 values are nonzero then the first alignment point values
547         are ignored and AutoCAD calculates new values based on the second alignment
548         point and the length and height of the text string itself (after applying the
549         text style). If the 72 and 73 values are zero or missing, then the second
550         alignment point is meaningless.
551         
552         I don't know how to calc text size...
553         """
554         # bottom left x, y, z and justification x, y, z = 0
555         x, y, z, jx, jy, jz = 0, 0, 0, 0, 0, 0
556         for item in data:
557             if item[0] == 10:   # 10 = x
558                 x = item[1]
559             elif item[0] == 20: # 20 = y
560                 y = item[1]
561             elif item[0] == 30: # 30 = z
562                 z = item[1]
563             elif item[0] == 11: # 11 = x
564                 jx = item[1]
565             elif item[0] == 21: # 21 = y
566                 jy = item[1]
567             elif item[0] == 31: # 31 = z
568                 jz = item[1]
569                 
570         if halign or valign:
571             x, y, z = jx, jy, jz
572         return [x, y, z]
573     
574     def get_extrusion(self, data):
575         """Find the axis of extrusion.
576         
577         Used to get the objects Object Coordinate System (ocs).
578         """
579         vec = [0,0,1]
580         for item in data:
581             if item[0] == 210:   # 210 = x
582                 vec[0] = item[1]
583             elif item[0] == 220: # 220 = y
584                 vec[1] = item[1]
585             elif item[0] == 230: # 230 = z
586                 vec[2] = item[1]
587         return vec
588     
589     
590     def __repr__(self):
591         return "%s: layer - %s, value - %s" %(self.__class__.__name__, self.layer, self.value)
592     
593
594
595 class Mtext:
596     """Class for objects representing dxf Mtext."""
597     
598     def __init__(self, obj):
599         """Expects an entity object of type mtext as input."""
600         if not obj.type == 'mtext':
601             raise TypeError, "Wrong type %s for mtext object!" %obj.type
602         self.type = obj.type
603         self.data = obj.data[:]
604         
605         # required data
606         self.height = obj.get_type(40)[0]
607         self.width = obj.get_type(41)[0]
608         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
609         self.value = self.get_text(obj.data) # The text string value
610         
611         # optional data (with defaults)
612         self.space = obj.get_type(67)
613         if self.space:
614             self.space = self.space[0]
615         else:
616             self.space = 0
617             
618         self.color_index = obj.get_type(62)
619         if self.color_index:
620             self.color_index = self.color_index[0]
621         else:
622             self.color_index = BYLAYER
623             
624         self.rotation = obj.get_type(50) # radians
625         if not self.rotation:
626             self.rotation = 0
627         else:
628             self.rotation = self.rotation[0]
629         
630         self.width_factor = obj.get_type(42) # Scaling factor along local x axis
631         if not self.width_factor:
632             self.width_factor = 1
633         else:
634             self.width_factor = self.width_factor[0]
635         
636         self.line_space = obj.get_type(44) # percentage of default
637         if not self.line_space:
638             self.line_space = 1
639         else:
640             self.line_space = self.line_space[0]
641             
642         discard, self.layer, discard_index = get_layer(obj.data)
643         del obj.data[discard_index]
644         self.loc = self.get_loc(obj.data)
645         self.extrusion = self.get_extrusion(obj.data)
646     
647     
648     
649     
650     
651     def get_text(self, data):
652         """Reconstructs mtext data from dxf codes."""
653         primary = ''
654         secondary = []
655         for item in data:
656             if item[0] == 1: # There should be only one primary...
657                 primary = item[1]
658             elif item[0] == 3: # There may be any number of extra strings (in order)
659                 secondary.append(item[1])
660         if not primary:
661             #raise ValueError, "Empty Mtext Object!"
662             string = "Empty Mtext Object!"
663         if not secondary:
664             string = primary.replace(r'\P', '\n')
665         else:
666             string = ''.join(secondary)+primary
667             string = string.replace(r'\P', '\n')
668         return string    
669     def get_loc(self, data):
670         """Gets location for a mtext type objects.
671         
672         Mtext objects have only one point indicating location.
673         """
674         loc = [0,0,0]
675         for item in data:
676             if item[0] == 10:   # 10 = x
677                 loc[0] = item[1]
678             elif item[0] == 20: # 20 = y
679                 loc[1] = item[1]
680             elif item[0] == 30: # 30 = z
681                 loc[2] = item[1]
682         return loc
683     
684     
685     
686     
687     def get_extrusion(self, data):
688         """Find the axis of extrusion.
689         
690         Used to get the objects Object Coordinate System (ocs).
691         """
692         vec = [0,0,1]
693         for item in data:
694             if item[0] == 210:   # 210 = x
695                 vec[0] = item[1]
696             elif item[0] == 220: # 220 = y
697                 vec[1] = item[1]
698             elif item[0] == 230: # 230 = z
699                 vec[2] = item[1]
700         return vec
701     
702     
703     def __repr__(self):
704         return "%s: layer - %s, value - %s" %(self.__class__.__name__, self.layer, self.value)
705     
706
707
708 class Circle:
709     """Class for objects representing dxf Circles."""
710     
711     def __init__(self, obj):
712         """Expects an entity object of type circle as input."""
713         if not obj.type == 'circle':
714             raise TypeError, "Wrong type %s for circle object!" %obj.type
715         self.type = obj.type
716         self.data = obj.data[:]
717         
718         # required data
719         self.radius = obj.get_type(40)[0]
720         
721         # optional data (with defaults)
722         self.space = obj.get_type(67)
723         if self.space:
724             self.space = self.space[0]
725         else:
726             self.space = 0
727             
728         self.color_index = obj.get_type(62)
729         if self.color_index:
730             self.color_index = self.color_index[0]
731         else:
732             self.color_index = BYLAYER
733             
734         discard, self.layer, discard_index = get_layer(obj.data)
735         del obj.data[discard_index]
736         self.loc = self.get_loc(obj.data)
737         self.extrusion = self.get_extrusion(obj.data)
738     
739     
740     
741     
742     
743     def get_loc(self, data):
744         """Gets the center location for circle type objects.
745         
746         Circles have a single coord location.
747         """
748         loc = [0, 0, 0]
749         for item in data:
750             if item[0] == 10:   # 10 = x
751                 loc[0] = item[1]
752             elif item[0] == 20: # 20 = y
753                 loc[1] = item[1]
754             elif item[0] == 30: # 30 = z
755                 loc[2] = item[1]
756         return loc
757     
758     
759     
760     def get_extrusion(self, data):
761         """Find the axis of extrusion.
762         
763         Used to get the objects Object Coordinate System (ocs).
764         """
765         vec = [0,0,1]
766         for item in data:
767             if item[0] == 210:   # 210 = x
768                 vec[0] = item[1]
769             elif item[0] == 220: # 220 = y
770                 vec[1] = item[1]
771             elif item[0] == 230: # 230 = z
772                 vec[2] = item[1]
773         return vec
774     
775     
776     def __repr__(self):
777         return "%s: layer - %s, radius - %s" %(self.__class__.__name__, self.layer, self.radius)
778     
779
780
781 class Arc:
782     """Class for objects representing dxf arcs."""
783     
784     def __init__(self, obj):
785         """Expects an entity object of type arc as input."""
786         if not obj.type == 'arc':
787             raise TypeError, "Wrong type %s for arc object!" %obj.type
788         self.type = obj.type
789         self.data = obj.data[:]
790         
791         # required data
792         self.radius = obj.get_type(40)[0]
793         self.start_angle = obj.get_type(50)[0]
794         self.end_angle = obj.get_type(51)[0]
795         
796         # optional data (with defaults)
797         self.space = obj.get_type(67)
798         if self.space:
799             self.space = self.space[0]
800         else:
801             self.space = 0
802             
803         self.color_index = obj.get_type(62)
804         if self.color_index:
805             self.color_index = self.color_index[0]
806         else:
807             self.color_index = BYLAYER
808             
809         discard, self.layer, discard_index = get_layer(obj.data)
810         del obj.data[discard_index]
811         self.loc = self.get_loc(obj.data)
812         self.extrusion = self.get_extrusion(obj.data)
813     
814     
815     
816     
817     
818     def get_loc(self, data):
819         """Gets the center location for arc type objects.
820         
821         Arcs have a single coord location.
822         """
823         loc = [0, 0, 0]
824         for item in data:
825             if item[0] == 10:   # 10 = x
826                 loc[0] = item[1]
827             elif item[0] == 20: # 20 = y
828                 loc[1] = item[1]
829             elif item[0] == 30: # 30 = z
830                 loc[2] = item[1]
831         return loc
832     
833     
834     
835     def get_extrusion(self, data):
836         """Find the axis of extrusion.
837         
838         Used to get the objects Object Coordinate System (ocs).
839         """
840         vec = [0,0,1]
841         for item in data:
842             if item[0] == 210:   # 210 = x
843                 vec[0] = item[1]
844             elif item[0] == 220: # 220 = y
845                 vec[1] = item[1]
846             elif item[0] == 230: # 230 = z
847                 vec[2] = item[1]
848         return vec
849     
850     
851     def __repr__(self):
852         return "%s: layer - %s, radius - %s" %(self.__class__.__name__, self.layer, self.radius)
853     
854
855
856 class BlockRecord:
857     """Class for objects representing dxf block_records."""
858     
859     def __init__(self, obj):
860         """Expects an entity object of type block_record as input."""
861         if not obj.type == 'block_record':
862             raise TypeError, "Wrong type %s for block_record object!" %obj.type
863         self.type = obj.type
864         self.data = obj.data[:]
865         
866         # required data
867         self.name = obj.get_type(2)[0]
868         
869         # optional data (with defaults)
870         self.insertion_units = obj.get_type(70)
871         if not self.insertion_units:
872             self.insertion_units = None
873         else:
874             self.insertion_units = self.insertion_units[0]
875             
876         self.insert_units = obj.get_type(1070)
877         if not self.insert_units:
878             self.insert_units = None
879         else:
880             self.insert_units = self.insert_units[0]
881         
882     
883     
884     
885     
886     
887     def __repr__(self):
888         return "%s: name - %s, insert units - %s" %(self.__class__.__name__, self.name, self.insertion_units)
889     
890
891
892
893 class Block:
894     """Class for objects representing dxf blocks."""
895     
896     def __init__(self, obj):
897         """Expects an entity object of type block as input."""
898         if not obj.type == 'block':
899             raise TypeError, "Wrong type %s for block object!" %obj.type
900         self.type = obj.type
901         self.data = obj.data[:]
902         
903         # required data
904         self.flags = obj.get_type(70)[0]
905         self.entities = Object('block_contents')
906         self.entities.data = objectify([ent for ent in obj.data if type(ent) != list])
907         
908         # optional data (with defaults)
909         self.name = obj.get_type(3)
910         if self.name:
911             self.name = self.name[0]
912         else:
913             self.name = ''
914             
915         self.path = obj.get_type(1)
916         if self.path:
917             self.path = self.path[0]
918         else:
919             self.path = ''
920             
921         self.discription = obj.get_type(4)
922         if self.discription:
923             self.discription = self.discription[0]
924         else:
925             self.discription = ''
926             
927         discard, self.layer, discard_index = get_layer(obj.data)
928         del obj.data[discard_index]
929         self.loc = self.get_loc(obj.data)
930     
931     
932     
933     
934     
935     def get_loc(self, data):
936         """Gets the insert point of the block."""
937         loc = [0, 0, 0]
938         for item in data:
939             if type(item) != list:
940                 continue
941             if item[0] == 10:   # 10 = x
942                 loc[0] = item[1]
943             elif item[0] == 20: # 20 = y
944                 loc[1] = item[1]
945             elif item[0] == 30: # 30 = z
946                 loc[2] = item[1]
947         return loc
948     
949     
950     
951     def __repr__(self):
952         return "%s: name - %s, description - %s, xref-path - %s" %(self.__class__.__name__, self.name, self.discription, self.path)
953     
954
955
956
957 class Insert:
958     """Class for objects representing dxf inserts."""
959     
960     def __init__(self, obj):
961         """Expects an entity object of type insert as input."""
962         if not obj.type == 'insert':
963             raise TypeError, "Wrong type %s for insert object!" %obj.type
964         self.type = obj.type
965         self.data = obj.data[:]
966         
967         # required data
968         self.block = obj.get_type(2)[0]
969         
970         # optional data (with defaults)
971         self.rotation = obj.get_type(50)
972         if self.rotation:
973             self.rotation = self.rotation[0]
974         else:
975             self.rotation = 0
976         
977         self.space = obj.get_type(67)
978         if self.space:
979             self.space = self.space[0]
980         else:
981             self.space = 0
982             
983         self.color_index = obj.get_type(62)
984         if self.color_index:
985             self.color_index = self.color_index[0]
986         else:
987             self.color_index = BYLAYER
988         
989         discard, self.layer, discard_index = get_layer(obj.data)
990         del obj.data[discard_index]
991         self.loc = self.get_loc(obj.data)
992         self.scale = self.get_scale(obj.data)
993         self.rows, self.columns = self.get_array(obj.data)
994         self.extrusion = self.get_extrusion(obj.data)
995     
996     
997     
998     
999     
1000     def get_loc(self, data):
1001         """Gets the center location for circle type objects.
1002         
1003         Circles have a single coord location.
1004         """
1005         loc = [0, 0, 0]
1006         for item in data:
1007             if item[0] == 10:   # 10 = x
1008                 loc[0] = item[1]
1009             elif item[0] == 20: # 20 = y
1010                 loc[1] = item[1]
1011             elif item[0] == 30: # 30 = z
1012                 loc[2] = item[1]
1013         return loc
1014     
1015     
1016     
1017     def get_scale(self, data):
1018         """Gets the x/y/z scale factor for the block.
1019         """
1020         scale = [1, 1, 1]
1021         for item in data:
1022             if item[0] == 41:   # 41 = x scale
1023                 scale[0] = item[1]
1024             elif item[0] == 42: # 42 = y scale
1025                 scale[1] = item[1]
1026             elif item[0] == 43: # 43 = z scale
1027                 scale[2] = item[1]
1028         return scale
1029     
1030     
1031     
1032     def get_array(self, data):
1033         """Returns the pair (row number, row spacing), (column number, column spacing)."""
1034         columns = 1
1035         rows = 1
1036         cspace = 0
1037         rspace = 0
1038         for item in data:
1039             if item[0] == 70:   # 70 = columns
1040                 columns = item[1]
1041             elif item[0] == 71: # 71 = rows
1042                 rows = item[1]
1043             if item[0] == 44:   # 44 = columns
1044                 cspace = item[1]
1045             elif item[0] == 45: # 45 = rows
1046                 rspace = item[1]
1047         return (rows, rspace), (columns, cspace)
1048     
1049     
1050     
1051     def get_extrusion(self, data):
1052         """Find the axis of extrusion.
1053         
1054         Used to get the objects Object Coordinate System (ocs).
1055         """
1056         vec = [0,0,1]
1057         for item in data:
1058             if item[0] == 210:   # 210 = x
1059                 vec[0] = item[1]
1060             elif item[0] == 220: # 220 = y
1061                 vec[1] = item[1]
1062             elif item[0] == 230: # 230 = z
1063                 vec[2] = item[1]
1064         return vec
1065     
1066     
1067     def __repr__(self):
1068         return "%s: layer - %s, block - %s" %(self.__class__.__name__, self.layer, self.block)
1069     
1070
1071
1072
1073 class Ellipse:
1074     """Class for objects representing dxf ellipses."""
1075     
1076     def __init__(self, obj):
1077         """Expects an entity object of type ellipse as input."""
1078         if not obj.type == 'ellipse':
1079             raise TypeError, "Wrong type %s for ellipse object!" %obj.type
1080         self.type = obj.type
1081         self.data = obj.data[:]
1082         
1083         # required data
1084         self.ratio = obj.get_type(40)[0]
1085         self.start_angle = obj.get_type(41)[0]
1086         self.end_angle = obj.get_type(42)[0]
1087         
1088         # optional data (with defaults)
1089         self.space = obj.get_type(67)
1090         if self.space:
1091             self.space = self.space[0]
1092         else:
1093             self.space = 0
1094             
1095         self.color_index = obj.get_type(62)
1096         if self.color_index:
1097             self.color_index = self.color_index[0]
1098         else:
1099             self.color_index = BYLAYER
1100             
1101         discard, self.layer, discard_index = get_layer(obj.data)
1102         del obj.data[discard_index]
1103         self.loc = self.get_loc(obj.data)
1104         self.major = self.get_major(obj.data)
1105         self.extrusion = self.get_extrusion(obj.data)
1106         self.radius = sqrt(self.major[0]**2 + self.major[0]**2 + self.major[0]**2)
1107     
1108     
1109     
1110     
1111     def get_loc(self, data):
1112         """Gets the center location for arc type objects.
1113         
1114         Arcs have a single coord location.
1115         """
1116         loc = [0, 0, 0]
1117         for item in data:
1118             if item[0] == 10:   # 10 = x
1119                 loc[0] = item[1]
1120             elif item[0] == 20: # 20 = y
1121                 loc[1] = item[1]
1122             elif item[0] == 30: # 30 = z
1123                 loc[2] = item[1]
1124         return loc
1125     
1126     
1127     
1128     def get_major(self, data):
1129         """Gets the major axis for ellipse type objects.
1130         
1131         The ellipse major axis defines the rotation of the ellipse and its radius.
1132         """
1133         loc = [0, 0, 0]
1134         for item in data:
1135             if item[0] == 11:   # 11 = x
1136                 loc[0] = item[1]
1137             elif item[0] == 21: # 21 = y
1138                 loc[1] = item[1]
1139             elif item[0] == 31: # 31 = z
1140                 loc[2] = item[1]
1141         return loc
1142     
1143     
1144     
1145     def get_extrusion(self, data):
1146         """Find the axis of extrusion.
1147         
1148         Used to get the objects Object Coordinate System (ocs).
1149         """
1150         vec = [0,0,1]
1151         for item in data:
1152             if item[0] == 210:   # 210 = x
1153                 vec[0] = item[1]
1154             elif item[0] == 220: # 220 = y
1155                 vec[1] = item[1]
1156             elif item[0] == 230: # 230 = z
1157                 vec[2] = item[1]
1158         return vec
1159     
1160     
1161     def __repr__(self):
1162         return "%s: layer - %s, radius - %s" %(self.__class__.__name__, self.layer, self.radius)
1163     
1164
1165
1166 class Face:
1167     """Class for objects representing dxf 3d faces."""
1168     
1169     def __init__(self, obj):
1170         """Expects an entity object of type 3dfaceplot as input."""
1171         if not obj.type == '3dface':
1172             raise TypeError, "Wrong type %s for 3dface object!" %obj.type
1173         self.type = obj.type
1174         self.data = obj.data[:]
1175         
1176         # optional data (with defaults)
1177         self.space = obj.get_type(67)
1178         if self.space:
1179             self.space = self.space[0]
1180         else:
1181             self.space = 0
1182         
1183         self.color_index = obj.get_type(62)
1184         if self.color_index:
1185             self.color_index = self.color_index[0]
1186         else:
1187             self.color_index = BYLAYER
1188         
1189         discard, self.layer, discard_index = get_layer(obj.data)
1190         del obj.data[discard_index]
1191         self.points = self.get_points(obj.data)
1192     
1193     
1194     
1195     
1196     def get_points(self, data):
1197         """Gets 3-4 points for a 3d face type object.
1198         
1199         Faces have three or optionally four verts.
1200         """
1201         
1202         a = [0, 0, 0]
1203         b = [0, 0, 0]
1204         c = [0, 0, 0]
1205         d = False
1206         for item in data:
1207             # ----------- a -------------
1208             if item[0] == 10:   # 10 = x
1209                 a[0] = item[1]
1210             elif item[0] == 20: # 20 = y
1211                 a[1] = item[1]
1212             elif item[0] == 30: # 30 = z
1213                 a[2] = item[1]
1214             # ----------- b -------------
1215             elif item[0] == 11: # 11 = x
1216                 b[0] = item[1]
1217             elif item[0] == 21: # 21 = y
1218                 b[1] = item[1]
1219             elif item[0] == 31: # 31 = z
1220                 b[2] = item[1]
1221             # ----------- c -------------
1222             elif item[0] == 12: # 12 = x
1223                 c[0] = item[1]
1224             elif item[0] == 22: # 22 = y
1225                 c[1] = item[1]
1226             elif item[0] == 32: # 32 = z
1227                 c[2] = item[1]
1228             # ----------- d -------------
1229             elif item[0] == 13: # 13 = x
1230                 d = [0, 0, 0]
1231                 d[0] = item[1]
1232             elif item[0] == 23: # 23 = y
1233                 d[1] = item[1]
1234             elif item[0] == 33: # 33 = z
1235                 d[2] = item[1]
1236         out = [a,b,c]
1237         if d:
1238             out.append(d)
1239         return out
1240     
1241     
1242     def __repr__(self):
1243         return "%s: layer - %s, points - %s" %(self.__class__.__name__, self.layer, self.points)
1244     
1245
1246 def get_name(data):
1247     """Get the name of an object from its object data.
1248     
1249     Returns a pair of (data_item, name) where data_item is the list entry where the name was found
1250     (the data_item can be used to remove the entry from the object data).  Be sure to check 
1251     name not None before using the returned values!
1252     """
1253     value = None
1254     for i, item in enumerate(data):
1255         if item[0] == 2:
1256             value = item[1]
1257             break
1258     return item, value, i
1259
1260 def get_layer(data):
1261     """Expects object data as input.
1262     
1263     Returns (entry, layer_name, entry_index) where entry is the data item that provided the layer name.
1264     """
1265     value = None
1266     for i, item in enumerate(data):
1267         if item[0] == 8:
1268             value = item[1]
1269             break
1270     return item, value, i
1271
1272
1273 # type to object map
1274 type_map = {
1275     'line':Line,
1276     'lwpolyline':LWpolyline,
1277     'text':Text,
1278     'mtext':Mtext,
1279     'circle':Circle,
1280     'arc':Arc,
1281     'layer':Layer,
1282     'block_record':BlockRecord,
1283     'block':Block,
1284     'insert':Insert,
1285     'ellipse':Ellipse,
1286     '3dface':Face
1287 }
1288
1289 def objectify(data):
1290     """Expects a section type object's data as input.
1291     
1292     Maps object data to the correct object type.
1293     """
1294     objects = [] # colector for finished objects
1295     known_types = type_map.keys() # so we don't have to call foo.keys() every iteration
1296     index = 0
1297     while index < len(data):
1298         item = data[index]
1299         if type(item) != list and item.type in known_types:
1300             # proccess the object and append the resulting object
1301             objects.append(type_map[item.type](item))
1302         elif type(item) != list and item.type == 'table':
1303             item.data = objectify(item.data) # tables have sub-objects
1304             objects.append(item)
1305         elif type(item) != list and item.type == 'polyline':
1306             pline = Polyline(item)
1307             while 1:
1308                 index += 1
1309                 item = data[index]
1310                 if item.type == 'vertex':
1311                     v = Vertex(item)
1312                     pline.points.append(v)
1313                 elif item.type == 'seqend':
1314                     break
1315                 else:
1316                     print "Error: non-vertex found before seqend!"
1317                     index -= 1
1318                     break
1319             objects.append(pline)
1320         else:
1321             # we will just let the data pass un-harrased
1322             objects.append(item)
1323         index += 1
1324     return objects    
1325 if __name__ == "__main__":
1326     print "No example yet!"