#*************************************************************************** #* * #* Copyright (c) 2012 Keith Sloan * #* * #* This program is free software; you can redistribute it and/or modify * #* it under the terms of the GNU Lesser General Public License (LGPL) * #* as published by the Free Software Foundation; either version 2 of * #* the License, or (at your option) any later version. * #* for detail see the LICENCE text file. * #* * #* This program is distributed in the hope that it will be useful, * #* but WITHOUT ANY WARRANTY; without even the implied warranty of * #* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * #* GNU Library General Public License for more details. * #* * #* You should have received a copy of the GNU Library General Public * #* License along with this program; if not, write to the Free Software * #* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * #* USA * #* * #* Acknowledgements : * #* * #* Thanks to shoogen on the FreeCAD forum for programming advice * #* and some code. * #* * #*************************************************************************** __title__="FreeCAD OpenSCAD Workbench - CSG exporter Version" __author__ = "Keith Sloan " __url__ = ["http://www.sloan-home.co.uk/Export/Export.html"] import FreeCAD, os, Part, math from FreeCAD import Vector try: import FreeCADGui except ValueError: gui = False else: gui = True #*************************************************************************** # Tailor following to your requirements ( Should all be strings ) * global fafs #fafs = '$fa = 12, $fs = 2' #convexity = 'convexity = 10' params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD") fa = params.GetFloat('exportFa',12.0) fs = params.GetFloat('exportFs',2.0) conv = params.GetInt('exportConvexity',10) fafs = '$fa = %f, $fs = %f' % (fa,fs) convexity = 'convexity = %d' % conv #*************************************************************************** # Radius values not fixed for value apart from cylinder & Cone # no doubt there will be a problem when they do implement Value if open.__module__ == '__builtin__': pythonopen = open def center(b): if b == 2: return 'true' else: return 'false' def check_multmatrix(csg,ob,x,y,z): b = FreeCAD.Vector(x,y,z) if ob.Placement.isNull(): return 0 # center = false no mm elif ob.Placement.Rotation.isNull() and \ (ob.Placement.Base - b).Length < 1e-6: return 2 # center = true and no mm else: m = ob.Placement.toMatrix() # adjust position for center displacments csg.write("multmatrix([["+str(m.A11)+", "+str(m.A12)+", "+str(m.A13)+",\ "+str(m.A14)+"], ["\ +str(m.A21)+", "+str(m.A22)+", "+str(m.A23)+", "+str(m.A24)+"], ["\ +str(m.A31)+", "+str(m.A32)+", "+str(m.A33)+", "+str(m.A34)+"], [\ 0, 0, 0, 1]]){\n") return 1 # center = false and mm def mesh2polyhedron(mesh): pointstr=','.join(['[%f,%f,%f]' % tuple(vec) for vec in mesh.Topology[0]]) trianglestr=','.join(['[%d,%d,%d]' % tuple(tri) for tri in mesh.Topology[1]]) return 'polyhedron ( points = [%s], triangles = [%s]);' % (pointstr,trianglestr) def vector2d(v): return [v[0],v[1]] def vertexs2polygon(vertex): pointstr=','.join(['[%f, %f]' % tuple(vector2d(v.Point)) for v in vertex]) return 'polygon ( points = [%s], paths = undef, convexity = 1);}' % pointstr def shape2polyhedron(shape): import MeshPart return mesh2polyhedron(MeshPart.meshFromShape(Shape=shape,\ Deflection= params.GetFloat('meshdeflection',0.0))) def process_object(csg,ob): print("Placement") print("Pos : "+str(ob.Placement.Base)) print("axis : "+str(ob.Placement.Rotation.Axis)) print("angle : "+str(ob.Placement.Rotation.Angle)) if ob.TypeId == "Part::Sphere" : print("Sphere Radius : "+str(ob.Radius)) check_multmatrix(csg,ob,0,0,0) csg.write("sphere($fn = 0, "+fafs+", r = "+str(ob.Radius)+");\n") elif ob.TypeId == "Part::Box" : print("cube : ("+ str(ob.Length)+","+str(ob.Width)+","+str(ob.Height)+")") mm = check_multmatrix(csg,ob,-ob.Length/2,-ob.Width/2,-ob.Height/2) csg.write("cube (size = ["+str(ob.Length.Value)+", "+str(ob.Width.Value)+", "+str(ob.Height.Value)+"], center = "+center(mm)+");\n") if mm == 1 : csg.write("}\n") elif ob.TypeId == "Part::Cylinder" : print("cylinder : Height "+str(ob.Height)+ " Radius "+str(ob.Radius)) mm = check_multmatrix(csg,ob,0,0,-ob.Height/2) csg.write("cylinder($fn = 0, "+fafs+", h = "+str(ob.Height.Value)+ ", r1 = "+str(ob.Radius.Value)+\ ", r2 = " + str(ob.Radius.Value) + ", center = "+center(mm)+");\n") if mm == 1 : csg.write("}\n") elif ob.TypeId == "Part::Cone" : print("cone : Height "+str(ob.Height)+ " Radius1 "+str(ob.Radius1)+" Radius2 "+str(ob.Radius2)) mm = check_multmatrix(csg,ob,0,0,-ob.Height/2) csg.write("cylinder($fn = 0, "+fafs+", h = "+str(ob.Height.Value)+ ", r1 = "+str(ob.Radius1.Value)+\ ", r2 = "+str(ob.Radius2.Value)+", center = "+center(mm)+");\n") if mm == 1 : csg.write("}\n") elif ob.TypeId == "Part::Torus" : print("Torus") print(ob.Radius1) print(ob.Radius2) if ob.Angle3 == 360.00 : mm = check_multmatrix(csg,ob,0,0,0) csg.write("rotate_extrude("+convexity+", $fn = 0, "+fafs+")\n") csg.write("multmatrix([[1, 0, 0, "+str(ob.Radius1)+"], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])\n") csg.write("circle($fn = 0, "+fafs+", r = "+str(ob.Radius2)+");\n") if mm == 1 : csg.write("}\n") else : # Cannot convert to rotate extrude so best effort is polyhedron csg.write('%s\n' % shape2polyhedron(ob.Shape)) elif ob.TypeId == "Part::Prism": import math f = str(ob.Polygon) # r = str(ob.Length/2.0/math.sin(math.pi/ob.Polygon)) r = str(ob.Circumradius) # length seems to be the outer radius h = str(ob.Height.Value) mm = check_multmatrix(csg,ob,0,0,-float(h)/2) csg.write("cylinder($fn = "+f+", "+fafs+", h = "+h+", r1 = "+r+\ ", r2 = "+r+", center = "+center(mm)+");\n") if mm == 1: csg.write("}\n") elif ob.TypeId == "Part::RegularPolygon": mm = check_multmatrix(csg,ob,0,0,-float(h)/2) csg.write("circle($fn = "+str(ob.NumberOfSides)+", "+fafs+", r = "+str(ob.Radius)+");\n") if mm == 1: csg.write("}\n") elif ob.TypeId == "Part::Extrusion" : print("Extrusion") print(ob.Base) print(ob.Base.Name) if ob.Base.isDerivedFrom('Part::Part2DObjectPython') and \ hasattr(ob.Base,'Proxy') and hasattr(ob.Base.Proxy,'TypeId'): ptype=ob.Base.Proxy.TypeId if ptype == "Polygon" : f = str(ob.Base.FacesNumber) r = str(ob.Base.Radius) h = str(ob.Dir[2]) print("Faces : " + f) print("Radius : " + r) print("Height : " + h) mm = check_multmatrix(csg,ob,0,0,-float(h)/2) csg.write("cylinder($fn = "+f+", "+fafs+", h = "+h+", r1 = "+r+\ ", r2 = "+r+", center = "+center(mm)+");\n") if mm == 1: csg.write("}\n") elif ptype == "Circle" : r = str(ob.Base.Radius) h = str(ob.Dir[2]) print("Radius : " + r) print("Height : " + h) mm = check_multmatrix(csg,ob,0,0,-float(h)/2) csg.write("cylinder($fn = 0, "+fafs+", h = "+h+", r1 = "+r+\ ", r2 = "+r+", center = "+center(mm)+");\n") if mm == 1: csg.write("}\n") elif ptype == "Wire" : print("Wire extrusion") print(ob.Base) mm = check_multmatrix(csg,ob,0,0,0) csg.write("linear_extrude(height = "+str(ob.Dir[2])+", center = "+center(mm)+", "+convexity+", twist = 0, slices = 2, $fn = 0, "+fafs+")\n{\n") csg.write(vertexs2polygon(ob.Base.Shape.Vertexes)) if mm == 1: csg.write("}\n") elif ob.Base.isDerivedFrom('Part::Plane'): mm = check_multmatrix(csg,ob,0,0,0) csg.write("linear_extrude(height = "+str(ob.Dir[2])+", center = true, "+convexity+", twist = 0, slices = 2, $fn = 0, "+fafs+")\n{\n") csg.write("square (size = ["+str(ob.Base.Length.Value)+", "+str(ob.Base.Width.Value)+"], center = "+center(mm)+");\n}\n") if mm == 1: csg.write("}\n") elif ob.Base.Name.startswith('this_is_a_bad_idea'): pass else: pass # There should be a fallback solution elif ob.TypeId == "Part::Cut" : print("Cut") csg.write("difference() {\n") process_object(csg,ob.Base) process_object(csg,ob.Tool) csg.write("}\n") elif ob.TypeId == "Part::Fuse" : print("union") csg.write("union() {\n") process_object(csg,ob.Base) process_object(csg,ob.Tool) csg.write("}\n") elif ob.TypeId == "Part::Common" : print("intersection") csg.write("intersection() {\n") process_object(csg,ob.Base) process_object(csg,ob.Tool) csg.write("}\n") elif ob.TypeId == "Part::MultiFuse" : print("Multi Fuse / union") csg.write("union() {\n") for subobj in ob.Shapes: process_object(csg,subobj) csg.write("}\n") elif ob.TypeId == "Part::MultiCommon" : print("Multi Common / intersection") csg.write("intersection() {\n") for subobj in ob.Shapes: process_object(csg,subobj) csg.write("}\n") elif ob.isDerivedFrom('Part::Feature') : print("Part::Feature") mm = check_multmatrix(csg,ob,0,0,0) csg.write('%s\n' % shape2polyhedron(ob.Shape)) if mm == 1 : csg.write("}\n") def export(exportList,filename): "called when FreeCAD exports a file" # process Objects print("\nStart Export 0.1d\n") print("Open Output File") csg = pythonopen(filename,'w') print("Write Initial Output") # Not sure if comments as per scad are allowed in csg file csg.write("// CSG file generated from FreeCAD %s\n" % \ '.'.join(FreeCAD.Version()[0:3])) #write initial group statements - not sure if required csg.write("group() {\n group(){\n") for ob in exportList: print(ob) print("Name : "+ob.Name) print("Type : "+ob.TypeId) print("Shape : ") print(ob.Shape) process_object(csg,ob) # write closing group braces csg.write("}\n}\n") # close file csg.close() FreeCAD.Console.PrintMessage("successfully exported "+filename)