96caa50cc416af9d777c644f8c1f7bb7a0dc0349
[blender.git] / release / scripts / bpymodules / dxfLibrary.py
1 #dxfLibrary.py : provides functions for generating DXF files
2 # --------------------------------------------------------------------------
3 __version__ = "v1.28beta - 2008.12.13"
4 __author__ = "Stani Michiels(Stani), Remigiusz Fiedler(migius)"
5 __license__ = "GPL"
6 __url__ = "http://wiki.blender.org/index.php/Scripts/Manual/Export/autodesk_dxf"
7 __bpydoc__ ="""The library to export geometry data to DXF format r12 version.
8
9 Copyright %s
10 Version %s
11 License %s
12 Homepage %s
13
14 See the homepage for documentation.
15 Dedicated thread on BlenderArtists: http://blenderartists.org/forum/showthread.php?t=136439
16
17 IDEAs:
18 -
19
20 TODO:
21 - add support for SPLINEs
22
23 History
24 v1.28 - 2008.12.13 by Steeve/BlenderArtists
25 - bugfix for EXTMIN/EXTMAX to suit Cycas-CAD
26 v1.27 - 2008.10.07 by migius
27 - beautifying output code: keys whitespace prefix
28 - refactoring DXF-strings format: NewLine moved to the end of
29 v1.26 - 2008.10.05 by migius
30 - modif POLYLINE to support POLYFACE
31 v1.25 - 2008.09.28 by migius
32 - modif FACE class for r12
33 v1.24 - 2008.09.27 by migius
34 - modif POLYLINE class for r12
35 - changing output format from r9 to r12(AC1009)
36 v1.1 (20/6/2005) by www.stani.be/python/sdxf
37 - Python library to generate dxf drawings
38 ______________________________________________________________
39 """ % (__author__,__version__,__license__,__url__)
40
41 # --------------------------------------------------------------------------
42 # DXF Library: copyright (C) 2005 by Stani Michiels (AKA Stani)
43 #                            2008 modif by Remigiusz Fiedler (AKA migius)
44 # --------------------------------------------------------------------------
45 # ***** BEGIN GPL LICENSE BLOCK *****
46 #
47 # This program is free software; you can redistribute it and/or
48 # modify it under the terms of the GNU General Public License
49 # as published by the Free Software Foundation; either version 2
50 # of the License, or (at your option) any later version.
51 #
52 # This program is distributed in the hope that it will be useful,
53 # but WITHOUT ANY WARRANTY; without even the implied warranty of
54 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
55 # GNU General Public License for more details.
56 #
57 # You should have received a copy of the GNU General Public License
58 # along with this program; if not, write to the Free Software Foundation,
59 # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
60 #
61 # ***** END GPL LICENCE BLOCK *****
62
63
64 #import Blender
65 #from Blender import Mathutils, Window, Scene, sys, Draw
66 #import BPyMessages
67
68 try:
69         import copy
70         #from struct import pack
71 except:
72         copy = None
73
74 ####1) Private (only for developpers)
75 _HEADER_POINTS=['insbase','extmin','extmax']
76
77 #---helper functions-----------------------------------
78 def _point(x,index=0):
79         """Convert tuple to a dxf point"""
80         #print 'deb: _point=', x #-------------
81         return '\n'.join([' %s\n%s'%((i+1)*10+index,x[i]) for i in range(len(x))])
82
83 def _points(plist):
84         """Convert a list of tuples to dxf points"""
85         out = '\n'.join([_point(plist[i],i)for i in range(len(plist))])
86         #print 'deb: points=\n', out #-------------------
87         return out
88
89 #---base classes----------------------------------------
90 class _Call:
91         """Makes a callable class."""
92         def copy(self):
93                 """Returns a copy."""
94                 return copy.deepcopy(self)
95
96         def __call__(self,**attrs):
97                 """Returns a copy with modified attributes."""
98                 copied=self.copy()
99                 for attr in attrs:setattr(copied,attr,attrs[attr])
100                 return copied
101
102 #-------------------------------------------------------
103 class _Entity(_Call):
104         """Base class for _common group codes for entities."""
105         def __init__(self,color=None,extrusion=None,layer='0',
106                                  lineType=None,lineTypeScale=None,lineWeight=None,
107                                  thickness=None,parent=None):
108                 """None values will be omitted."""
109                 self.color                = color
110                 self.extrusion    = extrusion
111                 self.layer                = layer
112                 self.lineType      = lineType
113                 self.lineTypeScale  = lineTypeScale
114                 self.lineWeight  = lineWeight
115                 self.thickness    = thickness
116                 self.parent              = parent
117
118         def _common(self):
119                 """Return common group codes as a string."""
120                 if self.parent:parent=self.parent
121                 else:parent=self
122                 result =''
123                 if parent.layer!=None: result+='  8\n%s\n'%parent.layer
124                 if parent.color!=None: result+=' 62\n%s\n'%parent.color
125                 if parent.extrusion!=None: result+='%s\n'%_point(parent.extrusion,200)
126                 if parent.lineType!=None: result+='  6\n%s\n'%parent.lineType
127                 #TODO: if parent.lineWeight!=None: result+='370\n%s\n'%parent.lineWeight
128                 if parent.lineTypeScale!=None: result+=' 48\n%s\n'%parent.lineTypeScale
129                 if parent.thickness!=None: result+=' 39\n%s\n'%parent.thickness
130                 return result
131
132 #--------------------------
133 class _Entities:
134         """Base class to deal with composed objects."""
135         def __dxf__(self):
136                 return []
137
138         def __str__(self):
139                 return ''.join([str(x) for x in self.__dxf__()])
140
141 #--------------------------
142 class _Collection(_Call):
143         """Base class to expose entities methods to main object."""
144         def __init__(self,entities=[]):
145                 self.entities=copy.copy(entities)
146                 #link entities methods to drawing
147                 for attr in dir(self.entities):
148                         if attr[0]!='_':
149                                 attrObject=getattr(self.entities,attr)
150                                 if callable(attrObject):
151                                         setattr(self,attr,attrObject)
152
153 ####2) Constants
154 #---color values
155 BYBLOCK=0
156 BYLAYER=256
157
158 #---block-type flags (bit coded values, may be combined):
159 ANONYMOUS =1  # This is an anonymous block generated by hatching, associative dimensioning, other internal operations, or an application
160 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)
161 XREF =4  # This block is an external reference (xref)
162 XREF_OVERLAY =8  # This block is an xref overlay
163 EXTERNAL =16 # This block is externally dependent
164 RESOLVED =32 # This is a resolved external reference, or dependent of an external reference (ignored on input)
165 REFERENCED =64 # This definition is a referenced external reference (ignored on input)
166
167 #---mtext flags
168 #attachment point
169 TOP_LEFT = 1
170 TOP_CENTER = 2
171 TOP_RIGHT = 3
172 MIDDLE_LEFT = 4
173 MIDDLE_CENTER = 5
174 MIDDLE_RIGHT    = 6
175 BOTTOM_LEFT = 7
176 BOTTOM_CENTER = 8
177 BOTTOM_RIGHT = 9
178 #drawing direction
179 LEFT_RIGHT = 1
180 TOP_BOTTOM = 3
181 BY_STYLE = 5 #the flow direction is inherited from the associated text style
182 #line spacing style (optional):
183 AT_LEAST = 1 #taller characters will override
184 EXACT = 2 #taller characters will not override
185
186 #---polyline flags
187 CLOSED =1         # This is a closed polyline (or a polygon mesh closed in the M direction)
188 CURVE_FIT =2      # Curve-fit vertices have been added
189 SPLINE_FIT =4     # Spline-fit vertices have been added
190 POLYLINE_3D =8    # This is a 3D polyline
191 POLYGON_MESH =16         # This is a 3D polygon mesh
192 CLOSED_N =32     # The polygon mesh is closed in the N direction
193 POLYFACE_MESH =64        # The polyline is a polyface mesh
194 CONTINOUS_LINETYPE_PATTERN =128 # The linetype pattern is generated continuously around the vertices of this polyline
195
196 #---text flags
197 #horizontal
198 LEFT = 0
199 CENTER = 1
200 RIGHT = 2
201 ALIGNED = 3 #if vertical alignment = 0
202 MIDDLE = 4 #if vertical alignment = 0
203 FIT = 5 #if vertical alignment = 0
204 #vertical
205 BASELINE = 0
206 BOTTOM  = 1
207 MIDDLE = 2
208 TOP = 3
209
210 ####3) Classes
211 #---entitities -----------------------------------------------
212 #--------------------------
213 class Arc(_Entity):
214         """Arc, angles in degrees."""
215         def __init__(self,center=(0,0,0),radius=1,
216                                  startAngle=0.0,endAngle=90,**common):
217                 """Angles in degrees."""
218                 _Entity.__init__(self,**common)
219                 self.center=center
220                 self.radius=radius
221                 self.startAngle=startAngle
222                 self.endAngle=endAngle
223         def __str__(self):
224                 return '  0\nARC\n%s%s\n 40\n%s\n 50\n%s\n 51\n%s\n'%\
225                            (self._common(),_point(self.center),
226                                 self.radius,self.startAngle,self.endAngle)
227
228 #-----------------------------------------------
229 class Circle(_Entity):
230         """Circle"""
231         def __init__(self,center=(0,0,0),radius=1,**common):
232                 _Entity.__init__(self,**common)
233                 self.center=center
234                 self.radius=radius
235         def __str__(self):
236                 return '  0\nCIRCLE\n%s%s\n 40\n%s\n'%\
237                            (self._common(),_point(self.center),self.radius)
238
239 #-----------------------------------------------
240 class Face(_Entity):
241         """3dface"""
242         def __init__(self,points,**common):
243                 _Entity.__init__(self,**common)
244                 while len(points)<4: #fix for r12 format
245                         points.append(points[-1])
246                 self.points=points
247                 
248         def __str__(self):
249                 out = '  0\n3DFACE\n%s%s\n' %(self._common(),_points(self.points))
250                 #print 'deb:out=', out #-------------------
251                 return out
252
253 #-----------------------------------------------
254 class Insert(_Entity):
255         """Block instance."""
256         def __init__(self,name,point=(0,0,0),
257                                  xscale=None,yscale=None,zscale=None,
258                                  cols=None,colspacing=None,rows=None,rowspacing=None,
259                                  rotation=None,
260                                  **common):
261                 _Entity.__init__(self,**common)
262                 self.name=name
263                 self.point=point
264                 self.xscale=xscale
265                 self.yscale=yscale
266                 self.zscale=zscale
267                 self.cols=cols
268                 self.colspacing=colspacing
269                 self.rows=rows
270                 self.rowspacing=rowspacing
271                 self.rotation=rotation
272
273         def __str__(self):
274                 result='  0\nINSERT\n  2\n%s\n%s\n%s\n'%\
275                                 (self.name,self._common(),_point(self.point))
276                 if self.xscale!=None:result+=' 41\n%s\n'%self.xscale
277                 if self.yscale!=None:result+=' 42\n%s\n'%self.yscale
278                 if self.zscale!=None:result+=' 43\n%s\n'%self.zscale
279                 if self.rotation:result+=' 50\n%s\n'%self.rotation
280                 if self.cols!=None:result+=' 70\n%s\n'%self.cols
281                 if self.colspacing!=None:result+=' 44\n%s\n'%self.colspacing
282                 if self.rows!=None:result+=' 71\n%s\n'%self.rows
283                 if self.rowspacing!=None:result+=' 45\n%s\n'%self.rowspacing
284                 return result
285
286 #-----------------------------------------------
287 class Line(_Entity):
288         """Line"""
289         def __init__(self,points,**common):
290                 _Entity.__init__(self,**common)
291                 self.points=points
292         def __str__(self):
293                 return '  0\nLINE\n%s%s\n' %(
294                                 self._common(), _points(self.points))
295
296
297 #-----------------------------------------------
298 class PolyLine(_Entity):
299         def __init__(self,points,org_point=[0,0,0],flag=0,width=None,**common):
300                 _Entity.__init__(self,**common)
301                 self.points=points
302                 self.org_point=org_point
303                 self.flag=flag
304                 if self.flag==64:
305                         self.points=points[0]
306                         self.faces=points[1]
307                         self.p_count=len(self.points)
308                         self.f_count=len(self.faces)
309                 self.width=width
310
311         def __str__(self):
312                 result= '  0\nPOLYLINE\n%s 70\n%s\n' %(self._common(),self.flag)
313                 #print 'deb: self._common()', self._common() #----------
314                 result+=' 66\n1\n'
315                 result+='%s\n' %_point(self.org_point)
316                 if self.flag==64:
317                         result+=' 71\n%s\n' %self.p_count
318                         result+=' 72\n%s\n' %self.f_count
319                 for point in self.points:
320                         result+='  0\nVERTEX\n'
321                         result+='  8\n%s\n' %self.layer
322                         result+='%s\n' %_point(point)
323                         if self.flag==64: result+=' 70\n192\n'
324                         if self.width: result+=' 40\n%s\n 41\n%s\n' %(self.width,self.width)
325                 if self.flag==64:
326                         for face in self.faces:
327                                 result+='  0\nVERTEX\n'
328                                 result+='  8\n%s\n' %self.layer
329                                 result+='%s\n' %_point(self.org_point)
330                                 result+=' 70\n128\n'
331                                 result+=' 71\n%s\n' %face[0]
332                                 result+=' 72\n%s\n' %face[1]
333                                 result+=' 73\n%s\n' %face[2]
334                                 if len(face)==4: result+=' 74\n%s\n' %face[3]
335                 result+='  0\nSEQEND\n'
336                 result+='  8\n%s\n' %self.layer
337                 return result
338
339 #-----------------------------------------------
340 class Point(_Entity):
341         """Point."""
342         def __init__(self,points=None,**common):
343                 _Entity.__init__(self,**common)
344                 self.points=points
345         def __str__(self): #TODO:
346                 return '  0\nPOINT\n%s%s\n' %(self._common(),
347                          _points(self.points)
348                         )
349
350 #-----------------------------------------------
351 class Solid(_Entity):
352         """Colored solid fill."""
353         def __init__(self,points=None,**common):
354                 _Entity.__init__(self,**common)
355                 self.points=points
356         def __str__(self):
357                 return '  0\nSOLID\n%s%s\n' %(self._common(),
358                          _points(self.points[:2]+[self.points[3],self.points[2]])
359                         )
360
361
362 #-----------------------------------------------
363 class Text(_Entity):
364         """Single text line."""
365         def __init__(self,text='',point=(0,0,0),alignment=None,
366                                  flag=None,height=1,justifyhor=None,justifyver=None,
367                                  rotation=None,obliqueAngle=None,style=None,xscale=None,**common):
368                 _Entity.__init__(self,**common)
369                 self.text=text
370                 self.point=point
371                 self.alignment=alignment
372                 self.flag=flag
373                 self.height=height
374                 self.justifyhor=justifyhor
375                 self.justifyver=justifyver
376                 self.rotation=rotation
377                 self.obliqueAngle=obliqueAngle
378                 self.style=style
379                 self.xscale=xscale
380         def __str__(self):
381                 result= '  0\nTEXT\n%s%s\n 40\n%s\n  1\n%s\n'%\
382                                 (self._common(),_point(self.point),self.height,self.text)
383                 if self.rotation: result+=' 50\n%s\n'%self.rotation
384                 if self.xscale: result+=' 41\n%s\n'%self.xscale
385                 if self.obliqueAngle: result+=' 51\n%s\n'%self.obliqueAngle
386                 if self.style: result+='  7\n%s\n'%self.style
387                 if self.flag: result+=' 71\n%s\n'%self.flag
388                 if self.justifyhor: result+=' 72\n%s\n'%self.justifyhor
389                 #TODO: if self.alignment: result+='%s\n'%_point(self.alignment,1)
390                 if self.justifyver: result+=' 73\n%s\n'%self.justifyver
391                 return result
392
393 #-----------------------------------------------
394 class Mtext(Text):
395         """Surrogate for mtext, generates some Text instances."""
396         def __init__(self,text='',point=(0,0,0),width=250,spacingFactor=1.5,down=0,spacingWidth=None,**options):
397                 Text.__init__(self,text=text,point=point,**options)
398                 if down:spacingFactor*=-1
399                 self.spacingFactor=spacingFactor
400                 self.spacingWidth=spacingWidth
401                 self.width=width
402                 self.down=down
403         def __str__(self):
404                 texts=self.text.replace('\r\n','\n').split('\n')
405                 if not self.down:texts.reverse()
406                 result=''
407                 x=y=0
408                 if self.spacingWidth:spacingWidth=self.spacingWidth
409                 else:spacingWidth=self.height*self.spacingFactor
410                 for text in texts:
411                         while text:
412                                 result+='%s\n'%Text(text[:self.width],
413                                         point=(self.point[0]+x*spacingWidth,
414                                                    self.point[1]+y*spacingWidth,
415                                                    self.point[2]),
416                                         alignment=self.alignment,flag=self.flag,height=self.height,
417                                         justifyhor=self.justifyhor,justifyver=self.justifyver,
418                                         rotation=self.rotation,obliqueAngle=self.obliqueAngle,
419                                         style=self.style,xscale=self.xscale,parent=self
420                                 )
421                                 text=text[self.width:]
422                                 if self.rotation:x+=1
423                                 else:y+=1
424                 return result[1:]
425
426 #-----------------------------------------------
427 ##class _Mtext(_Entity):
428 ##      """Mtext not functioning for minimal dxf."""
429 ##      def __init__(self,text='',point=(0,0,0),attachment=1,
430 ##                               charWidth=None,charHeight=1,direction=1,height=100,rotation=0,
431 ##                               spacingStyle=None,spacingFactor=None,style=None,width=100,
432 ##                               xdirection=None,**common):
433 ##              _Entity.__init__(self,**common)
434 ##              self.text=text
435 ##              self.point=point
436 ##              self.attachment=attachment
437 ##              self.charWidth=charWidth
438 ##              self.charHeight=charHeight
439 ##              self.direction=direction
440 ##              self.height=height
441 ##              self.rotation=rotation
442 ##              self.spacingStyle=spacingStyle
443 ##              self.spacingFactor=spacingFactor
444 ##              self.style=style
445 ##              self.width=width
446 ##              self.xdirection=xdirection
447 ##      def __str__(self):
448 ##              input=self.text
449 ##              text=''
450 ##              while len(input)>250:
451 ##                      text+='3\n%s\n'%input[:250]
452 ##                      input=input[250:]
453 ##              text+='1\n%s\n'%input
454 ##              result= '0\nMTEXT\n%s\n%s\n40\n%s\n41\n%s\n71\n%s\n72\n%s%s\n43\n%s\n50\n%s\n'%\
455 ##                              (self._common(),_point(self.point),self.charHeight,self.width,
456 ##                               self.attachment,self.direction,text,
457 ##                               self.height,
458 ##                               self.rotation)
459 ##              if self.style:result+='7\n%s\n'%self.style
460 ##              if self.xdirection:result+='%s\n'%_point(self.xdirection,1)
461 ##              if self.charWidth:result+='42\n%s\n'%self.charWidth
462 ##              if self.spacingStyle:result+='73\n%s\n'%self.spacingStyle
463 ##              if self.spacingFactor:result+='44\n%s\n'%self.spacingFactor
464 ##              return result
465
466 #---tables ---------------------------------------------------
467 #-----------------------------------------------
468 class Block(_Collection):
469         """Use list methods to add entities, eg append."""
470         def __init__(self,name,layer='0',flag=0,base=(0,0,0),entities=[]):
471                 self.entities=copy.copy(entities)
472                 _Collection.__init__(self,entities)
473                 self.layer=layer
474                 self.name=name
475                 self.flag=0
476                 self.base=base
477         def __str__(self): #TODO:
478                 e=''.join([str(x)for x in self.entities])
479                 return '  0\nBLOCK\n  8\n%s\n  2\n%s\n 70\n%s\n%s\n  3\n%s\n%s  0\nENDBLK\n'%\
480                            (self.layer,self.name.upper(),self.flag,_point(self.base),self.name.upper(),e)
481
482 #-----------------------------------------------
483 class Layer(_Call):
484         """Layer"""
485         def __init__(self,name='pydxf',color=7,lineType='continuous',flag=64):
486                 self.name=name
487                 self.color=color
488                 self.lineType=lineType
489                 self.flag=flag
490         def __str__(self):
491                 return '  0\nLAYER\n  2\n%s\n 70\n%s\n 62\n%s\n  6\n%s\n'%\
492                            (self.name.upper(),self.flag,self.color,self.lineType)
493
494 #-----------------------------------------------
495 class LineType(_Call):
496         """Custom linetype"""
497         def __init__(self,name='continuous',description='Solid line',elements=[],flag=64):
498                 # TODO: Implement lineType elements
499                 self.name=name
500                 self.description=description
501                 self.elements=copy.copy(elements)
502                 self.flag=flag
503         def __str__(self):
504                 return '  0\nLTYPE\n  2\n%s\n 70\n%s\n  3\n%s\n 72\n65\n 73\n%s\n 40\n0.0\n'%\
505                         (self.name.upper(),self.flag,self.description,len(self.elements))
506
507 #-----------------------------------------------
508 class Style(_Call):
509         """Text style"""
510         def __init__(self,name='standard',flag=0,height=0,widthFactor=40,obliqueAngle=50,
511                                  mirror=0,lastHeight=1,font='arial.ttf',bigFont=''):
512                 self.name=name
513                 self.flag=flag
514                 self.height=height
515                 self.widthFactor=widthFactor
516                 self.obliqueAngle=obliqueAngle
517                 self.mirror=mirror
518                 self.lastHeight=lastHeight
519                 self.font=font
520                 self.bigFont=bigFont
521         def __str__(self):
522                 return '  0\nSTYLE\n  2\n%s\n 70\n%s\n 40\n%s\n 41\n%s\n 50\n%s\n 71\n%s\n 42\n%s\n 3\n%s\n 4\n%s\n'%\
523                            (self.name.upper(),self.flag,self.flag,self.widthFactor,
524                                 self.obliqueAngle,self.mirror,self.lastHeight,
525                                 self.font.upper(),self.bigFont.upper())
526
527 #-----------------------------------------------
528 class View(_Call):
529         def __init__(self,name,flag=0,width=1,height=1,center=(0.5,0.5),
530                                  direction=(0,0,1),target=(0,0,0),lens=50,
531                                  frontClipping=0,backClipping=0,twist=0,mode=0):
532                 self.name=name
533                 self.flag=flag
534                 self.width=width
535                 self.height=height
536                 self.center=center
537                 self.direction=direction
538                 self.target=target
539                 self.lens=lens
540                 self.frontClipping=frontClipping
541                 self.backClipping=backClipping
542                 self.twist=twist
543                 self.mode=mode
544         def __str__(self):
545                 return '  0\nVIEW\n  2\n%s\n 70\n%s\n 40\n%s\n%s\n 41\n%s\n%s\n%s\n 42\n%s\n 43\n%s\n 44\n%s\n 50\n%s\n 71\n%s\n'%\
546                            (self.name,self.flag,self.height,_point(self.center),self.width,
547                                 _point(self.direction,1),_point(self.target,2),self.lens,
548                                 self.frontClipping,self.backClipping,self.twist,self.mode)
549
550 #-----------------------------------------------
551 def ViewByWindow(name,leftBottom=(0,0),rightTop=(1,1),**options):
552         width=abs(rightTop[0]-leftBottom[0])
553         height=abs(rightTop[1]-leftBottom[1])
554         center=((rightTop[0]+leftBottom[0])*0.5,(rightTop[1]+leftBottom[1])*0.5)
555         return View(name=name,width=width,height=height,center=center,**options)
556
557 #---drawing
558 #-----------------------------------------------
559 class Drawing(_Collection):
560         """Dxf drawing. Use append or any other list methods to add objects."""
561         def __init__(self,insbase=(0.0,0.0,0.0),extmin=(0.0,0.0,0.0),extmax=(0.0,0.0,0.0),
562                                  layers=[Layer()],linetypes=[LineType()],styles=[Style()],blocks=[],
563                                  views=[],entities=None,fileName='test.dxf'):
564                 # TODO: replace list with None,arial
565                 if not entities:
566                         entities=[]
567                 _Collection.__init__(self,entities)
568                 self.insbase=insbase
569                 self.extmin=extmin
570                 self.extmax=extmax
571                 self.layers=copy.copy(layers)
572                 self.linetypes=copy.copy(linetypes)
573                 self.styles=copy.copy(styles)
574                 self.views=copy.copy(views)
575                 self.blocks=copy.copy(blocks)
576                 self.fileName=fileName
577                 #private
578                 #self.acadver='9\n$ACADVER\n1\nAC1006\n'
579                 self.acadver='  9\n$ACADVER\n  1\nAC1009\n'
580                 """DXF AutoCAD-Release format codes
581                 AC1021  2008, 2007 
582                 AC1018  2006, 2005, 2004 
583                 AC1015  2002, 2000i, 2000 
584                 AC1014  R14,14.01 
585                 AC1012  R13    
586                 AC1009  R12,11 
587                 AC1006  R10    
588                 AC1004  R9    
589                 AC1002  R2.6  
590                 AC1.50  R2.05 
591                 """
592
593         def _name(self,x):
594                 """Helper function for self._point"""
595                 return '  9\n$%s\n' %x.upper()
596
597         def _point(self,name,x):
598                 """Point setting from drawing like extmin,extmax,..."""
599                 return '%s%s' %(self._name(name),_point(x))
600
601         def _section(self,name,x):
602                 """Sections like tables,blocks,entities,..."""
603                 if x: xstr=''.join(x)
604                 else: xstr=''
605                 return '  0\nSECTION\n  2\n%s\n%s  0\nENDSEC\n'%(name.upper(),xstr)
606
607         def _table(self,name,x):
608                 """Tables like ltype,layer,style,..."""
609                 if x: xstr=''.join(x)
610                 else: xstr=''
611                 return '  0\nTABLE\n  2\n%s\n 70\n%s\n%s  0\nENDTAB\n'%(name.upper(),len(x),xstr)
612
613         def __str__(self):
614                 """Returns drawing as dxf string."""
615                 header=[self.acadver]+[self._point(attr,getattr(self,attr))+'\n' for attr in _HEADER_POINTS]
616                 header=self._section('header',header)
617
618                 tables=[self._table('ltype',[str(x) for x in self.linetypes]),
619                                 self._table('layer',[str(x) for x in self.layers]),
620                                 self._table('style',[str(x) for x in self.styles]),
621                                 self._table('view',[str(x) for x in self.views]),
622                 ]
623                 tables=self._section('tables',tables)
624
625                 blocks=self._section('blocks',[str(x) for x in self.blocks])
626
627                 entities=self._section('entities',[str(x) for x in self.entities])
628
629                 all=''.join([header,tables,blocks,entities,'  0\nEOF\n'])
630                 return all
631
632         def saveas(self,fileName):
633                 self.fileName=fileName
634                 self.save()
635
636         def save(self):
637                 test=open(self.fileName,'w')
638                 test.write(str(self))
639                 test.close()
640
641
642 #---extras
643 #-----------------------------------------------
644 class Rectangle(_Entity):
645         """Rectangle, creates lines."""
646         def __init__(self,point=(0,0,0),width=1,height=1,solid=None,line=1,**common):
647                 _Entity.__init__(self,**common)
648                 self.point=point
649                 self.width=width
650                 self.height=height
651                 self.solid=solid
652                 self.line=line
653         def __str__(self):
654                 result=''
655                 points=[self.point,(self.point[0]+self.width,self.point[1],self.point[2]),
656                         (self.point[0]+self.width,self.point[1]+self.height,self.point[2]),
657                         (self.point[0],self.point[1]+self.height,self.point[2]),self.point]
658                 if self.solid:
659                         result+= Solid(points=points[:-1],parent=self.solid)
660                 if self.line:
661                         for i in range(4):
662                                 result+= Line(points=[points[i],points[i+1]],parent=self)
663                 return result[1:]
664
665 #-----------------------------------------------
666 class LineList(_Entity):
667         """Like polyline, but built of individual lines."""
668         def __init__(self,points=[],org_point=[0,0,0],closed=0,**common):
669                 _Entity.__init__(self,**common)
670                 self.closed=closed
671                 self.points=copy.copy(points)
672         def __str__(self):
673                 if self.closed:points=self.points+[self.points[0]]
674                 else: points=self.points
675                 result=''
676                 for i in range(len(points)-1):
677                         result+= Line(points=[points[i],points[i+1]],parent=self)
678                 return result[1:]
679
680 #-----------------------------------------------------
681 def test():
682         #Blocks
683         b=Block('test')
684         b.append(Solid(points=[(0,0,0),(1,0,0),(1,1,0),(0,1,0)],color=1))
685         b.append(Arc(center=(1,0,0),color=2))
686
687         #Drawing
688         d=Drawing()
689         #tables
690         d.blocks.append(b)  #table blocks
691         d.styles.append(Style())  #table styles
692         d.views.append(View('Normal'))  #table view
693         d.views.append(ViewByWindow('Window',leftBottom=(1,0),rightTop=(2,1)))  #idem
694
695         #entities
696         d.append(Circle(center=(1,1,0),color=3))
697         d.append(Face(points=[(0,0,0),(1,0,0),(1,1,0),(0,1,0)],color=4))
698         d.append(Insert('test',point=(3,3,3),cols=5,colspacing=2))
699         d.append(Line(points=[(0,0,0),(1,1,1)]))
700         d.append(Mtext('Click on Ads\nmultiple lines with mtext',point=(1,1,1),color=5,rotation=90))
701         d.append(Text('Please donate!',point=(3,0,1)))
702         d.append(Rectangle(point=(2,2,2),width=4,height=3,color=6,solid=Solid(color=2)))
703         d.append(Solid(points=[(4,4,0),(5,4,0),(7,8,0),(9,9,0)],color=3))
704         d.append(PolyLine(points=[(1,1,1),(2,1,1),(2,2,1),(1,2,1)],closed=1,color=1))
705
706         #d.saveas('c:\\test.dxf')
707         d.saveas('test.dxf')
708
709
710 #-----------------------------------------------------
711 if __name__=='__main__':
712         if not copy:
713                 Draw.PupMenu('Error%t|This script requires a full python install')
714         main()
715