Skip to content

Commit ab2f655

Browse files
authored
Merge pull request FreeCAD#26131 from tarman3/axismap
CAM: Axismap Dressup - Invert rotary axis
2 parents 650b15d + ab1b324 commit ab2f655

File tree

2 files changed

+91
-82
lines changed

2 files changed

+91
-82
lines changed

src/Mod/CAM/Gui/Resources/panels/AxisMapEdit.ui

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,20 @@
7878
</item>
7979
</widget>
8080
</item>
81+
<item row="2" column="0">
82+
<widget class="QLabel" name="lblReverse">
83+
<property name="text">
84+
<string>Reverse</string>
85+
</property>
86+
</widget>
87+
</item>
88+
<item row="2" column="2" colspan="2">
89+
<widget class="QComboBox" name="reverse">
90+
<property name="toolTip">
91+
<string>Reverse rotary axis direction</string>
92+
</property>
93+
</widget>
94+
</item>
8195
<item row="3" column="2">
8296
<spacer name="verticalSpacer">
8397
<property name="orientation">

src/Mod/CAM/Path/Dressup/Gui/AxisMap.py

Lines changed: 77 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import Path.Base.Gui.Util as PathGuiUtil
2828
import PathScripts.PathUtils as PathUtils
2929
import Path.Dressup.Utils as PathDressup
30+
import Path.Post.Utils as PostUtils
3031
from PySide.QtCore import QT_TRANSLATE_NOOP
3132

3233
if False:
@@ -39,7 +40,8 @@
3940
if FreeCAD.GuiUp:
4041
import FreeCADGui
4142

