~ubuntu-branches/ubuntu/trusty/blender/trusty

« back to all changes in this revision

Viewing changes to release/scripts/addons/io_export_dxf/model/dxfLibrary.py

  • Committer: Package Import Robot
  • Author(s): Jeremy Bicha
  • Date: 2013-03-06 12:08:47 UTC
  • mfrom: (1.5.1) (14.1.8 experimental)
  • Revision ID: package-import@ubuntu.com-20130306120847-frjfaryb2zrotwcg
Tags: 2.66a-1ubuntu1
* Resynchronize with Debian (LP: #1076930, #1089256, #1052743, #999024,
  #1122888, #1147084)
* debian/control:
  - Lower build-depends on libavcodec-dev since we're not
    doing the libav9 transition in Ubuntu yet

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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)"
 
5
__license__ = "GPL"
 
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.
 
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 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?)
 
25
 
 
26
History
 
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
 
30
 - bugfix POLYFACE
 
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
 
37
 - fix Insert class
 
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__)
 
63
 
 
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 *****
 
69
#
 
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.
 
74
#
 
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.
 
79
#
 
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.
 
83
#
 
84
# ***** END GPL LICENCE BLOCK *****
 
85
 
 
86
 
 
87
#import Blender
 
88
#from Blender import Mathutils, Window, Scene, sys, Draw
 
89
#import BPyMessages
 
90
 
 
91
try:
 
92
        import copy
 
93
        #from struct import pack
 
94
except:
 
95
        copy = None
 
96
 
 
97
####1) Private (only for developpers)
 
98
_HEADER_POINTS=['insbase','extmin','extmax']
 
99
 
 
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))])
 
104
 
 
105
def _points(plist):
 
106
        """Convert a list of tuples to dxf points"""
 
107
        out = '\n'.join([_point(plist[i],i)for i in range(len(plist))])
 
108
        return out
 
109
 
 
110
#---base classes----------------------------------------
 
111
class _Call:
 
112
        """Makes a callable class."""
 
113
        def copy(self):
 
114
                """Returns a copy."""
 
115
                return copy.deepcopy(self)
 
116
 
 
117
        def __call__(self,**attrs):
 
118
                """Returns a copy with modified attributes."""
 
119
                copied=self.copy()
 
120
                for attr in attrs:setattr(copied,attr,attrs[attr])
 
121
                return copied
 
122
 
 
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,
 
129
                                 parent=None):
 
130
                """None values will be omitted."""
 
131
                self.paperspace   = paperspace
 
132
                self.color                = color
 
133
                self.layer                = layer
 
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
 
141
                self.parent              = parent
 
142
 
 
143
        def _common(self):
 
144
                """Return common group codes as a string."""
 
145
                if self.parent:parent=self.parent
 
146
                else:parent=self
 
147
                result =''
 
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)
 
158
                return result
 
159
 
 
160
#--------------------------
 
161
class _Entities:
 
162
        """Base class to deal with composed objects."""
 
163
        def __dxf__(self):
 
164
                return []
 
165
 
 
166
        def __str__(self):
 
167
                return ''.join([str(x) for x in self.__dxf__()])
 
168
 
 
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):
 
176
                        if attr[0]!='_':
 
177
                                attrObject=getattr(self.entities,attr)
 
178
                                if callable(attrObject):
 
179
                                        setattr(self,attr,attrObject)
 
180
 
 
181
####2) Constants
 
182
#---color values
 
183
BYBLOCK=0
 
184
BYLAYER=256
 
185
 
 
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)
 
194
 
 
195
#---mtext flags
 
196
#attachment point
 
197
TOP_LEFT = 1
 
198
TOP_CENTER = 2
 
199
TOP_RIGHT = 3
 
200
MIDDLE_LEFT = 4
 
201
MIDDLE_CENTER = 5
 
202
MIDDLE_RIGHT    = 6
 
203
BOTTOM_LEFT = 7
 
204
BOTTOM_CENTER = 8
 
205
BOTTOM_RIGHT = 9
 
206
#drawing direction
 
207
LEFT_RIGHT = 1
 
208
TOP_BOTTOM = 3
 
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
 
213
 
 
214
#---polyline flag 70
 
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
 
223
 
 
224
#---polyline flag 75, = curve type
 
225
QUADRIC_NURBS = 5
 
226
CUBIC_NURBS = 6
 
227
BEZIER_CURVE = 8
 
228
 
 
229
 
 
230
#---text flags
 
231
#horizontal
 
232
LEFT = 0
 
