@@ -1,5 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 2016 sliptonic <shopinthewoods@gmail.com> *
|
||||
@@ -36,12 +35,11 @@ from PathScripts.PathUtils import findParentJob
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
from PySide import QtCore
|
||||
from DraftTools import translate
|
||||
else:
|
||||
def translate(ctxt, txt):
|
||||
return txt
|
||||
|
||||
__title__="FreeCAD Path Commands"
|
||||
__title__ = "FreeCAD Path Commands"
|
||||
__author__ = "sliptonic"
|
||||
__url__ = "http://www.freecadweb.org"
|
||||
|
||||
@@ -80,7 +78,6 @@ class _CommandSelectLoop:
|
||||
return False
|
||||
|
||||
def Activated(self):
|
||||
#from PathScripts.PathUtils import loopdetect
|
||||
from PathScripts.PathUtils import horizontalEdgeLoop
|
||||
from PathScripts.PathUtils import horizontalFaceLoop
|
||||
sel = FreeCADGui.Selection.getSelectionEx()[0]
|
||||
@@ -104,7 +101,7 @@ class _CommandSelectLoop:
|
||||
for e in elist:
|
||||
for i in loopwire.Edges:
|
||||
if e.hashCode() == i.hashCode():
|
||||
FreeCADGui.Selection.addSelection(obj, "Edge"+str(elist.index(e)+1))
|
||||
FreeCADGui.Selection.addSelection(obj, "Edge" + str(elist.index(e) + 1))
|
||||
|
||||
def formsPartOfALoop(self, obj, sub, names):
|
||||
if names[0][0:4] != 'Edge':
|
||||
@@ -117,9 +114,11 @@ class _CommandSelectLoop:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('Path_SelectLoop', _CommandSelectLoop())
|
||||
|
||||
|
||||
class _ToggleOperation:
|
||||
"command definition to toggle Operation Active state"
|
||||
def GetResources(self):
|
||||
@@ -135,7 +134,7 @@ class _ToggleOperation:
|
||||
try:
|
||||
obj = FreeCADGui.Selection.getSelectionEx()[0].Object
|
||||
return isinstance(obj.Proxy, PathScripts.PathOp.ObjectOp)
|
||||
except:
|
||||
except(IndexError, AttributeError):
|
||||
return False
|
||||
|
||||
def Activated(self):
|
||||
@@ -143,9 +142,11 @@ class _ToggleOperation:
|
||||
obj.Active = not(obj.Active)
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('Path_OpActiveToggle', _ToggleOperation())
|
||||
|
||||
|
||||
class _CopyOperation:
|
||||
"the Path Copy Operation command definition"
|
||||
def GetResources(self):
|
||||
@@ -160,7 +161,7 @@ class _CopyOperation:
|
||||
try:
|
||||
obj = FreeCADGui.Selection.getSelectionEx()[0].Object
|
||||
return isinstance(obj.Proxy, PathScripts.PathOp.ObjectOp)
|
||||
except:
|
||||
except(IndexError, AttributeError):
|
||||
return False
|
||||
|
||||
def Activated(self):
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
#***************************************************************************
|
||||
#* *
|
||||
#* Copyright (c) 2014 Daniel Falck <ddfalck@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 *
|
||||
#* *
|
||||
#***************************************************************************
|
||||
|
||||
'''
|
||||
This macro is used in conjunction with the toolpathparams script to create an object that represents a tool for use in a CNC program. Create a group and then select it- then run the macro.
|
||||
You will have to edit the parameters inside the Data tab of the tool object.
|
||||
'''
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import PathScripts
|
||||
import toolpathparams as tp
|
||||
|
||||
|
||||
tl = FreeCAD.ActiveDocument.addObject("App::FeaturePython","Tools")
|
||||
|
||||
tp.ToolParams(tl)
|
||||
tp.ViewProviderToolParams(tl.ViewObject)
|
||||
|
||||
sel = FreeCADGui.Selection.getSelection()
|
||||
g = sel[0]
|
||||
g.addObject(tl)
|
||||
App.activeDocument().recompute()
|
||||
@@ -1,65 +0,0 @@
|
||||
#***************************************************************************
|
||||
#* *
|
||||
#* Copyright (c) 2014 Daniel Falck <ddfalck@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
|
||||
from FreeCAD import Base
|
||||
import Part
|
||||
import Draft
|
||||
from PySide import QtGui,QtCore
|
||||
from math import pi
|
||||
'''
|
||||
This macro makes a list of holes for drilling from a solid
|
||||
1. Select a solid object that has holes in it and run the macro
|
||||
2. It only collects info on holes that are parallel to the Z axis- I don't have a 4 or 5 axis mill at the moment
|
||||
3. It pulls the center of the hole bounding box and the XLength for it's diameter
|
||||
4. It will place a list of the holes on the clipboard
|
||||
5. Uncomment the line that starts with '#Draft.makeLine' and manipulate it, if you want to see lines down the center of each hole.
|
||||
6. Manipulate the line that starts with 'holelist.append' to make the list fit your own needs- I've put the ZMax at the ZMax of the solid's bounding box
|
||||
because I want to make sure that my drill tip doesn't collide with anything on the top of the part. YMMV.
|
||||
'''
|
||||
def findholes(obj):
|
||||
facelist = []
|
||||
holelist =[]
|
||||
vz = Base.Vector(0,0,1)
|
||||
for f in obj.Faces:
|
||||
if ( round(f.ParameterRange[0], 8)==0.0 ) and ( round(f.ParameterRange[1],8) == round(pi*2, 8) ) : #eliminate flat faces
|
||||
facelist.append(f)
|
||||
|
||||
for h in facelist:
|
||||
for w in h.Wires:
|
||||
for c in w.Edges:
|
||||
if ( isinstance(c.Curve,Part.LineSegment)):
|
||||
v0=Base.Vector(c.Vertexes[0].X, c.Vertexes[0].Y, c.Vertexes[0].Z); v1=Base.Vector(c.Vertexes[1].X,c.Vertexes[1].Y, c.Vertexes[1].Z)
|
||||
if (v1.sub(v0).x == 0) and (v1.sub(v0).y == 0):
|
||||
lsp = Base.Vector(h.BoundBox.Center.x,h.BoundBox.Center.y,h.BoundBox.ZMax)
|
||||
lep = Base.Vector(h.BoundBox.Center.x,h.BoundBox.Center.y,h.BoundBox.ZMin)
|
||||
if obj.isInside(lsp, 0,False) or obj.isInside(lep, 0,False):
|
||||
pass
|
||||
else:
|
||||
Draft.makeLine((h.BoundBox.Center.x,h.BoundBox.Center.y,obj.BoundBox.ZMax ),(h.BoundBox.Center.x,h.BoundBox.Center.y,h.BoundBox.ZMin ))
|
||||
x =h.BoundBox.Center.x;y=h.BoundBox.Center.y;zmax=obj.BoundBox.ZMax;zmin=h.BoundBox.ZMin;diameter=h.BoundBox.XLength
|
||||
holelist.append((diameter, x,y,zmax,zmin))
|
||||
clipboard = QtGui.QApplication.clipboard()
|
||||
clipboard.setText(str(holelist))
|
||||
|
||||
sel=Gui.Selection.getSelection()
|
||||
obj = sel[0].Shape
|
||||
findholes(obj)
|
||||
@@ -1,45 +0,0 @@
|
||||
#***************************************************************************
|
||||
#* *
|
||||
#* Copyright (c) 2014 Daniel Falck <ddfalck@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 *
|
||||
#* *
|
||||
#***************************************************************************
|
||||
'''
|
||||
This macro finds the outside profile of a 3D shape. Right now it just creates wires that represent the shape,but it could be used for finding the outside profile of an object for a toolpath
|
||||
'''
|
||||
import FreeCADGui
|
||||
import DraftGeomUtils
|
||||
from FreeCAD import Vector
|
||||
from PathScripts import find_outer_profile as fop
|
||||
|
||||
sel = FreeCADGui.Selection.getSelection()[0]
|
||||
obj = sel
|
||||
el = fop.edgelist(obj)
|
||||
hl = fop.horizontal(el)
|
||||
connected = DraftGeomUtils.findWires(hl)
|
||||
goodwires = fop.openFilter(connected)
|
||||
|
||||
outerwires ,innerwires, same = fop.findOutsideWire(goodwires)
|
||||
#get distance from outerwires Z to bottom of part
|
||||
zdiff = obj.Shape.BoundBox.ZMin- outerwires.BoundBox.ZMax
|
||||
outerwires.Placement.move(Vector(0,0,zdiff))
|
||||
Part.show(outerwires)
|
||||
|
||||
zupperouter = outerwires
|
||||
zupperouter.Placement.move(Vector(0,0,obj.Shape.BoundBox.ZMax))
|
||||
Part.show(zupperouter)
|
||||
@@ -246,7 +246,7 @@ class CommandPathArray:
|
||||
try:
|
||||
obj = FreeCADGui.Selection.getSelectionEx()[0].Object
|
||||
return isinstance(obj.Proxy, PathScripts.PathOp.ObjectOp)
|
||||
except:
|
||||
except(IndexError, AttributeError):
|
||||
return False
|
||||
|
||||
def Activated(self):
|
||||
|
||||
@@ -32,10 +32,6 @@ PathLog.setLevel(PathLog.Level.DEBUG, LOG_MODULE)
|
||||
PathLog.trackModule('PathCollision')
|
||||
FreeCAD.setLogLevel('Path.Area', 0)
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
# from PySide import QtGui
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import Path
|
||||
from PySide import QtCore, QtGui
|
||||
from PySide import QtCore
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
|
||||
@@ -24,12 +24,10 @@
|
||||
|
||||
import FreeCAD
|
||||
import Part
|
||||
import Path
|
||||
import PathScripts.PathEngraveBase as PathEngraveBase
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathOp as PathOp
|
||||
import PathScripts.PathOpTools as PathOpTools
|
||||
import PathScripts.PathUtil as PathUtil
|
||||
import math
|
||||
|
||||
from PySide import QtCore
|
||||
@@ -40,25 +38,27 @@ if False:
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
|
||||
def toolDepthAndOffset(width, extraDepth, tool):
|
||||
'''toolDepthAndOffset(width, extraDepth, tool) ... return tuple for given parameters.'''
|
||||
angle = tool.CuttingEdgeAngle
|
||||
if 0 == angle:
|
||||
angle = 180
|
||||
tan = math.tan(math.radians(angle/2))
|
||||
tan = math.tan(math.radians(angle / 2))
|
||||
|
||||
toolDepth = 0 if 0 == tan else width / tan
|
||||
extraDepth = extraDepth
|
||||
depth = toolDepth + extraDepth
|
||||
toolOffset = tool.FlatRadius
|
||||
extraOffset = tool.Diameter/2 - width if 180 == angle else extraDepth / tan
|
||||
extraOffset = tool.Diameter / 2 - width if 180 == angle else extraDepth / tan
|
||||
offset = toolOffset + extraOffset
|
||||
return (depth, offset)
|
||||
|
||||
|
||||
class ObjectDeburr(PathEngraveBase.ObjectOp):
|
||||
'''Proxy class for Deburr operation.'''
|
||||
|
||||
@@ -67,14 +67,14 @@ class ObjectDeburr(PathEngraveBase.ObjectOp):
|
||||
|
||||
def initOperation(self, obj):
|
||||
PathLog.track(obj.Label)
|
||||
obj.addProperty('App::PropertyDistance', 'Width', 'Deburr', QtCore.QT_TRANSLATE_NOOP('PathDeburr', 'The desired width of the chamfer'))
|
||||
obj.addProperty('App::PropertyDistance', 'ExtraDepth', 'Deburr', QtCore.QT_TRANSLATE_NOOP('PathDeburr', 'The additional depth of the tool path'))
|
||||
obj.addProperty('App::PropertyEnumeration', 'Join', 'Deburr', QtCore.QT_TRANSLATE_NOOP('PathDeburr', 'How to join chamfer segments'))
|
||||
obj.addProperty('App::PropertyDistance', 'Width', 'Deburr', QtCore.QT_TRANSLATE_NOOP('PathDeburr', 'The desired width of the chamfer'))
|
||||
obj.addProperty('App::PropertyDistance', 'ExtraDepth', 'Deburr', QtCore.QT_TRANSLATE_NOOP('PathDeburr', 'The additional depth of the tool path'))
|
||||
obj.addProperty('App::PropertyEnumeration', 'Join', 'Deburr', QtCore.QT_TRANSLATE_NOOP('PathDeburr', 'How to join chamfer segments'))
|
||||
obj.Join = ['Round', 'Miter']
|
||||
obj.setEditorMode('Join', 2) # hide for now
|
||||
obj.setEditorMode('Join', 2) # hide for now
|
||||
|
||||
def opOnDocumentRestored(self, obj):
|
||||
obj.setEditorMode('Join', 2) # hide for now
|
||||
obj.setEditorMode('Join', 2) # hide for now
|
||||
|
||||
def opExecute(self, obj):
|
||||
PathLog.track(obj.Label)
|
||||
@@ -125,7 +125,7 @@ class ObjectDeburr(PathEngraveBase.ObjectOp):
|
||||
|
||||
def opRejectAddBase(self, obj, base, sub):
|
||||
'''The chamfer op can only deal with features of the base model, all others are rejected.'''
|
||||
return not base in self.model
|
||||
return base not in self.model
|
||||
|
||||
def opSetDefaultValues(self, obj, job):
|
||||
PathLog.track(obj.Label, job.Label)
|
||||
@@ -135,16 +135,17 @@ class ObjectDeburr(PathEngraveBase.ObjectOp):
|
||||
obj.setExpression('StepDown', '0 mm')
|
||||
obj.StepDown = '0 mm'
|
||||
|
||||
|
||||
def SetupProperties():
|
||||
setup = []
|
||||
setup.append('Width')
|
||||
setup.append('ExtraDepth')
|
||||
return setup
|
||||
|
||||
def Create(name, obj = None):
|
||||
|
||||
def Create(name, obj=None):
|
||||
'''Create(name) ... Creates and returns a Deburr operation.'''
|
||||
if obj is None:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
proxy = ObjectDeburr(obj, name)
|
||||
return obj
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ import PathScripts.PathJob as PathJob
|
||||
import PathScripts.PathJobCmd as PathJobCmd
|
||||
import PathScripts.PathJobDlg as PathJobDlg
|
||||
import PathScripts.PathGeom as PathGeom
|
||||
import PathScripts.PathGui as PathGui
|
||||
import PathScripts.PathGuiInit as PathGuiInit
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
@@ -41,7 +40,6 @@ import PathScripts.PathToolLibraryManager as PathToolLibraryManager
|
||||
import PathScripts.PathUtil as PathUtil
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
import math
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
@@ -49,16 +47,19 @@ from collections import Counter
|
||||
from contextlib import contextmanager
|
||||
from pivy import coin
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
def _OpenCloseResourceEditor(obj, vobj, edit):
|
||||
job = PathUtils.findParentJob(obj)
|
||||
if job and job.ViewObject and job.ViewObject.Proxy:
|
||||
@@ -74,6 +75,7 @@ def _OpenCloseResourceEditor(obj, vobj, edit):
|
||||
missing = 'Proxy'
|
||||
PathLog.warning("Cannot edit %s - no %s" % (obj.Label, missing))
|
||||
|
||||
|
||||
@contextmanager
|
||||
def selectionEx():
|
||||
sel = FreeCADGui.Selection.getSelectionEx()
|
||||
@@ -173,7 +175,7 @@ class ViewProvider:
|
||||
PathLog.info("Expected a specific object to edit - %s not recognized" % obj.Label)
|
||||
return self.openTaskPanel()
|
||||
|
||||
def uneditObject(self, obj = None):
|
||||
def uneditObject(self, obj=None):
|
||||
self.unsetEdit(None, None)
|
||||
|
||||
def getIcon(self):
|
||||
@@ -247,6 +249,7 @@ class ViewProvider:
|
||||
action.triggered.connect(self.setEdit)
|
||||
menu.addAction(action)
|
||||
|
||||
|
||||
class StockEdit(object):
|
||||
Index = -1
|
||||
StockType = PathStock.StockType.Unknown
|
||||
@@ -262,8 +265,9 @@ class StockEdit(object):
|
||||
def IsStock(cls, obj):
|
||||
return PathStock.StockType.FromStock(obj.Stock) == cls.StockType
|
||||
|
||||
def activate(self, obj, select = False):
|
||||
def activate(self, obj, select=False):
|
||||
PathLog.track(obj.Label, select)
|
||||
|
||||
def showHide(widget, activeWidget):
|
||||
if widget == activeWidget:
|
||||
widget.show()
|
||||
@@ -294,11 +298,14 @@ class StockEdit(object):
|
||||
# the following members must be overwritten by subclasses
|
||||
def editorFrame(self):
|
||||
return None
|
||||
|
||||
def setFields(self, obj):
|
||||
pass
|
||||
|
||||
def setupUi(self, obj):
|
||||
pass
|
||||
|
||||
|
||||
class StockFromBaseBoundBoxEdit(StockEdit):
|
||||
Index = 2
|
||||
StockType = PathStock.StockType.FromBase
|
||||
@@ -307,7 +314,7 @@ class StockFromBaseBoundBoxEdit(StockEdit):
|
||||
PathLog.track()
|
||||
return self.form.stockFromBase
|
||||
|
||||
def getFieldsStock(self, stock, fields = ['xneg', 'xpos', 'yneg', 'ypos', 'zneg', 'zpos']):
|
||||
def getFieldsStock(self, stock, fields=['xneg', 'xpos', 'yneg', 'ypos', 'zneg', 'zpos']):
|
||||
try:
|
||||
if 'xneg' in fields:
|
||||
stock.ExtXneg = FreeCAD.Units.Quantity(self.form.stockExtXneg.text())
|
||||
@@ -324,7 +331,7 @@ class StockFromBaseBoundBoxEdit(StockEdit):
|
||||
except:
|
||||
pass
|
||||
|
||||
def getFields(self, obj, fields = ['xneg', 'xpos', 'yneg', 'ypos', 'zneg', 'zpos']):
|
||||
def getFields(self, obj, fields=['xneg', 'xpos', 'yneg', 'ypos', 'zneg', 'zpos']):
|
||||
PathLog.track(obj.Label, fields)
|
||||
if self.IsStock(obj):
|
||||
self.getFieldsStock(obj.Stock, fields)
|
||||
@@ -363,9 +370,11 @@ class StockFromBaseBoundBoxEdit(StockEdit):
|
||||
def checkXpos(self):
|
||||
self.trackXpos = self.form.stockExtXneg.text() == self.form.stockExtXpos.text()
|
||||
self.getFields(self.obj, ['xpos'])
|
||||
|
||||
def checkYpos(self):
|
||||
self.trackYpos = self.form.stockExtYneg.text() == self.form.stockExtYpos.text()
|
||||
self.getFields(self.obj, ['ypos'])
|
||||
|
||||
def checkZpos(self):
|
||||
self.trackZpos = self.form.stockExtZneg.text() == self.form.stockExtZpos.text()
|
||||
self.getFields(self.obj, ['zpos'])
|
||||
@@ -376,12 +385,14 @@ class StockFromBaseBoundBoxEdit(StockEdit):
|
||||
self.form.stockExtXpos.setText(self.form.stockExtXneg.text())
|
||||
fields.append('xpos')
|
||||
self.getFields(self.obj, fields)
|
||||
|
||||
def updateYpos(self):
|
||||
fields = ['yneg']
|
||||
if self.trackYpos:
|
||||
self.form.stockExtYpos.setText(self.form.stockExtYneg.text())
|
||||
fields.append('ypos')
|
||||
self.getFields(self.obj, fields)
|
||||
|
||||
def updateZpos(self):
|
||||
fields = ['zneg']
|
||||
if self.trackZpos:
|
||||
@@ -389,6 +400,7 @@ class StockFromBaseBoundBoxEdit(StockEdit):
|
||||
fields.append('zpos')
|
||||
self.getFields(self.obj, fields)
|
||||
|
||||
|
||||
class StockCreateBoxEdit(StockEdit):
|
||||
Index = 0
|
||||
StockType = PathStock.StockType.CreateBox
|
||||
@@ -396,13 +408,13 @@ class StockCreateBoxEdit(StockEdit):
|
||||
def editorFrame(self):
|
||||
return self.form.stockCreateBox
|
||||
|
||||
def getFields(self, obj, fields = ['length', 'widht', 'height']):
|
||||
def getFields(self, obj, fields=['length', 'widht', 'height']):
|
||||
try:
|
||||
if self.IsStock(obj):
|
||||
if 'length' in fields:
|
||||
obj.Stock.Length = FreeCAD.Units.Quantity(self.form.stockBoxLength.text())
|
||||
if 'width' in fields:
|
||||
obj.Stock.Width = FreeCAD.Units.Quantity(self.form.stockBoxWidth.text())
|
||||
obj.Stock.Width = FreeCAD.Units.Quantity(self.form.stockBoxWidth.text())
|
||||
if 'height' in fields:
|
||||
obj.Stock.Height = FreeCAD.Units.Quantity(self.form.stockBoxHeight.text())
|
||||
else:
|
||||
@@ -415,15 +427,16 @@ class StockCreateBoxEdit(StockEdit):
|
||||
self.setStock(obj, PathStock.CreateBox(obj))
|
||||
self.force = False
|
||||
self.setLengthField(self.form.stockBoxLength, obj.Stock.Length)
|
||||
self.setLengthField(self.form.stockBoxWidth, obj.Stock.Width)
|
||||
self.setLengthField(self.form.stockBoxWidth, obj.Stock.Width)
|
||||
self.setLengthField(self.form.stockBoxHeight, obj.Stock.Height)
|
||||
|
||||
def setupUi(self, obj):
|
||||
self.setFields(obj)
|
||||
self.form.stockBoxLength.textChanged.connect(lambda: self.getFields(obj, ['length']))
|
||||
self.form.stockBoxWidth.textChanged.connect(lambda: self.getFields(obj, ['width']))
|
||||
self.form.stockBoxWidth.textChanged.connect(lambda: self.getFields(obj, ['width']))
|
||||
self.form.stockBoxHeight.textChanged.connect(lambda: self.getFields(obj, ['height']))
|
||||
|
||||
|
||||
class StockCreateCylinderEdit(StockEdit):
|
||||
Index = 1
|
||||
StockType = PathStock.StockType.CreateCylinder
|
||||
@@ -431,7 +444,7 @@ class StockCreateCylinderEdit(StockEdit):
|
||||
def editorFrame(self):
|
||||
return self.form.stockCreateCylinder
|
||||
|
||||
def getFields(self, obj, fields = ['radius', 'height']):
|
||||
def getFields(self, obj, fields=['radius', 'height']):
|
||||
try:
|
||||
if self.IsStock(obj):
|
||||
if 'radius' in fields:
|
||||
@@ -455,6 +468,7 @@ class StockCreateCylinderEdit(StockEdit):
|
||||
self.form.stockCylinderRadius.textChanged.connect(lambda: self.getFields(obj, ['radius']))
|
||||
self.form.stockCylinderHeight.textChanged.connect(lambda: self.getFields(obj, ['height']))
|
||||
|
||||
|
||||
class StockFromExistingEdit(StockEdit):
|
||||
Index = 3
|
||||
StockType = PathStock.StockType.Unknown
|
||||
@@ -464,7 +478,7 @@ class StockFromExistingEdit(StockEdit):
|
||||
|
||||
def getFields(self, obj):
|
||||
stock = self.form.stockExisting.itemData(self.form.stockExisting.currentIndex())
|
||||
if not (hasattr(obj.Stock, 'Objects') and len(obj.Stock.Objects) == 1 and obj.Stock.Objects[0] == stock):
|
||||
if not (hasattr(obj.Stock, 'Objects') and len(obj.Stock.Objects) == 1 and obj.Stock.Objects[0] == stock):
|
||||
if stock:
|
||||
stock = PathJob.createResourceClone(obj, stock, 'Stock', 'Stock')
|
||||
stock.ViewObject.Visibility = True
|
||||
@@ -499,6 +513,7 @@ class StockFromExistingEdit(StockEdit):
|
||||
self.setFields(obj)
|
||||
self.form.stockExisting.currentIndexChanged.connect(lambda: self.getFields(obj))
|
||||
|
||||
|
||||
class TaskPanel:
|
||||
DataObject = QtCore.Qt.ItemDataRole.UserRole
|
||||
DataProperty = QtCore.Qt.ItemDataRole.UserRole + 1
|
||||
@@ -609,7 +624,7 @@ class TaskPanel:
|
||||
flist.append(self.form.wcslist.item(i).text())
|
||||
self.obj.Fixtures = flist
|
||||
except:
|
||||
FreeCAD.Console.PrintWarning("The Job was created without fixture support. Please delete and recreate the job\r\n")
|
||||
FreeCAD.Console.PrintWarning("The Job was created without fixture support. Please delete and recreate the job\r\n")
|
||||
|
||||
self.updateTooltips()
|
||||
self.stockEdit.getFields(self.obj)
|
||||
@@ -641,7 +656,7 @@ class TaskPanel:
|
||||
|
||||
vUnit = FreeCAD.Units.Quantity(1, FreeCAD.Units.Velocity).getUserPreferred()[2]
|
||||
|
||||
for row,tc in enumerate(sorted(self.obj.ToolController, key=lambda tc: tc.Label)):
|
||||
for row, tc in enumerate(sorted(self.obj.ToolController, key=lambda tc: tc.Label)):
|
||||
self.form.activeToolController.addItem(tc.Label, tc)
|
||||
if tc == select:
|
||||
index = row
|
||||
@@ -701,11 +716,10 @@ class TaskPanel:
|
||||
item = self.form.wcslist.findItems(f, QtCore.Qt.MatchExactly)[0]
|
||||
item.setCheckState(QtCore.Qt.Checked)
|
||||
|
||||
|
||||
self.form.postProcessorOutputFile.setText(self.obj.PostProcessorOutputFile)
|
||||
self.selectComboBoxText(self.form.postProcessor, self.obj.PostProcessor)
|
||||
self.form.postProcessorArguments.setText(self.obj.PostProcessorArgs)
|
||||
#self.obj.Proxy.onChanged(self.obj, "PostProcessor")
|
||||
# self.obj.Proxy.onChanged(self.obj, "PostProcessor")
|
||||
self.updateTooltips()
|
||||
|
||||
self.form.operationsList.clear()
|
||||
@@ -758,16 +772,16 @@ class TaskPanel:
|
||||
row = self.form.operationsList.currentRow()
|
||||
if row > 0:
|
||||
item = self.form.operationsList.takeItem(row)
|
||||
self.form.operationsList.insertItem(row-1, item)
|
||||
self.form.operationsList.setCurrentRow(row-1)
|
||||
self.form.operationsList.insertItem(row - 1, item)
|
||||
self.form.operationsList.setCurrentRow(row - 1)
|
||||
self.getFields()
|
||||
|
||||
def operationMoveDown(self):
|
||||
row = self.form.operationsList.currentRow()
|
||||
if row < self.form.operationsList.count() - 1:
|
||||
item = self.form.operationsList.takeItem(row)
|
||||
self.form.operationsList.insertItem(row+1, item)
|
||||
self.form.operationsList.setCurrentRow(row+1)
|
||||
self.form.operationsList.insertItem(row + 1, item)
|
||||
self.form.operationsList.setCurrentRow(row + 1)
|
||||
self.getFields()
|
||||
|
||||
def toolControllerSelect(self):
|
||||
@@ -837,7 +851,7 @@ class TaskPanel:
|
||||
if FreeCAD.Units.Velocity == val.Unit:
|
||||
setattr(tc, prop, val)
|
||||
elif FreeCAD.Units.Unit() == val.Unit:
|
||||
val = FreeCAD.Units.Quantity(item.text()+vUnit);
|
||||
val = FreeCAD.Units.Quantity(item.text() + vUnit)
|
||||
setattr(tc, prop, val)
|
||||
except:
|
||||
pass
|
||||
@@ -857,13 +871,13 @@ class TaskPanel:
|
||||
PathLog.debug("flip")
|
||||
p = sel.Object.Placement
|
||||
loc = sel.Object.Placement.Base
|
||||
rot = FreeCAD.Rotation(FreeCAD.Vector(1-axis.x, 1-axis.y, 1-axis.z), 180)
|
||||
rot = FreeCAD.Rotation(FreeCAD.Vector(1 - axis.x, 1 - axis.y, 1 - axis.z), 180)
|
||||
sel.Object.Placement = FreeCAD.Placement(loc, p.Rotation.multiply(rot))
|
||||
|
||||
def rotateSel(sel, n):
|
||||
p = sel.Object.Placement
|
||||
loc = sel.Object.Placement.Base
|
||||
r = axis.cross(n) # rotation axis
|
||||
# p = sel.Object.Placement
|
||||
# loc = sel.Object.Placement.Base
|
||||
r = axis.cross(n) # rotation axis
|
||||
a = DraftVecUtils.angle(n, axis, r) * 180 / math.pi
|
||||
PathLog.debug("oh boy: (%.2f, %.2f, %.2f) -> %.2f" % (r.x, r.y, r.z, a))
|
||||
Draft.rotate(sel.Object, a, axis=r)
|
||||
@@ -969,22 +983,26 @@ class TaskPanel:
|
||||
FreeCADGui.Selection.addSelection(selObject, selFeature)
|
||||
return (selObject, p)
|
||||
|
||||
def updateStockEditor(self, index, force = False):
|
||||
def updateStockEditor(self, index, force=False):
|
||||
|
||||
def setupFromBaseEdit():
|
||||
PathLog.track(index, force)
|
||||
if force or not self.stockFromBase:
|
||||
self.stockFromBase = StockFromBaseBoundBoxEdit(self.obj, self.form, force)
|
||||
self.stockEdit = self.stockFromBase
|
||||
|
||||
def setupCreateBoxEdit():
|
||||
PathLog.track(index, force)
|
||||
if force or not self.stockCreateBox:
|
||||
self.stockCreateBox = StockCreateBoxEdit(self.obj, self.form, force)
|
||||
self.stockEdit = self.stockCreateBox
|
||||
|
||||
def setupCreateCylinderEdit():
|
||||
PathLog.track(index, force)
|
||||
if force or not self.stockCreateCylinder:
|
||||
self.stockCreateCylinder = StockCreateCylinderEdit(self.obj, self.form, force)
|
||||
self.stockEdit = self.stockCreateCylinder
|
||||
|
||||
def setupFromExisting():
|
||||
PathLog.track(index, force)
|
||||
if force or not self.stockFromExisting:
|
||||
@@ -1098,7 +1116,7 @@ class TaskPanel:
|
||||
want = Counter(models)
|
||||
have = Counter([proxy.baseObject(obj, o) for o in obj.Model.Group])
|
||||
|
||||
obsolete = have - want
|
||||
obsolete = have - want
|
||||
additions = want - have
|
||||
|
||||
# first remove all obsolete base models
|
||||
@@ -1123,7 +1141,6 @@ class TaskPanel:
|
||||
else:
|
||||
PathLog.track('no changes to model')
|
||||
|
||||
|
||||
def tabPageChanged(self, index):
|
||||
if index == 0:
|
||||
# update the template with potential changes
|
||||
@@ -1156,8 +1173,8 @@ class TaskPanel:
|
||||
self.form.operationUp.clicked.connect(self.operationMoveUp)
|
||||
self.form.operationDown.clicked.connect(self.operationMoveDown)
|
||||
|
||||
self.form.operationEdit.hide() # not supported yet
|
||||
self.form.activeToolGroup.hide() # not supported yet
|
||||
self.form.operationEdit.hide() # not supported yet
|
||||
self.form.activeToolGroup.hide() # not supported yet
|
||||
|
||||
# Tool controller
|
||||
self.form.toolControllerList.itemSelectionChanged.connect(self.toolControllerSelect)
|
||||
@@ -1179,26 +1196,26 @@ class TaskPanel:
|
||||
self.form.modelSetXAxis.clicked.connect(lambda: self.modelSetAxis(FreeCAD.Vector(1, 0, 0)))
|
||||
self.form.modelSetYAxis.clicked.connect(lambda: self.modelSetAxis(FreeCAD.Vector(0, 1, 0)))
|
||||
self.form.modelSetZAxis.clicked.connect(lambda: self.modelSetAxis(FreeCAD.Vector(0, 0, 1)))
|
||||
self.form.modelSetX0.clicked.connect(lambda: self.modelSet0(FreeCAD.Vector(-1, 0, 0)))
|
||||
self.form.modelSetY0.clicked.connect(lambda: self.modelSet0(FreeCAD.Vector( 0, -1, 0)))
|
||||
self.form.modelSetZ0.clicked.connect(lambda: self.modelSet0(FreeCAD.Vector( 0, 0, -1)))
|
||||
self.form.modelSetX0.clicked.connect(lambda: self.modelSet0(FreeCAD.Vector(-1, 0, 0)))
|
||||
self.form.modelSetY0.clicked.connect(lambda: self.modelSet0(FreeCAD.Vector(0, -1, 0)))
|
||||
self.form.modelSetZ0.clicked.connect(lambda: self.modelSet0(FreeCAD.Vector(0, 0, -1)))
|
||||
|
||||
self.form.setOrigin.clicked.connect(self.alignSetOrigin)
|
||||
self.form.moveToOrigin.clicked.connect(self.alignMoveToOrigin)
|
||||
|
||||
self.form.modelMoveLeftUp.clicked.connect( lambda: self.modelMove(FreeCAD.Vector(-1, 1, 0)))
|
||||
self.form.modelMoveLeft.clicked.connect( lambda: self.modelMove(FreeCAD.Vector(-1, 0, 0)))
|
||||
self.form.modelMoveLeftDown.clicked.connect( lambda: self.modelMove(FreeCAD.Vector(-1, -1, 0)))
|
||||
self.form.modelMoveLeftUp.clicked.connect(lambda: self.modelMove(FreeCAD.Vector(-1, 1, 0)))
|
||||
self.form.modelMoveLeft.clicked.connect(lambda: self.modelMove(FreeCAD.Vector(-1, 0, 0)))
|
||||
self.form.modelMoveLeftDown.clicked.connect(lambda: self.modelMove(FreeCAD.Vector(-1, -1, 0)))
|
||||
|
||||
self.form.modelMoveUp.clicked.connect( lambda: self.modelMove(FreeCAD.Vector( 0, 1, 0)))
|
||||
self.form.modelMoveDown.clicked.connect( lambda: self.modelMove(FreeCAD.Vector( 0, -1, 0)))
|
||||
self.form.modelMoveUp.clicked.connect(lambda: self.modelMove(FreeCAD.Vector(0, 1, 0)))
|
||||
self.form.modelMoveDown.clicked.connect(lambda: self.modelMove(FreeCAD.Vector(0, -1, 0)))
|
||||
|
||||
self.form.modelMoveRightUp.clicked.connect( lambda: self.modelMove(FreeCAD.Vector( 1, 1, 0)))
|
||||
self.form.modelMoveRight.clicked.connect( lambda: self.modelMove(FreeCAD.Vector( 1, 0, 0)))
|
||||
self.form.modelMoveRightDown.clicked.connect( lambda: self.modelMove(FreeCAD.Vector( 1, -1, 0)))
|
||||
self.form.modelMoveRightUp.clicked.connect(lambda: self.modelMove(FreeCAD.Vector(1, 1, 0)))
|
||||
self.form.modelMoveRight.clicked.connect(lambda: self.modelMove(FreeCAD.Vector(1, 0, 0)))
|
||||
self.form.modelMoveRightDown.clicked.connect(lambda: self.modelMove(FreeCAD.Vector(1, -1, 0)))
|
||||
|
||||
self.form.modelRotateLeft.clicked.connect( lambda: self.modelRotate(FreeCAD.Vector(0, 0, 1)))
|
||||
self.form.modelRotateRight.clicked.connect( lambda: self.modelRotate(FreeCAD.Vector(0, 0, -1)))
|
||||
self.form.modelRotateLeft.clicked.connect(lambda: self.modelRotate(FreeCAD.Vector(0, 0, 1)))
|
||||
self.form.modelRotateRight.clicked.connect(lambda: self.modelRotate(FreeCAD.Vector(0, 0, -1)))
|
||||
|
||||
self.updateSelection()
|
||||
|
||||
@@ -1227,13 +1244,17 @@ class TaskPanel:
|
||||
# SelectionObserver interface
|
||||
def addSelection(self, doc, obj, sub, pnt):
|
||||
self.updateSelection()
|
||||
|
||||
def removeSelection(self, doc, obj, sub):
|
||||
self.updateSelection()
|
||||
|
||||
def setSelection(self, doc):
|
||||
self.updateSelection()
|
||||
|
||||
def clearSelection(self, doc):
|
||||
self.updateSelection()
|
||||
|
||||
|
||||
def Create(base, template=None):
|
||||
'''Create(base, template) ... creates a job instance for the given base object
|
||||
using template to configure it.'''
|
||||
@@ -1251,6 +1272,6 @@ def Create(base, template=None):
|
||||
traceback.print_exc(exc)
|
||||
FreeCAD.ActiveDocument.abortTransaction()
|
||||
|
||||
|
||||
# make sure the UI has been initialized
|
||||
PathGuiInit.Startup()
|
||||
|
||||
|
||||
@@ -58,10 +58,8 @@ from __future__ import print_function
|
||||
|
||||
import FreeCAD
|
||||
import MeshPart
|
||||
# import Part
|
||||
import Path
|
||||
import PathScripts.PathLog as PathLog
|
||||
# import PathScripts.PathPocketBase as PathPocketBase
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
import PathScripts.PathOp as PathOp
|
||||
|
||||
@@ -94,7 +92,7 @@ def translate(context, text, disambig=None):
|
||||
# OCL must be installed
|
||||
try:
|
||||
import ocl
|
||||
except:
|
||||
except ImportError:
|
||||
FreeCAD.Console.PrintError(
|
||||
translate("Path_Surface", "This operation requires OpenCamLib to be installed.") + "\n")
|
||||
import sys
|
||||
@@ -113,7 +111,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
def baseObject(self):
|
||||
'''baseObject() ... returns super of receiver
|
||||
Used to call base implementation in overwritten functions.'''
|
||||
return super(__class__, self)
|
||||
return super(self.__class__, self)
|
||||
|
||||
def opFeatures(self, obj):
|
||||
'''opFeatures(obj) ... return all standard features and edges based geomtries'''
|
||||
@@ -184,13 +182,13 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
initIdx = 0.0
|
||||
|
||||
# Instantiate additional class operation variables
|
||||
rtn = self.resetOpVariables()
|
||||
self.resetOpVariables()
|
||||
|
||||
# mark beginning of operation
|
||||
self.startTime = time.time()
|
||||
|
||||
# Set cutter for OCL based on tool controller properties
|
||||
rtn = self.setOclCutter(obj)
|
||||
self.setOclCutter(obj)
|
||||
|
||||
self.reportThis("\n-----\n-----\nBegin 3D surface operation")
|
||||
self.reportThis("Script version: " + __scriptVersion__ + " Lm: " + __lastModified__)
|
||||
@@ -297,7 +295,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
t = ocl.Triangle(ocl.Point(p[0], p[1], p[2] + obj.DepthOffset.Value),
|
||||
ocl.Point(q[0], q[1], q[2] + obj.DepthOffset.Value),
|
||||
ocl.Point(r[0], r[1], r[2] + obj.DepthOffset.Value))
|
||||
aT = stl.addTriangle(t)
|
||||
stl.addTriangle(t)
|
||||
final = self._waterlineOp(obj, stl, bb)
|
||||
elif obj.Algorithm == 'OCL Dropcutter':
|
||||
# Create stl object via OCL
|
||||
@@ -309,7 +307,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
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]))
|
||||
aT = stl.addTriangle(t)
|
||||
stl.addTriangle(t)
|
||||
|
||||
# Rotate model back to original index
|
||||
if obj.ScanType == 'Rotational':
|
||||
@@ -479,8 +477,8 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
if ignoreWasteFlag is True:
|
||||
topoMap = createTopoMap(scanCLP, obj.IgnoreWasteDepth)
|
||||
self.topoMap = listToMultiDimensional(topoMap, numLines, pntsPerLine)
|
||||
rtnA = self._bufferTopoMap(numLines, pntsPerLine)
|
||||
rtnB = self._highlightWaterline(4, 1)
|
||||
self._bufferTopoMap(numLines, pntsPerLine)
|
||||
self._highlightWaterline(4, 1)
|
||||
self.topoMap = debufferMultiDimenList(self.topoMap)
|
||||
ignoreMap = multiDimensionalToList(self.topoMap)
|
||||
|
||||
@@ -568,7 +566,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
holdCount = 0
|
||||
holdLine = 0
|
||||
onLine = False
|
||||
lineOfTravel = "X"
|
||||
# lineOfTravel = "X"
|
||||
pointsOnLine = 0
|
||||
zMin = prvDep
|
||||
zMax = prvDep
|
||||
@@ -603,7 +601,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
travVect = ocl.Point(float("inf"), float("inf"), float("inf"))
|
||||
|
||||
# Determine if releasing model from ignore waste areas
|
||||
if obj.ReleaseFromWaste == True:
|
||||
if obj.ReleaseFromWaste is True:
|
||||
minIgnVal = 0
|
||||
|
||||
# Set values for first gcode point in layer
|
||||
@@ -630,7 +628,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
pntCount += 1
|
||||
if pntCount == 1:
|
||||
# PathLog.debug("--Start row: " + str(rowCount))
|
||||
nn = 1
|
||||
pass # nn = 1
|
||||
elif pntCount == pntsPerLine:
|
||||
# PathLog.debug("--End row: " + str(rowCount))
|
||||
pntCount = 0
|
||||
@@ -769,8 +767,8 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
if self.onHold is False:
|
||||
if not optimize or not self.isPointOnLine(FreeCAD.Vector(prev.x, prev.y, prev.z), FreeCAD.Vector(nxt.x, nxt.y, nxt.z), FreeCAD.Vector(pnt.x, pnt.y, pnt.z)):
|
||||
output.append(Path.Command('G1', {'X': pnt.x, 'Y': pnt.y, 'Z': pnt.z, 'F': self.horizFeed}))
|
||||
elif i == lastCLP:
|
||||
output.append(Path.Command('G1', {'X': pnt.x, 'Y': pnt.y, 'Z': pnt.z, 'F': self.horizFeed}))
|
||||
# elif i == lastCLP:
|
||||
# output.append(Path.Command('G1', {'X': pnt.x, 'Y': pnt.y, 'Z': pnt.z, 'F': self.horizFeed}))
|
||||
|
||||
# Rotate point data
|
||||
prev.x = pnt.x
|
||||
@@ -831,7 +829,6 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
hscType = self.holdStopTypes.pop(0)
|
||||
|
||||
if hscType == "End":
|
||||
N = None
|
||||
# Create gcode commands to connect p1 and p2
|
||||
hpCmds = self.holdStopEndCmds(obj, p2, "Hold Stop: End point")
|
||||
elif hscType == "Mid":
|
||||
@@ -867,7 +864,6 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
return commands
|
||||
|
||||
def _rotationalDropCutterOp(self, obj, stl, bb):
|
||||
eT = time.time()
|
||||
self.resetTolerance = 0.0000001 # degrees
|
||||
self.layerEndzMax = 0.0
|
||||
commands = []
|
||||
@@ -878,12 +874,12 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
rings = []
|
||||
lCnt = 0
|
||||
rNum = 0
|
||||
stepDeg = 1.1
|
||||
layCircum = 1.1
|
||||
begIdx = 0.0
|
||||
endIdx = 0.0
|
||||
arc = 0.0
|
||||
sumAdv = 0.0
|
||||
# stepDeg = 1.1
|
||||
# layCircum = 1.1
|
||||
# begIdx = 0.0
|
||||
# endIdx = 0.0
|
||||
# arc = 0.0
|
||||
# sumAdv = 0.0
|
||||
bbRad = self.bbRadius
|
||||
|
||||
def invertAdvances(advances):
|
||||
@@ -905,7 +901,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
rngs.append([1.1]) # Initiate new ring
|
||||
for line in scanLines: # extract circular set(ring) of points from scan lines
|
||||
rngs[num].append(line[num])
|
||||
nn = rngs[num].pop(0)
|
||||
rngs[num].pop(0)
|
||||
return rngs
|
||||
|
||||
def indexAdvances(arc, stepDeg):
|
||||
@@ -1027,8 +1023,8 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
else:
|
||||
if obj.CutMode == 'Conventional':
|
||||
advances = invertAdvances(advances)
|
||||
rt = advances.reverse()
|
||||
rtn = scanLines.reverse()
|
||||
advances.reverse()
|
||||
scanLines.reverse()
|
||||
|
||||
# Invert advances if RotationAxis == Y
|
||||
if obj.RotationAxis == 'Y':
|
||||
@@ -1062,8 +1058,8 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
|
||||
def _indexedDropCutScan(self, obj, stl, advances, xmin, ymin, xmax, ymax, layDep, sample):
|
||||
cutterOfst = 0.0
|
||||
radsRot = 0.0
|
||||
reset = 0.0
|
||||
# radsRot = 0.0
|
||||
# reset = 0.0
|
||||
iCnt = 0
|
||||
Lines = []
|
||||
result = None
|
||||
@@ -1085,9 +1081,9 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
# Rotate STL object using OCL method
|
||||
radsRot = math.radians(adv)
|
||||
if obj.RotationAxis == 'X':
|
||||
rStl = stl.rotate(radsRot, 0.0, 0.0)
|
||||
stl.rotate(radsRot, 0.0, 0.0)
|
||||
else:
|
||||
rStl = stl.rotate(0.0, radsRot, 0.0)
|
||||
stl.rotate(0.0, radsRot, 0.0)
|
||||
|
||||
# Set STL after rotation is made
|
||||
pdc.setSTL(stl)
|
||||
@@ -1127,9 +1123,9 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
# Rotate STL object back to original position using OCL method
|
||||
reset = -1 * math.radians(sumAdv - self.resetTolerance)
|
||||
if obj.RotationAxis == 'X':
|
||||
rStl = stl.rotate(reset, 0.0, 0.0)
|
||||
stl.rotate(reset, 0.0, 0.0)
|
||||
else:
|
||||
rStl = stl.rotate(0.0, reset, 0.0)
|
||||
stl.rotate(0.0, reset, 0.0)
|
||||
self.resetTolerance = 0.0
|
||||
|
||||
return Lines
|
||||
@@ -1217,8 +1213,8 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
if self.onHold is False:
|
||||
if not optimize or not self.isPointOnLine(FreeCAD.Vector(prev.x, prev.y, prev.z), FreeCAD.Vector(nxt.x, nxt.y, nxt.z), FreeCAD.Vector(pnt.x, pnt.y, pnt.z)):
|
||||
output.append(Path.Command('G1', {'X': pnt.x, 'Y': pnt.y, 'Z': pnt.z, 'F': self.horizFeed}))
|
||||
elif i == lastCLP:
|
||||
output.append(Path.Command('G1', {'X': pnt.x, 'Y': pnt.y, 'Z': pnt.z, 'F': self.horizFeed}))
|
||||
# elif i == lastCLP:
|
||||
# output.append(Path.Command('G1', {'X': pnt.x, 'Y': pnt.y, 'Z': pnt.z, 'F': self.horizFeed}))
|
||||
|
||||
# Rotate point data
|
||||
prev.x = pnt.x
|
||||
@@ -1242,7 +1238,7 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
output = []
|
||||
nxtAng = 0
|
||||
zMax = 0.0
|
||||
prev = ocl.Point(float("inf"), float("inf"), float("inf"))
|
||||
# prev = ocl.Point(float("inf"), float("inf"), float("inf"))
|
||||
nxt = ocl.Point(float("inf"), float("inf"), float("inf"))
|
||||
pnt = ocl.Point(float("inf"), float("inf"), float("inf"))
|
||||
|
||||
@@ -1390,8 +1386,8 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
yVal = ymin + (nSL * smplInt)
|
||||
p1 = ocl.Point(xmin, yVal, fd) # start-point of line
|
||||
p2 = ocl.Point(xmax, yVal, fd) # end-point of line
|
||||
l = ocl.Line(p1, p2) # line-object
|
||||
path.append(l) # add the line to the path
|
||||
path.append(ocl.Line(p1, p2))
|
||||
# path.append(l) # add the line to the path
|
||||
pdc.setPath(path)
|
||||
pdc.run() # run drop-cutter on the path
|
||||
|
||||
@@ -1406,9 +1402,9 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
# Create topo map from scanLines (highs and lows)
|
||||
self.topoMap = self._createTopoMap(scanLines, layDep, lenSL, pntsPerLine)
|
||||
# Add buffer lines and columns to topo map
|
||||
rtn = self._bufferTopoMap(lenSL, pntsPerLine)
|
||||
self._bufferTopoMap(lenSL, pntsPerLine)
|
||||
# Identify layer waterline from OCL scan
|
||||
rtn = self._highlightWaterline(4, 9)
|
||||
self._highlightWaterline(4, 9)
|
||||
# Extract waterline and convert to gcode
|
||||
loopList = self._extractWaterlines(obj, scanLines, lyr, layDep)
|
||||
time.sleep(0.1)
|
||||
|
||||
@@ -30,7 +30,6 @@ import PathScripts.PathGeom as PathGeom
|
||||
import TechDraw
|
||||
import math
|
||||
import numpy
|
||||
import sys
|
||||
|
||||
from DraftGeomUtils import geomType
|
||||
from FreeCAD import Vector
|
||||
@@ -44,13 +43,15 @@ if False:
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
#FreeCAD.setLogLevel('Path.Area', 0)
|
||||
|
||||
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
|
||||
UserInput = None
|
||||
|
||||
|
||||
def waiting_effects(function):
|
||||
def new_function(*args, **kwargs):
|
||||
if not FreeCAD.GuiUp:
|
||||
@@ -60,7 +61,7 @@ def waiting_effects(function):
|
||||
try:
|
||||
res = function(*args, **kwargs)
|
||||
# don't catch exceptions - want to know where they are coming from ....
|
||||
#except Exception as e:
|
||||
# except Exception as e:
|
||||
# raise e
|
||||
# print("Error {}".format(e.args[0]))
|
||||
finally:
|
||||
@@ -148,7 +149,7 @@ def isDrillable(obj, candidate, tooldiameter=None, includePartials=False):
|
||||
PathLog.debug("candidate is a circle")
|
||||
v0 = edge.Vertexes[0].Point
|
||||
v1 = edge.Vertexes[1].Point
|
||||
#check if the cylinder seam is vertically aligned. Eliminate tilted holes
|
||||
# check if the cylinder seam is vertically aligned. Eliminate tilted holes
|
||||
if (numpy.isclose(v1.sub(v0).x, 0, rtol=1e-05, atol=1e-06)) and \
|
||||
(numpy.isclose(v1.sub(v0).y, 0, rtol=1e-05, atol=1e-06)):
|
||||
drillable = True
|
||||
@@ -160,7 +161,7 @@ def isDrillable(obj, candidate, tooldiameter=None, includePartials=False):
|
||||
# object. This eliminates extruded circles but allows
|
||||
# actual holes.
|
||||
if obj.isInside(lsp, 1e-6, False) or obj.isInside(lep, 1e-6, False):
|
||||
PathLog.track("inside check failed. lsp: {} lep: {}".format(lsp,lep))
|
||||
PathLog.track("inside check failed. lsp: {} lep: {}".format(lsp, lep))
|
||||
drillable = False
|
||||
# eliminate elliptical holes
|
||||
elif not hasattr(face.Surface, "Radius"):
|
||||
@@ -168,15 +169,15 @@ def isDrillable(obj, candidate, tooldiameter=None, includePartials=False):
|
||||
drillable = False
|
||||
else:
|
||||
if tooldiameter is not None:
|
||||
drillable = face.Surface.Radius >= tooldiameter/2
|
||||
drillable = face.Surface.Radius >= tooldiameter / 2
|
||||
else:
|
||||
drillable = True
|
||||
elif type(face.Surface) == Part.Plane and PathGeom.pointsCoincide(face.Surface.Axis, FreeCAD.Vector(0,0,1)):
|
||||
elif type(face.Surface) == Part.Plane and PathGeom.pointsCoincide(face.Surface.Axis, FreeCAD.Vector(0, 0, 1)):
|
||||
if len(face.Edges) == 1 and type(face.Edges[0].Curve) == Part.Circle:
|
||||
center = face.Edges[0].Curve.Center
|
||||
if obj.isInside(center, 1e-6, False):
|
||||
if tooldiameter is not None:
|
||||
drillable = face.Edges[0].Curve.Radius >= tooldiameter/2
|
||||
drillable = face.Edges[0].Curve.Radius >= tooldiameter / 2
|
||||
else:
|
||||
drillable = True
|
||||
else:
|
||||
@@ -189,12 +190,12 @@ def isDrillable(obj, candidate, tooldiameter=None, includePartials=False):
|
||||
else:
|
||||
PathLog.debug("Has Radius, Circle")
|
||||
if tooldiameter is not None:
|
||||
drillable = edge.Curve.Radius >= tooldiameter/2
|
||||
drillable = edge.Curve.Radius >= tooldiameter / 2
|
||||
if not drillable:
|
||||
FreeCAD.Console.PrintMessage(
|
||||
"Found a drillable hole with diameter: {}: "
|
||||
"too small for the current tool with "
|
||||
"diameter: {}".format(edge.Curve.Radius*2, tooldiameter))
|
||||
"Found a drillable hole with diameter: {}: "
|
||||
"too small for the current tool with "
|
||||
"diameter: {}".format(edge.Curve.Radius * 2, tooldiameter))
|
||||
else:
|
||||
drillable = True
|
||||
PathLog.debug("candidate is drillable: {}".format(drillable))
|
||||
@@ -204,7 +205,8 @@ def isDrillable(obj, candidate, tooldiameter=None, includePartials=False):
|
||||
|
||||
|
||||
# fixme set at 4 decimal places for testing
|
||||
def fmt(val): return format(val, '.4f')
|
||||
def fmt(val):
|
||||
return format(val, '.4f')
|
||||
|
||||
|
||||
def segments(poly):
|
||||
@@ -235,6 +237,7 @@ def loopdetect(obj, edge1, edge2):
|
||||
loopwire = next(x for x in loop)[1]
|
||||
return loopwire
|
||||
|
||||
|
||||
def horizontalEdgeLoop(obj, edge):
|
||||
'''horizontalEdgeLoop(obj, edge) ... returns a wire in the horizontal plane, if that is the only horizontal wire the given edge is a part of.'''
|
||||
h = edge.hashCode()
|
||||
@@ -244,6 +247,7 @@ def horizontalEdgeLoop(obj, edge):
|
||||
return loops[0]
|
||||
return None
|
||||
|
||||
|
||||
def horizontalFaceLoop(obj, face, faceList=None):
|
||||
'''horizontalFaceLoop(obj, face, faceList=None) ... returns a list of face names which form the walls of a vertical hole face is a part of.
|
||||
All face names listed in faceList must be part of the hole for the solution to be returned.'''
|
||||
@@ -256,8 +260,8 @@ def horizontalFaceLoop(obj, face, faceList=None):
|
||||
for wire in wires:
|
||||
hashes = [e.hashCode() for e in wire.Edges]
|
||||
|
||||
#find all faces that share a an edge with the wire and are vertical
|
||||
faces = ["Face%d"%(i+1) for i,f in enumerate(obj.Shape.Faces) if any(e.hashCode() in hashes for e in f.Edges) and PathGeom.isVertical(f)]
|
||||
# find all faces that share a an edge with the wire and are vertical
|
||||
faces = ["Face%d" % (i + 1) for i, f in enumerate(obj.Shape.Faces) if any(e.hashCode() in hashes for e in f.Edges) and PathGeom.isVertical(f)]
|
||||
|
||||
if faceList and not all(f in faces for f in faceList):
|
||||
continue
|
||||
@@ -265,7 +269,7 @@ def horizontalFaceLoop(obj, face, faceList=None):
|
||||
# verify they form a valid hole by getting the outline and comparing
|
||||
# the resulting XY footprint with that of the faces
|
||||
comp = Part.makeCompound([obj.Shape.getElement(f) for f in faces])
|
||||
outline = TechDraw.findShapeOutline(comp, 1, FreeCAD.Vector(0,0,1))
|
||||
outline = TechDraw.findShapeOutline(comp, 1, FreeCAD.Vector(0, 0, 1))
|
||||
|
||||
# findShapeOutline always returns closed wires, by removing the
|
||||
# trace-backs single edge spikes don't contriubte to the bound box
|
||||
@@ -284,6 +288,7 @@ def horizontalFaceLoop(obj, face, faceList=None):
|
||||
return faces
|
||||
return None
|
||||
|
||||
|
||||
def filterArcs(arcEdge):
|
||||
'''filterArcs(Edge) -used to split arcs that over 180 degrees. Returns list '''
|
||||
PathLog.track()
|
||||
@@ -302,10 +307,8 @@ def filterArcs(arcEdge):
|
||||
arcstpt = s.valueAt(s.FirstParameter)
|
||||
arcmid = s.valueAt(
|
||||
(s.LastParameter - s.FirstParameter) * 0.5 + s.FirstParameter)
|
||||
arcquad1 = s.valueAt((s.LastParameter - s.FirstParameter) *
|
||||
0.25 + s.FirstParameter) # future midpt for arc1
|
||||
arcquad2 = s.valueAt((s.LastParameter - s.FirstParameter) *
|
||||
0.75 + s.FirstParameter) # future midpt for arc2
|
||||
arcquad1 = s.valueAt((s.LastParameter - s.FirstParameter) * 0.25 + s.FirstParameter) # future midpt for arc1
|
||||
arcquad2 = s.valueAt((s.LastParameter - s.FirstParameter) * 0.75 + s.FirstParameter) # future midpt for arc2
|
||||
arcendpt = s.valueAt(s.LastParameter)
|
||||
# reconstruct with 2 arcs
|
||||
arcseg1 = Part.ArcOfCircle(arcstpt, arcquad1, arcmid)
|
||||
@@ -345,10 +348,6 @@ def getEnvelope(partshape, subshape=None, depthparams=None):
|
||||
'''
|
||||
PathLog.track(partshape, subshape, depthparams)
|
||||
|
||||
# if partshape.Volume == 0.0: #Not a 3D object
|
||||
# return None
|
||||
|
||||
|
||||
zShift = 0
|
||||
if subshape is not None:
|
||||
if isinstance(subshape, Part.Face):
|
||||
@@ -360,7 +359,6 @@ def getEnvelope(partshape, subshape=None, depthparams=None):
|
||||
PathLog.debug("About to section with params: {}".format(area.getParams()))
|
||||
sec = area.makeSections(heights=[0.0], project=True)[0].getShape()
|
||||
|
||||
# zShift = partshape.BoundBox.ZMin - subshape.BoundBox.ZMin
|
||||
PathLog.debug('partshapeZmin: {}, subshapeZMin: {}, zShift: {}'.format(partshape.BoundBox.ZMin, subshape.BoundBox.ZMin, zShift))
|
||||
|
||||
else:
|
||||
@@ -371,9 +369,7 @@ def getEnvelope(partshape, subshape=None, depthparams=None):
|
||||
# If depthparams are passed, use it to calculate bottom and height of
|
||||
# envelope
|
||||
if depthparams is not None:
|
||||
# eLength = float(stockheight)-partshape.BoundBox.ZMin
|
||||
eLength = depthparams.safe_height - depthparams.final_depth
|
||||
#envelopeshape = sec.extrude(FreeCAD.Vector(0, 0, eLength))
|
||||
zShift = depthparams.final_depth - sec.BoundBox.ZMin
|
||||
PathLog.debug('boundbox zMIN: {} elength: {} zShift {}'.format(partshape.BoundBox.ZMin, eLength, zShift))
|
||||
else:
|
||||
@@ -467,6 +463,7 @@ def GetJobs(jobname=None):
|
||||
return [job for job in PathJob.Instances() if job.Name == jobname]
|
||||
return PathJob.Instances()
|
||||
|
||||
|
||||
def addToJob(obj, jobname=None):
|
||||
'''adds a path object to a job
|
||||
obj = obj
|
||||
@@ -492,6 +489,7 @@ def addToJob(obj, jobname=None):
|
||||
job.Proxy.addOperation(obj)
|
||||
return job
|
||||
|
||||
|
||||
def rapid(x=None, y=None, z=None):
|
||||
""" Returns gcode string to perform a rapid move."""
|
||||
retstr = "G00"
|
||||
@@ -583,11 +581,11 @@ def helicalPlunge(plungePos, rampangle, destZ, startZ, toold, plungeR, horizFeed
|
||||
raise Exception("Helical plunging requires a position!")
|
||||
return None
|
||||
|
||||
helixX = plungePos.x + toold/2 * plungeR
|
||||
helixX = plungePos.x + toold / 2 * plungeR
|
||||
helixY = plungePos.y
|
||||
|
||||
helixCirc = math.pi * toold * plungeR
|
||||
dzPerRev = math.sin(rampangle/180. * math.pi) * helixCirc
|
||||
dzPerRev = math.sin(rampangle / 180. * math.pi) * helixCirc
|
||||
|
||||
# Go to the start of the helix position
|
||||
helixCmds += rapid(helixX, helixY)
|
||||
@@ -595,7 +593,7 @@ def helicalPlunge(plungePos, rampangle, destZ, startZ, toold, plungeR, horizFeed
|
||||
|
||||
# Helix as required to get to the requested depth
|
||||
lastZ = startZ
|
||||
curZ = max(startZ-dzPerRev, destZ)
|
||||
curZ = max(startZ - dzPerRev, destZ)
|
||||
done = False
|
||||
while not done:
|
||||
done = (curZ == destZ)
|
||||
@@ -604,7 +602,7 @@ def helicalPlunge(plungePos, rampangle, destZ, startZ, toold, plungeR, horizFeed
|
||||
|
||||
# Use two half-helixes; FreeCAD renders that correctly,
|
||||
# and it fits with the other code breaking up 360-degree arcs
|
||||
helixCmds += arc(plungePos.x, plungePos.y, helixX, helixY, helixX - toold * plungeR, helixY, horizFeed, ez=(curZ + lastZ)/2., ccw=True)
|
||||
helixCmds += arc(plungePos.x, plungePos.y, helixX, helixY, helixX - toold * plungeR, helixY, horizFeed, ez=(curZ + lastZ) / 2., ccw=True)
|
||||
helixCmds += arc(plungePos.x, plungePos.y, helixX - toold * plungeR, helixY, helixX, helixY, horizFeed, ez=curZ, ccw=True)
|
||||
lastZ = curZ
|
||||
curZ = max(curZ - dzPerRev, destZ)
|
||||
@@ -641,7 +639,7 @@ def rampPlunge(edge, rampangle, destZ, startZ):
|
||||
ePoint = edge.Vertexes[-1].Point
|
||||
|
||||
rampDist = edge.Length
|
||||
rampDZ = math.sin(rampangle/180. * math.pi) * rampDist
|
||||
rampDZ = math.sin(rampangle / 180. * math.pi) * rampDist
|
||||
|
||||
rampCmds += rapid(sPoint.x, sPoint.y)
|
||||
rampCmds += rapid(z=startZ)
|
||||
@@ -649,7 +647,7 @@ def rampPlunge(edge, rampangle, destZ, startZ):
|
||||
# Ramp down to the requested depth
|
||||
# FIXME: This might be an arc, so handle that as well
|
||||
|
||||
curZ = max(startZ-rampDZ, destZ)
|
||||
curZ = max(startZ - rampDZ, destZ)
|
||||
done = False
|
||||
while not done:
|
||||
done = (curZ == destZ)
|
||||
@@ -699,7 +697,7 @@ def sort_jobs(locations, keys, attractors=[]):
|
||||
def find_closest(location_list, location, dist):
|
||||
q = PriorityQueue()
|
||||
|
||||
for i,j in enumerate(location_list):
|
||||
for i, j in enumerate(location_list):
|
||||
# prevent dictionary comparison by inserting the index
|
||||
q.put((dist(j, location) + weight(j), i, j))
|
||||
|
||||
@@ -720,6 +718,7 @@ def sort_jobs(locations, keys, attractors=[]):
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def guessDepths(objshape, subs=None):
|
||||
"""
|
||||
takes an object shape and optional list of subobjects and returns a depth_params
|
||||
@@ -751,6 +750,7 @@ def guessDepths(objshape, subs=None):
|
||||
|
||||
return depth_params(clearance, safe, start, 1.0, 0.0, final, user_depths=None, equalstep=False)
|
||||
|
||||
|
||||
def drillTipLength(tool):
|
||||
"""returns the length of the drillbit tip."""
|
||||
if tool.CuttingEdgeAngle == 180 or tool.CuttingEdgeAngle == 0.0 or tool.Diameter == 0.0:
|
||||
@@ -760,13 +760,14 @@ def drillTipLength(tool):
|
||||
PathLog.error(translate("Path", "Invalid Cutting Edge Angle %.2f, must be >0° and <=180°") % tool.CuttingEdgeAngle)
|
||||
return 0.0
|
||||
theta = math.radians(tool.CuttingEdgeAngle)
|
||||
length = (tool.Diameter/2) / math.tan(theta/2)
|
||||
length = (tool.Diameter / 2) / math.tan(theta / 2)
|
||||
if length < 0:
|
||||
PathLog.error(translate("Path", "Cutting Edge Angle (%.2f) results in negative tool tip length") % tool.CuttingEdgeAngle)
|
||||
return 0.0
|
||||
return length
|
||||
|
||||
class depth_params:
|
||||
|
||||
class depth_params(object):
|
||||
'''calculates the intermediate depth values for various operations given the starting, ending, and stepdown parameters
|
||||
(self, clearance_height, safe_height, start_depth, step_down, z_finish_depth, final_depth, [user_depths=None], equalstep=False)
|
||||
|
||||
@@ -821,7 +822,7 @@ class depth_params:
|
||||
@property
|
||||
def safe_height(self):
|
||||
"""
|
||||
Height of top of raw stock material. Rapid moves above safe height are
|
||||
Height of top of raw stock material. Rapid moves above safe height are
|
||||
assumed to be safe within an operation. May not be safe between
|
||||
operations or tool changes.
|
||||
All moves below safe height except retraction should be at feed rate.
|
||||
@@ -922,5 +923,3 @@ class depth_params:
|
||||
return depths
|
||||
else:
|
||||
return [stop] + depths
|
||||
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ import argparse
|
||||
import datetime
|
||||
import shlex
|
||||
from PathScripts import PostUtils
|
||||
from PathScripts import PathUtils
|
||||
|
||||
TOOLTIP = '''
|
||||
This is a postprocessor file for the Path workbench. It is used to
|
||||
@@ -54,7 +53,6 @@ parser.add_argument('--postamble', help='set commands to be issued after the las
|
||||
parser.add_argument('--inches', action='store_true', help='Convert output for US imperial mode (G20)')
|
||||
parser.add_argument('--modal', action='store_true', help='Output the Same G-command Name USE NonModal Mode')
|
||||
parser.add_argument('--axis-modal', action='store_true', help='Output the Same Axis Value Mode')
|
||||
#parser.add_argument('--power-max', help='set the max value for laser power default=255')
|
||||
parser.add_argument('--power-on-delay', default='255', help='milliseconds - Add a delay after laser on before moving to pre-heat material. Default=0')
|
||||
|
||||
|
||||
@@ -104,7 +102,6 @@ POST_OPERATION = ''''''
|
||||
# Tool Change commands will be inserted before a tool change
|
||||
TOOL_CHANGE = ''''''
|
||||
|
||||
#POWER_MAX = 255
|
||||
POWER_ON_DELAY = 0
|
||||
|
||||
# to distinguish python built-in open function from the one declared below
|
||||
@@ -152,9 +149,9 @@ def processArguments(argstring):
|
||||
MODAL = True
|
||||
if args.axis_modal:
|
||||
OUTPUT_DOUBLES = False
|
||||
POWER_ON_DELAY = float(args.power_on_delay) / 1000 #milliseconds
|
||||
POWER_ON_DELAY = float(args.power_on_delay) / 1000 # milliseconds
|
||||
|
||||
except:
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
return True
|
||||
@@ -290,7 +287,7 @@ def parse(pathobj):
|
||||
if command == lastcommand:
|
||||
outstring.pop(0)
|
||||
|
||||
if c.Name[0] == '(' and not OUTPUT_COMMENTS: # command is a comment
|
||||
if c.Name[0] == '(' and not OUTPUT_COMMENTS: # command is a comment
|
||||
continue
|
||||
|
||||
# Now add the remaining parameters in order
|
||||
@@ -326,10 +323,6 @@ def parse(pathobj):
|
||||
# Check for Tool Change:
|
||||
if command == 'M6':
|
||||
continue
|
||||
# if OUTPUT_COMMENTS:
|
||||
# out += linenumber() + "(begin toolchange)\n"
|
||||
for line in TOOL_CHANGE.splitlines(True):
|
||||
out += linenumber() + line
|
||||
|
||||
if command == "message":
|
||||
if OUTPUT_COMMENTS is False:
|
||||
@@ -349,4 +342,5 @@ def parse(pathobj):
|
||||
|
||||
return out
|
||||
|
||||
|
||||
print(__name__ + " gcode postprocessor loaded.")
|
||||
|
||||
@@ -142,7 +142,7 @@ def processArguments(argstring):
|
||||
print ('here')
|
||||
OUTPUT_DOUBLES = False
|
||||
|
||||
except:
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@@ -26,7 +26,7 @@ from PathScripts import PostUtils
|
||||
# ***************************************************************************/
|
||||
|
||||
|
||||
TOOLTIP='''
|
||||
TOOLTIP = '''
|
||||
This is an postprocessor file for the Path workbench. It will output path data
|
||||
in a format suitable for OpenSBP controllers like shopbot. This postprocessor,
|
||||
once placed in the appropriate PathScripts folder, can be used directly from
|
||||
@@ -50,7 +50,7 @@ ToDo
|
||||
|
||||
'''
|
||||
|
||||
TOOLTIP_ARGS='''
|
||||
TOOLTIP_ARGS = '''
|
||||
Arguments for opensbp:
|
||||
--comments ... insert comments - mostly for debugging
|
||||
--inches ... convert output to inches
|
||||
@@ -80,16 +80,20 @@ POST_OPERATION = ''''''
|
||||
TOOL_CHANGE = ''''''
|
||||
|
||||
# to distinguish python built-in open function from the one declared below
|
||||
if open.__module__ in ['__builtin__','io']:
|
||||
if open.__module__ in ['__builtin__', 'io']:
|
||||
pythonopen = open
|
||||
|
||||
CurrentState = {}
|
||||
|
||||
|
||||
def getMetricValue(val):
|
||||
return val
|
||||
|
||||
|
||||
def getImperialValue(val):
|
||||
return val / 25.4
|
||||
|
||||
|
||||
GetValue = getMetricValue
|
||||
|
||||
|
||||
@@ -110,7 +114,6 @@ def export(objectslist, filename, argstring):
|
||||
if arg == '--no-show-editor':
|
||||
SHOW_EDITOR = False
|
||||
|
||||
|
||||
for obj in objectslist:
|
||||
if not hasattr(obj, "Path"):
|
||||
s = "the object " + obj.Name
|
||||
@@ -333,7 +336,6 @@ def parse(pathobj):
|
||||
global CurrentState
|
||||
|
||||
output = ""
|
||||
params = ['X', 'Y', 'Z', 'A', 'B', 'I', 'J', 'K', 'F', 'S', 'T']
|
||||
# Above list controls the order of parameters
|
||||
|
||||
if hasattr(pathobj, "Group"): # We have a compound or project.
|
||||
@@ -356,7 +358,7 @@ def parse(pathobj):
|
||||
elif command[0] == '(':
|
||||
output += "' " + command + "\n"
|
||||
else:
|
||||
print("I don't know what the hell the command: ",end='')
|
||||
print("I don't know what the hell the command: ", end='')
|
||||
print(command + " means. Maybe I should support it.")
|
||||
return output
|
||||
|
||||
|
||||
@@ -49,13 +49,14 @@ Many other OpenSBP commands not handled
|
||||
from __future__ import print_function
|
||||
import FreeCAD
|
||||
import PathScripts.PathUtil as PathUtil
|
||||
import os, Path
|
||||
import os
|
||||
import Path
|
||||
|
||||
AXIS = 'X','Y','Z','A','B' #OpenSBP always puts multiaxis move parameters in this order
|
||||
SPEEDS = 'XY','Z','A','B'
|
||||
AXIS = 'X', 'Y', 'Z', 'A', 'B' # OpenSBP always puts multiaxis move parameters in this order
|
||||
SPEEDS = 'XY', 'Z', 'A', 'B'
|
||||
|
||||
# to distinguish python built-in open function from the one declared below
|
||||
if open.__module__ in ['__builtin__','io']:
|
||||
if open.__module__ in ['__builtin__', 'io']:
|
||||
pythonopen = open
|
||||
|
||||
|
||||
@@ -63,10 +64,10 @@ def open(filename):
|
||||
"called when freecad opens a file."
|
||||
docname = os.path.splitext(os.path.basename(filename))[0]
|
||||
doc = FreeCAD.newDocument(docname)
|
||||
insert(filename,doc.Name)
|
||||
insert(filename, doc.Name)
|
||||
|
||||
|
||||
def insert(filename,docname):
|
||||
def insert(filename, docname):
|
||||
"called when freecad imports a file"
|
||||
"This insert expects parse to return a list of strings"
|
||||
"each string will become a separate path"
|
||||
@@ -76,7 +77,7 @@ def insert(filename,docname):
|
||||
gcode = parse(gcode)
|
||||
doc = FreeCAD.getDocument(docname)
|
||||
for subpath in gcode:
|
||||
obj = doc.addObject("Path::Feature","Path")
|
||||
obj = doc.addObject("Path::Feature", "Path")
|
||||
path = Path.Path(subpath)
|
||||
obj.Path = path
|
||||
|
||||
@@ -88,123 +89,121 @@ def parse(inputstring):
|
||||
lines = inputstring.split("\n")
|
||||
return_output = []
|
||||
output = ""
|
||||
last = {'X':None,'Y':None,'Z':None,'A':None,'B':None}
|
||||
lastrapidspeed = {'XY':"50", 'Z':"50", 'A':"50", 'B':"50" } #set default rapid speeds
|
||||
lastfeedspeed = {'XY':"50", 'Z':"50", 'A':"50", 'B':"50" } #set default feed speed
|
||||
last = {'X': None, 'Y': None, 'Z': None, 'A': None, 'B': None}
|
||||
lastrapidspeed = {'XY': "50", 'Z': "50", 'A': "50", 'B': "50"} # set default rapid speeds
|
||||
lastfeedspeed = {'XY': "50", 'Z': "50", 'A': "50", 'B': "50"} # set default feed speed
|
||||
movecommand = ['G1', 'G0', 'G02', 'G03']
|
||||
|
||||
for l in lines:
|
||||
for line in lines:
|
||||
# remove any leftover trailing and preceding spaces
|
||||
l = l.strip()
|
||||
if not l:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
# discard empty lines
|
||||
continue
|
||||
if l[0] in ["'","&"]:
|
||||
if line[0] in ["'", "&"]:
|
||||
# discard comment and other non strictly gcode lines
|
||||
if l[0:9] == "'New Path":
|
||||
if line[0:9] == "'New Path":
|
||||
# starting new path
|
||||
if any (x in output for x in movecommand): #make sure the path has at least one move command.
|
||||
if any(x in output for x in movecommand): # make sure the path has at least one move command.
|
||||
return_output.append(output)
|
||||
output = ""
|
||||
continue
|
||||
|
||||
words = [a.strip() for a in l.split(",")]
|
||||
words = [a.strip() for a in line.split(",")]
|
||||
words[0] = words[0].upper()
|
||||
if words[0] in ["J2","J3","J4","J5","M2","M3","M4","M5"]: #multi-axis jogs and moves
|
||||
if words[0][0] == 'J': #jog move
|
||||
if words[0] in ["J2", "J3", "J4", "J5", "M2", "M3", "M4", "M5"]: # multi-axis jogs and moves
|
||||
if words[0][0] == 'J': # jog move
|
||||
s = "G0 "
|
||||
else: #feed move
|
||||
else: # feed move
|
||||
s = "G1 "
|
||||
speed = lastfeedspeed["XY"]
|
||||
|
||||
for i in range (1, len(words)):
|
||||
if words [i] == '':
|
||||
if last[AXIS[i-1]] == None:
|
||||
for i in range(1, len(words)):
|
||||
if words[i] == '':
|
||||
if last[AXIS[i - 1]] is None:
|
||||
continue
|
||||
else:
|
||||
s += AXIS[i-1] + last[AXIS[i-1]]
|
||||
s += AXIS[i - 1] + last[AXIS[i - 1]]
|
||||
else:
|
||||
s += AXIS[i-1] + words[i]
|
||||
last[AXIS[i-1]] = words[i]
|
||||
output += s +" F" + speed + '\n'
|
||||
s += AXIS[i - 1] + words[i]
|
||||
last[AXIS[i - 1]] = words[i]
|
||||
output += s + " F" + speed + '\n'
|
||||
|
||||
if words[0] in ["JA","JB","JX","JY","JZ","MA","MB","MX","MY","MZ"]: #single axis jogs and moves
|
||||
if words[0][0] == 'J': #jog move
|
||||
if words[0] in ["JA", "JB", "JX", "JY", "JZ", "MA", "MB", "MX", "MY", "MZ"]: # single axis jogs and moves
|
||||
if words[0][0] == 'J': # jog move
|
||||
s = "G0 "
|
||||
if words[0][1] in ['X','Y']:
|
||||
if words[0][1] in ['X', 'Y']:
|
||||
speed = lastrapidspeed["XY"]
|
||||
else:
|
||||
speed = lastrapidspeed[words[0][1]]
|
||||
|
||||
else: #feed move
|
||||
else: # feed move
|
||||
s = "G1 "
|
||||
if words[0][1] in ['X','Y']:
|
||||
if words[0][1] in ['X', 'Y']:
|
||||
speed = lastfeedspeed["XY"]
|
||||
else:
|
||||
speed = lastfeedspeed[words[0][1]]
|
||||
|
||||
|
||||
last[words[0][1]] = words[1]
|
||||
output += s
|
||||
for key, val in PathUtil.keyValueIter(last):
|
||||
if val is not None:
|
||||
output += key + str(val) + " F" + speed + "\n"
|
||||
|
||||
if words[0] in ["JS"]: #set jog speed
|
||||
for i in range (1, len(words)):
|
||||
if words [i] == '':
|
||||
if words[0] in ["JS"]: # set jog speed
|
||||
for i in range(1, len(words)):
|
||||
if words[i] == '':
|
||||
continue
|
||||
else:
|
||||
lastrapidspeed[SPEEDS[i-1]] = words[i]
|
||||
lastrapidspeed[SPEEDS[i - 1]] = words[i]
|
||||
|
||||
if words[0] in ["MD"]: #move distance with distance and angle.
|
||||
#unsupported at this time
|
||||
if words[0] in ["MD"]: # move distance with distance and angle.
|
||||
# unsupported at this time
|
||||
continue
|
||||
if words[0] in ["MH"]: #move home
|
||||
#unsupported at this time
|
||||
if words[0] in ["MH"]: # move home
|
||||
# unsupported at this time
|
||||
continue
|
||||
if words[0] in ["MS"]: #set move speed
|
||||
for i in range (1, len(words)):
|
||||
if words [i] == '':
|
||||
if words[0] in ["MS"]: # set move speed
|
||||
for i in range(1, len(words)):
|
||||
if words[i] == '':
|
||||
continue
|
||||
else:
|
||||
lastfeedspeed[SPEEDS[i-1]] = words[i]
|
||||
if words[0] in ["MO"]: #motors off
|
||||
#unsupported at this time
|
||||
lastfeedspeed[SPEEDS[i - 1]] = words[i]
|
||||
if words[0] in ["MO"]: # motors off
|
||||
# unsupported at this time
|
||||
continue
|
||||
|
||||
if words[0] in ["TR"]: #Setting spindle speed
|
||||
if float(words[1]) < 0:
|
||||
if words[0] in ["TR"]: # Setting spindle speed
|
||||
if float(words[1]) < 0:
|
||||
s = "M4 S"
|
||||
else:
|
||||
s = "M3 S"
|
||||
s += str(abs(float(words[1])))
|
||||
output += s + '\n'
|
||||
|
||||
if words[0] in ["CG"]: #Gcode circle/arc
|
||||
if words[1] != "": # diameter mode
|
||||
if words[0] in ["CG"]: # Gcode circle/arc
|
||||
if words[1] != "": # diameter mode
|
||||
print("diameter mode not supported")
|
||||
continue
|
||||
|
||||
else:
|
||||
if words[7] == "1": #CW
|
||||
if words[7] == "1": # CW
|
||||
s = "G2"
|
||||
else: #CCW
|
||||
else: # CCW
|
||||
s = "G3"
|
||||
|
||||
|
||||
s += " X" + words[2] + " Y" + words[3] + " I" + words[4] + " J" + words[5] + " F" + str(lastfeedspeed["XY"])
|
||||
output += s + '\n'
|
||||
output += s + '\n'
|
||||
|
||||
last["X"] = words[2]
|
||||
last["Y"] = words[3]
|
||||
|
||||
#Make sure all appended paths have at least one move command.
|
||||
if any (x in output for x in movecommand):
|
||||
# Make sure all appended paths have at least one move command.
|
||||
if any(x in output for x in movecommand):
|
||||
return_output.append(output)
|
||||
print("done preprocessing.")
|
||||
|
||||
return return_output
|
||||
|
||||
print(__name__ + " gcode preprocessor loaded.")
|
||||
|
||||
print(__name__ + " gcode preprocessor loaded.")
|
||||
|
||||
@@ -22,7 +22,14 @@
|
||||
# ***************************************************************************/
|
||||
from __future__ import print_function
|
||||
|
||||
TOOLTIP='''
|
||||
import argparse
|
||||
import datetime
|
||||
from PathScripts import PostUtils
|
||||
import FreeCAD
|
||||
from FreeCAD import Units
|
||||
import shlex
|
||||
|
||||
TOOLTIP = '''
|
||||
This is a postprocessor file for the Path workbench. It is used to
|
||||
take a pseudo-gcode fragment outputted by a Path object, and output
|
||||
real GCode suitable for a smoothieboard. This postprocessor, once placed
|
||||
@@ -33,13 +40,6 @@ import smoothie_post
|
||||
smoothie_post.export(object,"/path/to/file.ncc","")
|
||||
'''
|
||||
|
||||
import argparse
|
||||
import datetime
|
||||
from PathScripts import PostUtils
|
||||
import FreeCAD
|
||||
from FreeCAD import Units
|
||||
import shlex
|
||||
|
||||
now = datetime.datetime.now()
|
||||
|
||||
parser = argparse.ArgumentParser(prog='linuxcnc', add_help=False)
|
||||
@@ -58,7 +58,7 @@ parser.add_argument('--IP_ADDR', help='IP Address for machine target machine')
|
||||
parser.add_argument('--verbose', action='store_true', help='verbose output for debugging, default="False"')
|
||||
parser.add_argument('--inches', action='store_true', help='Convert output for US imperial mode (G20)')
|
||||
|
||||
TOOLTIP_ARGS=parser.format_help()
|
||||
TOOLTIP_ARGS = parser.format_help()
|
||||
|
||||
# These globals set common customization preferences
|
||||
OUTPUT_COMMENTS = True
|
||||
@@ -108,9 +108,10 @@ TOOL_CHANGE = ''''''
|
||||
|
||||
|
||||
# to distinguish python built-in open function from the one declared below
|
||||
if open.__module__ in ['__builtin__','io']:
|
||||
if open.__module__ in ['__builtin__', 'io']:
|
||||
pythonopen = open
|
||||
|
||||
|
||||
def processArguments(argstring):
|
||||
global OUTPUT_HEADER
|
||||
global OUTPUT_COMMENTS
|
||||
@@ -125,7 +126,6 @@ def processArguments(argstring):
|
||||
global UNIT_SPEED_FORMAT
|
||||
global UNIT_FORMAT
|
||||
|
||||
|
||||
try:
|
||||
args = parser.parse_args(shlex.split(argstring))
|
||||
|
||||
@@ -159,11 +159,12 @@ def processArguments(argstring):
|
||||
IP_ADDR = args.IP_ADDR
|
||||
VERBOSE = args.verbose
|
||||
|
||||
except:
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def export(objectslist, filename, argstring):
|
||||
processArguments(argstring)
|
||||
global UNITS
|
||||
@@ -181,13 +182,13 @@ def export(objectslist, filename, argstring):
|
||||
# sure we're using the current values in the Machine Def.
|
||||
myMachine = None
|
||||
for pathobj in objectslist:
|
||||
if hasattr(pathobj,"MachineName"):
|
||||
if hasattr(pathobj, "MachineName"):
|
||||
myMachine = pathobj.MachineName
|
||||
if hasattr(pathobj, "MachineUnits"):
|
||||
if pathobj.MachineUnits == "Metric":
|
||||
UNITS = "G21"
|
||||
UNITS = "G21"
|
||||
else:
|
||||
UNITS = "G20"
|
||||
UNITS = "G20"
|
||||
if myMachine is None:
|
||||
FreeCAD.Console.PrintWarning("No machine found in this selection\n")
|
||||
|
||||
@@ -258,68 +259,71 @@ def sendToSmoothie(IP_ADDR, GCODE, fname):
|
||||
global VERBOSE
|
||||
|
||||
fname = os.path.basename(fname)
|
||||
FreeCAD.Console.PrintMessage ('sending to smoothie: {}\n'.format(fname))
|
||||
FreeCAD.Console.PrintMessage('sending to smoothie: {}\n'.format(fname))
|
||||
|
||||
f = GCODE.rstrip()
|
||||
filesize= len(f)
|
||||
filesize = len(f)
|
||||
# make connection to sftp server
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.settimeout(4.0)
|
||||
s.connect((IP_ADDR, 115))
|
||||
tn= s.makefile(mode='rw')
|
||||
tn = s.makefile(mode='rw')
|
||||
|
||||
# read startup prompt
|
||||
ln= tn.readline()
|
||||
if not ln.startswith("+") :
|
||||
ln = tn.readline()
|
||||
if not ln.startswith("+"):
|
||||
FreeCAD.Console.PrintMessage("Failed to connect with sftp: {}\n".format(ln))
|
||||
sys.exit();
|
||||
sys.exit()
|
||||
|
||||
if VERBOSE: print("RSP: " + ln.strip())
|
||||
if VERBOSE:
|
||||
print("RSP: " + ln.strip())
|
||||
|
||||
# Issue initial store command
|
||||
tn.write("STOR OLD /sd/" + fname + "\n")
|
||||
tn.flush()
|
||||
|
||||
ln= tn.readline()
|
||||
if not ln.startswith("+") :
|
||||
ln = tn.readline()
|
||||
if not ln.startswith("+"):
|
||||
FreeCAD.Console.PrintError("Failed to create file: {}\n".format(ln))
|
||||
sys.exit();
|
||||
sys.exit()
|
||||
|
||||
if VERBOSE: print("RSP: " + ln.strip())
|
||||
if VERBOSE:
|
||||
print("RSP: " + ln.strip())
|
||||
|
||||
# send size of file
|
||||
tn.write("SIZE " + str(filesize) + "\n")
|
||||
tn.flush()
|
||||
|
||||
ln= tn.readline()
|
||||
if not ln.startswith("+") :
|
||||
ln = tn.readline()
|
||||
if not ln.startswith("+"):
|
||||
FreeCAD.Console.PrintError("Failed: {}\n".format(ln))
|
||||
sys.exit();
|
||||
sys.exit()
|
||||
|
||||
if VERBOSE: print("RSP: " + ln.strip())
|
||||
if VERBOSE:
|
||||
print("RSP: " + ln.strip())
|
||||
|
||||
cnt= 0
|
||||
cnt = 0
|
||||
# now send file
|
||||
for line in f.splitlines(1):
|
||||
tn.write(line)
|
||||
if VERBOSE :
|
||||
if VERBOSE:
|
||||
cnt += len(line)
|
||||
print("SND: " + line.strip())
|
||||
print(str(cnt) + "/" + str(filesize) + "\r", end='')
|
||||
|
||||
tn.flush()
|
||||
|
||||
ln= tn.readline()
|
||||
if not ln.startswith("+") :
|
||||
ln = tn.readline()
|
||||
if not ln.startswith("+"):
|
||||
FreeCAD.Console.PrintError("Failed to save file: {}\n".format(ln))
|
||||
sys.exit();
|
||||
sys.exit()
|
||||
|
||||
if VERBOSE: print("RSP: " + ln.strip())
|
||||
if VERBOSE:
|
||||
print("RSP: " + ln.strip())
|
||||
|
||||
# exit
|
||||
tn.write("DONE\n")
|
||||
tn.flush()
|
||||
ln= tn.readline()
|
||||
tn.close()
|
||||
|
||||
FreeCAD.Console.PrintMessage("Upload complete\n")
|
||||
@@ -332,12 +336,13 @@ def linenumber():
|
||||
return "N" + str(LINENR) + " "
|
||||
return ""
|
||||
|
||||
|
||||
def parse(pathobj):
|
||||
global PRECISION
|
||||
out = ""
|
||||
lastcommand = None
|
||||
global SPINDLE_SPEED
|
||||
precision_string = '.' + str(PRECISION) +'f'
|
||||
precision_string = '.' + str(PRECISION) + 'f'
|
||||
|
||||
# params = ['X','Y','Z','A','B','I','J','K','F','S'] #This list control
|
||||
# the order of parameters
|
||||
@@ -373,10 +378,10 @@ def parse(pathobj):
|
||||
for param in params:
|
||||
if param in c.Parameters:
|
||||
if param == 'F':
|
||||
if c.Name not in ["G0", "G00"]: #linuxcnc doesn't use rapid speeds
|
||||
if c.Name not in ["G0", "G00"]: # linuxcnc doesn't use rapid speeds
|
||||
speed = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity)
|
||||
outstring.append(
|
||||
param + format(float(speed.getValueAs(UNIT_SPEED_FORMAT)), precision_string ) )
|
||||
param + format(float(speed.getValueAs(UNIT_SPEED_FORMAT)), precision_string))
|
||||
elif param == 'T':
|
||||
outstring.append(param + str(c.Parameters['T']))
|
||||
elif param == 'S':
|
||||
@@ -385,8 +390,7 @@ def parse(pathobj):
|
||||
else:
|
||||
pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length)
|
||||
outstring.append(
|
||||
param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string) )
|
||||
#param + format(c.Parameters[param], precision_string))
|
||||
param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string))
|
||||
if command in ['G1', 'G01', 'G2', 'G02', 'G3', 'G03']:
|
||||
outstring.append('S' + str(SPINDLE_SPEED))
|
||||
|
||||
@@ -413,7 +417,7 @@ def parse(pathobj):
|
||||
|
||||
# append the line to the final output
|
||||
for w in outstring:
|
||||
out += w + COMMAND_SPACE
|
||||
out += w + COMMAND_SPACE
|
||||
out = out.strip() + "\n"
|
||||
|
||||
return out
|
||||
|
||||
Reference in New Issue
Block a user