Black pathdressupdragknife
This commit is contained in:
@@ -30,7 +30,8 @@ import PathScripts.PathGui as PathGui
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
D = LazyLoader('DraftVecUtils', globals(), 'DraftVecUtils')
|
||||
|
||||
D = LazyLoader("DraftVecUtils", globals(), "DraftVecUtils")
|
||||
|
||||
__doc__ = """Dragknife Dressup object and FreeCAD command"""
|
||||
|
||||
@@ -43,20 +44,46 @@ 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']
|
||||
movecommands = ["G1", "G01", "G2", "G02", "G3", "G03"]
|
||||
rapidcommands = ["G0", "G00"]
|
||||
arccommands = ["G2", "G3", "G02", "G03"]
|
||||
|
||||
currLocation = {}
|
||||
|
||||
|
||||
class ObjectDressup:
|
||||
|
||||
def __init__(self, obj):
|
||||
obj.addProperty("App::PropertyLink", "Base", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "The base path to modify"))
|
||||
obj.addProperty("App::PropertyAngle", "filterAngle", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "Angles less than filter angle will not receive corner actions"))
|
||||
obj.addProperty("App::PropertyFloat", "offset", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "Distance the point trails behind the spindle"))
|
||||
obj.addProperty("App::PropertyFloat", "pivotheight", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "Height to raise during corner action"))
|
||||
obj.addProperty(
|
||||
"App::PropertyLink",
|
||||
"Base",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP("App::Property", "The base path to modify"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyAngle",
|
||||
"filterAngle",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Angles less than filter angle will not receive corner actions",
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyFloat",
|
||||
"offset",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Distance the point trails behind the spindle"
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyFloat",
|
||||
"pivotheight",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Height to raise during corner action"
|
||||
),
|
||||
)
|
||||
|
||||
obj.Proxy = self
|
||||
|
||||
@@ -67,13 +94,17 @@ class ObjectDressup:
|
||||
return None
|
||||
|
||||
def shortcut(self, queue):
|
||||
'''Determines whether its shorter to twist CW or CCW to align with
|
||||
the next move'''
|
||||
"""Determines whether its shorter to twist CW or CCW to align with
|
||||
the next move"""
|
||||
# get the vector of the last move
|
||||
|
||||
if queue[1].Name in arccommands:
|
||||
arcLoc = FreeCAD.Vector(queue[2].x + queue[1].I, queue[2].y + queue[1].J, currLocation['Z'])
|
||||
radvector = arcLoc.sub(queue[1].Placement.Base) # .sub(arcLoc) # vector of chord from center to point
|
||||
arcLoc = FreeCAD.Vector(
|
||||
queue[2].x + queue[1].I, queue[2].y + queue[1].J, currLocation["Z"]
|
||||
)
|
||||
radvector = arcLoc.sub(
|
||||
queue[1].Placement.Base
|
||||
) # .sub(arcLoc) # vector of chord from center to point
|
||||
# vector of line perp to chord.
|
||||
v1 = radvector.cross(FreeCAD.Vector(0, 0, 1))
|
||||
else:
|
||||
@@ -81,7 +112,9 @@ class ObjectDressup:
|
||||
|
||||
# get the vector of the current move
|
||||
if queue[0].Name in arccommands:
|
||||
arcLoc = FreeCAD.Vector((queue[1].x + queue[0].I), (queue[1].y + queue[0].J), currLocation['Z'])
|
||||
arcLoc = FreeCAD.Vector(
|
||||
(queue[1].x + queue[0].I), (queue[1].y + queue[0].J), currLocation["Z"]
|
||||
)
|
||||
radvector = queue[1].Placement.Base.sub(arcLoc) # calculate arcangle
|
||||
v2 = radvector.cross(FreeCAD.Vector(0, 0, 1))
|
||||
else:
|
||||
@@ -93,24 +126,36 @@ class ObjectDressup:
|
||||
return "CCW"
|
||||
|
||||
def segmentAngleXY(self, prevCommand, currCommand, endpos=False, currentZ=0):
|
||||
'''returns in the starting angle in radians for a Path command.
|
||||
"""returns in the starting angle in radians for a Path command.
|
||||
requires the previous command in order to calculate arcs correctly
|
||||
if endpos = True, return the angle at the end of the segment.'''
|
||||
if endpos = True, return the angle at the end of the segment."""
|
||||
|
||||
if currCommand.Name in arccommands:
|
||||
arcLoc = FreeCAD.Vector((prevCommand.x + currCommand.I), (prevCommand.y + currCommand.J), currentZ)
|
||||
arcLoc = FreeCAD.Vector(
|
||||
(prevCommand.x + currCommand.I),
|
||||
(prevCommand.y + currCommand.J),
|
||||
currentZ,
|
||||
)
|
||||
if endpos is True:
|
||||
radvector = arcLoc.sub(currCommand.Placement.Base) # Calculate vector at start of arc
|
||||
radvector = arcLoc.sub(
|
||||
currCommand.Placement.Base
|
||||
) # Calculate vector at start of arc
|
||||
else:
|
||||
radvector = arcLoc.sub(prevCommand.Placement.Base) # Calculate vector at end of arc
|
||||
radvector = arcLoc.sub(
|
||||
prevCommand.Placement.Base
|
||||
) # Calculate vector at end of arc
|
||||
|
||||
v1 = radvector.cross(FreeCAD.Vector(0, 0, 1))
|
||||
if currCommand.Name in ["G2", "G02"]:
|
||||
v1 = D.rotate2D(v1, math.radians(180))
|
||||
else:
|
||||
v1 = currCommand.Placement.Base.sub(prevCommand.Placement.Base) # Straight segments are easy
|
||||
v1 = currCommand.Placement.Base.sub(
|
||||
prevCommand.Placement.Base
|
||||
) # Straight segments are easy
|
||||
|
||||
myAngle = D.angle(v1, FreeCAD.Base.Vector(1, 0, 0), FreeCAD.Base.Vector(0, 0, -1))
|
||||
myAngle = D.angle(
|
||||
v1, FreeCAD.Base.Vector(1, 0, 0), FreeCAD.Base.Vector(0, 0, -1)
|
||||
)
|
||||
return myAngle
|
||||
|
||||
def getIncidentAngle(self, queue):
|
||||
@@ -123,18 +168,20 @@ class ObjectDressup:
|
||||
if angleatstart < 0:
|
||||
angleatstart = 360 + angleatstart
|
||||
|
||||
incident_angle = angleatend-angleatstart
|
||||
incident_angle = angleatend - angleatstart
|
||||
|
||||
return incident_angle
|
||||
|
||||
def arcExtension(self, obj, queue):
|
||||
'''returns gcode for arc extension'''
|
||||
"""returns gcode for arc extension"""
|
||||
global currLocation # pylint: disable=global-statement
|
||||
results = []
|
||||
|
||||
offset = obj.offset
|
||||
# Find the center of the old arc
|
||||
C = FreeCAD.Base.Vector(queue[2].x + queue[1].I, queue[2].y + queue[1].J, currLocation['Z'])
|
||||
C = FreeCAD.Base.Vector(
|
||||
queue[2].x + queue[1].I, queue[2].y + queue[1].J, currLocation["Z"]
|
||||
)
|
||||
|
||||
# Find radius of old arc
|
||||
R = math.hypot(queue[1].I, queue[1].J)
|
||||
@@ -165,8 +212,8 @@ class ObjectDressup:
|
||||
return (results, replace)
|
||||
|
||||
def arcTwist(self, obj, queue, lastXY, twistCW=False):
|
||||
'''returns gcode to do an arc move toward an arc to perform
|
||||
a corner action twist. Includes lifting and plungeing the knife'''
|
||||
"""returns gcode to do an arc move toward an arc to perform
|
||||
a corner action twist. Includes lifting and plungeing the knife"""
|
||||
|
||||
global currLocation # pylint: disable=global-statement
|
||||
pivotheight = obj.pivotheight
|
||||
@@ -186,7 +233,9 @@ class ObjectDressup:
|
||||
currLocation.update(retract.Parameters)
|
||||
|
||||
# get the center of the destination arc
|
||||
arccenter = FreeCAD.Base.Vector(queue[1].x + queue[0].I, queue[1].y + queue[0].J, currLocation["Z"])
|
||||
arccenter = FreeCAD.Base.Vector(
|
||||
queue[1].x + queue[0].I, queue[1].y + queue[0].J, currLocation["Z"]
|
||||
)
|
||||
|
||||
# The center of the twist arc is the old line end point.
|
||||
C = queue[1].Placement.Base
|
||||
@@ -196,7 +245,9 @@ class ObjectDressup:
|
||||
|
||||
# find angle of original center to startpoint
|
||||
v1 = queue[1].Placement.Base.sub(arccenter)
|
||||
segAngle = D.angle(v1, FreeCAD.Base.Vector(1, 0, 0), FreeCAD.Base.Vector(0, 0, -1))
|
||||
segAngle = D.angle(
|
||||
v1, FreeCAD.Base.Vector(1, 0, 0), FreeCAD.Base.Vector(0, 0, -1)
|
||||
)
|
||||
|
||||
# Find angle subtended by the offset
|
||||
theta = offset / R
|
||||
@@ -210,14 +261,21 @@ class ObjectDressup:
|
||||
# calculate endpoints
|
||||
Bx = arccenter.x + R * math.cos(newangle)
|
||||
By = arccenter.y + R * math.sin(newangle)
|
||||
endpointvector = FreeCAD.Base.Vector(Bx, By, currLocation['Z'])
|
||||
endpointvector = FreeCAD.Base.Vector(Bx, By, currLocation["Z"])
|
||||
|
||||
# calculate IJ offsets of twist arc from current position.
|
||||
offsetvector = C.sub(lastXY)
|
||||
|
||||
# add G2/G3 move
|
||||
arcmove = Path.Command(
|
||||
arcdir, {"X": endpointvector.x, "Y": endpointvector.y, "I": offsetvector.x, "J": offsetvector.y})
|
||||
arcdir,
|
||||
{
|
||||
"X": endpointvector.x,
|
||||
"Y": endpointvector.y,
|
||||
"I": offsetvector.x,
|
||||
"J": offsetvector.y,
|
||||
},
|
||||
)
|
||||
results.append(arcmove)
|
||||
currLocation.update(arcmove.Parameters)
|
||||
|
||||
@@ -230,11 +288,13 @@ class ObjectDressup:
|
||||
offsetv = arccenter.sub(endpointvector)
|
||||
|
||||
replace = Path.Command(
|
||||
queue[0].Name, {"X": queue[0].X, "Y": queue[0].Y, "I": offsetv.x, "J": offsetv.y})
|
||||
queue[0].Name,
|
||||
{"X": queue[0].X, "Y": queue[0].Y, "I": offsetv.x, "J": offsetv.y},
|
||||
)
|
||||
return (results, replace)
|
||||
|
||||
def lineExtension(self, obj, queue):
|
||||
'''returns gcode for line extension'''
|
||||
"""returns gcode for line extension"""
|
||||
global currLocation # pylint: disable=global-statement
|
||||
|
||||
offset = float(obj.offset)
|
||||
@@ -243,14 +303,16 @@ class ObjectDressup:
|
||||
v1 = queue[1].Placement.Base.sub(queue[2].Placement.Base)
|
||||
|
||||
# extend the current segment to comp for offset
|
||||
segAngle = D.angle(v1, FreeCAD.Base.Vector(1, 0, 0), FreeCAD.Base.Vector(0, 0, -1))
|
||||
segAngle = D.angle(
|
||||
v1, FreeCAD.Base.Vector(1, 0, 0), FreeCAD.Base.Vector(0, 0, -1)
|
||||
)
|
||||
xoffset = math.cos(segAngle) * offset
|
||||
yoffset = math.sin(segAngle) * offset
|
||||
|
||||
newX = currLocation["X"] + xoffset
|
||||
newY = currLocation["Y"] + yoffset
|
||||
|
||||
extendcommand = Path.Command('G1', {"X": newX, "Y": newY})
|
||||
extendcommand = Path.Command("G1", {"X": newX, "Y": newY})
|
||||
results.append(extendcommand)
|
||||
|
||||
currLocation.update(extendcommand.Parameters)
|
||||
@@ -259,8 +321,8 @@ class ObjectDressup:
|
||||
return (results, replace)
|
||||
|
||||
def lineTwist(self, obj, queue, lastXY, twistCW=False):
|
||||
'''returns gcode to do an arc move toward a line to perform
|
||||
a corner action twist. Includes lifting and plungeing the knife'''
|
||||
"""returns gcode to do an arc move toward a line to perform
|
||||
a corner action twist. Includes lifting and plungeing the knife"""
|
||||
global currLocation # pylint: disable=global-statement
|
||||
pivotheight = obj.pivotheight
|
||||
offset = obj.offset
|
||||
@@ -285,7 +347,9 @@ class ObjectDressup:
|
||||
v2 = queue[0].Placement.Base.sub(queue[1].Placement.Base)
|
||||
|
||||
# calc arc endpoints to twist to
|
||||
segAngle = D.angle(v2, FreeCAD.Base.Vector(1, 0, 0), FreeCAD.Base.Vector(0, 0, -1))
|
||||
segAngle = D.angle(
|
||||
v2, FreeCAD.Base.Vector(1, 0, 0), FreeCAD.Base.Vector(0, 0, -1)
|
||||
)
|
||||
xoffset = math.cos(segAngle) * offset
|
||||
yoffset = math.sin(segAngle) * offset
|
||||
newX = queue[1].x + xoffset
|
||||
@@ -297,7 +361,8 @@ class ObjectDressup:
|
||||
|
||||
# add the arc move
|
||||
arcmove = Path.Command(
|
||||
arcdir, {"X": newX, "Y": newY, "I": I, "J": J}) # add G2/G3 move
|
||||
arcdir, {"X": newX, "Y": newY, "I": I, "J": J}
|
||||
) # add G2/G3 move
|
||||
results.append(arcmove)
|
||||
|
||||
currLocation.update(arcmove.Parameters)
|
||||
@@ -335,11 +400,11 @@ class ObjectDressup:
|
||||
continue
|
||||
|
||||
if curCommand.x is None:
|
||||
curCommand.x = currLocation['X']
|
||||
curCommand.x = currLocation["X"]
|
||||
if curCommand.y is None:
|
||||
curCommand.y = currLocation['Y']
|
||||
curCommand.y = currLocation["Y"]
|
||||
if curCommand.z is None:
|
||||
curCommand.z = currLocation['Z']
|
||||
curCommand.z = currLocation["Z"]
|
||||
|
||||
# rapid retract triggers exit move, else just add to output
|
||||
if curCommand.Name in rapidcommands:
|
||||
@@ -348,7 +413,7 @@ class ObjectDressup:
|
||||
tempqueue = queue
|
||||
tempqueue.insert(0, curCommand)
|
||||
|
||||
if queue[1].Name in ['G01', 'G1']:
|
||||
if queue[1].Name in ["G01", "G1"]:
|
||||
temp = self.lineExtension(obj, tempqueue)
|
||||
newpath.extend(temp[0])
|
||||
lastxy = temp[0][-1].Placement.Base
|
||||
@@ -395,7 +460,7 @@ class ObjectDressup:
|
||||
#
|
||||
# DO THE EXTENSION
|
||||
#
|
||||
if queue[1].Name in ['G01', 'G1']:
|
||||
if queue[1].Name in ["G01", "G1"]:
|
||||
temp = self.lineExtension(obj, queue)
|
||||
newpath.extend(temp[0])
|
||||
replace = temp[1]
|
||||
@@ -410,7 +475,7 @@ class ObjectDressup:
|
||||
#
|
||||
# DO THE TWIST
|
||||
#
|
||||
if queue[0].Name in ['G01', 'G1']:
|
||||
if queue[0].Name in ["G01", "G1"]:
|
||||
temp = self.lineTwist(obj, queue, lastxy, twistCW)
|
||||
replace = temp[1]
|
||||
newpath.extend(temp[0])
|
||||
@@ -433,15 +498,22 @@ class ObjectDressup:
|
||||
|
||||
|
||||
class TaskPanel:
|
||||
|
||||
def __init__(self, obj):
|
||||
self.obj = obj
|
||||
self.form = FreeCADGui.PySideUic.loadUi(":/panels/DragKnifeEdit.ui")
|
||||
self.filterAngle = PathGui.QuantitySpinBox(self.form.filterAngle, obj, 'filterAngle')
|
||||
self.offsetDistance = PathGui.QuantitySpinBox(self.form.offsetDistance, obj, 'offset')
|
||||
self.pivotHeight = PathGui.QuantitySpinBox(self.form.pivotHeight, obj, 'pivotheight')
|
||||
self.filterAngle = PathGui.QuantitySpinBox(
|
||||
self.form.filterAngle, obj, "filterAngle"
|
||||
)
|
||||
self.offsetDistance = PathGui.QuantitySpinBox(
|
||||
self.form.offsetDistance, obj, "offset"
|
||||
)
|
||||
self.pivotHeight = PathGui.QuantitySpinBox(
|
||||
self.form.pivotHeight, obj, "pivotheight"
|
||||
)
|
||||
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("Path_DressupDragKnife", "Edit Dragknife Dress-up"))
|
||||
FreeCAD.ActiveDocument.openTransaction(
|
||||
translate("Path_DressupDragKnife", "Edit Dragknife Dress-up")
|
||||
)
|
||||
|
||||
def reject(self):
|
||||
FreeCAD.ActiveDocument.abortTransaction()
|
||||
@@ -483,7 +555,6 @@ class TaskPanel:
|
||||
|
||||
|
||||
class ViewProviderDressup:
|
||||
|
||||
def __init__(self, vobj):
|
||||
self.Object = vobj.Object
|
||||
|
||||
@@ -537,9 +608,16 @@ class CommandDressupDragknife:
|
||||
# pylint: disable=no-init
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': 'Path_Dressup',
|
||||
'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_DressupDragKnife", "DragKnife Dress-up"),
|
||||
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_DressupDragKnife", "Modifies a path to add dragknife corner actions")}
|
||||
return {
|
||||
"Pixmap": "Path_Dressup",
|
||||
"MenuText": QtCore.QT_TRANSLATE_NOOP(
|
||||
"Path_DressupDragKnife", "DragKnife Dress-up"
|
||||
),
|
||||
"ToolTip": QtCore.QT_TRANSLATE_NOOP(
|
||||
"Path_DressupDragKnife",
|
||||
"Modifies a path to add dragknife corner actions",
|
||||
),
|
||||
}
|
||||
|
||||
def IsActive(self):
|
||||
if FreeCAD.ActiveDocument is not None:
|
||||
@@ -554,33 +632,46 @@ class CommandDressupDragknife:
|
||||
selection = FreeCADGui.Selection.getSelection()
|
||||
if len(selection) != 1:
|
||||
FreeCAD.Console.PrintError(
|
||||
translate("Path_DressupDragKnife", "Please select one path object")+"\n")
|
||||
translate("Path_DressupDragKnife", "Please select one path object")
|
||||
+ "\n"
|
||||
)
|
||||
return
|
||||
if not selection[0].isDerivedFrom("Path::Feature"):
|
||||
FreeCAD.Console.PrintError(
|
||||
translate("Path_DressupDragKnife", "The selected object is not a path")+"\n")
|
||||
translate("Path_DressupDragKnife", "The selected object is not a path")
|
||||
+ "\n"
|
||||
)
|
||||
return
|
||||
if selection[0].isDerivedFrom("Path::FeatureCompoundPython"):
|
||||
FreeCAD.Console.PrintError(
|
||||
translate("Path_DressupDragKnife", "Please select a Path object"))
|
||||
translate("Path_DressupDragKnife", "Please select a Path object")
|
||||
)
|
||||
return
|
||||
|
||||
# everything ok!
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("Path_DressupDragKnife", "Create Dress-up"))
|
||||
FreeCAD.ActiveDocument.openTransaction(
|
||||
translate("Path_DressupDragKnife", "Create Dress-up")
|
||||
)
|
||||
FreeCADGui.addModule("PathScripts.PathDressupDragknife")
|
||||
FreeCADGui.addModule("PathScripts.PathUtils")
|
||||
FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","DragknifeDressup")')
|
||||
FreeCADGui.doCommand('PathScripts.PathDressupDragknife.ObjectDressup(obj)')
|
||||
FreeCADGui.doCommand('base = FreeCAD.ActiveDocument.' + selection[0].Name)
|
||||
FreeCADGui.doCommand('job = PathScripts.PathUtils.findParentJob(base)')
|
||||
FreeCADGui.doCommand('obj.Base = base')
|
||||
FreeCADGui.doCommand('job.Proxy.addOperation(obj, base)')
|
||||
FreeCADGui.doCommand('obj.ViewObject.Proxy = PathScripts.PathDressupDragknife.ViewProviderDressup(obj.ViewObject)')
|
||||
FreeCADGui.doCommand('Gui.ActiveDocument.getObject(base.Name).Visibility = False')
|
||||
FreeCADGui.doCommand('obj.filterAngle = 20')
|
||||
FreeCADGui.doCommand('obj.offset = 2')
|
||||
FreeCADGui.doCommand('obj.pivotheight = 4')
|
||||
FreeCADGui.doCommand('obj.ViewObject.Document.setEdit(obj.ViewObject, 0)')
|
||||
FreeCADGui.doCommand(
|
||||
'obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","DragknifeDressup")'
|
||||
)
|
||||
FreeCADGui.doCommand("PathScripts.PathDressupDragknife.ObjectDressup(obj)")
|
||||
FreeCADGui.doCommand("base = FreeCAD.ActiveDocument." + selection[0].Name)
|
||||
FreeCADGui.doCommand("job = PathScripts.PathUtils.findParentJob(base)")
|
||||
FreeCADGui.doCommand("obj.Base = base")
|
||||
FreeCADGui.doCommand("job.Proxy.addOperation(obj, base)")
|
||||
FreeCADGui.doCommand(
|
||||
"obj.ViewObject.Proxy = PathScripts.PathDressupDragknife.ViewProviderDressup(obj.ViewObject)"
|
||||
)
|
||||
FreeCADGui.doCommand(
|
||||
"Gui.ActiveDocument.getObject(base.Name).Visibility = False"
|
||||
)
|
||||
FreeCADGui.doCommand("obj.filterAngle = 20")
|
||||
FreeCADGui.doCommand("obj.offset = 2")
|
||||
FreeCADGui.doCommand("obj.pivotheight = 4")
|
||||
FreeCADGui.doCommand("obj.ViewObject.Document.setEdit(obj.ViewObject, 0)")
|
||||
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
@@ -588,6 +679,6 @@ class CommandDressupDragknife:
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
# register the FreeCAD command
|
||||
FreeCADGui.addCommand('Path_DressupDragKnife', CommandDressupDragknife())
|
||||
FreeCADGui.addCommand("Path_DressupDragKnife", CommandDressupDragknife())
|
||||
|
||||
FreeCAD.Console.PrintLog("Loading Path_DressupDragKnife... done\n")
|
||||
|
||||
Reference in New Issue
Block a user