Merge pull request #5436 from sliptonic/bug/translationJob
[PATH] More translation cleanup (job, profile, dressups)
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -21,6 +21,9 @@
|
||||
# ***************************************************************************
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from PySide import QtCore
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
import FreeCAD
|
||||
import Path
|
||||
import PathScripts.PathDressup as PathDressup
|
||||
@@ -29,48 +32,49 @@ import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathUtil as PathUtil
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
import math
|
||||
|
||||
from PySide import QtCore
|
||||
from PathScripts.PathGeom import CmdMoveCW, CmdMoveStraight, CmdMoveArc, CmdMoveRapid
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
DraftGeomUtils = LazyLoader('DraftGeomUtils', globals(), 'DraftGeomUtils')
|
||||
Part = LazyLoader('Part', globals(), 'Part')
|
||||
|
||||
LOG_MODULE = PathLog.thisModule()
|
||||
|
||||
PathLog.setLevel(PathLog.Level.NOTICE, LOG_MODULE)
|
||||
#PathLog.trackModule(LOG_MODULE)
|
||||
DraftGeomUtils = LazyLoader("DraftGeomUtils", globals(), "DraftGeomUtils")
|
||||
Part = LazyLoader("Part", globals(), "Part")
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
movecommands = ['G0', 'G00', 'G1', 'G01', 'G2', 'G02', 'G3', 'G03']
|
||||
movestraight = ['G1', 'G01']
|
||||
movecw = ['G2', 'G02']
|
||||
moveccw = ['G3', 'G03']
|
||||
movearc = movecw + moveccw
|
||||
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
movecommands = CmdMoveStraight + CmdMoveRapid + CmdMoveArc
|
||||
|
||||
|
||||
def debugMarker(vector, label, color=None, radius=0.5):
|
||||
if PathLog.getLevel(LOG_MODULE) == PathLog.Level.DEBUG:
|
||||
if PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::Sphere", label)
|
||||
obj.Label = label
|
||||
obj.Radius = radius
|
||||
obj.Placement = FreeCAD.Placement(vector, FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0))
|
||||
obj.Placement = FreeCAD.Placement(
|
||||
vector, FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0)
|
||||
)
|
||||
if color:
|
||||
obj.ViewObject.ShapeColor = color
|
||||
|
||||
|
||||
def debugCircle(vector, r, label, color=None):
|
||||
if PathLog.getLevel(LOG_MODULE) == PathLog.Level.DEBUG:
|
||||
if PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::Cylinder", label)
|
||||
obj.Label = label
|
||||
obj.Radius = r
|
||||
obj.Height = 1
|
||||
obj.Placement = FreeCAD.Placement(vector, FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0))
|
||||
obj.Placement = FreeCAD.Placement(
|
||||
vector, FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 0)
|
||||
)
|
||||
obj.ViewObject.Transparency = 90
|
||||
if color:
|
||||
obj.ViewObject.ShapeColor = color
|
||||
@@ -79,9 +83,9 @@ def debugCircle(vector, r, label, color=None):
|
||||
def addAngle(a1, a2):
|
||||
a = a1 + a2
|
||||
while a <= -math.pi:
|
||||
a += 2*math.pi
|
||||
a += 2 * math.pi
|
||||
while a > math.pi:
|
||||
a -= 2*math.pi
|
||||
a -= 2 * math.pi
|
||||
return a
|
||||
|
||||
|
||||
@@ -102,7 +106,7 @@ def getAngle(v):
|
||||
return a
|
||||
|
||||
|
||||
def pointFromCommand(cmd, pt, X='X', Y='Y', Z='Z'):
|
||||
def pointFromCommand(cmd, pt, X="X", Y="Y", Z="Z"):
|
||||
x = cmd.Parameters.get(X, pt.x)
|
||||
y = cmd.Parameters.get(Y, pt.y)
|
||||
z = cmd.Parameters.get(Z, pt.z)
|
||||
@@ -115,18 +119,20 @@ def edgesForCommands(cmds, startPt):
|
||||
for cmd in cmds:
|
||||
if cmd.Name in movecommands:
|
||||
pt = pointFromCommand(cmd, lastPt)
|
||||
if cmd.Name in movestraight:
|
||||
if cmd.Name in CmdMoveStraight:
|
||||
edges.append(Part.Edge(Part.LineSegment(lastPt, pt)))
|
||||
elif cmd.Name in movearc:
|
||||
center = lastPt + pointFromCommand(cmd, FreeCAD.Vector(0, 0, 0), 'I', 'J', 'K')
|
||||
elif cmd.Name in CmdMoveArc:
|
||||
center = lastPt + pointFromCommand(
|
||||
cmd, FreeCAD.Vector(0, 0, 0), "I", "J", "K"
|
||||
)
|
||||
A = lastPt - center
|
||||
B = pt - center
|
||||
d = -B.x * A.y + B.y * A.x
|
||||
|
||||
if d == 0:
|
||||
# we're dealing with half a circle here
|
||||
angle = getAngle(A) + math.pi/2
|
||||
if cmd.Name in movecw:
|
||||
angle = getAngle(A) + math.pi / 2
|
||||
if cmd.Name in CmdMoveCW:
|
||||
angle -= math.pi
|
||||
else:
|
||||
C = A + B
|
||||
@@ -143,19 +149,19 @@ def edgesForCommands(cmds, startPt):
|
||||
class Style(object):
|
||||
# pylint: disable=no-init
|
||||
|
||||
Dogbone = 'Dogbone'
|
||||
Tbone_H = 'T-bone horizontal'
|
||||
Tbone_V = 'T-bone vertical'
|
||||
Tbone_L = 'T-bone long edge'
|
||||
Tbone_S = 'T-bone short edge'
|
||||
Dogbone = "Dogbone"
|
||||
Tbone_H = "T-bone horizontal"
|
||||
Tbone_V = "T-bone vertical"
|
||||
Tbone_L = "T-bone long edge"
|
||||
Tbone_S = "T-bone short edge"
|
||||
All = [Dogbone, Tbone_H, Tbone_V, Tbone_L, Tbone_S]
|
||||
|
||||
|
||||
class Side(object):
|
||||
# pylint: disable=no-init
|
||||
|
||||
Left = 'Left'
|
||||
Right = 'Right'
|
||||
Left = "Left"
|
||||
Right = "Right"
|
||||
All = [Left, Right]
|
||||
|
||||
@classmethod
|
||||
@@ -170,9 +176,9 @@ class Side(object):
|
||||
class Incision(object):
|
||||
# pylint: disable=no-init
|
||||
|
||||
Fixed = 'fixed'
|
||||
Adaptive = 'adaptive'
|
||||
Custom = 'custom'
|
||||
Fixed = "fixed"
|
||||
Adaptive = "adaptive"
|
||||
Custom = "custom"
|
||||
All = [Adaptive, Fixed, Custom]
|
||||
|
||||
|
||||
@@ -194,8 +200,7 @@ class Smooth(object):
|
||||
# be constant in all calculated results.
|
||||
# Instances of Chord are generally considered immutable and all movement member
|
||||
# functions return new instances.
|
||||
class Chord (object):
|
||||
|
||||
class Chord(object):
|
||||
def __init__(self, start=None, end=None):
|
||||
if not start:
|
||||
start = FreeCAD.Vector()
|
||||
@@ -205,15 +210,22 @@ class Chord (object):
|
||||
self.End = end
|
||||
|
||||
def __str__(self):
|
||||
return "Chord([%g, %g, %g] -> [%g, %g, %g])" % (self.Start.x, self.Start.y, self.Start.z, self.End.x, self.End.y, self.End.z)
|
||||
return "Chord([%g, %g, %g] -> [%g, %g, %g])" % (
|
||||
self.Start.x,
|
||||
self.Start.y,
|
||||
self.Start.z,
|
||||
self.End.x,
|
||||
self.End.y,
|
||||
self.End.z,
|
||||
)
|
||||
|
||||
def moveTo(self, newEnd):
|
||||
return Chord(self.End, newEnd)
|
||||
|
||||
def moveToParameters(self, params):
|
||||
x = params.get('X', self.End.x)
|
||||
y = params.get('Y', self.End.y)
|
||||
z = params.get('Z', self.End.z)
|
||||
x = params.get("X", self.End.x)
|
||||
y = params.get("Y", self.End.y)
|
||||
z = params.get("Z", self.End.z)
|
||||
return self.moveTo(FreeCAD.Vector(x, y, z))
|
||||
|
||||
def moveBy(self, x, y, z):
|
||||
@@ -244,14 +256,14 @@ class Chord (object):
|
||||
# if the 2 vectors are identical, they head in the same direction
|
||||
PathLog.debug(" {}.getDirectionOfVector({})".format(A, B))
|
||||
if PathGeom.pointsCoincide(A, B):
|
||||
return 'Straight'
|
||||
d = -A.x*B.y + A.y*B.x
|
||||
return "Straight"
|
||||
d = -A.x * B.y + A.y * B.x
|
||||
if d < 0:
|
||||
return Side.Left
|
||||
if d > 0:
|
||||
return Side.Right
|
||||
# at this point the only direction left is backwards
|
||||
return 'Back'
|
||||
return "Back"
|
||||
|
||||
def getDirectionOf(self, chordOrVector):
|
||||
if type(chordOrVector) is Chord:
|
||||
@@ -278,7 +290,7 @@ class Chord (object):
|
||||
def commandParams(self, f):
|
||||
params = {"X": self.End.x, "Y": self.End.y, "Z": self.End.z}
|
||||
if f:
|
||||
params['F'] = f
|
||||
params["F"] = f
|
||||
return params
|
||||
|
||||
def g1Command(self, f):
|
||||
@@ -287,9 +299,9 @@ class Chord (object):
|
||||
def arcCommand(self, cmd, center, f):
|
||||
params = self.commandParams(f)
|
||||
d = center - self.Start
|
||||
params['I'] = d.x
|
||||
params['J'] = d.y
|
||||
params['K'] = 0
|
||||
params["I"] = d.x
|
||||
params["J"] = d.y
|
||||
params["K"] = 0
|
||||
return Path.Command(cmd, params)
|
||||
|
||||
def g2Command(self, center, f):
|
||||
@@ -302,13 +314,17 @@ class Chord (object):
|
||||
return not PathGeom.isRoughly(self.End.z, self.Start.z)
|
||||
|
||||
def isANoopMove(self):
|
||||
PathLog.debug("{}.isANoopMove(): {}".format(self, PathGeom.pointsCoincide(self.Start, self.End)))
|
||||
PathLog.debug(
|
||||
"{}.isANoopMove(): {}".format(
|
||||
self, PathGeom.pointsCoincide(self.Start, self.End)
|
||||
)
|
||||
)
|
||||
return PathGeom.pointsCoincide(self.Start, self.End)
|
||||
|
||||
def foldsBackOrTurns(self, chord, side):
|
||||
direction = chord.getDirectionOf(self)
|
||||
PathLog.info(" - direction = %s/%s" % (direction, side))
|
||||
return direction == 'Back' or direction == side
|
||||
return direction == "Back" or direction == side
|
||||
|
||||
def connectsTo(self, chord):
|
||||
return PathGeom.pointsCoincide(self.End, chord.Start)
|
||||
@@ -335,7 +351,7 @@ class Bone(object):
|
||||
if self.cAngle is None:
|
||||
baseAngle = self.inChord.getAngleXY()
|
||||
turnAngle = self.outChord.getAngle(self.inChord)
|
||||
theta = addAngle(baseAngle, (turnAngle - math.pi)/2)
|
||||
theta = addAngle(baseAngle, (turnAngle - math.pi) / 2)
|
||||
if self.obj.Side == Side.Left:
|
||||
theta = addAngle(theta, math.pi)
|
||||
self.tAngle = turnAngle
|
||||
@@ -345,7 +361,7 @@ class Bone(object):
|
||||
def distance(self, toolRadius):
|
||||
if self.cDist is None:
|
||||
self.angle() # make sure the angles are initialized
|
||||
self.cDist = toolRadius / math.cos(self.tAngle/2)
|
||||
self.cDist = toolRadius / math.cos(self.tAngle / 2)
|
||||
return self.cDist
|
||||
|
||||
def corner(self, toolRadius):
|
||||
@@ -368,7 +384,10 @@ class Bone(object):
|
||||
# moving directly towards the corner
|
||||
PathLog.debug("adaptive - on target: %.2f - %.2f" % (distance, toolRadius))
|
||||
return distance - toolRadius
|
||||
PathLog.debug("adaptive - angles: corner=%.2f bone=%.2f diff=%.12f" % (theta/math.pi, boneAngle/math.pi, theta - boneAngle))
|
||||
PathLog.debug(
|
||||
"adaptive - angles: corner=%.2f bone=%.2f diff=%.12f"
|
||||
% (theta / math.pi, boneAngle / math.pi, theta - boneAngle)
|
||||
)
|
||||
|
||||
# The bones root and end point form a triangle with the intersection of the tool path
|
||||
# with the toolRadius circle around the bone end point.
|
||||
@@ -376,7 +395,9 @@ class Bone(object):
|
||||
# c = distance
|
||||
# b = self.toolRadius
|
||||
# beta = fabs(boneAngle - theta)
|
||||
beta = math.fabs(addAngle(boneAngle, -theta)) # pylint: disable=invalid-unary-operand-type
|
||||
beta = math.fabs(
|
||||
addAngle(boneAngle, -theta)
|
||||
) # pylint: disable=invalid-unary-operand-type
|
||||
D = (distance / toolRadius) * math.sin(beta)
|
||||
if D > 1: # no intersection
|
||||
PathLog.debug("adaptive - no intersection - no bone")
|
||||
@@ -395,28 +416,68 @@ class Bone(object):
|
||||
length2 = toolRadius * math.sin(alpha2) / math.sin(beta2)
|
||||
length = min(length, length2)
|
||||
|
||||
PathLog.debug("adaptive corner=%.2f * %.2f˚ -> bone=%.2f * %.2f˚" % (distance, theta, length, boneAngle))
|
||||
PathLog.debug(
|
||||
"adaptive corner=%.2f * %.2f˚ -> bone=%.2f * %.2f˚"
|
||||
% (distance, theta, length, boneAngle)
|
||||
)
|
||||
return length
|
||||
|
||||
|
||||
class ObjectDressup(object):
|
||||
|
||||
def __init__(self, obj, base):
|
||||
# Tool Properties
|
||||
obj.addProperty("App::PropertyLink", "Base", "Base", QtCore.QT_TRANSLATE_NOOP("Path_DressupDogbone", "The base path to modify"))
|
||||
obj.addProperty("App::PropertyEnumeration", "Side", "Dressup", QtCore.QT_TRANSLATE_NOOP("Path_DressupDogbone", "The side of path to insert bones"))
|
||||
obj.addProperty(
|
||||
"App::PropertyLink",
|
||||
"Base",
|
||||
"Base",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The base path to modify"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"Side",
|
||||
"Dressup",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "The side of path to insert bones"
|
||||
),
|
||||
)
|
||||
obj.Side = [Side.Left, Side.Right]
|
||||
obj.Side = Side.Right
|
||||
obj.addProperty("App::PropertyEnumeration", "Style", "Dressup", QtCore.QT_TRANSLATE_NOOP("Path_DressupDogbone", "The style of bones"))
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"Style",
|
||||
"Dressup",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The style of bones"),
|
||||
)
|
||||
obj.Style = Style.All
|
||||
obj.Style = Style.Dogbone
|
||||
obj.addProperty("App::PropertyIntegerList", "BoneBlacklist", "Dressup", QtCore.QT_TRANSLATE_NOOP("Path_DressupDogbone", "Bones that aren't dressed up"))
|
||||
obj.addProperty(
|
||||
"App::PropertyIntegerList",
|
||||
"BoneBlacklist",
|
||||
"Dressup",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Bones that aren't dressed up"
|
||||
),
|
||||
)
|
||||
obj.BoneBlacklist = []
|
||||
obj.setEditorMode('BoneBlacklist', 2) # hide this one
|
||||
obj.addProperty("App::PropertyEnumeration", "Incision", "Dressup", QtCore.QT_TRANSLATE_NOOP("Path_DressupDogbone", "The algorithm to determine the bone length"))
|
||||
obj.setEditorMode("BoneBlacklist", 2) # hide this one
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"Incision",
|
||||
"Dressup",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "The algorithm to determine the bone length"
|
||||
),
|
||||
)
|
||||
obj.Incision = Incision.All
|
||||
obj.Incision = Incision.Adaptive
|
||||
obj.addProperty("App::PropertyFloat", "Custom", "Dressup", QtCore.QT_TRANSLATE_NOOP("Path_DressupDogbone", "Dressup length if Incision == custom"))
|
||||
obj.addProperty(
|
||||
"App::PropertyFloat",
|
||||
"Custom",
|
||||
"Dressup",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Dressup length if Incision == custom"
|
||||
),
|
||||
)
|
||||
obj.Custom = 0.0
|
||||
obj.Proxy = self
|
||||
obj.Base = base
|
||||
@@ -431,7 +492,7 @@ class ObjectDressup(object):
|
||||
self.bones = None
|
||||
|
||||
def onDocumentRestored(self, obj):
|
||||
obj.setEditorMode('BoneBlacklist', 2) # hide this one
|
||||
obj.setEditorMode("BoneBlacklist", 2) # hide this one
|
||||
|
||||
def __getstate__(self):
|
||||
return None
|
||||
@@ -446,14 +507,29 @@ class ObjectDressup(object):
|
||||
|
||||
# Answer true if a dogbone could be on either end of the chord, given its command
|
||||
def canAttachDogbone(self, cmd, chord):
|
||||
return cmd.Name in movestraight and not chord.isAPlungeMove() and not chord.isANoopMove()
|
||||
return (
|
||||
cmd.Name in CmdMoveStraight
|
||||
and not chord.isAPlungeMove()
|
||||
and not chord.isANoopMove()
|
||||
)
|
||||
|
||||
def shouldInsertDogbone(self, obj, inChord, outChord):
|
||||
return outChord.foldsBackOrTurns(inChord, self.theOtherSideOf(obj.Side))
|
||||
|
||||
def findPivotIntersection(self, pivot, pivotEdge, edge, refPt, d, color):
|
||||
# pylint: disable=unused-argument
|
||||
PathLog.track("(%.2f, %.2f)^%.2f - [(%.2f, %.2f), (%.2f, %.2f)]" % (pivotEdge.Curve.Center.x, pivotEdge.Curve.Center.y, pivotEdge.Curve.Radius, edge.Vertexes[0].Point.x, edge.Vertexes[0].Point.y, edge.Vertexes[1].Point.x, edge.Vertexes[1].Point.y))
|
||||
PathLog.track(
|
||||
"(%.2f, %.2f)^%.2f - [(%.2f, %.2f), (%.2f, %.2f)]"
|
||||
% (
|
||||
pivotEdge.Curve.Center.x,
|
||||
pivotEdge.Curve.Center.y,
|
||||
pivotEdge.Curve.Radius,
|
||||
edge.Vertexes[0].Point.x,
|
||||
edge.Vertexes[0].Point.y,
|
||||
edge.Vertexes[1].Point.x,
|
||||
edge.Vertexes[1].Point.y,
|
||||
)
|
||||
)
|
||||
ppt = None
|
||||
pptDistance = 0
|
||||
for pt in DraftGeomUtils.findIntersection(edge, pivotEdge, dts=False):
|
||||
@@ -469,7 +545,9 @@ class ObjectDressup(object):
|
||||
PathLog.debug("Taking tangent as intersect %s" % tangent)
|
||||
ppt = pivot + tangent
|
||||
else:
|
||||
PathLog.debug("Taking chord start as intersect %s" % edge.Vertexes[0].Point)
|
||||
PathLog.debug(
|
||||
"Taking chord start as intersect %s" % edge.Vertexes[0].Point
|
||||
)
|
||||
ppt = edge.Vertexes[0].Point
|
||||
# debugMarker(ppt, "ptt.%d-%s.in" % (self.boneId, d), color, 0.2)
|
||||
PathLog.debug(" --> (%.2f, %.2f)" % (ppt.x, ppt.y))
|
||||
@@ -479,15 +557,17 @@ class ObjectDressup(object):
|
||||
param = edge.Curve.parameter(point)
|
||||
return edge.FirstParameter <= param <= edge.LastParameter
|
||||
|
||||
def smoothChordCommands(self, bone, inChord, outChord, edge, wire, corner, smooth, color=None):
|
||||
def smoothChordCommands(
|
||||
self, bone, inChord, outChord, edge, wire, corner, smooth, color=None
|
||||
):
|
||||
if smooth == 0:
|
||||
PathLog.info(" No smoothing requested")
|
||||
return [bone.lastCommand, outChord.g1Command(bone.F)]
|
||||
|
||||
d = 'in'
|
||||
d = "in"
|
||||
refPoint = inChord.Start
|
||||
if smooth == Smooth.Out:
|
||||
d = 'out'
|
||||
d = "out"
|
||||
refPoint = outChord.End
|
||||
|
||||
if DraftGeomUtils.areColinear(inChord.asEdge(), outChord.asEdge()):
|
||||
@@ -497,15 +577,36 @@ class ObjectDressup(object):
|
||||
pivot = None
|
||||
pivotDistance = 0
|
||||
|
||||
PathLog.info("smooth: (%.2f, %.2f)-(%.2f, %.2f)" % (edge.Vertexes[0].Point.x, edge.Vertexes[0].Point.y, edge.Vertexes[1].Point.x, edge.Vertexes[1].Point.y))
|
||||
PathLog.info(
|
||||
"smooth: (%.2f, %.2f)-(%.2f, %.2f)"
|
||||
% (
|
||||
edge.Vertexes[0].Point.x,
|
||||
edge.Vertexes[0].Point.y,
|
||||
edge.Vertexes[1].Point.x,
|
||||
edge.Vertexes[1].Point.y,
|
||||
)
|
||||
)
|
||||
for e in wire.Edges:
|
||||
self.dbg.append(e)
|
||||
if type(e.Curve) == Part.LineSegment or type(e.Curve) == Part.Line:
|
||||
PathLog.debug(" (%.2f, %.2f)-(%.2f, %.2f)" % (e.Vertexes[0].Point.x, e.Vertexes[0].Point.y, e.Vertexes[1].Point.x, e.Vertexes[1].Point.y))
|
||||
PathLog.debug(
|
||||
" (%.2f, %.2f)-(%.2f, %.2f)"
|
||||
% (
|
||||
e.Vertexes[0].Point.x,
|
||||
e.Vertexes[0].Point.y,
|
||||
e.Vertexes[1].Point.x,
|
||||
e.Vertexes[1].Point.y,
|
||||
)
|
||||
)
|
||||
else:
|
||||
PathLog.debug(" (%.2f, %.2f)^%.2f" % (e.Curve.Center.x, e.Curve.Center.y, e.Curve.Radius))
|
||||
PathLog.debug(
|
||||
" (%.2f, %.2f)^%.2f"
|
||||
% (e.Curve.Center.x, e.Curve.Center.y, e.Curve.Radius)
|
||||
)
|
||||
for pt in DraftGeomUtils.findIntersection(edge, e, True, findAll=True):
|
||||
if not PathGeom.pointsCoincide(pt, corner) and self.pointIsOnEdge(pt, e):
|
||||
if not PathGeom.pointsCoincide(pt, corner) and self.pointIsOnEdge(
|
||||
pt, e
|
||||
):
|
||||
# debugMarker(pt, "candidate-%d-%s" % (self.boneId, d), color, 0.05)
|
||||
PathLog.debug(" -> candidate")
|
||||
distance = (pt - refPoint).Length
|
||||
@@ -518,9 +619,15 @@ class ObjectDressup(object):
|
||||
if pivot:
|
||||
# debugCircle(pivot, self.toolRadius, "pivot.%d-%s" % (self.boneId, d), color)
|
||||
|
||||
pivotEdge = Part.Edge(Part.Circle(pivot, FreeCAD.Vector(0, 0, 1), self.toolRadius))
|
||||
t1 = self.findPivotIntersection(pivot, pivotEdge, inChord.asEdge(), inChord.End, d, color)
|
||||
t2 = self.findPivotIntersection(pivot, pivotEdge, outChord.asEdge(), inChord.End, d, color)
|
||||
pivotEdge = Part.Edge(
|
||||
Part.Circle(pivot, FreeCAD.Vector(0, 0, 1), self.toolRadius)
|
||||
)
|
||||
t1 = self.findPivotIntersection(
|
||||
pivot, pivotEdge, inChord.asEdge(), inChord.End, d, color
|
||||
)
|
||||
t2 = self.findPivotIntersection(
|
||||
pivot, pivotEdge, outChord.asEdge(), inChord.End, d, color
|
||||
)
|
||||
|
||||
commands = []
|
||||
if not PathGeom.pointsCoincide(t1, inChord.Start):
|
||||
@@ -530,7 +637,10 @@ class ObjectDressup(object):
|
||||
PathLog.debug(" add g3 command")
|
||||
commands.append(Chord(t1, t2).g3Command(pivot, bone.F))
|
||||
else:
|
||||
PathLog.debug(" add g2 command center=(%.2f, %.2f) -> from (%2f, %.2f) to (%.2f, %.2f" % (pivot.x, pivot.y, t1.x, t1.y, t2.x, t2.y))
|
||||
PathLog.debug(
|
||||
" add g2 command center=(%.2f, %.2f) -> from (%2f, %.2f) to (%.2f, %.2f"
|
||||
% (pivot.x, pivot.y, t1.x, t1.y, t2.x, t2.y)
|
||||
)
|
||||
commands.append(Chord(t1, t2).g2Command(pivot, bone.F))
|
||||
if not PathGeom.pointsCoincide(t2, outChord.End):
|
||||
PathLog.debug(" add lead out")
|
||||
@@ -575,7 +685,12 @@ class ObjectDressup(object):
|
||||
bone.tip = boneInChord.End
|
||||
|
||||
if bone.smooth == 0:
|
||||
return [bone.lastCommand, boneInChord.g1Command(bone.F), boneOutChord.g1Command(bone.F), bone.outChord.g1Command(bone.F)]
|
||||
return [
|
||||
bone.lastCommand,
|
||||
boneInChord.g1Command(bone.F),
|
||||
boneOutChord.g1Command(bone.F),
|
||||
bone.outChord.g1Command(bone.F),
|
||||
]
|
||||
|
||||
# reconstruct the corner and convert to an edge
|
||||
offset = corner - bone.inChord.End
|
||||
@@ -587,7 +702,7 @@ class ObjectDressup(object):
|
||||
|
||||
# construct a shape representing the cut made by the bone
|
||||
vt0 = FreeCAD.Vector(0, self.toolRadius, 0)
|
||||
vt1 = FreeCAD.Vector(length, self.toolRadius, 0)
|
||||
vt1 = FreeCAD.Vector(length, self.toolRadius, 0)
|
||||
vb0 = FreeCAD.Vector(0, -self.toolRadius, 0)
|
||||
vb1 = FreeCAD.Vector(length, -self.toolRadius, 0)
|
||||
vm2 = FreeCAD.Vector(length + self.toolRadius, 0, 0)
|
||||
@@ -601,12 +716,32 @@ class ObjectDressup(object):
|
||||
boneArc = Part.Arc(vt1, vm2, vb1)
|
||||
# boneArc = Part.Circle(FreeCAD.Vector(length, 0, 0), FreeCAD.Vector(0,0,1), self.toolRadius)
|
||||
boneWire = Part.Shape([boneTop, boneArc, boneBot, boneLid])
|
||||
boneWire.rotate(FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, 0, 1), boneAngle * 180 / math.pi)
|
||||
boneWire.rotate(
|
||||
FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, 0, 1), boneAngle * 180 / math.pi
|
||||
)
|
||||
boneWire.translate(bone.inChord.End)
|
||||
self.boneShapes = [cornerShape, boneWire]
|
||||
|
||||
bone.inCommands = self.smoothChordCommands(bone, bone.inChord, boneInChord, Part.Edge(iLine), boneWire, corner, bone.smooth & Smooth.In, (1., 0., 0.))
|
||||
bone.outCommands = self.smoothChordCommands(bone, boneOutChord, bone.outChord, Part.Edge(oLine), boneWire, corner, bone.smooth & Smooth.Out, (0., 1., 0.))
|
||||
bone.inCommands = self.smoothChordCommands(
|
||||
bone,
|
||||
bone.inChord,
|
||||
boneInChord,
|
||||
Part.Edge(iLine),
|
||||
boneWire,
|
||||
corner,
|
||||
bone.smooth & Smooth.In,
|
||||
(1.0, 0.0, 0.0),
|
||||
)
|
||||
bone.outCommands = self.smoothChordCommands(
|
||||
bone,
|
||||
boneOutChord,
|
||||
bone.outChord,
|
||||
Part.Edge(oLine),
|
||||
boneWire,
|
||||
corner,
|
||||
bone.smooth & Smooth.Out,
|
||||
(0.0, 1.0, 0.0),
|
||||
)
|
||||
return bone.inCommands + bone.outCommands
|
||||
|
||||
def dogbone(self, bone):
|
||||
@@ -617,13 +752,13 @@ class ObjectDressup(object):
|
||||
def tboneHorizontal(self, bone):
|
||||
angle = bone.angle()
|
||||
boneAngle = 0
|
||||
if math.fabs(angle) > math.pi/2:
|
||||
if math.fabs(angle) > math.pi / 2:
|
||||
boneAngle = math.pi
|
||||
return self.inOutBoneCommands(bone, boneAngle, self.toolRadius)
|
||||
|
||||
def tboneVertical(self, bone):
|
||||
angle = bone.angle()
|
||||
boneAngle = math.pi/2
|
||||
boneAngle = math.pi / 2
|
||||
if PathGeom.isRoughly(angle, math.pi) or angle < 0:
|
||||
boneAngle = -boneAngle
|
||||
return self.inOutBoneCommands(bone, boneAngle, self.toolRadius)
|
||||
@@ -635,14 +770,22 @@ class ObjectDressup(object):
|
||||
boneAngle = bone.outChord.getAngleXY()
|
||||
|
||||
if Side.Right == bone.outChord.getDirectionOf(bone.inChord):
|
||||
boneAngle = boneAngle - math.pi/2
|
||||
boneAngle = boneAngle - math.pi / 2
|
||||
else:
|
||||
boneAngle = boneAngle + math.pi/2
|
||||
boneAngle = boneAngle + math.pi / 2
|
||||
|
||||
onInString = 'out'
|
||||
onInString = "out"
|
||||
if onIn:
|
||||
onInString = 'in'
|
||||
PathLog.debug("tboneEdge boneAngle[%s]=%.2f (in=%.2f, out=%.2f)" % (onInString, boneAngle/math.pi, bone.inChord.getAngleXY()/math.pi, bone.outChord.getAngleXY()/math.pi))
|
||||
onInString = "in"
|
||||
PathLog.debug(
|
||||
"tboneEdge boneAngle[%s]=%.2f (in=%.2f, out=%.2f)"
|
||||
% (
|
||||
onInString,
|
||||
boneAngle / math.pi,
|
||||
bone.inChord.getAngleXY() / math.pi,
|
||||
bone.outChord.getAngleXY() / math.pi,
|
||||
)
|
||||
)
|
||||
return self.inOutBoneCommands(bone, boneAngle, self.toolRadius)
|
||||
|
||||
def tboneLongEdge(self, bone):
|
||||
@@ -661,7 +804,7 @@ class ObjectDressup(object):
|
||||
elif bone.location() in self.locationBlacklist:
|
||||
bone.obj.BoneBlacklist.append(bone.boneId)
|
||||
blacklisted = True
|
||||
elif hasattr(bone.obj.Base, 'BoneBlacklist'):
|
||||
elif hasattr(bone.obj.Base, "BoneBlacklist"):
|
||||
parentConsumed = bone.boneId not in bone.obj.Base.BoneBlacklist
|
||||
blacklisted = parentConsumed
|
||||
if blacklisted:
|
||||
@@ -685,7 +828,10 @@ class ObjectDressup(object):
|
||||
return [bone.lastCommand, bone.outChord.g1Command(bone.F)]
|
||||
|
||||
def insertBone(self, bone):
|
||||
PathLog.debug(">----------------------------------- %d --------------------------------------" % bone.boneId)
|
||||
PathLog.debug(
|
||||
">----------------------------------- %d --------------------------------------"
|
||||
% bone.boneId
|
||||
)
|
||||
self.boneShapes = []
|
||||
blacklisted, inaccessible = self.boneIsBlacklisted(bone)
|
||||
enabled = not blacklisted
|
||||
@@ -701,7 +847,10 @@ class ObjectDressup(object):
|
||||
bone.commands = commands
|
||||
|
||||
self.shapes[bone.boneId] = self.boneShapes
|
||||
PathLog.debug("<----------------------------------- %d --------------------------------------" % bone.boneId)
|
||||
PathLog.debug(
|
||||
"<----------------------------------- %d --------------------------------------"
|
||||
% bone.boneId
|
||||
)
|
||||
return commands
|
||||
|
||||
def removePathCrossing(self, commands, bone1, bone2):
|
||||
@@ -709,36 +858,40 @@ class ObjectDressup(object):
|
||||
bones = bone2.commands
|
||||
if True and hasattr(bone1, "outCommands") and hasattr(bone2, "inCommands"):
|
||||
inEdges = edgesForCommands(bone1.outCommands, bone1.tip)
|
||||
outEdges = edgesForCommands(bone2.inCommands, bone2.inChord.Start)
|
||||
outEdges = edgesForCommands(bone2.inCommands, bone2.inChord.Start)
|
||||
for i in range(len(inEdges)):
|
||||
e1 = inEdges[i]
|
||||
for j in range(len(outEdges)-1, -1, -1):
|
||||
for j in range(len(outEdges) - 1, -1, -1):
|
||||
e2 = outEdges[j]
|
||||
cutoff = DraftGeomUtils.findIntersection(e1, e2)
|
||||
for pt in cutoff:
|
||||
# debugCircle(e1.Curve.Center, e1.Curve.Radius, "bone.%d-1" % (self.boneId), (1.,0.,0.))
|
||||
# debugCircle(e2.Curve.Center, e2.Curve.Radius, "bone.%d-2" % (self.boneId), (0.,1.,0.))
|
||||
if PathGeom.pointsCoincide(pt, e1.valueAt(e1.LastParameter)) or PathGeom.pointsCoincide(pt, e2.valueAt(e2.FirstParameter)):
|
||||
if PathGeom.pointsCoincide(
|
||||
pt, e1.valueAt(e1.LastParameter)
|
||||
) or PathGeom.pointsCoincide(pt, e2.valueAt(e2.FirstParameter)):
|
||||
continue
|
||||
# debugMarker(pt, "it", (0.0, 1.0, 1.0))
|
||||
# 1. remove all redundant commands
|
||||
commands = commands[:-(len(inEdges) - i)]
|
||||
commands = commands[: -(len(inEdges) - i)]
|
||||
# 2., correct where c1 ends
|
||||
c1 = bone1.outCommands[i]
|
||||
c1Params = c1.Parameters
|
||||
c1Params.update({'X': pt.x, 'Y': pt.y, 'Z': pt.z})
|
||||
c1Params.update({"X": pt.x, "Y": pt.y, "Z": pt.z})
|
||||
c1 = Path.Command(c1.Name, c1Params)
|
||||
commands.append(c1)
|
||||
# 3. change where c2 starts, this depends on the command itself
|
||||
c2 = bone2.inCommands[j]
|
||||
if c2.Name in movearc:
|
||||
if c2.Name in CmdMoveArc:
|
||||
center = e2.Curve.Center
|
||||
offset = center - pt
|
||||
c2Params = c2.Parameters
|
||||
c2Params.update({'I': offset.x, 'J': offset.y, 'K': offset.z})
|
||||
c2Params.update(
|
||||
{"I": offset.x, "J": offset.y, "K": offset.z}
|
||||
)
|
||||
c2 = Path.Command(c2.Name, c2Params)
|
||||
bones = [c2]
|
||||
bones.extend(bone2.commands[j+1:])
|
||||
bones.extend(bone2.commands[j + 1 :])
|
||||
else:
|
||||
bones = bone2.commands[j:]
|
||||
# there can only be the one ...
|
||||
@@ -758,11 +911,13 @@ class ObjectDressup(object):
|
||||
|
||||
self.setup(obj, False)
|
||||
|
||||
commands = [] # the dressed commands
|
||||
lastChord = Chord() # the last chord
|
||||
lastCommand = None # the command that generated the last chord
|
||||
lastBone = None # track last bone for optimizations
|
||||
oddsAndEnds = [] # track chords that are connected to plunges - in case they form a loop
|
||||
commands = [] # the dressed commands
|
||||
lastChord = Chord() # the last chord
|
||||
lastCommand = None # the command that generated the last chord
|
||||
lastBone = None # track last bone for optimizations
|
||||
oddsAndEnds = (
|
||||
[]
|
||||
) # track chords that are connected to plunges - in case they form a loop
|
||||
|
||||
boneId = 1
|
||||
self.bones = []
|
||||
@@ -782,32 +937,60 @@ class ObjectDressup(object):
|
||||
thisChord = lastChord.moveToParameters(thisCommand.Parameters)
|
||||
thisIsACandidate = self.canAttachDogbone(thisCommand, thisChord)
|
||||
|
||||
if thisIsACandidate and lastCommand and self.shouldInsertDogbone(obj, lastChord, thisChord):
|
||||
if (
|
||||
thisIsACandidate
|
||||
and lastCommand
|
||||
and self.shouldInsertDogbone(obj, lastChord, thisChord)
|
||||
):
|
||||
PathLog.info(" Found bone corner: {}".format(lastChord.End))
|
||||
bone = Bone(boneId, obj, lastCommand, lastChord, thisChord, Smooth.InAndOut, thisCommand.Parameters.get('F'))
|
||||
bone = Bone(
|
||||
boneId,
|
||||
obj,
|
||||
lastCommand,
|
||||
lastChord,
|
||||
thisChord,
|
||||
Smooth.InAndOut,
|
||||
thisCommand.Parameters.get("F"),
|
||||
)
|
||||
bones = self.insertBone(bone)
|
||||
boneId += 1
|
||||
if lastBone:
|
||||
PathLog.info(" removing potential path crossing")
|
||||
# debugMarker(thisChord.Start, "it", (1.0, 0.0, 1.0))
|
||||
commands, bones = self.removePathCrossing(commands, lastBone, bone)
|
||||
commands, bones = self.removePathCrossing(
|
||||
commands, lastBone, bone
|
||||
)
|
||||
commands.extend(bones[:-1])
|
||||
lastCommand = bones[-1]
|
||||
lastBone = bone
|
||||
elif lastCommand and thisChord.isAPlungeMove():
|
||||
PathLog.info(" Looking for connection in odds and ends")
|
||||
haveNewLastCommand = False
|
||||
for chord in (chord for chord in oddsAndEnds if lastChord.connectsTo(chord)):
|
||||
for chord in (
|
||||
chord for chord in oddsAndEnds if lastChord.connectsTo(chord)
|
||||
):
|
||||
if self.shouldInsertDogbone(obj, lastChord, chord):
|
||||
PathLog.info(" and there is one")
|
||||
PathLog.debug(" odd/end={} last={}".format(chord, lastChord))
|
||||
bone = Bone(boneId, obj, lastCommand, lastChord, chord, Smooth.In, lastCommand.Parameters.get('F'))
|
||||
PathLog.debug(
|
||||
" odd/end={} last={}".format(chord, lastChord)
|
||||
)
|
||||
bone = Bone(
|
||||
boneId,
|
||||
obj,
|
||||
lastCommand,
|
||||
lastChord,
|
||||
chord,
|
||||
Smooth.In,
|
||||
lastCommand.Parameters.get("F"),
|
||||
)
|
||||
bones = self.insertBone(bone)
|
||||
boneId += 1
|
||||
if lastBone:
|
||||
PathLog.info(" removing potential path crossing")
|
||||
# debugMarker(chord.Start, "it", (0.0, 1.0, 1.0))
|
||||
commands, bones = self.removePathCrossing(commands, lastBone, bone)
|
||||
commands, bones = self.removePathCrossing(
|
||||
commands, lastBone, bone
|
||||
)
|
||||
commands.extend(bones[:-1])
|
||||
lastCommand = bones[-1]
|
||||
haveNewLastCommand = True
|
||||
@@ -839,7 +1022,7 @@ class ObjectDressup(object):
|
||||
|
||||
lastChord = thisChord
|
||||
else:
|
||||
if thisCommand.Name[0] != '(':
|
||||
if thisCommand.Name[0] != "(":
|
||||
PathLog.info(" Clean slate")
|
||||
if lastCommand:
|
||||
commands.append(lastCommand)
|
||||
@@ -861,12 +1044,12 @@ class ObjectDressup(object):
|
||||
PathLog.info("Default side = right")
|
||||
# otherwise dogbones are opposite of the base path's side
|
||||
side = Side.Right
|
||||
if hasattr(obj.Base, 'Side') and obj.Base.Side == 'Inside':
|
||||
if hasattr(obj.Base, "Side") and obj.Base.Side == "Inside":
|
||||
PathLog.info("inside -> side = left")
|
||||
side = Side.Left
|
||||
else:
|
||||
PathLog.info("not inside -> side stays right")
|
||||
if hasattr(obj.Base, 'Direction') and obj.Base.Direction == 'CCW':
|
||||
if hasattr(obj.Base, "Direction") and obj.Base.Direction == "CCW":
|
||||
PathLog.info("CCW -> switch sides")
|
||||
side = Side.oppositeOf(side)
|
||||
else:
|
||||
@@ -890,7 +1073,7 @@ class ObjectDressup(object):
|
||||
def boneStateList(self, obj):
|
||||
state = {}
|
||||
# If the receiver was loaded from file, then it never generated the bone list.
|
||||
if not hasattr(self, 'bones'):
|
||||
if not hasattr(self, "bones"):
|
||||
self.execute(obj)
|
||||
for (nr, loc, enabled, inaccessible) in self.bones:
|
||||
item = state.get((loc[0], loc[1]))
|
||||
@@ -901,14 +1084,14 @@ class ObjectDressup(object):
|
||||
state[(loc[0], loc[1])] = (enabled, inaccessible, [nr], [loc[2]])
|
||||
return state
|
||||
|
||||
class Marker(object):
|
||||
|
||||
class Marker(object):
|
||||
def __init__(self, pt, r, h):
|
||||
if PathGeom.isRoughly(h, 0):
|
||||
h = 0.1
|
||||
self.pt = pt
|
||||
self.r = r
|
||||
self.h = h
|
||||
self.r = r
|
||||
self.h = h
|
||||
self.sep = coin.SoSeparator()
|
||||
self.pos = coin.SoTranslation()
|
||||
self.pos.translation = (pt.x, pt.y, pt.z + h / 2)
|
||||
@@ -943,8 +1126,8 @@ class Marker(object):
|
||||
|
||||
def color(self, id):
|
||||
if id == 1:
|
||||
return coin.SbColor(.9, .9, .5)
|
||||
return coin.SbColor(.9, .5, .9)
|
||||
return coin.SbColor(0.9, 0.9, 0.5)
|
||||
return coin.SbColor(0.9, 0.5, 0.9)
|
||||
|
||||
|
||||
class TaskPanel(object):
|
||||
@@ -957,7 +1140,7 @@ class TaskPanel(object):
|
||||
self.obj = obj
|
||||
self.form = FreeCADGui.PySideUic.loadUi(":/panels/DogboneEdit.ui")
|
||||
self.s = None
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("Path_DressupDogbone", "Edit Dogbone Dress-up"))
|
||||
FreeCAD.ActiveDocument.openTransaction("Edit Dogbone Dress-up")
|
||||
self.height = 10
|
||||
self.markers = []
|
||||
|
||||
@@ -999,8 +1182,10 @@ class TaskPanel(object):
|
||||
|
||||
def updateBoneList(self):
|
||||
itemList = []
|
||||
for loc, (enabled, inaccessible, ids, zs) in PathUtil.keyValueIter(self.obj.Proxy.boneStateList(self.obj)):
|
||||
lbl = '(%.2f, %.2f): %s' % (loc[0], loc[1], ','.join(str(id) for id in ids))
|
||||
for loc, (enabled, inaccessible, ids, zs) in PathUtil.keyValueIter(
|
||||
self.obj.Proxy.boneStateList(self.obj)
|
||||
):
|
||||
lbl = "(%.2f, %.2f): %s" % (loc[0], loc[1], ",".join(str(id) for id in ids))
|
||||
item = QtGui.QListWidgetItem(lbl)
|
||||
if enabled:
|
||||
item.setCheckState(QtCore.Qt.CheckState.Checked)
|
||||
@@ -1008,7 +1193,10 @@ class TaskPanel(object):
|
||||
item.setCheckState(QtCore.Qt.CheckState.Unchecked)
|
||||
flags = QtCore.Qt.ItemFlag.ItemIsSelectable
|
||||
if not inaccessible:
|
||||
flags |= QtCore.Qt.ItemFlag.ItemIsEnabled | QtCore.Qt.ItemFlag.ItemIsUserCheckable
|
||||
flags |= (
|
||||
QtCore.Qt.ItemFlag.ItemIsEnabled
|
||||
| QtCore.Qt.ItemFlag.ItemIsUserCheckable
|
||||
)
|
||||
item.setFlags(flags)
|
||||
item.setData(self.DataIds, ids)
|
||||
item.setData(self.DataKey, ids[0])
|
||||
@@ -1020,7 +1208,13 @@ class TaskPanel(object):
|
||||
self.form.bones.addItem(item)
|
||||
loc = item.data(self.DataLoc)
|
||||
r = max(self.obj.Proxy.length, 1)
|
||||
markers.append(Marker(FreeCAD.Vector(loc[0], loc[1], min(zs)), r, max(1, max(zs) - min(zs))))
|
||||
markers.append(
|
||||
Marker(
|
||||
FreeCAD.Vector(loc[0], loc[1], min(zs)),
|
||||
r,
|
||||
max(1, max(zs) - min(zs)),
|
||||
)
|
||||
)
|
||||
for m in self.markers:
|
||||
self.viewProvider.switch.removeChild(m.sep)
|
||||
for m in markers:
|
||||
@@ -1033,11 +1227,11 @@ class TaskPanel(object):
|
||||
self.form.customLabel.setEnabled(customSelected)
|
||||
self.updateBoneList()
|
||||
|
||||
if PathLog.getLevel(LOG_MODULE) == PathLog.Level.DEBUG:
|
||||
if PathLog.getLevel(PathLog.thisModule()) == PathLog.Level.DEBUG:
|
||||
for obj in FreeCAD.ActiveDocument.Objects:
|
||||
if obj.Name.startswith('Shape'):
|
||||
if obj.Name.startswith("Shape"):
|
||||
FreeCAD.ActiveDocument.removeObject(obj.Name)
|
||||
PathLog.info('object name %s' % self.obj.Name)
|
||||
PathLog.info("object name %s" % self.obj.Name)
|
||||
if hasattr(self.obj.Proxy, "shapes"):
|
||||
PathLog.info("showing shapes attribute")
|
||||
for shapes in self.obj.Proxy.shapes.values():
|
||||
@@ -1091,23 +1285,27 @@ class TaskPanel(object):
|
||||
for i, m in enumerate(self.markers):
|
||||
m.setSelected(i == index)
|
||||
|
||||
|
||||
class SelObserver(object):
|
||||
def __init__(self):
|
||||
import PathScripts.PathSelection as PST
|
||||
|
||||
PST.eselect()
|
||||
|
||||
def __del__(self):
|
||||
import PathScripts.PathSelection as PST
|
||||
|
||||
PST.clear()
|
||||
|
||||
def addSelection(self, doc, obj, sub, pnt):
|
||||
# pylint: disable=unused-argument
|
||||
FreeCADGui.doCommand('Gui.Selection.addSelection(FreeCAD.ActiveDocument.' + obj + ')')
|
||||
FreeCADGui.doCommand(
|
||||
"Gui.Selection.addSelection(FreeCAD.ActiveDocument." + obj + ")"
|
||||
)
|
||||
FreeCADGui.updateGui()
|
||||
|
||||
|
||||
class ViewProviderDressup(object):
|
||||
|
||||
def __init__(self, vobj):
|
||||
self.vobj = vobj
|
||||
self.obj = None
|
||||
@@ -1148,7 +1346,7 @@ class ViewProviderDressup(object):
|
||||
return None
|
||||
|
||||
def onDelete(self, arg1=None, arg2=None):
|
||||
'''this makes sure that the base operation is added back to the project and visible'''
|
||||
"""this makes sure that the base operation is added back to the project and visible"""
|
||||
# pylint: disable=unused-argument
|
||||
if arg1.Object and arg1.Object.Base:
|
||||
FreeCADGui.ActiveDocument.getObject(arg1.Object.Base.Name).Visibility = True
|
||||
@@ -1159,11 +1357,11 @@ class ViewProviderDressup(object):
|
||||
return True
|
||||
|
||||
|
||||
def Create(base, name='DogboneDressup'):
|
||||
'''
|
||||
def Create(base, name="DogboneDressup"):
|
||||
"""
|
||||
Create(obj, name='DogboneDressup') ... dresses the given PathProfile/PathContour object with dogbones.
|
||||
'''
|
||||
obj = FreeCAD.ActiveDocument.addObject('Path::FeaturePython', name)
|
||||
"""
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
dbo = ObjectDressup(obj, base)
|
||||
job = PathUtils.findParentJob(base)
|
||||
job.Proxy.addOperation(obj, base)
|
||||
@@ -1180,9 +1378,16 @@ class CommandDressupDogbone(object):
|
||||
# pylint: disable=no-init
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': 'Path_Dressup',
|
||||
'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_DressupDogbone", "Dogbone Dress-up"),
|
||||
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_DressupDogbone", "Creates a Dogbone Dress-up object from a selected path")}
|
||||
return {
|
||||
"Pixmap": "Path_Dressup",
|
||||
"MenuText": QT_TRANSLATE_NOOP(
|
||||
"Path_DressupDogbone", "Dogbone Dress-up"
|
||||
),
|
||||
"ToolTip": QT_TRANSLATE_NOOP(
|
||||
"Path_DressupDogbone",
|
||||
"Creates a Dogbone Dress-up object from a selected path",
|
||||
),
|
||||
}
|
||||
|
||||
def IsActive(self):
|
||||
if FreeCAD.ActiveDocument is not None:
|
||||
@@ -1196,17 +1401,25 @@ class CommandDressupDogbone(object):
|
||||
# check that the selection contains exactly what we want
|
||||
selection = FreeCADGui.Selection.getSelection()
|
||||
if len(selection) != 1:
|
||||
FreeCAD.Console.PrintError(translate("Path_DressupDogbone", "Please select one path object")+"\n")
|
||||
FreeCAD.Console.PrintError(
|
||||
translate("Path_DressupDogbone", "Please select one path object") + "\n"
|
||||
)
|
||||
return
|
||||
baseObject = selection[0]
|
||||
if not baseObject.isDerivedFrom("Path::Feature"):
|
||||
FreeCAD.Console.PrintError(translate("Path_DressupDogbone", "The selected object is not a path")+"\n")
|
||||
FreeCAD.Console.PrintError(
|
||||
translate("Path_DressupDogbone", "The selected object is not a path")
|
||||
+ "\n"
|
||||
)
|
||||
return
|
||||
|
||||
# everything ok!
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("Path_DressupDogbone", "Create Dogbone Dress-up"))
|
||||
FreeCADGui.addModule('PathScripts.PathDressupDogbone')
|
||||
FreeCADGui.doCommand("PathScripts.PathDressupDogbone.Create(FreeCAD.ActiveDocument.%s)" % baseObject.Name)
|
||||
FreeCAD.ActiveDocument.openTransaction("Create Dogbone Dress-up")
|
||||
FreeCADGui.addModule("PathScripts.PathDressupDogbone")
|
||||
FreeCADGui.doCommand(
|
||||
"PathScripts.PathDressupDogbone.Create(FreeCAD.ActiveDocument.%s)"
|
||||
% baseObject.Name
|
||||
)
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
@@ -1215,6 +1428,7 @@ if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
from PySide import QtGui
|
||||
from pivy import coin
|
||||
FreeCADGui.addCommand('Path_DressupDogbone', CommandDressupDogbone())
|
||||
|
||||
FreeCADGui.addCommand("Path_DressupDogbone", CommandDressupDogbone())
|
||||
|
||||
FreeCAD.Console.PrintLog("Loading DressupDogbone... done\n")
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,6 +20,7 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
import FreeCAD
|
||||
import Path
|
||||
import PathScripts.PathDressup as PathDressup
|
||||
@@ -29,31 +30,50 @@ import PathScripts.PathStock as PathStock
|
||||
import PathScripts.PathUtil as PathUtil
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
|
||||
from PySide import QtCore
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
# PathLog.trackModule(PathLog.thisModule())
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
def _vstr(v):
|
||||
if v:
|
||||
return "(%.2f, %.2f, %.2f)" % (v.x, v.y, v.z)
|
||||
return '-'
|
||||
return "-"
|
||||
|
||||
|
||||
class DressupPathBoundary(object):
|
||||
|
||||
def __init__(self, obj, base, job):
|
||||
obj.addProperty("App::PropertyLink", "Base", "Base", QtCore.QT_TRANSLATE_NOOP("Path_DressupPathBoundary", "The base path to modify"))
|
||||
obj.addProperty(
|
||||
"App::PropertyLink",
|
||||
"Base",
|
||||
"Base",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The base path to modify"),
|
||||
)
|
||||
obj.Base = base
|
||||
obj.addProperty("App::PropertyLink", "Stock", "Boundary", QtCore.QT_TRANSLATE_NOOP("Path_DressupPathBoundary", "Solid object to be used to limit the generated Path."))
|
||||
obj.addProperty(
|
||||
"App::PropertyLink",
|
||||
"Stock",
|
||||
"Boundary",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Solid object to be used to limit the generated Path.",
|
||||
),
|
||||
)
|
||||
obj.Stock = PathStock.CreateFromBase(job)
|
||||
obj.addProperty("App::PropertyBool", "Inside", "Boundary", QtCore.QT_TRANSLATE_NOOP("Path_DressupPathBoundary", "Determines if Boundary describes an inclusion or exclusion mask."))
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"Inside",
|
||||
"Boundary",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Determines if Boundary describes an inclusion or exclusion mask.",
|
||||
),
|
||||
)
|
||||
obj.Inside = True
|
||||
|
||||
self.obj = obj
|
||||
@@ -85,6 +105,8 @@ class DressupPathBoundary(object):
|
||||
def execute(self, obj):
|
||||
pb = PathBoundary(obj.Base, obj.Stock.Shape, obj.Inside)
|
||||
obj.Path = pb.execute()
|
||||
|
||||
|
||||
# Eclass
|
||||
|
||||
|
||||
@@ -114,15 +136,19 @@ class PathBoundary:
|
||||
if begin.z < self.clearanceHeight:
|
||||
cmds.append(self.strG0ZclearanceHeight)
|
||||
if end:
|
||||
cmds.append(Path.Command('G0', {'X': end.x, 'Y': end.y}))
|
||||
cmds.append(Path.Command("G0", {"X": end.x, "Y": end.y}))
|
||||
if end.z < self.clearanceHeight:
|
||||
cmds.append(Path.Command('G0', {'Z': max(self.safeHeight, end.z)}))
|
||||
cmds.append(Path.Command("G0", {"Z": max(self.safeHeight, end.z)}))
|
||||
if end.z < self.safeHeight:
|
||||
cmds.append(Path.Command('G1', {'Z': end.z, 'F': verticalFeed}))
|
||||
cmds.append(Path.Command("G1", {"Z": end.z, "F": verticalFeed}))
|
||||
return cmds
|
||||
|
||||
def execute(self):
|
||||
if not self.baseOp or not self.baseOp.isDerivedFrom('Path::Feature') or not self.baseOp.Path:
|
||||
if (
|
||||
not self.baseOp
|
||||
or not self.baseOp.isDerivedFrom("Path::Feature")
|
||||
or not self.baseOp.Path
|
||||
):
|
||||
return None
|
||||
|
||||
if len(self.baseOp.Path.Commands) == 0:
|
||||
@@ -131,23 +157,27 @@ class PathBoundary:
|
||||
|
||||
tc = PathDressup.toolController(self.baseOp)
|
||||
|
||||
self.safeHeight = float(PathUtil.opProperty(self.baseOp, 'SafeHeight'))
|
||||
self.clearanceHeight = float(PathUtil.opProperty(self.baseOp, 'ClearanceHeight'))
|
||||
self.strG1ZsafeHeight = Path.Command('G1', {'Z': self.safeHeight, 'F': tc.VertFeed.Value})
|
||||
self.strG0ZclearanceHeight = Path.Command('G0', {'Z': self.clearanceHeight})
|
||||
self.safeHeight = float(PathUtil.opProperty(self.baseOp, "SafeHeight"))
|
||||
self.clearanceHeight = float(
|
||||
PathUtil.opProperty(self.baseOp, "ClearanceHeight")
|
||||
)
|
||||
self.strG1ZsafeHeight = Path.Command(
|
||||
"G1", {"Z": self.safeHeight, "F": tc.VertFeed.Value}
|
||||
)
|
||||
self.strG0ZclearanceHeight = Path.Command("G0", {"Z": self.clearanceHeight})
|
||||
|
||||
cmd = self.baseOp.Path.Commands[0]
|
||||
pos = cmd.Placement.Base # bogus m/c position to create first edge
|
||||
pos = cmd.Placement.Base # bogus m/c position to create first edge
|
||||
bogusX = True
|
||||
bogusY = True
|
||||
commands = [cmd]
|
||||
lastExit = None
|
||||
for cmd in self.baseOp.Path.Commands[1:]:
|
||||
if cmd.Name in PathGeom.CmdMoveAll:
|
||||
if bogusX == True :
|
||||
bogusX = ( 'X' not in cmd.Parameters )
|
||||
if bogusY :
|
||||
bogusY = ( 'Y' not in cmd.Parameters )
|
||||
if bogusX == True:
|
||||
bogusX = "X" not in cmd.Parameters
|
||||
if bogusY:
|
||||
bogusY = "Y" not in cmd.Parameters
|
||||
edge = PathGeom.edgeForCmd(cmd, pos)
|
||||
if edge:
|
||||
inside = edge.common(self.boundary).Edges
|
||||
@@ -159,22 +189,30 @@ class PathBoundary:
|
||||
# it's really a shame that one cannot trust the sequence and/or
|
||||
# orientation of edges
|
||||
if 1 == len(inside) and 0 == len(outside):
|
||||
PathLog.track(_vstr(pos), _vstr(lastExit), ' + ', cmd)
|
||||
PathLog.track(_vstr(pos), _vstr(lastExit), " + ", cmd)
|
||||
# cmd fully included by boundary
|
||||
if lastExit:
|
||||
if not ( bogusX or bogusY ) : # don't insert false paths based on bogus m/c position
|
||||
commands.extend(self.boundaryCommands(lastExit, pos, tc.VertFeed.Value))
|
||||
if not (
|
||||
bogusX or bogusY
|
||||
): # don't insert false paths based on bogus m/c position
|
||||
commands.extend(
|
||||
self.boundaryCommands(
|
||||
lastExit, pos, tc.VertFeed.Value
|
||||
)
|
||||
)
|
||||
lastExit = None
|
||||
commands.append(cmd)
|
||||
pos = PathGeom.commandEndPoint(cmd, pos)
|
||||
elif 0 == len(inside) and 1 == len(outside):
|
||||
PathLog.track(_vstr(pos), _vstr(lastExit), ' - ', cmd)
|
||||
PathLog.track(_vstr(pos), _vstr(lastExit), " - ", cmd)
|
||||
# cmd fully excluded by boundary
|
||||
if not lastExit:
|
||||
lastExit = pos
|
||||
pos = PathGeom.commandEndPoint(cmd, pos)
|
||||
else:
|
||||
PathLog.track(_vstr(pos), _vstr(lastExit), len(inside), len(outside), cmd)
|
||||
PathLog.track(
|
||||
_vstr(pos), _vstr(lastExit), len(inside), len(outside), cmd
|
||||
)
|
||||
# cmd pierces boundary
|
||||
while inside or outside:
|
||||
ie = [e for e in inside if PathGeom.edgeConnectsTo(e, pos)]
|
||||
@@ -187,35 +225,58 @@ class PathBoundary:
|
||||
# inside edges are taken at this point (see swap of inside/outside
|
||||
# above - so we can just connect the dots ...
|
||||
if lastExit:
|
||||
if not ( bogusX or bogusY ) : commands.extend(self.boundaryCommands(lastExit, pos, tc.VertFeed.Value))
|
||||
if not (bogusX or bogusY):
|
||||
commands.extend(
|
||||
self.boundaryCommands(
|
||||
lastExit, pos, tc.VertFeed.Value
|
||||
)
|
||||
)
|
||||
lastExit = None
|
||||
PathLog.track(e, flip)
|
||||
if not ( bogusX or bogusY ) : # don't insert false paths based on bogus m/c position
|
||||
commands.extend(PathGeom.cmdsForEdge(e, flip, False, 50, tc.HorizFeed.Value, tc.VertFeed.Value))
|
||||
if not (
|
||||
bogusX or bogusY
|
||||
): # don't insert false paths based on bogus m/c position
|
||||
commands.extend(
|
||||
PathGeom.cmdsForEdge(
|
||||
e,
|
||||
flip,
|
||||
False,
|
||||
50,
|
||||
tc.HorizFeed.Value,
|
||||
tc.VertFeed.Value,
|
||||
)
|
||||
)
|
||||
inside.remove(e)
|
||||
pos = newPos
|
||||
lastExit = newPos
|
||||
else:
|
||||
oe = [e for e in outside if PathGeom.edgeConnectsTo(e, pos)]
|
||||
oe = [
|
||||
e
|
||||
for e in outside
|
||||
if PathGeom.edgeConnectsTo(e, pos)
|
||||
]
|
||||
PathLog.track(oe)
|
||||
if oe:
|
||||
e = oe[0]
|
||||
ptL = e.valueAt(e.LastParameter)
|
||||
flip = PathGeom.pointsCoincide(pos, ptL)
|
||||
newPos = e.valueAt(e.FirstParameter) if flip else ptL
|
||||
newPos = (
|
||||
e.valueAt(e.FirstParameter) if flip else ptL
|
||||
)
|
||||
# outside edges are never taken at this point (see swap of
|
||||
# inside/outside above) - so just move along ...
|
||||
outside.remove(e)
|
||||
pos = newPos
|
||||
else:
|
||||
PathLog.error('huh?')
|
||||
PathLog.error("huh?")
|
||||
import Part
|
||||
Part.show(Part.Vertex(pos), 'pos')
|
||||
|
||||
Part.show(Part.Vertex(pos), "pos")
|
||||
for e in inside:
|
||||
Part.show(e, 'ei')
|
||||
Part.show(e, "ei")
|
||||
for e in outside:
|
||||
Part.show(e, 'eo')
|
||||
raise Exception('This is not supposed to happen')
|
||||
Part.show(e, "eo")
|
||||
raise Exception("This is not supposed to happen")
|
||||
# Eif
|
||||
# Eif
|
||||
# Ewhile
|
||||
@@ -223,7 +284,7 @@ class PathBoundary:
|
||||
# pos = PathGeom.commandEndPoint(cmd, pos)
|
||||
# Eif
|
||||
else:
|
||||
PathLog.track('no-move', cmd)
|
||||
PathLog.track("no-move", cmd)
|
||||
commands.append(cmd)
|
||||
if lastExit:
|
||||
commands.extend(self.boundaryCommands(lastExit, None, tc.VertFeed.Value))
|
||||
@@ -231,16 +292,22 @@ class PathBoundary:
|
||||
|
||||
PathLog.track(commands)
|
||||
return Path.Path(commands)
|
||||
|
||||
|
||||
# Eclass
|
||||
|
||||
def Create(base, name='DressupPathBoundary'):
|
||||
'''Create(base, name='DressupPathBoundary') ... creates a dressup limiting base's Path to a boundary.'''
|
||||
|
||||
if not base.isDerivedFrom('Path::Feature'):
|
||||
PathLog.error(translate('Path_DressupPathBoundary', 'The selected object is not a path')+'\n')
|
||||
def Create(base, name="DressupPathBoundary"):
|
||||
"""Create(base, name='DressupPathBoundary') ... creates a dressup limiting base's Path to a boundary."""
|
||||
|
||||
if not base.isDerivedFrom("Path::Feature"):
|
||||
PathLog.error(
|
||||
translate("Path_DressupPathBoundary", "The selected object is not a path")
|
||||
+ "\n"
|
||||
)
|
||||
return None
|
||||
|
||||
obj = FreeCAD.ActiveDocument.addObject('Path::FeaturePython', name)
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
job = PathUtils.findParentJob(base)
|
||||
obj.Proxy = DressupPathBoundary(obj, base, job)
|
||||
job.Proxy.addOperation(obj, base, True)
|
||||
|
||||
@@ -20,28 +20,29 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
from PySide import QtGui
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import PathGui as PGui # ensure Path/Gui/Resources are loaded
|
||||
import PathGui as PGui # ensure Path/Gui/Resources are loaded
|
||||
import PathScripts.PathDressupPathBoundary as PathDressupPathBoundary
|
||||
import PathScripts.PathLog as PathLog
|
||||
|
||||
from PySide import QtGui, QtCore
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
#PathLog.trackModule()
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
class TaskPanel(object):
|
||||
|
||||
def __init__(self, obj, viewProvider):
|
||||
self.obj = obj
|
||||
self.viewProvider = viewProvider
|
||||
self.form = FreeCADGui.PySideUic.loadUi(':/panels/DressupPathBoundary.ui')
|
||||
self.form = FreeCADGui.PySideUic.loadUi(":/panels/DressupPathBoundary.ui")
|
||||
if obj.Stock:
|
||||
self.visibilityBoundary = obj.Stock.ViewObject.Visibility
|
||||
obj.Stock.ViewObject.Visibility = True
|
||||
@@ -58,7 +59,11 @@ class TaskPanel(object):
|
||||
self.stockEdit = None
|
||||
|
||||
def getStandardButtons(self):
|
||||
return int(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Apply | QtGui.QDialogButtonBox.Cancel)
|
||||
return int(
|
||||
QtGui.QDialogButtonBox.Ok
|
||||
| QtGui.QDialogButtonBox.Apply
|
||||
| QtGui.QDialogButtonBox.Cancel
|
||||
)
|
||||
|
||||
def modifyStandardButtons(self, buttonBox):
|
||||
self.buttonBox = buttonBox
|
||||
@@ -113,32 +118,42 @@ class TaskPanel(object):
|
||||
def setupFromBaseEdit():
|
||||
PathLog.track(index, force)
|
||||
if force or not self.stockFromBase:
|
||||
self.stockFromBase = PathJobGui.StockFromBaseBoundBoxEdit(self.obj, self.form, force)
|
||||
self.stockFromBase = PathJobGui.StockFromBaseBoundBoxEdit(
|
||||
self.obj, self.form, force
|
||||
)
|
||||
self.stockEdit = self.stockFromBase
|
||||
|
||||
def setupCreateBoxEdit():
|
||||
PathLog.track(index, force)
|
||||
if force or not self.stockCreateBox:
|
||||
self.stockCreateBox = PathJobGui.StockCreateBoxEdit(self.obj, self.form, force)
|
||||
self.stockCreateBox = PathJobGui.StockCreateBoxEdit(
|
||||
self.obj, self.form, force
|
||||
)
|
||||
self.stockEdit = self.stockCreateBox
|
||||
|
||||
def setupCreateCylinderEdit():
|
||||
PathLog.track(index, force)
|
||||
if force or not self.stockCreateCylinder:
|
||||
self.stockCreateCylinder = PathJobGui.StockCreateCylinderEdit(self.obj, self.form, force)
|
||||
self.stockCreateCylinder = PathJobGui.StockCreateCylinderEdit(
|
||||
self.obj, self.form, force
|
||||
)
|
||||
self.stockEdit = self.stockCreateCylinder
|
||||
|
||||
def setupFromExisting():
|
||||
PathLog.track(index, force)
|
||||
if force or not self.stockFromExisting:
|
||||
self.stockFromExisting = PathJobGui.StockFromExistingEdit(self.obj, self.form, force)
|
||||
self.stockFromExisting = PathJobGui.StockFromExistingEdit(
|
||||
self.obj, self.form, force
|
||||
)
|
||||
if self.stockFromExisting.candidates(self.obj):
|
||||
self.stockEdit = self.stockFromExisting
|
||||
return True
|
||||
return False
|
||||
|
||||
if index == -1:
|
||||
if self.obj.Stock is None or PathJobGui.StockFromBaseBoundBoxEdit.IsStock(self.obj):
|
||||
if self.obj.Stock is None or PathJobGui.StockFromBaseBoundBoxEdit.IsStock(
|
||||
self.obj
|
||||
):
|
||||
setupFromBaseEdit()
|
||||
elif PathJobGui.StockCreateBoxEdit.IsStock(self.obj):
|
||||
setupCreateBoxEdit()
|
||||
@@ -147,7 +162,10 @@ class TaskPanel(object):
|
||||
elif PathJobGui.StockFromExistingEdit.IsStock(self.obj):
|
||||
setupFromExisting()
|
||||
else:
|
||||
PathLog.error(translate('PathJob', "Unsupported stock object %s") % self.obj.Stock.Label)
|
||||
PathLog.error(
|
||||
translate("PathJob", "Unsupported stock object %s")
|
||||
% self.obj.Stock.Label
|
||||
)
|
||||
else:
|
||||
if index == PathJobGui.StockFromBaseBoundBoxEdit.Index:
|
||||
setupFromBaseEdit()
|
||||
@@ -160,7 +178,10 @@ class TaskPanel(object):
|
||||
setupFromBaseEdit()
|
||||
index = -1
|
||||
else:
|
||||
PathLog.error(translate('PathJob', "Unsupported stock type %s (%d)") % (self.form.stock.currentText(), index))
|
||||
PathLog.error(
|
||||
translate("PathJob", "Unsupported stock type %s (%d)")
|
||||
% (self.form.stock.currentText(), index)
|
||||
)
|
||||
self.stockEdit.activate(self.obj, index == -1)
|
||||
|
||||
def setupUi(self):
|
||||
@@ -183,16 +204,15 @@ class TaskPanel(object):
|
||||
|
||||
|
||||
class DressupPathBoundaryViewProvider(object):
|
||||
|
||||
def __init__(self, vobj):
|
||||
self.attach(vobj)
|
||||
|
||||
def __getstate__(self):
|
||||
return None
|
||||
|
||||
def __setstate__(self, state):
|
||||
return None
|
||||
|
||||
|
||||
def attach(self, vobj):
|
||||
self.vobj = vobj
|
||||
self.obj = vobj.Object
|
||||
@@ -225,8 +245,8 @@ class DressupPathBoundaryViewProvider(object):
|
||||
self.panel = None
|
||||
|
||||
|
||||
def Create(base, name='DressupPathBoundary'):
|
||||
FreeCAD.ActiveDocument.openTransaction(translate('Path_DressupPathBoundary', 'Create a Boundary dressup'))
|
||||
def Create(base, name="DressupPathBoundary"):
|
||||
FreeCAD.ActiveDocument.openTransaction("Create a Boundary dressup")
|
||||
obj = PathDressupPathBoundary.Create(base, name)
|
||||
obj.ViewObject.Proxy = DressupPathBoundaryViewProvider(obj.ViewObject)
|
||||
obj.Base.ViewObject.Visibility = False
|
||||
@@ -235,18 +255,26 @@ def Create(base, name='DressupPathBoundary'):
|
||||
obj.ViewObject.Document.setEdit(obj.ViewObject, 0)
|
||||
return obj
|
||||
|
||||
|
||||
class CommandPathDressupPathBoundary:
|
||||
# pylint: disable=no-init
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': 'Path_Dressup',
|
||||
'MenuText': QtCore.QT_TRANSLATE_NOOP('Path_DressupPathBoundary', 'Boundary Dress-up'),
|
||||
'ToolTip': QtCore.QT_TRANSLATE_NOOP('Path_DressupPathBoundary', 'Creates a Path Boundary Dress-up object from a selected path')}
|
||||
return {
|
||||
"Pixmap": "Path_Dressup",
|
||||
"MenuText": QT_TRANSLATE_NOOP(
|
||||
"Path_DressupPathBoundary", "Boundary Dress-up"
|
||||
),
|
||||
"ToolTip": QT_TRANSLATE_NOOP(
|
||||
"Path_DressupPathBoundary",
|
||||
"Creates a Path Boundary Dress-up object from a selected path",
|
||||
),
|
||||
}
|
||||
|
||||
def IsActive(self):
|
||||
if FreeCAD.ActiveDocument is not None:
|
||||
for o in FreeCAD.ActiveDocument.Objects:
|
||||
if o.Name[:3] == 'Job':
|
||||
if o.Name[:3] == "Job":
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -254,19 +282,26 @@ class CommandPathDressupPathBoundary:
|
||||
# check that the selection contains exactly what we want
|
||||
selection = FreeCADGui.Selection.getSelection()
|
||||
if len(selection) != 1:
|
||||
PathLog.error(translate('Path_DressupPathBoundary', 'Please select one path object')+'\n')
|
||||
PathLog.error(
|
||||
translate("Path_DressupPathBoundary", "Please select one path object")
|
||||
+ "\n"
|
||||
)
|
||||
return
|
||||
baseObject = selection[0]
|
||||
|
||||
# everything ok!
|
||||
FreeCAD.ActiveDocument.openTransaction(translate('Path_DressupPathBoundary', 'Create Path Boundary Dress-up'))
|
||||
FreeCADGui.addModule('PathScripts.PathDressupPathBoundaryGui')
|
||||
FreeCADGui.doCommand("PathScripts.PathDressupPathBoundaryGui.Create(App.ActiveDocument.%s)" % baseObject.Name)
|
||||
FreeCAD.ActiveDocument.openTransaction("Create Path Boundary Dress-up")
|
||||
FreeCADGui.addModule("PathScripts.PathDressupPathBoundaryGui")
|
||||
FreeCADGui.doCommand(
|
||||
"PathScripts.PathDressupPathBoundaryGui.Create(App.ActiveDocument.%s)"
|
||||
% baseObject.Name
|
||||
)
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
# register the FreeCAD command
|
||||
FreeCADGui.addCommand('Path_DressupPathBoundary', CommandPathDressupPathBoundary())
|
||||
FreeCADGui.addCommand("Path_DressupPathBoundary", CommandPathDressupPathBoundary())
|
||||
|
||||
PathLog.notice('Loading PathDressupPathBoundaryGui... done\n')
|
||||
PathLog.notice("Loading PathDressupPathBoundaryGui... done\n")
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
from PathScripts import PathUtils
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
import FreeCAD
|
||||
import Path
|
||||
import PathScripts.PathDressup as PathDressup
|
||||
@@ -27,8 +29,6 @@ import PathScripts.PathGeom as PathGeom
|
||||
import PathScripts.PathLog as PathLog
|
||||
import math
|
||||
|
||||
from PathScripts import PathUtils
|
||||
from PySide import QtCore
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
@@ -39,12 +39,14 @@ if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
def translate(text, context="Path_DressupRampEntry", disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
class ObjectDressup:
|
||||
@@ -54,41 +56,37 @@ class ObjectDressup:
|
||||
"App::PropertyLink",
|
||||
"Base",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"Path_DressupRampEntry", "The base path to modify"
|
||||
),
|
||||
QT_TRANSLATE_NOOP("App::Property", "The base path to modify"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyAngle",
|
||||
"Angle",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP("Path_DressupRampEntry", "Angle of ramp."),
|
||||
QT_TRANSLATE_NOOP("App::Property", "Angle of ramp."),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"Method",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP("App::Property", "Ramping Method"),
|
||||
QT_TRANSLATE_NOOP("App::Property", "Ramping Method"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"RampFeedRate",
|
||||
"FeedRate",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Which feed rate to use for ramping"
|
||||
),
|
||||
QT_TRANSLATE_NOOP("App::Property", "Which feed rate to use for ramping"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertySpeed",
|
||||
"CustomFeedRate",
|
||||
"FeedRate",
|
||||
QtCore.QT_TRANSLATE_NOOP("App::Property", "Custom feed rate"),
|
||||
QT_TRANSLATE_NOOP("App::Property", "Custom feed rate"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"UseStartDepth",
|
||||
"StartDepth",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Should the dressup ignore motion commands above DressupStartDepth",
|
||||
),
|
||||
@@ -97,18 +95,16 @@ class ObjectDressup:
|
||||
"App::PropertyDistance",
|
||||
"DressupStartDepth",
|
||||
"StartDepth",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"The depth where the ramp dressup is enabled. Above this ramps are not generated, but motion commands are passed through as is.",
|
||||
),
|
||||
)
|
||||
obj.Method = ["RampMethod1", "RampMethod2", "RampMethod3", "Helix"]
|
||||
obj.RampFeedRate = [
|
||||
"Horizontal Feed Rate",
|
||||
"Vertical Feed Rate",
|
||||
"Ramp Feed Rate",
|
||||
"Custom",
|
||||
]
|
||||
|
||||
# populate the enumerations
|
||||
for n in self.propertyEnumerations():
|
||||
setattr(obj, n[0], n[1])
|
||||
|
||||
obj.Proxy = self
|
||||
self.setEditorProperties(obj)
|
||||
|
||||
@@ -121,6 +117,55 @@ class ObjectDressup:
|
||||
self.ignoreAboveEnabled = None
|
||||
self.ignoreAbove = None
|
||||
|
||||
@classmethod
|
||||
def propertyEnumerations(self, dataType="data"):
|
||||
"""PropertyEnumerations(dataType="data")... return property enumeration lists of specified dataType.
|
||||
Args:
|
||||
dataType = 'data', 'raw', 'translated'
|
||||
Notes:
|
||||
'data' is list of internal string literals used in code
|
||||
'raw' is list of (translated_text, data_string) tuples
|
||||
'translated' is list of translated string literals
|
||||
"""
|
||||
|
||||
enums = {
|
||||
"Object": [
|
||||
(translate("Path_DressupRampEntry", "RampMethod1"), "RampMethod1"),
|
||||
(translate("Path_DressupRampEntry", "RampMethod2"), "RampMethod2"),
|
||||
(translate("Path_DressupRampEntry", "RampMethod3"), "RampMethod3"),
|
||||
(translate("Path_DressupRampEntry", "Helix"), "Helix"),
|
||||
],
|
||||
"RampFeedRate": [
|
||||
(
|
||||
translate("Path_DressupRampEntry", "Horizontal Feed Rate"),
|
||||
"Horizontal Feed Rate",
|
||||
),
|
||||
(
|
||||
translate("Path_DressupRampEntry", "Vertical Feed Rate"),
|
||||
"Vertical Feed Rate",
|
||||
),
|
||||
(
|
||||
translate("Path_DressupRampEntry", "Ramp Feed Rate"),
|
||||
"Ramp Feed Rate",
|
||||
),
|
||||
(translate("Path_DressupRampEntry", "Custom"), "Custom"),
|
||||
],
|
||||
}
|
||||
|
||||
if dataType == "raw":
|
||||
return enums
|
||||
|
||||
data = list()
|
||||
idx = 0 if dataType == "translated" else 1
|
||||
|
||||
PathLog.debug(enums)
|
||||
|
||||
for k, v in enumerate(enums):
|
||||
data.append((v, [tup[idx] for tup in enums[v]]))
|
||||
PathLog.debug(data)
|
||||
|
||||
return data
|
||||
|
||||
def __getstate__(self):
|
||||
return None
|
||||
|
||||
@@ -855,10 +900,10 @@ class CommandPathDressupRampEntry:
|
||||
def GetResources(self):
|
||||
return {
|
||||
"Pixmap": "Path_Dressup",
|
||||
"MenuText": QtCore.QT_TRANSLATE_NOOP(
|
||||
"MenuText": QT_TRANSLATE_NOOP(
|
||||
"Path_DressupRampEntry", "RampEntry Dress-up"
|
||||
),
|
||||
"ToolTip": QtCore.QT_TRANSLATE_NOOP(
|
||||
"ToolTip": QT_TRANSLATE_NOOP(
|
||||
"Path_DressupRampEntry",
|
||||
"Creates a Ramp Entry Dress-up object from a selected path",
|
||||
),
|
||||
@@ -875,18 +920,26 @@ class CommandPathDressupRampEntry:
|
||||
# check that the selection contains exactly what we want
|
||||
selection = FreeCADGui.Selection.getSelection()
|
||||
if len(selection) != 1:
|
||||
PathLog.error(translate("Please select one path object") + "\n")
|
||||
PathLog.error(
|
||||
translate("Path_DressupRampEntry", "Please select one path object")
|
||||
+ "\n"
|
||||
)
|
||||
return
|
||||
baseObject = selection[0]
|
||||
if not baseObject.isDerivedFrom("Path::Feature"):
|
||||
PathLog.error(translate("The selected object is not a path") + "\n")
|
||||
PathLog.error(
|
||||
translate("Path_DressupRampEntry", "The selected object is not a path")
|
||||
+ "\n"
|
||||
)
|
||||
return
|
||||
if baseObject.isDerivedFrom("Path::FeatureCompoundPython"):
|
||||
PathLog.error(translate("Please select a Profile object"))
|
||||
PathLog.error(
|
||||
translate("Path_DressupRampEntry", "Please select a Profile object")
|
||||
)
|
||||
return
|
||||
|
||||
# everything ok!
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("Create RampEntry Dress-up"))
|
||||
FreeCAD.ActiveDocument.openTransaction("Create RampEntry Dress-up")
|
||||
FreeCADGui.addModule("PathScripts.PathDressupRampEntry")
|
||||
FreeCADGui.addModule("PathScripts.PathUtils")
|
||||
FreeCADGui.doCommand(
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
from PathScripts.PathDressupTagPreferences import HoldingTagPreferences
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
import FreeCAD
|
||||
import PathScripts.PathDressup as PathDressup
|
||||
import PathScripts.PathGeom as PathGeom
|
||||
@@ -29,22 +31,23 @@ import math
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
DraftGeomUtils = LazyLoader('DraftGeomUtils', globals(), 'DraftGeomUtils')
|
||||
Part = LazyLoader('Part', globals(), 'Part')
|
||||
|
||||
from PathScripts.PathDressupTagPreferences import HoldingTagPreferences
|
||||
from PySide import QtCore
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
#PathLog.trackModule()
|
||||
DraftGeomUtils = LazyLoader("DraftGeomUtils", globals(), "DraftGeomUtils")
|
||||
Part = LazyLoader("Part", globals(), "Part")
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
MaxInt = 99999999999999
|
||||
|
||||
|
||||
class TagSolid:
|
||||
def __init__(self, proxy, z, R):
|
||||
self.proxy = proxy
|
||||
@@ -76,7 +79,7 @@ class TagSolid:
|
||||
# with top
|
||||
r2 = r1 - dr
|
||||
s = height / math.sin(rad)
|
||||
radius = min(r2, s) * math.tan((math.pi - rad)/2) * 0.95
|
||||
radius = min(r2, s) * math.tan((math.pi - rad) / 2) * 0.95
|
||||
else:
|
||||
# triangular
|
||||
r2 = 0
|
||||
@@ -98,7 +101,9 @@ class TagSolid:
|
||||
|
||||
# lastly determine the center of the model, we want to make sure the seam of
|
||||
# the tag solid points away (in the hopes it doesn't coincide with a path)
|
||||
self.baseCenter = FreeCAD.Vector((proxy.ptMin.x+proxy.ptMax.x)/2, (proxy.ptMin.y+proxy.ptMax.y)/2, 0)
|
||||
self.baseCenter = FreeCAD.Vector(
|
||||
(proxy.ptMin.x + proxy.ptMax.x) / 2, (proxy.ptMin.y + proxy.ptMax.y) / 2, 0
|
||||
)
|
||||
|
||||
def cloneAt(self, pos):
|
||||
clone = self.solid.copy()
|
||||
@@ -111,17 +116,59 @@ class TagSolid:
|
||||
|
||||
|
||||
class ObjectDressup:
|
||||
|
||||
def __init__(self, obj, base):
|
||||
|
||||
obj.addProperty('App::PropertyLink', 'Base', 'Base', QtCore.QT_TRANSLATE_NOOP('Path_DressupTag', 'The base path to modify'))
|
||||
obj.addProperty('App::PropertyLength', 'Width', 'Tag', QtCore.QT_TRANSLATE_NOOP('Path_DressupTag', 'Width of tags.'))
|
||||
obj.addProperty('App::PropertyLength', 'Height', 'Tag', QtCore.QT_TRANSLATE_NOOP('Path_DressupTag', 'Height of tags.'))
|
||||
obj.addProperty('App::PropertyAngle', 'Angle', 'Tag', QtCore.QT_TRANSLATE_NOOP('Path_DressupTag', 'Angle of tag plunge and ascent.'))
|
||||
obj.addProperty('App::PropertyLength', 'Radius', 'Tag', QtCore.QT_TRANSLATE_NOOP('Path_DressupTag', 'Radius of the fillet for the tag.'))
|
||||
obj.addProperty('App::PropertyVectorList', 'Positions', 'Tag', QtCore.QT_TRANSLATE_NOOP('Path_DressupTag', 'Locations of inserted holding tags'))
|
||||
obj.addProperty('App::PropertyIntegerList', 'Disabled', 'Tag', QtCore.QT_TRANSLATE_NOOP('Path_DressupTag', 'IDs of disabled holding tags'))
|
||||
obj.addProperty('App::PropertyInteger', 'SegmentationFactor', 'Tag', QtCore.QT_TRANSLATE_NOOP('Path_DressupTag', 'Factor determining the # of segments used to approximate rounded tags.'))
|
||||
obj.addProperty(
|
||||
"App::PropertyLink",
|
||||
"Base",
|
||||
"Base",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The base path to modify"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyLength",
|
||||
"Width",
|
||||
"Tag",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Width of tags."),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyLength",
|
||||
"Height",
|
||||
"Tag",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Height of tags."),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyAngle",
|
||||
"Angle",
|
||||
"Tag",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Angle of tag plunge and ascent."),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyLength",
|
||||
"Radius",
|
||||
"Tag",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Radius of the fillet for the tag."),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyVectorList",
|
||||
"Positions",
|
||||
"Tag",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Locations of inserted holding tags"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyIntegerList",
|
||||
"Disabled",
|
||||
"Tag",
|
||||
QT_TRANSLATE_NOOP("App::Property", "IDs of disabled holding tags"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyInteger",
|
||||
"SegmentationFactor",
|
||||
"Tag",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Factor determining the # of segments used to approximate rounded tags.",
|
||||
),
|
||||
)
|
||||
|
||||
obj.Proxy = self
|
||||
obj.Base = base
|
||||
@@ -152,16 +199,20 @@ class ObjectDressup:
|
||||
def execute(self, obj):
|
||||
PathLog.track()
|
||||
if not obj.Base:
|
||||
PathLog.error(translate('Path_DressupTag', 'No Base object found.'))
|
||||
PathLog.error(translate("Path_DressupTag", "No Base object found."))
|
||||
return
|
||||
if not obj.Base.isDerivedFrom('Path::Feature'):
|
||||
PathLog.error(translate('Path_DressupTag', 'Base is not a Path::Feature object.'))
|
||||
if not obj.Base.isDerivedFrom("Path::Feature"):
|
||||
PathLog.error(
|
||||
translate("Path_DressupTag", "Base is not a Path::Feature object.")
|
||||
)
|
||||
return
|
||||
if not obj.Base.Path:
|
||||
PathLog.error(translate('Path_DressupTag', 'Base doesn\'t have a Path to dress-up.'))
|
||||
PathLog.error(
|
||||
translate("Path_DressupTag", "Base doesn't have a Path to dress-up.")
|
||||
)
|
||||
return
|
||||
if not obj.Base.Path.Commands:
|
||||
PathLog.error(translate('Path_DressupTag', 'Base Path is empty.'))
|
||||
PathLog.error(translate("Path_DressupTag", "Base Path is empty."))
|
||||
return
|
||||
|
||||
self.obj = obj
|
||||
@@ -190,14 +241,19 @@ class ObjectDressup:
|
||||
maxZ = max(pt.z, maxZ)
|
||||
minZ = min(pt.z, minZ)
|
||||
lastPt = pt
|
||||
PathLog.debug("bb = (%.2f, %.2f, %.2f) ... (%.2f, %.2f, %.2f)" % (minX, minY, minZ, maxX, maxY, maxZ))
|
||||
PathLog.debug(
|
||||
"bb = (%.2f, %.2f, %.2f) ... (%.2f, %.2f, %.2f)"
|
||||
% (minX, minY, minZ, maxX, maxY, maxZ)
|
||||
)
|
||||
self.ptMin = FreeCAD.Vector(minX, minY, minZ)
|
||||
self.ptMax = FreeCAD.Vector(maxX, maxY, maxZ)
|
||||
self.masterSolid = TagSolid(self, minZ, self.toolRadius())
|
||||
self.solids = [self.masterSolid.cloneAt(pos) for pos in self.obj.Positions]
|
||||
self.tagSolid = Part.Compound(self.solids)
|
||||
|
||||
self.wire, rapid = PathGeom.wireForPath(obj.Base.Path) # pylint: disable=unused-variable
|
||||
self.wire, rapid = PathGeom.wireForPath(
|
||||
obj.Base.Path
|
||||
) # pylint: disable=unused-variable
|
||||
self.edges = self.wire.Edges
|
||||
|
||||
maxTagZ = minZ + obj.Height.Value
|
||||
@@ -210,7 +266,7 @@ class ObjectDressup:
|
||||
|
||||
for cmd in obj.Base.Path.Commands:
|
||||
if cmd in PathGeom.CmdMove:
|
||||
if lastZ <= maxTagZ or cmd.Parameters.get('Z', lastZ) <= maxTagZ:
|
||||
if lastZ <= maxTagZ or cmd.Parameters.get("Z", lastZ) <= maxTagZ:
|
||||
pass
|
||||
else:
|
||||
commands.append(cmd)
|
||||
@@ -226,7 +282,7 @@ class ObjectDressup:
|
||||
|
||||
def addTagsToDocument(self):
|
||||
for i, solid in enumerate(self.solids):
|
||||
obj = FreeCAD.ActiveDocument.addObject('Part::Compound', "tag_%02d" % i)
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::Compound", "tag_%02d" % i)
|
||||
obj.Shape = solid
|
||||
|
||||
def supportsTagGeneration(self, obj):
|
||||
@@ -241,16 +297,18 @@ class ObjectDressup:
|
||||
return False
|
||||
|
||||
|
||||
def Create(baseObject, name='DressupTag'):
|
||||
'''
|
||||
def Create(baseObject, name="DressupTag"):
|
||||
"""
|
||||
Create(basePath, name = 'DressupTag') ... create tag dressup object for the given base path.
|
||||
'''
|
||||
if not baseObject.isDerivedFrom('Path::Feature'):
|
||||
PathLog.error(translate('Path_DressupTag', 'The selected object is not a path')+'\n')
|
||||
"""
|
||||
if not baseObject.isDerivedFrom("Path::Feature"):
|
||||
PathLog.error(
|
||||
translate("Path_DressupTag", "The selected object is not a path") + "\n"
|
||||
)
|
||||
return None
|
||||
|
||||
if baseObject.isDerivedFrom('Path::FeatureCompoundPython'):
|
||||
PathLog.error(translate('Path_DressupTag', 'Please select a Profile object'))
|
||||
if baseObject.isDerivedFrom("Path::FeatureCompoundPython"):
|
||||
PathLog.error(translate("Path_DressupTag", "Please select a Profile object"))
|
||||
return None
|
||||
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
@@ -260,4 +318,5 @@ def Create(baseObject, name='DressupTag'):
|
||||
dbo.assignDefaultValues()
|
||||
return obj
|
||||
|
||||
PathLog.notice('Loading Path_DressupTag... done\n')
|
||||
|
||||
PathLog.notice("Loading Path_DressupTag... done\n")
|
||||
|
||||
@@ -20,26 +20,27 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
from pivy import coin
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import PathGui as PGui # ensure Path/Gui/Resources are loaded
|
||||
import PathGui as PGui # ensure Path/Gui/Resources are loaded
|
||||
import PathScripts.PathDressupHoldingTags as PathDressupTag
|
||||
import PathScripts.PathGeom as PathGeom
|
||||
import PathScripts.PathGetPoint as PathGetPoint
|
||||
import PathScripts.PathDressupHoldingTags as PathDressupTag
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
from pivy import coin
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
# PathLog.trackModule()
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
def addDebugDisplay():
|
||||
@@ -60,7 +61,7 @@ class PathDressupTagTaskPanel:
|
||||
self.getPoint = PathGetPoint.TaskPanel(self.form.removeEditAddGroup, True)
|
||||
self.jvo = PathUtils.findParentJob(obj).ViewObject
|
||||
if jvoVisibility is None:
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("PathDressup_HoldingTags", "Edit HoldingTags Dress-up"))
|
||||
FreeCAD.ActiveDocument.openTransaction("Edit HoldingTags Dress-up")
|
||||
self.jvoVisible = self.jvo.isVisible()
|
||||
if self.jvoVisible:
|
||||
self.jvo.hide()
|
||||
@@ -76,7 +77,11 @@ class PathDressupTagTaskPanel:
|
||||
self.editItem = None
|
||||
|
||||
def getStandardButtons(self):
|
||||
return int(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Apply | QtGui.QDialogButtonBox.Cancel)
|
||||
return int(
|
||||
QtGui.QDialogButtonBox.Ok
|
||||
| QtGui.QDialogButtonBox.Apply
|
||||
| QtGui.QDialogButtonBox.Cancel
|
||||
)
|
||||
|
||||
def clicked(self, button):
|
||||
if button == QtGui.QDialogButtonBox.Apply:
|
||||
@@ -201,7 +206,7 @@ class PathDressupTagTaskPanel:
|
||||
self.Disabled = self.obj.Disabled
|
||||
self.updateTagsView()
|
||||
else:
|
||||
PathLog.error(translate('Path_DressupTag', 'Cannot copy tags - internal error')+'\n')
|
||||
PathLog.error("Cannot copy tags - internal error")
|
||||
|
||||
def updateModel(self):
|
||||
self.getFields()
|
||||
@@ -273,10 +278,16 @@ class PathDressupTagTaskPanel:
|
||||
def setFields(self):
|
||||
self.updateTagsView()
|
||||
self.form.sbCount.setValue(len(self.Positions))
|
||||
self.form.ifHeight.setText(FreeCAD.Units.Quantity(self.obj.Height, FreeCAD.Units.Length).UserString)
|
||||
self.form.ifWidth.setText(FreeCAD.Units.Quantity(self.obj.Width, FreeCAD.Units.Length).UserString)
|
||||
self.form.ifHeight.setText(
|
||||
FreeCAD.Units.Quantity(self.obj.Height, FreeCAD.Units.Length).UserString
|
||||
)
|
||||
self.form.ifWidth.setText(
|
||||
FreeCAD.Units.Quantity(self.obj.Width, FreeCAD.Units.Length).UserString
|
||||
)
|
||||
self.form.dsbAngle.setValue(self.obj.Angle)
|
||||
self.form.ifRadius.setText(FreeCAD.Units.Quantity(self.obj.Radius, FreeCAD.Units.Length).UserString)
|
||||
self.form.ifRadius.setText(
|
||||
FreeCAD.Units.Quantity(self.obj.Radius, FreeCAD.Units.Length).UserString
|
||||
)
|
||||
|
||||
def setupUi(self):
|
||||
self.Positions = self.obj.Positions
|
||||
@@ -292,7 +303,9 @@ class PathDressupTagTaskPanel:
|
||||
self.form.cbTagGeneration.setEnabled(False)
|
||||
|
||||
enableCopy = False
|
||||
for tags in sorted([o.Label for o in FreeCAD.ActiveDocument.Objects if 'DressupTag' in o.Name]):
|
||||
for tags in sorted(
|
||||
[o.Label for o in FreeCAD.ActiveDocument.Objects if "DressupTag" in o.Name]
|
||||
):
|
||||
if tags != self.obj.Label:
|
||||
enableCopy = True
|
||||
self.form.cbSource.addItem(tags)
|
||||
@@ -319,8 +332,8 @@ class HoldingTagMarker:
|
||||
self.pos = coin.SoTranslation()
|
||||
self.pos.translation = (point.x, point.y, point.z)
|
||||
self.sphere = coin.SoSphere()
|
||||
self.scale = coin.SoType.fromName('SoShapeScale').createInstance()
|
||||
self.scale.setPart('shape', self.sphere)
|
||||
self.scale = coin.SoType.fromName("SoShapeScale").createInstance()
|
||||
self.scale.setPart("shape", self.sphere)
|
||||
self.scale.scaleFactor.setValue(7)
|
||||
self.material = coin.SoMaterial()
|
||||
self.sep.addChild(self.pos)
|
||||
@@ -337,15 +350,18 @@ class HoldingTagMarker:
|
||||
def setEnabled(self, enabled):
|
||||
self.enabled = enabled
|
||||
if enabled:
|
||||
self.material.diffuseColor = self.color[0] if not self.selected else self.color[2]
|
||||
self.material.diffuseColor = (
|
||||
self.color[0] if not self.selected else self.color[2]
|
||||
)
|
||||
self.material.transparency = 0.0
|
||||
else:
|
||||
self.material.diffuseColor = self.color[1] if not self.selected else self.color[2]
|
||||
self.material.diffuseColor = (
|
||||
self.color[1] if not self.selected else self.color[2]
|
||||
)
|
||||
self.material.transparency = 0.6
|
||||
|
||||
|
||||
class PathDressupTagViewProvider:
|
||||
|
||||
def __init__(self, vobj):
|
||||
PathLog.track()
|
||||
self.vobj = vobj
|
||||
@@ -362,7 +378,7 @@ class PathDressupTagViewProvider:
|
||||
def debugDisplay(self):
|
||||
# if False and addDebugDisplay():
|
||||
# if not hasattr(self.vobj, 'Debug'):
|
||||
# self.vobj.addProperty('App::PropertyLink', 'Debug', 'Debug', QtCore.QT_TRANSLATE_NOOP('Path_DressupTag', 'Some elements for debugging'))
|
||||
# self.vobj.addProperty('App::PropertyLink', 'Debug', 'Debug', QT_TRANSLATE_NOOP('Path_DressupTag', 'Some elements for debugging'))
|
||||
# dbg = self.vobj.Object.Document.addObject('App::DocumentObjectGroup', 'TagDebug')
|
||||
# self.vobj.Debug = dbg
|
||||
# return True
|
||||
@@ -376,15 +392,25 @@ class PathDressupTagViewProvider:
|
||||
|
||||
def setupColors(self):
|
||||
def colorForColorValue(val):
|
||||
v = [((val >> n) & 0xff) / 255. for n in [24, 16, 8, 0]]
|
||||
v = [((val >> n) & 0xFF) / 255.0 for n in [24, 16, 8, 0]]
|
||||
return coin.SbColor(v[0], v[1], v[2])
|
||||
|
||||
pref = PathPreferences.preferences()
|
||||
# R G B A
|
||||
npc = pref.GetUnsigned('DefaultPathMarkerColor', ((85*256 + 255)*256 + 0) * 256 + 255)
|
||||
hpc = pref.GetUnsigned('DefaultHighlightPathColor', ((255*256 + 125)*256 + 0)*256 + 255)
|
||||
dpc = pref.GetUnsigned('DefaultDisabledPathColor', ((205*256 + 205)*256 + 205)*256 + 154)
|
||||
self.colors = [colorForColorValue(npc), colorForColorValue(dpc), colorForColorValue(hpc)]
|
||||
npc = pref.GetUnsigned(
|
||||
"DefaultPathMarkerColor", ((85 * 256 + 255) * 256 + 0) * 256 + 255
|
||||
)
|
||||
hpc = pref.GetUnsigned(
|
||||
"DefaultHighlightPathColor", ((255 * 256 + 125) * 256 + 0) * 256 + 255
|
||||
)
|
||||
dpc = pref.GetUnsigned(
|
||||
"DefaultDisabledPathColor", ((205 * 256 + 205) * 256 + 205) * 256 + 154
|
||||
)
|
||||
self.colors = [
|
||||
colorForColorValue(npc),
|
||||
colorForColorValue(dpc),
|
||||
colorForColorValue(hpc),
|
||||
]
|
||||
|
||||
def attach(self, vobj):
|
||||
PathLog.track()
|
||||
@@ -398,7 +424,9 @@ class PathDressupTagViewProvider:
|
||||
|
||||
if self.obj and self.obj.Base:
|
||||
for i in self.obj.Base.InList:
|
||||
if hasattr(i, 'Group') and self.obj.Base.Name in [o.Name for o in i.Group]:
|
||||
if hasattr(i, "Group") and self.obj.Base.Name in [
|
||||
o.Name for o in i.Group
|
||||
]:
|
||||
i.Group = [o for o in i.Group if o.Name != self.obj.Base.Name]
|
||||
if self.obj.Base.ViewObject:
|
||||
self.obj.Base.ViewObject.Visibility = False
|
||||
@@ -416,7 +444,7 @@ class PathDressupTagViewProvider:
|
||||
return [self.obj.Base]
|
||||
|
||||
def onDelete(self, arg1=None, arg2=None):
|
||||
'''this makes sure that the base operation is added back to the job and visible'''
|
||||
"""this makes sure that the base operation is added back to the job and visible"""
|
||||
# pylint: disable=unused-argument
|
||||
PathLog.track()
|
||||
if self.obj.Base and self.obj.Base.ViewObject:
|
||||
@@ -436,7 +464,9 @@ class PathDressupTagViewProvider:
|
||||
self.switch.removeChild(tag.sep)
|
||||
tags = []
|
||||
for i, p in enumerate(positions):
|
||||
tag = HoldingTagMarker(self.obj.Proxy.pointAtBottom(self.obj, p), self.colors)
|
||||
tag = HoldingTagMarker(
|
||||
self.obj.Proxy.pointAtBottom(self.obj, p), self.colors
|
||||
)
|
||||
tag.setEnabled(not i in disabled)
|
||||
tags.append(tag)
|
||||
self.switch.addChild(tag.sep)
|
||||
@@ -444,7 +474,7 @@ class PathDressupTagViewProvider:
|
||||
|
||||
def updateData(self, obj, propName):
|
||||
PathLog.track(propName)
|
||||
if 'Disabled' == propName:
|
||||
if "Disabled" == propName:
|
||||
self.updatePositions(obj.Positions, obj.Disabled)
|
||||
|
||||
def onModelChanged(self):
|
||||
@@ -468,7 +498,7 @@ class PathDressupTagViewProvider:
|
||||
|
||||
def unsetEdit(self, vobj, mode):
|
||||
# pylint: disable=unused-argument
|
||||
if hasattr(self, 'panel') and self.panel:
|
||||
if hasattr(self, "panel") and self.panel:
|
||||
self.panel.abort()
|
||||
|
||||
def setupTaskPanel(self, panel):
|
||||
@@ -500,7 +530,9 @@ class PathDressupTagViewProvider:
|
||||
z = self.tags[0].point.z
|
||||
p = FreeCAD.Vector(x, y, z)
|
||||
for i, tag in enumerate(self.tags):
|
||||
if PathGeom.pointsCoincide(p, tag.point, tag.sphere.radius.getValue() * 1.3):
|
||||
if PathGeom.pointsCoincide(
|
||||
p, tag.point, tag.sphere.radius.getValue() * 1.3
|
||||
):
|
||||
return i
|
||||
return -1
|
||||
|
||||
@@ -519,12 +551,12 @@ class PathDressupTagViewProvider:
|
||||
FreeCADGui.updateGui()
|
||||
|
||||
|
||||
def Create(baseObject, name='DressupTag'):
|
||||
'''
|
||||
def Create(baseObject, name="DressupTag"):
|
||||
"""
|
||||
Create(basePath, name = 'DressupTag') ... create tag dressup object for the given base path.
|
||||
Use this command only iff the UI is up - for batch processing see PathDressupTag.Create
|
||||
'''
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("Path_DressupTag", "Create a Tag dressup"))
|
||||
"""
|
||||
FreeCAD.ActiveDocument.openTransaction("Create a Tag dressup")
|
||||
obj = PathDressupTag.Create(baseObject, name)
|
||||
obj.ViewObject.Proxy = PathDressupTagViewProvider(obj.ViewObject)
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
@@ -536,14 +568,18 @@ class CommandPathDressupTag:
|
||||
# pylint: disable=no-init
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': 'Path_Dressup',
|
||||
'MenuText': QtCore.QT_TRANSLATE_NOOP('Path_DressupTag', 'Tag Dress-up'),
|
||||
'ToolTip': QtCore.QT_TRANSLATE_NOOP('Path_DressupTag', 'Creates a Tag Dress-up object from a selected path')}
|
||||
return {
|
||||
"Pixmap": "Path_Dressup",
|
||||
"MenuText": QT_TRANSLATE_NOOP("Path_DressupTag", "Tag Dress-up"),
|
||||
"ToolTip": QT_TRANSLATE_NOOP(
|
||||
"Path_DressupTag", "Creates a Tag Dress-up object from a selected path"
|
||||
),
|
||||
}
|
||||
|
||||
def IsActive(self):
|
||||
if FreeCAD.ActiveDocument is not None:
|
||||
for o in FreeCAD.ActiveDocument.Objects:
|
||||
if o.Name[:3] == 'Job':
|
||||
if o.Name[:3] == "Job":
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -551,19 +587,25 @@ class CommandPathDressupTag:
|
||||
# check that the selection contains exactly what we want
|
||||
selection = FreeCADGui.Selection.getSelection()
|
||||
if len(selection) != 1:
|
||||
PathLog.error(translate('Path_DressupTag', 'Please select one path object')+'\n')
|
||||
PathLog.error(
|
||||
translate("Path_DressupTag", "Please select one path object") + "\n"
|
||||
)
|
||||
return
|
||||
baseObject = selection[0]
|
||||
|
||||
# everything ok!
|
||||
FreeCAD.ActiveDocument.openTransaction(translate('Path_DressupTag', 'Create Tag Dress-up'))
|
||||
FreeCADGui.addModule('PathScripts.PathDressupTagGui')
|
||||
FreeCADGui.doCommand("PathScripts.PathDressupTagGui.Create(App.ActiveDocument.%s)" % baseObject.Name)
|
||||
FreeCAD.ActiveDocument.openTransaction("Create Tag Dress-up")
|
||||
FreeCADGui.addModule("PathScripts.PathDressupTagGui")
|
||||
FreeCADGui.doCommand(
|
||||
"PathScripts.PathDressupTagGui.Create(App.ActiveDocument.%s)"
|
||||
% baseObject.Name
|
||||
)
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
# register the FreeCAD command
|
||||
FreeCADGui.addCommand('Path_DressupTag', CommandPathDressupTag())
|
||||
FreeCADGui.addCommand("Path_DressupTag", CommandPathDressupTag())
|
||||
|
||||
PathLog.notice('Loading PathDressupTagGui... done\n')
|
||||
PathLog.notice("Loading PathDressupTagGui... done\n")
|
||||
|
||||
@@ -21,79 +21,116 @@
|
||||
# ***************************************************************************
|
||||
|
||||
import FreeCAD
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
import PathScripts.PathPreferencesPathDressup as PathPreferencesPathDressup
|
||||
|
||||
from PySide import QtCore
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
class HoldingTagPreferences:
|
||||
DefaultHoldingTagWidth = 'DefaultHoldingTagWidth'
|
||||
DefaultHoldingTagHeight = 'DefaultHoldingTagHeight'
|
||||
DefaultHoldingTagAngle = 'DefaultHoldingTagAngle'
|
||||
DefaultHoldingTagRadius = 'DefaultHoldingTagRadius'
|
||||
DefaultHoldingTagCount = 'DefaultHoldingTagCount'
|
||||
DefaultHoldingTagWidth = "DefaultHoldingTagWidth"
|
||||
DefaultHoldingTagHeight = "DefaultHoldingTagHeight"
|
||||
DefaultHoldingTagAngle = "DefaultHoldingTagAngle"
|
||||
DefaultHoldingTagRadius = "DefaultHoldingTagRadius"
|
||||
DefaultHoldingTagCount = "DefaultHoldingTagCount"
|
||||
|
||||
@classmethod
|
||||
def defaultWidth(cls, ifNotSet):
|
||||
value = PathPreferences.preferences().GetFloat(cls.DefaultHoldingTagWidth, ifNotSet)
|
||||
value = PathPreferences.preferences().GetFloat(
|
||||
cls.DefaultHoldingTagWidth, ifNotSet
|
||||
)
|
||||
if value == 0.0:
|
||||
return ifNotSet
|
||||
return value
|
||||
|
||||
@classmethod
|
||||
def defaultHeight(cls, ifNotSet):
|
||||
value = PathPreferences.preferences().GetFloat(cls.DefaultHoldingTagHeight, ifNotSet)
|
||||
value = PathPreferences.preferences().GetFloat(
|
||||
cls.DefaultHoldingTagHeight, ifNotSet
|
||||
)
|
||||
if value == 0.0:
|
||||
return ifNotSet
|
||||
return value
|
||||
|
||||
@classmethod
|
||||
def defaultAngle(cls, ifNotSet=45.0):
|
||||
value = PathPreferences.preferences().GetFloat(cls.DefaultHoldingTagAngle, ifNotSet)
|
||||
value = PathPreferences.preferences().GetFloat(
|
||||
cls.DefaultHoldingTagAngle, ifNotSet
|
||||
)
|
||||
if value < 10.0:
|
||||
return ifNotSet
|
||||
return value
|
||||
|
||||
@classmethod
|
||||
def defaultCount(cls, ifNotSet=4):
|
||||
value = PathPreferences.preferences().GetUnsigned(cls.DefaultHoldingTagCount, ifNotSet)
|
||||
value = PathPreferences.preferences().GetUnsigned(
|
||||
cls.DefaultHoldingTagCount, ifNotSet
|
||||
)
|
||||
if value < 2:
|
||||
return float(ifNotSet)
|
||||
return float(value)
|
||||
|
||||
@classmethod
|
||||
def defaultRadius(cls, ifNotSet=0.0):
|
||||
return PathPreferences.preferences().GetFloat(cls.DefaultHoldingTagRadius, ifNotSet)
|
||||
return PathPreferences.preferences().GetFloat(
|
||||
cls.DefaultHoldingTagRadius, ifNotSet
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
self.form = FreeCADGui.PySideUic.loadUi(":/preferences/PathDressupHoldingTags.ui")
|
||||
self.label = translate("Path_DressupTag", 'Holding Tag')
|
||||
|
||||
self.form = FreeCADGui.PySideUic.loadUi(
|
||||
":/preferences/PathDressupHoldingTags.ui"
|
||||
)
|
||||
self.label = translate("Path_DressupTag", "Holding Tag")
|
||||
|
||||
def loadSettings(self):
|
||||
self.form.ifWidth.setText(FreeCAD.Units.Quantity(self.defaultWidth(0), FreeCAD.Units.Length).UserString)
|
||||
self.form.ifHeight.setText(FreeCAD.Units.Quantity(self.defaultHeight(0), FreeCAD.Units.Length).UserString)
|
||||
self.form.ifWidth.setText(
|
||||
FreeCAD.Units.Quantity(
|
||||
self.defaultWidth(0), FreeCAD.Units.Length
|
||||
).UserString
|
||||
)
|
||||
self.form.ifHeight.setText(
|
||||
FreeCAD.Units.Quantity(
|
||||
self.defaultHeight(0), FreeCAD.Units.Length
|
||||
).UserString
|
||||
)
|
||||
self.form.dsbAngle.setValue(self.defaultAngle())
|
||||
self.form.ifRadius.setText(FreeCAD.Units.Quantity(self.defaultRadius(), FreeCAD.Units.Length).UserString)
|
||||
self.form.ifRadius.setText(
|
||||
FreeCAD.Units.Quantity(
|
||||
self.defaultRadius(), FreeCAD.Units.Length
|
||||
).UserString
|
||||
)
|
||||
self.form.sbCount.setValue(self.defaultCount())
|
||||
|
||||
def saveSettings(self):
|
||||
pref = PathPreferences.preferences()
|
||||
pref.SetFloat(self.DefaultHoldingTagWidth, FreeCAD.Units.Quantity(self.form.ifWidth.text()).Value)
|
||||
pref.SetFloat(self.DefaultHoldingTagHeight, FreeCAD.Units.Quantity(self.form.ifHeight.text()).Value)
|
||||
pref.SetFloat(
|
||||
self.DefaultHoldingTagWidth,
|
||||
FreeCAD.Units.Quantity(self.form.ifWidth.text()).Value,
|
||||
)
|
||||
pref.SetFloat(
|
||||
self.DefaultHoldingTagHeight,
|
||||
FreeCAD.Units.Quantity(self.form.ifHeight.text()).Value,
|
||||
)
|
||||
pref.SetFloat(self.DefaultHoldingTagAngle, self.form.dsbAngle.value())
|
||||
pref.SetFloat(self.DefaultHoldingTagRadius, FreeCAD.Units.Quantity(self.form.ifRadius.text()))
|
||||
pref.SetFloat(
|
||||
self.DefaultHoldingTagRadius,
|
||||
FreeCAD.Units.Quantity(self.form.ifRadius.text()),
|
||||
)
|
||||
pref.SetUnsigned(self.DefaultHoldingTagCount, self.form.sbCount.value())
|
||||
|
||||
@classmethod
|
||||
def preferencesPage(cls):
|
||||
return HoldingTagPreferences()
|
||||
|
||||
|
||||
PathPreferencesPathDressup.RegisterDressup(HoldingTagPreferences)
|
||||
|
||||
@@ -20,34 +20,36 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
import FreeCAD
|
||||
import Part
|
||||
import PathScripts.PathGeom as PathGeom
|
||||
import PathScripts.PathLog as PathLog
|
||||
import Part
|
||||
import math
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
PathUtils = LazyLoader('PathScripts.PathUtils', globals(), 'PathScripts.PathUtils')
|
||||
|
||||
from PySide import QtCore
|
||||
PathUtils = LazyLoader("PathScripts.PathUtils", globals(), "PathScripts.PathUtils")
|
||||
|
||||
|
||||
__title__ = "Path Features Extensions"
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "Class and implementation of face extensions features."
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
# PathLog.trackModule(PathLog.thisModule())
|
||||
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
def endPoints(edgeOrWire):
|
||||
'''endPoints(edgeOrWire) ... return the first and last point of the wire or the edge, assuming the argument is not a closed wire.'''
|
||||
"""endPoints(edgeOrWire) ... return the first and last point of the wire or the edge, assuming the argument is not a closed wire."""
|
||||
if Part.Wire == type(edgeOrWire):
|
||||
# edges = edgeOrWire.Edges
|
||||
pts = [e.valueAt(e.FirstParameter) for e in edgeOrWire.Edges]
|
||||
@@ -69,7 +71,7 @@ def endPoints(edgeOrWire):
|
||||
|
||||
|
||||
def includesPoint(p, pts):
|
||||
'''includesPoint(p, pts) ... answer True if the collection of pts includes the point p'''
|
||||
"""includesPoint(p, pts) ... answer True if the collection of pts includes the point p"""
|
||||
for pt in pts:
|
||||
if PathGeom.pointsCoincide(p, pt):
|
||||
return True
|
||||
@@ -78,11 +80,13 @@ def includesPoint(p, pts):
|
||||
|
||||
|
||||
def selectOffsetWire(feature, wires):
|
||||
'''selectOffsetWire(feature, wires) ... returns the Wire in wires which is does not intersect with feature'''
|
||||
"""selectOffsetWire(feature, wires) ... returns the Wire in wires which is does not intersect with feature"""
|
||||
closest = None
|
||||
for w in wires:
|
||||
dist = feature.distToShape(w)[0]
|
||||
if closest is None or dist > closest[0]: # pylint: disable=unsubscriptable-object
|
||||
if (
|
||||
closest is None or dist > closest[0]
|
||||
): # pylint: disable=unsubscriptable-object
|
||||
closest = (dist, w)
|
||||
|
||||
if closest is not None:
|
||||
@@ -92,19 +96,24 @@ def selectOffsetWire(feature, wires):
|
||||
|
||||
|
||||
def extendWire(feature, wire, length):
|
||||
'''extendWire(wire, length) ... return a closed Wire which extends wire by length'''
|
||||
"""extendWire(wire, length) ... return a closed Wire which extends wire by length"""
|
||||
PathLog.track(length)
|
||||
|
||||
if not length or length == 0:
|
||||
return None
|
||||
|
||||
|
||||
try:
|
||||
off2D = wire.makeOffset2D(length)
|
||||
except FreeCAD.Base.FreeCADError as ee:
|
||||
PathLog.debug(ee)
|
||||
return None
|
||||
endPts = endPoints(wire) # Assumes wire is NOT closed
|
||||
if endPts:
|
||||
edges = [e for e in off2D.Edges if Part.Circle != type(e.Curve) or not includesPoint(e.Curve.Center, endPts)]
|
||||
edges = [
|
||||
e
|
||||
for e in off2D.Edges
|
||||
if Part.Circle != type(e.Curve) or not includesPoint(e.Curve.Center, endPts)
|
||||
]
|
||||
wires = [Part.Wire(e) for e in Part.sortEdges(edges)]
|
||||
offset = selectOffsetWire(feature, wires)
|
||||
ePts = endPoints(offset)
|
||||
@@ -127,12 +136,14 @@ def extendWire(feature, wire, length):
|
||||
|
||||
|
||||
def createExtension(obj, extObj, extFeature, extSub):
|
||||
return Extension(obj,
|
||||
extObj,
|
||||
extFeature,
|
||||
extSub,
|
||||
obj.ExtensionLengthDefault,
|
||||
Extension.DirectionNormal)
|
||||
return Extension(
|
||||
obj,
|
||||
extObj,
|
||||
extFeature,
|
||||
extSub,
|
||||
obj.ExtensionLengthDefault,
|
||||
Extension.DirectionNormal,
|
||||
)
|
||||
|
||||
|
||||
def readObjExtensionFeature(obj):
|
||||
@@ -142,7 +153,7 @@ def readObjExtensionFeature(obj):
|
||||
|
||||
for extObj, features in obj.ExtensionFeature:
|
||||
for sub in features:
|
||||
extFeature, extSub = sub.split(':')
|
||||
extFeature, extSub = sub.split(":")
|
||||
extensions.append((extObj.Name, extFeature, extSub))
|
||||
return extensions
|
||||
|
||||
@@ -154,7 +165,7 @@ def getExtensions(obj):
|
||||
|
||||
for extObj, features in obj.ExtensionFeature:
|
||||
for sub in features:
|
||||
extFeature, extSub = sub.split(':')
|
||||
extFeature, extSub = sub.split(":")
|
||||
extensions.append(createExtension(obj, extObj, extFeature, extSub))
|
||||
i = i + 1
|
||||
return extensions
|
||||
@@ -167,11 +178,14 @@ def setExtensions(obj, extensions):
|
||||
|
||||
class Extension(object):
|
||||
DirectionNormal = 0
|
||||
DirectionX = 1
|
||||
DirectionY = 2
|
||||
DirectionX = 1
|
||||
DirectionY = 2
|
||||
|
||||
def __init__(self, op, obj, feature, sub, length, direction):
|
||||
PathLog.debug("Extension(%s, %s, %s, %.2f, %s" % (obj.Label, feature, sub, length, direction))
|
||||
PathLog.debug(
|
||||
"Extension(%s, %s, %s, %.2f, %s"
|
||||
% (obj.Label, feature, sub, length, direction)
|
||||
)
|
||||
self.op = op
|
||||
self.obj = obj
|
||||
self.feature = feature
|
||||
@@ -197,8 +211,16 @@ class Extension(object):
|
||||
off = self.length.Value * direction
|
||||
e2.translate(off)
|
||||
e2 = PathGeom.flipEdge(e2)
|
||||
e1 = Part.Edge(Part.LineSegment(e0.valueAt(e0.LastParameter), e2.valueAt(e2.FirstParameter)))
|
||||
e3 = Part.Edge(Part.LineSegment(e2.valueAt(e2.LastParameter), e0.valueAt(e0.FirstParameter)))
|
||||
e1 = Part.Edge(
|
||||
Part.LineSegment(
|
||||
e0.valueAt(e0.LastParameter), e2.valueAt(e2.FirstParameter)
|
||||
)
|
||||
)
|
||||
e3 = Part.Edge(
|
||||
Part.LineSegment(
|
||||
e2.valueAt(e2.LastParameter), e0.valueAt(e0.FirstParameter)
|
||||
)
|
||||
)
|
||||
wire = Part.Wire([e0, e1, e2, e3])
|
||||
self.wire = wire
|
||||
return wire
|
||||
@@ -206,8 +228,8 @@ class Extension(object):
|
||||
return extendWire(feature, Part.Wire([e0]), self.length.Value)
|
||||
|
||||
def _getEdgeNumbers(self):
|
||||
if 'Wire' in self.sub:
|
||||
numbers = [nr for nr in self.sub[5:-1].split(',')]
|
||||
if "Wire" in self.sub:
|
||||
numbers = [nr for nr in self.sub[5:-1].split(",")]
|
||||
else:
|
||||
numbers = [self.sub[4:]]
|
||||
|
||||
@@ -235,7 +257,7 @@ class Extension(object):
|
||||
e0 = wire.Edges[0]
|
||||
midparam = e0.FirstParameter + 0.5 * (e0.LastParameter - e0.FirstParameter)
|
||||
tangent = e0.tangentAt(midparam)
|
||||
PathLog.track('tangent', tangent, self.feature, self.sub)
|
||||
PathLog.track("tangent", tangent, self.feature, self.sub)
|
||||
normal = tangent.cross(FreeCAD.Vector(0, 0, 1))
|
||||
if PathGeom.pointsCoincide(normal, FreeCAD.Vector(0, 0, 0)):
|
||||
return None
|
||||
@@ -243,21 +265,21 @@ class Extension(object):
|
||||
return self._getDirectedNormal(e0.valueAt(midparam), normal.normalize())
|
||||
|
||||
def getExtensionFaces(self, extensionWire):
|
||||
'''getExtensionFace(extensionWire)...
|
||||
"""getExtensionFace(extensionWire)...
|
||||
A public helper method to retrieve the requested extension as a face,
|
||||
rather than a wire because some extensions require a face shape
|
||||
for definition that allows for two wires for boundary definition.
|
||||
'''
|
||||
"""
|
||||
|
||||
if self.extFaces:
|
||||
return self.extFaces
|
||||
|
||||
|
||||
return [Part.Face(extensionWire)]
|
||||
|
||||
def getWire(self):
|
||||
'''getWire()... Public method to retrieve the extension area, pertaining to the feature
|
||||
"""getWire()... Public method to retrieve the extension area, pertaining to the feature
|
||||
and sub element provided at class instantiation, as a closed wire. If no closed wire
|
||||
is possible, a `None` value is returned.'''
|
||||
is possible, a `None` value is returned."""
|
||||
|
||||
if self.sub[:6] == "Avoid_":
|
||||
feature = self.obj.Shape.getElement(self.feature)
|
||||
@@ -281,9 +303,9 @@ class Extension(object):
|
||||
return self._getRegularWire()
|
||||
|
||||
def _getRegularWire(self):
|
||||
'''_getRegularWire()... Private method to retrieve the extension area, pertaining to the feature
|
||||
"""_getRegularWire()... Private method to retrieve the extension area, pertaining to the feature
|
||||
and sub element provided at class instantiation, as a closed wire. If no closed wire
|
||||
is possible, a `None` value is returned.'''
|
||||
is possible, a `None` value is returned."""
|
||||
PathLog.track()
|
||||
|
||||
length = self.length.Value
|
||||
@@ -314,11 +336,23 @@ class Extension(object):
|
||||
|
||||
# assuming the offset produces a valid circle - go for it
|
||||
if r > 0:
|
||||
e3 = Part.makeCircle(r, circle.Center, circle.Axis, edge.FirstParameter * 180 / math.pi, edge.LastParameter * 180 / math.pi)
|
||||
e3 = Part.makeCircle(
|
||||
r,
|
||||
circle.Center,
|
||||
circle.Axis,
|
||||
edge.FirstParameter * 180 / math.pi,
|
||||
edge.LastParameter * 180 / math.pi,
|
||||
)
|
||||
if endPoints(edge):
|
||||
# need to construct the arc slice
|
||||
e0 = Part.makeLine(edge.valueAt(edge.FirstParameter), e3.valueAt(e3.FirstParameter))
|
||||
e2 = Part.makeLine(edge.valueAt(edge.LastParameter), e3.valueAt(e3.LastParameter))
|
||||
e0 = Part.makeLine(
|
||||
edge.valueAt(edge.FirstParameter),
|
||||
e3.valueAt(e3.FirstParameter),
|
||||
)
|
||||
e2 = Part.makeLine(
|
||||
edge.valueAt(edge.LastParameter),
|
||||
e3.valueAt(e3.LastParameter),
|
||||
)
|
||||
return Part.Wire([e0, edge, e2, e3])
|
||||
|
||||
extWire = Part.Wire([e3])
|
||||
@@ -357,8 +391,9 @@ class Extension(object):
|
||||
try:
|
||||
off2D = sub.makeOffset2D(length)
|
||||
except FreeCAD.Base.FreeCADError as ee:
|
||||
PathLog.debug(ee)
|
||||
return None
|
||||
|
||||
|
||||
if isOutside:
|
||||
self.extFaces = [Part.Face(off2D).cut(featFace)]
|
||||
else:
|
||||
@@ -369,9 +404,9 @@ class Extension(object):
|
||||
return extendWire(feature, sub, length)
|
||||
|
||||
def _getOutlineWire(self):
|
||||
'''_getOutlineWire()... Private method to retrieve an extended outline extension area,
|
||||
"""_getOutlineWire()... Private method to retrieve an extended outline extension area,
|
||||
pertaining to the feature and sub element provided at class instantiation, as a closed wire.
|
||||
If no closed wire is possible, a `None` value is returned.'''
|
||||
If no closed wire is possible, a `None` value is returned."""
|
||||
PathLog.track()
|
||||
|
||||
baseShape = self.obj.Shape
|
||||
@@ -411,10 +446,10 @@ class Extension(object):
|
||||
return None
|
||||
|
||||
def _getWaterlineWire(self):
|
||||
'''_getWaterlineWire()... Private method to retrieve a waterline extension area,
|
||||
"""_getWaterlineWire()... Private method to retrieve a waterline extension area,
|
||||
pertaining to the feature and sub element provided at class instantiation, as a closed wire.
|
||||
Only waterline faces touching source face are returned as part of the waterline extension area.
|
||||
If no closed wire is possible, a `None` value is returned.'''
|
||||
If no closed wire is possible, a `None` value is returned."""
|
||||
PathLog.track()
|
||||
|
||||
msg = translate("PathFeatureExtensions", "Waterline error")
|
||||
@@ -451,9 +486,9 @@ class Extension(object):
|
||||
return None
|
||||
|
||||
def _makeCircularExtFace(self, edge, extWire):
|
||||
'''_makeCircularExtensionFace(edge, extWire)...
|
||||
"""_makeCircularExtensionFace(edge, extWire)...
|
||||
Create proper circular extension face shape. Incoming edge is expected to be a circle.
|
||||
'''
|
||||
"""
|
||||
# Add original outer wire to cut faces if necessary
|
||||
edgeFace = Part.Face(Part.Wire([edge]))
|
||||
edgeFace.translate(FreeCAD.Vector(0.0, 0.0, 0.0 - edgeFace.BoundBox.ZMin))
|
||||
@@ -467,37 +502,58 @@ class Extension(object):
|
||||
extensionFace.translate(FreeCAD.Vector(0.0, 0.0, edge.BoundBox.ZMin))
|
||||
|
||||
return extensionFace
|
||||
|
||||
|
||||
# Eclass
|
||||
|
||||
|
||||
def initialize_properties(obj):
|
||||
"""initialize_properties(obj)... Adds feature properties to object argument"""
|
||||
if not hasattr(obj, 'ExtensionLengthDefault'):
|
||||
obj.addProperty('App::PropertyDistance', 'ExtensionLengthDefault', 'Extension', QtCore.QT_TRANSLATE_NOOP('PathPocketShape', 'Default length of extensions.'))
|
||||
if not hasattr(obj, 'ExtensionFeature'):
|
||||
obj.addProperty('App::PropertyLinkSubListGlobal', 'ExtensionFeature', 'Extension', QtCore.QT_TRANSLATE_NOOP('PathPocketShape', 'List of features to extend.'))
|
||||
if not hasattr(obj, 'ExtensionCorners'):
|
||||
obj.addProperty('App::PropertyBool', 'ExtensionCorners', 'Extension', QtCore.QT_TRANSLATE_NOOP('PathPocketShape', 'When enabled connected extension edges are combined to wires.'))
|
||||
if not hasattr(obj, "ExtensionLengthDefault"):
|
||||
obj.addProperty(
|
||||
"App::PropertyDistance",
|
||||
"ExtensionLengthDefault",
|
||||
"Extension",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Default length of extensions."),
|
||||
)
|
||||
if not hasattr(obj, "ExtensionFeature"):
|
||||
obj.addProperty(
|
||||
"App::PropertyLinkSubListGlobal",
|
||||
"ExtensionFeature",
|
||||
"Extension",
|
||||
QT_TRANSLATE_NOOP("App::Property", "List of features to extend."),
|
||||
)
|
||||
if not hasattr(obj, "ExtensionCorners"):
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"ExtensionCorners",
|
||||
"Extension",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"When enabled connected extension edges are combined to wires.",
|
||||
),
|
||||
)
|
||||
obj.ExtensionCorners = True
|
||||
|
||||
obj.setEditorMode('ExtensionFeature', 2)
|
||||
obj.setEditorMode("ExtensionFeature", 2)
|
||||
|
||||
|
||||
def set_default_property_values(obj, job):
|
||||
"""set_default_property_values(obj, job) ... set default values for feature properties"""
|
||||
obj.ExtensionCorners = True
|
||||
obj.setExpression('ExtensionLengthDefault', 'OpToolDiameter / 2.0')
|
||||
obj.setExpression("ExtensionLengthDefault", "OpToolDiameter / 2.0")
|
||||
|
||||
|
||||
def SetupProperties():
|
||||
"""SetupProperties()... Returns list of feature property names"""
|
||||
setup = ['ExtensionLengthDefault', 'ExtensionFeature',
|
||||
'ExtensionCorners']
|
||||
setup = ["ExtensionLengthDefault", "ExtensionFeature", "ExtensionCorners"]
|
||||
return setup
|
||||
|
||||
|
||||
# Extend outline face generation function
|
||||
def getExtendOutlineFace(base_shape, face, extension, remHoles=False, offset_tolerance=1e-4):
|
||||
def getExtendOutlineFace(
|
||||
base_shape, face, extension, remHoles=False, offset_tolerance=1e-4
|
||||
):
|
||||
"""getExtendOutlineFace(obj, base_shape, face, extension, remHoles) ...
|
||||
Creates an extended face for the pocket, taking into consideration lateral
|
||||
collision with the greater base shape.
|
||||
@@ -513,11 +569,9 @@ def getExtendOutlineFace(base_shape, face, extension, remHoles=False, offset_tol
|
||||
"""
|
||||
|
||||
# Make offset face per user-specified extension distance so as to allow full clearing of face where possible.
|
||||
offset_face = PathUtils.getOffsetArea(face,
|
||||
extension,
|
||||
removeHoles=remHoles,
|
||||
plane=face,
|
||||
tolerance=offset_tolerance)
|
||||
offset_face = PathUtils.getOffsetArea(
|
||||
face, extension, removeHoles=remHoles, plane=face, tolerance=offset_tolerance
|
||||
)
|
||||
if not offset_face:
|
||||
PathLog.error("Failed to offset a selected face.")
|
||||
return None
|
||||
@@ -540,9 +594,11 @@ def getExtendOutlineFace(base_shape, face, extension, remHoles=False, offset_tol
|
||||
for f in available.Faces:
|
||||
bbx = f.BoundBox
|
||||
zNorm = abs(f.normalAt(0.0, 0.0).z)
|
||||
if (PathGeom.isRoughly(zNorm, 1.0) and
|
||||
PathGeom.isRoughly(bbx.ZMax - bbx.ZMin, 0.0) and
|
||||
PathGeom.isRoughly(bbx.ZMin, face.BoundBox.ZMin)):
|
||||
if (
|
||||
PathGeom.isRoughly(zNorm, 1.0)
|
||||
and PathGeom.isRoughly(bbx.ZMax - bbx.ZMin, 0.0)
|
||||
and PathGeom.isRoughly(bbx.ZMin, face.BoundBox.ZMin)
|
||||
):
|
||||
if bbx.ZMin < zmin:
|
||||
bottom_faces.append(f)
|
||||
|
||||
@@ -561,6 +617,7 @@ def getExtendOutlineFace(base_shape, face, extension, remHoles=False, offset_tol
|
||||
PathLog.error("No bottom face for extend outline.")
|
||||
return None
|
||||
|
||||
|
||||
# Waterline extension face generation function
|
||||
def getWaterlineFace(base_shape, face):
|
||||
"""getWaterlineFace(base_shape, face) ...
|
||||
@@ -580,8 +637,11 @@ def getWaterlineFace(base_shape, face):
|
||||
step_down=math.floor(faceHeight - baseBB.ZMin + 2.0),
|
||||
z_finish_step=0.0,
|
||||
final_depth=baseBB.ZMin,
|
||||
user_depths=None)
|
||||
env = PathUtils.getEnvelope(partshape=base_shape, subshape=None, depthparams=depthparams)
|
||||
user_depths=None,
|
||||
)
|
||||
env = PathUtils.getEnvelope(
|
||||
partshape=base_shape, subshape=None, depthparams=depthparams
|
||||
)
|
||||
# Get top face(s) of envelope at face height
|
||||
rawList = list()
|
||||
for f in env.Faces:
|
||||
@@ -589,7 +649,9 @@ def getWaterlineFace(base_shape, face):
|
||||
rawList.append(f)
|
||||
# make compound and extrude downward
|
||||
rawComp = Part.makeCompound(rawList)
|
||||
rawCompExtNeg = rawComp.extrude(FreeCAD.Vector(0.0, 0.0, baseBB.ZMin - faceHeight - 1.0))
|
||||
rawCompExtNeg = rawComp.extrude(
|
||||
FreeCAD.Vector(0.0, 0.0, baseBB.ZMin - faceHeight - 1.0)
|
||||
)
|
||||
# Cut off bottom of base shape at face height
|
||||
topSolid = base_shape.cut(rawCompExtNeg)
|
||||
|
||||
|
||||
@@ -20,35 +20,39 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
from pivy import coin
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import PathScripts.PathFeatureExtensions as FeatureExtensions
|
||||
import PathScripts.PathGeom as PathGeom
|
||||
import PathScripts.PathGui as PathGui
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathOpGui as PathOpGui
|
||||
import PathScripts.PathFeatureExtensions as FeatureExtensions
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
from pivy import coin
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
Part = LazyLoader('Part', globals(), 'Part')
|
||||
|
||||
Part = LazyLoader("Part", globals(), "Part")
|
||||
|
||||
__title__ = "Path Feature Extensions UI"
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "Extensions feature page controller."
|
||||
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
#PathLog.trackModule(PathLog.thisModule())
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
class _Extension(object):
|
||||
ColourEnabled = (1.0, .5, 1.0)
|
||||
ColourDisabled = (1.0, 1.0, .5)
|
||||
ColourEnabled = (1.0, 0.5, 1.0)
|
||||
ColourDisabled = (1.0, 1.0, 0.5)
|
||||
TransparencySelected = 0.0
|
||||
TransparencyDeselected = 0.7
|
||||
|
||||
@@ -152,7 +156,7 @@ class _Extension(object):
|
||||
if self.switch:
|
||||
self.switch.whichChild = coin.SO_SWITCH_NONE
|
||||
|
||||
def enable(self, ena = True):
|
||||
def enable(self, ena=True):
|
||||
if ena:
|
||||
self.material.diffuseColor = self.ColourEnabled
|
||||
else:
|
||||
@@ -173,10 +177,10 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
|
||||
DataSwitch = QtCore.Qt.ItemDataRole.UserRole + 2
|
||||
|
||||
Direction = {
|
||||
FeatureExtensions.Extension.DirectionNormal: translate('PathPocket', 'Normal'),
|
||||
FeatureExtensions.Extension.DirectionX: translate('PathPocket', 'X'),
|
||||
FeatureExtensions.Extension.DirectionY: translate('PathPocket', 'Y')
|
||||
}
|
||||
FeatureExtensions.Extension.DirectionNormal: translate("PathPocket", "Normal"),
|
||||
FeatureExtensions.Extension.DirectionX: translate("PathPocket", "X"),
|
||||
FeatureExtensions.Extension.DirectionY: translate("PathPocket", "Y"),
|
||||
}
|
||||
|
||||
def initPage(self, obj):
|
||||
self.setTitle("Extensions")
|
||||
@@ -193,17 +197,21 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
|
||||
|
||||
self.extensions = list()
|
||||
|
||||
self.defaultLength = PathGui.QuantitySpinBox(self.form.defaultLength, obj, 'ExtensionLengthDefault') # pylint: disable=attribute-defined-outside-init
|
||||
self.defaultLength = PathGui.QuantitySpinBox(
|
||||
self.form.defaultLength, obj, "ExtensionLengthDefault"
|
||||
) # pylint: disable=attribute-defined-outside-init
|
||||
|
||||
self.form.extensionTree.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
|
||||
self.form.extensionTree.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
|
||||
|
||||
self.switch = coin.SoSwitch() # pylint: disable=attribute-defined-outside-init
|
||||
self.switch = coin.SoSwitch() # pylint: disable=attribute-defined-outside-init
|
||||
self.obj.ViewObject.RootNode.addChild(self.switch)
|
||||
self.switch.whichChild = coin.SO_SWITCH_ALL
|
||||
|
||||
self.model = QtGui.QStandardItemModel(self.form.extensionTree) # pylint: disable=attribute-defined-outside-init
|
||||
self.model.setHorizontalHeaderLabels(['Base', 'Extension'])
|
||||
self.model = QtGui.QStandardItemModel(
|
||||
self.form.extensionTree
|
||||
) # pylint: disable=attribute-defined-outside-init
|
||||
self.model.setHorizontalHeaderLabels(["Base", "Extension"])
|
||||
|
||||
"""
|
||||
# russ4262: This `if` block shows all available extensions upon edit of operation with any extension enabled.
|
||||
@@ -216,7 +224,7 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
|
||||
"""
|
||||
self.form.showExtensions.setCheckState(QtCore.Qt.Unchecked)
|
||||
|
||||
self.blockUpdateData = False # pylint: disable=attribute-defined-outside-init
|
||||
self.blockUpdateData = False # pylint: disable=attribute-defined-outside-init
|
||||
|
||||
def cleanupPage(self, obj):
|
||||
try:
|
||||
@@ -243,29 +251,33 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
|
||||
def currentExtensions(self):
|
||||
PathLog.debug("currentExtensions()")
|
||||
extensions = []
|
||||
|
||||
def extractExtension(item, ext):
|
||||
if ext and ext.edge and item.checkState() == QtCore.Qt.Checked:
|
||||
extensions.append(ext.ext)
|
||||
|
||||
if self.form.enableExtensions.isChecked():
|
||||
self.forAllItemsCall(extractExtension)
|
||||
PathLog.track('extensions', extensions)
|
||||
PathLog.track("extensions", extensions)
|
||||
return extensions
|
||||
|
||||
def updateProxyExtensions(self, obj):
|
||||
PathLog.debug("updateProxyExtensions()")
|
||||
self.extensions = self.currentExtensions() # pylint: disable=attribute-defined-outside-init
|
||||
self.extensions = (
|
||||
self.currentExtensions()
|
||||
) # pylint: disable=attribute-defined-outside-init
|
||||
FeatureExtensions.setExtensions(obj, self.extensions)
|
||||
|
||||
def getFields(self, obj):
|
||||
PathLog.track(obj.Label, self.model.rowCount(), self.model.columnCount())
|
||||
self.blockUpdateData = True # pylint: disable=attribute-defined-outside-init
|
||||
self.blockUpdateData = True # pylint: disable=attribute-defined-outside-init
|
||||
|
||||
if obj.ExtensionCorners != self.form.extendCorners.isChecked():
|
||||
obj.ExtensionCorners = self.form.extendCorners.isChecked()
|
||||
self.defaultLength.updateProperty()
|
||||
|
||||
self.updateProxyExtensions(obj)
|
||||
self.blockUpdateData = False # pylint: disable=attribute-defined-outside-init
|
||||
self.blockUpdateData = False # pylint: disable=attribute-defined-outside-init
|
||||
|
||||
def setFields(self, obj):
|
||||
PathLog.track(obj.Label)
|
||||
@@ -285,7 +297,9 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
|
||||
Subroutine called inside `setFields()` to initialize Extensions efficiently."""
|
||||
if self.enabled:
|
||||
self.extensions = FeatureExtensions.getExtensions(obj)
|
||||
elif len(obj.ExtensionFeature) > 0: # latter test loads pre-existing extensions (editing of existing operation)
|
||||
elif (
|
||||
len(obj.ExtensionFeature) > 0
|
||||
): # latter test loads pre-existing extensions (editing of existing operation)
|
||||
noEdges = True
|
||||
for (__, __, subFeat) in FeatureExtensions.readObjExtensionFeature(obj):
|
||||
if subFeat.startswith("Edge") or subFeat.startswith("Wire"):
|
||||
@@ -311,7 +325,9 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
|
||||
self._enableExtensions() # Recalculate extensions
|
||||
|
||||
def createItemForBaseModel(self, base, sub, edges, extensions):
|
||||
PathLog.track(base.Label, sub, '+', len(edges), len(base.Shape.getElement(sub).Edges))
|
||||
PathLog.track(
|
||||
base.Label, sub, "+", len(edges), len(base.Shape.getElement(sub).Edges)
|
||||
)
|
||||
# PathLog.debug("createItemForBaseModel() label: {}, sub: {}, {}, edgeCnt: {}, subEdges: {}".format(base.Label, sub, '+', len(edges), len(base.Shape.getElement(sub).Edges)))
|
||||
|
||||
extendCorners = self.form.extendCorners.isChecked()
|
||||
@@ -341,7 +357,7 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
|
||||
|
||||
extensionEdges = {}
|
||||
if includeEdges:
|
||||
if self.useOutline == 1 and sub.startswith('Face'):
|
||||
if self.useOutline == 1 and sub.startswith("Face"):
|
||||
# Only show exterior extensions if `Use Outline` is True
|
||||
subEdges = subShape.Wires[0].Edges
|
||||
else:
|
||||
@@ -358,26 +374,48 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
|
||||
createSubItem(label, ext1)
|
||||
|
||||
if extendCorners and includeEdges:
|
||||
|
||||
def edgesMatchShape(e0, e1):
|
||||
flipped = PathGeom.flipEdge(e1)
|
||||
if flipped:
|
||||
return PathGeom.edgesMatch(e0, e1) or PathGeom.edgesMatch(e0, flipped)
|
||||
return PathGeom.edgesMatch(e0, e1) or PathGeom.edgesMatch(
|
||||
e0, flipped
|
||||
)
|
||||
else:
|
||||
return PathGeom.edgesMatch(e0, e1)
|
||||
|
||||
self.extensionEdges = extensionEdges
|
||||
PathLog.debug("extensionEdges.values(): {}".format(extensionEdges.values()))
|
||||
for edgeList in Part.sortEdges(list(extensionEdges.keys())): # Identify connected edges that form wires
|
||||
for edgeList in Part.sortEdges(
|
||||
list(extensionEdges.keys())
|
||||
): # Identify connected edges that form wires
|
||||
self.edgeList = edgeList
|
||||
if len(edgeList) == 1:
|
||||
label = "Edge%s" % [extensionEdges[keyEdge] for keyEdge in extensionEdges.keys() if edgesMatchShape(keyEdge, edgeList[0])][0]
|
||||
label = (
|
||||
"Edge%s"
|
||||
% [
|
||||
extensionEdges[keyEdge]
|
||||
for keyEdge in extensionEdges.keys()
|
||||
if edgesMatchShape(keyEdge, edgeList[0])
|
||||
][0]
|
||||
)
|
||||
else:
|
||||
label = "Wire(%s)" % ','.join(sorted([extensionEdges[keyEdge] for e in edgeList for keyEdge in extensionEdges.keys() if edgesMatchShape(e, keyEdge)], key=lambda s: int(s))) # pylint: disable=unnecessary-lambda
|
||||
label = "Wire(%s)" % ",".join(
|
||||
sorted(
|
||||
[
|
||||
extensionEdges[keyEdge]
|
||||
for e in edgeList
|
||||
for keyEdge in extensionEdges.keys()
|
||||
if edgesMatchShape(e, keyEdge)
|
||||
],
|
||||
key=lambda s: int(s),
|
||||
)
|
||||
) # pylint: disable=unnecessary-lambda
|
||||
ext2 = self._cachedExtension(self.obj, base, sub, label)
|
||||
createSubItem(label, ext2)
|
||||
|
||||
# Only add these subItems for horizontally oriented faces, not edges or vertical faces (from vertical face loops)
|
||||
if sub.startswith('Face') and PathGeom.isHorizontal(subShape):
|
||||
if sub.startswith("Face") and PathGeom.isHorizontal(subShape):
|
||||
# Add entry to extend outline of face
|
||||
label = "Extend_" + sub
|
||||
ext3 = self._cachedExtension(self.obj, base, sub, label)
|
||||
@@ -406,8 +444,11 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
|
||||
self.form.extensionTree.blockSignals(True)
|
||||
|
||||
# remember current visual state
|
||||
if hasattr(self, 'selectionModel'):
|
||||
selectedExtensions = [self.model.itemFromIndex(index).data(self.DataObject).ext for index in self.selectionModel.selectedIndexes()]
|
||||
if hasattr(self, "selectionModel"):
|
||||
selectedExtensions = [
|
||||
self.model.itemFromIndex(index).data(self.DataObject).ext
|
||||
for index in self.selectionModel.selectedIndexes()
|
||||
]
|
||||
else:
|
||||
selectedExtensions = []
|
||||
collapsedModels = []
|
||||
@@ -420,7 +461,9 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
|
||||
for featureRow in range(model.rowCount()):
|
||||
feature = model.child(featureRow, 0)
|
||||
if not self.form.extensionTree.isExpanded(feature.index()):
|
||||
collapsedFeatures.append("%s.%s" % (modelName, feature.data(QtCore.Qt.EditRole)))
|
||||
collapsedFeatures.append(
|
||||
"%s.%s" % (modelName, feature.data(QtCore.Qt.EditRole))
|
||||
)
|
||||
|
||||
# remove current extensions and all their visuals
|
||||
def removeItemSwitch(item, ext):
|
||||
@@ -428,6 +471,7 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
|
||||
ext.hide()
|
||||
if ext.root:
|
||||
self.switch.removeChild(ext.root)
|
||||
|
||||
self.forAllItemsCall(removeItemSwitch)
|
||||
self.model.clear()
|
||||
|
||||
@@ -435,14 +479,19 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
|
||||
if self.enabled:
|
||||
for base in self.obj.Base:
|
||||
show = False
|
||||
edges = [(edge, "Edge%d" % (i + 1)) for i, edge in enumerate(base[0].Shape.Edges)]
|
||||
edges = [
|
||||
(edge, "Edge%d" % (i + 1))
|
||||
for i, edge in enumerate(base[0].Shape.Edges)
|
||||
]
|
||||
baseItem = QtGui.QStandardItem()
|
||||
baseItem.setData(base[0].Label, QtCore.Qt.EditRole)
|
||||
baseItem.setSelectable(False)
|
||||
for sub in sorted(base[1]):
|
||||
if sub.startswith('Face'):
|
||||
if sub.startswith("Face"):
|
||||
show = True
|
||||
baseItem.appendRow(self.createItemForBaseModel(base[0], sub, edges, extensions))
|
||||
baseItem.appendRow(
|
||||
self.createItemForBaseModel(base[0], sub, edges, extensions)
|
||||
)
|
||||
if show:
|
||||
self.model.appendRow(baseItem)
|
||||
|
||||
@@ -458,10 +507,10 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
|
||||
self.form.extensionTree.setExpanded(model.index(), False)
|
||||
for featureRow in range(model.rowCount()):
|
||||
feature = model.child(featureRow, 0)
|
||||
featureName = "%s.%s" % (modelName, feature.data(QtCore.Qt.EditRole))
|
||||
featureName = "%s.%s" % (modelName, feature.data(QtCore.Qt.EditRole))
|
||||
if featureName in collapsedFeatures:
|
||||
self.form.extensionTree.setExpanded(feature.index(), False)
|
||||
if hasattr(self, 'selectionModel') and selectedExtensions:
|
||||
if hasattr(self, "selectionModel") and selectedExtensions:
|
||||
self.restoreSelection(selectedExtensions)
|
||||
|
||||
self.form.extensionTree.blockSignals(False)
|
||||
@@ -475,22 +524,22 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
|
||||
if not self.blockUpdateData:
|
||||
if self.fieldsSet:
|
||||
if self.form.enableExtensions.isChecked():
|
||||
if prop == 'ExtensionLengthDefault':
|
||||
self.updateQuantitySpinBoxes()
|
||||
elif prop == 'Base':
|
||||
self.extensionsReady = False
|
||||
self.setExtensions(FeatureExtensions.getExtensions(obj))
|
||||
elif prop == 'UseOutline':
|
||||
self._getUseOutlineState() # Find `useOutline` checkbox and get its boolean value
|
||||
self._includeEdgesAndWires()
|
||||
elif prop == 'Base':
|
||||
if prop == "ExtensionLengthDefault":
|
||||
self.updateQuantitySpinBoxes()
|
||||
elif prop == "Base":
|
||||
self.extensionsReady = False
|
||||
self.setExtensions(FeatureExtensions.getExtensions(obj))
|
||||
elif prop == "UseOutline":
|
||||
self._getUseOutlineState() # Find `useOutline` checkbox and get its boolean value
|
||||
self._includeEdgesAndWires()
|
||||
elif prop == "Base":
|
||||
self.extensionsReady = False
|
||||
|
||||
def restoreSelection(self, selection):
|
||||
PathLog.debug("restoreSelection()")
|
||||
PathLog.track()
|
||||
if 0 == self.model.rowCount():
|
||||
PathLog.track('-')
|
||||
PathLog.track("-")
|
||||
self.form.buttonClear.setEnabled(False)
|
||||
self.form.buttonDisable.setEnabled(False)
|
||||
self.form.buttonEnable.setEnabled(False)
|
||||
@@ -515,7 +564,9 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
|
||||
|
||||
def setSelectionVisuals(item, ext):
|
||||
if selectItem(item, ext):
|
||||
self.selectionModel.select(item.index(), QtCore.QItemSelectionModel.Select)
|
||||
self.selectionModel.select(
|
||||
item.index(), QtCore.QItemSelectionModel.Select
|
||||
)
|
||||
|
||||
selected = self.selectionModel.isSelected(item.index())
|
||||
if selected:
|
||||
@@ -528,6 +579,7 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
|
||||
ext.show()
|
||||
else:
|
||||
ext.hide()
|
||||
|
||||
self.forAllItemsCall(setSelectionVisuals)
|
||||
|
||||
def selectionChanged(self):
|
||||
@@ -536,6 +588,7 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
|
||||
|
||||
def extensionsClear(self):
|
||||
PathLog.debug("extensionsClear()")
|
||||
|
||||
def disableItem(item, ext):
|
||||
item.setCheckState(QtCore.Qt.Unchecked)
|
||||
ext.disable()
|
||||
@@ -572,16 +625,20 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
|
||||
|
||||
def showHideExtension(self):
|
||||
if self.form.showExtensions.isChecked():
|
||||
|
||||
def enableExtensionEdit(item, ext):
|
||||
# pylint: disable=unused-argument
|
||||
ext.show()
|
||||
|
||||
self.forAllItemsCall(enableExtensionEdit)
|
||||
else:
|
||||
|
||||
def disableExtensionEdit(item, ext):
|
||||
if not self.selectionModel.isSelected(item.index()):
|
||||
ext.hide()
|
||||
|
||||
self.forAllItemsCall(disableExtensionEdit)
|
||||
#self.setDirty()
|
||||
# self.setDirty()
|
||||
|
||||
def toggleExtensionCorners(self):
|
||||
PathLog.debug("toggleExtensionCorners()")
|
||||
@@ -612,7 +669,9 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
|
||||
|
||||
self.model.itemChanged.connect(self.updateItemEnabled)
|
||||
|
||||
self.selectionModel = self.form.extensionTree.selectionModel() # pylint: disable=attribute-defined-outside-init
|
||||
self.selectionModel = (
|
||||
self.form.extensionTree.selectionModel()
|
||||
) # pylint: disable=attribute-defined-outside-init
|
||||
self.selectionModel.selectionChanged.connect(self.selectionChanged)
|
||||
self.selectionChanged()
|
||||
|
||||
@@ -626,12 +685,14 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
|
||||
if self.useOutlineCheckbox:
|
||||
self.useOutline = self.useOutlineCheckbox.isChecked()
|
||||
|
||||
if hasattr(self, 'parent'):
|
||||
parent = getattr(self, 'parent')
|
||||
if parent and hasattr(parent, 'featurePages'):
|
||||
if hasattr(self, "parent"):
|
||||
parent = getattr(self, "parent")
|
||||
if parent and hasattr(parent, "featurePages"):
|
||||
for page in parent.featurePages:
|
||||
if hasattr(page, 'panelTitle'):
|
||||
if page.panelTitle == 'Operation' and hasattr(page.form, 'useOutline'):
|
||||
if hasattr(page, "panelTitle"):
|
||||
if page.panelTitle == "Operation" and hasattr(
|
||||
page.form, "useOutline"
|
||||
):
|
||||
PathLog.debug("Found useOutline checkbox")
|
||||
self.useOutlineCheckbox = page.form.useOutline
|
||||
if page.form.useOutline.isChecked():
|
||||
@@ -669,10 +730,14 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
|
||||
self.form.includeEdges.blockSignals(True)
|
||||
|
||||
# Make changes to form
|
||||
msg = translate("PathPocketShape",
|
||||
"Edge count greater than threshold of" + " " +
|
||||
str(self.edgeCountThreshold) + ": " +
|
||||
str(self.initialEdgeCount))
|
||||
msg = translate(
|
||||
"PathPocketShape",
|
||||
"Edge count greater than threshold of"
|
||||
+ " "
|
||||
+ str(self.edgeCountThreshold)
|
||||
+ ": "
|
||||
+ str(self.initialEdgeCount),
|
||||
)
|
||||
self.form.enableExtensionsWarning.setText(msg)
|
||||
self.form.enableExtensions.setChecked(False)
|
||||
self.form.enableExtensionsWarning.show()
|
||||
@@ -763,6 +828,8 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
|
||||
reset[k] = self.extensionsCache[k]
|
||||
self.extensionsCache = reset
|
||||
self.extensionsReady = False
|
||||
|
||||
|
||||
# Eclass
|
||||
|
||||
FreeCAD.Console.PrintLog("Loading PathFeatureExtensionsGui... done\n")
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
from PathScripts.PathPostProcessor import PostProcessor
|
||||
from PySide import QtCore
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
import FreeCAD
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
@@ -29,8 +32,7 @@ import PathScripts.PathToolController as PathToolController
|
||||
import PathScripts.PathUtil as PathUtil
|
||||
import json
|
||||
import time
|
||||
from PathScripts.PathPostProcessor import PostProcessor
|
||||
from PySide import QtCore
|
||||
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
@@ -38,12 +40,13 @@ from lazy_loader.lazy_loader import LazyLoader
|
||||
Draft = LazyLoader("Draft", globals(), "Draft")
|
||||
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
class JobTemplate:
|
||||
@@ -111,34 +114,35 @@ class ObjectJob:
|
||||
"App::PropertyFile",
|
||||
"PostProcessorOutputFile",
|
||||
"Output",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathJob", "The NC output file for this project"),
|
||||
QT_TRANSLATE_NOOP("App::Property", "The NC output file for this project"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"PostProcessor",
|
||||
"Output",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathJob", "Select the Post Processor"),
|
||||
QT_TRANSLATE_NOOP("App::Property", "Select the Post Processor"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"PostProcessorArgs",
|
||||
"Output",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathJob", "Arguments for the Post Processor (specific to the script)"
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Arguments for the Post Processor (specific to the script)",
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"LastPostProcessDate",
|
||||
"Output",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathJob", "Last Time the Job was post-processed"),
|
||||
QT_TRANSLATE_NOOP("App::Property", "Last Time the Job was post-processed"),
|
||||
)
|
||||
obj.setEditorMode("LastPostProcessDate", 2) # Hide
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"LastPostProcessOutput",
|
||||
"Output",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathJob", "Last Time the Job was post-processed"),
|
||||
QT_TRANSLATE_NOOP("App::Property", "Last Time the Job was post-processed"),
|
||||
)
|
||||
obj.setEditorMode("LastPostProcessOutput", 2) # Hide
|
||||
|
||||
@@ -146,21 +150,21 @@ class ObjectJob:
|
||||
"App::PropertyString",
|
||||
"Description",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathJob", "An optional description for this job"),
|
||||
QT_TRANSLATE_NOOP("App::Property", "An optional description for this job"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"CycleTime",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathOp", "Job Cycle Time Estimation"),
|
||||
QT_TRANSLATE_NOOP("App::Property", "Job Cycle Time Estimation"),
|
||||
)
|
||||
obj.setEditorMode("CycleTime", 1) # read-only
|
||||
obj.addProperty(
|
||||
"App::PropertyDistance",
|
||||
"GeometryTolerance",
|
||||
"Geometry",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathJob",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"For computing Paths; smaller increases accuracy, but slows down computation",
|
||||
),
|
||||
)
|
||||
@@ -169,14 +173,14 @@ class ObjectJob:
|
||||
"App::PropertyLink",
|
||||
"Stock",
|
||||
"Base",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathJob", "Solid object to be used as stock."),
|
||||
QT_TRANSLATE_NOOP("App::Property", "Solid object to be used as stock."),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyLink",
|
||||
"Operations",
|
||||
"Base",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathJob",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Compound path of all operations in the order they are processed.",
|
||||
),
|
||||
)
|
||||
@@ -185,7 +189,7 @@ class ObjectJob:
|
||||
"App::PropertyEnumeration",
|
||||
"JobType",
|
||||
"Base",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathJob", "Select the Type of Job"),
|
||||
QT_TRANSLATE_NOOP("App::Property", "Select the Type of Job"),
|
||||
)
|
||||
obj.setEditorMode("JobType", 2) # Hide
|
||||
|
||||
@@ -193,30 +197,31 @@ class ObjectJob:
|
||||
"App::PropertyBool",
|
||||
"SplitOutput",
|
||||
"Output",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathJob", "Split output into multiple gcode files"
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Split output into multiple gcode files"
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"OrderOutputBy",
|
||||
"WCS",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathJob", "If multiple WCS, order the output this way"
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "If multiple WCS, order the output this way"
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyStringList",
|
||||
"Fixtures",
|
||||
"WCS",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathJob", "The Work Coordinate Systems for the Job"
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "The Work Coordinate Systems for the Job"
|
||||
),
|
||||
)
|
||||
obj.OrderOutputBy = ["Fixture", "Tool", "Operation"]
|
||||
|
||||
obj.Fixtures = ["G54"]
|
||||
|
||||
obj.JobType = ["2D", "2.5D", "Lathe", "Multiaxis"]
|
||||
for n in self.propertyEnumerations():
|
||||
setattr(obj, n[0], n[1])
|
||||
|
||||
obj.PostProcessorOutputFile = PathPreferences.defaultOutputFile()
|
||||
obj.PostProcessor = postProcessors = PathPreferences.allEnabledPostProcessors()
|
||||
@@ -236,6 +241,45 @@ class ObjectJob:
|
||||
self.setFromTemplateFile(obj, templateFile)
|
||||
self.setupStock(obj)
|
||||
|
||||
@classmethod
|
||||
def propertyEnumerations(self, dataType="data"):
|
||||
"""propertyEnumerations(dataType="data")... return property enumeration lists of specified dataType.
|
||||
Args:
|
||||
dataType = 'data', 'raw', 'translated'
|
||||
Notes:
|
||||
'data' is list of internal string literals used in code
|
||||
'raw' is list of (translated_text, data_string) tuples
|
||||
'translated' is list of translated string literals
|
||||
"""
|
||||
|
||||
enums = {
|
||||
"OrderOutputBy": [
|
||||
(translate("Path_Job", "Fixture"), "Fixture"),
|
||||
(translate("Path_Job", "Tool"), "Tool"),
|
||||
(translate("Path_Job", "Operation"), "Operation"),
|
||||
],
|
||||
"JobType": [
|
||||
(translate("Path_Job", "2D"), "2D"),
|
||||
(translate("Path_Job", "2.5D"), "2.5D"),
|
||||
(translate("Path_Job", "Lathe"), "Lathe"),
|
||||
(translate("Path_Job", "Multiaxis"), "Multiaxis"),
|
||||
],
|
||||
}
|
||||
|
||||
if dataType == "raw":
|
||||
return enums
|
||||
|
||||
data = list()
|
||||
idx = 0 if dataType == "translated" else 1
|
||||
|
||||
PathLog.debug(enums)
|
||||
|
||||
for k, v in enumerate(enums):
|
||||
data.append((v, [tup[idx] for tup in enums[v]]))
|
||||
PathLog.debug(data)
|
||||
|
||||
return data
|
||||
|
||||
def setupOperations(self, obj):
|
||||
"""setupOperations(obj)... setup the Operations group for the Job object."""
|
||||
ops = FreeCAD.ActiveDocument.addObject(
|
||||
@@ -255,8 +299,8 @@ class ObjectJob:
|
||||
"App::PropertyLink",
|
||||
"SetupSheet",
|
||||
"Base",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathJob", "SetupSheet holding the settings for this job"
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "SetupSheet holding the settings for this job"
|
||||
),
|
||||
)
|
||||
obj.SetupSheet = PathSetupSheet.Create()
|
||||
@@ -278,8 +322,8 @@ class ObjectJob:
|
||||
"App::PropertyLink",
|
||||
"Model",
|
||||
"Base",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathJob", "The base objects for all operations"
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "The base objects for all operations"
|
||||
),
|
||||
)
|
||||
addModels = True
|
||||
@@ -314,8 +358,8 @@ class ObjectJob:
|
||||
"App::PropertyLink",
|
||||
"Tools",
|
||||
"Base",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathJob", "Collection of all tool controllers for the job"
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Collection of all tool controllers for the job"
|
||||
),
|
||||
)
|
||||
addTable = True
|
||||
@@ -451,7 +495,7 @@ class ObjectJob:
|
||||
"App::PropertyString",
|
||||
"CycleTime",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathOp", "Operations Cycle Time Estimation"),
|
||||
QT_TRANSLATE_NOOP("App::Property", "Operations Cycle Time Estimation"),
|
||||
)
|
||||
obj.setEditorMode("CycleTime", 1) # read-only
|
||||
|
||||
@@ -460,8 +504,8 @@ class ObjectJob:
|
||||
"App::PropertyStringList",
|
||||
"Fixtures",
|
||||
"WCS",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathJob", "The Work Coordinate Systems for the Job"
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "The Work Coordinate Systems for the Job"
|
||||
),
|
||||
)
|
||||
obj.Fixtures = ["G54"]
|
||||
@@ -471,8 +515,8 @@ class ObjectJob:
|
||||
"App::PropertyEnumeration",
|
||||
"OrderOutputBy",
|
||||
"WCS",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathJob", "If multiple WCS, order the output this way"
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "If multiple WCS, order the output this way"
|
||||
),
|
||||
)
|
||||
obj.OrderOutputBy = ["Fixture", "Tool", "Operation"]
|
||||
@@ -482,8 +526,8 @@ class ObjectJob:
|
||||
"App::PropertyBool",
|
||||
"SplitOutput",
|
||||
"Output",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathJob", "Split output into multiple gcode files"
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Split output into multiple gcode files"
|
||||
),
|
||||
)
|
||||
obj.SplitOutput = False
|
||||
@@ -493,11 +537,12 @@ class ObjectJob:
|
||||
"App::PropertyEnumeration",
|
||||
"JobType",
|
||||
"Base",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathJob", "Select the Type of Job"),
|
||||
QT_TRANSLATE_NOOP("App::Property", "Select the Type of Job"),
|
||||
)
|
||||
obj.setEditorMode("JobType", 2) # Hide
|
||||
|
||||
obj.JobType = ["2D", "2.5D", "Lathe", "Multiaxis"]
|
||||
for n in self.propertyEnumerations():
|
||||
setattr(obj, n[0], n[1])
|
||||
|
||||
def onChanged(self, obj, prop):
|
||||
if prop == "PostProcessor" and obj.PostProcessor:
|
||||
@@ -577,9 +622,11 @@ class ObjectJob:
|
||||
obj.Tools.Group = tcs
|
||||
else:
|
||||
PathLog.error(
|
||||
translate("PathJob", "Unsupported PathJob template version %s")
|
||||
% attrs.get(JobTemplate.Version)
|
||||
"Unsupported PathJob template version {}".format(
|
||||
attrs.get(JobTemplate.Version)
|
||||
)
|
||||
)
|
||||
|
||||
if not tcs:
|
||||
self.addToolController(PathToolController.Create())
|
||||
|
||||
@@ -733,9 +780,7 @@ class ObjectJob:
|
||||
suffix = job.Name[3:]
|
||||
|
||||
def errorMessage(grp, job):
|
||||
PathLog.error(
|
||||
translate("PathJobGui", "{} corrupt in {} job.".format(grp, job.Name))
|
||||
)
|
||||
PathLog.error("{} corrupt in {} job.".format(grp, job.Name))
|
||||
|
||||
if not job.Operations:
|
||||
self.setupOperations(job)
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import PathScripts.PathJob as PathJob
|
||||
@@ -31,33 +33,35 @@ import PathScripts.PathUtil as PathUtil
|
||||
import json
|
||||
import os
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
# PathLog.trackModule(PathLog.thisModule())
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
class CommandJobCreate:
|
||||
'''
|
||||
"""
|
||||
Command used to create a command.
|
||||
When activated the command opens a dialog allowing the user to select a base object (has to be a solid)
|
||||
and a template to be used for the initial creation.
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': 'Path_Job',
|
||||
'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Job", "Job"),
|
||||
'Accel': "P, J",
|
||||
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Job", "Creates a Path Job object")}
|
||||
return {
|
||||
"Pixmap": "Path_Job",
|
||||
"MenuText": QT_TRANSLATE_NOOP("Path_Job", "Job"),
|
||||
"Accel": "P, J",
|
||||
"ToolTip": QT_TRANSLATE_NOOP(
|
||||
"Path_Job", "Creates a Path Job object"
|
||||
),
|
||||
}
|
||||
|
||||
def IsActive(self):
|
||||
return FreeCAD.ActiveDocument is not None
|
||||
@@ -74,29 +78,35 @@ class CommandJobCreate:
|
||||
|
||||
@classmethod
|
||||
def Execute(cls, base, template):
|
||||
FreeCADGui.addModule('PathScripts.PathJobGui')
|
||||
FreeCADGui.addModule("PathScripts.PathJobGui")
|
||||
if template:
|
||||
template = "'%s'" % template
|
||||
else:
|
||||
template = 'None'
|
||||
FreeCADGui.doCommand('PathScripts.PathJobGui.Create(%s, %s)' % ([o.Name for o in base], template))
|
||||
template = "None"
|
||||
FreeCADGui.doCommand(
|
||||
"PathScripts.PathJobGui.Create(%s, %s)" % ([o.Name for o in base], template)
|
||||
)
|
||||
|
||||
|
||||
class CommandJobTemplateExport:
|
||||
'''
|
||||
"""
|
||||
Command to export a template of a given job.
|
||||
Opens a dialog to select the file to store the template in. If the template is stored in Path's
|
||||
file path (see preferences) and named in accordance with job_*.json it will automatically be found
|
||||
on Job creation and be available for selection.
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': 'Path_ExportTemplate',
|
||||
'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Job", "Export Template"),
|
||||
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Job", "Exports Path Job as a template to be used for other jobs")}
|
||||
return {
|
||||
"Pixmap": "Path_ExportTemplate",
|
||||
"MenuText": QT_TRANSLATE_NOOP("Path_ExportTemplate", "Export Template"),
|
||||
"ToolTip": QT_TRANSLATE_NOOP(
|
||||
"Path_ExportTemplate", "Exports Path Job as a template to be used for other jobs"
|
||||
),
|
||||
}
|
||||
|
||||
def GetJob(self):
|
||||
# if there's only one Job in the document ...
|
||||
@@ -109,7 +119,7 @@ class CommandJobTemplateExport:
|
||||
sel = FreeCADGui.Selection.getSelection()
|
||||
if len(sel) == 1:
|
||||
job = sel[0]
|
||||
if hasattr(job, 'Proxy') and isinstance(job.Proxy, PathJob.ObjectJob):
|
||||
if hasattr(job, "Proxy") and isinstance(job.Proxy, PathJob.ObjectJob):
|
||||
return job
|
||||
return None
|
||||
|
||||
@@ -124,15 +134,17 @@ class CommandJobTemplateExport:
|
||||
|
||||
@classmethod
|
||||
def SaveDialog(cls, job, dialog):
|
||||
foo = QtGui.QFileDialog.getSaveFileName(QtGui.QApplication.activeWindow(),
|
||||
"Path - Job Template",
|
||||
PathPreferences.filePath(),
|
||||
"job_*.json")[0]
|
||||
foo = QtGui.QFileDialog.getSaveFileName(
|
||||
QtGui.QApplication.activeWindow(),
|
||||
"Path - Job Template",
|
||||
PathPreferences.filePath(),
|
||||
"job_*.json",
|
||||
)[0]
|
||||
if foo:
|
||||
if not os.path.basename(foo).startswith('job_'):
|
||||
foo = os.path.join(os.path.dirname(foo), 'job_' + os.path.basename(foo))
|
||||
if not foo.endswith('.json'):
|
||||
foo = foo + '.json'
|
||||
if not os.path.basename(foo).startswith("job_"):
|
||||
foo = os.path.join(os.path.dirname(foo), "job_" + os.path.basename(foo))
|
||||
if not foo.endswith(".json"):
|
||||
foo = foo + ".json"
|
||||
cls.Execute(job, foo, dialog)
|
||||
|
||||
@classmethod
|
||||
@@ -155,7 +167,11 @@ class CommandJobTemplateExport:
|
||||
stockAttrs = None
|
||||
if dialog:
|
||||
if dialog.includeStock():
|
||||
stockAttrs = PathStock.TemplateAttributes(job.Stock, dialog.includeStockExtent(), dialog.includeStockPlacement())
|
||||
stockAttrs = PathStock.TemplateAttributes(
|
||||
job.Stock,
|
||||
dialog.includeStockExtent(),
|
||||
dialog.includeStockPlacement(),
|
||||
)
|
||||
else:
|
||||
stockAttrs = PathStock.TemplateAttributes(job.Stock)
|
||||
if stockAttrs:
|
||||
@@ -169,7 +185,8 @@ class CommandJobTemplateExport:
|
||||
dialog.includeSettingCoolant(),
|
||||
dialog.includeSettingOperationHeights(),
|
||||
dialog.includeSettingOperationDepths(),
|
||||
dialog.includeSettingOpsSettings())
|
||||
dialog.includeSettingOpsSettings(),
|
||||
)
|
||||
else:
|
||||
setupSheetAttrs = job.Proxy.setupSheet.templateAttributes(True, True, True)
|
||||
if setupSheetAttrs:
|
||||
@@ -177,13 +194,13 @@ class CommandJobTemplateExport:
|
||||
|
||||
encoded = job.Proxy.setupSheet.encodeTemplateAttributes(attrs)
|
||||
# write template
|
||||
with open(PathUtil.toUnicode(path), 'w') as fp:
|
||||
with open(PathUtil.toUnicode(path), "w") as fp:
|
||||
json.dump(encoded, fp, sort_keys=True, indent=2)
|
||||
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
# register the FreeCAD command
|
||||
FreeCADGui.addCommand('Path_Job', CommandJobCreate())
|
||||
FreeCADGui.addCommand('Path_ExportTemplate', CommandJobTemplateExport())
|
||||
FreeCADGui.addCommand("Path_Job", CommandJobCreate())
|
||||
FreeCADGui.addCommand("Path_ExportTemplate", CommandJobTemplateExport())
|
||||
|
||||
FreeCAD.Console.PrintLog("Loading PathJobCmd... done\n")
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
from collections import Counter
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import PathScripts.PathJob as PathJob
|
||||
import PathScripts.PathLog as PathLog
|
||||
@@ -29,21 +32,17 @@ import PathScripts.PathUtil as PathUtil
|
||||
import glob
|
||||
import os
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
from collections import Counter
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
# PathLog.trackModule(PathLog.thisModule())
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
class _ItemDelegate(QtGui.QStyledItemDelegate):
|
||||
|
||||
def __init__(self, controller, parent):
|
||||
self.controller = controller
|
||||
QtGui.QStyledItemDelegate.__init__(self, parent)
|
||||
@@ -54,15 +53,16 @@ class _ItemDelegate(QtGui.QStyledItemDelegate):
|
||||
self.controller.setupColumnEditor(index, editor)
|
||||
return editor
|
||||
|
||||
|
||||
class JobCreate:
|
||||
DataObject = QtCore.Qt.ItemDataRole.UserRole
|
||||
|
||||
def __init__(self, parent=None, sel=None):
|
||||
# pylint: disable=unused-argument
|
||||
self.dialog = FreeCADGui.PySideUic.loadUi(":/panels/DlgJobCreate.ui")
|
||||
self.itemsSolid = QtGui.QStandardItem(translate('PathJob', 'Solids'))
|
||||
self.items2D = QtGui.QStandardItem(translate('PathJob', '2D'))
|
||||
self.itemsJob = QtGui.QStandardItem(translate('PathJob', 'Jobs'))
|
||||
self.itemsSolid = QtGui.QStandardItem(translate("Path_Job", "Solids"))
|
||||
self.items2D = QtGui.QStandardItem(translate("Path_Job", "2D"))
|
||||
self.itemsJob = QtGui.QStandardItem(translate("Path_Job", "Jobs"))
|
||||
self.dialog.templateGroup.hide()
|
||||
self.dialog.modelGroup.hide()
|
||||
# debugging support
|
||||
@@ -74,27 +74,40 @@ class JobCreate:
|
||||
def setupTitle(self, title):
|
||||
self.dialog.setWindowTitle(title)
|
||||
|
||||
def setupModel(self, job = None):
|
||||
def setupModel(self, job=None):
|
||||
|
||||
if job:
|
||||
preSelected = Counter([PathUtil.getPublicObject(job.Proxy.baseObject(job, obj)).Label for obj in job.Model.Group])
|
||||
preSelected = Counter(
|
||||
[
|
||||
PathUtil.getPublicObject(job.Proxy.baseObject(job, obj)).Label
|
||||
for obj in job.Model.Group
|
||||
]
|
||||
)
|
||||
jobResources = job.Model.Group + [job.Stock]
|
||||
else:
|
||||
preSelected = Counter([obj.Label for obj in FreeCADGui.Selection.getSelection()])
|
||||
preSelected = Counter(
|
||||
[obj.Label for obj in FreeCADGui.Selection.getSelection()]
|
||||
)
|
||||
jobResources = []
|
||||
|
||||
self.candidates = sorted(PathJob.ObjectJob.baseCandidates(), key=lambda o: o.Label)
|
||||
self.candidates = sorted(
|
||||
PathJob.ObjectJob.baseCandidates(), key=lambda o: o.Label
|
||||
)
|
||||
|
||||
# If there is only one possibility we might as well make sure it's selected
|
||||
if not preSelected and 1 == len(self.candidates):
|
||||
preSelected = Counter([self.candidates[0].Label])
|
||||
|
||||
expandSolids = False
|
||||
expand2Ds = False
|
||||
expandJobs = False
|
||||
expand2Ds = False
|
||||
expandJobs = False
|
||||
|
||||
for base in self.candidates:
|
||||
if not base in jobResources and not PathJob.isResourceClone(job, base, None) and not hasattr(base, 'StockType'):
|
||||
if (
|
||||
not base in jobResources
|
||||
and not PathJob.isResourceClone(job, base, None)
|
||||
and not hasattr(base, "StockType")
|
||||
):
|
||||
item0 = QtGui.QStandardItem()
|
||||
item1 = QtGui.QStandardItem()
|
||||
|
||||
@@ -149,7 +162,7 @@ class JobCreate:
|
||||
|
||||
self.delegate = _ItemDelegate(self, self.dialog.modelTree)
|
||||
self.model = QtGui.QStandardItemModel(self.dialog)
|
||||
self.model.setHorizontalHeaderLabels(['Model', 'Count'])
|
||||
self.model.setHorizontalHeaderLabels(["Model", "Count"])
|
||||
|
||||
if self.itemsSolid.hasChildren():
|
||||
self.model.appendRow(self.itemsSolid)
|
||||
@@ -213,7 +226,9 @@ class JobCreate:
|
||||
def setupTemplate(self):
|
||||
templateFiles = []
|
||||
for path in PathPreferences.searchPaths():
|
||||
cleanPaths = [f.replace("\\", "/") for f in self.templateFilesIn(path)] # Standardize slashes used across os platforms
|
||||
cleanPaths = [
|
||||
f.replace("\\", "/") for f in self.templateFilesIn(path)
|
||||
] # Standardize slashes used across os platforms
|
||||
templateFiles.extend(cleanPaths)
|
||||
|
||||
template = {}
|
||||
@@ -229,7 +244,7 @@ class JobCreate:
|
||||
template[name] = tFile
|
||||
selectTemplate = PathPreferences.defaultJobTemplate()
|
||||
index = 0
|
||||
self.dialog.jobTemplate.addItem('<none>', '')
|
||||
self.dialog.jobTemplate.addItem("<none>", "")
|
||||
for name in sorted(template.keys()):
|
||||
if template[name] == selectTemplate:
|
||||
index = self.dialog.jobTemplate.count()
|
||||
@@ -238,16 +253,18 @@ class JobCreate:
|
||||
self.dialog.templateGroup.show()
|
||||
|
||||
def templateFilesIn(self, path):
|
||||
'''templateFilesIn(path) ... answer all file in the given directory which fit the job template naming convention.
|
||||
PathJob template files are name job_*.json'''
|
||||
"""templateFilesIn(path) ... answer all file in the given directory which fit the job template naming convention.
|
||||
PathJob template files are name job_*.json"""
|
||||
PathLog.track(path)
|
||||
return glob.glob(path + '/job_*.json')
|
||||
return glob.glob(path + "/job_*.json")
|
||||
|
||||
def getModels(self):
|
||||
models = []
|
||||
|
||||
for i in range(self.itemsSolid.rowCount()):
|
||||
for j in range(self.itemsSolid.child(i, 1).data(QtCore.Qt.EditRole)): # pylint: disable=unused-variable
|
||||
for j in range(
|
||||
self.itemsSolid.child(i, 1).data(QtCore.Qt.EditRole)
|
||||
): # pylint: disable=unused-variable
|
||||
models.append(self.itemsSolid.child(i).data(self.DataObject))
|
||||
|
||||
for i in range(self.items2D.rowCount()):
|
||||
@@ -259,12 +276,14 @@ class JobCreate:
|
||||
# Note that we do want to use the models (resource clones) of the
|
||||
# source job as base objects for the new job in order to get the
|
||||
# identical placement, and anything else that's been customized.
|
||||
models.extend(self.itemsJob.child(i, 0).data(self.DataObject).Model.Group)
|
||||
models.extend(
|
||||
self.itemsJob.child(i, 0).data(self.DataObject).Model.Group
|
||||
)
|
||||
|
||||
return models
|
||||
|
||||
def getTemplate(self):
|
||||
'''answer the file name of the template to be assigned'''
|
||||
"""answer the file name of the template to be assigned"""
|
||||
return self.dialog.jobTemplate.itemData(self.dialog.jobTemplate.currentIndex())
|
||||
|
||||
def exec_(self):
|
||||
@@ -298,24 +317,44 @@ class JobTemplateExport:
|
||||
def updateUI(self):
|
||||
job = self.job
|
||||
if job.PostProcessor:
|
||||
ppHint = "%s %s %s" % (job.PostProcessor, job.PostProcessorArgs, job.PostProcessorOutputFile)
|
||||
ppHint = "%s %s %s" % (
|
||||
job.PostProcessor,
|
||||
job.PostProcessorArgs,
|
||||
job.PostProcessorOutputFile,
|
||||
)
|
||||
self.dialog.postProcessingHint.setText(ppHint)
|
||||
else:
|
||||
self.dialog.postProcessingGroup.setEnabled(False)
|
||||
self.dialog.postProcessingGroup.setChecked(False)
|
||||
|
||||
if job.Stock and not PathJob.isResourceClone(job, 'Stock', 'Stock'):
|
||||
if job.Stock and not PathJob.isResourceClone(job, "Stock", "Stock"):
|
||||
stockType = PathStock.StockType.FromStock(job.Stock)
|
||||
if stockType == PathStock.StockType.FromBase:
|
||||
seHint = translate('PathJob', "Base -/+ %.2f/%.2f %.2f/%.2f %.2f/%.2f") % (job.Stock.ExtXneg, job.Stock.ExtXpos, job.Stock.ExtYneg, job.Stock.ExtYpos, job.Stock.ExtZneg, job.Stock.ExtZpos)
|
||||
seHint = translate(
|
||||
"Path_Job", "Base -/+ %.2f/%.2f %.2f/%.2f %.2f/%.2f"
|
||||
) % (
|
||||
job.Stock.ExtXneg,
|
||||
job.Stock.ExtXpos,
|
||||
job.Stock.ExtYneg,
|
||||
job.Stock.ExtYpos,
|
||||
job.Stock.ExtZneg,
|
||||
job.Stock.ExtZpos,
|
||||
)
|
||||
self.dialog.stockPlacement.setChecked(False)
|
||||
elif stockType == PathStock.StockType.CreateBox:
|
||||
seHint = translate('PathJob', "Box: %.2f x %.2f x %.2f") % (job.Stock.Length, job.Stock.Width, job.Stock.Height)
|
||||
seHint = translate("Path_Job", "Box: %.2f x %.2f x %.2f") % (
|
||||
job.Stock.Length,
|
||||
job.Stock.Width,
|
||||
job.Stock.Height,
|
||||
)
|
||||
elif stockType == PathStock.StockType.CreateCylinder:
|
||||
seHint = translate('PathJob', "Cylinder: %.2f x %.2f") % (job.Stock.Radius, job.Stock.Height)
|
||||
seHint = translate("Path_Job", "Cylinder: %.2f x %.2f") % (
|
||||
job.Stock.Radius,
|
||||
job.Stock.Height,
|
||||
)
|
||||
else:
|
||||
seHint = '-'
|
||||
PathLog.error(translate('PathJob', 'Unsupported stock type'))
|
||||
seHint = "-"
|
||||
PathLog.error(translate("Path_Job", "Unsupported stock type"))
|
||||
self.dialog.stockExtentHint.setText(seHint)
|
||||
spHint = "%s" % job.Stock.Placement
|
||||
self.dialog.stockPlacementHint.setText(spHint)
|
||||
@@ -325,7 +364,13 @@ class JobTemplateExport:
|
||||
heightsChanged = not job.SetupSheet.Proxy.hasDefaultOperationHeights()
|
||||
coolantChanged = not job.SetupSheet.Proxy.hasDefaultCoolantMode()
|
||||
opsWithSettings = job.SetupSheet.Proxy.operationsWithSettings()
|
||||
settingsChanged = rapidChanged or depthsChanged or heightsChanged or coolantChanged or 0 != len(opsWithSettings)
|
||||
settingsChanged = (
|
||||
rapidChanged
|
||||
or depthsChanged
|
||||
or heightsChanged
|
||||
or coolantChanged
|
||||
or 0 != len(opsWithSettings)
|
||||
)
|
||||
self.dialog.settingsGroup.setChecked(settingsChanged)
|
||||
self.dialog.settingToolRapid.setChecked(rapidChanged)
|
||||
self.dialog.settingOperationDepths.setChecked(depthsChanged)
|
||||
@@ -346,7 +391,11 @@ class JobTemplateExport:
|
||||
self.dialog.toolsList.addItem(item)
|
||||
|
||||
def checkUncheckTools(self):
|
||||
state = QtCore.Qt.CheckState.Checked if self.dialog.toolsGroup.isChecked() else QtCore.Qt.CheckState.Unchecked
|
||||
state = (
|
||||
QtCore.Qt.CheckState.Checked
|
||||
if self.dialog.toolsGroup.isChecked()
|
||||
else QtCore.Qt.CheckState.Unchecked
|
||||
)
|
||||
for i in range(self.dialog.toolsList.count()):
|
||||
self.dialog.toolsList.item(i).setCheckState(state)
|
||||
|
||||
|
||||
@@ -25,27 +25,25 @@ from PySide import QtCore, QtGui
|
||||
from collections import Counter
|
||||
from contextlib import contextmanager
|
||||
from pivy import coin
|
||||
import json
|
||||
import math
|
||||
import traceback
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
|
||||
import PathScripts.PathGeom as PathGeom
|
||||
import PathScripts.PathGuiInit as PathGuiInit
|
||||
import PathScripts.PathJob as PathJob
|
||||
import PathScripts.PathJobCmd as PathJobCmd
|
||||
import PathScripts.PathJobDlg as PathJobDlg
|
||||
import PathScripts.PathGeom as PathGeom
|
||||
import PathScripts.PathGuiInit as PathGuiInit
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
import PathScripts.PathSetupSheetGui as PathSetupSheetGui
|
||||
import PathScripts.PathStock as PathStock
|
||||
import PathScripts.PathToolBitGui as PathToolBitGui
|
||||
import PathScripts.PathToolControllerGui as PathToolControllerGui
|
||||
import PathScripts.PathToolLibraryEditor as PathToolLibraryEditor
|
||||
import PathScripts.PathUtil as PathUtil
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
import PathScripts.PathToolBitGui as PathToolBitGui
|
||||
import json
|
||||
import math
|
||||
import traceback
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
@@ -54,14 +52,13 @@ Draft = LazyLoader("Draft", globals(), "Draft")
|
||||
Part = LazyLoader("Part", globals(), "Part")
|
||||
DraftVecUtils = LazyLoader("DraftVecUtils", globals(), "DraftVecUtils")
|
||||
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
# PathLog.trackModule(PathLog.thisModule())
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
def _OpenCloseResourceEditor(obj, vobj, edit):
|
||||
@@ -282,7 +279,7 @@ class ViewProvider:
|
||||
PathLog.track()
|
||||
for action in menu.actions():
|
||||
menu.removeAction(action)
|
||||
action = QtGui.QAction(translate("Path", "Edit"), menu)
|
||||
action = QtGui.QAction(translate("Path_Job", "Edit"), menu)
|
||||
action.triggered.connect(self.setEdit)
|
||||
menu.addAction(action)
|
||||
|
||||
@@ -387,7 +384,7 @@ class StockFromBaseBoundBoxEdit(StockEdit):
|
||||
if self.IsStock(obj):
|
||||
self.getFieldsStock(obj.Stock, fields)
|
||||
else:
|
||||
PathLog.error(translate("PathJob", "Stock not from Base bound box!"))
|
||||
PathLog.error("Stock not from Base bound box!")
|
||||
|
||||
def setFields(self, obj):
|
||||
PathLog.track()
|
||||
@@ -479,7 +476,7 @@ class StockCreateBoxEdit(StockEdit):
|
||||
self.form.stockBoxHeight.text()
|
||||
)
|
||||
else:
|
||||
PathLog.error(translate("PathJob", "Stock not a box!"))
|
||||
PathLog.error("Stock not a box!")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -525,7 +522,7 @@ class StockCreateCylinderEdit(StockEdit):
|
||||
self.form.stockCylinderHeight.text()
|
||||
)
|
||||
else:
|
||||
PathLog.error(translate("PathJob", "Stock not a cylinder!"))
|
||||
PathLog.error(translate("Path_Job", "Stock not a cylinder!"))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -609,7 +606,7 @@ class TaskPanel:
|
||||
DataProperty = QtCore.Qt.ItemDataRole.UserRole + 1
|
||||
|
||||
def __init__(self, vobj, deleteOnReject):
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("Path_Job", "Edit Job"))
|
||||
FreeCAD.ActiveDocument.openTransaction("Edit Job")
|
||||
self.vobj = vobj
|
||||
self.vproxy = vobj.Proxy
|
||||
self.obj = vobj.Object
|
||||
@@ -644,6 +641,11 @@ class TaskPanel:
|
||||
self.form.postProcessorArguments.toolTip()
|
||||
)
|
||||
|
||||
# Populate the other comboboxes with enums from the job class
|
||||
comboToPropertyMap = [("orderBy", "OrderOutputBy")]
|
||||
enumTups = PathJob.ObjectJob.propertyEnumerations(dataType="raw")
|
||||
self.populateCombobox(self.form, enumTups, comboToPropertyMap)
|
||||
|
||||
self.vproxy.setupEditVisibility(self.obj)
|
||||
|
||||
self.stockFromBase = None
|
||||
@@ -659,6 +661,21 @@ class TaskPanel:
|
||||
self.obj.SetupSheet, self.form
|
||||
)
|
||||
|
||||
def populateCombobox(self, form, enumTups, comboBoxesPropertyMap):
|
||||
"""fillComboboxes(form, comboBoxesPropertyMap) ... populate comboboxes with translated enumerations
|
||||
** comboBoxesPropertyMap will be unnecessary if UI files use strict combobox naming protocol.
|
||||
Args:
|
||||
form = UI form
|
||||
enumTups = list of (translated_text, data_string) tuples
|
||||
comboBoxesPropertyMap = list of (translated_text, data_string) tuples
|
||||
"""
|
||||
# Load appropriate enumerations in each combobox
|
||||
for cb, prop in comboBoxesPropertyMap:
|
||||
box = getattr(form, cb) # Get the combobox
|
||||
box.clear() # clear the combobox
|
||||
for text, data in enumTups[prop]: # load enumerations
|
||||
box.addItem(text, data)
|
||||
|
||||
def preCleanup(self):
|
||||
PathLog.track()
|
||||
FreeCADGui.Selection.removeObserver(self)
|
||||
@@ -682,9 +699,7 @@ class TaskPanel:
|
||||
FreeCAD.ActiveDocument.abortTransaction()
|
||||
if self.deleteOnReject and FreeCAD.ActiveDocument.getObject(self.name):
|
||||
PathLog.info("Uncreate Job")
|
||||
FreeCAD.ActiveDocument.openTransaction(
|
||||
translate("Path_Job", "Uncreate Job")
|
||||
)
|
||||
FreeCAD.ActiveDocument.openTransaction("Uncreate Job")
|
||||
if self.obj.ViewObject.Proxy.onDelete(self.obj.ViewObject, None):
|
||||
FreeCAD.ActiveDocument.removeObject(self.obj.Name)
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
@@ -1257,7 +1272,7 @@ class TaskPanel:
|
||||
setupFromExisting()
|
||||
else:
|
||||
PathLog.error(
|
||||
translate("PathJob", "Unsupported stock object %s")
|
||||
translate("Path_Job", "Unsupported stock object %s")
|
||||
% self.obj.Stock.Label
|
||||
)
|
||||
else:
|
||||
@@ -1273,7 +1288,7 @@ class TaskPanel:
|
||||
index = -1
|
||||
else:
|
||||
PathLog.error(
|
||||
translate("PathJob", "Unsupported stock type %s (%d)")
|
||||
translate("Path_Job", "Unsupported stock type %s (%d)")
|
||||
% (self.form.stock.currentText(), index)
|
||||
)
|
||||
self.stockEdit.activate(self.obj, index == -1)
|
||||
@@ -1562,7 +1577,7 @@ def Create(base, template=None):
|
||||
"""Create(base, template) ... creates a job instance for the given base object
|
||||
using template to configure it."""
|
||||
FreeCADGui.addModule("PathScripts.PathJob")
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("Path_Job", "Create Job"))
|
||||
FreeCAD.ActiveDocument.openTransaction("Create Job")
|
||||
try:
|
||||
obj = PathJob.Create("Job", base, template)
|
||||
obj.ViewObject.Proxy = ViewProvider(obj.ViewObject)
|
||||
|
||||
@@ -20,26 +20,28 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
import FreeCAD
|
||||
from PySide import QtCore, QtGui
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
_dressups = []
|
||||
|
||||
|
||||
def RegisterDressup(dressup):
|
||||
_dressups.append(dressup)
|
||||
|
||||
|
||||
class DressupPreferencesPage:
|
||||
def __init__(self, parent=None):
|
||||
# pylint: disable=unused-argument
|
||||
self.form = QtGui.QToolBox()
|
||||
self.form.setWindowTitle(translate("Path_PreferencesPathDressup", 'Dressups'))
|
||||
self.form.setWindowTitle(translate("Path_PreferencesPathDressup", "Dressups"))
|
||||
pages = []
|
||||
for dressup in _dressups:
|
||||
page = dressup.preferencesPage()
|
||||
if hasattr(page, 'icon') and page.icon:
|
||||
if hasattr(page, "icon") and page.icon:
|
||||
self.form.addItem(page.form, page.icon, page.label)
|
||||
else:
|
||||
self.form.addItem(page.form, page.label)
|
||||
@@ -53,4 +55,3 @@ class DressupPreferencesPage:
|
||||
def loadSettings(self):
|
||||
for page in self.pages:
|
||||
page.loadSettings()
|
||||
|
||||
|
||||
@@ -25,28 +25,31 @@ import FreeCAD
|
||||
import PathScripts.PathOpGui as PathOpGui
|
||||
import PathScripts.PathProfile as PathProfile
|
||||
import PathScripts.PathProfileGui as PathProfileGui
|
||||
from PySide import QtCore
|
||||
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
|
||||
__title__ = "Path Contour Operation UI (depreciated)"
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "Contour operation page controller and command implementation (depreciated)."
|
||||
__doc__ = "Contour operation page controller and command implementation (deprecated)."
|
||||
|
||||
|
||||
class TaskPanelOpPage(PathProfileGui.TaskPanelOpPage):
|
||||
'''Psuedo page controller class for Profile operation,
|
||||
allowing for backward compatibility with pre-existing "Contour" operations.'''
|
||||
"""Psuedo page controller class for Profile operation,
|
||||
allowing for backward compatibility with pre-existing "Contour" operations."""
|
||||
|
||||
pass
|
||||
# Eclass
|
||||
|
||||
|
||||
Command = PathOpGui.SetupOperation('Profile',
|
||||
PathProfile.Create,
|
||||
TaskPanelOpPage,
|
||||
'Path_Contour',
|
||||
QtCore.QT_TRANSLATE_NOOP("Path_Profile", "Profile"),
|
||||
QtCore.QT_TRANSLATE_NOOP("Path_Profile", "Profile entire model, selected face(s) or selected edge(s)"),
|
||||
PathProfile.SetupProperties)
|
||||
Command = PathOpGui.SetupOperation(
|
||||
"Profile",
|
||||
PathProfile.Create,
|
||||
TaskPanelOpPage,
|
||||
"Path_Contour",
|
||||
QT_TRANSLATE_NOOP("Path_Profile", "Profile"),
|
||||
QT_TRANSLATE_NOOP(
|
||||
"Path_Profile", "Profile entire model, selected face(s) or selected edge(s)"
|
||||
),
|
||||
PathProfile.SetupProperties,
|
||||
)
|
||||
|
||||
FreeCAD.Console.PrintLog("Loading PathProfileContourGui... done\n")
|
||||
|
||||
@@ -25,28 +25,33 @@ import FreeCAD
|
||||
import PathScripts.PathOpGui as PathOpGui
|
||||
import PathScripts.PathProfile as PathProfile
|
||||
import PathScripts.PathProfileGui as PathProfileGui
|
||||
from PySide import QtCore
|
||||
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
|
||||
__title__ = "Path Profile Edges Operation UI (depreciated)"
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "Profile Edges operation page controller and command implementation (depreciated)."
|
||||
__doc__ = (
|
||||
"Profile Edges operation page controller and command implementation (deprecated)."
|
||||
)
|
||||
|
||||
|
||||
class TaskPanelOpPage(PathProfileGui.TaskPanelOpPage):
|
||||
'''Psuedo page controller class for Profile operation,
|
||||
allowing for backward compatibility with pre-existing "Profile Edges" operations.'''
|
||||
"""Psuedo page controller class for Profile operation,
|
||||
allowing for backward compatibility with pre-existing "Profile Edges" operations."""
|
||||
|
||||
pass
|
||||
# Eclass
|
||||
|
||||
|
||||
Command = PathOpGui.SetupOperation('Profile',
|
||||
PathProfile.Create,
|
||||
TaskPanelOpPage,
|
||||
'Path_Contour',
|
||||
QtCore.QT_TRANSLATE_NOOP("Path_Profile", "Profile"),
|
||||
QtCore.QT_TRANSLATE_NOOP("Path_Profile", "Profile entire model, selected face(s) or selected edge(s)"),
|
||||
PathProfile.SetupProperties)
|
||||
Command = PathOpGui.SetupOperation(
|
||||
"Profile",
|
||||
PathProfile.Create,
|
||||
TaskPanelOpPage,
|
||||
"Path_Contour",
|
||||
QT_TRANSLATE_NOOP("Path_Profile", "Profile"),
|
||||
QT_TRANSLATE_NOOP(
|
||||
"Path_Profile", "Profile entire model, selected face(s) or selected edge(s)"
|
||||
),
|
||||
PathProfile.SetupProperties,
|
||||
)
|
||||
|
||||
FreeCAD.Console.PrintLog("Loading PathProfileEdgesGui... done\n")
|
||||
|
||||
@@ -25,28 +25,33 @@ import FreeCAD
|
||||
import PathScripts.PathOpGui as PathOpGui
|
||||
import PathScripts.PathProfile as PathProfile
|
||||
import PathScripts.PathProfileGui as PathProfileGui
|
||||
from PySide import QtCore
|
||||
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
|
||||
__title__ = "Path Profile Faces Operation UI (depreciated)"
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "Profile Faces operation page controller and command implementation (depreciated)."
|
||||
__doc__ = (
|
||||
"Profile Faces operation page controller and command implementation (deprecated)."
|
||||
)
|
||||
|
||||
|
||||
class TaskPanelOpPage(PathProfileGui.TaskPanelOpPage):
|
||||
'''Psuedo page controller class for Profile operation,
|
||||
allowing for backward compatibility with pre-existing "Profile Faces" operations.'''
|
||||
"""Psuedo page controller class for Profile operation,
|
||||
allowing for backward compatibility with pre-existing "Profile Faces" operations."""
|
||||
|
||||
pass
|
||||
# Eclass
|
||||
|
||||
|
||||
Command = PathOpGui.SetupOperation('Profile',
|
||||
PathProfile.Create,
|
||||
TaskPanelOpPage,
|
||||
'Path_Contour',
|
||||
QtCore.QT_TRANSLATE_NOOP("Path_Profile", "Profile"),
|
||||
QtCore.QT_TRANSLATE_NOOP("Path_Profile", "Profile entire model, selected face(s) or selected edge(s)"),
|
||||
PathProfile.SetupProperties)
|
||||
Command = PathOpGui.SetupOperation(
|
||||
"Profile",
|
||||
PathProfile.Create,
|
||||
TaskPanelOpPage,
|
||||
"Path_Contour",
|
||||
QT_TRANSLATE_NOOP("Path_Profile", "Profile"),
|
||||
QT_TRANSLATE_NOOP(
|
||||
"Path_Profile", "Profile entire model, selected face(s) or selected edge(s)"
|
||||
),
|
||||
PathProfile.SetupProperties,
|
||||
)
|
||||
|
||||
FreeCAD.Console.PrintLog("Loading PathProfileFacesGui... done\n")
|
||||
|
||||
Reference in New Issue
Block a user