233
CENTER = 1
 
234
RIGHT = 2
 
235
ALIGNED = 3 #if vertical alignment = 0
 
236
MIDDLE = 4 #if vertical alignment = 0
 
237
FIT = 5 #if vertical alignment = 0
 
238
#vertical
 
239
BASELINE = 0
 
240
BOTTOM  = 1
 
241
MIDDLE = 2
 
242
TOP = 3
 
243
 
 
244
####3) Classes
 
245
#---entitities -----------------------------------------------
 
246
#--------------------------
 
247
class Arc(_Entity):
 
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)
 
253
                self.center=center
 
254
                self.radius=radius
 
255
                self.startAngle=startAngle
 
256
                self.endAngle=endAngle
 
257
        def __str__(self):
 
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)
 
261
 
 
262
#-----------------------------------------------
 
263
class Circle(_Entity):
 
264
        """Circle"""
 
265
        def __init__(self,center=(0,0,0),radius=1,**common):
 
266
                _Entity.__init__(self,**common)
 
267
                self.center=center
 
268
                self.radius=radius
 
269
        def __str__(self):
 
270
                return '  0\nCIRCLE\n%s%s\n 40\n%s\n'%\
 
271
                           (self._common(),_point(self.center),self.radius)
 
272
 
 
273
#-----------------------------------------------
 
274
class Face(_Entity):
 
275
        """3dface"""
 
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])
 
280
                self.points=points
 
281
                
 
282
        def __str__(self):
 
283
                out = '  0\n3DFACE\n%s%s\n' %(self._common(),_points(self.points))
 
284
                return out
 
285
 
 
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,
 
292
                                 rotation=None,
 
293
                                 **common):
 
294
                _Entity.__init__(self,**common)
 
295
                self.name=name
 
296
                self.point=point
 
297
                self.xscale=xscale
 
298
                self.yscale=yscale
 
299
                self.zscale=zscale
 
300
                self.cols=cols
 
301
                self.colspacing=colspacing
 
302
                self.rows=rows
 
303
                self.rowspacing=rowspacing
 
304
                self.rotation=rotation
 
305
 
 
306
        def __str__(self):
 
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
 
317
                return result
 
318
 
 
319
#-----------------------------------------------
 
320
class Line(_Entity):
 
321
        """Line"""
 
322
        def __init__(self,points,**common):
 
323
                _Entity.__init__(self,**common)
 
324
                self.points=points
 
325
        def __str__(self):
 
326
                return '  0\nLINE\n%s%s\n' %(
 
327
                                self._common(), _points(self.points))
 
328
 
 
329
 
 
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)
 
338
                self.points=points
 
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:
 
347
                        self.polyface=True
 
348
                        self.points=points[0]
 
349
                        self.faces=points[1]
 
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
 
354
                        if width:
 
355
                                if type(width)!='list':  width=[width,width]
 
356
                                self.width=width
 
357
 
 
358
        def __str__(self):
 
359
                result= '  0\nPOLYLINE\n%s 70\n%s\n' %(self._common(),self.pflag70)
 
360
                result+=' 66\n1\n'
 
361
                result+='%s\n' %_point(self.org_point)
 
362
                if self.polyface:
 
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])
 
367
                if self.pflag75:
 
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
 
372
                        if self.polyface:
 
373
                                result+='%s\n' %_point(point)
 
374
                                result+=' 70\n192\n'
 
375
                        elif self.polyline2d:
 
376
                                result+='%s\n' %_point(point[0])
 
377
                                flag = point[1]
 
378
                                if len(point)>2:
 
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
 
382
                                if len(point)==4:
 
383
                                        bulge = point[3]
 
384
                                        if bulge: result+=' 42\n%s\n' %bulge
 
385
                                if flag:
 
386
                                                result+=' 70\n%s\n' %flag
 
387
                        else:
 
388
                                result+='%s\n' %_point(point[0])
 
389
                                flag = point[1]
 
390
                                if flag:
 
391
                                                result+=' 70\n%s\n' %flag
 
392
 
 
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)
 
397
                        result+=' 70\n128\n'
 
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
 
404
                return result
 
405
 
 
406
#-----------------------------------------------
 
407
class Point(_Entity):
 
408
        """Point."""
 
409
        def __init__(self,points=None,**common):
 
410
                _Entity.__init__(self,**common)
 
411
                self.points=points
 
412
        def __str__(self): # TODO:
 
413
                return '  0\nPOINT\n%s%s\n' %(self._common(),
 
414
                         _points(self.points)
 
415
                        )
 
