diff --git a/src/Mod/Path/CMakeLists.txt b/src/Mod/Path/CMakeLists.txt
index e6cc466f60..c4cf6bd043 100644
--- a/src/Mod/Path/CMakeLists.txt
+++ b/src/Mod/Path/CMakeLists.txt
@@ -33,6 +33,7 @@ SET(PathScripts_SRCS
PathScripts/PathProfile.py
PathScripts/PathProfileEdges.py
PathScripts/PathContour.py
+ PathScripts/PathMillFace.py
PathScripts/PathPocket.py
PathScripts/PathDrilling.py
PathScripts/PathDressup.py
diff --git a/src/Mod/Path/Gui/Resources/Path.qrc b/src/Mod/Path/Gui/Resources/Path.qrc
index 3269fd0653..27353e6759 100644
--- a/src/Mod/Path/Gui/Resources/Path.qrc
+++ b/src/Mod/Path/Gui/Resources/Path.qrc
@@ -45,6 +45,7 @@
icons/Path-Profile-Edges.svg
icons/Path-Profile-Face.svg
icons/Path-SelectLoop.svg
+ icons/Path-Face.svg
translations/Path_de.qm
translations/Path_af.qm
translations/Path_zh-CN.qm
@@ -84,6 +85,7 @@
panels/ToolEdit.ui
panels/DlgJobChooser.ui
panels/ContourEdit.ui
+ panels/MillFaceEdit.ui
panels/ProfileEdgesEdit.ui
panels/DogboneEdit.ui
panels/DlgSelectPostProcessor.ui
diff --git a/src/Mod/Path/Gui/Resources/icons/Path-Face.svg b/src/Mod/Path/Gui/Resources/icons/Path-Face.svg
new file mode 100644
index 0000000000..a9c190f11e
--- /dev/null
+++ b/src/Mod/Path/Gui/Resources/icons/Path-Face.svg
@@ -0,0 +1,926 @@
+
+
+
+
diff --git a/src/Mod/Path/Gui/Resources/panels/ContourEdit.ui b/src/Mod/Path/Gui/Resources/panels/ContourEdit.ui
index d68a4c97d9..f80fdf98c6 100644
--- a/src/Mod/Path/Gui/Resources/panels/ContourEdit.ui
+++ b/src/Mod/Path/Gui/Resources/panels/ContourEdit.ui
@@ -23,56 +23,28 @@
-
- 4
+ 0
-
-
- true
-
-
-
- 0
- 0
- 334
- 288
-
-
-
- Contour works on shape outer loop but maybe could work on something else
-
-
-
- :/icons/Path-BaseGeometry.svg:/icons/Path-BaseGeometry.svg
-
-
- Base Geometry
-
-
-
0
0
334
- 288
+ 318
-
+
:/icons/Path-Depths.svg:/icons/Path-Depths.svg
Depths
-
-
-
-
- mm
-
-
-
+
+ QFormLayout::AllNonFixedFieldsGrow
+
-
@@ -114,6 +86,13 @@
+ -
+
+
+ mm
+
+
+
@@ -122,11 +101,11 @@
0
0
334
- 288
+ 318
-
+
:/icons/Path-Heights.svg:/icons/Path-Heights.svg
@@ -172,7 +151,7 @@
0
0
334
- 288
+ 318
@@ -193,7 +172,7 @@
- -
+
-
@@ -230,11 +209,11 @@
0
0
334
- 288
+ 318
-
+
:/icons/Path-OperationB.svg:/icons/Path-OperationB.svg
@@ -358,8 +337,8 @@
-
-
+
+
diff --git a/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui b/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui
new file mode 100644
index 0000000000..2795ac19c6
--- /dev/null
+++ b/src/Mod/Path/Gui/Resources/panels/MillFaceEdit.ui
@@ -0,0 +1,418 @@
+
+
+ TaskPanel
+
+
+
+ 0
+ 0
+ 374
+ 453
+
+
+
+
+ 0
+ 400
+
+
+
+ Mill Facing
+
+
+
-
+
+
+ 0
+
+
+
+ true
+
+
+
+ 0
+ 0
+ 340
+ 277
+
+
+
+
+ :/icons/Path-BaseGeometry.svg:/icons/Path-BaseGeometry.svg
+
+
+ Base Geometry
+
+
+
-
+
+
+ Drag to reorder, then update.
+
+
+ QAbstractItemView::DragDrop
+
+
+ Qt::MoveAction
+
+
+ false
+
+
+
+ -
+
+
+ Add item selected in window.
+
+
+ add
+
+
+
+ -
+
+
+ Remove Item selected in list, then update.
+
+
+ Remove
+
+
+
+ -
+
+
+ Update the path with the removed and reordered items.
+
+
+ Update
+
+
+
+ -
+
+
+ All objects will be processed using the same operation properties.
+
+
+ Qt::AutoText
+
+
+ true
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 356
+ 124
+
+
+
+
+ :/icons/Path-Depths.svg:/icons/Path-Depths.svg
+
+
+ Depths
+
+
+ -
+
+
+ mm
+
+
+
+ -
+
+
+ Start Depth
+
+
+
+ -
+
+
+ mm
+
+
+
+ -
+
+
+ Final Depth
+
+
+
+ -
+
+
+ mm
+
+
+
+ -
+
+
+ Finish Depth
+
+
+
+ -
+
+
+ 3
+
+
+ 0.100000000000000
+
+
+ 1.000000000000000
+
+
+
+ -
+
+
+ Step Down
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 356
+ 95
+
+
+
+
+ :/icons/Path-Heights.svg:/icons/Path-Heights.svg
+
+
+ Heights
+
+
+
+ QFormLayout::AllNonFixedFieldsGrow
+
+ -
+
+
+ mm
+
+
+
+ -
+
+
+ Safe Height
+
+
+
+ -
+
+
+ mm
+
+
+
+ -
+
+
+ Clearance Height
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 356
+ 296
+
+
+
+
+ :/icons/Path-OperationB.svg:/icons/Path-OperationB.svg
+
+
+ Operation
+
+
+ -
+
+
+
-
+
+
+ Pass Extension
+
+
+
+ -
+
+
+ -99.000000000000000
+
+
+ 99.000000000000000
+
+
+
+ -
+
+
+ Step Over Percent
+
+
+
+ -
+
+
+ 1
+
+
+ 100
+
+
+ 10
+
+
+ 100
+
+
+
+
+
+
+ -
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
-
+
+
+ Use ZigZag
+
+
+
+ -
+
+
+ ZigZag Angle
+
+
+
+ -
+
+
+ -
+
+
+ ZigZag Unidirectional
+
+
+
+
+
+
+ -
+
+
+
-
+
+
+ Cut Mode
+
+
+
+ -
+
+
+ Boundary Shape
+
+
+
+ -
+
+
-
+
+ Perimeter
+
+
+ -
+
+ Boundbox
+
+
+
+
+ -
+
+
-
+
+ Climb
+
+
+ -
+
+ Conventional
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+ Gui::InputField
+ QLineEdit
+
+
+
+
+
+
+
+
+
diff --git a/src/Mod/Path/Gui/Resources/panels/ProfileEdgesEdit.ui b/src/Mod/Path/Gui/Resources/panels/ProfileEdgesEdit.ui
index 4732c2b253..80ad39a545 100644
--- a/src/Mod/Path/Gui/Resources/panels/ProfileEdgesEdit.ui
+++ b/src/Mod/Path/Gui/Resources/panels/ProfileEdgesEdit.ui
@@ -111,8 +111,8 @@
0
0
- 334
- 319
+ 168
+ 99
@@ -178,8 +178,8 @@
0
0
- 334
- 319
+ 165
+ 70
@@ -250,7 +250,7 @@
- -
+
-
@@ -286,8 +286,8 @@
0
0
- 334
- 319
+ 322
+ 306
diff --git a/src/Mod/Path/Gui/Resources/panels/ProfileEdit.ui b/src/Mod/Path/Gui/Resources/panels/ProfileEdit.ui
index 1161856f91..8238fc6856 100644
--- a/src/Mod/Path/Gui/Resources/panels/ProfileEdit.ui
+++ b/src/Mod/Path/Gui/Resources/panels/ProfileEdit.ui
@@ -6,8 +6,8 @@
0
0
- 352
- 525
+ 399
+ 519
@@ -33,8 +33,8 @@
0
0
- 334
- 358
+ 381
+ 353
@@ -45,22 +45,6 @@
Base Geometry
-
-
-
-
- Drag to reorder, then update.
-
-
- QAbstractItemView::DragDrop
-
-
- Qt::MoveAction
-
-
- false
-
-
-
-
@@ -104,6 +88,22 @@
+ -
+
+
+ Drag to reorder, then update.
+
+
+ QAbstractItemView::DragDrop
+
+
+ Qt::MoveAction
+
+
+ false
+
+
+
@@ -111,8 +111,8 @@
0
0
- 334
- 358
+ 381
+ 353
@@ -178,8 +178,8 @@
0
0
- 334
- 358
+ 381
+ 353
@@ -228,14 +228,28 @@
0
0
- 334
- 358
+ 381
+ 353
Holding
+ -
+
+
+ Add New
+
+
+
+ -
+
+
+ Delete
+
+
+
-
@@ -265,20 +279,6 @@
- -
-
-
- Add New
-
-
-
- -
-
-
- Delete
-
-
-
@@ -286,8 +286,8 @@
0
0
- 334
- 358
+ 381
+ 353
@@ -301,28 +301,107 @@
QFormLayout::AllNonFixedFieldsGrow
- -
-
-
- Algorithm
-
+
-
+
+
+
-
+
+
+ Use End Point
+
+
+
+ -
+
+
+ Use Start Point
+
+
+
+ -
+
+
+ Use Compensation
+
+
+
+ -
+
+
+ Process Holes
+
+
+
+ -
+
+
+ Process Perimeter
+
+
+
+
+ -
+
+
+
-
+
+
+ Extra Offset
+
+
+
+ -
+
+
+ -
+
+
+ Segment Length
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ Roll Radius
+
+
+
+ -
+
+
+ Plunge Angle
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
-
-
-
-
-
- OCC Native
-
-
- -
-
- libarea
-
-
-
-
- -
-
@@ -375,86 +454,6 @@
- -
-
-
-
-
-
-
- Use Start Point
-
-
-
- -
-
-
- Use Compensation
-
-
-
- -
-
-
- Use End Point
-
-
-
- -
-
-
- Process Holes
-
-
-
-
-
-
- -
-
-
-
-
-
-
- Extra Offset
-
-
-
- -
-
-
- -
-
-
- Segment Length
-
-
-
- -
-
-
- -
-
-
- Roll Radius
-
-
-
- -
-
-
- -
-
-
- Plunge Angle
-
-
-
- -
-
-
-
-
-
diff --git a/src/Mod/Path/InitGui.py b/src/Mod/Path/InitGui.py
index 9894b22017..468f07fa97 100644
--- a/src/Mod/Path/InitGui.py
+++ b/src/Mod/Path/InitGui.py
@@ -74,13 +74,14 @@ class PathWorkbench (Workbench):
from PathScripts import PathContour
from PathScripts import PathProfileEdges
from PathScripts import DogboneDressup
+ from PathScripts import PathMillFace
import PathCommands
# build commands list
projcmdlist = ["Path_Job", "Path_Post", "Path_Inspect", "Path_Sanity"]
toolcmdlist = ["Path_ToolLibraryEdit", "Path_LoadTool"]
prepcmdlist = ["Path_Plane", "Path_Fixture", "Path_ToolLenOffset", "Path_Comment", "Path_Stop", "Path_FaceProfile", "Path_FacePocket", "Path_Custom", "Path_FromShape"]
- twodopcmdlist = ["Path_Contour", "Path_Profile", "Path_Profile_Edges", "Path_Pocket", "Path_Drilling", "Path_Engrave"]
+ twodopcmdlist = ["Path_Contour", "Path_Profile", "Path_Profile_Edges", "Path_Pocket", "Path_Drilling", "Path_Engrave", "Path_MillFace"]
threedopcmdlist = ["Path_Surfacing"]
modcmdlist = ["Path_Copy", "Path_CompoundExtended", "Path_Array", "Path_SimpleCopy" ]
dressupcmdlist = ["Dogbone_Dressup", "DragKnife_Dressup"]
@@ -135,7 +136,7 @@ class PathWorkbench (Workbench):
if len(FreeCADGui.Selection.getSelection()) == 1:
if FreeCADGui.Selection.getSelection()[0].isDerivedFrom("Path::Feature"):
self.appendContextMenu("", ["Path_Inspect"])
- if "Profile" in FreeCADGui.Selection.getSelection()[0].Name:
+ if "Profile" or "Contour" in FreeCADGui.Selection.getSelection()[0].Name:
self.appendContextMenu("", ["Add_Tag"])
self.appendContextMenu("", ["Set_StartPoint"])
self.appendContextMenu("", ["Set_EndPoint"])
diff --git a/src/Mod/Path/PathScripts/PathContour.py b/src/Mod/Path/PathScripts/PathContour.py
index 5eedfaaa2c..a8386299ab 100644
--- a/src/Mod/Path/PathScripts/PathContour.py
+++ b/src/Mod/Path/PathScripts/PathContour.py
@@ -25,6 +25,7 @@
import FreeCAD
import Path
from FreeCAD import Vector
+import TechDraw
from PathScripts import PathUtils
from PathScripts.PathUtils import depth_params
@@ -246,8 +247,7 @@ class ObjectContour:
baseobject = parentJob.Base
if baseobject is None:
return
- print "base object: " + baseobject.Name
- contourwire = PathUtils.silhouette(baseobject)
+ contourwire = TechDraw.findShapeOutline(baseobject.Shape,1, Vector(0,0,1))
edgelist = contourwire.Edges
edgelist = Part.__sortEdges__(edgelist)
@@ -419,7 +419,6 @@ class TaskPanel:
self.updating = False
def accept(self):
- print "removed"
self.getFields()
FreeCADGui.ActiveDocument.resetEdit()
@@ -428,7 +427,6 @@ class TaskPanel:
FreeCAD.ActiveDocument.recompute()
def reject(self):
- print "removed1"
FreeCADGui.Control.closeDialog()
FreeCADGui.Selection.removeObserver(self.s)
FreeCAD.ActiveDocument.recompute()
@@ -482,6 +480,7 @@ class TaskPanel:
if index >= 0:
self.form.direction.setCurrentIndex(index)
+ self.form.tagTree.blockSignals(True)
for i in range(len(self.obj.locs)):
item = QtGui.QTreeWidgetItem(self.form.tagTree)
item.setText(0, str(i+1))
@@ -492,6 +491,7 @@ class TaskPanel:
item.setText(4, str(self.obj.angles[i]))
item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable)
item.setTextAlignment(0, QtCore.Qt.AlignLeft)
+ self.form.tagTree.blockSignals(False)
def open(self):
self.s = SelObserver()
diff --git a/src/Mod/Path/PathScripts/PathDrilling.py b/src/Mod/Path/PathScripts/PathDrilling.py
index 79f348f4bd..0cd13d2c6f 100644
--- a/src/Mod/Path/PathScripts/PathDrilling.py
+++ b/src/Mod/Path/PathScripts/PathDrilling.py
@@ -189,15 +189,26 @@ class ObjectDrilling:
drillable = False
if obj.ShapeType == 'Vertex':
drillable = True
- elif obj.ShapeType == 'Solid':
+ elif obj.ShapeType in['Solid', 'Compound']:
if sub[0:4] == 'Face':
subobj = obj.getElement(sub)
if isinstance(subobj.Edges[0].Curve, Part.Circle):
drillable = True
if str(subobj.Surface) == "":
drillable = True
- if len(subobj.Edges[0].Vertexes) > 1:
- drillable = False
+ if len(subobj.Edges) == 3:
+ cedge = []
+ ledge = []
+ for e in subobj.Edges:
+ if isinstance (e.Curve, Part.Circle):
+ cedge.append(e)
+ elif isinstance (e.Curve, Part.Line):
+ ledge.append(e)
+ if len(cedge) == 2 and len(ledge) == 1:
+ drillable = True
+ else:
+ #if len(subobj.Edges[0].Vertexes) > 1:
+ drillable = False
if sub[0:4] == 'Edge':
o = obj.getElement(sub)
if isinstance(o.Curve, Part.Circle) and len(o.Vertexes) == 1:
diff --git a/src/Mod/Path/PathScripts/PathKurveUtils.py b/src/Mod/Path/PathScripts/PathKurveUtils.py
index 97f4e5f4c9..4fcf5c8b6a 100644
--- a/src/Mod/Path/PathScripts/PathKurveUtils.py
+++ b/src/Mod/Path/PathScripts/PathKurveUtils.py
@@ -302,26 +302,26 @@ def profile2(curve, direction="on", radius=1.0, vertfeed=0.0,
extend_at_start=0.0, extend_at_end=0.0, lead_in_line_len=0.0,
lead_out_line_len=0.0):
- print "direction: " + str(direction)
- print "radius: " + str(radius)
- print "vertfeed: " + str(vertfeed)
- print "horizfeed: " + str(horizfeed)
- print "offset_extra: " + str(offset_extra)
- print "roll_radius: " + str(roll_radius)
- print "roll_on: " + str(roll_on)
- print "roll_off: " + str(roll_off)
- print "depthparams: " + str(depthparams)
- print "extend_at_start: " + str(extend_at_start)
- print "extend_at_end: " + str(extend_at_end)
- print "lead_in_line_len: " + str(lead_in_line_len)
- print "lead_out_line_len: " + str(lead_out_line_len)
- print "in profile2: 318"
+ # print "direction: " + str(direction)
+ # print "radius: " + str(radius)
+ # print "vertfeed: " + str(vertfeed)
+ # print "horizfeed: " + str(horizfeed)
+ # print "offset_extra: " + str(offset_extra)
+ # print "roll_radius: " + str(roll_radius)
+ # print "roll_on: " + str(roll_on)
+ # print "roll_off: " + str(roll_off)
+ # print "depthparams: " + str(depthparams)
+ # print "extend_at_start: " + str(extend_at_start)
+ # print "extend_at_end: " + str(extend_at_end)
+ # print "lead_in_line_len: " + str(lead_in_line_len)
+ # print "lead_out_line_len: " + str(lead_out_line_len)
+ # print "in profile2: 318"
global tags
direction = direction.lower()
offset_curve = area.Curve(curve)
- print "curve: " , str(curve)
- print "result curve: ", offset_curve.__dict__
+ # print "curve: " , str(curve)
+ # print "result curve: ", offset_curve.__dict__
if direction == "on":
use_CRC() == False
diff --git a/src/Mod/Path/PathScripts/PathLoadTool.py b/src/Mod/Path/PathScripts/PathLoadTool.py
index e4a5407d17..a115a099c2 100644
--- a/src/Mod/Path/PathScripts/PathLoadTool.py
+++ b/src/Mod/Path/PathScripts/PathLoadTool.py
@@ -26,6 +26,7 @@
import FreeCAD
import FreeCADGui
import PathUtils
+import Path
import Part
import PathScripts
from PySide import QtCore, QtGui
@@ -74,6 +75,15 @@ class LoadTool():
else:
commands += 'M4S' + str(obj.SpindleSpeed) + '\n'
+ # print output
+ if commands == "":
+ commands += "(No commands processed)"
+
+
+ path = Path.Path(commands)
+ obj.Path = path
+ obj.ViewObject.Visibility = True
+
def onChanged(self, obj, prop):
mode = 2
obj.setEditorMode('Placement', mode)
diff --git a/src/Mod/Path/PathScripts/PathMillFace.py b/src/Mod/Path/PathScripts/PathMillFace.py
new file mode 100644
index 0000000000..b799cf5f4b
--- /dev/null
+++ b/src/Mod/Path/PathScripts/PathMillFace.py
@@ -0,0 +1,635 @@
+# -*- coding: utf-8 -*-
+
+# ***************************************************************************
+# * *
+# * Copyright (c) 2016 sliptonic *
+# * *
+# * This program is free software; you can redistribute it and/or modify *
+# * it under the terms of the GNU Lesser General Public License (LGPL) *
+# * as published by the Free Software Foundation; either version 2 of *
+# * the License, or (at your option) any later version. *
+# * for detail see the LICENCE text file. *
+# * *
+# * This program is distributed in the hope that it will be useful, *
+# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+# * GNU Library General Public License for more details. *
+# * *
+# * You should have received a copy of the GNU Library General Public *
+# * License along with this program; if not, write to the Free Software *
+# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+# * USA *
+# * *
+# ***************************************************************************
+
+import FreeCAD
+import Path
+from PySide import QtCore, QtGui
+from PathScripts import PathUtils
+import Part
+import PathScripts.PathKurveUtils
+import area
+import TechDraw
+from FreeCAD import Vector
+
+FreeCADGui = None
+if FreeCAD.GuiUp:
+ import FreeCADGui
+
+"""Path Face object and FreeCAD command"""
+
+# Qt tanslation handling
+try:
+ _encoding = QtGui.QApplication.UnicodeUTF8
+
+ def translate(context, text, disambig=None):
+ return QtGui.QApplication.translate(context, text, disambig, _encoding)
+except AttributeError:
+ def translate(context, text, disambig=None):
+ return QtGui.QApplication.translate(context, text, disambig)
+
+
+class ObjectFace:
+
+ def __init__(self, obj):
+ obj.addProperty("App::PropertyLinkSubList", "Base", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property","The base geometry of this object"))
+ obj.addProperty("App::PropertyBool", "Active", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property","Make False, to prevent operation from generating code"))
+ obj.addProperty("App::PropertyString", "Comment", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property","An optional comment for this profile"))
+ obj.addProperty("App::PropertyString", "UserLabel", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property","User Assigned Label"))
+
+ # Tool Properties
+ obj.addProperty("App::PropertyIntegerConstraint", "ToolNumber", "Tool", QtCore.QT_TRANSLATE_NOOP("App::Property","The tool number in use"))
+ obj.ToolNumber = (0, 0, 1000, 0)
+ obj.setEditorMode('ToolNumber', 1) # make this read only
+ obj.addProperty("App::PropertyString", "ToolDescription", "Tool", QtCore.QT_TRANSLATE_NOOP("App::Property","The description of the tool "))
+ obj.setEditorMode('ToolDescription', 1) # make this read only
+
+ # Depth Properties
+ obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property","The height needed to clear clamps and obstructions"))
+ obj.addProperty("App::PropertyDistance", "SafeHeight", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property","Rapid Safety Height between locations."))
+ obj.addProperty("App::PropertyFloatConstraint", "StepDown", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property","Incremental Step Down of Tool"))
+ obj.StepDown = (0.0, 0.01, 100.0, 0.5)
+ obj.addProperty("App::PropertyDistance", "StartDepth", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property","Starting Depth of Tool- first cut depth in Z"))
+ obj.addProperty("App::PropertyDistance", "FinalDepth", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property","Final Depth of Tool- lowest value in Z"))
+ obj.addProperty("App::PropertyDistance", "FinishDepth", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property","Maximum material removed on final pass."))
+
+ # Face Properties
+ obj.addProperty("App::PropertyEnumeration", "CutMode", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property","The direction that the toolpath should go around the part ClockWise CW or CounterClockWise CCW"))
+ obj.CutMode = ['Climb', 'Conventional']
+ obj.addProperty("App::PropertyDistance", "PassExtension", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property","How far the cutter should extend past the boundary"))
+ obj.addProperty("App::PropertyEnumeration", "StartAt", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property","Start Faceing at center or boundary"))
+ obj.StartAt = ['Center', 'Edge']
+ obj.addProperty("App::PropertyPercent", "StepOver", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property","Percent of cutter diameter to step over on each pass"))
+ #obj.StepOver = (1, 1, 100, 1)
+ obj.addProperty("App::PropertyBool", "KeepToolDown", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property","Attempts to avoid unnecessary retractions."))
+ obj.addProperty("App::PropertyBool", "ZigUnidirectional", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property","Lifts tool at the end of each pass to respect cut mode."))
+ obj.addProperty("App::PropertyBool", "UseZigZag", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property","Use Zig Zag pattern to clear area."))
+ 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']
+
+
+ # 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.Proxy = self
+
+ def onChanged(self, obj, prop):
+
+ if prop == "UserLabel":
+ obj.Label = obj.UserLabel + " :" + obj.ToolDescription
+ if prop == "StepOver":
+ if obj.StepOver == 0:
+ obj.StepOver = 1
+
+ def __getstate__(self):
+ return None
+
+ def __setstate__(self, state):
+ return None
+
+ def _guessDepths(self, obj, ss, sub=""):
+ try:
+ bb = ss.Shape.BoundBox # parent boundbox
+ subobj = ss.Shape.getElement(sub)
+ fbb = subobj.BoundBox # feature boundbox
+ obj.StartDepth = bb.ZMax
+ obj.ClearanceHeight = bb.ZMax + 5.0
+ obj.SafeHeight = bb.ZMax + 3.0
+
+ if fbb.ZMax == fbb.ZMin and fbb.ZMax == bb.ZMax: # top face
+ obj.FinalDepth = bb.ZMin
+ elif fbb.ZMax > fbb.ZMin and fbb.ZMax == bb.ZMax: # vertical face, full cut
+ obj.FinalDepth = fbb.ZMin
+ elif fbb.ZMax > fbb.ZMin and fbb.ZMin > bb.ZMin: # internal vertical wall
+ obj.FinalDepth = fbb.ZMin
+ elif fbb.ZMax == fbb.ZMin and fbb.ZMax > bb.ZMin: # face/shelf
+ obj.FinalDepth = fbb.ZMin
+ else: # catch all
+ obj.FinalDepth = bb.ZMin
+ except:
+ obj.StartDepth = 5.0
+ obj.ClearanceHeight = 10.0
+ obj.SafeHeight = 8.0
+
+
+ def addFacebase(self, obj, ss, sub=""):
+ baselist = obj.Base
+ if baselist is None:
+ baselist = []
+ if len(baselist) == 0: # When adding the first base object, guess at heights
+ self._guessDepths(obj, ss, sub)
+
+ item = (ss, sub)
+ if item in baselist:
+ FreeCAD.Console.PrintWarning(translate("Path", "this object already in the list" + "\n"))
+ elif PathUtils.findParentJob(obj).Base.Name != ss.Name:
+ FreeCAD.Console.PrintWarning(translate("Path", "Please select features from the Job model object" +"\n"))
+ else:
+ baselist.append(item)
+ obj.Base = baselist
+ self.execute(obj)
+
+ def getStock(self, obj):
+ """find and return a stock object from hosting project if any"""
+ for o in obj.InList:
+ if hasattr(o, "Group"):
+ for g in o.Group:
+ if hasattr(g, "Height_Allowance"):
+ return o
+ # not found? search one level up
+ for o in obj.InList:
+ return self.getStock(o)
+ return None
+
+ def buildpathlibarea(self, obj, a):
+ """Build the face path using libarea algorithm"""
+ import PathScripts.PathAreaUtils as PathAreaUtils
+ from PathScripts.PathUtils import depth_params
+
+ FreeCAD.Console.PrintMessage(translate("PathFace", "Generating toolpath with libarea offsets.\n"))
+
+ depthparams = depth_params(
+ obj.ClearanceHeight.Value,
+ obj.SafeHeight.Value,
+ obj.StartDepth.Value,
+ obj.StepDown,
+ obj.FinishDepth.Value,
+ obj.FinalDepth.Value)
+
+ extraoffset = 1 - 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
+
+ PathAreaUtils.flush_nc()
+ PathAreaUtils.output('mem')
+ PathAreaUtils.feedrate_hv(self.horizFeed, self.vertFeed)
+ if obj.UseStartPoint:
+ start_point = (obj.StartPoint.x, obj.StartPoint.y)
+
+ 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()
+
+ # To reload this from FreeCAD, use: import PathScripts.PathFace; reload(PathScripts.PathFace)
+ def execute(self, obj):
+
+ if not obj.Active:
+ path = Path.Path("(inactive operation)")
+ obj.Path = path
+ obj.ViewObject.Visibility = False
+ return
+
+ #Tool may have changed. Refresh data
+ toolLoad = PathUtils.getLastToolLoad(obj)
+ if toolLoad is None or toolLoad.ToolNumber == 0:
+ self.vertFeed = 100
+ self.horizFeed = 100
+ self.vertRapid = 100
+ self.horiRrapid = 100
+ self.radius = 0.25
+ obj.ToolNumber = 0
+ obj.ToolDescription = "UNDEFINED"
+ else:
+ self.vertFeed = toolLoad.VertFeed.Value
+ self.horizFeed = toolLoad.HorizFeed.Value
+ self.vertRapid = toolLoad.VertRapid.Value
+ self.horizRapid = toolLoad.HorizRapid.Value
+ tool = PathUtils.getTool(obj, toolLoad.ToolNumber)
+ if tool.Diameter == 0:
+ self.radius = 0.25
+ else:
+ self.radius = tool.Diameter/2
+ obj.ToolNumber = toolLoad.ToolNumber
+ obj.ToolDescription = toolLoad.Name
+
+ #Build preliminary comments
+ output = ""
+ output += "(" + obj.Label + ")"
+
+ if obj.UserLabel == "":
+ obj.Label = obj.Name + " :" + obj.ToolDescription
+ else:
+ obj.Label = obj.UserLabel + " :" + obj.ToolDescription
+
+ #Facing is done either against base objects
+ if obj.Base:
+ faces = []
+ for b in obj.Base:
+ for sub in b[1]:
+ shape = getattr(b[0].Shape, sub)
+ if isinstance (shape, Part.Face):
+ faces.append(shape)
+ else:
+ print ('falling out')
+ return
+ planeshape = Part.makeCompound(faces)
+
+ #If no base object, do planing of top surface of entire model
+ else:
+ parentJob = PathUtils.findParentJob(obj)
+ if parentJob is None:
+ return
+ baseobject = parentJob.Base
+ if baseobject is None:
+ return
+ planeshape = baseobject.Shape
+
+ #if user wants the boundbox, calculate that
+ 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))
+ else:
+ contourwire = TechDraw.findShapeOutline(planeshape, 1, Vector(0,0,1))
+
+ edgelist = contourwire.Edges
+ edgelist = Part.__sortEdges__(edgelist)
+
+ #use libarea to build the pattern
+ a = area.Area()
+ c = PathScripts.PathKurveUtils.makeAreaCurve(edgelist, 'CW')
+ a.append(c)
+ a.Reorder()
+ output += self.buildpathlibarea(obj, a)
+
+ path = Path.Path(output)
+ obj.Path = path
+ obj.ViewObject.Visibility = True
+
+
+class _CommandSetFaceStartPoint:
+ def GetResources(self):
+ return {'Pixmap': 'Path-StartPoint',
+ 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathFace", "Pick Start Point"),
+ 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathFace", "Pick Start Point")}
+
+ def IsActive(self):
+ return FreeCAD.ActiveDocument is not None
+
+ def setpoint(self, point, o):
+ obj = FreeCADGui.Selection.getSelection()[0]
+ obj.StartPoint.x = point.x
+ obj.StartPoint.y = point.y
+
+ def Activated(self):
+ FreeCADGui.Snapper.getPoint(callback=self.setpoint)
+
+
+class ViewProviderFace:
+
+ def __init__(self, vobj):
+ vobj.Proxy = self
+
+ def attach(self, vobj):
+ self.Object = vobj.Object
+ return
+
+ def setEdit(self, vobj, mode=0):
+ FreeCADGui.Control.closeDialog()
+ taskd = TaskPanel()
+ taskd.obj = vobj.Object
+ taskd.setupUi()
+ FreeCADGui.Control.showDialog(taskd)
+ return True
+
+ def getIcon(self):
+ return ":/icons/Path-Face.svg"
+
+ def __getstate__(self):
+ return None
+
+ def __setstate__(self, state):
+ return None
+
+
+class CommandPathMillFace:
+
+ def GetResources(self):
+ return {'Pixmap': 'Path-Face',
+ 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathFace", "Face"),
+ 'Accel': "P, O",
+ 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathFace", "Create a Facing Operation from a model or face")}
+
+ def IsActive(self):
+ if FreeCAD.ActiveDocument is not None:
+ for o in FreeCAD.ActiveDocument.Objects:
+ if o.Name[:3] == "Job":
+ return True
+ return False
+
+ def Activated(self):
+
+ # zbottom = 0.0
+ ztop = 10.0
+
+ # if everything is ok, execute and register the transaction in the undo/redo stack
+ FreeCAD.ActiveDocument.openTransaction(translate("PathFace", "Create Face"))
+ FreeCADGui.addModule("PathScripts.PathMillFace")
+ FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", "Face")')
+ FreeCADGui.doCommand('PathScripts.PathMillFace.ObjectFace(obj)')
+ FreeCADGui.doCommand('obj.Active = True')
+ FreeCADGui.doCommand('PathScripts.PathMillFace.ViewProviderFace(obj.ViewObject)')
+ FreeCADGui.doCommand('from PathScripts import PathUtils')
+ FreeCADGui.doCommand('obj.StepOver = 50')
+ FreeCADGui.doCommand('obj.ClearanceHeight = 10') # + str(bb.ZMax + 2.0))
+ 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('PathScripts.PathUtils.addToJob(obj)')
+ snippet = '''
+parentJob = PathUtils.findParentJob(obj)
+if parentJob is None:
+ pass
+else:
+ baseobject = parentJob.Base
+ if baseobject is None:
+ pass
+ else:
+ obj.StartDepth = str(baseobject.Shape.BoundBox.ZMax + 1)
+ obj.FinalDepth = str(baseobject.Shape.BoundBox.ZMax)
+'''
+ FreeCADGui.doCommand(snippet)
+
+ FreeCAD.ActiveDocument.commitTransaction()
+
+ FreeCAD.ActiveDocument.recompute()
+ FreeCADGui.doCommand('obj.ViewObject.startEditing()')
+
+
+class TaskPanel:
+ def __init__(self):
+ #self.form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/MillFaceEdit.ui")
+ self.form = FreeCADGui.PySideUic.loadUi(":/panels/MillFaceEdit.ui")
+ self.updating = False
+
+ def accept(self):
+ self.getFields()
+
+ FreeCADGui.ActiveDocument.resetEdit()
+ FreeCADGui.Control.closeDialog()
+ FreeCAD.ActiveDocument.recompute()
+ FreeCADGui.Selection.removeObserver(self.s)
+
+ def reject(self):
+ FreeCADGui.Control.closeDialog()
+ FreeCAD.ActiveDocument.recompute()
+ FreeCADGui.Selection.removeObserver(self.s)
+
+ def getFields(self):
+ if self.obj:
+ if hasattr(self.obj, "StartDepth"):
+ self.obj.StartDepth = self.form.startDepth.text()
+ if hasattr(self.obj, "FinalDepth"):
+ self.obj.FinalDepth = self.form.finalDepth.text()
+ if hasattr(self.obj, "FinishDepth"):
+ self.obj.FinishDepth = self.form.finishDepth.text()
+ if hasattr(self.obj, "SafeHeight"):
+ self.obj.SafeHeight = self.form.safeHeight.text()
+ if hasattr(self.obj, "ClearanceHeight"):
+ self.obj.ClearanceHeight = self.form.clearanceHeight.text()
+ if hasattr(self.obj, "StepDown"):
+ self.obj.StepDown = self.form.stepDown.value()
+ if hasattr(self.obj, "PassExtension"):
+ self.obj.PassExtension = self.form.extraOffset.value()
+ # if hasattr(self.obj, "UseStartPoint"):
+ # self.obj.UseStartPoint = self.form.useStartPoint.isChecked()
+ 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 = self.form.zigZagAngle.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())
+
+ self.obj.Proxy.execute(self.obj)
+
+ def setFields(self):
+ self.form.startDepth.setText(str(self.obj.StartDepth.Value))
+ self.form.finalDepth.setText(str(self.obj.FinalDepth.Value))
+ self.form.finishDepth.setText(str(self.obj.FinishDepth.Value))
+ self.form.stepDown.setValue(self.obj.StepDown)
+ self.form.safeHeight.setText(str(self.obj.SafeHeight.Value))
+ self.form.clearanceHeight.setText(str(self.obj.ClearanceHeight.Value))
+ 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(self.obj.ZigZagAngle)
+ #self.form.useStartPoint.setChecked(self.obj.UseStartPoint)
+ self.form.extraOffset.setValue(self.obj.PassExtension.Value)
+
+ index = self.form.cutMode.findText(
+ self.obj.CutMode, QtCore.Qt.MatchFixedString)
+ if index >=0:
+
+ self.form.cutMode.blockSignals(True)
+ self.form.cutMode.setCurrentIndex(index)
+ self.form.cutMode.blockSignals(False)
+
+ index = self.form.boundaryShape.findText(
+ self.obj.BoundaryShape, QtCore.Qt.MatchFixedString)
+ if index >=0:
+ self.form.boundaryShape.blockSignals(True)
+ self.form.boundaryShape.setCurrentIndex(index)
+ self.form.boundaryShape.blockSignals(False)
+
+
+ for i in self.obj.Base:
+ for sub in i[1]:
+ self.form.baseList.addItem(i[0].Name + "." + sub)
+
+ def open(self):
+ self.s = SelObserver()
+ # install the function mode resident
+ FreeCADGui.Selection.addObserver(self.s)
+
+ def addBase(self):
+ # check that the selection contains exactly what we want
+ selection = FreeCADGui.Selection.getSelectionEx()
+
+ if len(selection) != 1:
+ FreeCAD.Console.PrintError(translate("PathProject", "Please select only faces from one solid\n"))
+ return
+ sel = selection[0]
+ if not sel.HasSubObjects:
+ FreeCAD.Console.PrintError(translate("PathProject", "Please select faces from one solid\n"))
+ return
+ if not selection[0].SubObjects[0].ShapeType == "Face":
+ FreeCAD.Console.PrintError(translate("PathProject", "Please select faces from one solid\n"))
+ return
+ for i in sel.SubElementNames:
+ self.obj.Proxy.addFacebase(self.obj, sel.Object, i)
+
+ self.setFields() # defaults may have changed. Reload.
+ self.form.baseList.clear()
+
+ for i in self.obj.Base:
+ for sub in i[1]:
+ self.form.baseList.addItem(i[0].Name + "." + sub)
+
+ def deleteBase(self):
+ dlist = self.form.baseList.selectedItems()
+ newlist = []
+ for d in dlist:
+ deletebase = d.text().partition(".")[0]
+ deletesub = d.text().partition(".")[2]
+
+ for i in self.obj.Base:
+ sublist = []
+ #baseobj = i[0]
+ basesubs = i[1]
+ for sub in basesubs:
+ if sub != deletesub:
+ sublist.append(sub)
+ if len(sublist) >= 1:
+ newlist.append ((deletebase, tuple(sublist)))
+
+ if i[0].Name != d.text().partition(".")[0] and d.text().partition(".")[2] not in i[1]:
+ newlist.append(i)
+ self.form.baseList.takeItem(self.form.baseList.row(d))
+ self.obj.Base = newlist
+ self.obj.Proxy.execute(self.obj)
+ FreeCAD.ActiveDocument.recompute()
+
+ def itemActivated(self):
+ print self.form.baseList.selectedItems()[0].text()
+ FreeCADGui.Selection.clearSelection()
+ slist = self.form.baseList.selectedItems()
+ for i in slist:
+ objstring = i.text().partition(".")
+ obj = FreeCAD.ActiveDocument.getObject(objstring[0])
+ if objstring[2] != "":
+ FreeCADGui.Selection.addSelection(obj, objstring[2])
+ else:
+ FreeCADGui.Selection.addSelection(obj)
+
+ FreeCADGui.updateGui()
+
+ def reorderBase(self):
+ newlist = []
+ for i in range(self.form.baseList.count()):
+ s = self.form.baseList.item(i).text()
+ objstring = s.partition(".")
+
+ obj = FreeCAD.ActiveDocument.getObject(objstring[0])
+ item = (obj, str(objstring[2]))
+ newlist.append(item)
+ self.obj.Base = newlist
+
+ self.obj.Proxy.execute(self.obj)
+ FreeCAD.ActiveDocument.recompute()
+
+ def getStandardButtons(self):
+ return int(QtGui.QDialogButtonBox.Ok)
+
+ def edit(self, item, column):
+ if not self.updating:
+ self.resetObject()
+
+ def resetObject(self, remove=None):
+ "transfers the values from the widget to the object"
+
+ self.obj.touch()
+ FreeCAD.ActiveDocument.recompute()
+
+ def setupUi(self):
+
+ # Connect Signals and Slots
+ # Base Controls
+ self.form.baseList.itemSelectionChanged.connect(self.itemActivated)
+ self.form.addBase.clicked.connect(self.addBase)
+ self.form.deleteBase.clicked.connect(self.deleteBase)
+ self.form.reorderBase.clicked.connect(self.reorderBase)
+
+ # Depths
+ self.form.startDepth.editingFinished.connect(self.getFields)
+ self.form.finalDepth.editingFinished.connect(self.getFields)
+ self.form.finishDepth.editingFinished.connect(self.getFields)
+ self.form.stepDown.editingFinished.connect(self.getFields)
+
+ # Heights
+ self.form.safeHeight.editingFinished.connect(self.getFields)
+ self.form.clearanceHeight.editingFinished.connect(self.getFields)
+
+ # operation
+ self.form.cutMode.currentIndexChanged.connect(self.getFields)
+ #self.form.useStartPoint.clicked.connect(self.getFields)
+ 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.zigZagAngle.editingFinished.connect(self.getFields)
+
+ self.setFields()
+
+ sel = FreeCADGui.Selection.getSelectionEx()
+ if len(sel) != 0 and sel[0].HasSubObjects:
+ self.addBase()
+
+
+class SelObserver:
+ def __init__(self):
+ import PathScripts.PathSelection as PST
+ PST.pocketselect()
+
+ def __del__(self):
+ import PathScripts.PathSelection as PST
+ PST.clear()
+
+ def addSelection(self, doc, obj, sub, pnt):
+ FreeCADGui.doCommand('Gui.Selection.addSelection(FreeCAD.ActiveDocument.' + obj + ')')
+ FreeCADGui.updateGui()
+
+if FreeCAD.GuiUp:
+ # register the FreeCAD command
+ FreeCADGui.addCommand('Path_MillFace', CommandPathMillFace())
+ FreeCADGui.addCommand('Set_FaceStartPoint', _CommandSetFaceStartPoint())
+
+
+FreeCAD.Console.PrintLog("Loading PathMillFace... done\n")
diff --git a/src/Mod/Path/PathScripts/PathProfile.py b/src/Mod/Path/PathScripts/PathProfile.py
index 12d14f6fa9..72e04326e2 100644
--- a/src/Mod/Path/PathScripts/PathProfile.py
+++ b/src/Mod/Path/PathScripts/PathProfile.py
@@ -25,6 +25,7 @@
import FreeCAD
import Path
import numpy
+import TechDraw
from FreeCAD import Vector
from PathScripts import PathUtils
from PathScripts.PathUtils import depth_params
@@ -58,14 +59,11 @@ class ObjectProfile:
obj.addProperty("App::PropertyString", "Comment", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property","An optional comment for this profile"))
obj.addProperty("App::PropertyString", "UserLabel", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property","User Assigned Label"))
- obj.addProperty("App::PropertyEnumeration", "Algorithm", "Algorithm", QtCore.QT_TRANSLATE_NOOP("App::Property","The library or algorithm used to generate the path"))
- obj.Algorithm = ['OCC Native', 'libarea']
-
obj.addProperty("App::PropertyIntegerConstraint", "ToolNumber", "Tool", "The tool number in use")
obj.ToolNumber = (0, 0, 1000, 1)
obj.setEditorMode('ToolNumber', 1) # make this read only
obj.addProperty("App::PropertyString", "ToolDescription", "Tool", "The description of the tool ")
- obj.setEditorMode('ToolDescription', 1) # make this read onlyt
+ obj.setEditorMode('ToolDescription', 1) # make this read only
# Depth Properties
obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property","The height needed to clear clamps and obstructions"))
@@ -98,7 +96,8 @@ class ObjectProfile:
obj.addProperty("App::PropertyDistance", "OffsetExtra", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property","Extra value to stay away from final profile- good for roughing toolpath"))
obj.addProperty("App::PropertyLength", "SegLen", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property","Tesselation value for tool paths made from beziers, bsplines, and ellipses"))
obj.addProperty("App::PropertyAngle", "PlungeAngle", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property","Plunge angle with which the tool enters the work piece. Straight down is 90 degrees, if set small enough or zero the tool will descent exactly one layer depth down per turn"))
- obj.addProperty("App::PropertyBool", "processHoles", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property","Handl holes as well as the outline"))
+ obj.addProperty("App::PropertyBool", "processHoles", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property","Profile holes as well as the outline"))
+ obj.addProperty("App::PropertyBool", "processPerimeter", "Profile", QtCore.QT_TRANSLATE_NOOP("App::Property","Profile the outline"))
obj.addProperty("App::PropertyVectorList", "locs", "Tags", QtCore.QT_TRANSLATE_NOOP("App::Property","List of holding tag locations"))
@@ -114,7 +113,6 @@ class ObjectProfile:
obj.angles = angles
obj.lengths = lengths
obj.heights = heights
- #obj.ToolDescription = "UNDEFINED"
obj.Proxy = self
@@ -167,29 +165,6 @@ class ObjectProfile:
obj.Base = baselist
self.execute(obj)
- def _buildPathOCC(self, obj, wire):
- import DraftGeomUtils
- output = ""
- if obj.Comment != "":
- output += '(' + str(obj.Comment)+')\n'
-
- if obj.Direction == 'CCW':
- clockwise = False
- else:
- clockwise = True
-
- FirstEdge = None
- PathClosed = DraftGeomUtils.isReallyClosed(wire)
-
- output += PathUtils.MakePath(
- wire, obj.Side, self.radius, clockwise,
- obj.ClearanceHeight.Value, obj.StepDown, obj.StartDepth.Value,
- obj.FinalDepth.Value, FirstEdge, PathClosed, obj.SegLen.Value,
- self.vertFeed, self.horizFeed, self.vertRapid, self.horizRapid,
- PlungeAngle=obj.PlungeAngle.Value)
-
- return output
-
def _buildPathLibarea(self, obj, edgelist, isHole):
import PathScripts.PathKurveUtils as PathKurveUtils
import math
@@ -228,7 +203,6 @@ print "y - " + str(point.y)
start location
CRC
or probably other features in heekscnc'''
- # output += PathKurveUtils.profile(curve, side, radius, vf, hf, offset_extra, rapid_safety_space, clearance, start_depth, step_down, final_depth, use_CRC)
'''The following calls the original procedure from h
toolLoad = obj.activeTCeekscnc profile function. This, in turn, calls many other procedures to modify the profile.
@@ -273,8 +247,6 @@ print "y - " + str(point.y)
output = ""
toolLoad = PathUtils.getLastToolLoad(obj)
- # obj.ToolController = PathUtils.getToolControllers(obj)
- # toolLoad = PathUtils.getToolLoad(obj, obj.ToolController)
if toolLoad is None or toolLoad.ToolNumber == 0:
self.vertFeed = 100
@@ -309,62 +281,32 @@ print "y - " + str(point.y)
output += "(Uncompensated Tool Path)"
if obj.Base:
- hfaces = []
- vfaces = []
- wires = []
holes = []
-
+ faces = []
for b in obj.Base:
for sub in b[1]:
- # we only consider the outer wire if this is a Face
- # Horizontal and vertical faces are handled differently
shape = getattr(b[0].Shape, sub)
- if numpy.isclose(abs(shape.normalAt(0, 0).z), 1): # horizontal face
- hfaces.append(shape)
+ if isinstance (shape, Part.Face):
+ faces.append(shape)
+ if numpy.isclose(abs(shape.normalAt(0, 0).z), 1): # horizontal face
+ holes += shape.Wires[1:]
- elif numpy.isclose(shape.normalAt(0, 0).z, 0): # vertical face
- vfaces.append(shape)
else:
- FreeCAD.Console.PrintError(translate("Path", "Face doesn't appear to be parallel or perpendicular to the XY plane. No path will be generated for: \n"))
- FreeCAD.Console.PrintError(b[0].Name + "." + sub + "\n")
+ print ("found a base object which is not a face. Can't continue.")
+ return
+ profileshape = Part.makeCompound(faces)
+ profilewire = TechDraw.findShapeOutline(profileshape, 1, Vector(0,0,1))
if obj.processHoles:
- for h in hfaces:
- holes += h.Wires[1:]
-
- for h in hfaces:
- wires.append(h.OuterWire)
-
- tempshell = Part.makeShell(vfaces)
- slices = tempshell.slice(FreeCAD.Base.Vector(0, 0, 1), tempshell.CenterOfMass.z )
-
- wires = wires + slices
-
- for wire in holes:
- if obj.Algorithm == "OCC Native":
- output += self._buildPathOCC(obj, wire)
- else:
- try:
- import area
- except:
- FreeCAD.Console.PrintError(translate("Path", "libarea needs to be installed for this command to work.\n"))
- return
+ for wire in holes:
edgelist = wire.Edges
edgelist = Part.__sortEdges__(edgelist)
output += self._buildPathLibarea(obj, edgelist, True)
-
- for wire in wires:
- if obj.Algorithm == "OCC Native":
- output += self._buildPathOCC(obj, wire)
- else:
- try:
- import area
- except:
- FreeCAD.Console.PrintError(translate("Path", "libarea needs to be installed for this command to work.\n"))
- return
- edgelist = wire.Edges
- edgelist = Part.__sortEdges__(edgelist)
- output += self._buildPathLibarea(obj, edgelist, False)
+
+ if obj.processPerimeter:
+ edgelist = profilewire.Edges
+ edgelist = Part.__sortEdges__(edgelist)
+ output += self._buildPathLibarea(obj, edgelist, False)
if obj.Active:
path = Path.Path(output)
@@ -500,12 +442,9 @@ class CommandPathProfile:
FreeCADGui.addModule("PathScripts.PathProfile")
FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", "Profile")')
FreeCADGui.doCommand('PathScripts.PathProfile.ObjectProfile(obj)')
- FreeCADGui.doCommand('PathScripts.PathProfile._ViewProviderProfile(obj.ViewObject)')
FreeCADGui.doCommand('obj.Active = True')
- FreeCADGui.doCommand('obj.Algorithm = "libarea"')
-
FreeCADGui.doCommand('obj.ClearanceHeight = ' + str(ztop + 10.0))
FreeCADGui.doCommand('obj.StepDown = 1.0')
FreeCADGui.doCommand('obj.StartDepth= ' + str(ztop))
@@ -514,11 +453,12 @@ class CommandPathProfile:
FreeCADGui.doCommand('obj.SafeHeight = ' + str(ztop + 2.0))
FreeCADGui.doCommand('obj.Side = "Left"')
FreeCADGui.doCommand('obj.OffsetExtra = 0.0')
- FreeCADGui.doCommand('obj.Direction = "CW"')
+ FreeCADGui.doCommand('obj.Direction = "CCW"')
FreeCADGui.doCommand('obj.UseComp = False')
- FreeCADGui.doCommand('obj.processHoles = False')
+ FreeCADGui.doCommand('obj.processHoles = False')
+ FreeCADGui.doCommand('obj.processPerimeter = True')
FreeCADGui.doCommand('obj.PlungeAngle = 90.0')
- #FreeCADGui.doCommand('obj.ActiveTC = None')
+ FreeCADGui.doCommand('PathScripts.PathProfile._ViewProviderProfile(obj.ViewObject)')
FreeCADGui.doCommand('PathScripts.PathUtils.addToJob(obj)')
FreeCAD.ActiveDocument.commitTransaction()
@@ -570,14 +510,14 @@ class TaskPanel:
self.obj.UseStartPoint = self.form.useStartPoint.isChecked()
if hasattr(self.obj, "UseEndPoint"):
self.obj.UseEndPoint = self.form.useEndPoint.isChecked()
- if hasattr(self.obj, "Algorithm"):
- self.obj.Algorithm = str(self.form.algorithmSelect.currentText())
if hasattr(self.obj, "Side"):
self.obj.Side = str(self.form.cutSide.currentText())
if hasattr(self.obj, "Direction"):
self.obj.Direction = str(self.form.direction.currentText())
if hasattr(self.obj, "processHoles"):
self.obj.processHoles = self.form.processHoles.isChecked()
+ if hasattr(self.obj, "processPerimeter"):
+ self.obj.processPerimeter = self.form.processPerimeter.isChecked()
self.obj.Proxy.execute(self.obj)
def setFields(self):
@@ -594,13 +534,7 @@ class TaskPanel:
self.form.useStartPoint.setChecked(self.obj.UseStartPoint)
self.form.useEndPoint.setChecked(self.obj.UseEndPoint)
self.form.processHoles.setChecked(self.obj.processHoles)
-
- index = self.form.algorithmSelect.findText(
- self.obj.Algorithm, QtCore.Qt.MatchFixedString)
- if index >= 0:
- self.form.algorithmSelect.blockSignals(True)
- self.form.algorithmSelect.setCurrentIndex(index)
- self.form.algorithmSelect.blockSignals(False)
+ self.form.processPerimeter.setChecked(self.obj.processPerimeter)
index = self.form.cutSide.findText(
self.obj.Side, QtCore.Qt.MatchFixedString)
@@ -616,10 +550,13 @@ class TaskPanel:
self.form.direction.setCurrentIndex(index)
self.form.direction.blockSignals(False)
+ self.form.baseList.blockSignals(True)
for i in self.obj.Base:
for sub in i[1]:
self.form.baseList.addItem(i[0].Name + "." + sub)
+ self.form.baseList.blockSignals(False)
+ self.form.tagTree.blockSignals(True)
for i in range(len(self.obj.locs)):
item = QtGui.QTreeWidgetItem(self.form.tagTree)
item.setText(0, str(i+1))
@@ -630,6 +567,9 @@ class TaskPanel:
item.setText(4, str(self.obj.angles[i]))
item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable)
item.setTextAlignment(0, QtCore.Qt.AlignLeft)
+ self.form.tagTree.blockSignals(False)
+ self.form.update()
+
def open(self):
self.s = SelObserver()
@@ -807,7 +747,6 @@ class TaskPanel:
self.form.clearanceHeight.editingFinished.connect(self.getFields)
# operation
- self.form.algorithmSelect.currentIndexChanged.connect(self.getFields)
self.form.cutSide.currentIndexChanged.connect(self.getFields)
self.form.direction.currentIndexChanged.connect(self.getFields)
self.form.useCompensation.clicked.connect(self.getFields)
@@ -817,6 +756,7 @@ class TaskPanel:
self.form.segLen.editingFinished.connect(self.getFields)
self.form.rollRadius.editingFinished.connect(self.getFields)
self.form.processHoles.clicked.connect(self.getFields)
+ self.form.processPerimeter.clicked.connect(self.getFields)
# Tag Form
QtCore.QObject.connect(
diff --git a/src/Mod/Path/PathScripts/PathProfileEdges.py b/src/Mod/Path/PathScripts/PathProfileEdges.py
index cd13dcf9b6..a56a21440e 100644
--- a/src/Mod/Path/PathScripts/PathProfileEdges.py
+++ b/src/Mod/Path/PathScripts/PathProfileEdges.py
@@ -528,11 +528,13 @@ class TaskPanel:
self.obj.Direction, QtCore.Qt.MatchFixedString)
if index >= 0:
self.form.direction.setCurrentIndex(index)
-
+ self.form.baseList.blockSignals(True)
for i in self.obj.Base:
for sub in i[1]:
self.form.baseList.addItem(i[0].Name + "." + sub)
+ self.form.baseList.blockSignals(False)
+ self.form.tagTree.blockSignals(True)
for i in range(len(self.obj.locs)):
item = QtGui.QTreeWidgetItem(self.form.tagTree)
item.setText(0, str(i+1))
@@ -543,6 +545,8 @@ class TaskPanel:
item.setText(4, str(self.obj.angles[i]))
item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable)
item.setTextAlignment(0, QtCore.Qt.AlignLeft)
+ self.form.tagTree.blockSignals(False)
+
def open(self):
self.s = SelObserver()
diff --git a/src/Mod/Path/PathScripts/PathSelection.py b/src/Mod/Path/PathScripts/PathSelection.py
index a2e3fb4556..a6566e9258 100644
--- a/src/Mod/Path/PathScripts/PathSelection.py
+++ b/src/Mod/Path/PathScripts/PathSelection.py
@@ -100,7 +100,7 @@ class DRILLGate:
return False
if obj.ShapeType == 'Vertex':
drillable = True
- elif obj.ShapeType == 'Solid':
+ elif obj.ShapeType in['Solid', 'Compound']:
if sub[0:4] == 'Face':
subobj = obj.getElement(sub)
drillable = isinstance(subobj.Edges[0].Curve, Part.Circle)
diff --git a/src/Mod/Path/PathScripts/PathUtils.py b/src/Mod/Path/PathScripts/PathUtils.py
index 62e8fd08eb..c1e7cce99f 100644
--- a/src/Mod/Path/PathScripts/PathUtils.py
+++ b/src/Mod/Path/PathScripts/PathUtils.py
@@ -84,8 +84,26 @@ def curvetowire(obj, steps):
# fixme set at 4 decimal places for testing
def fmt(val): return format(val, '.4f')
+
+def getProjected(shape,direction):
+ "returns projected edges from a shape and a direction"
+ import Part,Drawing
+ edges = []
+ groups = Drawing.projectEx(shape,direction)
+ for g in groups[0:5]:
+ if g:
+ edges.append(g)
+ # if hasattr(obj,"Tessellation") and obj.Tessellation:
+ # return DraftGeomUtils.cleanProjection(Part.makeCompound(edges),obj.Tessellation,obj.SegmentLength)
+ # else:
+ return Part.makeCompound(edges)
+
+
def silhouette(obj):
- w = TechDraw.findOuterWire(obj.Shape.Edges)
+ from FreeCAD import Vector
+ s = getProjected(obj.Shape, Vector(0,0,1))
+ print s
+ w = TechDraw.findOuterWire(s.Edges)
return w
def isSameEdge(e1, e2):
@@ -591,7 +609,9 @@ 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)
r = form.exec_()