Added support for global extend corners

This commit is contained in:
Markus Lampert
2018-12-28 03:42:47 -08:00
committed by wmayer
parent dcbe97144b
commit 0367c28fbc
3 changed files with 141 additions and 79 deletions

View File

@@ -22,10 +22,7 @@
</property>
<item>
<widget class="QWidget" name="widget_2" native="true">
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
@@ -33,6 +30,16 @@
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="extendCorners">
<property name="text">
<string>Extend Corners</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="Gui::QuantitySpinBox" name="defaultLength">
<property name="minimum">
@@ -41,16 +48,13 @@
<property name="maximum">
<double>999999999.000000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QTreeView" name="extensions">
<widget class="QTreeView" name="extensionTree">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>

View File

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

View File

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