2727import Path .Base .Gui .Util as PathGuiUtil
2828import PathScripts .PathUtils as PathUtils
2929import Path .Dressup .Utils as PathDressup
30+ import Path .Post .Utils as PostUtils
3031from PySide .QtCore import QT_TRANSLATE_NOOP
3132
3233if False :
3940if 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.
4345For example, you can re-map the Y axis to A to control a 4th axis rotary."""
4446
4547
4850
4951class 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
206204class 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