Merge pull request #2245 from sliptonic/lgtm

Path: lgtm Cleanup
This commit is contained in:
sliptonic
2019-06-10 16:43:38 -05:00
committed by GitHub
16 changed files with 280 additions and 419 deletions

View File

@@ -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):

View File

@@ -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()

View File

@@ -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)

View File

@@ -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)

View File

@@ -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):

View File

@@ -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):

View File

@@ -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):

View File

@@ -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

View File

@@ -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()

View File

@@ -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)

View File

@@ -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

View File

@@ -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.")

View File

@@ -142,7 +142,7 @@ def processArguments(argstring):
print ('here')
OUTPUT_DOUBLES = False
except:
except Exception:
return False
return True

View File

@@ -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

View File

@@ -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.")

View File

@@ -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