Files
create/src/Mod/Path/PathScripts/PathDressupAxisMap.py
2018-11-04 14:50:04 -03:00

225 lines
9.9 KiB
Python

# -*- coding: utf-8 -*-
# ***************************************************************************
# * *
# * Copyright (c) 2018 sliptonic <shopinthewoods@gmail.com> *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
# * as published by the Free Software Foundation; either version 2 of *
# * the License, or (at your option) any later version. *
# * for detail see the LICENCE text file. *
# * *
# * This program is distributed in the hope that it will be useful, *
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
# * GNU Library General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
import FreeCAD
import FreeCADGui
import Path
import math
import PathScripts.PathGeom as PathGeom
import PathScripts.PathUtils as PathUtils
from PySide import QtCore, QtGui
"""Axis remapping Dressup object and FreeCAD command. This dressup remaps one axis of motion to another.
For example, you can re-map the Y axis to A to control a 4th axis rotary."""
# Qt tanslation handling
def translate(context, text, disambig=None):
return QtCore.QCoreApplication.translate(context, text, disambig)
movecommands = ['G1', 'G01', 'G2', 'G02', 'G3', 'G03']
rapidcommands = ['G0', 'G00']
arccommands = ['G2', 'G3', 'G02', 'G03']
class ObjectDressup:
def __init__(self, obj):
maplist = ["X->A", "Y->A", "X->B", "Y->B", "X->C", "Y->C"]
obj.addProperty("App::PropertyLink", "Base", "Path", QtCore.QT_TRANSLATE_NOOP("Path_DressupAxisMap", "The base path to modify"))
obj.addProperty("App::PropertyEnumeration", "AxisMap", "Path", QtCore.QT_TRANSLATE_NOOP("Path_DressupAxisMap", "The input mapping axis"))
obj.addProperty("App::PropertyDistance", "Radius", "Path", QtCore.QT_TRANSLATE_NOOP("Path_DressupAxisMap", "The radius of the wrapped axis"))
obj.AxisMap = maplist
obj.AxisMap = "Y->A"
obj.Proxy = self
def __getstate__(self):
return None
def __setstate__(self, state):
return None
def _linear2angular(self, radius, length):
'''returns an angular distance in degrees to achieve a linear move of a given lenth'''
circum = 2 * math.pi * float(radius)
return 360 * (float(length) / circum)
def _stripArcs(self, path, d):
'''converts all G2/G3 commands into G1 commands'''
newcommandlist = []
currLocation = {'X':0,'Y':0,'Z':0, 'F': 0}
for p in path:
if p.Name in arccommands:
curVec = FreeCAD.Vector(currLocation['X'], currLocation['Y'], currLocation['Z'])
arcwire = PathGeom.edgeForCmd(p, curVec)
pointlist = arcwire.discretize(Deflection=d)
for point in pointlist:
newcommand = Path.Command("G1", {'X':point.x, 'Y':point.y, 'Z':point.z})
newcommandlist.append(newcommand)
currLocation.update(newcommand.Parameters)
else:
newcommandlist.append(p)
currLocation.update(p.Parameters)
return newcommandlist
def execute(self, obj):
inAxis = obj.AxisMap[0]
outAxis = obj.AxisMap[3]
d = 0.1
if obj.Base:
if obj.Base.isDerivedFrom("Path::Feature"):
if obj.Base.Path:
if obj.Base.Path.Commands:
pp = obj.Base.Path.Commands
if len([i for i in pp if i.Name in arccommands]) == 0:
pathlist = pp
else:
pathlist = self._stripArcs(pp, d)
newcommandlist = []
currLocation = {'X':0,'Y':0,'Z':0, 'F': 0}
for c in pathlist: #obj.Base.Path.Commands:
newparams = dict(c.Parameters)
remapvar = newparams.pop(inAxis, None)
if remapvar is not None:
newparams[outAxis] = self._linear2angular(obj.Radius, remapvar)
locdiff = dict(set(newparams.items()) - set(currLocation.items()))
if len(locdiff) == 1 and outAxis in locdiff: #pure rotation. Calculate rotational feed rate
if 'F' in c.Parameters:
feed = c.Parameters['F']
else:
feed = currLocation['F']
newparams.update({"F":self._linear2angular(obj.Radius, feed)})
newcommand = Path.Command(c.Name, newparams)
newcommandlist.append(newcommand)
currLocation.update(newparams)
else:
newcommandlist.append(c)
currLocation.update(c.Parameters)
path = Path.Path(newcommandlist)
path.Center = self.center(obj)
obj.Path = path
def onChanged(self, obj, prop):
if not 'Restore' in obj.State and prop == "Radius":
job = PathUtils.findParentJob(obj)
if job:
job.Proxy.setCenterOfRotation(self.center(obj))
def center(self, obj):
return FreeCAD.Vector(0, 0, 0 - obj.Radius.Value)
class ViewProviderDressup:
def __init__(self, vobj):
vobj.Proxy = self
def attach(self, vobj):
self.obj = vobj.Object
if self.obj and self.obj.Base:
for i in self.obj.Base.InList:
if hasattr(i, "Group"):
group = i.Group
for g in group:
if g.Name == self.obj.Base.Name:
group.remove(g)
i.Group = group
# FreeCADGui.ActiveDocument.getObject(obj.Base.Name).Visibility = False
return
def claimChildren(self):
return [self.obj.Base]
def __getstate__(self):
return None
def __setstate__(self, state):
return None
def onDelete(self, arg1=None, arg2=None):
'''this makes sure that the base operation is added back to the project and visible'''
FreeCADGui.ActiveDocument.getObject(arg1.Object.Base.Name).Visibility = True
job = PathUtils.findParentJob(arg1.Object)
job.Proxy.addOperation(arg1.Object.Base, arg1.Object)
arg1.Object.Base = None
return True
class CommandPathDressup:
def GetResources(self):
return {'Pixmap': 'Path-Dressup',
'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_DressupAxisMap", "Axis Map Dress-up"),
'Accel': "",
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_DressupAxisMap", "Remap one axis to another.")}
def IsActive(self):
if FreeCAD.ActiveDocument is not None:
for o in FreeCAD.ActiveDocument.Objects:
if o.Name[:3] == "Job":
return True
return False
def Activated(self):
# check that the selection contains exactly what we want
selection = FreeCADGui.Selection.getSelection()
if len(selection) != 1:
FreeCAD.Console.PrintError(translate("Path_Dressup", "Please select one path object\n"))
return
if not selection[0].isDerivedFrom("Path::Feature"):
FreeCAD.Console.PrintError(translate("Path_Dressup", "The selected object is not a path\n"))
return
if selection[0].isDerivedFrom("Path::FeatureCompoundPython"):
FreeCAD.Console.PrintError(translate("Path_Dressup", "Please select a Path object"))
return
# everything ok!
FreeCAD.ActiveDocument.openTransaction(translate("Path_DressupAxisMap", "Create Dress-up"))
FreeCADGui.addModule("PathScripts.PathDressupAxisMap")
FreeCADGui.addModule("PathScripts.PathUtils")
FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", "AxisMapDressup")')
FreeCADGui.doCommand('PathScripts.PathDressupAxisMap.ObjectDressup(obj)')
FreeCADGui.doCommand('base = FreeCAD.ActiveDocument.' + selection[0].Name)
FreeCADGui.doCommand('job = PathScripts.PathUtils.findParentJob(base)')
FreeCADGui.doCommand('obj.Base = base')
FreeCADGui.doCommand('obj.Radius = 45')
FreeCADGui.doCommand('job.Proxy.addOperation(obj, base)')
FreeCADGui.doCommand('PathScripts.PathDressupAxisMap.ViewProviderDressup(obj.ViewObject)')
FreeCADGui.doCommand('Gui.ActiveDocument.getObject(base.Name).Visibility = False')
FreeCAD.ActiveDocument.commitTransaction()
FreeCAD.ActiveDocument.recompute()
if FreeCAD.GuiUp:
# register the FreeCAD command
FreeCADGui.addCommand('Path_DressupAxisMap', CommandPathDressup())
FreeCAD.Console.PrintLog("Loading PathDressup... done\n")