416
 
 
417
#-----------------------------------------------
 
418
class Solid(_Entity):
 
419
        """Colored solid fill."""
 
420
        def __init__(self,points=None,**common):
 
421
                _Entity.__init__(self,**common)
 
422
                self.points=points
 
423
        def __str__(self):
 
424
                return '  0\nSOLID\n%s%s\n' %(self._common(),
 
425
                         _points(self.points[:2]+[self.points[3],self.points[2]])
 
426
                        )
 
427
 
 
428
 
 
429
#-----------------------------------------------
 
430
class Text(_Entity):
 
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)
 
436
                self.text=text
 
437
                self.point=point
 
438
                self.alignment=alignment
 
439
                self.flag=flag
 
440
                self.height=height
 
441
                self.justifyhor=justifyhor
 
442
                self.justifyver=justifyver
 
443
                self.rotation=rotation
 
444
                self.obliqueAngle=obliqueAngle
 
445
                self.style=style
 
446
                self.xscale=xscale
 
447
        def __str__(self):
 
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
 
458
                return result
 
459
 
 
460
#-----------------------------------------------
 
461
class Mtext(Text):
 
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
 
468
                self.width=width
 
469
                self.down=down
 
470
        def __str__(self):
 
471
                texts=self.text.replace('\r\n','\n').split('\n')
 
472
                if not self.down:texts.reverse()
 
473
                result=''
 
474
                x=y=0
 
475
                if self.spacingWidth:spacingWidth=self.spacingWidth
 
476
                else:spacingWidth=self.height*self.spacingFactor
 
477
                for text in texts:
 
478
                        while text:
 
479
                                result+='%s' %Text(text[:self.width],
 
480
                                        point=(self.point[0]+x*spacingWidth,
 
481
                                                   self.point[1]+y*spacingWidth,
 
482
                                                   self.point[2]),
 
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
 
487
                                )
 
488
                                text=text[self.width:]
 
489
                                if self.rotation:x+=1
 
490
                                else:y+=1
 
491
                return result[1:]
 
492
 
 
493
#-----------------------------------------------
 
494
##class _Mtext(_Entity):
 
495
        """Mtext not functioning for minimal dxf."""
 
496
        """
 
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)
 
502
                self.text=text
 
503
                self.point=point
 
504
                self.attachment=attachment
 
505
                self.charWidth=charWidth
 
506
                self.charHeight=charHeight
 
507
                self.direction=direction
 
508
                self.height=height
 
509
                self.rotation=rotation
 
510
                self.spacingStyle=spacingStyle
 
511
                self.spacingFactor=spacingFactor
 
512
                self.style=style
 
513
                self.width=width
 
514
                self.xdirection=xdirection
 
515
        def __str__(self):
 
516
                input=self.text
 
517
                text=''
 
518
                while len(input)>250:
 
519
                        text+='3\n%s\n'%input[:250]
 
520
                        input=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,
 
525
                                 self.height,
 
526
                                 self.rotation)
 
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
 
532
                return result
 
533
        """
 
534
 
 
535
 
 
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)
 
543
                self.layer=layer
 
544
                self.name=name
 
545
                self.flag=0
 
546
                self.base=base
 
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)
 
551
 
 
552
#-----------------------------------------------
 
553
class Layer(_Call):
 
554
        """Layer"""
 
555
        def __init__(self,name='pydxf',color=7,lineType='continuous',flag=64):
 
556
                self.name=name
 
557
                self.color=color
 
558
                self.lineType=lineType
 
559
                self.flag=flag
 
560
        def __str__(self):
 
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)
 
563
 
 
564
#-----------------------------------------------
 
565
class LineType(_Call):
 
566
        """Custom linetype"""
 
567
        def __init__(self,name='CONTINUOUS',description='Solid line',elements=[0.0],flag=0):
 
568
                self.name=name
 
569
                self.description=description
 
570
                self.elements=copy.copy(elements)
 
571
                self.flag=flag
 
572
        def __str__(self):
 
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)
 
575
                if self.elements:
 
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
 
580
                        result += elements
 
581
                return result
 
582
                 
 
583
 
 
584
#-----------------------------------------------
 
585
class Style(_Call):
 
586
        """Text style"""
 
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=''):
 
589
                self.name=name
 
590
                self.flag=flag
 
591
                self.height=height
 
592
                self.widthFactor=widthFactor
 
593
                self.obliqueAngle=obliqueAngle
 
594
                self.mirror=mirror
 
