Merge pull request #5436 from sliptonic/bug/translationJob
[PATH] More translation cleanup (job, profile, dressups)
This commit is contained in:
@@ -31,8 +31,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>146</width>
|
||||
<height>378</height>
|
||||
<width>561</width>
|
||||
<height>675</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
@@ -112,8 +112,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>96</width>
|
||||
<height>26</height>
|
||||
<width>98</width>
|
||||
<height>28</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
@@ -205,21 +205,6 @@
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Ordering by Fixture, will cause all operations to be performed in the first coordinate system before switching to the second. Then all operations will be performed there in the same order.</p><p>This is useful if the operator can safely load work into one coordinate system while the machine is doing work in another.</p><p>Ordering by Tool, will minimize the Tool Changes. A tool change will be done, then all operations in all coordinate systems before changing tools.</p><p>Ordering by operation will do each operation in all coordinate systems before moving to the next operation. This is especially useful in conjunction with the 'split output' even with only a single work coordinate system since it will put each operation into a separate file</p></body></html></string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Fixture</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Tool</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Operation</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
@@ -251,7 +236,7 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p><span style=" font-style:italic;">Work Coordinate Systems</span> also called <span style=" font-style:italic;">Work Offsets</span>, <span style=" font-style:italic;">Fixture Offsets</span>, or <span style=" font-style:italic;">Fixtures </span>are useful for building efficient production jobs where the same part is done many times on the machine.</p><p>FreeCAD has no knowledge of where a particular coordinate system exists within the machine coordinate system so adding additional coordinate systems to your job will have no visual change within your job. It will, however, change your gcode output. The exact way in which the output is affected is controlled by the 'order by' setting.</p></body></html></string>
|
||||
<string><html><head/><body><p><span style=" font-style:italic;">Work Coordinate Systems</span> also called <span style=" font-style:italic;">Work Offsets</span>, <span style=" font-style:italic;">Fixture Offsets</span>, or <span style=" font-style:italic;">Fixtures </span>are useful for building efficient production jobs where the same part is done many times on the machine.</p><p>FreeCAD has no knowledge of where a particular coordinate system exists within the machine coordinate system so adding additional coordinate systems to your job will have no visual change within your job. It will, however, change your gcode output. The exact way in which the output is affected is controlled by the 'order by' setting.</p></body></html></string>
|
||||
</property>
|
||||
<property name="resizeMode">
|
||||
<enum>QListView::Adjust</enum>
|
||||
@@ -404,7 +389,7 @@
|
||||
<string><html><head/><body><p>If multiple coordinate systems are in use, setting this to TRUE will cause the gcode to be written to multiple output files as controlled by the 'order by' property. For example, if ordering by Fixture, the first output file will be for the first fixture and separate file for the second.</p><p><br/></p><p><br/></p></body></html></string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string><html><head/><body><p>If True, post-processing will create multiple output files based on the <span style=" font-style:italic;">order by</span> setting.</p><p><br/></p><p>For example, if <span style=" font-style:italic;">order by</span> is set to Tool, the first output file will contain the first tool change and all operations, in all coordinate systems, that can be done with that tool before the next tool change is called.</p><p><br/></p><p>If <span style=" font-style:italic;">order by</span> is set to <span style=" font-style:italic;">operation</span> and <span style=" font-style:italic;">split output</span> is true, each operation will be written to a separate file.</p></body></html></string>
|
||||
<string><html><head/><body><p>If True, post-processing will create multiple output files based on the <span style=" font-style:italic;">order by</span> setting.</p><p><br/></p><p>For example, if <span style=" font-style:italic;">order by</span> is set to Tool, the first output file will contain the first tool change and all operations, in all coordinate systems, that can be done with that tool before the next tool change is called.</p><p><br/></p><p>If <span style=" font-style:italic;">order by</span> is set to <span style=" font-style:italic;">operation</span> and <span style=" font-style:italic;">split output</span> is true, each operation will be written to a separate file.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Split Output</string>
|
||||
@@ -424,15 +409,15 @@
|
||||
<item>
|
||||
<widget class="QToolBox" name="toolBox_2">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
<number>1</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="page_3">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>545</width>
|
||||
<height>896</height>
|
||||
<width>548</width>
|
||||
<height>941</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
@@ -1002,8 +987,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>213</width>
|
||||
<height>321</height>
|
||||
<width>561</width>
|
||||
<height>675</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
@@ -1185,8 +1170,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>317</width>
|
||||
<height>173</height>
|
||||
<width>561</width>
|
||||
<height>675</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
@@ -1285,8 +1270,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>138</width>
|
||||
<height>112</height>
|
||||
<width>151</width>
|
||||
<height>119</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
|
||||
@@ -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