diff --git a/src/Mod/Path/Gui/Resources/panels/ContourEdit.ui b/src/Mod/Path/Gui/Resources/panels/ContourEdit.ui
index 5e18b18dec..2ce88aaeee 100644
--- a/src/Mod/Path/Gui/Resources/panels/ContourEdit.ui
+++ b/src/Mod/Path/Gui/Resources/panels/ContourEdit.ui
@@ -30,8 +30,8 @@
0
0
- 334
- 292
+ 108
+ 92
@@ -82,7 +82,7 @@
-
-
+
0.000000000000000
@@ -97,8 +97,8 @@
0
0
- 334
- 292
+ 141
+ 65
@@ -147,8 +147,8 @@
0
0
- 334
- 292
+ 336
+ 303
@@ -212,13 +212,6 @@
- -
-
-
- Use End Point
-
-
-
@@ -238,19 +231,9 @@
- -
-
-
- 0.000000000000000
-
-
- mm
-
-
-
-
-
+
0.000000000000000
@@ -258,13 +241,6 @@
- -
-
-
- Roll Radius
-
-
-
diff --git a/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui b/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui
index c9bb25b85e..254d763508 100644
--- a/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui
+++ b/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui
@@ -6,8 +6,8 @@
0
0
- 323
- 482
+ 379
+ 523
@@ -33,7 +33,7 @@
0
0
- 305
+ 361
277
@@ -111,8 +111,8 @@
0
0
- 305
- 124
+ 361
+ 130
@@ -192,8 +192,8 @@
0
0
- 305
- 109
+ 361
+ 130
@@ -242,8 +242,8 @@
0
0
- 289
- 297
+ 361
+ 329
@@ -254,94 +254,6 @@
Operation
- -
-
-
- QFrame::StyledPanel
-
-
- QFrame::Raised
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Use ZigZag
-
-
-
- -
-
-
- ZigZag Unidirectional
-
-
-
- -
-
-
- ZigZag Angle
-
-
-
-
-
-
- -
-
-
-
-
-
-
- Cut Mode
-
-
-
- -
-
-
- Boundary Shape
-
-
-
- -
-
-
-
-
- Perimeter
-
-
- -
-
- Boundbox
-
-
-
-
- -
-
-
-
-
- Climb
-
-
- -
-
- Conventional
-
-
-
-
-
-
-
-
@@ -410,6 +322,114 @@
+ -
+
+
+
-
+
+
+ Cut Mode
+
+
+
+ -
+
+
-
+
+ Climb
+
+
+ -
+
+ Conventional
+
+
+
+
+ -
+
+
+ Boundary Shape
+
+
+
+ -
+
+
-
+
+ Perimeter
+
+
+ -
+
+ Boundbox
+
+
+
+
+ -
+
+
-
+
+ ZigZag
+
+
+ -
+
+ Offset
+
+
+ -
+
+ Spiral
+
+
+ -
+
+ ZigZagOffset
+
+
+ -
+
+ Line
+
+
+ -
+
+ Grid
+
+
+ -
+
+ Triangle
+
+
+
+
+ -
+
+
+ Pattern
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ ZigZag Angle
+
+
+
+
+
+
diff --git a/src/Mod/Path/Gui/Resources/panels/ToolControl.ui b/src/Mod/Path/Gui/Resources/panels/ToolControl.ui
index 857f71a3d7..fd50c4536c 100644
--- a/src/Mod/Path/Gui/Resources/panels/ToolControl.ui
+++ b/src/Mod/Path/Gui/Resources/panels/ToolControl.ui
@@ -166,13 +166,6 @@
QFormLayout::AllNonFixedFieldsGrow
- -
-
-
- 99999
-
-
-
-
-
@@ -187,6 +180,9 @@
+ -
+
+
diff --git a/src/Mod/Path/InitGui.py b/src/Mod/Path/InitGui.py
index d375ecff65..f60387bb5c 100644
--- a/src/Mod/Path/InitGui.py
+++ b/src/Mod/Path/InitGui.py
@@ -140,7 +140,7 @@ class PathWorkbench (Workbench):
if "Profile" or "Contour" in FreeCADGui.Selection.getSelection()[0].Name:
#self.appendContextMenu("", ["Add_Tag"])
self.appendContextMenu("", ["Set_StartPoint"])
- self.appendContextMenu("", ["Set_EndPoint"])
+ #self.appendContextMenu("", ["Set_EndPoint"])
if "Remote" in FreeCADGui.Selection.getSelection()[0].Name:
self.appendContextMenu("", ["Refresh_Path"])
diff --git a/src/Mod/Path/PathScripts/PathContour.py b/src/Mod/Path/PathScripts/PathContour.py
index e6000e25f9..b9c9b5a963 100644
--- a/src/Mod/Path/PathScripts/PathContour.py
+++ b/src/Mod/Path/PathScripts/PathContour.py
@@ -23,18 +23,20 @@
# ***************************************************************************
import FreeCAD
-from FreeCAD import Vector
import Path
import PathScripts.PathLog as PathLog
from PathScripts import PathUtils
from PathScripts.PathUtils import depth_params
from PySide import QtCore
-import TechDraw
import ArchPanel
+import Part
+from PathScripts.PathUtils import waiting_effects
+from PathScripts.PathUtils import makeWorkplane
LOG_MODULE = 'PathContour'
PathLog.setLevel(PathLog.Level.INFO, LOG_MODULE)
#PathLog.trackModule('PathContour')
+FreeCAD.setLogLevel('Path.Area', 0)
if FreeCAD.GuiUp:
import FreeCADGui
@@ -78,15 +80,6 @@ class ObjectContour:
# Start Point Properties
obj.addProperty("App::PropertyVector", "StartPoint", "Start Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "The start point of this path"))
- obj.addProperty("App::PropertyBool", "UseStartPoint", "Start Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "make True, if specifying a Start Point"))
- obj.addProperty("App::PropertyLength", "ExtendAtStart", "Start Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "extra length of tool path before start of part edge"))
- obj.addProperty("App::PropertyLength", "LeadInLineLen", "Start Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "length of straight segment of toolpath that comes in at angle to first part edge"))
-
- # End Point Properties
- obj.addProperty("App::PropertyBool", "UseEndPoint", "End Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "make True, if specifying an End Point"))
- obj.addProperty("App::PropertyLength", "ExtendAtEnd", "End Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "extra length of tool path after end of part edge"))
- obj.addProperty("App::PropertyLength", "LeadOutLineLen", "End Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "length of straight segment of toolpath that comes in at angle to last part edge"))
- obj.addProperty("App::PropertyVector", "EndPoint", "End Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "The end point of this path"))
# Contour Properties
obj.addProperty("App::PropertyEnumeration", "Direction", "Contour", QtCore.QT_TRANSLATE_NOOP("App::Property", "The direction that the toolpath should go around the part ClockWise CW or CounterClockWise CCW"))
@@ -96,10 +89,10 @@ class ObjectContour:
obj.Side = ['Left', 'Right', 'On'] # side of profile that cutter is on in relation to direction of profile
obj.setEditorMode('Side', 2) # hide
- obj.addProperty("App::PropertyDistance", "RollRadius", "Contour", QtCore.QT_TRANSLATE_NOOP("App::Property", "Radius at start and end"))
obj.addProperty("App::PropertyDistance", "OffsetExtra", "Contour", QtCore.QT_TRANSLATE_NOOP("App::Property", "Extra value to stay away from final Contour- good for roughing toolpath"))
obj.Proxy = self
+ self.endVector = None
def __getstate__(self):
return None
@@ -109,6 +102,8 @@ class ObjectContour:
def onChanged(self, obj, prop):
pass
+ # if prop in ['ClearanceHeight', 'StartPoint']:
+ # obj.StartPoint.z = obj.ClearanceHeight.Value
def setDepths(proxy, obj):
PathLog.track()
@@ -131,65 +126,67 @@ class ObjectContour:
obj.ClearanceHeight = 10.0
obj.SafeHeight = 8.0
- def _buildPathLibarea(self, obj, edgelist):
- import PathScripts.PathKurveUtils as PathKurveUtils
+ @waiting_effects
+ def _buildPathArea(self, obj, baseobject, start=None):
PathLog.track()
- # import math
- # import area
- output = ""
- if obj.Comment != "":
- output += '(' + str(obj.Comment)+')\n'
+ profile = Path.Area()
+ profile.setPlane(makeWorkplane(baseobject))
+ profile.add(baseobject)
- if obj.StartPoint and obj.UseStartPoint:
- startpoint = obj.StartPoint
- else:
- startpoint = None
-
- if obj.EndPoint and obj.UseEndPoint:
- endpoint = obj.EndPoint
- else:
- endpoint = None
-
- PathKurveUtils.output('mem')
- PathKurveUtils.feedrate_hv(self.horizFeed, self.vertFeed)
-
- output = ""
- output += "G0 Z" + str(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n"
- curve = PathKurveUtils.makeAreaCurve(edgelist, obj.Direction, startpoint, endpoint)
-
- roll_radius = 2.0
- extend_at_start = 0.0
- extend_at_end = 0.0
- lead_in_line_len = 0.0
- lead_out_line_len = 0.0
+ profileparams = {'Fill': 0,
+ 'Coplanar': 0}
if obj.UseComp is False:
- obj.Side = 'On'
+ profileparams['Offset'] = 0.0
else:
- if obj.Direction == 'CW':
- obj.Side = 'Left'
- else:
- obj.Side = 'Right'
+ profileparams['Offset'] = self.radius+obj.OffsetExtra.Value
depthparams = depth_params(
- obj.ClearanceHeight.Value,
- obj.SafeHeight.Value, obj.StartDepth.Value, obj.StepDown.Value, 0.0,
- obj.FinalDepth.Value, None)
+ clearance_height=obj.ClearanceHeight.Value,
+ rapid_safety_space=obj.SafeHeight.Value,
+ start_depth=obj.StartDepth.Value,
+ step_down=obj.StepDown.Value,
+ z_finish_step=0.0,
+ final_depth=obj.FinalDepth.Value,
+ user_depths=None)
- PathKurveUtils.profile2(
- curve, obj.Side, self.radius, self.vertFeed, self.horizFeed,
- self.vertRapid, self.horizRapid, obj.OffsetExtra.Value, roll_radius,
- None, None, depthparams, extend_at_start, extend_at_end,
- lead_in_line_len, lead_out_line_len)
+ PathLog.debug('depths: {}'.format(depthparams.get_depths()))
+ profile.setParams(**profileparams)
+ PathLog.debug("Contour with params: {}".format(profile.getParams()))
- output += PathKurveUtils.retrieve_gcode()
- return output
+ sections = profile.makeSections(mode=0, project=True, heights=depthparams.get_depths())
+ shapelist = [sec.getShape() for sec in sections]
+
+ params = {'shapes': shapelist,
+ 'feedrate': self.horizFeed,
+ 'feedrate_v': self.vertFeed,
+ 'verbose': True,
+ 'resume_height': obj.StepDown.Value,
+ 'retraction': obj.ClearanceHeight.Value,
+ 'return_end': True}
+
+ if obj.Direction == 'CCW':
+ params['orientation'] = 1
+ else:
+ params['orientation'] = 0
+
+ if self.endVector is not None:
+ params['start'] = self.endVector
+ elif start is not None:
+ params['start'] = start
+
+ (pp, end_vector) = Path.fromShapes(**params)
+ PathLog.debug("Generating Path with params: {}".format(params))
+ PathLog.debug('pp: {}, end vector: {}'.format(pp, end_vector))
+ self.endVector = end_vector
+
+ return pp
def execute(self, obj):
PathLog.track()
- import Part # math #DraftGeomUtils
- output = ""
+ self.endVector = None
+ commandlist = []
toolLoad = obj.ToolController
if toolLoad is None or toolLoad.ToolNumber == 0:
@@ -200,20 +197,19 @@ class ObjectContour:
self.horizFeed = toolLoad.HorizFeed.Value
self.vertRapid = toolLoad.VertRapid.Value
self.horizRapid = toolLoad.HorizRapid.Value
- tool = toolLoad.Proxy.getTool(toolLoad) #PathUtils.getTool(obj, toolLoad.ToolNumber)
+ tool = toolLoad.Proxy.getTool(toolLoad)
if not tool or tool.Diameter == 0:
FreeCAD.Console.PrintError("No Tool found or diameter is zero. We need a tool to build a Path.")
return
-
- #self.radius = 0.25
else:
self.radius = tool.Diameter/2
- output += "(" + obj.Label + ")"
- if not obj.UseComp:
- output += "(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")"
+ commandlist.append(Path.Command("(" + obj.Label + ")"))
+
+ if obj.UseComp:
+ commandlist.append(Path.Command("(Compensated Tool Path. Diameter: " + str(self.radius * 2) + ")"))
else:
- output += "(Uncompensated Tool Path)"
+ commandlist.append(Path.Command("(Uncompensated Tool Path)"))
parentJob = PathUtils.findParentJob(obj)
if parentJob is None:
@@ -222,33 +218,33 @@ class ObjectContour:
if baseobject is None:
return
+ # Let's always start by rapid to clearance...just for safety
+ commandlist.append(Path.Command("G0", {"Z": obj.ClearanceHeight.Value}))
+
if hasattr(baseobject, "Proxy"):
if isinstance(baseobject.Proxy, ArchPanel.PanelSheet): # process the sheet
baseobject.Proxy.execute(baseobject)
- for subobj in baseobject.Group: # process the group of panels
- if isinstance(subobj.Proxy, ArchPanel.PanelCut):
- shapes = baseobject.Proxy.getOutlines(baseobject, transform=True)
- for shape in shapes:
- for wire in shape.Wires:
- edgelist = wire.Edges
- edgelist = Part.__sortEdges__(edgelist)
- PathLog.debug("Processing panel perimeter. edges found: {}".format(len(edgelist))) # subobj.Proxy.execute(subobj)
- try:
- output += self._buildPathLibarea(obj, edgelist)
- except:
- FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.")
+ shapes = baseobject.Proxy.getOutlines(baseobject, transform=True)
+ for shape in shapes:
+ f = Part.makeFace([shape], 'Part::FaceMakerSimple')
+ thickness = baseobject.Group[0].Source.Thickness
+ contourshape = f.extrude(FreeCAD.Vector(0, 0, thickness))
+ try:
+ commandlist.extend(self._buildPathArea(obj, contourshape, start=obj.StartPoint).Commands)
+ except Exception as e:
+ print(e)
+ FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.")
else:
- contourwire = TechDraw.findShapeOutline(baseobject.Shape, 1, Vector(0, 0, 1))
-
- edgelist = contourwire.Edges
- edgelist = Part.__sortEdges__(edgelist)
-
+ bb = baseobject.Shape.BoundBox
+ env = PathUtils.getEnvelope(baseobject.Shape, bb.ZLength + (obj.StartDepth.Value-bb.ZMax))
try:
- output += self._buildPathLibarea(obj, edgelist)
- except:
+ commandlist.extend(self._buildPathArea(obj, env, start=obj.StartPoint).Commands)
+ except Exception as e:
+ print(e)
FreeCAD.Console.PrintError("Something unexpected happened. Unable to generate a contour path. Check project and tool config.")
+
if obj.Active:
- path = Path.Path(output)
+ path = Path.Path(commandlist)
obj.Path = path
if obj.ViewObject:
obj.ViewObject.Visibility = True
@@ -298,28 +294,9 @@ class _CommandSetStartPoint:
obj = FreeCADGui.Selection.getSelection()[0]
obj.StartPoint.x = point.x
obj.StartPoint.y = point.y
+ obj.StartPoint.z = obj.ClearanceHeight.Value
def Activated(self):
-
- FreeCADGui.Snapper.getPoint(callback=self.setpoint)
-
-
-class _CommandSetEndPoint:
- def GetResources(self):
- return {'Pixmap': 'Path-EndPoint',
- 'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Contour", "Pick End Point"),
- 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Contour", "Pick End Point")}
-
- def IsActive(self):
- return FreeCAD.ActiveDocument is not None
-
- def setpoint(self, point, o):
- obj = FreeCADGui.Selection.getSelection()[0]
- obj.EndPoint.x = point.x
- obj.EndPoint.y = point.y
-
- def Activated(self):
-
FreeCADGui.Snapper.getPoint(callback=self.setpoint)
@@ -402,14 +379,10 @@ class TaskPanel:
self.obj.StepDown = FreeCAD.Units.Quantity(self.form.stepDown.text()).Value
if hasattr(self.obj, "OffsetExtra"):
self.obj.OffsetExtra = FreeCAD.Units.Quantity(self.form.extraOffset.text()).Value
- if hasattr(self.obj, "RollRadius"):
- self.obj.RollRadius = FreeCAD.Units.Quantity(self.form.rollRadius.text()).Value
if hasattr(self.obj, "UseComp"):
self.obj.UseComp = self.form.useCompensation.isChecked()
- if hasattr(self.obj, "UseStartPoint"):
- self.obj.UseStartPoint = self.form.useStartPoint.isChecked()
- if hasattr(self.obj, "UseEndPoint"):
- self.obj.UseEndPoint = self.form.useEndPoint.isChecked()
+ # if hasattr(self.obj, "UseStartPoint"):
+ # self.obj.UseStartPoint = self.form.useStartPoint.isChecked()
if hasattr(self.obj, "Direction"):
self.obj.Direction = str(self.form.direction.currentText())
if hasattr(self.obj, "ToolController"):
@@ -425,10 +398,8 @@ class TaskPanel:
self.form.clearanceHeight.setText(FreeCAD.Units.Quantity(self.obj.ClearanceHeight.Value, FreeCAD.Units.Length).UserString)
self.form.stepDown.setText(FreeCAD.Units.Quantity(self.obj.StepDown.Value, FreeCAD.Units.Length).UserString)
self.form.extraOffset.setText(FreeCAD.Units.Quantity(self.obj.OffsetExtra.Value, FreeCAD.Units.Length).UserString)
- self.form.rollRadius.setText(FreeCAD.Units.Quantity(self.obj.RollRadius.Value, FreeCAD.Units.Length).UserString)
self.form.useCompensation.setChecked(self.obj.UseComp)
- self.form.useStartPoint.setChecked(self.obj.UseStartPoint)
- self.form.useEndPoint.setChecked(self.obj.UseEndPoint)
+ # self.form.useStartPoint.setChecked(self.obj.UseStartPoint)
index = self.form.direction.findText(
self.obj.Direction, QtCore.Qt.MatchFixedString)
@@ -481,9 +452,7 @@ class TaskPanel:
self.form.uiToolController.currentIndexChanged.connect(self.getFields)
self.form.useCompensation.clicked.connect(self.getFields)
self.form.useStartPoint.clicked.connect(self.getFields)
- self.form.useEndPoint.clicked.connect(self.getFields)
self.form.extraOffset.editingFinished.connect(self.getFields)
- self.form.rollRadius.editingFinished.connect(self.getFields)
self.setFields()
@@ -506,6 +475,5 @@ if FreeCAD.GuiUp:
# register the FreeCAD command
FreeCADGui.addCommand('Path_Contour', CommandPathContour())
FreeCADGui.addCommand('Set_StartPoint', _CommandSetStartPoint())
- FreeCADGui.addCommand('Set_EndPoint', _CommandSetEndPoint())
FreeCAD.Console.PrintLog("Loading PathContour... done\n")
diff --git a/src/Mod/Path/PathScripts/PathDrilling.py b/src/Mod/Path/PathScripts/PathDrilling.py
index 8655906f7c..df58190634 100644
--- a/src/Mod/Path/PathScripts/PathDrilling.py
+++ b/src/Mod/Path/PathScripts/PathDrilling.py
@@ -25,14 +25,11 @@
from __future__ import print_function
import sys
import FreeCAD
-# from FreeCAD import Vector
import Path
import PathScripts.PathLog as PathLog
-# import Part
from PySide import QtCore, QtGui
from PathScripts import PathUtils
-from PathScripts.PathUtils import fmt
-# from math import pi
+from PathScripts.PathUtils import fmt, waiting_effects
import ArchPanel
@@ -105,9 +102,8 @@ class ObjectDrilling:
def onChanged(self, obj, prop):
pass
- # if prop == "UserLabel":
- # obj.Label = obj.UserLabel + " :" + obj.ToolDescription
+ @waiting_effects
def execute(self, obj):
PathLog.track()
output = ""
diff --git a/src/Mod/Path/PathScripts/PathEngrave.py b/src/Mod/Path/PathScripts/PathEngrave.py
index 45605e2605..982992aad6 100644
--- a/src/Mod/Path/PathScripts/PathEngrave.py
+++ b/src/Mod/Path/PathScripts/PathEngrave.py
@@ -118,7 +118,8 @@ class ObjectPathEngrave:
return
try:
if baseobject.isDerivedFrom('Sketcher::SketchObject') or \
- baseobject.isDerivedFrom('Part::Part2DObject'):
+ baseobject.isDerivedFrom('Part::Part2DObject') or \
+ hasattr(baseobject, 'ArrayType'):
output += "G0 Z" + PathUtils.fmt(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.vertRapid) + "\n"
@@ -181,7 +182,8 @@ class ObjectPathEngrave:
if not last:
# we set the first move to our first point
last = edge.Vertexes[0].Point
- output += "G0" + " X" + PathUtils.fmt(last.x) + " Y" + PathUtils.fmt(last.y) + " Z" + PathUtils.fmt(obj.SafeHeight.Value) + "F " + PathUtils.fmt(self.horizRapid) # Rapid sto starting position
+ output += "G0" + " X" + PathUtils.fmt(last.x) + " Y" + PathUtils.fmt(last.y) + " Z" + PathUtils.fmt(obj.ClearanceHeight.Value) + "F " + PathUtils.fmt(self.horizRapid) # Rapid to starting position
+ output += "G0" + " X" + PathUtils.fmt(last.x) + " Y" + PathUtils.fmt(last.y) + " Z" + PathUtils.fmt(obj.SafeHeight.Value) + "F " + PathUtils.fmt(self.horizRapid) # Rapid to safe height
output += "G1" + " X" + PathUtils.fmt(last.x) + " Y" + PathUtils.fmt(last.y) + " Z" + PathUtils.fmt(obj.FinalDepth.Value) + "F " + PathUtils.fmt(self.vertFeed) + "\n" # Vertical feed to depth
if isinstance(edge.Curve, Part.Circle):
point = edge.Vertexes[-1].Point
@@ -208,7 +210,7 @@ class ObjectPathEngrave:
output += " F " + PathUtils.fmt(self.horizFeed)
output += "\n"
last = point
- output += "G0 Z " + PathUtils.fmt(obj.SafeHeight.Value)
+ output += "G0 Z " + PathUtils.fmt(obj.ClearanceHeight.Value)
return output
diff --git a/src/Mod/Path/PathScripts/PathMillFace.py b/src/Mod/Path/PathScripts/PathMillFace.py
index 7487b1b46a..5175d71741 100644
--- a/src/Mod/Path/PathScripts/PathMillFace.py
+++ b/src/Mod/Path/PathScripts/PathMillFace.py
@@ -28,15 +28,15 @@ import Path
from PySide import QtCore, QtGui
from PathScripts import PathUtils
import Part
-import PathScripts.PathKurveUtils
-import area
-import TechDraw
from FreeCAD import Vector
import PathScripts.PathLog as PathLog
+from PathScripts.PathUtils import waiting_effects
+from PathScripts.PathUtils import makeWorkplane
LOG_MODULE = 'PathMillFace'
PathLog.setLevel(PathLog.Level.INFO, LOG_MODULE)
PathLog.trackModule()
+FreeCAD.setLogLevel('Path.Area', 0)
FreeCADGui = None
if FreeCAD.GuiUp:
@@ -88,6 +88,8 @@ class ObjectFace:
obj.addProperty("App::PropertyFloat", "ZigZagAngle", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property", "Angle of the zigzag pattern"))
obj.addProperty("App::PropertyEnumeration", "BoundaryShape", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property", "Shape to use for calculating Boundary"))
obj.BoundaryShape = ['Perimeter', 'Boundbox']
+ obj.addProperty("App::PropertyEnumeration", "OffsetPattern", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property", "clearing pattern to use"))
+ obj.OffsetPattern = ['ZigZag', 'Offset', 'Spiral', 'ZigZagOffset', 'Line', 'Grid', 'Triangle']
# Start Point Properties
obj.addProperty("App::PropertyVector", "StartPoint", "Start Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "The start point of this path"))
@@ -159,60 +161,63 @@ class ObjectFace:
return self.getStock(o)
return None
- def buildpathlibarea(self, obj, a):
- """Build the face path using libarea algorithm"""
-
- import PathScripts.PathAreaUtils as PathAreaUtils
+ @waiting_effects
+ def _buildPathArea(self, obj, baseobject):
+ """build the face path using PathArea"""
from PathScripts.PathUtils import depth_params
PathLog.track()
- for p in a.getCurves():
- PathLog.debug(p.text())
+ boundary = Path.Area()
+ boundary.setPlane(makeWorkplane(baseobject))
+ boundary.add(baseobject)
- FreeCAD.Console.PrintMessage(translate("PathMillFace", "Generating toolpath with libarea offsets.\n"))
+ stepover = (self.radius * 2) * (float(obj.StepOver)/100)
+
+ pocketparams = {'Fill': 0,
+ 'Coplanar': 0,
+ 'PocketMode': 1,
+ 'SectionCount': -1,
+ 'Angle': obj.ZigZagAngle,
+ 'FromCenter': (obj.StartAt == "Center"),
+ 'PocketStepover': stepover,
+ 'PocketExtraOffset': obj.PassExtension.Value}
+
+ Pattern = ['ZigZag', 'Offset', 'Spiral', 'ZigZagOffset', 'Line', 'Grid', 'Triangle']
+ pocketparams['PocketMode'] = Pattern.index(obj.OffsetPattern) + 1
depthparams = depth_params(
- obj.ClearanceHeight.Value,
- obj.SafeHeight.Value,
- obj.StartDepth.Value,
- obj.StepDown,
- obj.FinishDepth.Value,
- obj.FinalDepth.Value)
+ clearance_height=obj.ClearanceHeight.Value,
+ rapid_safety_space=obj.SafeHeight.Value,
+ start_depth=obj.StartDepth.Value,
+ step_down=obj.StepDown,
+ z_finish_step=obj.FinishDepth.Value,
+ final_depth=obj.FinalDepth.Value,
+ user_depths=None)
- extraoffset = - obj.PassExtension.Value
- stepover = (self.radius * 2) * (float(obj.StepOver)/100)
- use_zig_zag = obj.UseZigZag
- zig_angle = obj.ZigZagAngle
- from_center = (obj.StartAt == "Center")
- keep_tool_down = obj.KeepToolDown
- zig_unidirectional = obj.ZigUnidirectional
- start_point = None
- cut_mode = obj.CutMode
+ boundary.setParams(**pocketparams)
+ sections = boundary.makeSections(mode=0, project=False, heights=depthparams.get_depths())
+ shapelist = [sec.getShape() for sec in sections]
- PathAreaUtils.flush_nc()
- PathAreaUtils.output('mem')
- PathAreaUtils.feedrate_hv(self.horizFeed, self.vertFeed)
- if obj.UseStartPoint:
- start_point = (obj.StartPoint.x, obj.StartPoint.y)
+ params = {'shapes': shapelist,
+ 'feedrate': self.horizFeed,
+ 'feedrate_v': self.vertFeed,
+ 'verbose': True,
+ 'resume_height': obj.StepDown,
+ 'retraction': obj.ClearanceHeight.Value}
- PathAreaUtils.pocket(
- a,
- self.radius,
- extraoffset,
- stepover,
- depthparams,
- from_center,
- keep_tool_down,
- use_zig_zag,
- zig_angle,
- zig_unidirectional,
- start_point,
- cut_mode)
- return PathAreaUtils.retrieve_gcode()
+ # if obj.Direction == 'CCW':
+ # params['orientation'] = 1
+ # else:
+ # params['orientation'] = 0
+
+ PathLog.debug("Generating Path with params: {}".format(params))
+ pp = Path.fromShapes(**params)
+
+ return pp
def execute(self, obj):
PathLog.track()
- output = ""
+ commandlist = []
toolLoad = obj.ToolController
@@ -232,9 +237,7 @@ class ObjectFace:
else:
self.radius = tool.Diameter/2
- # Build preliminary comments
- output = ""
- output += "(" + obj.Label + ")"
+ commandlist.append(Path.Command("(" + obj.Label + ")"))
# Facing is done either against base objects
if obj.Base:
@@ -266,36 +269,28 @@ class ObjectFace:
# if user wants the boundbox, calculate that
PathLog.info("Boundary Shape: {}".format(obj.BoundaryShape))
+ bb = planeshape.BoundBox
if obj.BoundaryShape == 'Boundbox':
- bb = planeshape.BoundBox
bbperim = Part.makeBox(bb.XLength, bb.YLength, 1, Vector(bb.XMin, bb.YMin, bb.ZMin), Vector(0, 0, 1))
- contourwire = TechDraw.findShapeOutline(bbperim, 1, Vector(0, 0, 1))
+ env = PathUtils.getEnvelope(bbperim, bb.ZLength + (obj.StartDepth.Value-bb.ZMax))
else:
- contourwire = TechDraw.findShapeOutline(planeshape, 1, Vector(0, 0, 1))
+ env = PathUtils.getEnvelope(planeshape, bb.ZLength + (obj.StartDepth.Value-bb.ZMax))
- # pocket = Path.Area(PocketMode=4,SectionCount=-1,SectionMode=1,Stepdown=0.499)
- # pocket.setParams(PocketExtraOffset = obj.PassExtension.Value, ToolRadius = self.radius)
- # pocket.add(planeshape, op=1)
- # #Part.show(contourwire)
- # path = Path.fromShapes(pocket.getShape())
+ try:
+ commandlist.extend(self._buildPathArea(obj, env).Commands)
+ except Exception as e:
+ print(e)
+ FreeCAD.Console.PrintWarning(translate("PathMillFace", "The selected settings did not produce a valid path.\n"))
- edgelist = contourwire.Edges
- edgelist = Part.__sortEdges__(edgelist)
-
- # use libarea to build the pattern
- a = area.Area()
- c = PathScripts.PathKurveUtils.makeAreaCurve(edgelist, 'CW')
- PathLog.debug(c.text())
- a.append(c)
- a.Reorder()
- output += self.buildpathlibarea(obj, a)
-
- path = Path.Path(output)
- if len(path.Commands) == 0:
- FreeCAD.Console.PrintMessage(translate("PathMillFace", "The selected settings did not produce a valid path.\n"))
-
- obj.Path = path
- obj.ViewObject.Visibility = True
+ if obj.Active:
+ path = Path.Path(commandlist)
+ obj.Path = path
+ if obj.ViewObject:
+ obj.ViewObject.Visibility = True
+ else:
+ path = Path.Path("(inactive operation)")
+ obj.Path = path
+ obj.ViewObject.Visibility = False
class _CommandSetFaceStartPoint:
@@ -375,8 +370,7 @@ class CommandPathMillFace:
FreeCADGui.doCommand('obj.StepDown = 1.0')
FreeCADGui.doCommand('obj.StartDepth = ' + str(ztop + 1))
FreeCADGui.doCommand('obj.FinalDepth =' + str(ztop))
- FreeCADGui.doCommand('obj.ZigZagAngle = 0.0')
- FreeCADGui.doCommand('obj.UseZigZag = True')
+ FreeCADGui.doCommand('obj.ZigZagAngle = 45.0')
FreeCADGui.doCommand('PathScripts.PathUtils.addToJob(obj)')
FreeCADGui.doCommand('obj.ToolController = PathScripts.PathUtils.findToolController(obj)')
snippet = '''
@@ -436,16 +430,15 @@ class TaskPanel:
self.obj.PassExtension = FreeCAD.Units.Quantity(self.form.extraOffset.text()).Value
if hasattr(self.obj, "CutMode"):
self.obj.CutMode = str(self.form.cutMode.currentText())
- if hasattr(self.obj, "UseZigZag"):
- self.obj.UseZigZag = self.form.useZigZag.isChecked()
- if hasattr(self.obj, "ZigUnidirectional"):
- self.obj.ZigUnidirectional = self.form.zigZagUnidirectional.isChecked()
if hasattr(self.obj, "ZigZagAngle"):
self.obj.ZigZagAngle = FreeCAD.Units.Quantity(self.form.zigZagAngle.text()).Value
if hasattr(self.obj, "StepOver"):
self.obj.StepOver = self.form.stepOverPercent.value()
if hasattr(self.obj, "BoundaryShape"):
self.obj.BoundaryShape = str(self.form.boundaryShape.currentText())
+ if hasattr(self.obj, "OffsetPattern"):
+ self.obj.OffsetPattern = str(self.form.offsetpattern.currentText())
+
if hasattr(self.obj, "ToolController"):
tc = PathUtils.findToolController(self.obj, self.form.uiToolController.currentText())
self.obj.ToolController = tc
@@ -460,8 +453,6 @@ class TaskPanel:
self.form.clearanceHeight.setText(FreeCAD.Units.Quantity(self.obj.ClearanceHeight.Value, FreeCAD.Units.Length).UserString)
self.form.stepOverPercent.setValue(self.obj.StepOver)
- self.form.useZigZag.setChecked(self.obj.UseZigZag)
- self.form.zigZagUnidirectional.setChecked(self.obj.ZigUnidirectional)
self.form.zigZagAngle.setValue(FreeCAD.Units.Quantity(self.obj.ZigZagAngle, FreeCAD.Units.Angle))
self.form.extraOffset.setValue(self.obj.PassExtension.Value)
@@ -480,6 +471,13 @@ class TaskPanel:
self.form.boundaryShape.setCurrentIndex(index)
self.form.boundaryShape.blockSignals(False)
+ index = self.form.offsetpattern.findText(
+ self.obj.OffsetPattern, QtCore.Qt.MatchFixedString)
+ if index >= 0:
+ self.form.offsetpattern.blockSignals(True)
+ self.form.offsetpattern.setCurrentIndex(index)
+ self.form.offsetpattern.blockSignals(False)
+
for i in self.obj.Base:
for sub in i[1]:
self.form.baseList.addItem(i[0].Name + "." + sub)
@@ -616,8 +614,7 @@ class TaskPanel:
self.form.extraOffset.editingFinished.connect(self.getFields)
self.form.boundaryShape.currentIndexChanged.connect(self.getFields)
self.form.stepOverPercent.editingFinished.connect(self.getFields)
- self.form.useZigZag.clicked.connect(self.getFields)
- self.form.zigZagUnidirectional.clicked.connect(self.getFields)
+ self.form.offsetpattern.currentIndexChanged.connect(self.getFields)
self.form.zigZagAngle.editingFinished.connect(self.getFields)
self.form.uiToolController.currentIndexChanged.connect(self.getFields)
diff --git a/src/Mod/Path/PathScripts/PathPocket.py b/src/Mod/Path/PathScripts/PathPocket.py
index c21bfcc8e7..f4a4c8a8fc 100644
--- a/src/Mod/Path/PathScripts/PathPocket.py
+++ b/src/Mod/Path/PathScripts/PathPocket.py
@@ -109,9 +109,6 @@ class ObjectPocket:
obj.setEditorMode('RampAngle', 2) # make this hidden
obj.setEditorMode('RampSize', 2) # make this hidden
- if prop == "UserLabel":
- obj.Label = obj.UserLabel + " :" + obj.ToolDescription
-
def __getstate__(self):
return None
diff --git a/src/Mod/Path/PathScripts/PathProfileEdges.py b/src/Mod/Path/PathScripts/PathProfileEdges.py
index f2ae43cd6b..4f797bb3c6 100644
--- a/src/Mod/Path/PathScripts/PathProfileEdges.py
+++ b/src/Mod/Path/PathScripts/PathProfileEdges.py
@@ -28,6 +28,7 @@ from PathScripts import PathUtils
from PathScripts.PathUtils import depth_params
from DraftGeomUtils import findWires
import PathScripts.PathLog as PathLog
+from PathScripts.PathUtils import waiting_effects
"""Path Profile from Edges Object and Command"""
@@ -107,8 +108,7 @@ class ObjectProfile:
return None
def onChanged(self, obj, prop):
- if prop == "UserLabel":
- obj.Label = obj.UserLabel + " :" + obj.ToolDescription
+ pass
def addprofilebase(self, obj, ss, sub=""):
baselist = obj.Base
@@ -144,6 +144,7 @@ class ObjectProfile:
obj.Base = baselist
self.execute(obj)
+ @waiting_effects
def _buildPathLibarea(self, obj, edgelist):
import PathScripts.PathKurveUtils as PathKurveUtils
# import math
diff --git a/src/Mod/Path/PathScripts/PathSurface.py b/src/Mod/Path/PathScripts/PathSurface.py
index 5f5899ffe3..3851e0ab9a 100644
--- a/src/Mod/Path/PathScripts/PathSurface.py
+++ b/src/Mod/Path/PathScripts/PathSurface.py
@@ -27,6 +27,7 @@ import FreeCAD
import Path
from PathScripts import PathUtils
import PathScripts.PathLog as PathLog
+from PathScripts.PathUtils import waiting_effects
import sys
# xrange is not available in python3
@@ -129,8 +130,7 @@ class ObjectSurface:
return None
def onChanged(self, obj, prop):
- if prop == "UserLabel":
- obj.Label = obj.UserLabel + " :" + obj.ToolDescription
+ pass
def _waterline(self, obj, s, bb):
import ocl
@@ -262,6 +262,7 @@ class ObjectSurface:
return output
+ @waiting_effects
def execute(self, obj):
import MeshPart
FreeCAD.Console.PrintWarning(
diff --git a/src/Mod/Path/PathScripts/PathUtils.py b/src/Mod/Path/PathScripts/PathUtils.py
index fd6aab8be4..8dccbb0234 100644
--- a/src/Mod/Path/PathScripts/PathUtils.py
+++ b/src/Mod/Path/PathScripts/PathUtils.py
@@ -31,16 +31,38 @@ import PathScripts
from PathScripts import PathJob
import numpy
import PathLog
-#from math import pi
from FreeCAD import Vector
+import Path
+from PySide import QtCore
+from PySide import QtGui
LOG_MODULE = 'PathUtils'
PathLog.setLevel(PathLog.Level.INFO, LOG_MODULE)
-#PathLog.trackModule('PathUtils')
+# PathLog.trackModule('PathUtils')
+FreeCAD.setLogLevel('Path.Area', 0)
+
+
+def waiting_effects(function):
+ def new_function(*args, **kwargs):
+ if not FreeCAD.GuiUp:
+ return function(*args, **kwargs)
+ QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor)
+ res = None
+ try:
+ res = function(*args, **kwargs)
+ except Exception as e:
+ raise e
+ print("Error {}".format(e.args[0]))
+ finally:
+ QtGui.QApplication.restoreOverrideCursor()
+ return res
+ return new_function
+
def cleanedges(splines, precision):
'''cleanedges([splines],precision). Convert BSpline curves, Beziers, to arcs that can be used for cnc paths.
Returns Lines as is. Filters Circle and Arcs for over 180 degrees. Discretizes Ellipses. Ignores other geometry. '''
+ PathLog.track()
edges = []
for spline in splines:
if geomType(spline) == "BSplineCurve":
@@ -73,8 +95,11 @@ def cleanedges(splines, precision):
return edges
+
def curvetowire(obj, steps):
'''adapted from DraftGeomUtils, because the discretize function changed a bit '''
+
+ PathLog.track()
points = obj.copy().discretize(Distance=eval('steps'))
p0 = points[0]
edgelist = []
@@ -84,6 +109,7 @@ def curvetowire(obj, steps):
p0 = p
return edgelist
+
def isDrillable(obj, candidate, tooldiameter=None):
PathLog.track('obj: {} candidate: {} tooldiameter {}'.format(obj, candidate, tooldiameter))
drillable = False
@@ -98,13 +124,13 @@ def isDrillable(obj, candidate, tooldiameter=None):
v1 = edge.Vertexes[1].Point
if (v1.sub(v0).x == 0) and (v1.sub(v0).y == 0):
# vector of top center
- lsp = Vector(face.BoundBox.Center.x,face.BoundBox.Center.y, face.BoundBox.ZMax)
+ lsp = Vector(face.BoundBox.Center.x, face.BoundBox.Center.y, face.BoundBox.ZMax)
# vector of bottom center
- lep = Vector(face.BoundBox.Center.x,face.BoundBox.Center.y, face.BoundBox.ZMin)
+ lep = Vector(face.BoundBox.Center.x, face.BoundBox.Center.y, face.BoundBox.ZMin)
if obj.isInside(lsp, 0, False) or obj.isInside(lep, 0, False):
drillable = False
# eliminate elliptical holes
- elif not hasattr(face.Surface, "Radius"): #abs(face.BoundBox.XLength - face.BoundBox.YLength) > 0.05:
+ elif not hasattr(face.Surface, "Radius"):
drillable = False
else:
if tooldiameter is not None:
@@ -115,7 +141,7 @@ def isDrillable(obj, candidate, tooldiameter=None):
for edge in candidate.Edges:
if isinstance(edge.Curve, Part.Circle) and edge.isClosed():
PathLog.debug("candidate is a circle or ellipse")
- if not hasattr(edge.Curve, "Radius"): #bbdiff > 0.05:
+ if not hasattr(edge.Curve, "Radius"):
PathLog.debug("No radius. Ellipse.")
drillable = False
else:
@@ -127,13 +153,16 @@ def isDrillable(obj, candidate, tooldiameter=None):
PathLog.debug("candidate is drillable: {}".format(drillable))
return drillable
+
# fixme set at 4 decimal places for testing
def fmt(val): return format(val, '.4f')
+
def segments(poly):
''' A sequence of (x,y) numeric coordinates pairs '''
return zip(poly, poly[1:] + [poly[0]])
+
def loopdetect(obj, edge1, edge2):
'''
Returns a loop wire that includes the two edges.
@@ -148,17 +177,19 @@ def loopdetect(obj, edge1, edge2):
for wire in obj.Shape.Wires:
for e in wire.Edges:
if e.hashCode() == edge1.hashCode():
- candidates.append((wire.hashCode(),wire))
+ candidates.append((wire.hashCode(), wire))
if e.hashCode() == edge2.hashCode():
- candidates.append((wire.hashCode(),wire))
- loop = set([x for x in candidates if candidates.count(x) > 1]) #return the duplicate item
+ candidates.append((wire.hashCode(), wire))
+ loop = set([x for x in candidates if candidates.count(x) > 1]) # return the duplicate item
if len(loop) != 1:
return None
loopwire = next(x for x in loop)[1]
return loopwire
+
def filterArcs(arcEdge):
'''filterArcs(Edge) -used to split arcs that over 180 degrees. Returns list '''
+ PathLog.track()
s = arcEdge
if isinstance(s.Curve, Part.Circle):
splitlist = []
@@ -174,10 +205,10 @@ 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)
@@ -194,6 +225,40 @@ def filterArcs(arcEdge):
return splitlist
+def makeWorkplane(shape):
+ """
+ Creates a workplane circle at the ZMin level.
+ """
+ PathLog.track()
+ loc = FreeCAD.Vector(shape.BoundBox.Center.x,
+ shape.BoundBox.Center.y,
+ shape.BoundBox.ZMin)
+ c = Part.makeCircle(10, loc)
+ return c
+
+
+def getEnvelope(partshape, stockheight=None):
+ '''
+ getEnvelop(partshape, stockheight=None)
+ returns a shape corresponding to the partshape silhouette extruded to height.
+ if stockheight is given, the returned shape is extruded to that height otherwise the returned shape
+ is the height of the original shape boundbox
+ partshape = solid object
+ stockheight = float
+ '''
+ PathLog.track(partshape, stockheight)
+ area = Path.Area(Fill=1, Coplanar=0).add(partshape)
+ # loc = FreeCAD.Vector(partshape.BoundBox.Center.x,
+ # partshape.BoundBox.Center.y,
+ # partshape.BoundBox.ZMin)
+ area.setPlane(makeWorkplane(partshape))
+ sec = area.makeSections(heights=[1.0], project=True)[0].getShape()
+ if stockheight is not None:
+ return sec.extrude(FreeCAD.Vector(0, 0, stockheight))
+ else:
+ return sec.extrude(FreeCAD.Vector(0, 0, partshape.BoundBox.ZLength))
+
+
def reverseEdge(e):
if geomType(e) == "Circle":
arcstpt = e.valueAt(e.FirstParameter)
@@ -208,6 +273,7 @@ def reverseEdge(e):
return newedge
+
def changeTool(obj, job):
tlnum = 0
for p in job.Group:
@@ -223,6 +289,7 @@ def changeTool(obj, job):
if g == obj:
return tlnum
+
def getToolControllers(obj):
'''returns all the tool controllers'''
controllers = []
@@ -238,6 +305,7 @@ def getToolControllers(obj):
controllers.append(g)
return controllers
+
def findToolController(obj, name=None):
'''returns a tool controller with a given name.
If no name is specified, returns the first controller.
@@ -245,7 +313,7 @@ def findToolController(obj, name=None):
PathLog.track('name: {}'.format(name))
c = None
- #First check if a user has selected a tool controller in the tree. Return the first one and remove all from selection
+ # First check if a user has selected a tool controller in the tree. Return the first one and remove all from selection
for sel in FreeCADGui.Selection.getSelectionEx():
if hasattr(sel.Object, 'Proxy'):
if isinstance(sel.Object.Proxy, PathScripts.PathLoadTool.LoadTool):
@@ -260,16 +328,16 @@ def findToolController(obj, name=None):
if len(controllers) == 0:
return None
- #If there's only one in the job, use it.
+ # If there's only one in the job, use it.
if len(controllers) == 1:
if name is None or name == controllers[0].Label:
tc = controllers[0]
else:
tc = None
- elif name is not None: #More than one, make the user choose.
+ elif name is not None: # More than one, make the user choose.
tc = [i for i in controllers if i.Label == name][0]
else:
- #form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/DlgTCChooser.ui")
+ # form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/DlgTCChooser.ui")
form = FreeCADGui.PySideUic.loadUi(":/panels/DlgTCChooser.ui")
mylist = [i.Label for i in controllers]
form.uiToolController.addItems(mylist)
@@ -280,6 +348,7 @@ def findToolController(obj, name=None):
tc = [i for i in controllers if i.Label == form.uiToolController.currentText()][0]
return tc
+
def findParentJob(obj):
'''retrieves a parent job object for an operation or other Path object'''
PathLog.track()
@@ -292,7 +361,8 @@ def findParentJob(obj):
return grandParent
return None
-def GetJobs(jobname = None):
+
+def GetJobs(jobname=None):
'''returns all jobs in the current document. If name is given, returns that job'''
PathLog.track()
jobs = []
@@ -306,7 +376,8 @@ def GetJobs(jobname = None):
jobs.append(o)
return jobs
-def addToJob(obj, jobname = None):
+
+def addToJob(obj, jobname=None):
'''adds a path object to a job
obj = obj
jobname = None'''
@@ -326,7 +397,7 @@ def addToJob(obj, jobname = None):
elif len(jobs) == 1:
job = jobs[0]
else:
- #form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/DlgJobChooser.ui")
+ # form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/DlgJobChooser.ui")
form = FreeCADGui.PySideUic.loadUi(":/panels/DlgJobChooser.ui")
mylist = [i.Name for i in jobs]
form.cboProject.addItems(mylist)
@@ -342,6 +413,7 @@ def addToJob(obj, jobname = None):
job.Group = g
return job
+
def rapid(x=None, y=None, z=None):
""" Returns gcode string to perform a rapid move."""
retstr = "G00"
@@ -356,6 +428,7 @@ def rapid(x=None, y=None, z=None):
return ""
return retstr + "\n"
+
def feed(x=None, y=None, z=None, horizFeed=0, vertFeed=0):
""" Return gcode string to perform a linear feed."""
global feedxy
@@ -376,6 +449,7 @@ def feed(x=None, y=None, z=None, horizFeed=0, vertFeed=0):
return ""
return retstr + "\n"
+
def arc(cx, cy, sx, sy, ex, ey, horizFeed=0, ez=None, ccw=False):
"""
Return gcode string to perform an arc.
@@ -412,6 +486,7 @@ def arc(cx, cy, sx, sy, ex, ey, horizFeed=0, ez=None, ccw=False):
return retstr + "\n"
+
def helicalPlunge(plungePos, rampangle, destZ, startZ, toold, plungeR, horizFeed):
"""
Return gcode string to perform helical entry move.
@@ -458,6 +533,7 @@ def helicalPlunge(plungePos, rampangle, destZ, startZ, toold, plungeR, horizFeed
return helixCmds
+
def rampPlunge(edge, rampangle, destZ, startZ):
"""
Return gcode string to linearly ramp down to milling level.
@@ -562,6 +638,7 @@ def sort_jobs(locations, keys, attractors=[]):
return out
+
class depth_params:
'''calculates the intermediate depth values for various operations given the starting, ending, and stepdown parameters
(self, clearance_height, rapid_safety_space, start_depth, step_down, z_finish_depth, final_depth, [user_depths=None])
diff --git a/src/Mod/Path/PathScripts/linuxcnc_post.py b/src/Mod/Path/PathScripts/linuxcnc_post.py
index 4b161c6024..b39f1ddf96 100644
--- a/src/Mod/Path/PathScripts/linuxcnc_post.py
+++ b/src/Mod/Path/PathScripts/linuxcnc_post.py
@@ -39,6 +39,7 @@ Arguments for linuxcnc:
--comments,--no-comments ... output comments (--comments)
--line-numbers,--no-line-numbers ... prefix with line numbers (--no-lin-numbers)
--show-editor, --no-show-editor ... pop up editor before writing output(--show-editor)
+ --output-precision=4 ... number of digits of precision. Default=4
'''
import FreeCAD
from FreeCAD import Units
@@ -66,6 +67,7 @@ UNIT_FORMAT = 'mm/min'
MACHINE_NAME = "LinuxCNC"
CORNER_MIN = {'x': 0, 'y': 0, 'z': 0}
CORNER_MAX = {'x': 500, 'y': 300, 'z': 300}
+PRECISION=4
# Preamble text will appear at the beginning of the GCODE output file.
PREAMBLE = '''G17 G90
@@ -98,6 +100,8 @@ def processArguments(argstring):
global OUTPUT_COMMENTS
global OUTPUT_LINE_NUMBERS
global SHOW_EDITOR
+ global PRECISION
+
for arg in argstring.split():
if arg == '--header':
OUTPUT_HEADER = True
@@ -115,6 +119,8 @@ def processArguments(argstring):
SHOW_EDITOR = True
elif arg == '--no-show-editor':
SHOW_EDITOR = False
+ elif arg.split('=')[0] == '--output-precision':
+ PRECISION = arg.split('=')[1]
def export(objectslist, filename, argstring):
processArguments(argstring)
@@ -211,8 +217,10 @@ def linenumber():
return ""
def parse(pathobj):
+ global PRECISION
out = ""
lastcommand = None
+ precision_string = '.' + str(PRECISION) +'f'
# params = ['X','Y','Z','A','B','I','J','K','F','S'] #This list control
# the order of parameters
@@ -251,12 +259,12 @@ def parse(pathobj):
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_FORMAT)), '.2f') )
+ param + format(float(speed.getValueAs(UNIT_FORMAT)), precision_string) )
elif param == 'T':
outstring.append(param + str(c.Parameters['T']))
else:
outstring.append(
- param + format(c.Parameters[param], '.4f'))
+ param + format(c.Parameters[param], precision_string))
# store the latest command
lastcommand = command
diff --git a/src/Mod/Path/PathScripts/smoothie_post.py b/src/Mod/Path/PathScripts/smoothie_post.py
index bd691f0270..f07d151762 100644
--- a/src/Mod/Path/PathScripts/smoothie_post.py
+++ b/src/Mod/Path/PathScripts/smoothie_post.py
@@ -56,6 +56,8 @@ OUTPUT_LINE_NUMBERS = False
IP_ADDR = None
VERBOSE = False
+SPINDLE_SPEED = 0.0
+
if FreeCAD.GuiUp:
SHOW_EDITOR = True
else:
@@ -298,6 +300,7 @@ def linenumber():
def parse(pathobj):
out = ""
lastcommand = None
+ global SPINDLE_SPEED
# params = ['X','Y','Z','A','B','I','J','K','F','S'] #This list control
# the order of parameters
@@ -339,9 +342,14 @@ def parse(pathobj):
param + format(float(speed.getValueAs(UNIT_FORMAT)), '.2f') )
elif param == 'T':
outstring.append(param + str(c.Parameters['T']))
+ elif param == 'S':
+ outstring.append(param + str(c.Parameters['S']))
+ SPINDLE_SPEED = c.Parameters['S']
else:
outstring.append(
param + format(c.Parameters[param], '.4f'))
+ if command in ['G1', 'G01', 'G2', 'G02', 'G3', 'G03']:
+ outstring.append('S' + str(SPINDLE_SPEED))
# store the latest command
lastcommand = command
@@ -366,7 +374,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
diff --git a/src/Mod/Path/PathTests/TestPathCore.py b/src/Mod/Path/PathTests/TestPathCore.py
index da9ee3cfcd..77ccbb0941 100644
--- a/src/Mod/Path/PathTests/TestPathCore.py
+++ b/src/Mod/Path/PathTests/TestPathCore.py
@@ -109,6 +109,7 @@ G1X-0.5905Y-0.3937
G0Z0.5
'''
+
output = '''G0 S3000.000000 X-0.590500 Y-0.393700
M03
G0 Z0.125000
@@ -119,6 +120,8 @@ G1 X-0.590500 Y0.433000
G1 X-0.590500 Y-0.393700
G0 Z0.500000
'''
+
+
#create a path directly form a piece of gcode.
p = Path.Path()
p.setFromGCode(lines)
diff --git a/src/Mod/Path/PathTests/TestPathPost.py b/src/Mod/Path/PathTests/TestPathPost.py
index a33f4fad7c..284969596a 100644
--- a/src/Mod/Path/PathTests/TestPathPost.py
+++ b/src/Mod/Path/PathTests/TestPathPost.py
@@ -78,13 +78,13 @@ class PathPostTestCases(unittest.TestCase):
contour.OffsetExtra = 0.0
contour.Direction = 'CW'
contour.ToolController = tc
- contour.UseComp = True
+ contour.UseComp = False
PathScripts.PathUtils.addToJob(contour)
PathScripts.PathContour.ObjectContour.setDepths(contour.Proxy, contour)
self.doc.recompute()
job.PostProcessor = 'linuxcnc'
- job.PostProcessorArgs = '--no-header --no-line-numbers --no-comments --no-show-editor'
+ job.PostProcessorArgs = '--no-header --no-line-numbers --no-comments --no-show-editor --output-precision=2'
post = PathScripts.PathPost.CommandPathPost()
(fail, gcode) = post.exportObjectsWith([job], job, False)
diff --git a/src/Mod/Path/PathTests/test_linuxcnc_00.ngc b/src/Mod/Path/PathTests/test_linuxcnc_00.ngc
index 79a55e10e0..9f3c6e3cca 100644
--- a/src/Mod/Path/PathTests/test_linuxcnc_00.ngc
+++ b/src/Mod/Path/PathTests/test_linuxcnc_00.ngc
@@ -2,106 +2,68 @@ G17 G90
G21
(Default_Tool)
M6 T1.0
-M3 S0.0000
+M3 S0.00
(TC)
M6 T2.0
-M3 S0.0000
+M3 S0.00
(Contour)
(Uncompensated Tool Path)
-G0 Z15.0000
-G00 X-2.5000 Y0.0000
-G00 Z23.0000
-G01 X-2.5000 Y0.0000 Z9.0000 F0.00
-G01 X-2.5000 Y10.0000 Z9.0000 F0.00
-G02 X0.0000 Y12.5000 Z9.0000 I2.5000 J0.0000 F0.00
-G01 X10.0000 Y12.5000 Z9.0000 F0.00
-G02 X12.5000 Y10.0000 Z9.0000 I0.0000 J-2.5000 F0.00
-G01 X12.5000 Y0.0000 Z9.0000 F0.00
-G02 X10.0000 Y-2.5000 Z9.0000 I-2.5000 J0.0000 F0.00
-G01 X0.0000 Y-2.5000 Z9.0000 F0.00
-G02 X-2.5000 Y0.0000 Z9.0000 I0.0000 J2.5000 F0.00
-G01 X-2.5000 Y0.0000 Z8.0000 F0.00
-G01 X-2.5000 Y10.0000 Z8.0000 F0.00
-G02 X0.0000 Y12.5000 Z8.0000 I2.5000 J0.0000 F0.00
-G01 X10.0000 Y12.5000 Z8.0000 F0.00
-G02 X12.5000 Y10.0000 Z8.0000 I0.0000 J-2.5000 F0.00
-G01 X12.5000 Y0.0000 Z8.0000 F0.00
-G02 X10.0000 Y-2.5000 Z8.0000 I-2.5000 J0.0000 F0.00
-G01 X0.0000 Y-2.5000 Z8.0000 F0.00
-G02 X-2.5000 Y0.0000 Z8.0000 I0.0000 J2.5000 F0.00
-G01 X-2.5000 Y0.0000 Z7.0000 F0.00
-G01 X-2.5000 Y10.0000 Z7.0000 F0.00
-G02 X0.0000 Y12.5000 Z7.0000 I2.5000 J0.0000 F0.00
-G01 X10.0000 Y12.5000 Z7.0000 F0.00
-G02 X12.5000 Y10.0000 Z7.0000 I0.0000 J-2.5000 F0.00
-G01 X12.5000 Y0.0000 Z7.0000 F0.00
-G02 X10.0000 Y-2.5000 Z7.0000 I-2.5000 J0.0000 F0.00
-G01 X0.0000 Y-2.5000 Z7.0000 F0.00
-G02 X-2.5000 Y0.0000 Z7.0000 I0.0000 J2.5000 F0.00
-G01 X-2.5000 Y0.0000 Z6.0000 F0.00
-G01 X-2.5000 Y10.0000 Z6.0000 F0.00
-G02 X0.0000 Y12.5000 Z6.0000 I2.5000 J0.0000 F0.00
-G01 X10.0000 Y12.5000 Z6.0000 F0.00
-G02 X12.5000 Y10.0000 Z6.0000 I0.0000 J-2.5000 F0.00
-G01 X12.5000 Y0.0000 Z6.0000 F0.00
-G02 X10.0000 Y-2.5000 Z6.0000 I-2.5000 J0.0000 F0.00
-G01 X0.0000 Y-2.5000 Z6.0000 F0.00
-G02 X-2.5000 Y0.0000 Z6.0000 I0.0000 J2.5000 F0.00
-G01 X-2.5000 Y0.0000 Z5.0000 F0.00
-G01 X-2.5000 Y10.0000 Z5.0000 F0.00
-G02 X0.0000 Y12.5000 Z5.0000 I2.5000 J0.0000 F0.00
-G01 X10.0000 Y12.5000 Z5.0000 F0.00
-G02 X12.5000 Y10.0000 Z5.0000 I0.0000 J-2.5000 F0.00
-G01 X12.5000 Y0.0000 Z5.0000 F0.00
-G02 X10.0000 Y-2.5000 Z5.0000 I-2.5000 J0.0000 F0.00
-G01 X0.0000 Y-2.5000 Z5.0000 F0.00
-G02 X-2.5000 Y0.0000 Z5.0000 I0.0000 J2.5000 F0.00
-G01 X-2.5000 Y0.0000 Z4.0000 F0.00
-G01 X-2.5000 Y10.0000 Z4.0000 F0.00
-G02 X0.0000 Y12.5000 Z4.0000 I2.5000 J0.0000 F0.00
-G01 X10.0000 Y12.5000 Z4.0000 F0.00
-G02 X12.5000 Y10.0000 Z4.0000 I0.0000 J-2.5000 F0.00
-G01 X12.5000 Y0.0000 Z4.0000 F0.00
-G02 X10.0000 Y-2.5000 Z4.0000 I-2.5000 J0.0000 F0.00
-G01 X0.0000 Y-2.5000 Z4.0000 F0.00
-G02 X-2.5000 Y0.0000 Z4.0000 I0.0000 J2.5000 F0.00
-G01 X-2.5000 Y0.0000 Z3.0000 F0.00
-G01 X-2.5000 Y10.0000 Z3.0000 F0.00
-G02 X0.0000 Y12.5000 Z3.0000 I2.5000 J0.0000 F0.00
-G01 X10.0000 Y12.5000 Z3.0000 F0.00
-G02 X12.5000 Y10.0000 Z3.0000 I0.0000 J-2.5000 F0.00
-G01 X12.5000 Y0.0000 Z3.0000 F0.00
-G02 X10.0000 Y-2.5000 Z3.0000 I-2.5000 J0.0000 F0.00
-G01 X0.0000 Y-2.5000 Z3.0000 F0.00
-G02 X-2.5000 Y0.0000 Z3.0000 I0.0000 J2.5000 F0.00
-G01 X-2.5000 Y0.0000 Z2.0000 F0.00
-G01 X-2.5000 Y10.0000 Z2.0000 F0.00
-G02 X0.0000 Y12.5000 Z2.0000 I2.5000 J0.0000 F0.00
-G01 X10.0000 Y12.5000 Z2.0000 F0.00
-G02 X12.5000 Y10.0000 Z2.0000 I0.0000 J-2.5000 F0.00
-G01 X12.5000 Y0.0000 Z2.0000 F0.00
-G02 X10.0000 Y-2.5000 Z2.0000 I-2.5000 J0.0000 F0.00
-G01 X0.0000 Y-2.5000 Z2.0000 F0.00
-G02 X-2.5000 Y0.0000 Z2.0000 I0.0000 J2.5000 F0.00
-G01 X-2.5000 Y0.0000 Z1.0000 F0.00
-G01 X-2.5000 Y10.0000 Z1.0000 F0.00
-G02 X0.0000 Y12.5000 Z1.0000 I2.5000 J0.0000 F0.00
-G01 X10.0000 Y12.5000 Z1.0000 F0.00
-G02 X12.5000 Y10.0000 Z1.0000 I0.0000 J-2.5000 F0.00
-G01 X12.5000 Y0.0000 Z1.0000 F0.00
-G02 X10.0000 Y-2.5000 Z1.0000 I-2.5000 J0.0000 F0.00
-G01 X0.0000 Y-2.5000 Z1.0000 F0.00
-G02 X-2.5000 Y0.0000 Z1.0000 I0.0000 J2.5000 F0.00
-G01 X-2.5000 Y0.0000 Z0.0000 F0.00
-G01 X-2.5000 Y10.0000 Z0.0000 F0.00
-G02 X0.0000 Y12.5000 Z0.0000 I2.5000 J0.0000 F0.00
-G01 X10.0000 Y12.5000 Z0.0000 F0.00
-G02 X12.5000 Y10.0000 Z0.0000 I0.0000 J-2.5000 F0.00
-G01 X12.5000 Y0.0000 Z0.0000 F0.00
-G02 X10.0000 Y-2.5000 Z0.0000 I-2.5000 J0.0000 F0.00
-G01 X0.0000 Y-2.5000 Z0.0000 F0.00
-G02 X-2.5000 Y0.0000 Z0.0000 I0.0000 J2.5000 F0.00
-G00 Z15.0000
+G0 Z15.00
+G90
+G17
+G0 X0.00 Y0.00 Z15.00
+G0 X10.00 Y10.00 Z15.00
+G0 X10.00 Y10.00 Z10.00
+G1 X10.00 Y10.00 Z9.00
+G1 X10.00 Y0.00 Z9.00
+G1 X0.00 Y0.00 Z9.00
+G1 X0.00 Y10.00 Z9.00
+G1 X10.00 Y10.00 Z9.00
+G1 X10.00 Y10.00 Z8.00
+G1 X10.00 Y0.00 Z8.00
+G1 X0.00 Y0.00 Z8.00
+G1 X0.00 Y10.00 Z8.00
+G1 X10.00 Y10.00 Z8.00
+G1 X10.00 Y10.00 Z7.00
+G1 X10.00 Y0.00 Z7.00
+G1 X0.00 Y0.00 Z7.00
+G1 X0.00 Y10.00 Z7.00
+G1 X10.00 Y10.00 Z7.00
+G1 X10.00 Y10.00 Z6.00
+G1 X10.00 Y0.00 Z6.00
+G1 X0.00 Y0.00 Z6.00
+G1 X0.00 Y10.00 Z6.00
+G1 X10.00 Y10.00 Z6.00
+G1 X10.00 Y10.00 Z5.00
+G1 X10.00 Y0.00 Z5.00
+G1 X0.00 Y0.00 Z5.00
+G1 X0.00 Y10.00 Z5.00
+G1 X10.00 Y10.00 Z5.00
+G1 X10.00 Y10.00 Z4.00
+G1 X10.00 Y0.00 Z4.00
+G1 X0.00 Y0.00 Z4.00
+G1 X0.00 Y10.00 Z4.00
+G1 X10.00 Y10.00 Z4.00
+G1 X10.00 Y10.00 Z3.00
+G1 X10.00 Y0.00 Z3.00
+G1 X0.00 Y0.00 Z3.00
+G1 X0.00 Y10.00 Z3.00
+G1 X10.00 Y10.00 Z3.00
+G1 X10.00 Y10.00 Z2.00
+G1 X10.00 Y0.00 Z2.00
+G1 X0.00 Y0.00 Z2.00
+G1 X0.00 Y10.00 Z2.00
+G1 X10.00 Y10.00 Z2.00
+G1 X10.00 Y10.00 Z1.00
+G1 X10.00 Y0.00 Z1.00
+G1 X0.00 Y0.00 Z1.00
+G1 X0.00 Y10.00 Z1.00
+G1 X10.00 Y10.00 Z1.00
+G1 X10.00 Y10.00 Z0.00
+G1 X10.00 Y0.00 Z0.00
+G1 X0.00 Y0.00 Z0.00
+G1 X0.00 Y10.00 Z0.00
+G1 X10.00 Y10.00 Z0.00
M05
G00 X-1.0 Y1.0
G17 G90
diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp
index 55eaa58dbf..ee1780ebc8 100644
--- a/src/Mod/Sketcher/App/SketchObject.cpp
+++ b/src/Mod/Sketcher/App/SketchObject.cpp
@@ -960,7 +960,8 @@ int SketchObject::transferConstraints(int fromGeoId, PointPos fromPosId, int toG
std::vector changed;
for (int i=0; i < int(newVals.size()); i++) {
if (vals[i]->First == fromGeoId && vals[i]->FirstPos == fromPosId &&
- !(vals[i]->Second == toGeoId && vals[i]->SecondPos == toPosId)) {
+ !(vals[i]->Second == toGeoId && vals[i]->SecondPos == toPosId) &&
+ !(toGeoId < 0 && vals[i]->Second <0) ) {
Constraint *constNew = newVals[i]->clone();
constNew->First = toGeoId;
constNew->FirstPos = toPosId;
@@ -968,7 +969,8 @@ int SketchObject::transferConstraints(int fromGeoId, PointPos fromPosId, int toG
changed.push_back(constNew);
}
else if (vals[i]->Second == fromGeoId && vals[i]->SecondPos == fromPosId &&
- !(vals[i]->First == toGeoId && vals[i]->FirstPos == toPosId)) {
+ !(vals[i]->First == toGeoId && vals[i]->FirstPos == toPosId) &&
+ !(toGeoId < 0 && vals[i]->First< 0)) {
Constraint *constNew = newVals[i]->clone();
constNew->Second = toGeoId;
constNew->SecondPos = toPosId;