604 lines
27 KiB
Python
604 lines
27 KiB
Python
#***************************************************************************
|
|
#* Copyright (c) 2012 Sebastian Hoogen <github@sebastianhoogen.de> *
|
|
#* *
|
|
#* 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 *
|
|
#* *
|
|
#***************************************************************************
|
|
|
|
__title__ = "FreeCAD OpenSCAD Workbench - GUI Commands"
|
|
__author__ = "Sebastian Hoogen"
|
|
__url__ = ["https://www.freecad.org"]
|
|
|
|
'''
|
|
This Script includes the GUI Commands of the OpenSCAD module
|
|
'''
|
|
|
|
import FreeCAD
|
|
import OpenSCADUtils
|
|
|
|
if FreeCAD.GuiUp:
|
|
import FreeCADGui
|
|
from PySide import QtCore, QtGui
|
|
gui = True
|
|
else:
|
|
gui = False
|
|
|
|
translate = FreeCAD.Qt.translate
|
|
|
|
class ExplodeGroup:
|
|
"Ungroup Objects"
|
|
def IsActive(self):
|
|
return FreeCADGui.Selection.countObjectsOfType('Part::Feature') > 0
|
|
|
|
def Activated(self):
|
|
def isdefault(shapecolor):
|
|
def comparefloat(f1,f2):
|
|
if f1 == 0.0:
|
|
return f1 == f2
|
|
else:
|
|
return abs((f1-f2)/f1) < 2**-24
|
|
scol=FreeCAD.ParamGet("User parameter:BaseApp/Preferences/View").GetUnsigned('DefaultShapeColor',0xccccccff)
|
|
defaultcolor = (((scol >> 24) & 0xff) / 255.0,
|
|
((scol >> 16) & 0xff) / 255.0,
|
|
((scol >> 8) & 0xff) / 255.0, 0.0)
|
|
return all(all(comparefloat(fcc,dcc) for fcc,dcc in \
|
|
zip(facecolor,defaultcolor)) for facecolor in shapecolor)
|
|
|
|
def isgrey(shapecolor):
|
|
defaultcolor=(float.fromhex('0x1.99999ap-1'),float.fromhex(\
|
|
'0x1.99999ap-1'),float.fromhex('0x1.99999ap-1'),0.0)
|
|
return all(facecolor == defaultcolor for facecolor in shapecolor)
|
|
|
|
def randomcolor(transp=0.0):
|
|
import random
|
|
return (random.random(),random.random(),random.random(),transp)
|
|
|
|
def explode(obj,color=True):
|
|
if obj.isDerivedFrom('Part::Fuse') or \
|
|
obj.isDerivedFrom('Part::MultiFuse') or \
|
|
obj.isDerivedFrom('Part::Compound'):
|
|
plm = obj.Placement
|
|
outlist = obj.OutList[:]
|
|
if plm.isNull() or all((len(oo.InList)==1 and \
|
|
not oo.isDerivedFrom('PartDesign::Feature')) \
|
|
for oo in obj.OutList):
|
|
obj.Document.removeObject(obj.Name)
|
|
for oo in outlist:
|
|
if not plm.isNull():
|
|
oo.Placement=plm.multiply(oo.Placement)
|
|
if FreeCAD.GuiUp:
|
|
import FreeCADGui
|
|
oo.ViewObject.show()
|
|
if color and isdefault(oo.ViewObject.DiffuseColor):
|
|
if color:
|
|
oo.ViewObject.DiffuseColor=randomcolor()
|
|
else:
|
|
oo.ViewObject.DiffuseColor=color
|
|
else:
|
|
FreeCAD.Console.PrintError(translate('OpenSCAD', 'Unable to explode %s') % obj.Name + '\n')
|
|
|
|
for obj in FreeCADGui.Selection.getSelection():
|
|
if len(obj.InList) == 0: # allowed only for for top level objects
|
|
explode(obj)
|
|
|
|
def GetResources(self):
|
|
return {'Pixmap' : 'OpenSCAD_Explode_Group',
|
|
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ExplodeGroup', 'Explode Group'),
|
|
'ToolTip': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ExplodeGroup', 'Remove fusion, apply placement to children, and color randomly')}
|
|
|
|
class ColorCodeShape:
|
|
"Change the Color of selected or all Shapes based on their validity"
|
|
def Activated(self):
|
|
import colorcodeshapes
|
|
selection=FreeCADGui.Selection.getSelectionEx()
|
|
if len(selection) > 0:
|
|
objs=[selobj.Object for selobj in selection]
|
|
|
|
else:
|
|
objs=FreeCAD.ActiveDocument.Objects
|
|
colorcodeshapes.colorcodeshapes(objs)
|
|
def GetResources(self):
|
|
return {'Pixmap' : 'OpenSCAD_ColorCodeShape',
|
|
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ColorCodeShape', 'Color Shapes'),
|
|
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ColorCodeShape', 'Color Shapes by validity and type')}
|
|
|
|
class Edgestofaces:
|
|
def IsActive(self):
|
|
return FreeCADGui.Selection.countObjectsOfType('Part::Feature') > 0
|
|
|
|
def Activated(self):
|
|
from OpenSCAD2Dgeom import edgestofaces,Overlappingfaces
|
|
selection=FreeCADGui.Selection.getSelectionEx()
|
|
edges=[]
|
|
for selobj in selection:
|
|
edges.extend(selobj.Object.Shape.Edges)
|
|
Overlappingfaces(edgestofaces(edges,None)).makefeatures(FreeCAD.ActiveDocument)
|
|
for selobj in selection:
|
|
selobj.Object.ViewObject.hide()
|
|
FreeCAD.ActiveDocument.recompute()
|
|
|
|
def GetResources(self):
|
|
return {'Pixmap' : 'OpenSCAD_Edgestofaces',
|
|
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_Edgestofaces', 'Convert Edges To Faces'),
|
|
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD', 'Convert Edges to Faces')}
|
|
|
|
class RefineShapeFeature:
|
|
def IsActive(self):
|
|
return FreeCADGui.Selection.countObjectsOfType('Part::Feature') > 0
|
|
|
|
def Activated(self):
|
|
import Part
|
|
import OpenSCADFeatures
|
|
selection=FreeCADGui.Selection.getSelectionEx()
|
|
for selobj in selection:
|
|
newobj=selobj.Document.addObject("Part::FeaturePython",'refine')
|
|
OpenSCADFeatures.RefineShape(newobj,selobj.Object)
|
|
OpenSCADFeatures.ViewProviderTree(newobj.ViewObject)
|
|
newobj.Label='refine_%s' % selobj.Object.Label
|
|
selobj.Object.ViewObject.hide()
|
|
FreeCAD.ActiveDocument.recompute()
|
|
def GetResources(self):
|
|
return {'Pixmap' : 'OpenSCAD_RefineShapeFeature',
|
|
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_RefineShapeFeature', 'Refine Shape Feature'),
|
|
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_RefineShapeFeature', 'Create Refine Shape Feature')}
|
|
|
|
class MirrorMeshFeature:
|
|
def IsActive(self):
|
|
return FreeCADGui.Selection.countObjectsOfType('Mesh::Feature') > 0
|
|
|
|
def Activated(self):
|
|
selection=FreeCADGui.Selection.getSelectionEx()
|
|
for selobj in selection:
|
|
newobj=selobj.Document.addObject("Mesh::Feature",'mirror')
|
|
newobj.Label='mirror_%s' % selobj.Object.Label
|
|
msh=selobj.Object.Mesh
|
|
items=["[1;0;0]","[0;1;0]","[0;0;1]","[1;1;0]","[0;1;1]","[1;0;1]","[1;1;1]"]
|
|
item, ok = QtGui.QInputDialog.getItem(QtGui.QApplication.activeWindow(),'Mirror about which Axis?','Select Axis (or enter custom value):',items,editable=True)
|
|
if ok:
|
|
splits = list(item.replace('[','').replace(']','').split(';'))
|
|
x = float(splits[0])
|
|
y = float(splits[1])
|
|
z = float(splits[2])
|
|
vec = FreeCAD.Base.Vector(x,y,z)
|
|
newmesh=OpenSCADUtils.mirrormesh(msh, vec)
|
|
newobj.Mesh=newmesh
|
|
selobj.Object.ViewObject.hide()
|
|
else:
|
|
selobj.Document.removeObject(newobj.Name)
|
|
|
|
FreeCAD.ActiveDocument.recompute()
|
|
def GetResources(self):
|
|
return {'Pixmap' : 'OpenSCAD_MirrorMeshFeature',
|
|
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_MirrorMeshFeature', 'Mirror Mesh Feature...'),
|
|
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_MirrorMeshFeature', 'Create Mirror Mesh Feature')}
|
|
|
|
class ScaleMeshFeature:
|
|
def IsActive(self):
|
|
return FreeCADGui.Selection.countObjectsOfType('Mesh::Feature') > 0
|
|
|
|
def Activated(self):
|
|
selection=FreeCADGui.Selection.getSelectionEx()
|
|
for selobj in selection:
|
|
newobj=selobj.Document.addObject("Mesh::Feature",'scale')
|
|
newobj.Label='scale_%s' % selobj.Object.Label
|
|
msh=selobj.Object.Mesh
|
|
items=["[1;1;1]"]
|
|
item, ok = QtGui.QInputDialog.getItem(QtGui.QApplication.activeWindow(),'Scale about which Axis?','Enter scaling value:',items,editable=True)
|
|
if ok:
|
|
splits = list(item.replace('[','').replace(']','').split(';'))
|
|
x = float(splits[0])
|
|
y = float(splits[1])
|
|
z = float(splits[2])
|
|
vec = FreeCAD.Base.Vector(x,y,z)
|
|
newmesh=OpenSCADUtils.scalemesh(msh, vec)
|
|
newobj.Mesh=newmesh
|
|
selobj.Object.ViewObject.hide()
|
|
else:
|
|
selobj.Document.removeObject(newobj.Name)
|
|
FreeCAD.ActiveDocument.recompute()
|
|
def GetResources(self):
|
|
return {'Pixmap' : 'OpenSCAD_ScaleMeshFeature',
|
|
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ScaleMeshFeature', 'Scale Mesh Feature...'),
|
|
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ScaleMeshFeature', 'Create Scale Mesh Feature')}
|
|
|
|
|
|
class ResizeMeshFeature:
|
|
def IsActive(self):
|
|
return FreeCADGui.Selection.countObjectsOfType('Mesh::Feature') > 0
|
|
|
|
def Activated(self):
|
|
selection=FreeCADGui.Selection.getSelectionEx()
|
|
for selobj in selection:
|
|
newobj=selobj.Document.addObject("Mesh::Feature",'resize')
|
|
newobj.Label='resize_%s' % selobj.Object.Label
|
|
msh=selobj.Object.Mesh
|
|
items=["[1;1;1]"]
|
|
item, ok = QtGui.QInputDialog.getItem(QtGui.QApplication.activeWindow(),'Resize about which Axis?','Enter resizing value:',items,editable=True)
|
|
if ok:
|
|
splits = list(item.replace('[','').replace(']','').split(';'))
|
|
x = float(splits[0])
|
|
y = float(splits[1])
|
|
z = float(splits[2])
|
|
vec = FreeCAD.Base.Vector(x,y,z)
|
|
newmesh=OpenSCADUtils.resizemesh(msh, vec)
|
|
newobj.Mesh=newmesh
|
|
selobj.Object.ViewObject.hide()
|
|
else:
|
|
selobj.Document.removeObject(newobj.Name)
|
|
FreeCAD.ActiveDocument.recompute()
|
|
def GetResources(self):
|
|
return {'Pixmap' : 'OpenSCAD_ResizeMeshFeature',
|
|
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ResizeMeshFeature', 'Resize Mesh Feature...'),
|
|
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ResizeMeshFeature', 'Create Resize Mesh Feature')}
|
|
|
|
|
|
class IncreaseToleranceFeature:
|
|
def IsActive(self):
|
|
return FreeCADGui.Selection.countObjectsOfType('Part::Feature') > 0
|
|
|
|
def Activated(self):
|
|
import Part
|
|
import OpenSCADFeatures
|
|
selection=FreeCADGui.Selection.getSelectionEx()
|
|
for selobj in selection:
|
|
newobj=selobj.Document.addObject("Part::FeaturePython",'tolerance')
|
|
OpenSCADFeatures.IncreaseTolerance(newobj,selobj.Object)
|
|
OpenSCADFeatures.ViewProviderTree(newobj.ViewObject)
|
|
newobj.Label='tolerance_%s' % selobj.Object.Label
|
|
selobj.Object.ViewObject.hide()
|
|
FreeCAD.ActiveDocument.recompute()
|
|
def GetResources(self):
|
|
return {'Pixmap' : 'OpenSCAD_IncreaseToleranceFeature',
|
|
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_IncreaseToleranceFeature', 'Increase Tolerance Feature'),
|
|
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_IncreaseToleranceFeature', 'Create Feature that allows increasing the tolerance')}
|
|
|
|
class ExpandPlacements:
|
|
'''This should aid interactive repair in the future
|
|
but currently it breaks extrusions, as axis, base and so on have to be
|
|
recalculated'''
|
|
def IsActive(self):
|
|
return FreeCADGui.Selection.countObjectsOfType('Part::Feature') > 0
|
|
|
|
def Activated(self):
|
|
import expandplacements
|
|
for selobj in FreeCADGui.Selection.getSelectionEx():
|
|
expandplacements.expandplacements(selobj.Object,FreeCAD.Placement())
|
|
FreeCAD.ActiveDocument.recompute()
|
|
def GetResources(self):
|
|
return {'Pixmap' : 'OpenSCAD_ExpandPlacements',
|
|
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ExpandPlacements', 'Expand Placements'),
|
|
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ExpandPlacements', 'Expand all placements downwards in the Tree view')}
|
|
|
|
class ReplaceObject:
|
|
def IsActive(self):
|
|
nobj = FreeCADGui.Selection.countObjectsOfType('Part::Feature')
|
|
if nobj == 3: return True
|
|
elif nobj == 2: return tuple((len(obj.InList)) for obj in \
|
|
FreeCADGui.Selection.getSelection()) in ((0,1),(1,0))
|
|
#else: return False
|
|
|
|
def Activated(self):
|
|
import replaceobj
|
|
objs=FreeCADGui.Selection.getSelection()
|
|
if len(objs)==3 or \
|
|
tuple((len(obj.InList)) for obj in objs) in ((0,1),(1,0)):
|
|
replaceobj.replaceobjfromselection(objs)
|
|
else:
|
|
FreeCAD.Console.PrintError(translate('OpenSCAD', 'Please select 3 objects first')+ '\n')
|
|
def GetResources(self):
|
|
return {'Pixmap' : 'OpenSCAD_ReplaceObject',
|
|
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ReplaceObject', 'Replace Object'),
|
|
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_ReplaceObject', 'Replace an object in the Tree view. Please select old, new, and parent object')}
|
|
|
|
class RemoveSubtree:
|
|
def IsActive(self):
|
|
return FreeCADGui.Selection.countObjectsOfType('Part::Feature') > 0
|
|
def Activated(self):
|
|
import OpenSCADUtils
|
|
import FreeCADGui
|
|
OpenSCADUtils.removesubtree(FreeCADGui.Selection.getSelection())
|
|
|
|
def GetResources(self):
|
|
return {'Pixmap' : 'OpenSCAD_RemoveSubtree',
|
|
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_RemoveSubtree', 'Remove Objects and their Children'),
|
|
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_RemoveSubtree', 'Removes the selected objects and all children that are not referenced from other objects')}
|
|
|
|
class AddSCADWidget(QtGui.QWidget):
|
|
def __init__(self,*args):
|
|
QtGui.QWidget.__init__(self,*args)
|
|
# Main text area
|
|
self.textEdit=QtGui.QTextEdit()
|
|
self.textEdit.setAcceptRichText(False)
|
|
#print(self.textEdit.maximumHeight())
|
|
#print(self.textEdit.maximumViewportSize())
|
|
# Message Area
|
|
self.textMsg=QtGui.QPlainTextEdit()
|
|
self.textMsg.setReadOnly(True)
|
|
h = int(2.5 * self.textMsg.fontMetrics().height())
|
|
self.textMsg.setMaximumHeight(h)
|
|
self.textMsg.resize(self.textMsg.width(),h)
|
|
self.buttonadd = QtGui.QPushButton(translate('OpenSCAD','Add'))
|
|
self.buttonrefresh = QtGui.QPushButton(translate('OpenSCAD','Refresh'))
|
|
self.buttonclear = QtGui.QPushButton(translate('OpenSCAD','Clear code'))
|
|
self.buttonload = QtGui.QPushButton(translate('OpenSCAD','Open...'))
|
|
self.buttonsave = QtGui.QPushButton(translate('OpenSCAD','Save...'))
|
|
self.checkboxmesh = QtGui.QCheckBox(translate('OpenSCAD','as Mesh'))
|
|
layouth=QtGui.QHBoxLayout()
|
|
layouth.addWidget(self.buttonadd)
|
|
layouth.addWidget(self.buttonrefresh)
|
|
layouth.addWidget(self.buttonload)
|
|
layouth.addWidget(self.buttonsave)
|
|
layouth.addWidget(self.buttonclear)
|
|
layout= QtGui.QVBoxLayout()
|
|
layout.addLayout(layouth)
|
|
layout.addWidget(self.checkboxmesh)
|
|
layout.addWidget(self.textEdit)
|
|
layout.addWidget(self.textMsg)
|
|
self.setLayout(layout)
|
|
self.setWindowTitle(translate('OpenSCAD','Add OpenSCAD Element'))
|
|
self.textEdit.setText(u'cube();')
|
|
|
|
def undoable_clear():
|
|
"""Clears the textEdit in a way that allows undo of the action"""
|
|
self.textEdit.setFocus()
|
|
self.textEdit.selectAll()
|
|
keypress = QtGui.QKeyEvent(QtGui.QKeyEvent.KeyPress, QtCore.Qt.Key_Delete, QtCore.Qt.NoModifier)
|
|
QtGui.QGuiApplication.sendEvent(self.textEdit, keypress)
|
|
|
|
self.buttonclear.clicked.connect(undoable_clear)
|
|
|
|
def retranslateUi(self, widget=None):
|
|
self.buttonadd.setText(translate('OpenSCAD','Add'))
|
|
self.buttonload.setText(translate('OpenSCAD','Load'))
|
|
self.buttonsave.setText(translate('OpenSCAD','Save'))
|
|
self.buttonrefresh.setText(translate('OpenSCAD','Refresh'))
|
|
self.buttonclear.setText(translate('OpenSCAD','Clear'))
|
|
self.checkboxmesh.setText(translate('OpenSCAD','as Mesh'))
|
|
self.setWindowTitle(translate('OpenSCAD','Add OpenSCAD Element'))
|
|
|
|
class AddSCADTask:
|
|
def __init__(self):
|
|
self.form = AddSCADWidget()
|
|
self.form.buttonadd.clicked.connect(self.addelement)
|
|
self.form.buttonload.clicked.connect(self.loadelement)
|
|
self.form.buttonsave.clicked.connect(self.saveelement)
|
|
self.form.buttonrefresh.clicked.connect(self.refreshelement)
|
|
|
|
def getStandardButtons(self):
|
|
return QtGui.QDialogButtonBox.Close
|
|
|
|
def isAllowedAlterSelection(self):
|
|
return True
|
|
|
|
def isAllowedAlterView(self):
|
|
return True
|
|
|
|
def isAllowedAlterDocument(self):
|
|
return True
|
|
|
|
def addelement(self):
|
|
scadstr=self.form.textEdit.toPlainText()
|
|
asmesh=self.form.checkboxmesh.checkState()
|
|
import OpenSCADUtils
|
|
import os
|
|
extension= 'stl' if asmesh else 'csg'
|
|
try:
|
|
tmpfilename=OpenSCADUtils.callopenscadstring(scadstr,extension)
|
|
doc=FreeCAD.activeDocument() or FreeCAD.newDocument()
|
|
if asmesh:
|
|
import Mesh
|
|
Mesh.insert(tmpfilename,doc.Name)
|
|
else:
|
|
import importCSG
|
|
importCSG.insert(tmpfilename,doc.Name)
|
|
try:
|
|
os.unlink(tmpfilename)
|
|
except OSError:
|
|
pass
|
|
|
|
except OpenSCADUtils.OpenSCADError as e:
|
|
self.form.textMsg.setPlainText(e.value)
|
|
FreeCAD.Console.PrintError(e.value)
|
|
|
|
def refreshelement(self):
|
|
self.form.textMsg.setPlainText('')
|
|
doc=FreeCAD.activeDocument()
|
|
if doc :
|
|
for obj in doc.Objects :
|
|
doc.removeObject(obj.Name)
|
|
self.addelement()
|
|
|
|
def loadelement(self):
|
|
filename, _ = QtGui.QFileDialog.getOpenFileName(
|
|
parent=self.form,
|
|
caption=translate("OpenSCAD", "Open file"),
|
|
dir='.',
|
|
filter=translate("OpenSCAD", "OpenSCAD Files") + " (*.scad *.csg)"
|
|
)
|
|
|
|
if filename:
|
|
print('filename :'+filename)
|
|
with open(filename,'r') as fp :
|
|
data = fp.read()
|
|
self.form.textEdit.setText(data)
|
|
|
|
def saveelement(self) :
|
|
filename, _ = QtGui.QFileDialog.getSaveFileName(
|
|
parent=self.form,
|
|
caption=translate("OpenSCAD", "Save file"),
|
|
dir='.',
|
|
filter=translate("OpenSCAD", "OpenSCAD Files") + " (*.scad *.csg)"
|
|
)
|
|
|
|
if filename:
|
|
Text = self.form.textEdit.toPlainText()
|
|
with open(filename,'w') as fp :
|
|
fp.write(Text)
|
|
|
|
class OpenSCADMeshBooleanWidget(QtGui.QWidget):
|
|
def __init__(self,*args):
|
|
QtGui.QWidget.__init__(self,*args)
|
|
#self.textEdit=QtGui.QTextEdit()
|
|
self.buttonadd = QtGui.QPushButton(translate('OpenSCAD','Perform'))
|
|
self.rb_group = QtGui.QButtonGroup()
|
|
self.rb_group_box = QtGui.QGroupBox()
|
|
self.rb_group_box_layout = QtGui.QVBoxLayout()
|
|
self.rb_group_box.setLayout(self.rb_group_box_layout)
|
|
self.rb_union = QtGui.QRadioButton("Union")
|
|
self.rb_group.addButton(self.rb_union)
|
|
self.rb_group_box_layout.addWidget(self.rb_union)
|
|
self.rb_intersection = QtGui.QRadioButton("Intersection")
|
|
self.rb_group.addButton(self.rb_intersection)
|
|
self.rb_group_box_layout.addWidget(self.rb_intersection)
|
|
self.rb_difference = QtGui.QRadioButton("Difference")
|
|
self.rb_group.addButton(self.rb_difference)
|
|
self.rb_group_box_layout.addWidget(self.rb_difference)
|
|
self.rb_hull = QtGui.QRadioButton("Hull")
|
|
self.rb_group.addButton(self.rb_hull)
|
|
self.rb_group_box_layout.addWidget(self.rb_hull)
|
|
self.rb_minkowski = QtGui.QRadioButton("Minkowski sum")
|
|
self.rb_group.addButton(self.rb_minkowski)
|
|
self.rb_group_box_layout.addWidget(self.rb_minkowski)
|
|
layouth=QtGui.QHBoxLayout()
|
|
layouth.addWidget(self.buttonadd)
|
|
layout= QtGui.QVBoxLayout()
|
|
layout.addLayout(layouth)
|
|
layout.addWidget(self.rb_group_box)
|
|
self.setLayout(layout)
|
|
self.setWindowTitle(translate('OpenSCAD','Mesh Boolean'))
|
|
|
|
def retranslateUi(self, widget=None):
|
|
self.buttonadd.setText(translate('OpenSCAD','Perform'))
|
|
self.setWindowTitle(translate('OpenSCAD','Mesh Boolean'))
|
|
self.rb_minkowski.setText(translate('OpenSCAD','Minkowski sum'))
|
|
|
|
class OpenSCADMeshBooleanTask:
|
|
def __init__(self):
|
|
self.form = OpenSCADMeshBooleanWidget()
|
|
self.form.buttonadd.clicked.connect(self.doboolean)
|
|
|
|
def getStandardButtons(self):
|
|
return QtGui.QDialogButtonBox.Close
|
|
|
|
def isAllowedAlterSelection(self):
|
|
return False
|
|
|
|
def isAllowedAlterView(self):
|
|
return False
|
|
|
|
def isAllowedAlterDocument(self):
|
|
return True
|
|
|
|
def doboolean(self):
|
|
from OpenSCADUtils import meshoponobjs
|
|
if self.form.rb_intersection.isChecked(): opname = 'intersection'
|
|
elif self.form.rb_difference.isChecked(): opname = 'difference'
|
|
elif self.form.rb_hull.isChecked(): opname = 'hull'
|
|
elif self.form.rb_minkowski.isChecked(): opname = 'minkowski'
|
|
else: opname = 'union'
|
|
newmesh,objsused = meshoponobjs(opname,FreeCADGui.Selection.getSelection())
|
|
if len(objsused) > 0:
|
|
newmeshobj = FreeCAD.activeDocument().addObject('Mesh::Feature',opname) #create a Feature for the result
|
|
newmeshobj.Mesh = newmesh #assign the result to the new Feature
|
|
for obj in objsused:
|
|
obj.ViewObject.hide() #hide the selected Features
|
|
|
|
class AddOpenSCADElement:
|
|
def IsActive(self):
|
|
return not FreeCADGui.Control.activeDialog()
|
|
|
|
def Activated(self):
|
|
panel = AddSCADTask()
|
|
FreeCADGui.Control.showDialog(panel)
|
|
|
|
def GetResources(self):
|
|
return {'Pixmap' : 'OpenSCAD_AddOpenSCADElement',
|
|
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_AddOpenSCADElement', 'Add OpenSCAD Element...'),
|
|
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_AddOpenSCADElement',
|
|
'Add an OpenSCAD element by entering OpenSCAD code and executing the OpenSCAD binary')}
|
|
|
|
class OpenSCADMeshBoolean:
|
|
def IsActive(self):
|
|
return not FreeCADGui.Control.activeDialog() and \
|
|
len(FreeCADGui.Selection.getSelection()) >= 1
|
|
|
|
def Activated(self):
|
|
panel = OpenSCADMeshBooleanTask()
|
|
FreeCADGui.Control.showDialog(panel)
|
|
|
|
def GetResources(self):
|
|
return {'Pixmap' : 'OpenSCAD_MeshBooleans',
|
|
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_MeshBoolean','Mesh Boolean...'),
|
|
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_MeshBoolean',
|
|
'Export objects as meshes and use OpenSCAD to perform a boolean operation')}
|
|
|
|
class Hull:
|
|
def IsActive(self):
|
|
return len(FreeCADGui.Selection.getSelection()) >= 1
|
|
|
|
def Activated(self):
|
|
import Part
|
|
import OpenSCADFeatures
|
|
import importCSG
|
|
selection=FreeCADGui.Selection.getSelectionEx()
|
|
objList = []
|
|
for selobj in selection:
|
|
objList.append(selobj.Object)
|
|
selobj.Object.ViewObject.hide()
|
|
importCSG.process_ObjectsViaOpenSCAD(FreeCAD.activeDocument(),objList,"hull")
|
|
FreeCAD.ActiveDocument.recompute()
|
|
def GetResources(self):
|
|
return {'Pixmap' : 'OpenSCAD_Hull',
|
|
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_Hull', 'Hull'),
|
|
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_Hull', 'Use OpenSCAD to create a hull')}
|
|
|
|
class Minkowski:
|
|
def IsActive(self):
|
|
return len(FreeCADGui.Selection.getSelection()) >= 1
|
|
|
|
def Activated(self):
|
|
import Part
|
|
import OpenSCADFeatures
|
|
import importCSG
|
|
selection=FreeCADGui.Selection.getSelectionEx()
|
|
objList = []
|
|
for selobj in selection:
|
|
objList.append(selobj.Object)
|
|
selobj.Object.ViewObject.hide()
|
|
importCSG.process_ObjectsViaOpenSCAD(FreeCAD.activeDocument(),objList,"minkowski")
|
|
FreeCAD.ActiveDocument.recompute()
|
|
def GetResources(self):
|
|
return {'Pixmap' : 'OpenSCAD_Minkowski',
|
|
'MenuText': QtCore.QT_TRANSLATE_NOOP('OpenSCAD_Minkowski', 'Minkowski sum'),
|
|
'ToolTip' : QtCore.QT_TRANSLATE_NOOP('OpenSCAD_Minkowski', 'Use OpenSCAD to create a Minkowski sum')}
|
|
|
|
FreeCADGui.addCommand('OpenSCAD_ColorCodeShape',ColorCodeShape())
|
|
FreeCADGui.addCommand('OpenSCAD_ExplodeGroup',ExplodeGroup())
|
|
FreeCADGui.addCommand('OpenSCAD_Edgestofaces',Edgestofaces())
|
|
FreeCADGui.addCommand('OpenSCAD_RefineShapeFeature',RefineShapeFeature())
|
|
FreeCADGui.addCommand('OpenSCAD_MirrorMeshFeature',MirrorMeshFeature())
|
|
FreeCADGui.addCommand('OpenSCAD_ScaleMeshFeature',ScaleMeshFeature())
|
|
FreeCADGui.addCommand('OpenSCAD_ResizeMeshFeature',ResizeMeshFeature())
|
|
FreeCADGui.addCommand('OpenSCAD_IncreaseToleranceFeature',IncreaseToleranceFeature())
|
|
FreeCADGui.addCommand('OpenSCAD_ExpandPlacements',ExpandPlacements())
|
|
FreeCADGui.addCommand('OpenSCAD_ReplaceObject',ReplaceObject())
|
|
FreeCADGui.addCommand('OpenSCAD_RemoveSubtree',RemoveSubtree())
|
|
FreeCADGui.addCommand('OpenSCAD_AddOpenSCADElement',AddOpenSCADElement())
|
|
FreeCADGui.addCommand('OpenSCAD_MeshBoolean',OpenSCADMeshBoolean())
|
|
FreeCADGui.addCommand('OpenSCAD_Hull',Hull())
|
|
FreeCADGui.addCommand('OpenSCAD_Minkowski',Minkowski())
|