595
                self.lastHeight=lastHeight
 
596
                self.font=font
 
597
                self.bigFont=bigFont
 
598
        def __str__(self):
 
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())
 
603
 
 
604
#-----------------------------------------------
 
605
class VPort(_Call):
 
606
        def __init__(self,name,flag=0,
 
607
                                leftBottom=(0.0,0.0),
 
608
                                rightTop=(1.0,1.0),
 
609
                                center=(0.5,0.5),
 
610
                                snap_base=(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),
 
615
                                height=1.0,
 
616
                                ratio=1.0,
 
617
                                lens=50.0,
 
618
                                frontClipping=0.0,
 
619
                                backClipping=0.0,
 
620
                                snap_rotation=0.0,
 
621
                                twist=0.0,
 
622
                                mode=0,
 
623
                                circle_zoom=100,
 
624
                                fast_zoom=1,
 
625
                                ucsicon=1,
 
626
                                snap_on=0,
 
627
                                grid_on=0,
 
628
                                snap_style=0,
 
629
                                snap_isopair=0
 
630
                                ):
 
631
                self.name=name
 
632
                self.flag=flag
 
633
                self.leftBottom=leftBottom
 
634
                self.rightTop=rightTop
 
635
                self.center=center
 
636
                self.snap_base=snap_base
 
637
                self.snap_spacing=snap_spacing
 
638
                self.grid_spacing=grid_spacing
 
639
                self.direction=direction
 
640
                self.target=target
 
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)
 
648
                self.mode=mode
 
649
                self.circle_zoom=circle_zoom
 
650
                self.fast_zoom=fast_zoom
 
651
                self.ucsicon=ucsicon
 
652
                self.snap_on=snap_on
 
653
                self.grid_on=grid_on
 
654
                self.snap_style=snap_style
 
655
                self.snap_isopair=snap_isopair
 
656
        def __str__(self):
 
657
                output = ['  0', 'VPORT',
 
658
                        '  2', self.name,
 
659
                        ' 70', self.flag,
 
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),
 
668
                        ' 40', self.height,
 
669
                        ' 41', self.ratio,
 
670
                        ' 42', self.lens,
 
671
                        ' 43', self.frontClipping,
 
672
                        ' 44', self.backClipping,
 
673
                        ' 50', self.snap_rotation,
 
674
                        ' 51', self.twist,
 
675
                        ' 71', self.mode,
 
676
                        ' 72', self.circle_zoom,
 
677
                        ' 73', self.fast_zoom,
 
678
                        ' 74', self.ucsicon,
 
679
                        ' 75', self.snap_on,
 
680
                        ' 76', self.grid_on,
 
681
                        ' 77', self.snap_style,
 
682
                        ' 78', self.snap_isopair
 
683
                        ]
 
684
 
 
685
                output_str = ''
 
686
                for s in output:
 
687
                        output_str += '%s\n' %s
 
688
                return output_str
 
689
 
 
690
 
 
691
 
 
692
#-----------------------------------------------
 
693
class View(_Call):
 
694
        def __init__(self,name,flag=0,
 
695
                        width=1.0,
 
696
                        height=1.0,
 
697
                        center=(0.5,0.5),
 
698
                        direction=(0.0,0.0,1.0),
 
699
                        target=(0.0,0.0,0.0),
 
700
                        lens=50.0,
 
701
                        frontClipping=0.0,
 
702
                        backClipping=0.0,
 
703
                        twist=0.0,mode=0
 
704
                        ):
 
705
                self.name=name
 
706
                self.flag=flag
 
707
                self.width=float(width)
 
708
                self.height=float(height)
 
709
                self.center=center
 
710
                self.direction=direction
 
711
                self.target=target
 
712
                self.lens=float(lens)
 
713
                self.frontClipping=float(frontClipping)
 
714
                self.backClipping=float(backClipping)
 
715
                self.twist=float(twist)
 
716
                self.mode=mode
 
717
        def __str__(self):
 
718
                output = ['  0', 'VIEW',
 
719
                        '  2', self.name,
 
720
                        ' 70', self.flag,
 
721
                        ' 40', self.height,
 
722
                        _point(self.center),
 
723
                        ' 41', self.width,
 
724
                        _point(self.direction,1),
 
725
                        _point(self.target,2),
 
726
                        ' 42', self.lens,
 
727
                        ' 43', self.frontClipping,
 
728
                        ' 44', self.backClipping,
 
729
                        ' 50', self.twist,
 
730
                        ' 71', self.mode
 
731
                        ]
 
