23
__title__="FreeCAD OpenSCAD Workbench - DRAWEXE exporter"
24
__author__ = "Sebastian Hoogen <github@sebastianhoogen.de>"
27
from builtins import open as pyopen
35
def quaternionToString(rot):
38
mantisse, exponent = f.hex().split('p',1)
39
return '%sp%s' % (mantisse.rstrip('0'),exponent)
41
return 'q=%s+%s*i+%s*j+%s*k' % (shorthexfloat(w),shorthexfloat(x),
42
shorthexfloat(y),shorthexfloat(z))
44
def f2s(n,angle=False,axis=False):
45
'''convert to numerical value to string
46
try to remove no significant digits, by guessing a former rounding
48
if abs(n) < 1e-14: return '0'
49
if angle and len(('%0.6e' % n).split('e')[0].rstrip('0') ) < 3:
50
return ('%0.5f' % n).rstrip('0').rstrip('.')
51
elif axis and len(('%0.13e' % n).split('e')[0].rstrip('0') ) < 6:
52
return ('%0.10f' % n).rstrip('0').rstrip('.')
55
s = ('%%1.%df'% i) % n
59
s = ('%%0.%de'% i) % n
69
if ya <= xa and ya <= za:
71
return FreeCAD.Vector(-normal.z,0, normal.x)
73
return FreeCAD.Vector( normal.z,0,-normal.x)
74
elif xa <= ya and xa <= za:
76
return FreeCAD.Vector(0,-normal.z, normal.y)
78
return FreeCAD.Vector(0, normal.z,-normal.y)
81
return FreeCAD.Vector(-normal.y, normal.x,0)
83
return FreeCAD.Vector( normal.y,-normal.x,0)
87
occmajs,occmins,occfixs = FreeCAD.ConfigGet('OCC_VERSION').split('.')[:3]
88
return (int(occmajs),int(occmins),int(occfixs))
90
def polygonstr(r,pcount):
92
v=FreeCAD.Vector(r,0,0)
94
m.rotateZ(2*math.pi/pcount)
96
for i in range(pcount):
100
return ' '.join('%s %s %s'%(f2s(v.x),f2s(v.y),f2s(v.z)) \
103
def formatobjtype(ob):
105
if (ob.isDerivedFrom('Part::FeaturePython') or \
106
ob.isDerivedFrom('Part::Part2DObjectPython') or\
107
ob.isDerivedFrom('App::FeaturePython')) and \
108
hasattr(ob.Proxy,'__module__'):
109
return '%s::%s.%s' % (ob.TypeId,ob.Proxy.__module__,\
110
ob.Proxy.__class__.__name__)
114
def placement2draw(placement,name='object'):
115
"""converts a FreeCAD Placement to trotate and ttranslate commands"""
117
if not placement.Rotation.isNull():
120
ax=placement.Rotation.Axis
123
for t in itertools.product((0,1,-1),repeat=3):
125
if (ax-FreeCAD.Vector(*t).normalize()).Length < 1e-15:
129
dx,dy,dz=placement.Rotation.Axis
131
an=math.degrees(placement.Rotation.Angle)
132
drawcommand += "trotate %s 0 0 0 %s %s %s %s\n" % (name,\
133
f2s(dx),f2s(dy),f2s(dz),\
136
if placement.Base.Length > 1e-8:
138
drawcommand += "ttranslate %s %s %s %s\n" % \
139
(name,f2s(x),f2s(y),f2s(z))
142
def saveShape(csg,filename,shape,name,hasplacement = True,cleanshape=False):
144
spath,sname = os.path.split(filename)
145
sname=sname.replace('.','-')
146
uname='%s-%s' %(sname,name)
147
breppath=os.path.join(spath,'%s.brep'%uname)
148
csg.write("restore %s.brep %s\n" % (uname,name))
152
shape = shape.cleaned()
153
except Part.OCCError:
155
if hasplacement is None:
157
shape.exportBrep(breppath)
158
elif not hasplacement:
159
shape.exportBrep(breppath)
162
sh.Placement=FreeCAD.Placement()
167
shape = shape.cleaned()
168
sh.exportBrep(breppath)
173
def isDraftFeature(ob):
174
if (ob.isDerivedFrom('Part::FeaturePython') or \
175
ob.isDerivedFrom('Part::Part2DObjectPython')) and \
176
hasattr(ob.Proxy,'__module__') and \
177
ob.Proxy.__module__ == 'Draft':
181
if (ob.isDerivedFrom('Part::FeaturePython') or \
182
ob.isDerivedFrom('Part::Part2DObjectPython')) and \
183
hasattr(ob.Proxy,'__module__') and \
184
ob.Proxy.__module__ == 'Draft':
186
return isinstance(ob.Proxy,Draft._Clone)
188
def isDraftCircle(ob):
189
if isDraftFeature(ob):
191
return isinstance(ob.Proxy,Draft._Circle)
193
def isDraftEllipse(ob):
194
if isDraftFeature(ob):
196
return isinstance(ob.Proxy,Draft._Ellipse)
198
def isDraftPolygon(ob):
199
if isDraftFeature(ob):
201
return isinstance(ob.Proxy,Draft._Polygon)
204
if isDraftFeature(ob):
206
return isinstance(ob.Proxy,Draft._Point)
209
if isDraftFeature(ob):
211
if isinstance(ob.Proxy,Draft._Wire):
217
if ob.Base is None and ob.Tool is None and \
218
ob.FilletRadius.Value == 0.0 and \
219
ob.ChamferSize.Value == 0.0:
222
def isDraftShape2DView(ob):
223
if isDraftFeature(ob):
225
return isinstance(ob.Proxy,Draft._Shape2DView)
227
def isOpenSCADFeature(ob):
228
if ob.isDerivedFrom('Part::FeaturePython') and \
229
hasattr(ob.Proxy,'__module__') and \
230
ob.Proxy.__module__ == 'OpenSCADFeatures':
233
def isOpenSCADMultMatrixFeature(ob):
234
if ob.isDerivedFrom('Part::FeaturePython') and \
235
hasattr(ob.Proxy,'__module__') and \
236
ob.Proxy.__module__ == 'OpenSCADFeatures':
237
import OpenSCADFeatures
238
return isinstance(ob.Proxy,OpenSCADFeatures.MatrixTransform)
241
"""tests whether the object is a Matrix transformation
242
that does a non-uniform scaling"""
246
return isOpenSCADMultMatrixFeature(ob) and \
247
ob.Matrix.analyze().startswith('Scale [')
252
class Drawexporter(object):
253
def __init__(self, filename):
254
self.objectcache=set()
255
self.csg = pyopen(filename,'w')
257
self.filename=filename
259
self.alwaysexplode = True
260
self.cleanshape = False
265
def write_header(self):
268
self.csg.write('#generated by FreeCAD %s\n' % \
269
'.'.join(FreeCAD.Version()[0:3]))
270
self.csg.write('pload MODELING\n')
272
def write_displayonly(self,objlst):
273
self.csg.write('donly %s\n'%' '.join([obj.Name for obj in objlst]))
275
def saveSweep(self,ob):
277
spine,subshapelst=ob.Spine
279
explodeshape = self.alwaysexplode or self.process_object(spine,True)
281
self.process_object(spine)
282
if len(subshapelst) and spine.Shape.ShapeType != 'Edge':
285
self.csg.write('explode %s E\n' % spine.Name )
286
edgelst = ' '.join(('%s_%s' % (spine.Name,ss[4:]) for ss \
288
spinename = '%s-0-spine' % ob.Name
289
self.csg.write('wire %s %s\n' %(spinename,edgelst))
290
elif spine.Shape.ShapeType == 'Wire':
291
spinename = spine.Name
292
elif spine.Shape.ShapeType == 'Edge':
293
spinename = '%s-0-spine' % ob.Name
294
self.csg.write('wire %s %s\n' %(spinename,spine.Name))
297
path=Part.Wire([spine.Shape.getElement(subshapename) for \
298
subshapename in subshapelst])
299
elif spine.Shape.ShapeType == 'Edge':
301
elif spine.Shape.ShapeType == 'Wire':
302
path = Part.Wire(spine.Shape)
304
raise ValueError('Unsuitabel Shape Type')
305
spinename = '%s-0-spine' % ob.Name
306
saveShape(self.csg,self.filename, path,spinename,None,\
309
self.csg.write('mksweep %s\n' % spinename)
314
setoptions.append('-FR')
316
setoptions.append('-CF')
317
if ob.Transition == 'Transformed':
318
buildoptions.append('-M')
319
elif ob.Transition == 'Right corner':
320
buildoptions.append('-C')
321
elif ob.Transition == 'Round corner':
322
buildoptions.append('-R')
324
buildoptions.append('-S')
325
self.csg.write('setsweep %s\n' % (" ".join(setoptions)))
329
for i,subobj in enumerate(ob.Sections):
333
sectionname = '%s-0-section-%02d-%s' % (ob.Name,i,subobj.Name)
335
explodeshape = self.alwaysexplode or \
336
self.process_object(subobj,True)
339
if sh.ShapeType == 'Vertex' or sh.ShapeType == 'Wire' or \
340
sh.ShapeType == 'Edge' or \
341
sh.ShapeType == 'Face' and len(sh.Wires) == 1:
342
self.process_object(subobj)
343
if sh.ShapeType == 'Wire' or sh.ShapeType == 'Vertex':
345
sectionname = subobj.Name
346
if sh.ShapeType == 'Edge':
347
self.csg.write('explode %s E\n' % subobj.Name )
348
self.csg.write('wire %s %s_1\n' %(sectionname,subobj.Name))
349
if sh.ShapeType == 'Face':
351
self.csg.write('explode %s W\n' % subobj.Name )
353
sectionname ='%s_1' % subobj.Name
358
if sh.ShapeType == 'Vertex':
360
elif sh.ShapeType == 'Wire' or sh.ShapeType == 'Edge':
362
elif sh.ShapeType == 'Face':
365
raise ValueError('Unrecognized Shape Type')
366
saveShape(self.csg,self.filename,sh,sectionname,None,\
368
self.csg.write('addsweep %s %s\n' % \
369
(sectionname," ".join(addoptions)))
370
self.csg.write('buildsweep %s %s\n' % (ob.Name," ".join(buildoptions)))
372
def process_object(self,ob,checksupported=False,toplevel=False):
373
if not checksupported and ob.Name in self.objectcache:
375
if not checksupported:
376
self.objectcache.add(ob.Name)
377
d1 = {'name':ob.Name}
378
if hasattr(ob,'Placement'):
379
hasplacement = not ob.Placement.isNull()
382
if ob.TypeId in ["Part::Cut","Part::Fuse","Part::Common",\
384
if checksupported: return True
385
d1.update({'part':ob.Base.Name,'tool':ob.Tool.Name,\
386
'command':'b%s' % ob.TypeId[6:].lower()})
387
self.process_object(ob.Base)
388
self.process_object(ob.Tool)
389
self.csg.write("%(command)s %(name)s %(part)s %(tool)s\n"%d1)
390
elif ob.TypeId == "Part::Plane" :
391
if checksupported: return True
392
d1.update({'uname':'%s-untrimmed' % d1['name'],\
393
'length': f2s(ob.Length),'width': f2s(ob.Width)})
394
self.csg.write("plane %s 0 0 0\n"%d1['uname'])
396
"mkface %(name)s %(uname)s 0 %(length)s 0 %(width)s\n"%d1)
397
elif ob.TypeId == "Part::Ellipse" :
398
if checksupported: return True
399
d1.update({'uname':'%s-untrimmed'%d1['name'], 'maj':\
400
f2s(ob.MajorRadius), 'min': f2s(ob.MinorRadius),\
401
'pf':f2s(ob.Angle0.getValueAs('rad').Value), \
402
'pl':f2s(ob.Angle1.getValueAs('rad').Value)})
403
self.csg.write("ellipse %(uname)s 0 0 0 %(maj)s %(min)s\n"%d1)
404
self.csg.write('mkedge %(name)s %(uname)s %(pf)s %(pl)s\n' % d1)
405
elif ob.TypeId == "Part::Sphere" :
406
if checksupported: return True
407
d1.update({'radius':f2s(ob.Radius),'angle1':f2s(ob.Angle1),\
408
'angle2':f2s(ob.Angle2),'angle3':f2s(ob.Angle3)})
409
if ob.Angle1.Value == -90 and ob.Angle2.Value == 90 and \
410
ob.Angle3.Value == 360:
411
self.csg.write('psphere %(name)s %(radius)s\n'%d1)
413
self.csg.write('psphere %(name)s %(radius)s %(angle1)s '
414
'%(angle2)s %(angle3)s\n'%d1)
415
elif ob.TypeId == "Part::Box" :
416
if checksupported: return True
417
d1.update({'dx':f2s(ob.Length),'dy':f2s(ob.Width),'dz':f2s(ob.Height)})
418
self.csg.write('box %(name)s %(dx)s %(dy)s %(dz)s\n'%d1)
419
elif ob.TypeId == "Part::Cylinder" :
420
if checksupported: return True
421
d1.update({'radius':f2s(ob.Radius),'height':f2s(ob.Height),\
422
'angle':f2s(ob.Angle)})
423
if ob.Angle.Value == 360:
424
self.csg.write('pcylinder %(name)s %(radius)s %(height)s\n'%d1)
426
self.csg.write('pcylinder %(name)s %(radius)s %(height)s '\
428
elif ob.TypeId == "Part::Cone" :
429
if checksupported: return True
430
d1.update({'radius1':f2s(ob.Radius1),'radius2':f2s(ob.Radius2),\
431
'height':f2s(ob.Height),'angle':f2s(ob.Angle)})
432
if ob.Angle.Value == 360:
433
self.csg.write('pcone %(name)s %(radius1)s %(radius2)s '\
436
self.csg.write('pcone %(name)s %(radius1)s %(radius2)s '\
437
'%(height)s %(angle)s\n'%d1)
438
elif ob.TypeId == "Part::Torus" :
439
if checksupported: return True
440
d1.update({'radius1':f2s(ob.Radius1),'radius2':f2s(ob.Radius2),\
441
'angle1': f2s(ob.Angle1),'angle2':f2s(ob.Angle2),\
442
'angle3': f2s(ob.Angle3)})
443
if ob.Angle1.Value == -180 and ob.Angle2.Value == 180 and \
444
ob.Angle3.Value == 360:
445
self.csg.write('ptorus %(name)s %(radius1)s %(radius2)s\n'%d1)
447
self.csg.write('ptorus %(name)s %(radius1)s %(radius2)s '\
448
'%(angle1)s %(angle2)s %(angle3)s\n' % d1)
449
elif ob.TypeId == "Part::Mirroring" :
450
if checksupported: return True
451
self.process_object(ob.Source)
452
self.csg.write('tcopy %s %s\n'%(ob.Source.Name,d1['name']))
454
d1['x']=f2s(ob.Base.x)
455
d1['y']=f2s(ob.Base.y)
456
d1['z']=f2s(ob.Base.z)
457
d1['dx']=f2s(ob.Normal.x)
458
d1['dy']=f2s(ob.Normal.y)
459
d1['dz']=f2s(ob.Normal.z)
460
self.csg.write('tmirror %(name)s %(x)s %(y)s %(z)s %(dx)s %(dy)s %(dz)s\n' \
462
elif ob.TypeId == 'Part::Compound':
463
if len(ob.Links) == 0:
465
elif len(ob.Links) == 1:
467
return self.process_object(ob.Links[0],True)
468
self.process_object(ob.Links[0])
469
self.csg.write('tcopy %s %s\n'%(ob.Links[0].Name,d1['name']))
471
if checksupported: return True
473
for i,subobj in enumerate(ob.Links):
474
self.process_object(subobj)
475
basenames.append(subobj.Name)
476
self.csg.write('compound %s %s\n' % (' '.join(basenames),ob.Name))
477
elif ob.TypeId in ["Part::MultiCommon", "Part::MultiFuse"]:
478
if len(ob.Shapes) == 0:
480
elif len(ob.Shapes) == 1:
482
return self.process_object(ob.Shapes[0],True)
483
self.process_object(ob.Shapes[0],)
484
self.csg.write('tcopy %s %s\n'%(ob.Shapes[0].Name,d1['name']))
485
elif ob.TypeId == "Part::MultiFuse" and \
486
occversiontuple() >= (6,8,1):
487
if checksupported: return True
488
for subobj in ob.Shapes:
489
self.process_object(subobj)
490
self.csg.write("bclearobjects\nbcleartools\n")
491
self.csg.write("baddobjects %s\n" % ob.Shapes[0].Name)
492
self.csg.write("baddtools %s\n" % " ".join(subobj.Name for \
493
subobj in ob.Shapes[1:]))
494
self.csg.write("bfillds\n")
495
self.csg.write("bbop %s 1\n" % ob.Name)
497
if checksupported: return True
499
command = 'b%s' % ob.TypeId[11:].lower()
503
self.process_object(current)
507
nxtname='to-%s-%03d-t'%(topname,i)
511
self.process_object(nxt)
512
self.csg.write("%s %s %s %s\n"%(command,nxtname,curname,nxt.Name))
515
elif (isDraftPolygon(ob) and ob.ChamferSize.Value == 0 and\
516
ob.FilletRadius.Value == 0 and ob.AttachmentSupport is None) or\
517
ob.TypeId == "Part::Prism" or \
518
ob.TypeId == "Part::RegularPolygon":
519
if checksupported: return True
520
draftpolygon = isDraftPolygon(ob)
522
pcount = ob.FacesNumber
523
if ob.DrawMode =='inscribed':
525
elif ob.DrawMode =='circumscribed':
527
r = ob.Radius.Value/math.cos(math.pi/pcount)
532
r=ob.Circumradius.Value
533
justwire = ob.TypeId == "Part::RegularPolygon" or \
534
(draftpolygon and ob.MakeFace == False)
535
polyname = '%s-polyline' % d1['name']
537
wirename = d1['name']
539
wirename = '%s-polywire' % d1['name']
540
if ob.TypeId == "Part::Prism":
541
facename = '%s-polyface' % d1['name']
543
facename = d1['name']
544
self.csg.write('polyline %s %s\n' % (polyname,polygonstr(r,pcount)))
545
self.csg.write('wire %s %s\n' %(wirename,polyname))
547
self.csg.write('mkplane %s %s\n' % (facename,polyname))
548
if ob.TypeId == "Part::Prism":
549
self.csg.write('prism %s %s 0 0 %s\n' % \
550
(d1['name'],facename, f2s(ob.Height.Value)))
551
elif ob.TypeId == "Part::Extrusion" and ob.TaperAngle.Value == 0:
552
if checksupported: return True
553
self.process_object(ob.Base)
557
facename=ob.Base.Name
558
self.csg.write('prism %s %s %s %s %s\n' % (d1['name'],facename,\
559
f2s(ob.Dir.x),f2s(ob.Dir.y),f2s(ob.Dir.z)))
560
elif ob.TypeId == "Part::Fillet" and True:
561
if checksupported: return True
562
self.process_object(ob.Base)
563
self.csg.write('explode %s E\n' % ob.Base.Name )
564
self.csg.write('blend %s %s %s\n' % (d1['name'],ob.Base.Name,\
565
' '.join(('%s %s'%(f2s(e[1]),'%s_%d' % (ob.Base.Name,e[0])) \
566
for e in ob.Edges))))
567
elif ob.TypeId == "Part::Thickness" and not ob.SelfIntersection and \
569
if checksupported: return True
570
jointype = {'Arc':'a','Intersection':'i','Tangent':'t'}
571
inter = {False: 'p', True: 'c'}
572
baseobj, facelist = ob.Faces
573
self.process_object(baseobj)
574
faces = ' '.join([('%s_%s' %(baseobj.Name,f[4:])) \
576
value = f2s(ob.Value)
577
self.csg.write('explode %s F\n' % baseobj.Name )
578
self.csg.write('offsetparameter 1e-7 %s %s\n' % \
579
(inter[ob.Intersection],jointype[ob.Join]))
580
self.csg.write('offsetload %s %s %s\n'%(baseobj.Name,value,faces))
581
self.csg.write('offsetperform %s\n' % d1['name'] )
583
elif ob.TypeId == "Part::Sweep" and True:
584
if checksupported: return True
586
elif ob.TypeId == "Part::Loft":
587
if checksupported: return True
589
for i,subobj in enumerate(ob.Sections):
590
explodeshape = self.alwaysexplode or \
591
self.process_object(suboobj,True)
592
if explodeshape and False:
594
raise NotImplementedError
595
sectionname = '%s-%02d-section' % (ob.Name,i)
600
if sh.ShapeType == 'Compound':
601
sh = sh.childShapes()[0]
602
self.csg.write('explode %s\n' % tempname )
603
tempname = '%s_1' % tempname
604
if sh.ShapeType == 'Face':
606
if len(sh.Wires) == 1:
608
self.csg.write('explode %s\n W' % tempname )
609
tempname = '%s_1' % tempname
611
raise NotImplementedError
612
elif sh.ShapeType == 'Edge':
613
self.csg.write('wire %s %s\n' %(sectionname,tempname))
614
tempname = sectionname
615
sectionname = tempname
616
except NotImplementedError:
623
if sh.ShapeType == 'Compound':
624
sh = sh.childShapes()[0]
625
if sh.ShapeType == 'Face':
627
elif sh.ShapeType == 'Edge':
630
elif sh.ShapeType == 'Wire':
633
elif sh.ShapeType == 'Vertex':
636
raise ValueError('Unsuitabel Shape Type')
637
sectionname = '%s-%02d-section' % (ob.Name,i)
638
saveShape(self.csg,self.filename, sh,sectionname,None,\
640
sectionnames.append(sectionname)
642
sectionnames.append(sectionnames[0])
643
self.csg.write('thrusections %s %d %d %s\n' % \
644
(ob.Name,int(ob.Solid),\
645
int(ob.Ruled), ' '.join(sectionnames)))
647
if checksupported: return True
649
self.process_object(ob.Base)
651
d1['basename']=ob.Base.Name
655
self.csg.write('deform %(name)s %(basename)s %(cx)s %(cy)s %(cz)s\n' % d1)
656
if m.A14 > 1e-8 or m.A24 > 1e-8 or m.A34 > 1e-8:
657
self.csg.write("ttranslate %s %s %s %s\n" % \
658
(ob.Name,f2s(m.A14),f2s(m.A24),f2s(m.A34)))
659
elif isDraftPoint(ob) or ob.TypeId == "Part::Vertex":
660
if checksupported: return True
664
self.csg.write('vertex %(name)s %(x)s %(y)s %(z)s\n' % d1)
665
elif isDraftCircle(ob) or ob.TypeId == "Part::Circle" or \
667
if checksupported: return True
668
isdraftcircle=isDraftCircle(ob)
669
isdraftellipse=isDraftCircle(ob)
670
"circle name x y [z [dx dy dz]] [ux uy [uz]] radius"
671
curvename = '%s-curve' % d1['name']
672
if ob.TypeId == "Part::Circle":
673
radius=f2s(float(ob.Radius))
674
pfirst=f2s(ob.Angle0.getValueAs('rad').Value)
675
plast=f2s(ob.Angle1.getValueAs('rad').Value)
676
self.csg.write('circle %s 0 0 0 %s\n' % (curvename,radius))
677
self.csg.write('mkedge %s %s %s %s\n' % \
678
(d1['name'],curvename,pfirst,plast))
680
makeface = ob.MakeFace and \
681
(ob.Shape.isNull() or ob.Shape.ShapeType == 'Face')
684
edgename = '%s-edge' % d1['name']
687
pfirst=f2s(ob.FirstAngle.getValueAs('rad').Value)
688
plast=f2s(ob.LastAngle.getValueAs('rad').Value)
689
radius=f2s(ob.Radius.Value)
690
self.csg.write('circle %s 0 0 0 %s\n' % (curvename,radius))
693
majr=f2s(float(ob.MajorRadius))
694
minr=f2s(float(ob.MinorRadius))
695
pfirst=f2s(math.radians(ob.FirstAngle))
696
plast =f2s(math.radians(ob.LastAngle))
697
self.csg.write('ellipse %s 0 0 0 %s %s\n' % \
698
(curvename,majr,minr))
699
self.csg.write('mkedge %s %s %s %s\n' % \
700
(edgename,curvename,pfirst,plast))
702
wirename = '%s-wire' % d1['name']
703
self.csg.write('wire %s %s\n' %(wirename,edgename))
704
self.csg.write('mkplane %s %s\n' % (d1['name'],wirename))
706
self.csg.write('wire %s %s\n' %(d1['name'],edgename))
707
elif ob.TypeId == "Part::Line":
708
if checksupported: return True
709
self.csg.write('polyline %s %s %s %s %s %s %s\n' % \
710
(d1['name'],f2s(ob.X1),f2s(ob.Y1),f2s(ob.Z1),\
711
f2s(ob.X2),f2s(ob.Y2),f2s(ob.Z2)))
712
elif isDraftWire(ob):
713
if checksupported: return True
716
points.append(points[0])
717
polyname = '%s-dwireline' % d1['name']
718
pointstr=' '.join('%s %s %s'%(f2s(v.x),f2s(v.y),f2s(v.z)) \
720
self.csg.write('polyline %s %s\n' % (polyname,pointstr))
722
wirename = '%s-dwirewire' % d1['name']
723
self.csg.write('wire %s %s\n' %(wirename,polyname))
724
facename = d1['name']
725
self.csg.write('mkplane %s %s\n' % (facename,polyname))
727
wirename = d1['name']
728
self.csg.write('wire %s %s\n' %(wirename,polyname))
729
elif isDraftClone(ob):
730
if checksupported: return True
738
if len(ob.Objects) == 1:
739
d1['basename']=ob.Objects[0].Name
740
self.process_object(ob.Objects[0])
742
self.csg.write('tcopy %(basename)s %(name)s\n' % d1)
743
self.csg.write('pscale %(name)s 0 0 0 %(scale)s\n' % d1)
745
self.csg.write('deform %(name)s %(basename)s'\
746
' %(cx)s %(cy)s %(cz)s\n' % d1)
749
for i,subobj in enumerate(ob.Objects):
750
self.process_object(subobj)
751
d1['basename']=subobj.Name
752
newname='%s-%2d' % (ob.Name,i)
753
d1['newname']=newname
754
newnames.append(newname)
756
self.csg.write('tcopy %(basename)s %(newname)s\n' % d1)
757
self.csg.write('pscale %(newname)s 0 0 0 %(scale)s\n' % d1)
759
self.csg.write('deform %(newname)s %(basename)s'\
760
' %(cx)s %(cy)s %(cz)s\n' % d1)
761
self.csg.write('compound %s %s\n' % (' '.join(newnames),ob.Name))
762
elif isDraftShape2DView(ob) and not ob.Tessellation and \
763
ob.ProjectionMode == "Solid" and ob.Base is not None and \
764
hasattr(ob.Base,'Shape'):
766
if checksupported: return True
767
self.process_object(ob.Base)
770
self.csg.write('hprj %s_proj 0 0 0 %s %s %s %s %s %s\n' % \
771
( ob.Name,f2s(v.x),f2s(v.y),f2s(v.z)\
772
, f2s(x.x),f2s(x.y),f2s(x.z)))
773
self.csg.write('houtl %s_outl %s\n' % (ob.Name, ob.Base.Name))
774
self.csg.write('hfill %s_outl %s_proj 0\n' %(ob.Name,ob.Name))
775
self.csg.write('hload %s_outl\n' % (ob.Name))
776
self.csg.write('hsetprj %s_proj\n' % (ob.Name))
777
self.csg.write('hupdate\n')
778
self.csg.write('hhide\n')
779
self.csg.write('unset -nocomplain vl v1l vnl vol vil hl h1l hnl hol hil\n')
780
self.csg.write('hres2d\n')
782
self.csg.write('compound vl v1l vnl vol vil hl h1l hnl hol hil %s\n' % ob.Name)
784
self.csg.write('compound vl v1l vnl vol vil %s\n' % ob.Name)
788
elif ob.isDerivedFrom('Part::Feature') :
789
if ob.Shape.isNull():
790
raise ValueError('Shape of %s is Null' % ob.Name)
791
if checksupported: return False
792
self.csg.write('#saved shape of unsupported %s Object\n' % \
794
hasplacement = saveShape(self.csg,self.filename,ob.Shape,ob.Name,\
795
hasplacement,self.cleanshape)
796
elif ob.isDerivedFrom('App::Annotation') :
801
raise ValueError('Can not export child object')
803
if ob.Name != ob.Label:
804
labelstr = 'Label %s' % ob.Label.encode('unicode-escape')
807
self.csg.write('#omitted unsupported %s Object %s%s\n' %\
808
(formatobjtype(ob),ob.Name,labelstr))
809
self.csg.write('#Properties: %s\n' % \
810
','.join(ob.PropertiesList))
814
self.csg.write(placement2draw(ob.Placement,ob.Name))
815
if ob.Name != ob.Label:
816
self.csg.write('#Object Label: %s\n' % ob.Label.encode('unicode-escape'))
819
def export_annotations(self,objlst):
821
if ob.isDerivedFrom('App::Annotation') :
822
if ob.Name != ob.Label:
823
self.csg.write('#Annotation Name %s Label %s"\n' % \
824
(ob.Name,ob.Label.encode('unicode-escape')))
826
self.csg.write('#Annotation %s\n' % (ob.Name))
828
self.csg.write('dtext %s %s %s "%s"\n' % \
829
(f2s(v.x),f2s(v.y),f2s(v.z), '\\n'.join(\
830
ob.LabelText).encode(\
831
'ascii', errors='xmlcharrefreplace')))
833
def export_objects(self,objlst,toplevel=True):
835
toplevelobjs = [self.process_object(ob, toplevel=toplevel)\
837
names = [name for name in toplevelobjs if name is not False]
838
self.csg.write('donly %s\n'%(' '.join(names)))
839
self.export_annotations(objlst)
844
def __exit__(self,exc_type, exc_val, exc_tb ):
847
def export(exportList,filename):
848
"called when freecad exports a file"
849
with Drawexporter(filename) as exporter:
850
exporter.export_objects(exportList)
852
if 'tcl' not in FreeCAD.getExportType():
853
FreeCAD.addExportType("DRAWEXE script (*.tcl)","exportDRAWEXE")