diff --git a/src/Mod/Sandbox/Init.py b/src/Mod/Sandbox/Init.py index 9fe51e8f4..f00f56f12 100644 --- a/src/Mod/Sandbox/Init.py +++ b/src/Mod/Sandbox/Init.py @@ -26,5 +26,5 @@ #***************************************************************************/ import FreeCAD -FreeCAD.addExportType("DRAWEXE source (*.draw)","exportDRAWEXE") +FreeCAD.addExportType("DRAWEXE script (*.tcl)","exportDRAWEXE") diff --git a/src/Mod/Sandbox/exportDRAWEXE.py b/src/Mod/Sandbox/exportDRAWEXE.py index 8f863ed59..776a06574 100644 --- a/src/Mod/Sandbox/exportDRAWEXE.py +++ b/src/Mod/Sandbox/exportDRAWEXE.py @@ -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")