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