diff --git a/src/Mod/Path/Gui/Resources/panels/PageOpPocketExtEdit.ui b/src/Mod/Path/Gui/Resources/panels/PageOpPocketExtEdit.ui
index 828bbb3e91..8787ec7dae 100644
--- a/src/Mod/Path/Gui/Resources/panels/PageOpPocketExtEdit.ui
+++ b/src/Mod/Path/Gui/Resources/panels/PageOpPocketExtEdit.ui
@@ -22,10 +22,7 @@
-
-
-
- QFormLayout::AllNonFixedFieldsGrow
-
+
-
@@ -33,6 +30,16 @@
+ -
+
+
+ Extend Corners
+
+
+ true
+
+
+
-
@@ -41,16 +48,13 @@
999999999.000000000000000
-
- 1.000000000000000
-
-
-
+
QAbstractItemView::NoEditTriggers
diff --git a/src/Mod/Path/PathScripts/PathPocketShape.py b/src/Mod/Path/PathScripts/PathPocketShape.py
index f1dc0b5ba9..51da1ca4b2 100644
--- a/src/Mod/Path/PathScripts/PathPocketShape.py
+++ b/src/Mod/Path/PathScripts/PathPocketShape.py
@@ -72,27 +72,25 @@ def includesPoint(p, pts):
return True
return False
-def selectOffsetWire(wire, wires, offset):
- '''selectOffsetWire(wire, wires, offset) ... returns the Wire in wires which most likely is wire offset by offset'''
- startPoint = endPoints(wire)[0] + offset;
+def selectOffsetWire(feature, wires):
+ '''selectOffsetWire(feature, wires) ... returns the Wire in wires which is does not intersect with feature'''
closest = None
for w in wires:
- for ep in endPoints(w):
- dist = (startPoint - ep).Length
- if closest is None or dist < closest[0]:
- closest = (dist, w)
+ dist = feature.distToShape(w)[0]
+ if closest is None or dist > closest[0]:
+ closest = (dist, w)
if not closest is None:
return closest[1]
return None
-def extendWire(wire, length, direction):
- '''extendWire(wire, length, direction) ... return a closed Wire which extends wire by length into direction'''
+def extendWire(feature, wire, length):
+ '''extendWire(wire, length) ... return a closed Wire which extends wire by length'''
off2D = wire.makeOffset2D(length)
endPts = endPoints(wire)
edges = [e for e in off2D.Edges if Part.Circle != type(e.Curve) or not includesPoint(e.Curve.Center, endPts)]
wires = [Part.Wire(e) for e in Part.sortEdges(edges)]
- offset = selectOffsetWire(wire, wires, direction * length)
+ offset = selectOffsetWire(feature, wires)
ePts = endPoints(offset)
l0 = (ePts[0] - endPts[0]).Length
l1 = (ePts[1] - endPts[0]).Length
@@ -112,13 +110,17 @@ class Extension(object):
DirectionX = 1
DirectionY = 2
- def __init__(self, obj, sub, length, direction):
+ def __init__(self, obj, feature, sub, length, direction):
self.obj = obj
+ self.feature = feature
self.sub = sub
self.length = length
self.direction = direction
- def extendEdge(self, e0, direction):
+ def getSubLink(self):
+ return "%s:%s" % (self.feature, self.sub)
+
+ def extendEdge(self, feature, e0, direction):
if Part.Line == type(e0.Curve) or Part.LineSegment == type(e0.Curve):
e2 = e0.copy()
off = self.length.Value * direction
@@ -129,26 +131,46 @@ class Extension(object):
wire = Part.Wire([e0, e1, e2, e3])
self.wire = wire
return wire
- return extendWire(Part.Wire([e0]), self.length.Value, direction)
+ return extendWire(feature, Part.Wire([e0]), self.length.Value)
+
+ def getEdgeNumbers(self):
+ if 'Wire' in self.sub:
+ return [nr for nr in self.sub[5:-1].split(',')]
+ return [self.sub[4:]]
+
+ def getEdgeNames(self):
+ return ["Edge%s" % nr for nr in self.getEdgeNumbers()]
+
+ def getEdges(self):
+ return [self.obj.Shape.getElement(sub) for sub in self.getEdgeNames()]
+
+ def getDirection(self, wire):
+ e0 = wire.Edges[0]
+ midparam = e0.FirstParameter + 0.5 * (e0.LastParameter - e0.FirstParameter)
+ tangent = e0.tangentAt(midparam)
+ normal = tangent.cross(FreeCAD.Vector(0, 0, 1)).normalize()
+ poffPlus = e0.valueAt(midparam) + 0.01 * normal
+ poffMinus = e0.valueAt(midparam) - 0.01 * normal
+ if not self.obj.Shape.isInside(poffPlus, 0.005, True):
+ return normal
+ if not self.obj.Shape.isInside(poffMinus, 0.005, True):
+ return normal.negative()
+ return None
def getWire(self):
- if PathGeom.isRoughly(0, self.length.Value):
+ if PathGeom.isRoughly(0, self.length.Value) or not self.sub:
return None
- feature = self.obj.Shape.getElement(self.sub)
- if Part.Edge == type(feature):
- e0 = feature
- midparam = e0.FirstParameter + 0.5 * (e0.LastParameter - e0.FirstParameter)
- tangent = e0.tangentAt(midparam)
- normal = tangent.cross(FreeCAD.Vector(0, 0, 1)).normalize()
- poffPlus = e0.valueAt(midparam) + 0.01 * normal
- poffMinus = e0.valueAt(midparam) - 0.01 * normal
- if not self.obj.Shape.isInside(poffPlus, 0.005, True):
- return self.extendEdge(e0, normal)
- if not self.obj.Shape.isInside(poffMinus, 0.005, True):
- return self.extendEdge(e0, normal.negative())
- else:
- PathLog.warning("getWire does not support %s" % type(feature))
+ feature = self.obj.Shape.getElement(self.feature)
+ edges = self.getEdges()
+ sub = Part.Wire(edges)
+
+ if 1 == len(edges):
+ direction = self.getDirection(sub)
+ if direction is None:
+ return None
+ return self.extendEdge(feature, edges[0], direction)
+ return extendWire(feature, sub, self.length.Value)
class ObjectPocket(PathPocketBase.ObjectPocket):
@@ -166,14 +188,8 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
obj.addProperty('App::PropertyDistance', 'ExtensionLengthDefault', 'Extension', QtCore.QT_TRANSLATE_NOOP('PathPocketShape', 'Default length of extensions.'))
if not hasattr(obj, 'ExtensionFeature'):
obj.addProperty('App::PropertyLinkSubListGlobal', 'ExtensionFeature', 'Extension', QtCore.QT_TRANSLATE_NOOP('PathPocketShape', 'List of features to extend.'))
- if not hasattr(obj, 'ExtensionLength'):
- obj.addProperty('App::PropertyFloatList', 'ExtensionLength', 'Extension', QtCore.QT_TRANSLATE_NOOP('PathPocketShape', 'List of extension lenght of corresponding feature.'))
- if not hasattr(obj, 'ExtensionDirection'):
- obj.addProperty('App::PropertyIntegerList', 'ExtensionDirection', 'Extension', QtCore.QT_TRANSLATE_NOOP('PathPocketShape', 'List of extension direction of corresponding feature.'))
obj.setEditorMode('ExtensionFeature', 2)
- obj.setEditorMode('ExtensionLength', 2)
- obj.setEditorMode('ExtensionDirection', 2)
def opOnDocumentRestored(self, obj):
'''opOnDocumentRestored(obj) ... adds the UseOutline property if it doesn't exist.'''
@@ -285,26 +301,22 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
obj.OpStartDepth = bb.ZMax
obj.ExtensionLengthDefault = obj.OpToolDiameter / 2
- def createExtension(self, obj, extObj, extSub):
- return Extension(extObj, extSub, obj.ExtensionLengthDefault, Extension.DirectionNormal)
+ def createExtension(self, obj, extObj, extFeature, extSub):
+ return Extension(extObj, extFeature, extSub, obj.ExtensionLengthDefault, Extension.DirectionNormal)
def getExtensions(self, obj):
extensions = []
i = 0
for extObj,features in obj.ExtensionFeature:
- for extSub in features:
- extensions.append(self.createExtension(obj, extObj, extSub))
+ for sub in features:
+ extFeature, extSub = sub.split(':')
+ extensions.append(self.createExtension(obj, extObj, extFeature, extSub))
i = i + 1
return extensions
def setExtensions(self, obj, extensions):
PathLog.track(obj.Label, len(extensions))
- features = {}
- for ext in extensions:
- subs = features.get(ext.obj, [])
- subs.append(ext.sub)
- features[ext.obj] = subs
- obj.ExtensionFeature = [(ext.obj, ext.sub) for ext in extensions]
+ obj.ExtensionFeature = [(ext.obj, ext.getSubLink()) for ext in extensions]
def SetupProperties():
return PathPocketBase.SetupProperties() + [ 'UseOutline' ]
diff --git a/src/Mod/Path/PathScripts/PathPocketShapeGui.py b/src/Mod/Path/PathScripts/PathPocketShapeGui.py
index 5cb6457e09..40103e0c04 100644
--- a/src/Mod/Path/PathScripts/PathPocketShapeGui.py
+++ b/src/Mod/Path/PathScripts/PathPocketShapeGui.py
@@ -24,6 +24,8 @@
import FreeCAD
import FreeCADGui
+import Part
+import PathScripts.PathGeom as PathGeom
import PathScripts.PathGui as PathGui
import PathScripts.PathLog as PathLog
import PathScripts.PathOp as PathOp
@@ -55,13 +57,13 @@ def createExtensionSoSwitch(ext):
mat = coin.SoMaterial()
crd = coin.SoCoordinate3()
fce = coin.SoFaceSet()
+ hnt = coin.SoShapeHints()
if not ext is None:
wire = ext.getWire()
if wire:
- polygon = []
- for p in wire.discretize(Deflection=0.01):
- polygon.append((p.x, p.y, p.z))
+ poly = [p for p in wire.discretize(Deflection=0.01)][:-1]
+ polygon = [(p.x, p.y, p.z) for p in poly]
crd.point.setValues(polygon)
else:
return None
@@ -69,8 +71,12 @@ def createExtensionSoSwitch(ext):
mat.diffuseColor = (1.0, 0.0, 0.0)
mat.transparency = 0.5
+ hnt.faceType = coin.SoShapeHints.UNKNOWN_FACE_TYPE
+ hnt.vertexOrdering = coin.SoShapeHints.CLOCKWISE
+
sep.addChild(pos)
sep.addChild(mat)
+ sep.addChild(hnt)
sep.addChild(crd)
sep.addChild(fce)
@@ -82,19 +88,30 @@ def createExtensionSoSwitch(ext):
class _Extension(object):
def __init__(self, obj, base, face, edge):
+ self.obj = obj
self.base = base
self.face = face
self.edge = edge
if edge is None:
self.ext = None
else:
- self.ext = obj.Proxy.createExtension(obj, base, edge)
+ self.ext = obj.Proxy.createExtension(obj, base, face, edge)
self.switch = createExtensionSoSwitch(self.ext)
self.root = self.switch
def isValid(self):
return not self.root is None
+ def getEdgeNumbers(self):
+ return self.ext.getEdgeNumbers()
+
+ def getEdges(self):
+ return self.ext.getEdges()
+
+ def isWire(self):
+ return 1 == len(self.getEdgeNumbers())
+
+
Page = None
class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
@@ -113,14 +130,14 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
self.defaultLength = PathGui.QuantitySpinBox(self.form.defaultLength, obj, 'ExtensionLengthDefault')
- self.form.extensions.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
- self.form.extensions.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
+ self.form.extensionTree.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
+ self.form.extensionTree.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
self.switch = coin.SoSwitch()
self.obj.ViewObject.RootNode.addChild(self.switch)
self.switch.whichChild = coin.SO_SWITCH_ALL
- self.model = QtGui.QStandardItemModel(self.form.extensions)
+ self.model = QtGui.QStandardItemModel(self.form.extensionTree)
self.model.setHorizontalHeaderLabels(['Base', 'Extension'])
global Page
@@ -143,7 +160,9 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
def getFields(self, obj):
PathLog.track(obj.Label, self.model.rowCount(), self.model.columnCount())
+
self.defaultLength.updateProperty()
+
extensions = []
def extractExtension(item):
@@ -158,7 +177,9 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
def setFields(self, obj):
PathLog.track(obj.Label)
+
self.defaultLength.updateSpinBox()
+ self.extensions = obj.Proxy.getExtensions(obj)
self.setExtensions(self.extensions)
def createItemForBaseModel(self, base, sub, edges, extensions):
@@ -169,28 +190,50 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
item.setData(ext, self.DataObject)
item.setSelectable(False)
+ extendCorners = self.form.extendCorners.isChecked()
+
+ def createSubItem(label, ext0):
+ self.switch.addChild(ext0.root)
+ item0 = QtGui.QStandardItem()
+ item0.setData(label, QtCore.Qt.EditRole)
+ item0.setData(ext0, self.DataObject)
+ item0.setCheckable(True)
+ for e in extensions:
+ if e.obj == base and e.sub == label:
+ item0.setCheckState(QtCore.Qt.Checked)
+ break
+ item.appendRow([item0])
+
+ extensionEdges = {}
for edge in base.Shape.getElement(sub).Edges:
for (e, label) in edges:
if edge.isSame(e):
ext0 = _Extension(self.obj, base, sub, label)
if ext0.isValid():
- self.switch.addChild(ext0.root)
- item0 = QtGui.QStandardItem()
- item0.setData(label, QtCore.Qt.EditRole)
- item0.setData(ext0, self.DataObject)
- item0.setCheckable(True)
- for e in extensions:
- if e.obj == base and e.sub == label:
- item0.setCheckState(QtCore.Qt.Checked)
- break
- item.appendRow([item0])
+ extensionEdges[e] = label[4:]
+ if not extendCorners:
+ createSubItem(label, ext0)
break
+ if extendCorners:
+ def edgesMatchShape(e0, e1):
+ return PathGeom.edgesMatch(e0, e1) or PathGeom.edgesMatch(e0, PathGeom.flipEdge(e1))
+
+ self.extensionEdges = extensionEdges
+ for edgeList in Part.sortEdges(extensionEdges.keys()):
+ self.edgeList = edgeList
+ if len(edgeList) == 1:
+ label = "Edge%s" % [extensionEdges[keyEdge] for keyEdge in extensionEdges.keys() if edgesMatchShape(keyEdge, edgeList[0])][0]
+ else:
+ label = "Wire(%s)" % ','.join(sorted([extensionEdges[keyEdge] for e in edgeList for keyEdge in extensionEdges.keys() if edgesMatchShape(e, keyEdge)], key=lambda s: int(s)))
+ ext0 = _Extension(self.obj, base, sub, label)
+ createSubItem(label, ext0)
+
return item
def setExtensions(self, extensions):
PathLog.track(len(extensions))
- self.form.extensions.blockSignals(True)
+ self.form.extensionTree.blockSignals(True)
# remember current visual state
if hasattr(self, 'selectionModel'):
@@ -202,11 +245,11 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
for modelRow in range(self.model.rowCount()):
model = self.model.item(modelRow, 0)
modelName = model.data(QtCore.Qt.EditRole)
- if not self.form.extensions.isExpanded(model.index()):
+ if not self.form.extensionTree.isExpanded(model.index()):
collapsedModels.append(modelName)
for featureRow in range(model.rowCount()):
feature = model.child(featureRow, 0);
- if not self.form.extensions.isExpanded(feature.index()):
+ if not self.form.extensionTree.isExpanded(feature.index()):
collapsedFeatures.append("%s.%s" % (modelName, feature.data(QtCore.Qt.EditRole)))
# remove current extensions and all their visuals
@@ -227,21 +270,21 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
baseItem.appendRow(self.createItemForBaseModel(base[0], sub, edges, extensions))
self.model.appendRow(baseItem)
- self.form.extensions.setModel(self.model)
- self.form.extensions.expandAll()
- self.form.extensions.resizeColumnToContents(0)
+ self.form.extensionTree.setModel(self.model)
+ self.form.extensionTree.expandAll()
+ self.form.extensionTree.resizeColumnToContents(0)
# restore previous state - at least the parts that are still valid
for modelRow in range(self.model.rowCount()):
model = self.model.item(modelRow, 0)
modelName = model.data(QtCore.Qt.EditRole)
if modelName in collapsedModels:
- self.form.extensions.setExpanded(model.index(), False)
+ self.form.extensionTree.setExpanded(model.index(), False)
for featureRow in range(model.rowCount()):
feature = model.child(featureRow, 0);
featureName = "%s.%s" % (modelName, feature.data(QtCore.Qt.EditRole))
if featureName in collapsedFeatures:
- self.form.extensions.setExpanded(feature.index(), False)
+ self.form.extensionTree.setExpanded(feature.index(), False)
if hasattr(self, 'selectionModel') and selectedExtensions:
self.restoreSelection(selectedExtensions)
@@ -309,16 +352,19 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
def getSignalsForUpdate(self, obj):
PathLog.track(obj.Label)
- return [self.form.defaultLength.editingFinished]
+ signals = []
+ signals.append(self.form.defaultLength.editingFinished)
+ return signals
def registerSignalHandlers(self, obj):
+ self.form.extendCorners.clicked.connect(lambda : self.setExtensions(obj.Proxy.getExtensions(obj)))
self.form.buttonClear.clicked.connect(self.extensionsClear)
self.form.buttonDisable.clicked.connect(self.extensionsDisable)
self.form.buttonEnable.clicked.connect(self.extensionsEnable)
self.model.itemChanged.connect(lambda x: self.setDirty())
- self.selectionModel = self.form.extensions.selectionModel()
+ self.selectionModel = self.form.extensionTree.selectionModel()
self.selectionModel.selectionChanged.connect(self.selectionChanged)
class TaskPanelOpPage(PathPocketBaseGui.TaskPanelOpPage):