732
                output_str = ''
 
733
                for s in output:
 
734
                        output_str += '%s\n' %s
 
735
                return output_str
 
736
 
 
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)
 
743
 
 
744
#---drawing
 
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
 
752
                if not entities:
 
753
                        entities=[]
 
754
                _Collection.__init__(self,entities)
 
755
                self.insbase=insbase
 
756
                self.extmin=extmin
 
757
                self.extmax=extmax
 
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 #----------
 
766
                #private
 
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:
 
770
                AC1021  2008, 2007 
 
771
                AC1018  2006, 2005, 2004 
 
772
                AC1015  2002, 2000i, 2000 
 
773
                AC1014  R14,14.01 
 
774
                AC1012  R13    
 
775
                AC1009  R12,11 
 
776
                AC1006  R10    
 
777
                AC1004  R9    
 
778
                AC1002  R2.6  
 
779
                AC1.50  R2.05 
 
780
                """
 
781
 
 
782
        def _name(self,x):
 
783
                """Helper function for self._point"""
 
784
                return '  9\n$%s\n' %x.upper()
 
785
 
 
786
        def _point(self,name,x):
 
787
                """Point setting from drawing like extmin,extmax,..."""
 
788
                return '%s%s' %(self._name(name),_point(x))
 
789
 
 
790
        def _section(self,name,x):
 
791
                """Sections like tables,blocks,entities,..."""
 
792
                if x: xstr=''.join(x)
 
793
                else: xstr=''
 
794
                return '  0\nSECTION\n  2\n%s\n%s  0\nENDSEC\n'%(name.upper(),xstr)
 
795
 
 
796
        def _table(self,name,x):
 
797
                """Tables like ltype,layer,style,..."""
 
798
                if x: xstr=''.join(x)
 
799
                else: xstr=''
 
800
                return '  0\nTABLE\n  2\n%s\n 70\n%s\n%s  0\nENDTAB\n'%(name.upper(),len(x),xstr)
 
801
 
 
802
        def __str__(self):
 
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)
 
806
 
 
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]),
 
812
                ]
 
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'])
 
817
                return all
 
818
                
 
819
        def _write_section(self,file,name,data):
 
820
                file.write('  0\nSECTION\n  2\n%s\n'%name.upper())
 
821
                for x in data:
 
822
                        file.write(str(x))
 
823
                file.write('  0\nENDSEC\n')
 
824
 
 
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.
 
827
                """
 
828
                self.fileName=fileName
 
829
                if buffer: self.save()
 
830
                else: self.export()
 
831
 
 
832
        def save(self):
 
833
                outfile=open(self.fileName,'w')
 
834
                outfile.write(str(self))
 
835
                outfile.close()
 
836
 
 
837
        def export(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]),
 
846
                        ]
 
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')
 
851
                outfile.close()
 
852
 
 
853
 
 
854
#---extras
 
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)
 
860
                self.point=point
 
861
                self.width=width
 
862
                self.height=height
 
863
                self.solid=solid
 
864
                self.line=line
 
865
        def __str__(self):
 
866
                result=''
 
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]
 
870
                if self.solid:
 
871
                        result+= Solid(points=points[:-1],parent=self.solid)
 
872
                if self.line:
 
873
                        for i in range(4):
 
874
                                result+= Line(points=[points[i],points[i+1]],parent=self)
 
875
                return result[1:]
 
876
 
 
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)
 
882
                self.closed=closed
 
883
                self.points=copy.copy(points)
 
884
        def __str__(self):
 
885
                if self.closed:
 
886
                        points=self.points+[self.points[0]]
 
887
                else: points=self.points
 
888
                result=''
 
889
                for i in range(len(points)-1):
 
890
                        result+= Line(points=[points[i][0],points[i+1][0]],parent=self)
 
891
                return result[1:]
 
892
 
 
893
#-----------------------------------------------------
 
894
def test():
 
895
        #Blocks
 
896
        b=Block('test')
 
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))
 
899
 
 
900
        #Drawing
 
901
        d=Drawing()
 
902
        #tables
 
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
 
907
 
 
908
        #entities
 
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))
 
918
 
 
919
        #d.saveas('c:\\test.dxf')
 
920
        d.saveas('test.dxf')
 
921
 
 
922
#-----------------------------------------------------
 
923
if __name__=='__main__':
 
924
        if not copy:
 
925
                Draw.PupMenu('Error%t|This script requires a full python install')
 
926
        else: test()
 
927