From d1d6846af02ee7770fdde479450b0d10e819f38c Mon Sep 17 00:00:00 2001 From: David Kaufman Date: Thu, 4 Sep 2025 17:54:07 -0400 Subject: [PATCH] [CAM] migrate ramp and lead in/out feed rates to the tool controller --- src/Mod/CAM/Path/Dressup/Gui/RampEntry.py | 85 +++++---------------- src/Mod/CAM/Path/Main/Job.py | 4 +- src/Mod/CAM/Path/Op/Gui/Base.py | 16 +--- src/Mod/CAM/Path/Tool/Controller.py | 93 ++++++++++++++++++++++- 4 files changed, 114 insertions(+), 84 deletions(-) diff --git a/src/Mod/CAM/Path/Dressup/Gui/RampEntry.py b/src/Mod/CAM/Path/Dressup/Gui/RampEntry.py index e13f76f56c..6abc8f7f07 100644 --- a/src/Mod/CAM/Path/Dressup/Gui/RampEntry.py +++ b/src/Mod/CAM/Path/Dressup/Gui/RampEntry.py @@ -181,26 +181,6 @@ class ObjectDressup: "Path", QT_TRANSLATE_NOOP("App::Property", "Ramping Method"), ) - obj.addProperty( - "App::PropertyEnumeration", - "RampFeedRate", - "FeedRate", - QT_TRANSLATE_NOOP("App::Property", "Which feed rate to use for ramping"), - ) - obj.addProperty( - "App::PropertyInteger", - "FeedRatePercent", - "FeedRate", - QT_TRANSLATE_NOOP( - "App::Property", "Percentage modifier to apply to feed rate while ramping" - ), - ) - obj.addProperty( - "App::PropertySpeed", - "CustomFeedRate", - "FeedRate", - QT_TRANSLATE_NOOP("App::Property", "Custom feed rate"), - ) obj.addProperty( "App::PropertyBool", "UseStartDepth", @@ -254,21 +234,6 @@ class ObjectDressup: (translate("CAM_DressupRampEntry", "RampMethod3"), "RampMethod3"), (translate("CAM_DressupRampEntry", "Helix"), "Helix"), ], - "RampFeedRate": [ - ( - translate("CAM_DressupRampEntry", "Horizontal Feed Rate"), - "Horizontal Feed Rate", - ), - ( - translate("CAM_DressupRampEntry", "Vertical Feed Rate"), - "Vertical Feed Rate", - ), - ( - translate("CAM_DressupRampEntry", "Ramp Feed Rate"), - "Ramp Feed Rate", - ), - (translate("CAM_DressupRampEntry", "Custom"), "Custom"), - ], } if dataType == "raw": @@ -292,7 +257,7 @@ class ObjectDressup: return None def onChanged(self, obj, prop): - if prop in ["RampFeedRate", "UseStartDepth"]: + if prop in ["UseStartDepth"]: self.setEditorProperties(obj) if prop == "Path" and obj.ViewObject: obj.ViewObject.signalChangeIcon() @@ -304,33 +269,29 @@ class ObjectDressup: else: obj.setEditorMode("DressupStartDepth", 2) - if obj.RampFeedRate == "Custom": - obj.setEditorMode("CustomFeedRate", 0) - if hasattr(obj, "FeedRatePercent"): - obj.setEditorMode("FeedRatePercent", 2) - else: - obj.setEditorMode("CustomFeedRate", 2) - if hasattr(obj, "FeedRatePercent"): - obj.setEditorMode("FeedRatePercent", 0) - def onDocumentRestored(self, obj): - if not hasattr(obj, "FeedRatePercent"): - obj.addProperty( - "App::PropertyInteger", - "FeedRatePercent", - "FeedRate", - QT_TRANSLATE_NOOP( - "App::Property", "Percentage modifier to apply to feed rate while ramping" - ), - ) - obj.FeedRatePercent = 100 - self.setEditorProperties(obj) + # Remove RampFeedRate + CustomFeedRate properties, but keep the values around temporarily + # This is required for tool controller migration: if a TC migrates with onDocumentRestored + # called after this, the prior ramp feed rate still needs to be accessible. + if hasattr(obj, "RampFeedRate"): + obj.Proxy.RampFeedRate = obj.RampFeedRate + obj.removeProperty("RampFeedRate") + print("delete RampFeedRate", obj.Label) + + if hasattr(obj, "CustomFeedRate"): + tmp = obj.CustomFeedRate.Value + for prop, exp in obj.ExpressionEngine: + if prop == "CustomFeedRate": + tmp = exp + obj.Proxy.CustomFeedRate = tmp + obj.removeProperty("CustomFeedRate") + print("delete CustomFeedRate", obj.Label) + def setup(self, obj): obj.Angle = 60 obj.Method = 2 - obj.FeedRatePercent = 100 if PathDressup.baseOp(obj.Base).StartDepth is not None: obj.DressupStartDepth = PathDressup.baseOp(obj.Base).StartDepth @@ -707,15 +668,7 @@ class ObjectDressup: vertFeed = tc.VertFeed.Value horizRapid = tc.HorizRapid.Value vertRapid = tc.VertRapid.Value - - if obj.RampFeedRate == "Horizontal Feed Rate": - rampFeed = horizFeed * obj.FeedRatePercent / 100 - elif obj.RampFeedRate == "Vertical Feed Rate": - rampFeed = vertFeed * obj.FeedRatePercent / 100 - elif obj.RampFeedRate == "Ramp Feed Rate": - rampFeed = (math.sqrt(pow(vertFeed, 2) + pow(horizFeed, 2))) * obj.FeedRatePercent / 100 - else: - rampFeed = obj.CustomFeedRate.Value + rampFeed = tc.RampFeed.Value lastX = lastY = lastZ = 0 for cmd in commands: diff --git a/src/Mod/CAM/Path/Main/Job.py b/src/Mod/CAM/Path/Main/Job.py index 03afd88d84..8e5eb8837e 100644 --- a/src/Mod/CAM/Path/Main/Job.py +++ b/src/Mod/CAM/Path/Main/Job.py @@ -705,7 +705,7 @@ class ObjectJob: "VertRapid", "%s.%s" % ( - self.setupSheet.expressionReference(), + self.obj.SetupSheet.Proxy.expressionReference(), PathSetupSheet.Template.VertRapid, ), ) @@ -713,7 +713,7 @@ class ObjectJob: "HorizRapid", "%s.%s" % ( - self.setupSheet.expressionReference(), + self.obj.SetupSheet.Proxy.expressionReference(), PathSetupSheet.Template.HorizRapid, ), ) diff --git a/src/Mod/CAM/Path/Op/Gui/Base.py b/src/Mod/CAM/Path/Op/Gui/Base.py index da8cb52ac5..25e4668551 100644 --- a/src/Mod/CAM/Path/Op/Gui/Base.py +++ b/src/Mod/CAM/Path/Op/Gui/Base.py @@ -429,22 +429,8 @@ class TaskPanelPage(object): def copyToolController(self): oldTc = self.tcEditor.obj self.tcEditor.updateToolController() - tc = PathToolController.Create( - name=oldTc.Label, tool=oldTc.Tool, toolNumber=oldTc.ToolNumber - ) job = self.obj.Proxy.getJob(self.obj) - job.Proxy.addToolController(tc) - - tc.HorizFeed = oldTc.HorizFeed - tc.VertFeed = oldTc.VertFeed - tc.HorizRapid = oldTc.HorizRapid - tc.VertRapid = oldTc.VertRapid - tc.SpindleSpeed = oldTc.SpindleSpeed - tc.SpindleDir = oldTc.SpindleDir - for attr, expr in oldTc.ExpressionEngine: - tc.setExpression(attr, expr) - - self.obj.ToolController = tc + self.obj.ToolController = PathToolController.copyTC(oldTc, job) self.setupToolController() def tcEditorChanged(self): diff --git a/src/Mod/CAM/Path/Tool/Controller.py b/src/Mod/CAM/Path/Tool/Controller.py index 133a7582eb..6d33c6635c 100644 --- a/src/Mod/CAM/Path/Tool/Controller.py +++ b/src/Mod/CAM/Path/Tool/Controller.py @@ -59,6 +59,80 @@ class ToolControllerTemplate: VertRapid = "vrapid" +def _migrateRampDressups(tc): + # Enumerate ramp dressups using this TC and their feed rates + ramps = set() + job_ramp_feeds = [] + print("search tc", tc.Name) + for job in tc.Document.Objects: + if hasattr(job, "Operations") and hasattr(job.Operations, "Group"): + print("job", job.Name) + for op in job.Operations.Group: + for ramp in [op] + op.OutListRecursive: + try: + if ramp not in ramps and ( + hasattr(ramp, "RampFeedRate") or hasattr(ramp.Proxy, "RampFeedRate") + ): + rampFeedRate = ( + ramp.RampFeedRate + if hasattr(ramp, "RampFeedRate") + else ramp.Proxy.RampFeedRate + ) + if hasattr(ramp, "CustomFeedRate"): + customFeedRate = ramp.CustomFeedRate.Value + for prop, exp in ramp.ExpressionEngine: + if prop == "CustomFeedRate": + customFeedRate = exp + else: + ramp.Proxy.CustomFeedRate + + if op.Base.ToolController == tc: + ramps.add(ramp) + feed = "HorizFeed" + if rampFeedRate == "Horizontal Feed Rate": + feed = "HorizFeed" + elif rampFeedRate == "Vertical Feed Rate": + feed = "VertFeed" + elif rampFeedRate == "Ramp Feed Rate": + feed = "sqrt(HorizFeed * HorizFeed + VertFeed * VertFeed)" + else: + feed = customFeedRate + job_ramp_feeds.append((job, ramp, feed)) + print("add", ramp.Label) + except: + pass + + # Ensure there is a TC for each required feed, starting with this one + print("job_ramp_feeds", job_ramp_feeds) + feed_to_tc = {} + for i, (job, ramp, feed) in enumerate(job_ramp_feeds): + if feed in feed_to_tc: + continue + + if len(feed_to_tc) == 0: + opTc = tc + else: + opTc = copyTC(tc, job) + # Note: C++ doesn't make an effort to deduplicate the Labels of + # objects created during document restore, so here we will reuse the + # (deduplicated) name as the label + opTc.Label = opTc.Name + + feed_to_tc[feed] = opTc + + if isinstance(feed, str): + opTc.setExpression("RampFeed", feed) + else: + opTc.setExpression("RampFeed", None) + opTc.RampFeed = feed + opTc.recompute() + + # Loop over ramps and assign each one the appropriate TC + for _, ramp, feed in job_ramp_feeds: + print("assign", ramp.Name, feed_to_tc[feed].Name) + ramp.Base.ToolController = feed_to_tc[feed] + + class ToolController: def __init__(self, obj, createTool=True): Path.Log.track("tool: ") @@ -199,7 +273,7 @@ class ToolController: "Feed", QT_TRANSLATE_NOOP("App::Property", "Feed rate for ramp moves"), ) - obj.setExpression("RampFeed", "HorizFeed") + _migrateRampDressups(obj) needsRecompute = True if not hasattr(obj, "LeadInFeed"): @@ -386,6 +460,23 @@ def Create( return obj +def copyTC(tc, job): + newtc = Create(name=tc.Label, tool=tc.Tool, toolNumber=tc.ToolNumber) + job.Proxy.addToolController(newtc) + + for prop in tc.PropertiesList: + try: + if prop not in ["Label", "Label2"]: + print("set prop", prop, getattr(tc, prop)) + setattr(newtc, prop, getattr(tc, prop)) + except: + pass + for attr, expr in tc.ExpressionEngine: + newtc.setExpression(attr, expr) + + return newtc + + def FromTemplate(template, assignViewProvider=True): Path.Log.track()