| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | __title__="FreeCAD OpenSCAD Workbench - DRAWEXE exporter" |
| | __author__ = "Sebastian Hoogen <github@sebastianhoogen.de>" |
| |
|
| | import FreeCAD, Part |
| | from builtins import open as pyopen |
| |
|
| |
|
| |
|
| | |
| | |
| | |
| |
|
| | 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 |
| | ''' |
| | if abs(n) < 1e-14: return '0' |
| | if angle and len(('%0.6e' % n).split('e')[0].rstrip('0') ) < 3: |
| | return ('%0.5f' % n).rstrip('0').rstrip('.') |
| | elif axis and len(('%0.13e' % n).split('e')[0].rstrip('0') ) < 6: |
| | return ('%0.10f' % n).rstrip('0').rstrip('.') |
| | else: |
| | for i in range(20): |
| | s = ('%%1.%df'% i) % n |
| | if float(s) == n: |
| | return s |
| | for i in range(20): |
| | s = ('%%0.%de'% i) % n |
| | if float(s) == n: |
| | return s |
| |
|
| | def ax2_xdir(normal): |
| | |
| |
|
| | 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) |
| | m=FreeCAD.Matrix() |
| | m.rotateZ(2*math.pi/pcount) |
| | points=[] |
| | for i in range(pcount): |
| | points.append(v) |
| | v=m.multiply(v) |
| | points.append(v) |
| | return ' '.join('%s %s %s'%(f2s(v.x),f2s(v.y),f2s(v.z)) \ |
| | for v in points) |
| |
|
| | def formatobjtype(ob): |
| | objtype=ob.TypeId |
| | if (ob.isDerivedFrom('Part::FeaturePython') or \ |
| | ob.isDerivedFrom('Part::Part2DObjectPython') or\ |
| | ob.isDerivedFrom('App::FeaturePython')) and \ |
| | hasattr(ob.Proxy,'__module__'): |
| | return '%s::%s.%s' % (ob.TypeId,ob.Proxy.__module__,\ |
| | ob.Proxy.__class__.__name__) |
| | else: |
| | return ob.TypeId |
| |
|
| | def placement2draw(placement,name='object'): |
| | """converts a FreeCAD Placement to trotate and ttranslate commands""" |
| | drawcommand='' |
| | if not placement.Rotation.isNull(): |
| | import math |
| | |
| | ax=placement.Rotation.Axis |
| | import itertools |
| | |
| | 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 |
| | |
| | an=math.degrees(placement.Rotation.Angle) |
| | drawcommand += "trotate %s 0 0 0 %s %s %s %s\n" % (name,\ |
| | f2s(dx),f2s(dy),f2s(dz),\ |
| | |
| | f2s(an,angle=True)) |
| | if placement.Base.Length > 1e-8: |
| | x,y,z=placement.Base |
| | drawcommand += "ttranslate %s %s %s %s\n" % \ |
| | (name,f2s(x),f2s(y),f2s(z)) |
| | return drawcommand |
| |
|
| | def saveShape(csg,filename,shape,name,hasplacement = True,cleanshape=False): |
| | import os |
| | spath,sname = os.path.split(filename) |
| | sname=sname.replace('.','-') |
| | uname='%s-%s' %(sname,name) |
| | breppath=os.path.join(spath,'%s.brep'%uname) |
| | csg.write("restore %s.brep %s\n" % (uname,name)) |
| | if cleanshape: |
| | import Part |
| | try: |
| | shape = shape.cleaned() |
| | except Part.OCCError: |
| | shape = shape.copy() |
| | if hasplacement is None: |
| | hasplacement = False |
| | shape.exportBrep(breppath) |
| | elif not hasplacement: |
| | shape.exportBrep(breppath) |
| | else: |
| | sh=shape.copy() |
| | sh.Placement=FreeCAD.Placement() |
| | |
| | |
| | |
| | if cleanshape: |
| | shape = shape.cleaned() |
| | sh.exportBrep(breppath) |
| | return hasplacement |
| |
|
| |
|
| |
|
| | def isDraftFeature(ob): |
| | if (ob.isDerivedFrom('Part::FeaturePython') or \ |
| | ob.isDerivedFrom('Part::Part2DObjectPython')) and \ |
| | hasattr(ob.Proxy,'__module__') and \ |
| | ob.Proxy.__module__ == 'Draft': |
| | return True |
| |
|
| | def isDraftClone(ob): |
| | if (ob.isDerivedFrom('Part::FeaturePython') or \ |
| | ob.isDerivedFrom('Part::Part2DObjectPython')) and \ |
| | hasattr(ob.Proxy,'__module__') and \ |
| | ob.Proxy.__module__ == 'Draft': |
| | import Draft |
| | return isinstance(ob.Proxy,Draft._Clone) |
| |
|
| | def isDraftCircle(ob): |
| | if isDraftFeature(ob): |
| | import Draft |
| | return isinstance(ob.Proxy,Draft._Circle) |
| |
|
| | def isDraftEllipse(ob): |
| | if isDraftFeature(ob): |
| | import Draft |
| | return isinstance(ob.Proxy,Draft._Ellipse) |
| |
|
| | def isDraftPolygon(ob): |
| | if isDraftFeature(ob): |
| | import Draft |
| | return isinstance(ob.Proxy,Draft._Polygon) |
| |
|
| | def isDraftPoint(ob): |
| | if isDraftFeature(ob): |
| | import Draft |
| | return isinstance(ob.Proxy,Draft._Point) |
| |
|
| | def isDraftWire(ob): |
| | if isDraftFeature(ob): |
| | import Draft |
| | if isinstance(ob.Proxy,Draft._Wire): |
| | |
| | |
| | |
| | |
| | |
| | if ob.Base is None and ob.Tool is None and \ |
| | ob.FilletRadius.Value == 0.0 and \ |
| | 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 \ |
| | hasattr(ob.Proxy,'__module__') and \ |
| | ob.Proxy.__module__ == 'OpenSCADFeatures': |
| | return True |
| |
|
| | def isOpenSCADMultMatrixFeature(ob): |
| | if ob.isDerivedFrom('Part::FeaturePython') and \ |
| | hasattr(ob.Proxy,'__module__') and \ |
| | ob.Proxy.__module__ == 'OpenSCADFeatures': |
| | import OpenSCADFeatures |
| | return isinstance(ob.Proxy,OpenSCADFeatures.MatrixTransform) |
| |
|
| | def isDeform(ob): |
| | """tests whether the object is a Matrix transformation |
| | that does a non-uniform scaling""" |
| | |
| | |
| | |
| | return isOpenSCADMultMatrixFeature(ob) and \ |
| | ob.Matrix.analyze().startswith('Scale [') |
| |
|
| |
|
| |
|
| |
|
| | class Drawexporter(object): |
| | def __init__(self, filename): |
| | self.objectcache=set() |
| | self.csg = pyopen(filename,'w') |
| | |
| | self.filename=filename |
| | |
| | self.alwaysexplode = True |
| | self.cleanshape = False |
| |
|
| | def __enter__(self): |
| | return self |
| |
|
| | def write_header(self): |
| | import FreeCAD |
| | |
| | self.csg.write('#generated by FreeCAD %s\n' % \ |
| | '.'.join(FreeCAD.Version()[0:3])) |
| | self.csg.write('pload MODELING\n') |
| |
|
| | def write_displayonly(self,objlst): |
| | self.csg.write('donly %s\n'%' '.join([obj.Name for obj in objlst])) |
| |
|
| | def saveSweep(self,ob): |
| | import Part |
| | spine,subshapelst=ob.Spine |
| | |
| | explodeshape = self.alwaysexplode or self.process_object(spine,True) |
| | if explodeshape: |
| | self.process_object(spine) |
| | if len(subshapelst) and spine.Shape.ShapeType != 'Edge': |
| | |
| | |
| | self.csg.write('explode %s E\n' % spine.Name ) |
| | edgelst = ' '.join(('%s_%s' % (spine.Name,ss[4:]) for ss \ |
| | in subshapelst)) |
| | spinename = '%s-0-spine' % ob.Name |
| | self.csg.write('wire %s %s\n' %(spinename,edgelst)) |
| | elif spine.Shape.ShapeType == 'Wire': |
| | spinename = spine.Name |
| | elif spine.Shape.ShapeType == 'Edge': |
| | spinename = '%s-0-spine' % ob.Name |
| | self.csg.write('wire %s %s\n' %(spinename,spine.Name)) |
| | else: |
| | if len(subshapelst): |
| | path=Part.Wire([spine.Shape.getElement(subshapename) for \ |
| | subshapename in subshapelst]) |
| | elif spine.Shape.ShapeType == 'Edge': |
| | path = spine.Shape |
| | elif spine.Shape.ShapeType == 'Wire': |
| | path = Part.Wire(spine.Shape) |
| | else: |
| | raise ValueError('Unsuitabel Shape Type') |
| | spinename = '%s-0-spine' % ob.Name |
| | saveShape(self.csg,self.filename, path,spinename,None,\ |
| | self.cleanshape) |
| | self.csg.write('mksweep %s\n' % spinename) |
| | |
| | setoptions=[] |
| | buildoptions=[] |
| | if ob.Frenet: |
| | setoptions.append('-FR') |
| | else: |
| | setoptions.append('-CF') |
| | if ob.Transition == 'Transformed': |
| | buildoptions.append('-M') |
| | elif ob.Transition == 'Right corner': |
| | buildoptions.append('-C') |
| | elif ob.Transition == 'Round corner': |
| | buildoptions.append('-R') |
| | if ob.Solid: |
| | buildoptions.append('-S') |
| | self.csg.write('setsweep %s\n' % (" ".join(setoptions))) |
| | |
| | sections=ob.Sections |
| | sectionnames = [] |
| | for i,subobj in enumerate(ob.Sections): |
| | sectionname = '%s-0-section-%02d-%s' % (ob.Name,i,subobj.Name) |
| | addoptions=[] |
| | explodeshape = self.alwaysexplode or \ |
| | self.process_object(subobj,True) |
| | if explodeshape: |
| | sh = subobj.Shape |
| | if sh.ShapeType == 'Vertex' or sh.ShapeType == 'Wire' or \ |
| | sh.ShapeType == 'Edge' or \ |
| | sh.ShapeType == 'Face' and len(sh.Wires) == 1: |
| | self.process_object(subobj) |
| | if sh.ShapeType == 'Wire' or sh.ShapeType == 'Vertex': |
| | |
| | sectionname = subobj.Name |
| | if sh.ShapeType == 'Edge': |
| | self.csg.write('explode %s E\n' % subobj.Name ) |
| | self.csg.write('wire %s %s_1\n' %(sectionname,subobj.Name)) |
| | if sh.ShapeType == 'Face': |
| | |
| | self.csg.write('explode %s W\n' % subobj.Name ) |
| | |
| | sectionname ='%s_1' % subobj.Name |
| | else: |
| | explodeshape = False |
| | if not explodeshape: |
| | sh = subobj.Shape |
| | if sh.ShapeType == 'Vertex': |
| | pass |
| | elif sh.ShapeType == 'Wire' or sh.ShapeType == 'Edge': |
| | sh = Part.Wire(sh) |
| | elif sh.ShapeType == 'Face': |
| | sh = sh.OuterWire |
| | else: |
| | raise ValueError('Unrecognized Shape Type') |
| | saveShape(self.csg,self.filename,sh,sectionname,None,\ |
| | self.cleanshape) |
| | self.csg.write('addsweep %s %s\n' % \ |
| | (sectionname," ".join(addoptions))) |
| | self.csg.write('buildsweep %s %s\n' % (ob.Name," ".join(buildoptions))) |
| |
|
| | def process_object(self,ob,checksupported=False,toplevel=False): |
| | if not checksupported and ob.Name in self.objectcache: |
| | return |
| | if not checksupported: |
| | self.objectcache.add(ob.Name) |
| | d1 = {'name':ob.Name} |
| | if hasattr(ob,'Placement'): |
| | hasplacement = not ob.Placement.isNull() |
| | else: |
| | hasplacement = False |
| | if ob.TypeId in ["Part::Cut","Part::Fuse","Part::Common",\ |
| | "Part::Section"]: |
| | if checksupported: return True |
| | d1.update({'part':ob.Base.Name,'tool':ob.Tool.Name,\ |
| | 'command':'b%s' % ob.TypeId[6:].lower()}) |
| | self.process_object(ob.Base) |
| | self.process_object(ob.Tool) |
| | self.csg.write("%(command)s %(name)s %(part)s %(tool)s\n"%d1) |
| | elif ob.TypeId == "Part::Plane" : |
| | if checksupported: return True |
| | d1.update({'uname':'%s-untrimmed' % d1['name'],\ |
| | 'length': f2s(ob.Length),'width': f2s(ob.Width)}) |
| | self.csg.write("plane %s 0 0 0\n"%d1['uname']) |
| | self.csg.write(\ |
| | "mkface %(name)s %(uname)s 0 %(length)s 0 %(width)s\n"%d1) |
| | elif ob.TypeId == "Part::Ellipse" : |
| | if checksupported: return True |
| | d1.update({'uname':'%s-untrimmed'%d1['name'], 'maj':\ |
| | f2s(ob.MajorRadius), 'min': f2s(ob.MinorRadius),\ |
| | 'pf':f2s(ob.Angle0.getValueAs('rad').Value), \ |
| | 'pl':f2s(ob.Angle1.getValueAs('rad').Value)}) |
| | self.csg.write("ellipse %(uname)s 0 0 0 %(maj)s %(min)s\n"%d1) |
| | self.csg.write('mkedge %(name)s %(uname)s %(pf)s %(pl)s\n' % d1) |
| | elif ob.TypeId == "Part::Sphere" : |
| | if checksupported: return True |
| | d1.update({'radius':f2s(ob.Radius),'angle1':f2s(ob.Angle1),\ |
| | 'angle2':f2s(ob.Angle2),'angle3':f2s(ob.Angle3)}) |
| | 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 |
| | d1.update({'dx':f2s(ob.Length),'dy':f2s(ob.Width),'dz':f2s(ob.Height)}) |
| | self.csg.write('box %(name)s %(dx)s %(dy)s %(dz)s\n'%d1) |
| | elif ob.TypeId == "Part::Cylinder" : |
| | if checksupported: return True |
| | d1.update({'radius':f2s(ob.Radius),'height':f2s(ob.Height),\ |
| | 'angle':f2s(ob.Angle)}) |
| | 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 |
| | d1.update({'radius1':f2s(ob.Radius1),'radius2':f2s(ob.Radius2),\ |
| | 'height':f2s(ob.Height),'angle':f2s(ob.Angle)}) |
| | 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 |
| | d1.update({'radius1':f2s(ob.Radius1),'radius2':f2s(ob.Radius2),\ |
| | 'angle1': f2s(ob.Angle1),'angle2':f2s(ob.Angle2),\ |
| | 'angle3': f2s(ob.Angle3)}) |
| | 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 |
| | self.process_object(ob.Source) |
| | self.csg.write('tcopy %s %s\n'%(ob.Source.Name,d1['name'])) |
| | b=ob.Base |
| | d1['x']=f2s(ob.Base.x) |
| | d1['y']=f2s(ob.Base.y) |
| | d1['z']=f2s(ob.Base.z) |
| | d1['dx']=f2s(ob.Normal.x) |
| | d1['dy']=f2s(ob.Normal.y) |
| | d1['dz']=f2s(ob.Normal.z) |
| | self.csg.write('tmirror %(name)s %(x)s %(y)s %(z)s %(dx)s %(dy)s %(dz)s\n' \ |
| | % d1) |
| | elif ob.TypeId == 'Part::Compound': |
| | if len(ob.Links) == 0: |
| | pass |
| | elif len(ob.Links) == 1: |
| | if checksupported: |
| | return self.process_object(ob.Links[0],True) |
| | self.process_object(ob.Links[0]) |
| | self.csg.write('tcopy %s %s\n'%(ob.Links[0].Name,d1['name'])) |
| | else: |
| | if checksupported: return True |
| | basenames=[] |
| | for i,subobj in enumerate(ob.Links): |
| | self.process_object(subobj) |
| | basenames.append(subobj.Name) |
| | self.csg.write('compound %s %s\n' % (' '.join(basenames),ob.Name)) |
| | elif ob.TypeId in ["Part::MultiCommon", "Part::MultiFuse"]: |
| | if len(ob.Shapes) == 0: |
| | pass |
| | elif len(ob.Shapes) == 1: |
| | if checksupported: |
| | 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 |
| | 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) |
| | else: |
| | if checksupported: return True |
| | topname = ob.Name |
| | command = 'b%s' % ob.TypeId[11:].lower() |
| | lst1=ob.Shapes[:] |
| | current=lst1.pop(0) |
| | curname=current.Name |
| | self.process_object(current) |
| | i=1 |
| | while lst1: |
| | if len(lst1) >= 2: |
| | nxtname='to-%s-%03d-t'%(topname,i) |
| | else: |
| | nxtname=topname |
| | nxt=lst1.pop(0) |
| | self.process_object(nxt) |
| | self.csg.write("%s %s %s %s\n"%(command,nxtname,curname,nxt.Name)) |
| | curname=nxtname |
| | i+=1 |
| | elif (isDraftPolygon(ob) and ob.ChamferSize.Value == 0 and\ |
| | ob.FilletRadius.Value == 0 and ob.AttachmentSupport is None) or\ |
| | ob.TypeId == "Part::Prism" or \ |
| | ob.TypeId == "Part::RegularPolygon": |
| | if checksupported: return True |
| | draftpolygon = isDraftPolygon(ob) |
| | if draftpolygon: |
| | pcount = ob.FacesNumber |
| | if ob.DrawMode =='inscribed': |
| | r=ob.Radius.Value |
| | elif ob.DrawMode =='circumscribed': |
| | import math |
| | r = ob.Radius.Value/math.cos(math.pi/pcount) |
| | else: |
| | raise ValueError |
| | else: |
| | pcount = ob.Polygon |
| | r=ob.Circumradius.Value |
| | justwire = ob.TypeId == "Part::RegularPolygon" or \ |
| | (draftpolygon and ob.MakeFace == False) |
| | polyname = '%s-polyline' % d1['name'] |
| | if justwire: |
| | wirename = d1['name'] |
| | else: |
| | wirename = '%s-polywire' % d1['name'] |
| | if ob.TypeId == "Part::Prism": |
| | facename = '%s-polyface' % d1['name'] |
| | else: |
| | facename = d1['name'] |
| | self.csg.write('polyline %s %s\n' % (polyname,polygonstr(r,pcount))) |
| | self.csg.write('wire %s %s\n' %(wirename,polyname)) |
| | if not justwire: |
| | self.csg.write('mkplane %s %s\n' % (facename,polyname)) |
| | if ob.TypeId == "Part::Prism": |
| | self.csg.write('prism %s %s 0 0 %s\n' % \ |
| | (d1['name'],facename, f2s(ob.Height.Value))) |
| | elif ob.TypeId == "Part::Extrusion" and ob.TaperAngle.Value == 0: |
| | if checksupported: return True |
| | self.process_object(ob.Base) |
| | |
| | |
| | |
| | facename=ob.Base.Name |
| | self.csg.write('prism %s %s %s %s %s\n' % (d1['name'],facename,\ |
| | f2s(ob.Dir.x),f2s(ob.Dir.y),f2s(ob.Dir.z))) |
| | elif ob.TypeId == "Part::Fillet" and True: |
| | if checksupported: return True |
| | self.process_object(ob.Base) |
| | self.csg.write('explode %s E\n' % ob.Base.Name ) |
| | self.csg.write('blend %s %s %s\n' % (d1['name'],ob.Base.Name,\ |
| | ' '.join(('%s %s'%(f2s(e[1]),'%s_%d' % (ob.Base.Name,e[0])) \ |
| | for e in ob.Edges)))) |
| | elif ob.TypeId == "Part::Thickness" and not ob.SelfIntersection and \ |
| | ob.Mode == 'Skin': |
| | if checksupported: return True |
| | jointype = {'Arc':'a','Intersection':'i','Tangent':'t'} |
| | inter = {False: 'p', True: 'c'} |
| | baseobj, facelist = ob.Faces |
| | self.process_object(baseobj) |
| | faces = ' '.join([('%s_%s' %(baseobj.Name,f[4:])) \ |
| | for f in facelist]) |
| | value = f2s(ob.Value) |
| | self.csg.write('explode %s F\n' % baseobj.Name ) |
| | self.csg.write('offsetparameter 1e-7 %s %s\n' % \ |
| | (inter[ob.Intersection],jointype[ob.Join])) |
| | self.csg.write('offsetload %s %s %s\n'%(baseobj.Name,value,faces)) |
| | self.csg.write('offsetperform %s\n' % d1['name'] ) |
| |
|
| | elif ob.TypeId == "Part::Sweep" and True: |
| | if checksupported: return True |
| | self.saveSweep(ob) |
| | elif ob.TypeId == "Part::Loft": |
| | if checksupported: return True |
| | sectionnames=[] |
| | for i,subobj in enumerate(ob.Sections): |
| | explodeshape = self.alwaysexplode or \ |
| | self.process_object(suboobj,True) |
| | if explodeshape and False: |
| | try: |
| | raise NotImplementedError |
| | sectionname = '%s-%02d-section' % (ob.Name,i) |
| | sh = subobj.Shape |
| | if sh.isNull(): |
| | raise ValueError |
| | tempname=spine.Name |
| | if sh.ShapeType == 'Compound': |
| | sh = sh.childShapes()[0] |
| | self.csg.write('explode %s\n' % tempname ) |
| | tempname = '%s_1' % tempname |
| | if sh.ShapeType == 'Face': |
| | |
| | if len(sh.Wires) == 1: |
| | sh=sh.Wires[0] |
| | self.csg.write('explode %s\n W' % tempname ) |
| | tempname = '%s_1' % tempname |
| | else: |
| | raise NotImplementedError |
| | elif sh.ShapeType == 'Edge': |
| | self.csg.write('wire %s %s\n' %(sectionname,tempname)) |
| | tempname = sectionname |
| | sectionname = tempname |
| | except NotImplementedError: |
| | explodeshape = False |
| | else: |
| | explodeshape = False |
| | if not explodeshape: |
| | sh = subobj.Shape |
| | if not sh.isNull(): |
| | if sh.ShapeType == 'Compound': |
| | sh = sh.childShapes()[0] |
| | if sh.ShapeType == 'Face': |
| | sh = sh.OuterWire |
| | elif sh.ShapeType == 'Edge': |
| | import Part |
| | sh = Part.Wire([sh]) |
| | elif sh.ShapeType == 'Wire': |
| | import Part |
| | sh = Part.Wire(sh) |
| | elif sh.ShapeType == 'Vertex': |
| | pass |
| | else: |
| | raise ValueError('Unsuitabel Shape Type') |
| | sectionname = '%s-%02d-section' % (ob.Name,i) |
| | saveShape(self.csg,self.filename, sh,sectionname,None,\ |
| | self.cleanshape) |
| | sectionnames.append(sectionname) |
| | if ob.Closed: |
| | sectionnames.append(sectionnames[0]) |
| | self.csg.write('thrusections %s %d %d %s\n' % \ |
| | (ob.Name,int(ob.Solid),\ |
| | int(ob.Ruled), ' '.join(sectionnames))) |
| | elif isDeform(ob): |
| | if checksupported: return True |
| | m=ob.Matrix |
| | self.process_object(ob.Base) |
| | |
| | d1['basename']=ob.Base.Name |
| | d1['cx']=f2s(m.A11) |
| | d1['cy']=f2s(m.A22) |
| | d1['cz']=f2s(m.A33) |
| | self.csg.write('deform %(name)s %(basename)s %(cx)s %(cy)s %(cz)s\n' % d1) |
| | if m.A14 > 1e-8 or m.A24 > 1e-8 or m.A34 > 1e-8: |
| | self.csg.write("ttranslate %s %s %s %s\n" % \ |
| | (ob.Name,f2s(m.A14),f2s(m.A24),f2s(m.A34))) |
| | elif isDraftPoint(ob) or ob.TypeId == "Part::Vertex": |
| | if checksupported: return True |
| | d1['x']=f2s(ob.X) |
| | d1['y']=f2s(ob.Y) |
| | d1['z']=f2s(ob.Z) |
| | self.csg.write('vertex %(name)s %(x)s %(y)s %(z)s\n' % d1) |
| | elif isDraftCircle(ob) or ob.TypeId == "Part::Circle" or \ |
| | isDraftEllipse(ob): |
| | if checksupported: return True |
| | isdraftcircle=isDraftCircle(ob) |
| | isdraftellipse=isDraftCircle(ob) |
| | "circle name x y [z [dx dy dz]] [ux uy [uz]] radius" |
| | curvename = '%s-curve' % d1['name'] |
| | if ob.TypeId == "Part::Circle": |
| | radius=f2s(float(ob.Radius)) |
| | pfirst=f2s(ob.Angle0.getValueAs('rad').Value) |
| | plast=f2s(ob.Angle1.getValueAs('rad').Value) |
| | self.csg.write('circle %s 0 0 0 %s\n' % (curvename,radius)) |
| | self.csg.write('mkedge %s %s %s %s\n' % \ |
| | (d1['name'],curvename,pfirst,plast)) |
| | else: |
| | makeface = ob.MakeFace and \ |
| | (ob.Shape.isNull() or ob.Shape.ShapeType == 'Face') |
| | |
| | |
| | edgename = '%s-edge' % d1['name'] |
| |
|
| | if isdraftcircle: |
| | pfirst=f2s(ob.FirstAngle.getValueAs('rad').Value) |
| | plast=f2s(ob.LastAngle.getValueAs('rad').Value) |
| | radius=f2s(ob.Radius.Value) |
| | self.csg.write('circle %s 0 0 0 %s\n' % (curvename,radius)) |
| | else: |
| | import math |
| | majr=f2s(float(ob.MajorRadius)) |
| | minr=f2s(float(ob.MinorRadius)) |
| | pfirst=f2s(math.radians(ob.FirstAngle)) |
| | plast =f2s(math.radians(ob.LastAngle)) |
| | self.csg.write('ellipse %s 0 0 0 %s %s\n' % \ |
| | (curvename,majr,minr)) |
| | self.csg.write('mkedge %s %s %s %s\n' % \ |
| | (edgename,curvename,pfirst,plast)) |
| | if makeface: |
| | wirename = '%s-wire' % d1['name'] |
| | self.csg.write('wire %s %s\n' %(wirename,edgename)) |
| | self.csg.write('mkplane %s %s\n' % (d1['name'],wirename)) |
| | else: |
| | self.csg.write('wire %s %s\n' %(d1['name'],edgename)) |
| | elif ob.TypeId == "Part::Line": |
| | if checksupported: return True |
| | self.csg.write('polyline %s %s %s %s %s %s %s\n' % \ |
| | (d1['name'],f2s(ob.X1),f2s(ob.Y1),f2s(ob.Z1),\ |
| | f2s(ob.X2),f2s(ob.Y2),f2s(ob.Z2))) |
| | elif isDraftWire(ob): |
| | if checksupported: return True |
| | points=ob.Points |
| | if ob.Closed: |
| | points.append(points[0]) |
| | polyname = '%s-dwireline' % d1['name'] |
| | pointstr=' '.join('%s %s %s'%(f2s(v.x),f2s(v.y),f2s(v.z)) \ |
| | for v in points) |
| | self.csg.write('polyline %s %s\n' % (polyname,pointstr)) |
| | if ob.MakeFace: |
| | wirename = '%s-dwirewire' % d1['name'] |
| | self.csg.write('wire %s %s\n' %(wirename,polyname)) |
| | facename = d1['name'] |
| | self.csg.write('mkplane %s %s\n' % (facename,polyname)) |
| | else: |
| | wirename = d1['name'] |
| | self.csg.write('wire %s %s\n' %(wirename,polyname)) |
| | elif isDraftClone(ob): |
| | if checksupported: return True |
| | x,y,z=ob.Scale |
| | if x == y == z: |
| | d1['scale']=f2s(x) |
| | else: |
| | d1['cx']=f2s(x) |
| | d1['cy']=f2s(y) |
| | d1['cz']=f2s(z) |
| | if len(ob.Objects) == 1: |
| | d1['basename']=ob.Objects[0].Name |
| | self.process_object(ob.Objects[0]) |
| | if x == y == z: |
| | self.csg.write('tcopy %(basename)s %(name)s\n' % d1) |
| | self.csg.write('pscale %(name)s 0 0 0 %(scale)s\n' % d1) |
| | else: |
| | self.csg.write('deform %(name)s %(basename)s'\ |
| | ' %(cx)s %(cy)s %(cz)s\n' % d1) |
| | else: |
| | newnames=[] |
| | for i,subobj in enumerate(ob.Objects): |
| | self.process_object(subobj) |
| | d1['basename']=subobj.Name |
| | newname='%s-%2d' % (ob.Name,i) |
| | d1['newname']=newname |
| | newnames.append(newname) |
| | if x == y == z: |
| | self.csg.write('tcopy %(basename)s %(newname)s\n' % d1) |
| | self.csg.write('pscale %(newname)s 0 0 0 %(scale)s\n' % d1) |
| | else: |
| | 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'): |
| | |
| | if checksupported: return True |
| | 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)) |
| | 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::Feature') : |
| | if ob.Shape.isNull(): |
| | raise ValueError('Shape of %s is Null' % ob.Name) |
| | if checksupported: return False |
| | self.csg.write('#saved shape of unsupported %s Object\n' % \ |
| | formatobjtype(ob)) |
| | hasplacement = saveShape(self.csg,self.filename,ob.Shape,ob.Name,\ |
| | hasplacement,self.cleanshape) |
| | elif ob.isDerivedFrom('App::Annotation') : |
| | return False |
| | |
| | else: |
| | if not toplevel: |
| | raise ValueError('Can not export child object') |
| | else: |
| | if ob.Name != ob.Label: |
| | labelstr = 'Label %s' % ob.Label.encode('unicode-escape') |
| | else: |
| | labelstr = '' |
| | self.csg.write('#omitted unsupported %s Object %s%s\n' %\ |
| | (formatobjtype(ob),ob.Name,labelstr)) |
| | self.csg.write('#Properties: %s\n' % \ |
| | ','.join(ob.PropertiesList)) |
| | return False |
| | |
| | if hasplacement: |
| | self.csg.write(placement2draw(ob.Placement,ob.Name)) |
| | if ob.Name != ob.Label: |
| | self.csg.write('#Object Label: %s\n' % ob.Label.encode('unicode-escape')) |
| | return ob.Name |
| |
|
| | def export_annotations(self,objlst): |
| | for ob in objlst: |
| | if ob.isDerivedFrom('App::Annotation') : |
| | if ob.Name != ob.Label: |
| | self.csg.write('#Annotation Name %s Label %s"\n' % \ |
| | (ob.Name,ob.Label.encode('unicode-escape'))) |
| | else: |
| | self.csg.write('#Annotation %s\n' % (ob.Name)) |
| | v=ob.Position |
| | self.csg.write('dtext %s %s %s "%s"\n' % \ |
| | (f2s(v.x),f2s(v.y),f2s(v.z), '\\n'.join(\ |
| | ob.LabelText).encode(\ |
| | 'ascii', errors='xmlcharrefreplace'))) |
| |
|
| | def export_objects(self,objlst,toplevel=True): |
| | self.write_header() |
| | toplevelobjs = [self.process_object(ob, toplevel=toplevel)\ |
| | for ob in objlst] |
| | names = [name for name in toplevelobjs if name is not False] |
| | self.csg.write('donly %s\n'%(' '.join(names))) |
| | self.export_annotations(objlst) |
| |
|
| | def __exit__(self,exc_type, exc_val, exc_tb ): |
| | self.csg.close() |
| |
|
| | 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") |
| |
|