22
__title__ = "FreeCAD OpenSCAD Workbench - GUI Commands"
23
__author__ = "Sebastian Hoogen"
24
__url__ = ["https://www.freecad.org"]
27
This Script includes the GUI Commands of the OpenSCAD module
35
from PySide import QtCore, QtGui
40
translate = FreeCAD.Qt.translate
45
return FreeCADGui.Selection.countObjectsOfType('Part::Feature') > 0
48
def isdefault(shapecolor):
49
def comparefloat(f1,f2):
53
return abs((f1-f2)/f1) < 2**-24
54
scol=FreeCAD.ParamGet("User parameter:BaseApp/Preferences/View").GetUnsigned('DefaultShapeColor',0xccccccff)
55
defaultcolor = (((scol >> 24) & 0xff) / 255.0,
56
((scol >> 16) & 0xff) / 255.0,
57
((scol >> 8) & 0xff) / 255.0, 0.0)
58
return all(all(comparefloat(fcc,dcc) for fcc,dcc in \
59
zip(facecolor,defaultcolor)) for facecolor in shapecolor)
61
def isgrey(shapecolor):
62
defaultcolor=(float.fromhex('0x1.99999ap-1'),float.fromhex(\
63
'0x1.99999ap-1'),float.fromhex('0x1.99999ap-1'),0.0)
64
return all(facecolor == defaultcolor for facecolor in shapecolor)
66
def randomcolor(transp=0.0):
68
return (random.random(),random.random(),random.random(),transp)
70
def explode(obj,color=True):
71
if obj.isDerivedFrom('Part::Fuse') or \
72
obj.isDerivedFrom('Part::MultiFuse') or \
73
obj.isDerivedFrom('Part::Compound'):
75
outlist = obj.OutList[:]
76
if plm.isNull() or all((len(oo.InList)==1 and \
77
not oo.isDerivedFrom('PartDesign::Feature')) \
78
for oo in obj.OutList):
79
obj.Document.removeObject(obj.Name)
82
oo.Placement=plm.multiply(oo.Placement)
86
if color and isdefault(oo.ViewObject.DiffuseColor):
88
oo.ViewObject.DiffuseColor=randomcolor()
90
oo.ViewObject.DiffuseColor=color
92
FreeCAD.Console.PrintError(translate('OpenSCAD', 'Unable to explode %s') % obj.Name + '\n')
94
for obj in FreeCADGui.Selection.getSelection():
95
if len(obj.InList) == 0:
98
def GetResources(self):
99
return {'Pixmap' : 'OpenSCAD_Explode_Group',
100
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ExplodeGroup', 'Explode Group'),
101
'ToolTip': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ExplodeGroup', 'Remove fusion, apply placement to children, and color randomly')}
104
"Change the Color of selected or all Shapes based on their validity"
106
import colorcodeshapes
107
selection=FreeCADGui.Selection.getSelectionEx()
108
if len(selection) > 0:
109
objs=[selobj.Object for selobj in selection]
112
objs=FreeCAD.ActiveDocument.Objects
113
colorcodeshapes.colorcodeshapes(objs)
114
def GetResources(self):
115
return {'Pixmap' : 'OpenSCAD_ColorCodeShape',
116
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ColorCodeShape', 'Color Shapes'),
117
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ColorCodeShape', 'Color Shapes by validity and type')}
121
return FreeCADGui.Selection.countObjectsOfType('Part::Feature') > 0
124
from OpenSCAD2Dgeom import edgestofaces,Overlappingfaces
125
selection=FreeCADGui.Selection.getSelectionEx()
127
for selobj in selection:
128
edges.extend(selobj.Object.Shape.Edges)
129
Overlappingfaces(edgestofaces(edges,None)).makefeatures(FreeCAD.ActiveDocument)
130
for selobj in selection:
131
selobj.Object.ViewObject.hide()
132
FreeCAD.ActiveDocument.recompute()
134
def GetResources(self):
135
return {'Pixmap' : 'OpenSCAD_Edgestofaces',
136
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_Edgestofaces', 'Convert Edges To Faces'),
137
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD', 'Convert Edges to Faces')}
139
class RefineShapeFeature:
141
return FreeCADGui.Selection.countObjectsOfType('Part::Feature') > 0
145
import OpenSCADFeatures
146
selection=FreeCADGui.Selection.getSelectionEx()
147
for selobj in selection:
148
newobj=selobj.Document.addObject("Part::FeaturePython",'refine')
149
OpenSCADFeatures.RefineShape(newobj,selobj.Object)
150
OpenSCADFeatures.ViewProviderTree(newobj.ViewObject)
151
newobj.Label='refine_%s' % selobj.Object.Label
152
selobj.Object.ViewObject.hide()
153
FreeCAD.ActiveDocument.recompute()
154
def GetResources(self):
155
return {'Pixmap' : 'OpenSCAD_RefineShapeFeature',
156
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_RefineShapeFeature', 'Refine Shape Feature'),
157
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_RefineShapeFeature', 'Create Refine Shape Feature')}
159
class MirrorMeshFeature:
161
return FreeCADGui.Selection.countObjectsOfType('Mesh::Feature') > 0
164
selection=FreeCADGui.Selection.getSelectionEx()
165
for selobj in selection:
166
newobj=selobj.Document.addObject("Mesh::Feature",'mirror')
167
newobj.Label='mirror_%s' % selobj.Object.Label
168
msh=selobj.Object.Mesh
169
items=["[1;0;0]","[0;1;0]","[0;0;1]","[1;1;0]","[0;1;1]","[1;0;1]","[1;1;1]"]
170
item, ok = QtGui.QInputDialog.getItem(QtGui.QApplication.activeWindow(),'Mirror about which Axis?','Select Axis (or enter custom value):',items,editable=True)
172
splits = list(item.replace('[','').replace(']','').split(';'))
176
vec = FreeCAD.Base.Vector(x,y,z)
177
newmesh=OpenSCADUtils.mirrormesh(msh, vec)
179
selobj.Object.ViewObject.hide()
181
selobj.Document.removeObject(newobj.Name)
183
FreeCAD.ActiveDocument.recompute()
184
def GetResources(self):
185
return {'Pixmap' : 'OpenSCAD_MirrorMeshFeature',
186
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_MirrorMeshFeature', 'Mirror Mesh Feature...'),
187
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_MirrorMeshFeature', 'Create Mirror Mesh Feature')}
189
class ScaleMeshFeature:
191
return FreeCADGui.Selection.countObjectsOfType('Mesh::Feature') > 0
194
selection=FreeCADGui.Selection.getSelectionEx()
195
for selobj in selection:
196
newobj=selobj.Document.addObject("Mesh::Feature",'scale')
197
newobj.Label='scale_%s' % selobj.Object.Label
198
msh=selobj.Object.Mesh
200
item, ok = QtGui.QInputDialog.getItem(QtGui.QApplication.activeWindow(),'Scale about which Axis?','Enter scaling value:',items,editable=True)
202
splits = list(item.replace('[','').replace(']','').split(';'))
206
vec = FreeCAD.Base.Vector(x,y,z)
207
newmesh=OpenSCADUtils.scalemesh(msh, vec)
209
selobj.Object.ViewObject.hide()
211
selobj.Document.removeObject(newobj.Name)
212
FreeCAD.ActiveDocument.recompute()
213
def GetResources(self):
214
return {'Pixmap' : 'OpenSCAD_ScaleMeshFeature',
215
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ScaleMeshFeature', 'Scale Mesh Feature...'),
216
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ScaleMeshFeature', 'Create Scale Mesh Feature')}
219
class ResizeMeshFeature:
221
return FreeCADGui.Selection.countObjectsOfType('Mesh::Feature') > 0
224
selection=FreeCADGui.Selection.getSelectionEx()
225
for selobj in selection:
226
newobj=selobj.Document.addObject("Mesh::Feature",'resize')
227
newobj.Label='resize_%s' % selobj.Object.Label
228
msh=selobj.Object.Mesh
230
item, ok = QtGui.QInputDialog.getItem(QtGui.QApplication.activeWindow(),'Resize about which Axis?','Enter resizing value:',items,editable=True)
232
splits = list(item.replace('[','').replace(']','').split(';'))
236
vec = FreeCAD.Base.Vector(x,y,z)
237
newmesh=OpenSCADUtils.resizemesh(msh, vec)
239
selobj.Object.ViewObject.hide()
241
selobj.Document.removeObject(newobj.Name)
242
FreeCAD.ActiveDocument.recompute()
243
def GetResources(self):
244
return {'Pixmap' : 'OpenSCAD_ResizeMeshFeature',
245
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ResizeMeshFeature', 'Resize Mesh Feature...'),
246
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ResizeMeshFeature', 'Create Resize Mesh Feature')}
249
class IncreaseToleranceFeature:
251
return FreeCADGui.Selection.countObjectsOfType('Part::Feature') > 0
255
import OpenSCADFeatures
256
selection=FreeCADGui.Selection.getSelectionEx()
257
for selobj in selection:
258
newobj=selobj.Document.addObject("Part::FeaturePython",'tolerance')
259
OpenSCADFeatures.IncreaseTolerance(newobj,selobj.Object)
260
OpenSCADFeatures.ViewProviderTree(newobj.ViewObject)
261
newobj.Label='tolerance_%s' % selobj.Object.Label
262
selobj.Object.ViewObject.hide()
263
FreeCAD.ActiveDocument.recompute()
264
def GetResources(self):
265
return {'Pixmap' : 'OpenSCAD_IncreaseToleranceFeature',
266
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_IncreaseToleranceFeature', 'Increase Tolerance Feature'),
267
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_IncreaseToleranceFeature', 'Create Feature that allows increasing the tolerance')}
269
class ExpandPlacements:
270
'''This should aid interactive repair in the future
271
but currently it breaks extrusions, as axis, base and so on have to be
274
return FreeCADGui.Selection.countObjectsOfType('Part::Feature') > 0
277
import expandplacements
278
for selobj in FreeCADGui.Selection.getSelectionEx():
279
expandplacements.expandplacements(selobj.Object,FreeCAD.Placement())
280
FreeCAD.ActiveDocument.recompute()
281
def GetResources(self):
282
return {'Pixmap' : 'OpenSCAD_ExpandPlacements',
283
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ExpandPlacements', 'Expand Placements'),
284
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ExpandPlacements', 'Expand all placements downwards in the Tree view')}
288
nobj = FreeCADGui.Selection.countObjectsOfType('Part::Feature')
289
if nobj == 3: return True
290
elif nobj == 2: return tuple((len(obj.InList)) for obj in \
291
FreeCADGui.Selection.getSelection()) in ((0,1),(1,0))
296
objs=FreeCADGui.Selection.getSelection()
298
tuple((len(obj.InList)) for obj in objs) in ((0,1),(1,0)):
299
replaceobj.replaceobjfromselection(objs)
301
FreeCAD.Console.PrintError(translate('OpenSCAD', 'Please select 3 objects first')+ '\n')
302
def GetResources(self):
303
return {'Pixmap' : 'OpenSCAD_ReplaceObject',
304
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ReplaceObject', 'Replace Object'),
305
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ReplaceObject', 'Replace an object in the Tree view. Please select old, new, and parent object')}
309
return FreeCADGui.Selection.countObjectsOfType('Part::Feature') > 0
313
OpenSCADUtils.removesubtree(FreeCADGui.Selection.getSelection())
315
def GetResources(self):
316
return {'Pixmap' : 'OpenSCAD_RemoveSubtree',
317
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_RemoveSubtree', 'Remove Objects and their Children'),
318
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_RemoveSubtree', 'Removes the selected objects and all children that are not referenced from other objects')}
320
class AddSCADWidget(QtGui.QWidget):
321
def __init__(self,*args):
322
QtGui.QWidget.__init__(self,*args)
324
self.textEdit=QtGui.QTextEdit()
325
self.textEdit.setAcceptRichText(False)
329
self.textMsg=QtGui.QPlainTextEdit()
330
self.textMsg.setReadOnly(True)
331
h = int(2.5 * self.textMsg.fontMetrics().height())
332
self.textMsg.setMaximumHeight(h)
333
self.textMsg.resize(self.textMsg.width(),h)
334
self.buttonadd = QtGui.QPushButton(translate('OpenSCAD','Add'))
335
self.buttonrefresh = QtGui.QPushButton(translate('OpenSCAD','Refresh'))
336
self.buttonclear = QtGui.QPushButton(translate('OpenSCAD','Clear code'))
337
self.buttonload = QtGui.QPushButton(translate('OpenSCAD','Open...'))
338
self.buttonsave = QtGui.QPushButton(translate('OpenSCAD','Save...'))
339
self.checkboxmesh = QtGui.QCheckBox(translate('OpenSCAD','as Mesh'))
340
layouth=QtGui.QHBoxLayout()
341
layouth.addWidget(self.buttonadd)
342
layouth.addWidget(self.buttonrefresh)
343
layouth.addWidget(self.buttonload)
344
layouth.addWidget(self.buttonsave)
345
layouth.addWidget(self.buttonclear)
346
layout= QtGui.QVBoxLayout()
347
layout.addLayout(layouth)
348
layout.addWidget(self.checkboxmesh)
349
layout.addWidget(self.textEdit)
350
layout.addWidget(self.textMsg)
351
self.setLayout(layout)
352
self.setWindowTitle(translate('OpenSCAD','Add OpenSCAD Element'))
353
self.textEdit.setText(u'cube();')
355
def undoable_clear():
356
"""Clears the textEdit in a way that allows undo of the action"""
357
self.textEdit.setFocus()
358
self.textEdit.selectAll()
359
keypress = QtGui.QKeyEvent(QtGui.QKeyEvent.KeyPress, QtCore.Qt.Key_Delete, QtCore.Qt.NoModifier)
360
QtGui.QGuiApplication.sendEvent(self.textEdit, keypress)
362
self.buttonclear.clicked.connect(undoable_clear)
364
def retranslateUi(self, widget=None):
365
self.buttonadd.setText(translate('OpenSCAD','Add'))
366
self.buttonload.setText(translate('OpenSCAD','Load'))
367
self.buttonsave.setText(translate('OpenSCAD','Save'))
368
self.buttonrefresh.setText(translate('OpenSCAD','Refresh'))
369
self.buttonclear.setText(translate('OpenSCAD','Clear'))
370
self.checkboxmesh.setText(translate('OpenSCAD','as Mesh'))
371
self.setWindowTitle(translate('OpenSCAD','Add OpenSCAD Element'))
375
self.form = AddSCADWidget()
376
self.form.buttonadd.clicked.connect(self.addelement)
377
self.form.buttonload.clicked.connect(self.loadelement)
378
self.form.buttonsave.clicked.connect(self.saveelement)
379
self.form.buttonrefresh.clicked.connect(self.refreshelement)
381
def getStandardButtons(self):
382
return int(QtGui.QDialogButtonBox.Close)
384
def isAllowedAlterSelection(self):
387
def isAllowedAlterView(self):
390
def isAllowedAlterDocument(self):
393
def addelement(self):
394
scadstr=self.form.textEdit.toPlainText()
395
asmesh=self.form.checkboxmesh.checkState()
398
extension= 'stl' if asmesh else 'csg'
400
tmpfilename=OpenSCADUtils.callopenscadstring(scadstr,extension)
401
doc=FreeCAD.activeDocument() or FreeCAD.newDocument()
404
Mesh.insert(tmpfilename,doc.Name)
407
importCSG.insert(tmpfilename,doc.Name)
409
os.unlink(tmpfilename)
413
except OpenSCADUtils.OpenSCADError as e:
414
self.form.textMsg.setPlainText(e.value)
415
FreeCAD.Console.PrintError(e.value)
417
def refreshelement(self):
418
self.form.textMsg.setPlainText('')
419
doc=FreeCAD.activeDocument()
421
for obj in doc.Objects :
422
doc.removeObject(obj.Name)
425
def loadelement(self):
426
filename, _ = QtGui.QFileDialog.getOpenFileName(
428
caption=translate("OpenSCAD", "Open file"),
430
filter=translate("OpenSCAD", "OpenSCAD Files") + " (*.scad *.csg)"
434
print('filename :'+filename)
435
with open(filename,'r') as fp :
437
self.form.textEdit.setText(data)
439
def saveelement(self) :
440
filename, _ = QtGui.QFileDialog.getSaveFileName(
442
caption=translate("OpenSCAD", "Save file"),
444
filter=translate("OpenSCAD", "OpenSCAD Files") + " (*.scad *.csg)"
448
Text = self.form.textEdit.toPlainText()
449
with open(filename,'w') as fp :
452
class OpenSCADMeshBooleanWidget(QtGui.QWidget):
453
def __init__(self,*args):
454
QtGui.QWidget.__init__(self,*args)
456
self.buttonadd = QtGui.QPushButton(translate('OpenSCAD','Perform'))
457
self.rb_group = QtGui.QButtonGroup()
458
self.rb_group_box = QtGui.QGroupBox()
459
self.rb_group_box_layout = QtGui.QVBoxLayout()
460
self.rb_group_box.setLayout(self.rb_group_box_layout)
461
self.rb_union = QtGui.QRadioButton("Union")
462
self.rb_group.addButton(self.rb_union)
463
self.rb_group_box_layout.addWidget(self.rb_union)
464
self.rb_intersection = QtGui.QRadioButton("Intersection")
465
self.rb_group.addButton(self.rb_intersection)
466
self.rb_group_box_layout.addWidget(self.rb_intersection)
467
self.rb_difference = QtGui.QRadioButton("Difference")
468
self.rb_group.addButton(self.rb_difference)
469
self.rb_group_box_layout.addWidget(self.rb_difference)
470
self.rb_hull = QtGui.QRadioButton("Hull")
471
self.rb_group.addButton(self.rb_hull)
472
self.rb_group_box_layout.addWidget(self.rb_hull)
473
self.rb_minkowski = QtGui.QRadioButton("Minkowski sum")
474
self.rb_group.addButton(self.rb_minkowski)
475
self.rb_group_box_layout.addWidget(self.rb_minkowski)
476
layouth=QtGui.QHBoxLayout()
477
layouth.addWidget(self.buttonadd)
478
layout= QtGui.QVBoxLayout()
479
layout.addLayout(layouth)
480
layout.addWidget(self.rb_group_box)
481
self.setLayout(layout)
482
self.setWindowTitle(translate('OpenSCAD','Mesh Boolean'))
484
def retranslateUi(self, widget=None):
485
self.buttonadd.setText(translate('OpenSCAD','Perform'))
486
self.setWindowTitle(translate('OpenSCAD','Mesh Boolean'))
487
self.rb_minkowski.setText(translate('OpenSCAD','Minkowski sum'))
489
class OpenSCADMeshBooleanTask:
491
self.form = OpenSCADMeshBooleanWidget()
492
self.form.buttonadd.clicked.connect(self.doboolean)
494
def getStandardButtons(self):
495
return int(QtGui.QDialogButtonBox.Close)
497
def isAllowedAlterSelection(self):
500
def isAllowedAlterView(self):
503
def isAllowedAlterDocument(self):
507
from OpenSCADUtils import meshoponobjs
508
if self.form.rb_intersection.isChecked(): opname = 'intersection'
509
elif self.form.rb_difference.isChecked(): opname = 'difference'
510
elif self.form.rb_hull.isChecked(): opname = 'hull'
511
elif self.form.rb_minkowski.isChecked(): opname = 'minkowski'
512
else: opname = 'union'
513
newmesh,objsused = meshoponobjs(opname,FreeCADGui.Selection.getSelection())
514
if len(objsused) > 0:
515
newmeshobj = FreeCAD.activeDocument().addObject('Mesh::Feature',opname)
516
newmeshobj.Mesh = newmesh
518
obj.ViewObject.hide()
520
class AddOpenSCADElement:
522
return not FreeCADGui.Control.activeDialog()
525
panel = AddSCADTask()
526
FreeCADGui.Control.showDialog(panel)
528
def GetResources(self):
529
return {'Pixmap' : 'OpenSCAD_AddOpenSCADElement',
530
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_AddOpenSCADElement', 'Add OpenSCAD Element...'),
531
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_AddOpenSCADElement',
532
'Add an OpenSCAD element by entering OpenSCAD code and executing the OpenSCAD binary')}
534
class OpenSCADMeshBoolean:
536
return not FreeCADGui.Control.activeDialog() and \
537
len(FreeCADGui.Selection.getSelection()) >= 1
540
panel = OpenSCADMeshBooleanTask()
541
FreeCADGui.Control.showDialog(panel)
543
def GetResources(self):
544
return {'Pixmap' : 'OpenSCAD_MeshBooleans',
545
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_MeshBoolean','Mesh Boolean...'),
546
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_MeshBoolean',
547
'Export objects as meshes and use OpenSCAD to perform a boolean operation')}
551
return len(FreeCADGui.Selection.getSelection()) >= 1
555
import OpenSCADFeatures
557
selection=FreeCADGui.Selection.getSelectionEx()
559
for selobj in selection:
560
objList.append(selobj.Object)
561
selobj.Object.ViewObject.hide()
562
importCSG.process_ObjectsViaOpenSCAD(FreeCAD.activeDocument(),objList,"hull")
563
FreeCAD.ActiveDocument.recompute()
564
def GetResources(self):
565
return {'Pixmap' : 'OpenSCAD_Hull',
566
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_Hull', 'Hull'),
567
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_Hull', 'Use OpenSCAD to create a hull')}
571
return len(FreeCADGui.Selection.getSelection()) >= 1
575
import OpenSCADFeatures
577
selection=FreeCADGui.Selection.getSelectionEx()
579
for selobj in selection:
580
objList.append(selobj.Object)
581
selobj.Object.ViewObject.hide()
582
importCSG.process_ObjectsViaOpenSCAD(FreeCAD.activeDocument(),objList,"minkowski")
583
FreeCAD.ActiveDocument.recompute()
584
def GetResources(self):
585
return {'Pixmap' : 'OpenSCAD_Minkowski',
586
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_Minkowski', 'Minkowski sum'),
587
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_Minkowski', 'Use OpenSCAD to create a Minkowski sum')}
589
FreeCADGui.addCommand('OpenSCAD_ColorCodeShape',ColorCodeShape())
590
FreeCADGui.addCommand('OpenSCAD_ExplodeGroup',ExplodeGroup())
591
FreeCADGui.addCommand('OpenSCAD_Edgestofaces',Edgestofaces())
592
FreeCADGui.addCommand('OpenSCAD_RefineShapeFeature',RefineShapeFeature())
593
FreeCADGui.addCommand('OpenSCAD_MirrorMeshFeature',MirrorMeshFeature())
594
FreeCADGui.addCommand('OpenSCAD_ScaleMeshFeature',ScaleMeshFeature())
595
FreeCADGui.addCommand('OpenSCAD_ResizeMeshFeature',ResizeMeshFeature())
596
FreeCADGui.addCommand('OpenSCAD_IncreaseToleranceFeature',IncreaseToleranceFeature())
597
FreeCADGui.addCommand('OpenSCAD_ExpandPlacements',ExpandPlacements())
598
FreeCADGui.addCommand('OpenSCAD_ReplaceObject',ReplaceObject())
599
FreeCADGui.addCommand('OpenSCAD_RemoveSubtree',RemoveSubtree())
600
FreeCADGui.addCommand('OpenSCAD_AddOpenSCADElement',AddOpenSCADElement())
601
FreeCADGui.addCommand('OpenSCAD_MeshBoolean',OpenSCADMeshBoolean())
602
FreeCADGui.addCommand('OpenSCAD_Hull',Hull())
603
FreeCADGui.addCommand('OpenSCAD_Minkowski',Minkowski())