2
#***************************************************************************
3
#* Copyright (c) 2012 Keith Sloan <keith@sloan-home.co.uk> *
5
#* This program is free software; you can redistribute it and/or modify *
6
#* it under the terms of the GNU Lesser General Public License (LGPL) *
7
#* as published by the Free Software Foundation; either version 2 of *
8
#* the License, or (at your option) any later version. *
9
#* for detail see the LICENCE text file. *
11
#* This program is distributed in the hope that it will be useful, *
12
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
#* GNU Library General Public License for more details. *
16
#* You should have received a copy of the GNU Library General Public *
17
#* License along with this program; if not, write to the Free Software *
18
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
21
#* Acknowledgements : *
23
#* Thanks to shoogen on the FreeCAD forum for programming advice *
26
#***************************************************************************
27
__title__ = "FreeCAD OpenSCAD Workbench - CSG exporter Version"
28
__author__ = "Keith Sloan <keith@sloan-home.co.uk>"
29
__url__ = ["http://www.sloan-home.co.uk/Export/Export.html"]
32
from builtins import open as pyopen
39
#***************************************************************************
40
# Tailor following to your requirements ( Should all be strings ) *
41
#fafs = '$fa = 12, $fs = 2'
42
#convexity = 'convexity = 10'
43
params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD")
44
fa = params.GetFloat('exportFa', 12.0)
45
fs = params.GetFloat('exportFs', 2.0)
46
conv = params.GetInt('exportConvexity', 10)
47
fafs = '$fa = %f, $fs = %f' % (fa, fs)
48
convexity = 'convexity = %d' % conv
49
#***************************************************************************
50
# Radius values not fixed for value apart from cylinder & Cone
51
# no doubt there will be a problem when they do implement Value
60
def check_multmatrix(csg, ob, x, y, z):
61
b = FreeCAD.Vector(x,y,z)
62
if ob.Placement.isNull():
63
return 0 # center = false no mm
64
elif ob.Placement.Rotation.isNull() and \
65
(ob.Placement.Base - b).Length < 1e-6:
66
return 2 # center = true and no mm
68
m = ob.Placement.toMatrix()
69
# adjust position for center displacements
70
csg.write("multmatrix([["+str(m.A11)+", "+str(m.A12)+", "+str(m.A13)+",\
72
+str(m.A21)+", "+str(m.A22)+", "+str(m.A23)+", "+str(m.A24)+"], ["\
73
+str(m.A31)+", "+str(m.A32)+", "+str(m.A33)+", "+str(m.A34)+"], [\
75
return 1 # center = false and mm
78
def mesh2polyhedron(mesh):
79
pointstr = ','.join(['[%f,%f,%f]' % tuple(vec) for vec in mesh.Topology[0]])
80
trianglestr = ','.join(['[%d,%d,%d]' % tuple(tri) for tri in mesh.Topology[1]])
81
#avoid deprecation warning by changing triangles to faces
82
#return 'polyhedron ( points = [%s], triangles = [%s]);' % (pointstr, trianglestr)
83
return 'polyhedron ( points = [%s], faces = [%s]);' % (pointstr, trianglestr)
90
def vertexs2polygon(vertex):
91
pointstr = ','.join(['[%f, %f]' % tuple(vector2d(v.Point)) for v in vertex])
92
return 'polygon ( points = [%s], paths = undef, convexity = 1);}' % pointstr
95
def shape2polyhedron(shape):
97
return mesh2polyhedron(MeshPart.meshFromShape(Shape=shape,\
98
Deflection = params.GetFloat('meshdeflection', 0.0)))
101
def process_object(csg,ob):
103
print("Pos : "+str(ob.Placement.Base))
104
print("axis : "+str(ob.Placement.Rotation.Axis))
105
print("angle : "+str(ob.Placement.Rotation.Angle))
107
if ob.TypeId == "Part::Sphere":
108
print("Sphere Radius : "+str(ob.Radius))
109
check_multmatrix(csg, ob, 0, 0, 0)
110
csg.write("sphere($fn = 0, "+fafs+", r = "+str(ob.Radius)+");\n")
112
elif ob.TypeId == "Part::Box":
113
print("cube : ("+ str(ob.Length)+","+str(ob.Width)+","+str(ob.Height)+")")
114
mm = check_multmatrix(csg,ob,-ob.Length/2,-ob.Width/2,-ob.Height/2)
115
csg.write("cube (size = ["+str(ob.Length.Value)+", "+str(ob.Width.Value)+", "+str(ob.Height.Value)+"], center = "+center(mm)+");\n")
116
if mm == 1 : csg.write("}\n")
118
elif ob.TypeId == "Part::Cylinder":
119
print("cylinder : Height "+str(ob.Height) + " Radius "+str(ob.Radius))
120
mm = check_multmatrix(csg, ob, 0, 0, -ob.Height/2)
121
csg.write("cylinder($fn = 0, "+fafs+", h = "+str(ob.Height.Value) + ", r1 = "+str(ob.Radius.Value)+\
122
", r2 = " + str(ob.Radius.Value) + ", center = "+center(mm)+");\n")
123
if mm == 1 : csg.write("}\n")
125
elif ob.TypeId == "Part::Cone":
126
print("cone : Height "+str(ob.Height) + " Radius1 "+str(ob.Radius1)+" Radius2 "+str(ob.Radius2))
127
mm = check_multmatrix(csg, ob, 0, 0, -ob.Height/2)
128
csg.write("cylinder($fn = 0, "+fafs+", h = "+str(ob.Height.Value)+ ", r1 = "+str(ob.Radius1.Value)+\
129
", r2 = "+str(ob.Radius2.Value)+", center = "+center(mm)+");\n")
130
if mm == 1 : csg.write("}\n")
132
elif ob.TypeId == "Part::Torus":
136
if ob.Angle3 == 360.00:
137
mm = check_multmatrix(csg, ob, 0, 0, 0)
138
csg.write("rotate_extrude("+convexity+", $fn = 0, "+fafs+")\n")
139
csg.write("multmatrix([[1, 0, 0, "+str(ob.Radius1)+"], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])\n")
140
csg.write("circle($fn = 0, "+fafs+", r = "+str(ob.Radius2)+");\n")
141
if mm == 1 : csg.write("}\n")
142
else: # Cannot convert to rotate extrude so best effort is polyhedron
143
csg.write('%s\n' % shape2polyhedron(ob.Shape))
145
elif ob.TypeId == "Part::Prism":
148
# r = str(ob.Length/2.0/math.sin(math.pi/ob.Polygon))
149
r = str(ob.Circumradius) # length seems to be the outer radius
150
h = str(ob.Height.Value)
151
mm = check_multmatrix(csg, ob, 0, 0, -float(h)/2)
152
csg.write("cylinder($fn = "+f+", "+fafs+", h = "+h+", r1 = "+r+\
153
", r2 = "+r+", center = "+center(mm)+");\n")
154
if mm == 1: csg.write("}\n")
156
elif ob.TypeId == "Part::RegularPolygon":
157
mm = check_multmatrix(csg, ob, 0, 0, -float(h)/2)
158
csg.write("circle($fn = "+str(ob.NumberOfSides)+", "+fafs+", r = "+str(ob.Radius)+");\n")
159
if mm == 1: csg.write("}\n")
161
elif ob.TypeId == "Part::Extrusion":
165
if ob.Base.isDerivedFrom('Part::Part2DObjectPython') and \
166
hasattr(ob.Base,'Proxy') and hasattr(ob.Base.Proxy, 'TypeId'):
167
ptype = ob.Base.Proxy.TypeId
168
if ptype == "Polygon":
169
f = str(ob.Base.FacesNumber)
170
r = str(ob.Base.Radius)
172
print("Faces : " + f)
173
print("Radius : " + r)
174
print("Height : " + h)
175
mm = check_multmatrix(csg,ob,0,0,-float(h)/2)
176
csg.write("cylinder($fn = "+f+", "+fafs+", h = "+h+", r1 = "+r+\
177
", r2 = "+r+", center = "+center(mm)+");\n")
178
if mm == 1: csg.write("}\n")
180
elif ptype == "Circle":
181
r = str(ob.Base.Radius)
183
print("Radius : " + r)
184
print("Height : " + h)
185
mm = check_multmatrix(csg,ob,0,0,-float(h)/2)
186
csg.write("cylinder($fn = 0, "+fafs+", h = "+h+", r1 = "+r+\
187
", r2 = "+r+", center = "+center(mm)+");\n")
188
if mm == 1: csg.write("}\n")
190
elif ptype == "Wire":
191
print("Wire extrusion")
193
mm = check_multmatrix(csg, ob, 0, 0, 0)
194
csg.write("linear_extrude(height = "+str(ob.Dir[2])+", center = "+center(mm)+", "+convexity+", twist = 0, slices = 2, $fn = 0, "+fafs+")\n{\n")
195
csg.write(vertexs2polygon(ob.Base.Shape.Vertexes))
196
if mm == 1: csg.write("}\n")
198
elif ob.Base.isDerivedFrom('Part::Plane'):
199
mm = check_multmatrix(csg,ob,0,0,0)
200
csg.write("linear_extrude(height = "+str(ob.Dir[2])+", center = true, "+convexity+", twist = 0, slices = 2, $fn = 0, "+fafs+")\n{\n")
201
csg.write("square (size = ["+str(ob.Base.Length.Value)+", "+str(ob.Base.Width.Value)+"], center = "+center(mm)+");\n}\n")
202
if mm == 1: csg.write("}\n")
203
elif ob.Base.Name.startswith('this_is_a_bad_idea'):
206
pass # There should be a fallback solution
208
elif ob.TypeId == "Part::Cut":
210
csg.write("difference() {\n")
211
process_object(csg,ob.Base)
212
process_object(csg,ob.Tool)
215
elif ob.TypeId == "Part::Fuse":
217
csg.write("union() {\n")
218
process_object(csg,ob.Base)
219
process_object(csg,ob.Tool)
222
elif ob.TypeId == "Part::Common":
223
print("intersection")
224
csg.write("intersection() {\n")
225
process_object(csg,ob.Base)
226
process_object(csg,ob.Tool)
229
elif ob.TypeId == "Part::MultiFuse":
230
print("Multi Fuse / union")
231
csg.write("union() {\n")
232
for subobj in ob.Shapes:
233
process_object(csg,subobj)
236
elif ob.TypeId == "Part::MultiCommon":
237
print("Multi Common / intersection")
238
csg.write("intersection() {\n")
239
for subobj in ob.Shapes:
240
process_object(csg,subobj)
243
elif ob.isDerivedFrom('Part::Feature'):
244
print("Part::Feature")
245
mm = check_multmatrix(csg,ob,0,0,0)
246
csg.write('%s\n' % shape2polyhedron(ob.Shape))
247
if mm == 1 : csg.write("}\n")
249
def export(exportList, filename):
250
"called when FreeCAD exports a file"
253
print("\nStart Export 0.1d\n")
254
print("Open Output File")
255
csg = pyopen(filename,'w')
256
print("Write Initial Output")
257
# Not sure if comments as per scad are allowed in csg file
258
csg.write("// CSG file generated from FreeCAD %s\n" % \
259
'.'.join(FreeCAD.Version()[0:3]))
260
#write initial group statements - not sure if required
261
csg.write("group() {\n group(){\n")
262
for ob in exportList:
264
print("Name : " + ob.Name)
265
print("Type : " + ob.TypeId)
268
process_object(csg, ob)
270
# write closing group braces
274
FreeCAD.Console.PrintMessage("successfully exported" + " " + filename)