1 #dxfLibrary.py : provides functions for generating DXF files
2 # --------------------------------------------------------------------------
3 __version__
= "v1.35 - 2010.06.23"
4 __author__
= "Stani Michiels(Stani), Remigiusz Fiedler(migius)"
6 __url__
= "http://wiki.blender.org/index.php/Extensions:2.4/Py/Scripts/Export/DXF"
7 __bpydoc__
="""The library to export geometry data to DXF format r12 version.
14 See the homepage for documentation.
15 Dedicated thread on BlenderArtists: http://blenderartists.org/forum/showthread.php?t=136439
21 - add support for DXFr14 version (needs extended file header)
22 - add support for DXF-SPLINEs (possible first in DXFr14 version)
23 - add support for DXF-MTEXT
24 - add user preset for floating point precision (3-16?)
27 v1.35 - 2010.06.23 by migius
28 - added (as default) writing to DXF file without RAM-buffering: faster and low-RAM-machines friendly
29 v1.34 - 2010.06.20 by migius
31 - added DXF-flags for POLYLINE and VERTEX class (NURBS-export)
32 v1.33 - 2009.07.03 by migius
33 - fix MTEXT newline bug (not supported by DXF-Exporter yet)
34 - modif _point(): converts all coords to floats
35 - modif LineType class: implement elements
36 - added VPORT class, incl. defaults
38 v1.32 - 2009.06.06 by migius
39 - modif Style class: changed defaults to widthFactor=1.0, obliqueAngle=0.0
40 - modif Text class: alignment parameter reactivated
41 v1.31 - 2009.06.02 by migius
42 - modif _Entity class: added paperspace,elevation
43 v1.30 - 2009.05.28 by migius
44 - bugfix 3dPOLYLINE/POLYFACE: VERTEX needs x,y,z coordinates, index starts with 1 not 0
45 v1.29 - 2008.12.28 by Yorik
46 - modif POLYLINE to support bulge segments
47 v1.28 - 2008.12.13 by Steeve/BlenderArtists
48 - bugfix for EXTMIN/EXTMAX to suit Cycas-CAD
49 v1.27 - 2008.10.07 by migius
50 - beautifying output code: keys whitespace prefix
51 - refactoring DXF-strings format: NewLine moved to the end of
52 v1.26 - 2008.10.05 by migius
53 - modif POLYLINE to support POLYFACE
54 v1.25 - 2008.09.28 by migius
55 - modif FACE class for r12
56 v1.24 - 2008.09.27 by migius
57 - modif POLYLINE class for r12
58 - changing output format from r9 to r12(AC1009)
59 v1.1 (20/6/2005) by www.stani.be/python/sdxf
60 - Python library to generate dxf drawings
61 ______________________________________________________________
62 """ % (__author__
,__version__
,__license__
,__url__
)
64 # --------------------------------------------------------------------------
65 # DXF Library: copyright (C) 2005 by Stani Michiels (AKA Stani)
66 # 2008/2009 modif by Remigiusz Fiedler (AKA migius)
67 # --------------------------------------------------------------------------
68 # ***** BEGIN GPL LICENSE BLOCK *****
70 # This program is free software; you can redistribute it and/or
71 # modify it under the terms of the GNU General Public License
72 # as published by the Free Software Foundation; either version 2
73 # of the License, or (at your option) any later version.
75 # This program is distributed in the hope that it will be useful,
76 # but WITHOUT ANY WARRANTY; without even the implied warranty of
77 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
78 # GNU General Public License for more details.
80 # You should have received a copy of the GNU General Public License
81 # along with this program; if not, write to the Free Software Foundation,
82 # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
84 # ***** END GPL LICENCE BLOCK *****
88 #from Blender import Mathutils, Window, Scene, sys, Draw
93 #from struct import pack
97 ####1) Private (only for developers)
98 _HEADER_POINTS
=['insbase','extmin','extmax']
100 #---helper functions-----------------------------------
101 def _point(x
,index
=0):
102 """Convert tuple to a dxf point"""
103 return '\n'.join([' %s\n%s'%((i
+1)*10+index
,float(x
[i
])) for i
in range(len(x
))])
106 """Convert a list of tuples to dxf points"""
107 out
= '\n'.join([_point(plist
[i
],i
)for i
in range(len(plist
))])
110 #---base classes----------------------------------------
112 """Makes a callable class."""
114 """Returns a copy."""
115 return copy
.deepcopy(self
)
117 def __call__(self
,**attrs
):
118 """Returns a copy with modified attributes."""
120 for attr
in attrs
:setattr(copied
,attr
,attrs
[attr
])
123 #-------------------------------------------------------
124 class _Entity(_Call
):
125 """Base class for _common group codes for entities."""
126 def __init__(self
,paperspace
=None,color
=None,layer
='0',
127 lineType
=None,lineTypeScale
=None,lineWeight
=None,
128 extrusion
=None,elevation
=None,thickness
=None,
130 """None values will be omitted."""
131 self
.paperspace
= paperspace
134 self
.lineType
= lineType
135 self
.lineTypeScale
= lineTypeScale
136 self
.lineWeight
= lineWeight
137 self
.extrusion
= extrusion
138 self
.elevation
= elevation
139 self
.thickness
= thickness
140 #self.visible = visible
144 """Return common group codes as a string."""
145 if self
.parent
:parent
=self
.parent
148 if parent
.paperspace
==1: result
+=' 67\n1\n'
149 if parent
.layer
!=None: result
+=' 8\n%s\n'%parent
.layer
150 if parent
.color
!=None: result
+=' 62\n%s\n'%parent
.color
151 if parent
.lineType
!=None: result
+=' 6\n%s\n'%parent
.lineType
152 # TODO: if parent.lineWeight!=None: result+='370\n%s\n'%parent.lineWeight
153 # TODO: if parent.visible!=None: result+='60\n%s\n'%parent.visible
154 if parent
.lineTypeScale
!=None: result
+=' 48\n%s\n'%parent
.lineTypeScale
155 if parent
.elevation
!=None: result
+=' 38\n%s\n'%parent
.elevation
156 if parent
.thickness
!=None: result
+=' 39\n%s\n'%parent
.thickness
157 if parent
.extrusion
!=None: result
+='%s\n'%_point(parent
.extrusion
,200)
160 #--------------------------
162 """Base class to deal with composed objects."""
167 return ''.join([str(x
) for x
in self
.__dxf
__()])
169 #--------------------------
170 class _Collection(_Call
):
171 """Base class to expose entities methods to main object."""
172 def __init__(self
,entities
=[]):
173 self
.entities
=copy
.copy(entities
)
174 #link entities methods to drawing
175 for attr
in dir(self
.entities
):
177 attrObject
=getattr(self
.entities
,attr
)
178 if callable(attrObject
):
179 setattr(self
,attr
,attrObject
)
186 #---block-type flags (bit coded values, may be combined):
187 ANONYMOUS
=1 # This is an anonymous block generated by hatching, associative dimensioning, other internal operations, or an application
188 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)
189 XREF
=4 # This block is an external reference (xref)
190 XREF_OVERLAY
=8 # This block is an xref overlay
191 EXTERNAL
=16 # This block is externally dependent
192 RESOLVED
=32 # This is a resolved external reference, or dependent of an external reference (ignored on input)
193 REFERENCED
=64 # This definition is a referenced external reference (ignored on input)
209 BY_STYLE
= 5 #the flow direction is inherited from the associated text style
210 #line spacing style (optional):
211 AT_LEAST
= 1 #taller characters will override
212 EXACT
= 2 #taller characters will not override
215 CLOSED
=1 # This is a closed polyline (or a polygon mesh closed in the M direction)
216 CURVE_FIT
=2 # Curve-fit vertices have been added
217 SPLINE_FIT
=4 # Spline-fit vertices have been added
218 POLYLINE_3D
=8 # This is a 3D polyline
219 POLYGON_MESH
=16 # This is a 3D polygon mesh
220 CLOSED_N
=32 # The polygon mesh is closed in the N direction
221 POLYFACE_MESH
=64 # The polyline is a polyface mesh
222 CONTINOUS_LINETYPE_PATTERN
=128 # The linetype pattern is generated continuously around the vertices of this polyline
224 #---polyline flag 75, = curve type
235 ALIGNED
= 3 #if vertical alignment = 0
236 MIDDLE
= 4 #if vertical alignment = 0
237 FIT
= 5 #if vertical alignment = 0
245 #---entitities -----------------------------------------------
246 #--------------------------
248 """Arc, angles in degrees."""
249 def __init__(self
,center
=(0,0,0),radius
=1,
250 startAngle
=0.0,endAngle
=90,**common
):
251 """Angles in degrees."""
252 _Entity
.__init
__(self
,**common
)
255 self
.startAngle
=startAngle
256 self
.endAngle
=endAngle
258 return ' 0\nARC\n%s%s\n 40\n%s\n 50\n%s\n 51\n%s\n'%\
259 (self
._common
(),_point(self
.center
),
260 self
.radius
,self
.startAngle
,self
.endAngle
)
262 #-----------------------------------------------
263 class Circle(_Entity
):
265 def __init__(self
,center
=(0,0,0),radius
=1,**common
):
266 _Entity
.__init
__(self
,**common
)
270 return ' 0\nCIRCLE\n%s%s\n 40\n%s\n'%\
271 (self
._common
(),_point(self
.center
),self
.radius
)
273 #-----------------------------------------------
276 def __init__(self
,points
,**common
):
277 _Entity
.__init
__(self
,**common
)
278 while len(points
)<4: #fix for r12 format
279 points
.append(points
[-1])
283 out
= ' 0\n3DFACE\n%s%s\n' %(self
._common
(),_points(self
.points
))
286 #-----------------------------------------------
287 class Insert(_Entity
):
288 """Block instance."""
289 def __init__(self
,name
,point
=(0,0,0),
290 xscale
=None,yscale
=None,zscale
=None,
291 cols
=None,colspacing
=None,rows
=None,rowspacing
=None,
294 _Entity
.__init
__(self
,**common
)
301 self
.colspacing
=colspacing
303 self
.rowspacing
=rowspacing
304 self
.rotation
=rotation
307 result
=' 0\nINSERT\n 2\n%s\n%s%s\n'%\
308 (self
.name
,self
._common
(),_point(self
.point
))
309 if self
.xscale
!=None:result
+=' 41\n%s\n'%self
.xscale
310 if self
.yscale
!=None:result
+=' 42\n%s\n'%self
.yscale
311 if self
.zscale
!=None:result
+=' 43\n%s\n'%self
.zscale
312 if self
.rotation
:result
+=' 50\n%s\n'%self
.rotation
313 if self
.cols
!=None:result
+=' 70\n%s\n'%self
.cols
314 if self
.colspacing
!=None:result
+=' 44\n%s\n'%self
.colspacing
315 if self
.rows
!=None:result
+=' 71\n%s\n'%self
.rows
316 if self
.rowspacing
!=None:result
+=' 45\n%s\n'%self
.rowspacing
319 #-----------------------------------------------
322 def __init__(self
,points
,**common
):
323 _Entity
.__init
__(self
,**common
)
326 return ' 0\nLINE\n%s%s\n' %(
327 self
._common
(), _points(self
.points
))
330 #-----------------------------------------------
331 class PolyLine(_Entity
):
332 def __init__(self
,points
,org_point
=[0,0,0],flag70
=0,flag75
=0,width
=None,**common
):
333 #width = number, or width = list [width_start=None, width_end=None]
334 #for 2d-polyline: points = [ [[x, y, z], vflag=None, [width_start=None, width_end=None], bulge=0 or None] ...]
335 #for 3d-polyline: points = [ [[x, y, z], vflag=None], ...]
336 #for polyface: points = [points_list, faces_list]
337 _Entity
.__init
__(self
,**common
)
339 self
.org_point
=org_point
340 self
.pflag70
= flag70
341 self
.pflag75
= flag75
342 self
.polyface
= False
343 self
.polyline2d
= False
344 self
.faces
= [] # dummy value
345 self
.width
= None # dummy value
346 if self
.pflag70
& POLYFACE_MESH
:
348 self
.points
=points
[0]
350 self
.p_count
=len(self
.points
)
351 self
.f_count
=len(self
.faces
)
352 elif not (self
.pflag70
& POLYLINE_3D
):
353 self
.polyline2d
= True
355 if type(width
)!='list': width
=[width
,width
]
359 result
= ' 0\nPOLYLINE\n%s 70\n%s\n' %(self
._common
(),self
.pflag70
)
361 result
+='%s\n' %_point(self
.org_point
)
363 result
+=' 71\n%s\n' %self
.p_count
364 result
+=' 72\n%s\n' %self
.f_count
365 elif self
.polyline2d
:
366 if self
.width
!=None: result
+=' 40\n%s\n 41\n%s\n' %(self
.width
[0],self
.width
[1])
368 result
+=' 75\n%s\n' %self
.pflag75
369 for point
in self
.points
:
370 result
+=' 0\nVERTEX\n'
371 result
+=' 8\n%s\n' %self
.layer
373 result
+='%s\n' %_point(point
)
375 elif self
.polyline2d
:
376 result
+='%s\n' %_point(point
[0])
379 [width1
, width2
] = point
[2]
380 if width1
!=None: result
+=' 40\n%s\n' %width1
381 if width2
!=None: result
+=' 41\n%s\n' %width2
384 if bulge
: result
+=' 42\n%s\n' %bulge
386 result
+=' 70\n%s\n' %flag
388 result
+='%s\n' %_point(point
[0])
391 result
+=' 70\n%s\n' %flag
393 for face
in self
.faces
:
394 result
+=' 0\nVERTEX\n'
395 result
+=' 8\n%s\n' %self
.layer
396 result
+='%s\n' %_point(self
.org_point
)
398 result
+=' 71\n%s\n' %face
[0]
399 result
+=' 72\n%s\n' %face
[1]
400 result
+=' 73\n%s\n' %face
[2]
401 if len(face
)==4: result
+=' 74\n%s\n' %face
[3]
402 result
+=' 0\nSEQEND\n'
403 result
+=' 8\n%s\n' %self
.layer
406 #-----------------------------------------------
407 class Point(_Entity
):
409 def __init__(self
,points
=None,**common
):
410 _Entity
.__init
__(self
,**common
)
412 def __str__(self
): # TODO:
413 return ' 0\nPOINT\n%s%s\n' %(self
._common
(),
417 #-----------------------------------------------
418 class Solid(_Entity
):
419 """Colored solid fill."""
420 def __init__(self
,points
=None,**common
):
421 _Entity
.__init
__(self
,**common
)
424 return ' 0\nSOLID\n%s%s\n' %(self
._common
(),
425 _points(self
.points
[:2]+[self
.points
[3],self
.points
[2]])
429 #-----------------------------------------------
431 """Single text line."""
432 def __init__(self
,text
='',point
=(0,0,0),alignment
=None,
433 flag
=None,height
=1,justifyhor
=None,justifyver
=None,
434 rotation
=None,obliqueAngle
=None,style
=None,xscale
=None,**common
):
435 _Entity
.__init
__(self
,**common
)
438 self
.alignment
=alignment
441 self
.justifyhor
=justifyhor
442 self
.justifyver
=justifyver
443 self
.rotation
=rotation
444 self
.obliqueAngle
=obliqueAngle
448 result
= ' 0\nTEXT\n%s%s\n 40\n%s\n 1\n%s\n'%\
449 (self
._common
(),_point(self
.point
),self
.height
,self
.text
)
450 if self
.rotation
: result
+=' 50\n%s\n'%self
.rotation
451 if self
.xscale
: result
+=' 41\n%s\n'%self
.xscale
452 if self
.obliqueAngle
: result
+=' 51\n%s\n'%self
.obliqueAngle
453 if self
.style
: result
+=' 7\n%s\n'%self
.style
454 if self
.flag
: result
+=' 71\n%s\n'%self
.flag
455 if self
.justifyhor
: result
+=' 72\n%s\n'%self
.justifyhor
456 if self
.alignment
: result
+='%s\n'%_point(self
.alignment
,1)
457 if self
.justifyver
: result
+=' 73\n%s\n'%self
.justifyver
460 #-----------------------------------------------
462 """Surrogate for mtext, generates some Text instances."""
463 def __init__(self
,text
='',point
=(0,0,0),width
=250,spacingFactor
=1.5,down
=0,spacingWidth
=None,**options
):
464 Text
.__init
__(self
,text
=text
,point
=point
,**options
)
465 if down
:spacingFactor
*=-1
466 self
.spacingFactor
=spacingFactor
467 self
.spacingWidth
=spacingWidth
471 texts
=self
.text
.replace('\r\n','\n').split('\n')
472 if not self
.down
:texts
.reverse()
475 if self
.spacingWidth
:spacingWidth
=self
.spacingWidth
476 else:spacingWidth
=self
.height
*self
.spacingFactor
479 result
+='%s' %Text(text
[:self
.width
],
480 point
=(self
.point
[0]+x
*spacingWidth
,
481 self
.point
[1]+y
*spacingWidth
,
483 alignment
=self
.alignment
,flag
=self
.flag
,height
=self
.height
,
484 justifyhor
=self
.justifyhor
,justifyver
=self
.justifyver
,
485 rotation
=self
.rotation
,obliqueAngle
=self
.obliqueAngle
,
486 style
=self
.style
,xscale
=self
.xscale
,parent
=self
488 text
=text
[self
.width
:]
489 if self
.rotation
:x
+=1
493 #-----------------------------------------------
494 ##class _Mtext(_Entity):
495 """Mtext not functioning for minimal dxf."""
497 def __init__(self,text='',point=(0,0,0),attachment=1,
498 charWidth=None,charHeight=1,direction=1,height=100,rotation=0,
499 spacingStyle=None,spacingFactor=None,style=None,width=100,
500 xdirection=None,**common):
501 _Entity.__init__(self,**common)
504 self.attachment=attachment
505 self.charWidth=charWidth
506 self.charHeight=charHeight
507 self.direction=direction
509 self.rotation=rotation
510 self.spacingStyle=spacingStyle
511 self.spacingFactor=spacingFactor
514 self.xdirection=xdirection
518 while len(input)>250:
519 text+='3\n%s\n'%input[:250]
521 text+='1\n%s\n'%input
522 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'%\
523 (self._common(),_point(self.point),self.charHeight,self.width,
524 self.attachment,self.direction,text,
527 if self.style:result+='7\n%s\n'%self.style
528 if self.xdirection:result+='%s\n'%_point(self.xdirection,1)
529 if self.charWidth:result+='42\n%s\n'%self.charWidth
530 if self.spacingStyle:result+='73\n%s\n'%self.spacingStyle
531 if self.spacingFactor:result+='44\n%s\n'%self.spacingFactor
536 #---tables ---------------------------------------------------
537 #-----------------------------------------------
538 class Block(_Collection
):
539 """Use list methods to add entities, eg append."""
540 def __init__(self
,name
,layer
='0',flag
=0,base
=(0,0,0),entities
=[]):
541 self
.entities
=copy
.copy(entities
)
542 _Collection
.__init
__(self
,entities
)
547 def __str__(self
): # TODO:
548 e
=''.join([str(x
)for x
in self
.entities
])
549 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'%\
550 (self
.layer
,self
.name
.upper(),self
.flag
,_point(self
.base
),self
.name
.upper(),e
)
552 #-----------------------------------------------
555 def __init__(self
,name
='pydxf',color
=7,lineType
='continuous',flag
=64):
558 self
.lineType
=lineType
561 return ' 0\nLAYER\n 2\n%s\n 70\n%s\n 62\n%s\n 6\n%s\n'%\
562 (self
.name
.upper(),self
.flag
,self
.color
,self
.lineType
)
564 #-----------------------------------------------
565 class LineType(_Call
):
566 """Custom linetype"""
567 def __init__(self
,name
='CONTINUOUS',description
='Solid line',elements
=[0.0],flag
=0):
569 self
.description
=description
570 self
.elements
=copy
.copy(elements
)
573 result
= ' 0\nLTYPE\n 2\n%s\n 70\n%s\n 3\n%s\n 72\n65\n'%\
574 (self
.name
.upper(),self
.flag
,self
.description
)
576 elements
= ' 73\n%s\n' %(len(self
.elements
)-1)
577 elements
+= ' 40\n%s\n' %(self
.elements
[0])
578 for e
in self
.elements
[1:]:
579 elements
+= ' 49\n%s\n' %e
584 #-----------------------------------------------
587 def __init__(self
,name
='standard',flag
=0,height
=0,widthFactor
=1.0,obliqueAngle
=0.0,
588 mirror
=0,lastHeight
=1,font
='arial.ttf',bigFont
=''):
592 self
.widthFactor
=widthFactor
593 self
.obliqueAngle
=obliqueAngle
595 self
.lastHeight
=lastHeight
599 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'%\
600 (self
.name
.upper(),self
.flag
,self
.flag
,self
.widthFactor
,
601 self
.obliqueAngle
,self
.mirror
,self
.lastHeight
,
602 self
.font
.upper(),self
.bigFont
.upper())
604 #-----------------------------------------------
606 def __init__(self
,name
,flag
=0,
607 leftBottom
=(0.0,0.0),
611 snap_spacing
=(0.1,0.1),
612 grid_spacing
=(0.1,0.1),
613 direction
=(0.0,0.0,1.0),
614 target
=(0.0,0.0,0.0),
633 self
.leftBottom
=leftBottom
634 self
.rightTop
=rightTop
636 self
.snap_base
=snap_base
637 self
.snap_spacing
=snap_spacing
638 self
.grid_spacing
=grid_spacing
639 self
.direction
=direction
641 self
.height
=float(height
)
642 self
.ratio
=float(ratio
)
643 self
.lens
=float(lens
)
644 self
.frontClipping
=float(frontClipping
)
645 self
.backClipping
=float(backClipping
)
646 self
.snap_rotation
=float(snap_rotation
)
647 self
.twist
=float(twist
)
649 self
.circle_zoom
=circle_zoom
650 self
.fast_zoom
=fast_zoom
654 self
.snap_style
=snap_style
655 self
.snap_isopair
=snap_isopair
657 output
= [' 0', 'VPORT',
660 _point(self
.leftBottom
),
661 _point(self
.rightTop
,1),
662 _point(self
.center
,2), # View center point (in DCS)
663 _point(self
.snap_base
,3),
664 _point(self
.snap_spacing
,4),
665 _point(self
.grid_spacing
,5),
666 _point(self
.direction
,6), #view direction from target (in WCS)
667 _point(self
.target
,7),
671 ' 43', self
.frontClipping
,
672 ' 44', self
.backClipping
,
673 ' 50', self
.snap_rotation
,
676 ' 72', self
.circle_zoom
,
677 ' 73', self
.fast_zoom
,
681 ' 77', self
.snap_style
,
682 ' 78', self
.snap_isopair
687 output_str
+= '%s\n' %s
692 #-----------------------------------------------
694 def __init__(self
,name
,flag
=0,
698 direction
=(0.0,0.0,1.0),
699 target
=(0.0,0.0,0.0),
707 self
.width
=float(width
)
708 self
.height
=float(height
)
710 self
.direction
=direction
712 self
.lens
=float(lens
)
713 self
.frontClipping
=float(frontClipping
)
714 self
.backClipping
=float(backClipping
)
715 self
.twist
=float(twist
)
718 output
= [' 0', 'VIEW',
724 _point(self
.direction
,1),
725 _point(self
.target
,2),
727 ' 43', self
.frontClipping
,
728 ' 44', self
.backClipping
,
734 output_str
+= '%s\n' %s
737 #-----------------------------------------------
738 def ViewByWindow(name
,leftBottom
=(0,0),rightTop
=(1,1),**options
):
739 width
=abs(rightTop
[0]-leftBottom
[0])
740 height
=abs(rightTop
[1]-leftBottom
[1])
741 center
=((rightTop
[0]+leftBottom
[0])*0.5,(rightTop
[1]+leftBottom
[1])*0.5)
742 return View(name
=name
,width
=width
,height
=height
,center
=center
,**options
)
745 #-----------------------------------------------
746 class Drawing(_Collection
):
747 """Dxf drawing. Use append or any other list methods to add objects."""
748 def __init__(self
,insbase
=(0.0,0.0,0.0),extmin
=(0.0,0.0,0.0),extmax
=(0.0,0.0,0.0),
749 layers
=[Layer()],linetypes
=[LineType()],styles
=[Style()],blocks
=[],
750 views
=[],vports
=[],entities
=None,fileName
='test.dxf'):
751 # TODO: replace list with None,arial
754 _Collection
.__init
__(self
,entities
)
758 self
.layers
=copy
.copy(layers
)
759 self
.linetypes
=copy
.copy(linetypes
)
760 self
.styles
=copy
.copy(styles
)
761 self
.views
=copy
.copy(views
)
762 self
.vports
=copy
.copy(vports
)
763 self
.blocks
=copy
.copy(blocks
)
764 self
.fileName
=fileName
765 #print 'deb: blocks=',blocks #----------
767 #self.acadver='9\n$ACADVER\n1\nAC1006\n'
768 self
.acadver
=' 9\n$ACADVER\n 1\nAC1009\n'
769 """DXF AutoCAD-Release format codes:
771 AC1018 2006, 2005, 2004
772 AC1015 2002, 2000i, 2000
783 """Helper function for self._point"""
784 return ' 9\n$%s\n' %x.upper()
786 def _point(self
,name
,x
):
787 """Point setting from drawing like extmin,extmax,..."""
788 return '%s%s' %(self
._name
(name
),_point(x
))
790 def _section(self
,name
,x
):
791 """Sections like tables,blocks,entities,..."""
792 if x
: xstr
=''.join(x
)
794 return ' 0\nSECTION\n 2\n%s\n%s 0\nENDSEC\n'%(name
.upper(),xstr
)
796 def _table(self
,name
,x
):
797 """Tables like ltype,layer,style,..."""
798 if x
: xstr
=''.join(x
)
800 return ' 0\nTABLE\n 2\n%s\n 70\n%s\n%s 0\nENDTAB\n'%(name
.upper(),len(x
),xstr
)
803 """Returns drawing as dxf string."""
804 header
=[self
.acadver
]+[self
._point
(attr
,getattr(self
,attr
))+'\n' for attr
in _HEADER_POINTS
]
805 header
=self
._section
('header',header
)
807 tables
=[self
._table
('vport',[str(x
) for x
in self
.vports
]),
808 self
._table
('ltype',[str(x
) for x
in self
.linetypes
]),
809 self
._table
('layer',[str(x
) for x
in self
.layers
]),
810 self
._table
('style',[str(x
) for x
in self
.styles
]),
811 self
._table
('view',[str(x
) for x
in self
.views
]),
813 tables
=self
._section
('tables',tables
)
814 blocks
=self
._section
('blocks',[str(x
) for x
in self
.blocks
])
815 entities
=self
._section
('entities',[str(x
) for x
in self
.entities
])
816 all
=''.join([header
,tables
,blocks
,entities
,' 0\nEOF\n'])
819 def _write_section(self
,file,name
,data
):
820 file.write(' 0\nSECTION\n 2\n%s\n'%name
.upper())
823 file.write(' 0\nENDSEC\n')
825 def saveas(self
,fileName
,buffer=0):
826 """Writes DXF file. Needs target file name. If optional parameter buffer>0, then switch to old behavior: store entire output string in RAM.
828 self
.fileName
=fileName
829 if buffer: self
.save()
833 outfile
=open(self
.fileName
,'w')
834 outfile
.write(str(self
))
838 outfile
=open(self
.fileName
,'w')
839 header
=[self
.acadver
]+[self
._point
(attr
,getattr(self
,attr
))+'\n' for attr
in _HEADER_POINTS
]
840 self
._write
_section
(outfile
,'header',header
)
841 tables
=[self
._table
('vport',[str(x
) for x
in self
.vports
]),
842 self
._table
('ltype',[str(x
) for x
in self
.linetypes
]),
843 self
._table
('layer',[str(x
) for x
in self
.layers
]),
844 self
._table
('style',[str(x
) for x
in self
.styles
]),
845 self
._table
('view',[str(x
) for x
in self
.views
]),
847 self
._write
_section
(outfile
,'tables',tables
)
848 self
._write
_section
(outfile
,'blocks',self
.blocks
)
849 self
._write
_section
(outfile
,'entities',self
.entities
)
850 outfile
.write(' 0\nEOF\n')
855 #-----------------------------------------------
856 class Rectangle(_Entity
):
857 """Rectangle, creates lines."""
858 def __init__(self
,point
=(0,0,0),width
=1,height
=1,solid
=None,line
=1,**common
):
859 _Entity
.__init
__(self
,**common
)
867 points
=[self
.point
,(self
.point
[0]+self
.width
,self
.point
[1],self
.point
[2]),
868 (self
.point
[0]+self
.width
,self
.point
[1]+self
.height
,self
.point
[2]),
869 (self
.point
[0],self
.point
[1]+self
.height
,self
.point
[2]),self
.point
]
871 result
+= Solid(points
=points
[:-1],parent
=self
.solid
)
874 result
+= Line(points
=[points
[i
],points
[i
+1]],parent
=self
)
877 #-----------------------------------------------
878 class LineList(_Entity
):
879 """Like polyline, but built of individual lines."""
880 def __init__(self
,points
=[],org_point
=[0,0,0],closed
=0,**common
):
881 _Entity
.__init
__(self
,**common
)
883 self
.points
=copy
.copy(points
)
886 points
=self
.points
+[self
.points
[0]]
887 else: points
=self
.points
889 for i
in range(len(points
)-1):
890 result
+= Line(points
=[points
[i
][0],points
[i
+1][0]],parent
=self
)
893 #-----------------------------------------------------
897 b
.append(Solid(points
=[(0,0,0),(1,0,0),(1,1,0),(0,1,0)],color
=1))
898 b
.append(Arc(center
=(1,0,0),color
=2))
903 d
.blocks
.append(b
) #table blocks
904 d
.styles
.append(Style()) #table styles
905 d
.views
.append(View('Normal')) #table view
906 d
.views
.append(ViewByWindow('Window',leftBottom
=(1,0),rightTop
=(2,1))) #idem
909 d
.append(Circle(center
=(1,1,0),color
=3))
910 d
.append(Face(points
=[(0,0,0),(1,0,0),(1,1,0),(0,1,0)],color
=4))
911 d
.append(Insert('test',point
=(3,3,3),cols
=5,colspacing
=2))
912 d
.append(Line(points
=[(0,0,0),(1,1,1)]))
913 d
.append(Mtext('Click on Ads\nmultiple lines with mtext',point
=(1,1,1),color
=5,rotation
=90))
914 d
.append(Text('Please donate!',point
=(3,0,1)))
915 #d.append(Rectangle(point=(2,2,2),width=4,height=3,color=6,solid=Solid(color=2)))
916 d
.append(Solid(points
=[(4,4,0),(5,4,0),(7,8,0),(9,9,0)],color
=3))
917 #d.append(PolyLine(points=[(1,1,1),(2,1,1),(2,2,1),(1,2,1)],flag=1,color=1))
919 #d.saveas('c:\\test.dxf')
922 #-----------------------------------------------------
923 if __name__
=='__main__':
925 Draw
.PupMenu('Error%t|This script requires a full python install')