
This means Surface won't work on Meshes directly. They should be converted so FreeCAD solids first. Surface still needs lots of work.
607 lines
23 KiB
Python
607 lines
23 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
# ***************************************************************************
|
|
# * *
|
|
# * Copyright (c) 2016 sliptonic <shopinthewoods@gmail.com> *
|
|
# * *
|
|
# * 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 *
|
|
# * *
|
|
# ***************************************************************************
|
|
|
|
import FreeCAD
|
|
import Path
|
|
from PathScripts import PathUtils
|
|
|
|
if FreeCAD.GuiUp:
|
|
import FreeCADGui
|
|
from PySide import QtCore, QtGui
|
|
|
|
__title__ = "Path Surface Operation"
|
|
__author__ = "sliptonic (Brad Collette)"
|
|
__url__ = "http://www.freecadweb.org"
|
|
|
|
"""Path surface object and FreeCAD command"""
|
|
|
|
# Qt tanslation handling
|
|
try:
|
|
_encoding = QtGui.QApplication.UnicodeUTF8
|
|
|
|
def translate(context, text, disambig=None):
|
|
return QtGui.QApplication.translate(context, text, disambig, _encoding)
|
|
except AttributeError:
|
|
def translate(context, text, disambig=None):
|
|
return QtGui.QApplication.translate(context, text, disambig)
|
|
|
|
|
|
class ObjectSurface:
|
|
|
|
def __init__(self, obj):
|
|
obj.addProperty("App::PropertyLinkSubList", "Base", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property","The base geometry of this toolpath"))
|
|
obj.addProperty("App::PropertyBool", "Active", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property","Make False, to prevent operation from generating code"))
|
|
obj.addProperty("App::PropertyString", "Comment", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property","An optional comment for this profile"))
|
|
obj.addProperty("App::PropertyString", "UserLabel", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property","User Assigned Label"))
|
|
|
|
obj.addProperty("App::PropertyEnumeration", "Algorithm", "Algorithm", QtCore.QT_TRANSLATE_NOOP("App::Property","The library to use to generate the path"))
|
|
obj.Algorithm = ['OCL Dropcutter', 'OCL Waterline']
|
|
|
|
# Tool Properties
|
|
obj.addProperty("App::PropertyIntegerConstraint", "ToolNumber", "Tool", QtCore.QT_TRANSLATE_NOOP("App::Property","The tool number in use"))
|
|
obj.ToolNumber = (0, 0, 1000, 0)
|
|
obj.setEditorMode('ToolNumber', 1) # make this read only
|
|
obj.addProperty("App::PropertyString", "ToolDescription", "Tool", QtCore.QT_TRANSLATE_NOOP("App::Property","The description of the tool "))
|
|
obj.setEditorMode('ToolDescription', 1) # make this read onlyt
|
|
|
|
# Surface Properties
|
|
obj.addProperty("App::PropertyFloatConstraint", "SampleInterval", "Surface", QtCore.QT_TRANSLATE_NOOP("App::Property","The Sample Interval. Small values cause long wait"))
|
|
obj.SampleInterval = (0, 0, 1, 0)
|
|
|
|
# Depth Properties
|
|
obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property","The height needed to clear clamps and obstructions"))
|
|
obj.addProperty("App::PropertyDistance", "SafeHeight", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property","Rapid Safety Height between locations."))
|
|
obj.addProperty("App::PropertyFloatConstraint", "StepDown", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property","Incremental Step Down of Tool"))
|
|
obj.StepDown = (0.0, 0.01, 100.0, 0.5)
|
|
obj.addProperty("App::PropertyDistance", "StartDepth", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property","Starting Depth of Tool- first cut depth in Z"))
|
|
obj.addProperty("App::PropertyDistance", "FinalDepth", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property","Final Depth of Tool- lowest value in Z"))
|
|
obj.addProperty("App::PropertyDistance", "FinishDepth", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property","Maximum material removed on final pass."))
|
|
|
|
obj.Proxy = self
|
|
|
|
def addsurfacebase(self, obj, ss, sub=""):
|
|
baselist = obj.Base
|
|
if len(baselist) == 0: # When adding the first base object, guess at heights
|
|
try:
|
|
bb = ss.Shape.BoundBox # parent boundbox
|
|
subobj = ss.Shape.getElement(sub)
|
|
fbb = subobj.BoundBox # feature boundbox
|
|
obj.StartDepth = bb.ZMax
|
|
obj.ClearanceHeight = bb.ZMax + 5.0
|
|
obj.SafeHeight = bb.ZMax + 3.0
|
|
|
|
if fbb.ZMax < bb.ZMax:
|
|
obj.FinalDepth = fbb.ZMax
|
|
else:
|
|
obj.FinalDepth = bb.ZMin
|
|
except:
|
|
obj.StartDepth = 5.0
|
|
obj.ClearanceHeight = 10.0
|
|
obj.SafeHeight = 8.0
|
|
|
|
item = (ss, sub)
|
|
if item in baselist:
|
|
FreeCAD.Console.PrintWarning(
|
|
"this object already in the list" + "\n")
|
|
else:
|
|
baselist.append(item)
|
|
obj.Base = baselist
|
|
self.execute(obj)
|
|
|
|
def __getstate__(self):
|
|
return None
|
|
|
|
def __setstate__(self, state):
|
|
return None
|
|
|
|
def onChanged(self, obj, prop):
|
|
if prop == "UserLabel":
|
|
obj.Label = obj.UserLabel + " :" + obj.ToolDescription
|
|
|
|
def _waterline(self, obj, s, bb):
|
|
import ocl
|
|
from PathScripts.PathUtils import depth_params, fmt
|
|
import time
|
|
|
|
def drawLoops(loops):
|
|
nloop = 0
|
|
waterlinestring = ""
|
|
waterlinestring += "(waterline begin)"
|
|
for loop in loops:
|
|
p = loop[0]
|
|
loopstring = "(loop begin)" + "\n"
|
|
loopstring += "G0 Z" + str(obj.SafeHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n"
|
|
loopstring += "G0 X" + \
|
|
str(fmt(p.x)) + " Y" + str(fmt(p.y)) + "F " + PathUtils.fmt(self.horizRapid) + "\n"
|
|
loopstring += "G1 Z" + str(fmt(p.z)) + "\n"
|
|
for p in loop[1:]:
|
|
loopstring += "G1 X" + \
|
|
str(fmt(p.x)) + " Y" + str(fmt(p.y)) + \
|
|
" Z" + str(fmt(p.z)) + "\n"
|
|
zheight = p.z
|
|
p = loop[0]
|
|
loopstring += "G1 X" + \
|
|
str(fmt(p.x)) + " Y" + str(fmt(p.y)) + \
|
|
" Z" + str(fmt(zheight)) + "\n"
|
|
loopstring += "(loop end)" + "\n"
|
|
print " loop ", nloop, " with ", len(loop), " points"
|
|
nloop = nloop + 1
|
|
waterlinestring += loopstring
|
|
waterlinestring += "(waterline end)" + "\n"
|
|
return waterlinestring
|
|
|
|
depthparams = depth_params(obj.ClearanceHeight.Value, obj.SafeHeight.Value,
|
|
obj.StartDepth.Value, obj.StepDown, obj.FinishDepth.Value, obj.FinalDepth.Value)
|
|
# stlfile = "../../stl/gnu_tux_mod.stl"
|
|
# surface = STLSurfaceSource(stlfile)
|
|
surface = s
|
|
|
|
t_before = time.time()
|
|
zheights = depthparams.get_depths()
|
|
wl = ocl.Waterline()
|
|
# wl = ocl.AdaptiveWaterline() # this is slower, ca 60 seconds on i7
|
|
# CPU
|
|
wl.setSTL(surface)
|
|
diam = 0.5
|
|
length = 10.0
|
|
# any ocl MillingCutter class should work here
|
|
cutter = ocl.BallCutter(diam, length)
|
|
wl.setCutter(cutter)
|
|
# this should be smaller than the smallest details in the STL file
|
|
wl.setSampling(obj.SampleInterval)
|
|
# AdaptiveWaterline() also has settings for minimum sampling interval
|
|
# (see c++ code)
|
|
all_loops = []
|
|
for zh in zheights:
|
|
print "calculating Waterline at z= ", zh
|
|
wl.reset()
|
|
wl.setZ(zh) # height for this waterline
|
|
wl.run()
|
|
all_loops.append(wl.getLoops())
|
|
t_after = time.time()
|
|
calctime = t_after - t_before
|
|
n = 0
|
|
output = ""
|
|
for loops in all_loops: # at each z-height, we may get many loops
|
|
print " %d/%d:" % (n, len(all_loops))
|
|
output += drawLoops(loops)
|
|
n = n + 1
|
|
print "(" + str(calctime) + ")"
|
|
return output
|
|
|
|
def _dropcutter(self, obj, s, bb):
|
|
import ocl
|
|
import time
|
|
|
|
cutter = ocl.CylCutter(self.radius * 2, 5)
|
|
pdc = ocl.PathDropCutter() # create a pdc
|
|
pdc.setSTL(s)
|
|
pdc.setCutter(cutter)
|
|
pdc.minimumZ = 0.25
|
|
pdc.setSampling(obj.SampleInterval)
|
|
|
|
# some parameters for this "zigzig" pattern
|
|
xmin = bb.XMin - cutter.getDiameter()
|
|
xmax = bb.XMax + cutter.getDiameter()
|
|
ymin = bb.YMin - cutter.getDiameter()
|
|
ymax = bb.YMax + cutter.getDiameter()
|
|
|
|
# number of lines in the y-direction
|
|
Ny = int(bb.YLength / cutter.getDiameter())
|
|
dy = float(ymax - ymin) / Ny # the y step-over
|
|
|
|
path = ocl.Path() # create an empty path object
|
|
|
|
# add Line objects to the path in this loop
|
|
for n in xrange(0, Ny):
|
|
y = ymin + n * dy
|
|
p1 = ocl.Point(xmin, y, 0) # start-point of line
|
|
p2 = ocl.Point(xmax, y, 0) # end-point of line
|
|
if (n % 2 == 0): # even
|
|
l = ocl.Line(p1, p2) # line-object
|
|
else: # odd
|
|
l = ocl.Line(p2, p1) # line-object
|
|
|
|
path.append(l) # add the line to the path
|
|
|
|
pdc.setPath(path)
|
|
|
|
# run drop-cutter on the path
|
|
t_before = time.time()
|
|
pdc.run()
|
|
t_after = time.time()
|
|
print "calculation took ", t_after - t_before, " s"
|
|
|
|
# retrieve the points
|
|
clp = pdc.getCLPoints()
|
|
print "points received: " + str(len(clp))
|
|
|
|
# generate the path commands
|
|
output = ""
|
|
output += "G0 Z" + str(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n"
|
|
output += "G0 X" + str(clp[0].x) + " Y" + str(clp[0].y) + "F " + PathUtils.fmt(self.horizRapid) + "\n"
|
|
output += "G1 Z" + str(clp[0].z) + " F" + str(self.vertFeed) + "\n"
|
|
|
|
for c in clp:
|
|
output += "G1 X" + str(c.x) + " Y" + \
|
|
str(c.y) + " Z" + str(c.z) + "\n"
|
|
|
|
return output
|
|
|
|
def execute(self, obj):
|
|
import MeshPart
|
|
FreeCAD.Console.PrintWarning(
|
|
translate("PathSurface", "Hold on. This might take a minute.\n"))
|
|
output = ""
|
|
if obj.Comment != "":
|
|
output += '(' + str(obj.Comment)+')\n'
|
|
|
|
toolLoad = PathUtils.getLastToolLoad(obj)
|
|
if toolLoad is None or toolLoad.ToolNumber == 0:
|
|
self.vertFeed = 100
|
|
self.horizFeed = 100
|
|
self.vertRapid = 100
|
|
self.horizRapid = 100
|
|
self.radius = 0.25
|
|
obj.ToolNumber = 0
|
|
obj.ToolDescription = "UNDEFINED"
|
|
else:
|
|
self.vertFeed = toolLoad.VertFeed.Value
|
|
self.horizFeed = toolLoad.HorizFeed.Value
|
|
self.vertRapid = toolLoad.VertRapid.Value
|
|
self.horizRapid = toolLoad.HorizRapid.Value
|
|
tool = PathUtils.getTool(obj, toolLoad.ToolNumber)
|
|
if tool.Diameter == 0:
|
|
self.radius = 0.25
|
|
else:
|
|
self.radius = tool.Diameter/2
|
|
obj.ToolNumber = toolLoad.ToolNumber
|
|
obj.ToolDescription = toolLoad.Name
|
|
|
|
if obj.UserLabel == "":
|
|
obj.Label = obj.Name + " :" + obj.ToolDescription
|
|
else:
|
|
obj.Label = obj.UserLabel + " :" + obj.ToolDescription
|
|
|
|
output += "(" + obj.Label + ")"
|
|
output += "(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")"
|
|
|
|
# if obj.Base:
|
|
# for b in obj.Base:
|
|
|
|
parentJob = PathUtils.findParentJob(obj)
|
|
if parentJob is None:
|
|
return
|
|
mesh = parentJob.Base
|
|
if mesh is None:
|
|
return
|
|
print "base object: " + mesh.Name
|
|
|
|
|
|
|
|
if obj.Algorithm in ['OCL Dropcutter', 'OCL Waterline']:
|
|
try:
|
|
import ocl
|
|
except:
|
|
FreeCAD.Console.PrintError(translate(
|
|
"PathSurface", "This operation requires OpenCamLib to be installed.\n"))
|
|
return
|
|
|
|
#mesh = b[0]
|
|
if mesh.TypeId.startswith('Mesh'):
|
|
mesh = mesh.Mesh
|
|
bb = mesh.BoundBox
|
|
else:
|
|
bb = mesh.Shape.BoundBox
|
|
mesh = MeshPart.meshFromShape(mesh.Shape, MaxLength=2)
|
|
|
|
s = ocl.STLSurf()
|
|
for f in mesh.Facets:
|
|
p = f.Points[0]
|
|
q = f.Points[1]
|
|
r = f.Points[2]
|
|
t = ocl.Triangle(ocl.Point(p[0], p[1], p[2]), ocl.Point(
|
|
q[0], q[1], q[2]), ocl.Point(r[0], r[1], r[2]))
|
|
s.addTriangle(t)
|
|
|
|
if obj.Algorithm == 'OCL Dropcutter':
|
|
output = self._dropcutter(obj, s, bb)
|
|
elif obj.Algorithm == 'OCL Waterline':
|
|
output = self._waterline(obj, s, bb)
|
|
|
|
if obj.Active:
|
|
path = Path.Path(output)
|
|
obj.Path = path
|
|
obj.ViewObject.Visibility = True
|
|
|
|
else:
|
|
path = Path.Path("(inactive operation)")
|
|
obj.Path = path
|
|
obj.ViewObject.Visibility = False
|
|
|
|
class ViewProviderSurface:
|
|
|
|
def __init__(self, obj): # mandatory
|
|
# obj.addProperty("App::PropertyFloat","SomePropertyName","PropertyGroup","Description of this property")
|
|
obj.Proxy = self
|
|
|
|
def __getstate__(self): # mandatory
|
|
return None
|
|
|
|
def __setstate__(self, state): # mandatory
|
|
return None
|
|
|
|
def getIcon(self): # optional
|
|
return ":/icons/Path-Surfacing.svg"
|
|
|
|
def onChanged(self, obj, prop): # optional
|
|
# this is executed when a property of the VIEW PROVIDER changes
|
|
pass
|
|
|
|
def updateData(self, obj, prop): # optional
|
|
# this is executed when a property of the APP OBJECT changes
|
|
pass
|
|
|
|
def setEdit(self, vobj, mode=0):
|
|
FreeCADGui.Control.closeDialog()
|
|
taskd = TaskPanel()
|
|
taskd.obj = vobj.Object
|
|
FreeCADGui.Control.showDialog(taskd)
|
|
taskd.setupUi()
|
|
return True
|
|
|
|
def unsetEdit(self, vobj, mode): # optional
|
|
# this is executed when the user cancels or terminates edit mode
|
|
pass
|
|
|
|
|
|
class CommandPathSurfacing:
|
|
|
|
def GetResources(self):
|
|
return {'Pixmap': 'Path-3DSurface',
|
|
'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Surface", "Surfacing"),
|
|
'Accel': "P, S",
|
|
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Surface", "Creates a Path Surfacing object")}
|
|
|
|
def IsActive(self):
|
|
if FreeCAD.ActiveDocument is not None:
|
|
for o in FreeCAD.ActiveDocument.Objects:
|
|
if o.Name[:3] == "Job":
|
|
return True
|
|
return False
|
|
|
|
def Activated(self):
|
|
|
|
ztop = 10
|
|
zbottom = 0
|
|
|
|
FreeCAD.ActiveDocument.openTransaction(
|
|
translate("Path_Surfacing", "Create Surface"))
|
|
FreeCADGui.addModule("PathScripts.PathSurface")
|
|
FreeCADGui.doCommand(
|
|
'obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Surface")')
|
|
FreeCADGui.doCommand('PathScripts.PathSurface.ObjectSurface(obj)')
|
|
FreeCADGui.doCommand('obj.Active = True')
|
|
FreeCADGui.doCommand(
|
|
'PathScripts.PathSurface.ViewProviderSurface(obj.ViewObject)')
|
|
FreeCADGui.doCommand('from PathScripts import PathUtils')
|
|
FreeCADGui.doCommand('obj.ClearanceHeight = ' + str(ztop + 2))
|
|
FreeCADGui.doCommand('obj.StartDepth = ' + str(ztop))
|
|
FreeCADGui.doCommand('obj.SafeHeight = ' + str(ztop + 2))
|
|
FreeCADGui.doCommand('obj.StepDown = ' + str((ztop - zbottom) / 8))
|
|
FreeCADGui.doCommand('obj.SampleInterval = 0.4')
|
|
|
|
FreeCADGui.doCommand('obj.FinalDepth=' + str(zbottom))
|
|
FreeCADGui.doCommand('PathScripts.PathUtils.addToJob(obj)')
|
|
|
|
FreeCAD.ActiveDocument.commitTransaction()
|
|
FreeCAD.ActiveDocument.recompute()
|
|
FreeCADGui.doCommand('obj.ViewObject.startEditing()')
|
|
|
|
|
|
class TaskPanel:
|
|
|
|
def __init__(self):
|
|
# self.form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/SurfaceEdit.ui")
|
|
self.form = FreeCADGui.PySideUic.loadUi(":/panels/SurfaceEdit.ui")
|
|
FreeCAD.Console.PrintWarning("Surface calculations can be slow. Don't Panic.\n")
|
|
|
|
def accept(self):
|
|
self.getFields()
|
|
|
|
FreeCADGui.ActiveDocument.resetEdit()
|
|
FreeCADGui.Control.closeDialog()
|
|
FreeCAD.ActiveDocument.recompute()
|
|
FreeCADGui.Selection.removeObserver(self.s)
|
|
|
|
def reject(self):
|
|
FreeCADGui.Control.closeDialog()
|
|
FreeCAD.ActiveDocument.recompute()
|
|
FreeCADGui.Selection.removeObserver(self.s)
|
|
|
|
def getFields(self):
|
|
if self.obj:
|
|
if hasattr(self.obj, "StartDepth"):
|
|
self.obj.StartDepth = self.form.startDepth.text()
|
|
if hasattr(self.obj, "FinalDepth"):
|
|
self.obj.FinalDepth = self.form.finalDepth.text()
|
|
if hasattr(self.obj, "FinishDepth"):
|
|
self.obj.FinishDepth = self.form.finishDepth.text()
|
|
if hasattr(self.obj, "StepDown"):
|
|
self.obj.StepDown = self.form.stepDown.value()
|
|
|
|
if hasattr(self.obj, "SafeHeight"):
|
|
self.obj.SafeHeight = self.form.safeHeight.text()
|
|
if hasattr(self.obj, "ClearanceHeight"):
|
|
self.obj.ClearanceHeight = self.form.clearanceHeight.text()
|
|
if hasattr(self.obj, "Algorithm"):
|
|
self.obj.Algorithm = str(
|
|
self.form.algorithmSelect.currentText())
|
|
|
|
self.obj.Proxy.execute(self.obj)
|
|
|
|
def setFields(self):
|
|
self.form.startDepth.setText(str(self.obj.StartDepth.Value))
|
|
self.form.finalDepth.setText(str(self.obj.FinalDepth.Value))
|
|
self.form.finishDepth.setText(str(self.obj.FinishDepth.Value))
|
|
self.form.stepDown.setValue(self.obj.StepDown)
|
|
|
|
self.form.safeHeight.setText(str(self.obj.SafeHeight.Value))
|
|
self.form.clearanceHeight.setText(str(self.obj.ClearanceHeight.Value))
|
|
|
|
for i in self.obj.Base:
|
|
self.form.baseList.addItem(i[0].Name)
|
|
|
|
index = self.form.algorithmSelect.findText(
|
|
self.obj.Algorithm, QtCore.Qt.MatchFixedString)
|
|
if index >= 0:
|
|
self.form.algorithmSelect.setCurrentIndex(index)
|
|
|
|
def open(self):
|
|
self.s = SelObserver()
|
|
# install the function mode resident
|
|
FreeCADGui.Selection.addObserver(self.s)
|
|
|
|
def addBase(self):
|
|
# check that the selection contains exactly what we want
|
|
selection = FreeCADGui.Selection.getSelectionEx()
|
|
if len(selection) != 1:
|
|
FreeCAD.Console.PrintError(translate(
|
|
"PathSurface", "Please select a single solid object from the project tree\n"))
|
|
return
|
|
|
|
if not len(selection[0].SubObjects) == 0:
|
|
FreeCAD.Console.PrintError(translate(
|
|
"PathSurface", "Please select a single solid object from the project tree\n"))
|
|
return
|
|
|
|
sel = selection[0].Object
|
|
# get type of object
|
|
if sel.TypeId.startswith('Mesh'):
|
|
# it is a mesh already
|
|
print 'was already mesh'
|
|
|
|
elif sel.TypeId.startswith('Part') and \
|
|
(sel.Shape.BoundBox.XLength > 0) and \
|
|
(sel.Shape.BoundBox.YLength > 0) and \
|
|
(sel.Shape.BoundBox.ZLength > 0):
|
|
print 'this is a solid Part object'
|
|
|
|
else:
|
|
FreeCAD.Console.PrintError(
|
|
translate("PathSurface", "Cannot work with this object\n"))
|
|
return
|
|
|
|
self.obj.Proxy.addsurfacebase(self.obj, sel)
|
|
|
|
self.setFields() # defaults may have changed. Reload.
|
|
self.form.baseList.clear()
|
|
for i in self.obj.Base:
|
|
self.form.baseList.addItem(i[0].Name)
|
|
|
|
def deleteBase(self):
|
|
dlist = self.form.baseList.selectedItems()
|
|
for d in dlist:
|
|
newlist = []
|
|
for i in self.obj.Base:
|
|
if not i[0].Name == d.text():
|
|
newlist.append(i)
|
|
self.obj.Base = newlist
|
|
self.form.baseList.takeItem(self.form.baseList.row(d))
|
|
self.obj.Proxy.execute(self.obj)
|
|
FreeCAD.ActiveDocument.recompute()
|
|
|
|
def itemActivated(self):
|
|
FreeCADGui.Selection.clearSelection()
|
|
slist = self.form.baseList.selectedItems()
|
|
for i in slist:
|
|
o = FreeCAD.ActiveDocument.getObject(i.text())
|
|
FreeCADGui.Selection.addSelection(o)
|
|
FreeCADGui.updateGui()
|
|
|
|
def reorderBase(self):
|
|
newlist = []
|
|
for i in range(self.form.baseList.count()):
|
|
s = self.form.baseList.item(i).text()
|
|
obj = FreeCAD.ActiveDocument.getObject(s)
|
|
newlist.append(obj)
|
|
self.obj.Base = newlist
|
|
self.obj.Proxy.execute(self.obj)
|
|
FreeCAD.ActiveDocument.recompute()
|
|
|
|
def getStandardButtons(self):
|
|
return int(QtGui.QDialogButtonBox.Ok)
|
|
|
|
def setupUi(self):
|
|
|
|
# Connect Signals and Slots
|
|
|
|
#Base Geometry
|
|
self.form.addBase.clicked.connect(self.addBase)
|
|
self.form.deleteBase.clicked.connect(self.deleteBase)
|
|
self.form.reorderBase.clicked.connect(self.reorderBase)
|
|
self.form.baseList.itemSelectionChanged.connect(self.itemActivated)
|
|
|
|
# Depths
|
|
self.form.startDepth.editingFinished.connect(self.getFields)
|
|
self.form.finalDepth.editingFinished.connect(self.getFields)
|
|
self.form.finishDepth.editingFinished.connect(self.getFields)
|
|
self.form.stepDown.editingFinished.connect(self.getFields)
|
|
|
|
# Heights
|
|
self.form.safeHeight.editingFinished.connect(self.getFields)
|
|
self.form.clearanceHeight.editingFinished.connect(self.getFields)
|
|
|
|
# Operation
|
|
self.form.algorithmSelect.currentIndexChanged.connect(self.getFields)
|
|
|
|
sel = FreeCADGui.Selection.getSelectionEx()
|
|
self.setFields()
|
|
|
|
if len(sel) != 0:
|
|
self.addBase()
|
|
|
|
|
|
class SelObserver:
|
|
|
|
def __init__(self):
|
|
import PathScripts.PathSelection as PST
|
|
PST.surfaceselect()
|
|
|
|
def __del__(self):
|
|
import PathScripts.PathSelection as PST
|
|
PST.clear()
|
|
|
|
def addSelection(self, doc, obj, sub, pnt): # Selection object
|
|
FreeCADGui.doCommand(
|
|
'Gui.Selection.addSelection(FreeCAD.ActiveDocument.' + obj + ')')
|
|
FreeCADGui.updateGui()
|
|
|
|
|
|
if FreeCAD.GuiUp:
|
|
# register the FreeCAD command
|
|
FreeCADGui.addCommand('Path_Surfacing', CommandPathSurfacing())
|
|
|
|
FreeCAD.Console.PrintLog("Loading PathSurfacing... done\n")
|