42-
__doc__ = """Axis remapping Dressup object and FreeCAD command. This dressup remaps one axis of motion to another.
43+
__doc__ = """Axis remapping Dressup object and FreeCAD command.
44+
This dressup remaps one axis of motion to another.
4345
For example, you can re-map the Y axis to A to control a 4th axis rotary."""
4446

4547

@@ -48,7 +50,6 @@
4850

4951
class ObjectDressup:
5052
def __init__(self, obj):
51-
maplist = ["X->A", "Y->A", "X->B", "Y->B", "X->C", "Y->C"]
5253
obj.addProperty(
5354
"App::PropertyLink",
5455
"Base",
@@ -67,92 +68,85 @@ def __init__(self, obj):
6768
"Path",
6869
QT_TRANSLATE_NOOP("App::Property", "The radius of the wrapped axis"),
6970
)
70-
obj.AxisMap = maplist
71+
obj.addProperty(
72+
"App::PropertyBool",
73+
"Reverse",
74+
"Path",
75+
QT_TRANSLATE_NOOP("App::Property", "Reverse rotary axis direction"),
76+
)
77+
obj.AxisMap = ("X->A", "Y->A", "X->B", "Y->B", "X->C", "Y->C")
7178
obj.AxisMap = "Y->A"
79+
obj.Radius = 45
7280
obj.Proxy = self
7381

7482
def dumps(self):
75-
return None
83+
return
7684

7785
def loads(self, state):
78-
return None
86+
return
7987

80-
def _linear2angular(self, radius, length):
81-
"""returns an angular distance in degrees to achieve a linear move of a given length"""
82-
circum = 2 * math.pi * float(radius)
83-
return 360 * (float(length) / circum)
88+
def onChanged(self, obj, prop):
89+
if "Restore" not in obj.State and prop == "Radius":
90+
job = PathUtils.findParentJob(obj)
91+
if job:
92+
job.Proxy.setCenterOfRotation(self.center(obj))
8493

85-
def _stripArcs(self, path, d):
86-
"""converts all G2/G3 commands into G1 commands"""
87-
newcommandlist = []
88-
currLocation = {"X": 0, "Y": 0, "Z": 0, "F": 0}
89-
90-
for p in path:
91-
if p.Name in Path.Geom.CmdMoveArc:
92-
curVec = FreeCAD.Vector(currLocation["X"], currLocation["Y"], currLocation["Z"])
93-
arcwire = Path.Geom.edgeForCmd(p, curVec)
94-
pointlist = arcwire.discretize(Deflection=d)
95-
for point in pointlist:
96-
newcommand = Path.Command("G1", {"X": point.x, "Y": point.y, "Z": point.z})
97-
newcommandlist.append(newcommand)
98-
currLocation.update(newcommand.Parameters)
99-
else:
100-
newcommandlist.append(p)
101-
currLocation.update(p.Parameters)
94+
if prop == "Path" and obj.ViewObject:
95+
obj.ViewObject.signalChangeIcon()
10296

103-
return newcommandlist
97+
def onDocumentRestored(self, obj):
98+
if not hasattr(obj, "Reverse"):
99+
obj.addProperty(
100+
"App::PropertyBool",
101+
"Reverse",
102+
"Path",
103+
QT_TRANSLATE_NOOP("App::Property", "Reverse rotary axis direction"),
104+
)
104105

105106
def execute(self, obj):
106107

107108
inAxis = obj.AxisMap[0]
108109
outAxis = obj.AxisMap[3]
109-
d = 0.1
110-
111-
if obj.Base:
112-
if obj.Base.isDerivedFrom("Path::Feature"):
113-
if obj.Base.Path:
114-
if obj.Base.Path.Commands:
115-
pp = PathUtils.getPathWithPlacement(obj.Base).Commands
116-
if len([i for i in pp if i.Name in Path.Geom.CmdMoveArc]) == 0:
117-
pathlist = pp
118-
else:
119-
pathlist = self._stripArcs(pp, d)
120-
121-
newcommandlist = []
122-
currLocation = {"X": 0, "Y": 0, "Z": 0, "F": 0}
123-
124-
for c in pathlist:
125-
newparams = dict(c.Parameters)
126-
remapvar = newparams.pop(inAxis, None)
127-
if remapvar is not None:
128-
newparams[outAxis] = self._linear2angular(obj.Radius, remapvar)
129-
locdiff = dict(set(newparams.items()) - set(currLocation.items()))
130-
if (
131-
len(locdiff) == 1 and outAxis in locdiff
132-
): # pure rotation. Calculate rotational feed rate
133-
if "F" in c.Parameters:
134-
feed = c.Parameters["F"]
135-
else:
136-
feed = currLocation["F"]
137-
newparams.update({"F": self._linear2angular(obj.Radius, feed)})
138-
newcommand = Path.Command(c.Name, newparams)
139-
newcommandlist.append(newcommand)
140-
currLocation.update(newparams)
141-
else:
142-
newcommandlist.append(c)
143-
currLocation.update(c.Parameters)
144-
145-
path = Path.Path(newcommandlist)
146-
path.Center = self.center(obj)
147-
obj.Path = path
148110

149-
def onChanged(self, obj, prop):
150-
if "Restore" not in obj.State and prop == "Radius":
151-
job = PathUtils.findParentJob(obj)
152-
if job:
153-
job.Proxy.setCenterOfRotation(self.center(obj))
154-
if prop == "Path" and obj.ViewObject:
155-
obj.ViewObject.signalChangeIcon()
111+
if (
112+
not obj.Base
113+
or not obj.Base.isDerivedFrom("Path::Feature")
114+
or not obj.Base.Path
115+
or not obj.Base.Path.Commands
116+
):
117+
obj.Path = Path.Path()
118+
return
119+
120+
job = PathUtils.findParentJob(obj)
121+
deflection = job.GeometryTolerance.Value
122+
path = PathUtils.getPathWithPlacement(obj.Base)
123+
path = PostUtils.splitArcs(path, deflection=deflection)
124+
125+
newcommandlist = []
126+
lastPar = {"X": 0, "Y": 0, "Z": 0, "F": 0}
127+
128+
for cmd in path.Commands:
129+
newparams = dict(cmd.Parameters)
130+
remapvar = newparams.pop(inAxis, None)
131+
if remapvar is not None:
132+
if obj.Reverse:
133+
remapvar = -remapvar
134+
newparams[outAxis] = math.degrees(remapvar / obj.Radius.Value)
135+
locdiff = dict(set(newparams.items()) - set(lastPar.items()))
136+
if len(locdiff) == 1 and outAxis in locdiff:
137+
# calculate rotational feed rate
138+
feed = cmd.Parameters["F"] if "F" in cmd.Parameters else lastPar["F"]
139+
newparams.update({"F": math.degrees(feed / obj.Radius.Value)})
140+
newcommand = Path.Command(cmd.Name, newparams)
141+
newcommandlist.append(newcommand)
142+
lastPar.update(newparams)
143+
else:
144+
newcommandlist.append(cmd)
145+
lastPar.update(cmd.Parameters)
146+
147+
path = Path.Path(newcommandlist)
148+
path.Center = self.center(obj)
149+
obj.Path = path
156150

157151
def center(self, obj):
158152
return FreeCAD.Vector(0, 0, 0 - obj.Radius.Value)
@@ -163,7 +157,8 @@ def __init__(self, obj):
163157
self.obj = obj
164158
self.form = FreeCADGui.PySideUic.loadUi(":/panels/AxisMapEdit.ui")
165159
self.radius = PathGuiUtil.QuantitySpinBox(self.form.radius, obj, "Radius")
166-
FreeCAD.ActiveDocument.openTransaction("Edit Dragknife Dress-up")
160+
self.reverse = PathGuiUtil.BooleanComboBox(self.form.reverse, obj, "Reverse")
161+
FreeCAD.ActiveDocument.openTransaction("Edit AxisMap Dress-up")
167162

168163
def reject(self):
169164
FreeCAD.ActiveDocument.abortTransaction()
@@ -179,11 +174,13 @@ def accept(self):
179174

180175
def getFields(self):
181176
self.radius.updateProperty()
177+
self.reverse.updateProperty()
182178
self.obj.AxisMap = self.form.axisMapInput.currentText()
183179
self.obj.Proxy.execute(self.obj)
184180

185181
def updateUI(self):
186182
self.radius.updateWidget()
183+
self.reverse.updateWidget()
187184
self.form.axisMapInput.setCurrentText(self.obj.AxisMap)
188185
self.updateModel()
189186

@@ -200,12 +197,14 @@ def open(self):
200197
def setupUi(self):
201198
self.setFields()
202199
self.form.radius.valueChanged.connect(self.updateModel)
200+
self.form.reverse.currentIndexChanged.connect(self.updateModel)
203201
self.form.axisMapInput.currentIndexChanged.connect(self.updateModel)
204202

205203

206204
class ViewProviderDressup:
207205
def __init__(self, vobj):
208206
self.obj = vobj.Object
207+
vobj.Proxy = self
209208

210209
def attach(self, vobj):
211210
self.obj = vobj.Object
@@ -217,7 +216,6 @@ def attach(self, vobj):
217216
if g.Name == self.obj.Base.Name:
218217
group.remove(g)
219218
i.Group = group
220-
# FreeCADGui.ActiveDocument.getObject(obj.Base.Name).Visibility = False
221219
return
222220

223221
def unsetEdit(self, vobj, mode=0):
@@ -234,10 +232,10 @@ def claimChildren(self):
234232
return [self.obj.Base]
235233

236234
def dumps(self):
237-
return None
235+
return
238236

239237
def loads(self, state):
240-
return None
238+
return
241239

242240
def onDelete(self, arg1=None, arg2=None):
243241
"""this makes sure that the base operation is added back to the project and visible"""
@@ -299,12 +297,9 @@ def Activated(self):
299297
FreeCADGui.doCommand("base = FreeCAD.ActiveDocument." + selection[0].Name)
300298
FreeCADGui.doCommand("job = PathScripts.PathUtils.findParentJob(base)")
301299
FreeCADGui.doCommand("obj.Base = base")
302-
FreeCADGui.doCommand("obj.Radius = 45")
303300
FreeCADGui.doCommand("job.Proxy.addOperation(obj, base)")
304-
FreeCADGui.doCommand(
305-
"obj.ViewObject.Proxy = Path.Dressup.Gui.AxisMap.ViewProviderDressup(obj.ViewObject)"
306-
)
307-
FreeCADGui.doCommand("Gui.ActiveDocument.getObject(base.Name).Visibility = False")
301+
FreeCADGui.doCommand("Path.Dressup.Gui.AxisMap.ViewProviderDressup(obj.ViewObject)")
302+
FreeCADGui.doCommand("base.Visibility = False")
308303
FreeCADGui.doCommand("obj.ViewObject.Document.setEdit(obj.ViewObject, 0)")
309304
# FreeCAD.ActiveDocument.commitTransaction() # Final `commitTransaction()` called via TaskPanel.accept()
310305
FreeCAD.ActiveDocument.recompute()

0 commit comments

Comments
 (0)