improvements in exportDRAWEXE

fix Draft._clone output
export Shape2DViews
export Part::MultiFuse with single boolean operation like done in c2ce8f5eba
make DRAWEXE ouput more readable
* omit unessary parameters of spheres, cylinders, cones and tori
* denormalize the rotation axis in placements
* use the extension .tcl for the output
This commit is contained in:
Sebastian Hoogen 2015-03-12 01:27:47 +01:00 committed by wmayer
parent 16c1dbd25d
commit 1846087723
2 changed files with 120 additions and 13 deletions

View File

@ -26,5 +26,5 @@
#***************************************************************************/
import FreeCAD
FreeCAD.addExportType("DRAWEXE source (*.draw)","exportDRAWEXE")
FreeCAD.addExportType("DRAWEXE script (*.tcl)","exportDRAWEXE")

View File

@ -32,6 +32,15 @@ if open.__module__ == '__builtin__':
# Part:: Wedge, Helix, Spiral, Elipsoid
# Draft: Rectangle, BSpline, BezCurve
def quaternionToString(rot):
def shorthexfloat(f):
s=f.hex()
mantisse, exponent = f.hex().split('p',1)
return '%sp%s' % (mantisse.rstrip('0'),exponent)
x,y,z,w=rot.Q
return 'q=%s+%s*i+%s*j+%s*k' % (shorthexfloat(w),shorthexfloat(x),
shorthexfloat(y),shorthexfloat(z))
def f2s(n,angle=False,axis=False):
'''convert to numerical value to string
try to remove no significant digits, by guessing a former rounding
@ -51,6 +60,33 @@ def f2s(n,angle=False,axis=False):
if float(s) == n:
return s
def ax2_xdir(normal):
#adaped from gp_Ax2.ccc (c) OpenCascade SAS LGPL 2.1+
xa=abs(normal.x)
ya=abs(normal.y)
za=abs(normal.z)
if ya <= xa and ya <= za:
if xa > za:
return FreeCAD.Vector(-normal.z,0, normal.x)
else:
return FreeCAD.Vector( normal.z,0,-normal.x)
elif xa <= ya and xa <= za:
if ya > za:
return FreeCAD.Vector(0,-normal.z, normal.y)
else:
return FreeCAD.Vector(0, normal.z,-normal.y)
else:
if xa > ya:
return FreeCAD.Vector(-normal.y, normal.x,0)
else:
return FreeCAD.Vector( normal.y,-normal.x,0)
def occversiontuple():
import FreeCAD,Part
occmajs,occmins,occfixs = FreeCAD.ConfigGet('OCC_VERSION').split('.')[:3]
return (int(occmajs),int(occmins),int(occfixs))
def polygonstr(r,pcount):
import math
v=FreeCAD.Vector(r,0,0)
@ -80,10 +116,22 @@ def placement2draw(placement,name='object'):
drawcommand=''
if not placement.Rotation.isNull():
import math
dx,dy,dz=placement.Rotation.Axis
#dx,dy,dz=placement.Rotation.Axis
ax=placement.Rotation.Axis
import itertools
# denormalize rotation axis
for t in itertools.product((0,1,-1),repeat=3):
if t != (0,0,0):
if (ax-FreeCAD.Vector(*t).normalize()).Length < 1e-15:
dx,dy,dz = t
break
else:
dx,dy,dz=placement.Rotation.Axis
#drawcommand += "# %s\n" %quaternionToString(placement.Rotation)
an=math.degrees(placement.Rotation.Angle)
drawcommand += "trotate %s 0 0 0 %s %s %s %s\n" % \
(name,f2s(dx,axis=True),f2s(dy,axis=True),f2s(dz,axis=True),\
drawcommand += "trotate %s 0 0 0 %s %s %s %s\n" % (name,\
f2s(dx),f2s(dy),f2s(dz),\
# f2s(dx,axis=True),f2s(dy,axis=True),f2s(dz,axis=True),\
f2s(an,angle=True))
if placement.Base.Length > 1e-8:
x,y,z=placement.Base
@ -130,7 +178,8 @@ def isDraftFeature(ob):
return True
def isDraftClone(ob):
if ob.isDerivedFrom('Part::FeaturePython') and \
if (ob.isDerivedFrom('Part::FeaturePython') or \
ob.isDerivedFrom('Part::Part2DObjectPython')) and \
hasattr(ob.Proxy,'__module__') and \
ob.Proxy.__module__ == 'Draft':
import Draft
@ -170,6 +219,10 @@ def isDraftWire(ob):
ob.ChamferSize.Value == 0.0:
return True
def isDraftShape2DView(ob):
if isDraftFeature(ob):
import Draft
return isinstance(ob.Proxy,Draft._Shape2DView)
def isOpenSCADFeature(ob):
if ob.isDerivedFrom('Part::FeaturePython') and \
@ -211,6 +264,7 @@ class Drawexporter(object):
def write_header(self):
import FreeCAD
#self.csg.write('#!/usr/bin/env DRAWEXE\n')
self.csg.write('#generated by FreeCAD %s\n' % \
'.'.join(FreeCAD.Version()[0:3]))
self.csg.write('pload MODELING\n')
@ -352,8 +406,12 @@ class Drawexporter(object):
if checksupported: return True # The object is supported
d1.update({'radius':f2s(ob.Radius),'angle1':f2s(ob.Angle1),\
'angle2':f2s(ob.Angle2),'angle3':f2s(ob.Angle3)})
self.csg.write('psphere %(name)s %(radius)s %(angle1)s %(angle2)s '\
'%(angle3)s\n'%d1)
if ob.Angle1.Value == -90 and ob.Angle2.Value == 90 and \
ob.Angle3.Value == 360:
self.csg.write('psphere %(name)s %(radius)s\n'%d1)
else:
self.csg.write('psphere %(name)s %(radius)s %(angle1)s '
'%(angle2)s %(angle3)s\n'%d1)
elif ob.TypeId == "Part::Box" :
if checksupported: return True # The object is supported
d1.update({'dx':f2s(ob.Length),'dy':f2s(ob.Width),'dz':f2s(ob.Height)})
@ -362,19 +420,32 @@ class Drawexporter(object):
if checksupported: return True # The object is supported
d1.update({'radius':f2s(ob.Radius),'height':f2s(ob.Height),\
'angle':f2s(ob.Angle)})
self.csg.write('pcylinder %(name)s %(radius)s %(height)s %(angle)s\n'%d1)
if ob.Angle.Value == 360:
self.csg.write('pcylinder %(name)s %(radius)s %(height)s\n'%d1)
else:
self.csg.write('pcylinder %(name)s %(radius)s %(height)s '\
'%(angle)s\n'%d1)
elif ob.TypeId == "Part::Cone" :
if checksupported: return True # The object is supported
d1.update({'radius1':f2s(ob.Radius1),'radius2':f2s(ob.Radius2),\
'height':f2s(ob.Height),'angle':f2s(ob.Angle)})
self.csg.write('pcone %(name)s %(radius1)s %(radius2)s %(height)s %(angle)s\n'%d1)
if ob.Angle.Value == 360:
self.csg.write('pcone %(name)s %(radius1)s %(radius2)s '\
'%(height)s\n'%d1)
else:
self.csg.write('pcone %(name)s %(radius1)s %(radius2)s '\
'%(height)s %(angle)s\n'%d1)
elif ob.TypeId == "Part::Torus" :
if checksupported: return True # The object is supported
d1.update({'radius1':f2s(ob.Radius1),'radius2':f2s(ob.Radius2),\
'angle1': f2s(ob.Angle1),'angle2':f2s(ob.Angle2),\
'angle3': f2s(ob.Angle3)})
self.csg.write('ptorus %(name)s %(radius1)s %(radius2)s %(angle1)s '\
'%(angle2)s %(angle3)s\n' % d1)
if ob.Angle1.Value == -180 and ob.Angle2.Value == 180 and \
ob.Angle3.Value == 360:
self.csg.write('ptorus %(name)s %(radius1)s %(radius2)s\n'%d1)
else:
self.csg.write('ptorus %(name)s %(radius1)s %(radius2)s '\
'%(angle1)s %(angle2)s %(angle3)s\n' % d1)
elif ob.TypeId == "Part::Mirroring" :
if checksupported: return True # The object is supported
self.process_object(ob.Source)
@ -411,6 +482,17 @@ class Drawexporter(object):
return self.process_object(ob.Shapes[0],True)
self.process_object(ob.Shapes[0],)
self.csg.write('tcopy %s %s\n'%(ob.Shapes[0].Name,d1['name']))
elif ob.TypeId == "Part::MultiFuse" and \
occversiontuple() >= (6,8,1):
if checksupported: return True # The object is supported
for subobj in ob.Shapes:
self.process_object(subobj)
self.csg.write("bclearobjects\nbcleartools\n")
self.csg.write("baddobjects %s\n" % ob.Shapes[0].Name)
self.csg.write("baddtools %s\n" % " ".join(subobj.Name for \
subobj in ob.Shapes[1:]))
self.csg.write("bfillds\n")
self.csg.write("bbop %s 1\n" % ob.Name) #BOPAlgo_FUSE == 1
else:
if checksupported: return True # The object is supported
topname = ob.Name
@ -646,7 +728,7 @@ class Drawexporter(object):
self.csg.write('wire %s %s\n' %(wirename,polyname))
elif isDraftClone(ob):
if checksupported: return True # The object is supported
x,y,z=ob.Scale.x
x,y,z=ob.Scale
if x == y == z: #uniform scaling
d1['scale']=f2s(x)
else:
@ -677,7 +759,29 @@ class Drawexporter(object):
self.csg.write('deform %(newname)s %(basename)s'\
' %(cx)s %(cy)s %(cz)s\n' % d1)
self.csg.write('compound %s %s\n' % (' '.join(newnames),ob.Name))
elif isDraftShape2DView(ob) and not ob.Tessellation and \
ob.ProjectionMode == "Solid" and ob.Base is not None and \
hasattr(ob.Base,'Shape'):
# not supported are groups, Arch/Sections and individual faces mode
if checksupported: return True # The object is supported
self.process_object(ob.Base)
v=ob.Projection
x=ax2_xdir(v)
self.csg.write('hprj %s_proj 0 0 0 %s %s %s %s %s %s\n' % \
( ob.Name,f2s(v.x),f2s(v.y),f2s(v.z)\
, f2s(x.x),f2s(x.y),f2s(x.z)))
self.csg.write('houtl %s_outl %s\n' % (ob.Name, ob.Base.Name))
self.csg.write('hfill %s_outl %s_proj 0\n' %(ob.Name,ob.Name)) #0?
self.csg.write('hload %s_outl\n' % (ob.Name))
self.csg.write('hsetprj %s_proj\n' % (ob.Name))
self.csg.write('hupdate\n')
self.csg.write('hhide\n')
self.csg.write('unset -nocomplain vl v1l vnl vol vil hl h1l hnl hol hil\n')
self.csg.write('hres2d\n')
if ob.HiddenLines:
self.csg.write('compound vl v1l vnl vol vil hl h1l hnl hol hil %s\n' % ob.Name)
else:
self.csg.write('compound vl v1l vnl vol vil %s\n' % ob.Name)
#elif ob.isDerivedFrom('Part::FeaturePython') and \
# hasattr(ob.Proxy,'__module__'):
# pass
@ -744,3 +848,6 @@ def export(exportList,filename):
"called when freecad exports a file"
with Drawexporter(filename) as exporter:
exporter.export_objects(exportList)
if 'tcl' not in FreeCAD.getExportType():
FreeCAD.addExportType("DRAWEXE script (*.tcl)","exportDRAWEXE")