From fb4ec3e18ec411e782d97b961a06205a8180f11f Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Wed, 17 Mar 2021 19:46:51 -0700 Subject: [PATCH 1/4] Added visualisation for dogbones during editing --- .../Path/PathScripts/PathDressupDogbone.py | 119 +++++++++++++++--- 1 file changed, 101 insertions(+), 18 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathDressupDogbone.py b/src/Mod/Path/PathScripts/PathDressupDogbone.py index ce134a23c3..dea1696060 100644 --- a/src/Mod/Path/PathScripts/PathDressupDogbone.py +++ b/src/Mod/Path/PathScripts/PathDressupDogbone.py @@ -22,15 +22,16 @@ from __future__ import print_function import FreeCAD -import math import Path import PathScripts.PathDressup as PathDressup import PathScripts.PathGeom as PathGeom import PathScripts.PathLog as PathLog import PathScripts.PathUtil as PathUtil import PathScripts.PathUtils as PathUtils +import math from PySide import QtCore +from pivy import coin # lazily loaded modules from lazy_loader.lazy_loader import LazyLoader @@ -140,7 +141,7 @@ def edgesForCommands(cmds, startPt): return edges -class Style: +class Style(object): # pylint: disable=no-init Dogbone = 'Dogbone' @@ -151,7 +152,7 @@ class Style: All = [Dogbone, Tbone_H, Tbone_V, Tbone_L, Tbone_S] -class Side: +class Side(object): # pylint: disable=no-init Left = 'Left' @@ -167,7 +168,7 @@ class Side: return None -class Incision: +class Incision(object): # pylint: disable=no-init Fixed = 'fixed' @@ -176,7 +177,7 @@ class Incision: All = [Adaptive, Fixed, Custom] -class Smooth: +class Smooth(object): # pylint: disable=no-init Neither = 0 @@ -306,7 +307,7 @@ class Chord (object): return PathGeom.pointsCoincide(self.End, chord.Start) -class Bone: +class Bone(object): def __init__(self, boneId, obj, lastCommand, inChord, outChord, smooth, F): self.obj = obj self.boneId = boneId @@ -348,6 +349,9 @@ class Bone: def location(self): return (self.inChord.End.x, self.inChord.End.y) + def locationZ(self): + return (self.inChord.End.x, self.inChord.End.y, self.inChord.End.z) + def adaptiveLength(self, boneAngle, toolRadius): theta = self.angle() distance = self.distance(toolRadius) @@ -388,7 +392,7 @@ class Bone: return length -class ObjectDressup: +class ObjectDressup(object): def __init__(self, obj, base): # Tool Properties @@ -552,6 +556,9 @@ class ObjectDressup: PathLog.info("no bone after all ..") return [bone.lastCommand, bone.outChord.g1Command(bone.F)] + # track length for marker visuals + self.length = max(self.length, length) + boneInChord = bone.inChord.move(length, boneAngle) boneOutChord = boneInChord.moveTo(bone.outChord.Start) @@ -675,7 +682,7 @@ class ObjectDressup: self.boneShapes = [] blacklisted, inaccessible = self.boneIsBlacklisted(bone) enabled = not blacklisted - self.bones.append((bone.boneId, bone.location(), enabled, inaccessible)) + self.bones.append((bone.boneId, bone.locationZ(), enabled, inaccessible)) self.boneId = bone.boneId if False and PathLog.getLevel(LOG_MODULE) == PathLog.Level.DEBUG and bone.boneId > 2: @@ -751,6 +758,7 @@ class ObjectDressup: boneId = 1 self.bones = [] self.locationBlacklist = set() + self.length = 0 # boneIserted = False for (i, thisCommand) in enumerate(obj.Base.Path.Commands): @@ -871,29 +879,76 @@ class ObjectDressup: if not hasattr(self, 'bones'): self.execute(obj) for (nr, loc, enabled, inaccessible) in self.bones: - item = state.get(loc) + item = state.get((loc[0], loc[1])) if item: item[2].append(nr) + item[3].append(loc[2]) else: - state[loc] = (enabled, inaccessible, [nr]) + state[(loc[0], loc[1])] = (enabled, inaccessible, [nr], [loc[2]]) return state +class Marker(object): + Color = [coin.SbColor(.9, .5, .9), coin.SbColor(.9, .9, .5)] -class TaskPanel: + def __init__(self, pt, r, h): + if PathGeom.isRoughly(h, 0): + h = 0.1 + self.pt = pt + self.r = r + self.h = h + self.sep = coin.SoSeparator() + self.pos = coin.SoTranslation() + self.pos.translation = (pt.x, pt.y, pt.z + h / 2) + self.rot = coin.SoRotationXYZ() + self.rot.axis = self.rot.X + self.rot.angle = math.pi / 2 + self.cyl = coin.SoCylinder() + self.cyl.radius = r + self.cyl.height = h + # self.cyl.removePart(self.cyl.TOP) + # self.cyl.removePart(self.cyl.BOTTOM) + self.material = coin.SoMaterial() + self.sep.addChild(self.pos) + self.sep.addChild(self.rot) + self.sep.addChild(self.material) + self.sep.addChild(self.cyl) + self.lowlight() + + def setSelected(self, selected): + if selected: + self.highlight() + else: + self.lowlight() + + def highlight(self): + self.material.diffuseColor = self.Color[1] + self.material.transparency = 0.45 + + def lowlight(self): + self.material.diffuseColor = self.Color[0] + self.material.transparency = 0.75 + + +class TaskPanel(object): DataIds = QtCore.Qt.ItemDataRole.UserRole DataKey = QtCore.Qt.ItemDataRole.UserRole + 1 + DataLoc = QtCore.Qt.ItemDataRole.UserRole + 2 - def __init__(self, obj): + def __init__(self, viewProvider, obj): + self.viewProvider = viewProvider self.obj = obj self.form = FreeCADGui.PySideUic.loadUi(":/panels/DogboneEdit.ui") self.s = None FreeCAD.ActiveDocument.openTransaction(translate("Path_DressupDogbone", "Edit Dogbone Dress-up")) + self.height = 10 + self.markers = [] def reject(self): FreeCAD.ActiveDocument.abortTransaction() FreeCADGui.Control.closeDialog() FreeCAD.ActiveDocument.recompute() FreeCADGui.Selection.removeObserver(self.s) + self.cleanup() def accept(self): self.getFields() @@ -903,6 +958,12 @@ class TaskPanel: FreeCAD.ActiveDocument.recompute() FreeCADGui.Selection.removeObserver(self.s) FreeCAD.ActiveDocument.recompute() + self.cleanup() + + def cleanup(self): + self.viewProvider.showMarkers(False) + for m in self.markers: + self.viewProvider.switch.removeChild(m.sep) def getFields(self): self.obj.Style = str(self.form.styleCombo.currentText()) @@ -919,7 +980,7 @@ class TaskPanel: def updateBoneList(self): itemList = [] - for loc, (enabled, inaccessible, ids) in PathUtil.keyValueIter(self.obj.Proxy.boneStateList(self.obj)): + for loc, (enabled, inaccessible, ids, zs) in PathUtil.keyValueIter(self.obj.Proxy.boneStateList(self.obj)): lbl = '(%.2f, %.2f): %s' % (loc[0], loc[1], ','.join(str(id) for id in ids)) item = QtGui.QListWidgetItem(lbl) if enabled: @@ -932,10 +993,20 @@ class TaskPanel: item.setFlags(flags) item.setData(self.DataIds, ids) item.setData(self.DataKey, ids[0]) + item.setData(self.DataLoc, loc) itemList.append(item) self.form.bones.clear() + markers = [] for item in sorted(itemList, key=lambda item: item.data(self.DataKey)): self.form.bones.addItem(item) + loc = item.data(self.DataLoc) + r = max(self.obj.Proxy.length, 1) + markers.append(Marker(FreeCAD.Vector(loc[0], loc[1], min(zs)), r, max(1, max(zs) - min(zs)))) + for m in self.markers: + self.viewProvider.switch.removeChild(m.sep) + for m in markers: + self.viewProvider.switch.addChild(m.sep) + self.markers = markers def updateUI(self): customSelected = self.obj.Incision == Incision.Custom @@ -992,9 +1063,16 @@ class TaskPanel: self.form.incisionCombo.currentIndexChanged.connect(self.updateModel) self.form.custom.valueChanged.connect(self.updateModel) self.form.bones.itemChanged.connect(self.updateModel) + self.form.bones.itemSelectionChanged.connect(self.updateMarkers) + self.viewProvider.showMarkers(True) -class SelObserver: + def updateMarkers(self): + index = self.form.bones.currentRow() + for i, m in enumerate(self.markers): + m.setSelected(i == index) + +class SelObserver(object): def __init__(self): import PathScripts.PathSelection as PST PST.eselect() @@ -1009,7 +1087,7 @@ class SelObserver: FreeCADGui.updateGui() -class ViewProviderDressup: +class ViewProviderDressup(object): def __init__(self, vobj): self.vobj = vobj @@ -1026,7 +1104,12 @@ class ViewProviderDressup: group.remove(g) i.Group = group # FreeCADGui.ActiveDocument.getObject(obj.Base.Name).Visibility = False - return + self.switch = coin.SoSwitch() + vobj.RootNode.addChild(self.switch) + + def showMarkers(self, on): + sw = coin.SO_SWITCH_ALL if on else coin.SO_SWITCH_NONE + self.switch.whichChild = sw def claimChildren(self): return [self.obj.Base] @@ -1034,7 +1117,7 @@ class ViewProviderDressup: def setEdit(self, vobj, mode=0): # pylint: disable=unused-argument FreeCADGui.Control.closeDialog() - panel = TaskPanel(vobj.Object) + panel = TaskPanel(self, vobj.Object) FreeCADGui.Control.showDialog(panel) panel.setupUi() return True @@ -1074,7 +1157,7 @@ def Create(base, name='DogboneDressup'): return obj -class CommandDressupDogbone: +class CommandDressupDogbone(object): # pylint: disable=no-init def GetResources(self): From 462b88d0743ff468bda198f17864968072c14ab3 Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Wed, 17 Mar 2021 20:06:01 -0700 Subject: [PATCH 2/4] Cleanup marker after deleting them --- src/Mod/Path/PathScripts/PathDressupDogbone.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Mod/Path/PathScripts/PathDressupDogbone.py b/src/Mod/Path/PathScripts/PathDressupDogbone.py index dea1696060..d14dc2c118 100644 --- a/src/Mod/Path/PathScripts/PathDressupDogbone.py +++ b/src/Mod/Path/PathScripts/PathDressupDogbone.py @@ -964,6 +964,7 @@ class TaskPanel(object): self.viewProvider.showMarkers(False) for m in self.markers: self.viewProvider.switch.removeChild(m.sep) + self.markers = [] def getFields(self): self.obj.Style = str(self.form.styleCombo.currentText()) From 18a0ff7bd01aa7419182795c6645da9df6a63ca3 Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Thu, 18 Mar 2021 11:44:57 -0700 Subject: [PATCH 3/4] Moved coin import under GuiUp clause --- src/Mod/Path/PathScripts/PathDressupDogbone.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Path/PathScripts/PathDressupDogbone.py b/src/Mod/Path/PathScripts/PathDressupDogbone.py index d14dc2c118..9407fcefbc 100644 --- a/src/Mod/Path/PathScripts/PathDressupDogbone.py +++ b/src/Mod/Path/PathScripts/PathDressupDogbone.py @@ -31,7 +31,6 @@ import PathScripts.PathUtils as PathUtils import math from PySide import QtCore -from pivy import coin # lazily loaded modules from lazy_loader.lazy_loader import LazyLoader @@ -1196,6 +1195,7 @@ class CommandDressupDogbone(object): if FreeCAD.GuiUp: import FreeCADGui from PySide import QtGui + from pivy import coin FreeCADGui.addCommand('Path_DressupDogbone', CommandDressupDogbone()) FreeCAD.Console.PrintLog("Loading DressupDogbone... done\n") From 6a3815c9c0c92aa270190dd230ed380cb52b231f Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Thu, 18 Mar 2021 18:00:55 -0700 Subject: [PATCH 4/4] Fixed coin import for py3 class initialisation --- src/Mod/Path/PathScripts/PathDressupDogbone.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathDressupDogbone.py b/src/Mod/Path/PathScripts/PathDressupDogbone.py index 9407fcefbc..8ccb4f9023 100644 --- a/src/Mod/Path/PathScripts/PathDressupDogbone.py +++ b/src/Mod/Path/PathScripts/PathDressupDogbone.py @@ -887,7 +887,6 @@ class ObjectDressup(object): return state class Marker(object): - Color = [coin.SbColor(.9, .5, .9), coin.SbColor(.9, .9, .5)] def __init__(self, pt, r, h): if PathGeom.isRoughly(h, 0): @@ -920,13 +919,18 @@ class Marker(object): self.lowlight() def highlight(self): - self.material.diffuseColor = self.Color[1] + self.material.diffuseColor = self.color(1) self.material.transparency = 0.45 def lowlight(self): - self.material.diffuseColor = self.Color[0] + self.material.diffuseColor = self.color(0) self.material.transparency = 0.75 + def color(self, id): + if id == 1: + return coin.SbColor(.9, .9, .5) + return coin.SbColor(.9, .5, .9) + class TaskPanel(object): DataIds = QtCore.Qt.ItemDataRole.UserRole