Merge pull request #474 from mlampert/logging
Path: minor fix in dogbones and introduction of PathLog module (with initial use).
This commit is contained in:
@@ -39,6 +39,7 @@ SET(PathScripts_SRCS
|
||||
PathScripts/PathJob.py
|
||||
PathScripts/PathKurveUtils.py
|
||||
PathScripts/PathLoadTool.py
|
||||
PathScripts/PathLog.py
|
||||
PathScripts/PathMillFace.py
|
||||
PathScripts/PathPlane.py
|
||||
PathScripts/PathPocket.py
|
||||
@@ -79,6 +80,7 @@ SET(PathScripts_SRCS
|
||||
PathTests/TestPathDepthParams.py
|
||||
PathTests/TestPathDressupHoldingTags.py
|
||||
PathTests/TestPathGeom.py
|
||||
PathTests/TestPathLog.py
|
||||
PathTests/TestPathPost.py
|
||||
PathTests/__init__.py
|
||||
PathTests/test_linuxcnc_00.ngc
|
||||
|
||||
@@ -21,31 +21,26 @@
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
import DraftGeomUtils
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import Path
|
||||
from PathScripts import PathUtils
|
||||
from PathScripts.PathGeom import *
|
||||
from PySide import QtCore, QtGui
|
||||
import math
|
||||
import Part
|
||||
import DraftGeomUtils
|
||||
import Path
|
||||
import PathScripts.PathLog as PathLog
|
||||
|
||||
from PathScripts import PathUtils
|
||||
from PathScripts.PathGeom import PathGeom
|
||||
from PySide import QtCore, QtGui
|
||||
|
||||
"""Dogbone Dressup object and FreeCAD command"""
|
||||
|
||||
debugDressup = False
|
||||
LOG_MODULE = 'PathDressupDogbone'
|
||||
#PathLog.setLevel(PathLog.Level.INFO, LOG_MODULE)
|
||||
|
||||
# Qt tanslation handling
|
||||
try:
|
||||
_encoding = QtGui.QApplication.UnicodeUTF8
|
||||
|
||||
def translate(context, text, disambig=None):
|
||||
return QtGui.QApplication.translate(context, text, disambig, _encoding)
|
||||
|
||||
except AttributeError:
|
||||
|
||||
def translate(context, text, disambig=None):
|
||||
return QtGui.QApplication.translate(context, text, disambig)
|
||||
def translate(text, context = "PathDressup_Dogbone", disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
movecommands = ['G0', 'G00', 'G1', 'G01', 'G2', 'G02', 'G3', 'G03']
|
||||
movestraight = ['G1', 'G01']
|
||||
@@ -53,12 +48,8 @@ movecw = ['G2', 'G02']
|
||||
moveccw = ['G3', 'G03']
|
||||
movearc = movecw + moveccw
|
||||
|
||||
def debugPrint(msg):
|
||||
if debugDressup:
|
||||
print(msg)
|
||||
|
||||
def debugMarker(vector, label, color = None, radius = 0.5):
|
||||
if debugDressup:
|
||||
if PathLog.getLevel(LOG_MODULE) == PathLog.Level.DEBUG:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::Sphere", label)
|
||||
obj.Label = label
|
||||
obj.Radius = radius
|
||||
@@ -67,7 +58,7 @@ def debugMarker(vector, label, color = None, radius = 0.5):
|
||||
obj.ViewObject.ShapeColor = color
|
||||
|
||||
def debugCircle(vector, r, label, color = None):
|
||||
if debugDressup:
|
||||
if PathLog.getLevel(LOG_MODULE) == PathLog.Level.DEBUG:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::Cylinder", label)
|
||||
obj.Label = label
|
||||
obj.Radius = r
|
||||
@@ -285,6 +276,7 @@ class Bone:
|
||||
self.inChord = inChord
|
||||
self.outChord = outChord
|
||||
self.smooth = smooth
|
||||
self.smooth = Smooth.Neither
|
||||
|
||||
def angle(self):
|
||||
if not hasattr(self, 'cAngle'):
|
||||
@@ -318,9 +310,9 @@ class Bone:
|
||||
# for some reason pi/2 is not equal to pi/2
|
||||
if math.fabs(angle - boneAngle) < 0.00001:
|
||||
# moving directly towards the corner
|
||||
debugPrint("adaptive - on target: %.2f - %.2f" % (distance, toolRadius))
|
||||
PathLog.debug("adaptive - on target: %.2f - %.2f" % (distance, toolRadius))
|
||||
return distance - toolRadius
|
||||
debugPrint("adaptive - angles: corner=%.2f bone=%.2f diff=%.12f" % (angle/math.pi, boneAngle/math.pi, angle - boneAngle))
|
||||
PathLog.debug("adaptive - angles: corner=%.2f bone=%.2f diff=%.12f" % (angle/math.pi, boneAngle/math.pi, angle - 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.
|
||||
@@ -331,7 +323,7 @@ class Bone:
|
||||
beta = math.fabs(addAngle(boneAngle, -angle))
|
||||
D = (distance / toolRadius) * math.sin(beta)
|
||||
if D > 1: # no intersection
|
||||
debugPrint("adaptive - no intersection - no bone")
|
||||
PathLog.debug("adaptive - no intersection - no bone")
|
||||
return 0
|
||||
gamma = math.asin(D)
|
||||
alpha = math.pi - beta - gamma
|
||||
@@ -343,7 +335,7 @@ class Bone:
|
||||
length2 = toolRadius * math.sin(alpha2) / math.sin(beta2)
|
||||
length = min(length, length2)
|
||||
|
||||
debugPrint("adaptive corner=%.2f * %.2f˚ -> bone=%.2f * %.2f˚" % (distance, angle, length, boneAngle))
|
||||
PathLog.debug("adaptive corner=%.2f * %.2f˚ -> bone=%.2f * %.2f˚" % (distance, angle, length, boneAngle))
|
||||
return length
|
||||
|
||||
def edges(self):
|
||||
@@ -390,26 +382,26 @@ class ObjectDressup:
|
||||
return outChord.foldsBackOrTurns(inChord, self.theOtherSideOf(obj.Side))
|
||||
|
||||
def findPivotIntersection(self, pivot, pivotEdge, edge, refPt, d, color):
|
||||
debugPrint("Intersection (%.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):
|
||||
#debugMarker(pt, "pti.%d-%s.in" % (self.boneId, d), color, 0.2)
|
||||
distance = (pt - refPt).Length
|
||||
debugPrint(" --> (%.2f, %.2f): %.2f" % (pt.x, pt.y, distance))
|
||||
PathLog.debug(" --> (%.2f, %.2f): %.2f" % (pt.x, pt.y, distance))
|
||||
if not ppt or pptDistance < distance:
|
||||
ppt = pt
|
||||
pptDistance = distance
|
||||
if not ppt:
|
||||
tangent = DraftGeomUtils.findDistance(pivot, edge)
|
||||
if tangent:
|
||||
debugPrint("Taking tangent as intersect %s" % tangent)
|
||||
PathLog.debug("Taking tangent as intersect %s" % tangent)
|
||||
ppt = pivot + tangent
|
||||
else:
|
||||
debugPrint("Taking chord start as intersect %s" % inChordStart)
|
||||
PathLog.debug("Taking chord start as intersect %s" % inChordStart)
|
||||
ppt = inChord.Start
|
||||
#debugMarker(ppt, "ptt.%d-%s.in" % (self.boneId, d), color, 0.2)
|
||||
debugPrint(" --> (%.2f, %.2f)" % (ppt.x, ppt.y))
|
||||
PathLog.debug(" --> (%.2f, %.2f)" % (ppt.x, ppt.y))
|
||||
return ppt
|
||||
|
||||
def pointIsOnEdge(self, point, edge):
|
||||
@@ -418,7 +410,7 @@ class ObjectDressup:
|
||||
|
||||
def smoothChordCommands(self, bone, inChord, outChord, edge, wire, corner, smooth, color = None):
|
||||
if smooth == 0:
|
||||
debugPrint(" No smoothing requested")
|
||||
PathLog.info(" No smoothing requested")
|
||||
return [ bone.lastCommand, outChord.g1Command() ]
|
||||
|
||||
d = 'in'
|
||||
@@ -428,32 +420,32 @@ class ObjectDressup:
|
||||
refPoint = outChord.End
|
||||
|
||||
if DraftGeomUtils.areColinear(inChord.asEdge(), outChord.asEdge()):
|
||||
debugPrint(" straight edge %s" % d)
|
||||
PathLog.info(" straight edge %s" % d)
|
||||
return [ outChord.g1Command() ]
|
||||
|
||||
pivot = None
|
||||
pivotDistance = 0
|
||||
|
||||
debugPrint("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:
|
||||
debugPrint(" (%.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:
|
||||
debugPrint(" (%.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):
|
||||
debugMarker(pt, "candidate-%d-%s" % (self.boneId, d), color, 0.05)
|
||||
debugPrint(" -> candidate")
|
||||
#debugMarker(pt, "candidate-%d-%s" % (self.boneId, d), color, 0.05)
|
||||
PathLog.debug(" -> candidate")
|
||||
distance = (pt - refPoint).Length
|
||||
if not pivot or pivotDistance > distance:
|
||||
pivot = pt
|
||||
pivotDistance = distance
|
||||
else:
|
||||
debugPrint(" -> corner intersect")
|
||||
PathLog.debug(" -> corner intersect")
|
||||
|
||||
if pivot:
|
||||
debugCircle(pivot, self.toolRadius, "pivot.%d-%s" % (self.boneId, d), color)
|
||||
#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)
|
||||
@@ -461,16 +453,16 @@ class ObjectDressup:
|
||||
|
||||
commands = []
|
||||
if not PathGeom.pointsCoincide(t1, inChord.Start):
|
||||
debugPrint(" add lead in")
|
||||
PathLog.debug(" add lead in")
|
||||
commands.append(Chord(inChord.Start, t1).g1Command())
|
||||
if bone.obj.Side == Side.Left:
|
||||
debugPrint(" add g3 command")
|
||||
PathLog.debug(" add g3 command")
|
||||
commands.append(Chord(t1, t2).g3Command(pivot))
|
||||
else:
|
||||
debugPrint(" 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))
|
||||
if not PathGeom.pointsCoincide(t2, outChord.End):
|
||||
debugPrint(" add lead out")
|
||||
PathLog.debug(" add lead out")
|
||||
commands.append(Chord(t2, outChord.End).g1Command())
|
||||
|
||||
#debugMarker(pivot, "pivot.%d-%s" % (self.boneId, d), color, 0.2)
|
||||
@@ -479,7 +471,7 @@ class ObjectDressup:
|
||||
|
||||
return commands
|
||||
|
||||
debugPrint(" no pivot found - straight command")
|
||||
PathLog.info(" no pivot found - straight command")
|
||||
return [ inChord.g1Command(), outChord.g1Command() ]
|
||||
|
||||
def inOutBoneCommands(self, bone, boneAngle, fixedLength):
|
||||
@@ -487,8 +479,8 @@ class ObjectDressup:
|
||||
|
||||
bone.tip = bone.inChord.End # in case there is no bone
|
||||
|
||||
debugPrint("corner = (%.2f, %.2f)" % (corner.x, corner.y))
|
||||
debugMarker(corner, 'corner', (1., 0., 1.), self.toolRadius)
|
||||
PathLog.debug("corner = (%.2f, %.2f)" % (corner.x, corner.y))
|
||||
#debugMarker(corner, 'corner', (1., 0., 1.), self.toolRadius)
|
||||
|
||||
length = fixedLength
|
||||
if bone.obj.Incision == Incision.Custom:
|
||||
@@ -497,7 +489,7 @@ class ObjectDressup:
|
||||
length = bone.adaptiveLength(boneAngle, self.toolRadius)
|
||||
|
||||
if length == 0:
|
||||
# no bone after all ..
|
||||
PathLog.info("no bone after all ..")
|
||||
return [ bone.lastCommand, bone.outChord.g1Command() ]
|
||||
|
||||
boneInChord = bone.inChord.move(length, boneAngle)
|
||||
@@ -576,7 +568,7 @@ class ObjectDressup:
|
||||
onInString = 'out'
|
||||
if onIn:
|
||||
onInString = 'in'
|
||||
debugPrint("tboneEdge boneAngle[%s]=%.2f (in=%.2f, out=%.2f)" % (onInString, boneAngle/math.pi, bone.inChord.getAngleXY()/math.pi, bone.outChord.getAngleXY()/math.pi))
|
||||
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):
|
||||
@@ -619,21 +611,21 @@ class ObjectDressup:
|
||||
return [ bone.lastCommand, bone.outChord.g1Command() ]
|
||||
|
||||
def insertBone(self, bone):
|
||||
debugPrint(">----------------------------------- %d --------------------------------------" % bone.boneId)
|
||||
PathLog.debug(">----------------------------------- %d --------------------------------------" % bone.boneId)
|
||||
self.boneShapes = []
|
||||
blacklisted, inaccessible = self.boneIsBlacklisted(bone)
|
||||
enabled = not blacklisted
|
||||
self.bones.append((bone.boneId, bone.location(), enabled, inaccessible))
|
||||
|
||||
self.boneId = bone.boneId
|
||||
if debugDressup and bone.boneId > 2:
|
||||
if False and PathLog.getLevel(LOG_MODULE) == PathLog.Level.DEBUG and bone.boneId > 2:
|
||||
commands = self.boneCommands(bone, False)
|
||||
else:
|
||||
commands = self.boneCommands(bone, enabled)
|
||||
bone.commands = commands
|
||||
|
||||
self.shapes[bone.boneId] = self.boneShapes
|
||||
debugPrint("<----------------------------------- %d --------------------------------------" % bone.boneId)
|
||||
PathLog.debug("<----------------------------------- %d --------------------------------------" % bone.boneId)
|
||||
return commands
|
||||
|
||||
def removePathCrossing(self, commands, bone1, bone2):
|
||||
@@ -652,7 +644,7 @@ class ObjectDressup:
|
||||
#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)):
|
||||
continue
|
||||
debugMarker(pt, "it", (0.0, 1.0, 1.0))
|
||||
#debugMarker(pt, "it", (0.0, 1.0, 1.0))
|
||||
# 1. remove all redundant commands
|
||||
commands = commands[:-(len(inEdges) - i)]
|
||||
# 2., correct where c1 ends
|
||||
@@ -702,40 +694,52 @@ class ObjectDressup:
|
||||
boneIserted = False
|
||||
|
||||
for thisCommand in obj.Base.Path.Commands:
|
||||
PathLog.info("Command: %s" % thisCommand)
|
||||
if thisCommand.Name in movecommands:
|
||||
thisChord = lastChord.moveToParameters(thisCommand.Parameters)
|
||||
thisIsACandidate = self.canAttachDogbone(thisCommand, thisChord)
|
||||
|
||||
if thisIsACandidate and lastCommand and self.shouldInsertDogbone(obj, lastChord, thisChord):
|
||||
PathLog.info(" Found bone corner")
|
||||
bone = Bone(boneId, obj, lastCommand, lastChord, thisChord, Smooth.InAndOut)
|
||||
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.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)):
|
||||
if self.shouldInsertDogbone(obj, lastChord, chord):
|
||||
PathLog.info(" and there is one")
|
||||
bone = Bone(boneId, obj, lastCommand, lastChord, chord, Smooth.In)
|
||||
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.extend(bones[:-1])
|
||||
lastCommand = bones[-1]
|
||||
haveNewLastCommand = True
|
||||
if not haveNewLastCommand:
|
||||
commands.append(lastCommand)
|
||||
lastCommand = None
|
||||
commands.append(thisCommand)
|
||||
lastBone = None
|
||||
elif thisIsACandidate:
|
||||
PathLog.info(" is a candidate, keeping for later")
|
||||
if lastCommand:
|
||||
commands.append(lastCommand)
|
||||
lastCommand = thisCommand
|
||||
lastBone = None
|
||||
else:
|
||||
PathLog.info(" nope")
|
||||
if lastCommand:
|
||||
commands.append(lastCommand)
|
||||
lastCommand = None
|
||||
@@ -743,21 +747,24 @@ class ObjectDressup:
|
||||
lastBone = None
|
||||
|
||||
if lastChord.isAPlungeMove() and thisIsACandidate:
|
||||
PathLog.info(" adding to odds and ends")
|
||||
oddsAndEnds.append(thisChord)
|
||||
|
||||
lastChord = thisChord
|
||||
else:
|
||||
PathLog.info(" Clean slate")
|
||||
if lastCommand:
|
||||
commands.append(lastCommand)
|
||||
lastCommand = None
|
||||
commands.append(thisCommand)
|
||||
lastBone = None
|
||||
#for cmd in commands:
|
||||
# debugPrint("cmd = '%s'" % cmd)
|
||||
# PathLog.debug("cmd = '%s'" % cmd)
|
||||
path = Path.Path(commands)
|
||||
obj.Path = path
|
||||
|
||||
def setup(self, obj):
|
||||
debugPrint("Here we go ... ")
|
||||
PathLog.info("Here we go ... ")
|
||||
if hasattr(obj.Base, "BoneBlacklist"):
|
||||
# dressing up a bone dressup
|
||||
obj.Side = obj.Base.Side
|
||||
@@ -861,18 +868,18 @@ class TaskPanel:
|
||||
self.form.customLabel.setEnabled(customSelected)
|
||||
self.updateBoneList()
|
||||
|
||||
if debugDressup:
|
||||
if PathLog.getLevel(LOG_MODULE) == PathLog.Level.DEBUG:
|
||||
for obj in FreeCAD.ActiveDocument.Objects:
|
||||
if obj.Name.startswith('Shape'):
|
||||
FreeCAD.ActiveDocument.removeObject(obj.Name)
|
||||
print('object name %s' % self.obj.Name)
|
||||
if hasattr(self.obj.Proxy, "shapes"):
|
||||
debugPrint("showing shapes attribute")
|
||||
PathLog.info("showing shapes attribute")
|
||||
for shapes in self.obj.Proxy.shapes.itervalues():
|
||||
for shape in shapes:
|
||||
Part.show(shape)
|
||||
else:
|
||||
debugPrint("no shapes attribute found")
|
||||
PathLog.info("no shapes attribute found")
|
||||
|
||||
|
||||
def updateModel(self):
|
||||
|
||||
@@ -27,18 +27,16 @@ import Draft
|
||||
import DraftGeomUtils
|
||||
import DraftGui
|
||||
import Path
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathPreferencesPathDressup as PathPreferencesPathDressup
|
||||
import Part
|
||||
import copy
|
||||
import math
|
||||
|
||||
import cProfile
|
||||
import time
|
||||
|
||||
from DraftGui import todo
|
||||
from PathScripts import PathUtils
|
||||
from PathScripts.PathGeom import *
|
||||
from PathScripts.PathPreferences import *
|
||||
from PathScripts.PathGeom import PathGeom
|
||||
from PathScripts.PathPreferences import PathPreferences
|
||||
from PySide import QtCore, QtGui
|
||||
from pivy import coin
|
||||
|
||||
@@ -48,16 +46,14 @@ from pivy import coin
|
||||
def translate(text, context = "PathDressup_HoldingTags", disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
debugDressup = False
|
||||
|
||||
def debugPrint(msg):
|
||||
if debugDressup:
|
||||
print(msg)
|
||||
LOG_MODULE = 'PathDressupHoldingTags'
|
||||
#PathLog.setLevel(PathLog.Level.DEBUG, LOG_MODULE)
|
||||
|
||||
def debugEdge(edge, prefix, force = False):
|
||||
pf = edge.valueAt(edge.FirstParameter)
|
||||
pl = edge.valueAt(edge.LastParameter)
|
||||
if force or debugDressup:
|
||||
if force or PathLog.getLevel(LOG_MODULE) == PathLog.Level.DEBUG:
|
||||
pf = edge.valueAt(edge.FirstParameter)
|
||||
pl = edge.valueAt(edge.LastParameter)
|
||||
if type(edge.Curve) == Part.Line or type(edge.Curve) == Part.LineSegment:
|
||||
print("%s %s((%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f))" % (prefix, type(edge.Curve), pf.x, pf.y, pf.z, pl.x, pl.y, pl.z))
|
||||
else:
|
||||
@@ -65,7 +61,7 @@ def debugEdge(edge, prefix, force = False):
|
||||
print("%s %s((%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f))" % (prefix, type(edge.Curve), pf.x, pf.y, pf.z, pm.x, pm.y, pm.z, pl.x, pl.y, pl.z))
|
||||
|
||||
def debugMarker(vector, label, color = None, radius = 0.5):
|
||||
if debugDressup:
|
||||
if PathLog.getLevel(LOG_MODULE) == PathLog.Level.DEBUG:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::Sphere", label)
|
||||
obj.Label = label
|
||||
obj.Radius = radius
|
||||
@@ -74,7 +70,7 @@ def debugMarker(vector, label, color = None, radius = 0.5):
|
||||
obj.ViewObject.ShapeColor = color
|
||||
|
||||
def debugCylinder(vector, r, height, label, color = None):
|
||||
if debugDressup:
|
||||
if PathLog.getLevel(LOG_MODULE) == PathLog.Level.DEBUG:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::Cylinder", label)
|
||||
obj.Label = label
|
||||
obj.Radius = r
|
||||
@@ -85,7 +81,7 @@ def debugCylinder(vector, r, height, label, color = None):
|
||||
obj.ViewObject.ShapeColor = color
|
||||
|
||||
def debugCone(vector, r1, r2, height, label, color = None):
|
||||
if debugDressup:
|
||||
if PathLog.getLevel(LOG_MODULE) == PathLog.Level.DEBUG:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::Cone", label)
|
||||
obj.Label = label
|
||||
obj.Radius1 = r1
|
||||
@@ -162,7 +158,7 @@ class HoldingTagsPreferences:
|
||||
|
||||
class Tag:
|
||||
def __init__(self, x, y, width, height, angle, radius, enabled=True):
|
||||
debugPrint("Tag(%.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d)" % (x, y, width, height, angle, radius, enabled))
|
||||
PathLog.track("%.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d" % (x, y, width, height, angle, radius, enabled))
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.width = math.fabs(width)
|
||||
@@ -198,7 +194,7 @@ class Tag:
|
||||
self.isSquare = True
|
||||
self.solid = Part.makeCylinder(r1, height)
|
||||
radius = min(min(self.radius, r1), self.height)
|
||||
debugPrint("Part.makeCone(%f, %f)" % (r1, height))
|
||||
PathLog.debug("Part.makeCone(%f, %f)" % (r1, height))
|
||||
elif self.angle > 0.0 and height > 0.0:
|
||||
# cone
|
||||
rad = math.radians(self.angle)
|
||||
@@ -215,29 +211,29 @@ class Tag:
|
||||
height = r1 * tangens * 1.01
|
||||
self.actualHeight = height
|
||||
self.r2 = r2
|
||||
debugPrint("Part.makeCone(%f, %f, %f)" % (r1, r2, height))
|
||||
PathLog.debug("Part.makeCone(%f, %f, %f)" % (r1, r2, height))
|
||||
self.solid = Part.makeCone(r1, r2, height)
|
||||
else:
|
||||
# degenerated case - no tag
|
||||
debugPrint("Part.makeSphere(%f / 10000)" % (r1))
|
||||
PathLog.debug("Part.makeSphere(%f / 10000)" % (r1))
|
||||
self.solid = Part.makeSphere(r1 / 10000)
|
||||
if not R == 0: # testing is easier if the solid is not rotated
|
||||
angle = -PathGeom.getAngle(self.originAt(0)) * 180 / math.pi
|
||||
debugPrint("solid.rotate(%f)" % angle)
|
||||
PathLog.debug("solid.rotate(%f)" % angle)
|
||||
self.solid.rotate(FreeCAD.Vector(0,0,0), FreeCAD.Vector(0,0,1), angle)
|
||||
debugPrint("solid.translate(%s)" % self.originAt(z))
|
||||
PathLog.debug("solid.translate(%s)" % self.originAt(z))
|
||||
self.solid.translate(self.originAt(z - 0.01 * self.actualHeight))
|
||||
self.realRadius = radius
|
||||
if radius != 0:
|
||||
debugPrint("makeFillet(%.4f)" % radius)
|
||||
PathLog.debug("makeFillet(%.4f)" % radius)
|
||||
self.solid = self.solid.makeFillet(radius, [self.solid.Edges[0]])
|
||||
|
||||
def filterIntersections(self, pts, face):
|
||||
if type(face.Surface) == Part.Cone or type(face.Surface) == Part.Cylinder or type(face.Surface) == Part.Toroid:
|
||||
#print("it's a cone/cylinder, checking z")
|
||||
PathLog.track("it's a cone/cylinder, checking z")
|
||||
return filter(lambda pt: pt.z >= self.bottom() and pt.z <= self.top(), pts)
|
||||
if type(face.Surface) == Part.Plane:
|
||||
#print("it's a plane, checking R")
|
||||
PathLog.track("it's a plane, checking R")
|
||||
c = face.Edges[0].Curve
|
||||
if (type(c) == Part.Circle):
|
||||
return filter(lambda pt: (pt - c.Center).Length <= c.Radius or PathGeom.isRoughly((pt - c.Center).Length, c.Radius), pts)
|
||||
@@ -312,9 +308,9 @@ class MapWireToTag:
|
||||
self.edges = []
|
||||
self.entry = i
|
||||
if tail:
|
||||
debugPrint("MapWireToTag(%s - %s)" % (i, tail.valueAt(tail.FirstParameter)))
|
||||
PathLog.debug("MapWireToTag(%s - %s)" % (i, tail.valueAt(tail.FirstParameter)))
|
||||
else:
|
||||
debugPrint("MapWireToTag(%s - )" % i)
|
||||
PathLog.debug("MapWireToTag(%s - )" % i)
|
||||
self.complete = False
|
||||
self.haveProblem = False
|
||||
|
||||
@@ -342,11 +338,11 @@ class MapWireToTag:
|
||||
|
||||
def cleanupEdges(self, edges):
|
||||
# want to remove all edges from the wire itself, and all internal struts
|
||||
#print("+cleanupEdges")
|
||||
#print(" edges:")
|
||||
PathLog.track("+cleanupEdges")
|
||||
PathLog.debug(" edges:")
|
||||
for e in edges:
|
||||
debugEdge(e, ' ')
|
||||
#print(":")
|
||||
PathLog.debug(":")
|
||||
self.edgesCleanup = [copy.copy(edges)]
|
||||
|
||||
# remove any edge that has a point inside the tag solid
|
||||
@@ -413,7 +409,7 @@ class MapWireToTag:
|
||||
return edges
|
||||
|
||||
def orderAndFlipEdges(self, edges):
|
||||
#print("entry(%.2f, %.2f, %.2f), exit(%.2f, %.2f, %.2f)" % (self.entry.x, self.entry.y, self.entry.z, self.exit.x, self.exit.y, self.exit.z))
|
||||
PathLog.track("entry(%.2f, %.2f, %.2f), exit(%.2f, %.2f, %.2f)" % (self.entry.x, self.entry.y, self.entry.z, self.exit.x, self.exit.y, self.exit.z))
|
||||
self.edgesOrder = []
|
||||
outputEdges = []
|
||||
p0 = self.entry
|
||||
@@ -450,11 +446,11 @@ class MapWireToTag:
|
||||
debugEdge(e, ' ', False)
|
||||
raise ValueError("No connection to %s" % (p0))
|
||||
elif lastP:
|
||||
debugPrint("xxxxxx (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f)" % (p0.x, p0.y, p0.z, lastP.x, lastP.y, lastP.z))
|
||||
PathLog.debug("xxxxxx (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f)" % (p0.x, p0.y, p0.z, lastP.x, lastP.y, lastP.z))
|
||||
else:
|
||||
debugPrint("xxxxxx (%.2f, %.2f, %.2f) -" % (p0.x, p0.y, p0.z))
|
||||
PathLog.debug("xxxxxx (%.2f, %.2f, %.2f) -" % (p0.x, p0.y, p0.z))
|
||||
lastP = p0
|
||||
#print("-cleanupEdges")
|
||||
PathLog.track("-")
|
||||
return outputEdges
|
||||
|
||||
def isStrut(self, edge):
|
||||
@@ -546,14 +542,9 @@ class _RapidEdges:
|
||||
return True
|
||||
return False
|
||||
|
||||
def debugPrint(self):
|
||||
debugPrint('rapid:')
|
||||
for r in self.rapid:
|
||||
debugEdge(r, ' ')
|
||||
|
||||
class PathData:
|
||||
def __init__(self, obj):
|
||||
debugPrint("PathData(%s)" % obj.Base.Name)
|
||||
PathLog.track(obj.Base.Name)
|
||||
self.obj = obj
|
||||
self.wire, rapid = PathGeom.wireForPath(obj.Base.Path)
|
||||
self.rapid = _RapidEdges(rapid)
|
||||
@@ -593,7 +584,7 @@ class PathData:
|
||||
return (edges[0], edges[-1])
|
||||
|
||||
def generateTags(self, obj, count, width=None, height=None, angle=None, radius=None, spacing=None):
|
||||
debugPrint("generateTags(%s, %s, %s, %s, %s)" % (count, width, height, angle, spacing))
|
||||
PathLog.track(count, width, height, angle, spacing)
|
||||
#for e in self.base.Edges:
|
||||
# debugMarker(e.Vertexes[0].Point, 'base', (0.0, 1.0, 1.0), 0.2)
|
||||
|
||||
@@ -613,7 +604,7 @@ class PathData:
|
||||
startIndex = 0
|
||||
for i in range(0, len(self.base.Edges)):
|
||||
edge = self.base.Edges[i]
|
||||
debugPrint(' %d: %.2f' % (i, edge.Length))
|
||||
PathLog.debug(' %d: %.2f' % (i, edge.Length))
|
||||
if edge.Length == longestEdge.Length:
|
||||
startIndex = i
|
||||
break
|
||||
@@ -628,10 +619,10 @@ class PathData:
|
||||
|
||||
minLength = min(2. * W, longestEdge.Length)
|
||||
|
||||
debugPrint("length=%.2f shortestEdge=%.2f(%.2f) longestEdge=%.2f(%.2f) minLength=%.2f" % (self.base.Length, shortestEdge.Length, shortestEdge.Length/self.base.Length, longestEdge.Length, longestEdge.Length / self.base.Length, minLength))
|
||||
debugPrint(" start: index=%-2d count=%d (length=%.2f, distance=%.2f)" % (startIndex, startCount, startEdge.Length, tagDistance))
|
||||
debugPrint(" -> lastTagLength=%.2f)" % lastTagLength)
|
||||
debugPrint(" -> currentLength=%.2f)" % currentLength)
|
||||
PathLog.debug("length=%.2f shortestEdge=%.2f(%.2f) longestEdge=%.2f(%.2f) minLength=%.2f" % (self.base.Length, shortestEdge.Length, shortestEdge.Length/self.base.Length, longestEdge.Length, longestEdge.Length / self.base.Length, minLength))
|
||||
PathLog.debug(" start: index=%-2d count=%d (length=%.2f, distance=%.2f)" % (startIndex, startCount, startEdge.Length, tagDistance))
|
||||
PathLog.debug(" -> lastTagLength=%.2f)" % lastTagLength)
|
||||
PathLog.debug(" -> currentLength=%.2f)" % currentLength)
|
||||
|
||||
edgeDict = { startIndex: startCount }
|
||||
|
||||
@@ -646,7 +637,7 @@ class PathData:
|
||||
|
||||
for (i, count) in edgeDict.iteritems():
|
||||
edge = self.base.Edges[i]
|
||||
debugPrint(" %d: %d" % (i, count))
|
||||
PathLog.debug(" %d: %d" % (i, count))
|
||||
#debugMarker(edge.Vertexes[0].Point, 'base', (1.0, 0.0, 0.0), 0.2)
|
||||
#debugMarker(edge.Vertexes[1].Point, 'base', (0.0, 1.0, 0.0), 0.2)
|
||||
if 0 != count:
|
||||
@@ -665,10 +656,10 @@ class PathData:
|
||||
tagCount += 1
|
||||
lastTagLength += tagDistance
|
||||
if tagCount > 0:
|
||||
debugPrint(" index=%d -> count=%d" % (index, tagCount))
|
||||
PathLog.debug(" index=%d -> count=%d" % (index, tagCount))
|
||||
edgeDict[index] = tagCount
|
||||
else:
|
||||
debugPrint(" skipping=%-2d (%.2f)" % (index, edge.Length))
|
||||
PathLog.debug(" skipping=%-2d (%.2f)" % (index, edge.Length))
|
||||
|
||||
return (currentLength, lastTagLength)
|
||||
|
||||
@@ -701,7 +692,7 @@ class PathData:
|
||||
ordered.append(t)
|
||||
# disable all tags that are not on the base wire.
|
||||
for tag in tags:
|
||||
FreeCAD.Console.PrintMessage("Tag #%d not on base wire - disabling\n" % len(ordered))
|
||||
PathLog.notice("Tag #%d not on base wire - disabling\n" % len(ordered))
|
||||
tag.enabled = False
|
||||
ordered.append(tag)
|
||||
return ordered
|
||||
@@ -756,7 +747,7 @@ class ObjectDressup:
|
||||
return True
|
||||
|
||||
def createPath(self, obj, pathData, tags):
|
||||
#print("createPath")
|
||||
PathLog.track()
|
||||
commands = []
|
||||
lastEdge = 0
|
||||
lastTag = 0
|
||||
@@ -776,7 +767,7 @@ class ObjectDressup:
|
||||
mapper = None
|
||||
|
||||
while edge or lastEdge < len(pathData.edges):
|
||||
debugPrint("------- lastEdge = %d/%d.%d/%d" % (lastEdge, lastTag, t, len(tags)))
|
||||
PathLog.debug("------- lastEdge = %d/%d.%d/%d" % (lastEdge, lastTag, t, len(tags)))
|
||||
if not edge:
|
||||
edge = pathData.edges[lastEdge]
|
||||
debugEdge(edge, "======= new edge: %d/%d" % (lastEdge, len(pathData.edges)))
|
||||
@@ -836,19 +827,19 @@ class ObjectDressup:
|
||||
if tag.enabled:
|
||||
if prev:
|
||||
if prev.solid.common(tag.solid).Faces:
|
||||
FreeCAD.Console.PrintMessage("Tag #%d intersects with previous tag - disabling\n" % i)
|
||||
debugPrint("this tag = %d [%s]" % (i, tag.solid.BoundBox))
|
||||
PathLog.notice("Tag #%d intersects with previous tag - disabling\n" % i)
|
||||
PathLog.debug("this tag = %d [%s]" % (i, tag.solid.BoundBox))
|
||||
tag.enabled = False
|
||||
elif self.pathData.edges:
|
||||
e = self.pathData.edges[0]
|
||||
p0 = e.valueAt(e.FirstParameter)
|
||||
p1 = e.valueAt(e.LastParameter)
|
||||
if tag.solid.isInside(p0, PathGeom.Tolerance, True) or tag.solid.isInside(p1, PathGeom.Tolerance, True):
|
||||
FreeCAD.Console.PrintMessage("Tag #%d intersects with starting point - disabling\n" % i)
|
||||
PathLog.notice("Tag #%d intersects with starting point - disabling\n" % i)
|
||||
tag.enabled = False
|
||||
if tag.enabled:
|
||||
prev = tag
|
||||
debugPrint("previousTag = %d [%s]" % (i, prev))
|
||||
PathLog.debug("previousTag = %d [%s]" % (i, prev))
|
||||
else:
|
||||
disabled.append(i)
|
||||
tags.append(tag)
|
||||
@@ -856,6 +847,7 @@ class ObjectDressup:
|
||||
return (tags, positions, disabled)
|
||||
|
||||
def execute(self, obj):
|
||||
#import cProfile
|
||||
#pr = cProfile.Profile()
|
||||
#pr.enable()
|
||||
self.doExecute(obj)
|
||||
@@ -881,7 +873,7 @@ class ObjectDressup:
|
||||
if hasattr(obj, "Positions"):
|
||||
self.tags, positions, disabled = self.createTagsPositionDisabled(obj, obj.Positions, obj.Disabled)
|
||||
if obj.Disabled != disabled:
|
||||
debugPrint("Updating properties.... %s vs. %s" % (obj.Disabled, disabled))
|
||||
PathLog.debug("Updating properties.... %s vs. %s" % (obj.Disabled, disabled))
|
||||
obj.Positions = positions
|
||||
obj.Disabled = disabled
|
||||
|
||||
@@ -894,11 +886,11 @@ class ObjectDressup:
|
||||
|
||||
def processTags(self, obj):
|
||||
tagID = 0
|
||||
if debugDressup:
|
||||
if PathLog.getLevel(LOG_MODULE) == PathLog.Level.DEBUG:
|
||||
for tag in self.tags:
|
||||
tagID += 1
|
||||
if tag.enabled:
|
||||
debugPrint("x=%s, y=%s, z=%s" % (tag.x, tag.y, self.pathData.minZ))
|
||||
PathLog.debug("x=%s, y=%s, z=%s" % (tag.x, tag.y, self.pathData.minZ))
|
||||
#debugMarker(FreeCAD.Vector(tag.x, tag.y, self.pathData.minZ), "tag-%02d" % tagID , (1.0, 0.0, 1.0), 0.5)
|
||||
#if tag.angle != 90:
|
||||
# debugCone(tag.originAt(self.pathData.minZ), tag.r1, tag.r2, tag.actualHeight, "tag-%02d" % tagID)
|
||||
@@ -906,15 +898,14 @@ class ObjectDressup:
|
||||
# debugCylinder(tag.originAt(self.pathData.minZ), tag.fullWidth()/2, tag.actualHeight, "tag-%02d" % tagID)
|
||||
|
||||
obj.Path = self.createPath(obj, self.pathData, self.tags)
|
||||
#print("execute - done")
|
||||
|
||||
def setup(self, obj, generate=False):
|
||||
debugPrint("setup")
|
||||
PathLog.debug("setup")
|
||||
self.obj = obj
|
||||
try:
|
||||
pathData = PathData(obj)
|
||||
except ValueError:
|
||||
FreeCAD.Console.PrintError(translate("Cannot insert holding tags for this path - please select a Profile path\n"))
|
||||
PathLog.error(translate("Cannot insert holding tags for this path - please select a Profile path\n"))
|
||||
return None
|
||||
|
||||
self.toolRadius = 5
|
||||
@@ -938,7 +929,7 @@ class ObjectDressup:
|
||||
return self.pathData
|
||||
|
||||
def setXyEnabled(self, triples):
|
||||
debugPrint("setXyEnabled")
|
||||
PathLog.track()
|
||||
if not hasattr(self, 'pathData'):
|
||||
self.setup(self.obj)
|
||||
positions = []
|
||||
@@ -1038,7 +1029,7 @@ class TaskPanel:
|
||||
self.obj.Proxy.setXyEnabled(tags)
|
||||
|
||||
def updateTagsView(self):
|
||||
#print("updateTagsView")
|
||||
PathLog.track()
|
||||
self.formTags.lwTags.blockSignals(True)
|
||||
self.formTags.lwTags.clear()
|
||||
for i, pos in enumerate(self.obj.Positions):
|
||||
@@ -1066,7 +1057,7 @@ class TaskPanel:
|
||||
self.obj.Proxy.execute(self.obj)
|
||||
|
||||
self.updateTagsView()
|
||||
#if debugDressup:
|
||||
#if PathLog.getLevel(LOG_MODULE) == PathLog.Level.DEBUG:
|
||||
# # this causes a big of an echo and a double click on the spin buttons, don't know why though
|
||||
# FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
@@ -1359,7 +1350,7 @@ class ViewProviderDressup:
|
||||
self.tags = tags
|
||||
|
||||
def selectTag(self, index):
|
||||
#print("selectTag(%s)" % index)
|
||||
PathLog.track(index)
|
||||
for i, tag in enumerate(self.tags):
|
||||
tag.setSelected(i == index)
|
||||
|
||||
@@ -1401,14 +1392,14 @@ class CommandPathDressupHoldingTags:
|
||||
# check that the selection contains exactly what we want
|
||||
selection = FreeCADGui.Selection.getSelection()
|
||||
if len(selection) != 1:
|
||||
FreeCAD.Console.PrintError(translate("Please select one path object\n"))
|
||||
PathLog.error(translate("Please select one path object\n"))
|
||||
return
|
||||
baseObject = selection[0]
|
||||
if not baseObject.isDerivedFrom("Path::Feature"):
|
||||
FreeCAD.Console.PrintError(translate("The selected object is not a path\n"))
|
||||
PathLog.error(translate("The selected object is not a path\n"))
|
||||
return
|
||||
if baseObject.isDerivedFrom("Path::FeatureCompoundPython"):
|
||||
FreeCAD.Console.PrintError(translate("Please select a Profile object"))
|
||||
PathLog.error(translate("Please select a Profile object"))
|
||||
return
|
||||
|
||||
# everything ok!
|
||||
@@ -1430,4 +1421,4 @@ if FreeCAD.GuiUp:
|
||||
# register the FreeCAD command
|
||||
FreeCADGui.addCommand('PathDressup_HoldingTags', CommandPathDressupHoldingTags())
|
||||
|
||||
FreeCAD.Console.PrintLog("Loading PathDressupHoldingTags... done\n")
|
||||
PathLog.notice("Loading PathDressupHoldingTags... done\n")
|
||||
|
||||
163
src/Mod/Path/PathScripts/PathLog.py
Normal file
163
src/Mod/Path/PathScripts/PathLog.py
Normal file
@@ -0,0 +1,163 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 2016 sliptonic <shopinthewoods@gmail.com> *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
# * as published by the Free Software Foundation; either version 2 of *
|
||||
# * the License, or (at your option) any later version. *
|
||||
# * for detail see the LICENCE text file. *
|
||||
# * *
|
||||
# * This program is distributed in the hope that it will be useful, *
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
# * GNU Library General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with this program; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
import FreeCAD
|
||||
import os
|
||||
import traceback
|
||||
|
||||
class Level:
|
||||
"""Enumeration of log levels, used for setLevel and getLevel."""
|
||||
RESET = -1
|
||||
ERROR = 0
|
||||
WARNING = 1
|
||||
NOTICE = 2
|
||||
INFO = 3
|
||||
DEBUG = 4
|
||||
|
||||
_names = { ERROR: 'ERROR', WARNING: 'WARNING', NOTICE: 'NOTICE', INFO: 'INFO', DEBUG: 'DEBUG' }
|
||||
|
||||
@classmethod
|
||||
def toString(cls, level):
|
||||
return cls._names.get(level, 'UNKNOWN')
|
||||
|
||||
_defaultLogLevel = Level.NOTICE
|
||||
_moduleLogLevel = { }
|
||||
_useConsole = True
|
||||
_trackModule = { }
|
||||
_trackAll = False
|
||||
|
||||
def logToConsole(yes):
|
||||
"""(boolean) - if set to True (default behaviour) log messages are printed to the console. Otherwise they are printed to stdout."""
|
||||
global _useConsole
|
||||
_useConsole = yes
|
||||
|
||||
def setLevel(level, module = None):
|
||||
"""(level, module = None)
|
||||
if no module is specified the default log level is set.
|
||||
Otherwise the module specific log level is changed (use RESET to clear)."""
|
||||
global _defaultLogLevel
|
||||
global _moduleLogLevel
|
||||
if module:
|
||||
if level == Level.RESET:
|
||||
if _moduleLogLevel.get(module, -1) != -1:
|
||||
del _moduleLogLevel[module]
|
||||
else:
|
||||
_moduleLogLevel[module] = level
|
||||
else:
|
||||
if level == Level.RESET:
|
||||
_defaultLogLevel = Level.NOTICE
|
||||
_moduleLogLevel = { }
|
||||
else:
|
||||
_defaultLogLevel = level
|
||||
|
||||
def getLevel(module = None):
|
||||
"""(module = None) - return the global (None) or module specific log level."""
|
||||
if module:
|
||||
return _moduleLogLevel.get(module, _defaultLogLevel)
|
||||
return _defaultLogLevel
|
||||
|
||||
def _caller():
|
||||
"""internal function to determine the calling module."""
|
||||
file, line, func, text = traceback.extract_stack(limit=3)[0]
|
||||
return os.path.splitext(os.path.basename(file))[0], line, func
|
||||
|
||||
def _log(level, (module, line, func), msg):
|
||||
"""internal function to do the logging"""
|
||||
if getLevel(module) >= level:
|
||||
message = "%s.%s: %s" % (module, Level.toString(level), msg)
|
||||
if _useConsole:
|
||||
message += "\n"
|
||||
if level == Level.NOTICE:
|
||||
FreeCAD.Console.PrintLog(message)
|
||||
elif level == Level.WARNING:
|
||||
FreeCAD.Console.PrintWarning(message)
|
||||
elif level == Level.ERROR:
|
||||
FreeCAD.Console.PrintError(message)
|
||||
else:
|
||||
FreeCAD.Console.PrintMessage(message)
|
||||
else:
|
||||
print(message)
|
||||
return message
|
||||
return None
|
||||
|
||||
def debug(msg):
|
||||
"""(message)"""
|
||||
return _log(Level.DEBUG, _caller(), msg)
|
||||
def info(msg):
|
||||
"""(message)"""
|
||||
return _log(Level.INFO, _caller(), msg)
|
||||
def notice(msg):
|
||||
"""(message)"""
|
||||
return _log(Level.NOTICE, _caller(), msg)
|
||||
def warning(msg):
|
||||
"""(message)"""
|
||||
return _log(Level.WARNING, _caller(), msg)
|
||||
def error(msg):
|
||||
"""(message)"""
|
||||
return _log(Level.ERROR, _caller(), msg)
|
||||
|
||||
def trackAllModules(boolean):
|
||||
"""(boolean) - if True all modules will be tracked, otherwise tracking is up to the module setting."""
|
||||
global _trackAll
|
||||
_trackAll = boolean
|
||||
|
||||
def untrackAllModules():
|
||||
"""In addition to stop tracking all modules it also clears the tracking flag for all individual modules."""
|
||||
global _trackAll
|
||||
global _trackModule
|
||||
_trackAll = False
|
||||
_trackModule = { }
|
||||
|
||||
def trackModule(module = None):
|
||||
"""(module = None) - start tracking given module, current module if not set."""
|
||||
global _trackModule
|
||||
if module:
|
||||
_trackModule[module] = True
|
||||
else:
|
||||
mod, line, func = _caller()
|
||||
_trackModule[mod] = True
|
||||
|
||||
def untrackModule(module = None):
|
||||
"""(module = None) - stop tracking given module, current module if not set."""
|
||||
global _trackModule
|
||||
if module and _trackModule.get(module, None):
|
||||
del _trackModule[module]
|
||||
elif not module:
|
||||
mod, line, func = _caller()
|
||||
if _trackModule.get(mod, None):
|
||||
del _trackModule[mod]
|
||||
|
||||
def track(*args):
|
||||
"""(....) - call with arguments of current function you want logged if tracking is enabled."""
|
||||
module, line, func = _caller()
|
||||
if _trackAll or _trackModule.get(module, None):
|
||||
message = "%s(%d).%s(%s)" % (module, line, func, ', '.join([str(arg) for arg in args]))
|
||||
if _useConsole:
|
||||
FreeCAD.Console.PrintMessage(message + "\n")
|
||||
else:
|
||||
print(message)
|
||||
return message
|
||||
return None
|
||||
|
||||
|
||||
195
src/Mod/Path/PathTests/TestPathLog.py
Normal file
195
src/Mod/Path/PathTests/TestPathLog.py
Normal file
@@ -0,0 +1,195 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 2016 sliptonic <shopinthewoods@gmail.com> *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
# * as published by the Free Software Foundation; either version 2 of *
|
||||
# * the License, or (at your option) any later version. *
|
||||
# * for detail see the LICENCE text file. *
|
||||
# * *
|
||||
# * This program is distributed in the hope that it will be useful, *
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
# * GNU Library General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with this program; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
import PathScripts.PathLog as PathLog
|
||||
import unittest
|
||||
|
||||
class TestPathLog(unittest.TestCase):
|
||||
"""Some basic tests for the logging framework."""
|
||||
|
||||
MODULE = 'TestPathLog' # file name without extension
|
||||
|
||||
def setUp(self):
|
||||
PathLog.setLevel(PathLog.Level.RESET)
|
||||
PathLog.untrackAllModules()
|
||||
|
||||
def callerFile(self):
|
||||
return PathLog._caller()[0]
|
||||
def callerLine(self):
|
||||
return PathLog._caller()[1]
|
||||
def callerFunc(self):
|
||||
return PathLog._caller()[2]
|
||||
|
||||
def test00(self):
|
||||
"""Check for proper module extraction."""
|
||||
self.assertEqual(self.callerFile(), self.MODULE)
|
||||
|
||||
def test01(self):
|
||||
"""Check for proper function extraction."""
|
||||
self.assertEqual(self.callerFunc(), 'test01')
|
||||
|
||||
def test10(self):
|
||||
"""Verify default log levels is NOTICE."""
|
||||
self.assertEqual(PathLog.getLevel(), PathLog.Level.NOTICE)
|
||||
self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.NOTICE)
|
||||
|
||||
def test11(self):
|
||||
"""Verify setting global log level."""
|
||||
PathLog.setLevel(PathLog.Level.DEBUG)
|
||||
|
||||
self.assertEqual(PathLog.getLevel(), PathLog.Level.DEBUG)
|
||||
self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.DEBUG)
|
||||
|
||||
def test12(self):
|
||||
"""Verify setting module log level."""
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, self.MODULE)
|
||||
|
||||
self.assertEqual(PathLog.getLevel(), PathLog.Level.NOTICE)
|
||||
self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.DEBUG)
|
||||
|
||||
def test13(self):
|
||||
"""Verify setting other modul's log level doesn't change this one's."""
|
||||
# if this test fails then most likely the global RESET is broken
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, 'SomeOtherModule')
|
||||
|
||||
self.assertEqual(PathLog.getLevel(), PathLog.Level.NOTICE)
|
||||
self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.NOTICE)
|
||||
|
||||
def test14(self):
|
||||
"""Verify resetting log level for module falls back to global level."""
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, self.MODULE)
|
||||
self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.DEBUG)
|
||||
# changing global log level does not affect module
|
||||
PathLog.setLevel(PathLog.Level.ERROR)
|
||||
self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.DEBUG)
|
||||
# resetting module log level restores global log level for module
|
||||
PathLog.setLevel(PathLog.Level.RESET, self.MODULE)
|
||||
self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.ERROR)
|
||||
# changing the global log level will also change the module log level
|
||||
PathLog.setLevel(PathLog.Level.DEBUG)
|
||||
self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.DEBUG)
|
||||
|
||||
def test20(self):
|
||||
"""Verify debug logs aren't logged by default."""
|
||||
self.assertIsNone(PathLog.debug("this"))
|
||||
|
||||
def test21(self):
|
||||
"""Verify debug logs are logged if log level is set to DEBUG."""
|
||||
PathLog.setLevel(PathLog.Level.DEBUG)
|
||||
self.assertIsNotNone(PathLog.debug("this"))
|
||||
|
||||
def test30(self):
|
||||
"""Verify log level ERROR."""
|
||||
PathLog.setLevel(PathLog.Level.ERROR)
|
||||
self.assertIsNone(PathLog.debug('something'))
|
||||
self.assertIsNone(PathLog.info('something'))
|
||||
self.assertIsNone(PathLog.notice('something'))
|
||||
self.assertIsNone(PathLog.warning('something'))
|
||||
self.assertIsNotNone(PathLog.error('something'))
|
||||
|
||||
def test31(self):
|
||||
"""Verify log level WARNING."""
|
||||
PathLog.setLevel(PathLog.Level.WARNING)
|
||||
self.assertIsNone(PathLog.debug('something'))
|
||||
self.assertIsNone(PathLog.info('something'))
|
||||
self.assertIsNone(PathLog.notice('something'))
|
||||
self.assertIsNotNone(PathLog.warning('something'))
|
||||
self.assertIsNotNone(PathLog.error('something'))
|
||||
|
||||
def test32(self):
|
||||
"""Verify log level NOTICE."""
|
||||
PathLog.setLevel(PathLog.Level.NOTICE)
|
||||
self.assertIsNone(PathLog.debug('something'))
|
||||
self.assertIsNone(PathLog.info('something'))
|
||||
self.assertIsNotNone(PathLog.notice('something'))
|
||||
self.assertIsNotNone(PathLog.warning('something'))
|
||||
self.assertIsNotNone(PathLog.error('something'))
|
||||
|
||||
def test33(self):
|
||||
"""Verify log level INFO."""
|
||||
PathLog.setLevel(PathLog.Level.INFO)
|
||||
self.assertIsNone(PathLog.debug('something'))
|
||||
self.assertIsNotNone(PathLog.info('something'))
|
||||
self.assertIsNotNone(PathLog.notice('something'))
|
||||
self.assertIsNotNone(PathLog.warning('something'))
|
||||
self.assertIsNotNone(PathLog.error('something'))
|
||||
|
||||
def test34(self):
|
||||
"""Verify log level DEBUG."""
|
||||
PathLog.setLevel(PathLog.Level.DEBUG)
|
||||
self.assertIsNotNone(PathLog.debug('something'))
|
||||
self.assertIsNotNone(PathLog.info('something'))
|
||||
self.assertIsNotNone(PathLog.notice('something'))
|
||||
self.assertIsNotNone(PathLog.warning('something'))
|
||||
self.assertIsNotNone(PathLog.error('something'))
|
||||
|
||||
def test50(self):
|
||||
"""Verify no tracking by default."""
|
||||
self.assertIsNone(PathLog.track('this', 'and', 'that'))
|
||||
|
||||
def test51(self):
|
||||
"""Verify enabling tracking for module results in tracking."""
|
||||
PathLog.trackModule()
|
||||
# Don't want to rely on the line number matching - still want some
|
||||
# indication that track does the right thing ....
|
||||
msg = PathLog.track('this', 'and', 'that')
|
||||
self.assertTrue(msg.startswith(self.MODULE))
|
||||
self.assertTrue(msg.endswith('test51(this, and, that)'))
|
||||
|
||||
def test52(self):
|
||||
"""Verify untracking stops tracking."""
|
||||
PathLog.trackModule()
|
||||
self.assertIsNotNone(PathLog.track('this', 'and', 'that'))
|
||||
PathLog.untrackModule()
|
||||
self.assertIsNone(PathLog.track('this', 'and', 'that'))
|
||||
|
||||
def test53(self):
|
||||
"""Verify trackAllModules works correctly."""
|
||||
PathLog.trackAllModules(True)
|
||||
self.assertIsNotNone(PathLog.track('this', 'and', 'that'))
|
||||
PathLog.trackAllModules(False)
|
||||
self.assertIsNone(PathLog.track('this', 'and', 'that'))
|
||||
PathLog.trackAllModules(True)
|
||||
PathLog.trackModule()
|
||||
self.assertIsNotNone(PathLog.track('this', 'and', 'that'))
|
||||
PathLog.trackAllModules(False)
|
||||
self.assertIsNotNone(PathLog.track('this', 'and', 'that'))
|
||||
|
||||
def test60(self):
|
||||
"""Verify track handles no argument."""
|
||||
PathLog.trackModule()
|
||||
msg = PathLog.track()
|
||||
self.assertTrue(msg.startswith(self.MODULE))
|
||||
self.assertTrue(msg.endswith('test60()'))
|
||||
|
||||
def test61(self):
|
||||
"""Verify track handles arbitrary argument types correctly."""
|
||||
PathLog.trackModule()
|
||||
msg = PathLog.track('this', None, 1, 18.25)
|
||||
self.assertTrue(msg.startswith(self.MODULE))
|
||||
self.assertTrue(msg.endswith('test61(this, None, 1, 18.25)'))
|
||||
|
||||
def testzz(self):
|
||||
"""Restoring environment after tests."""
|
||||
PathLog.setLevel(PathLog.Level.RESET)
|
||||
@@ -74,7 +74,6 @@ class PathPostTestCases(unittest.TestCase):
|
||||
contour.OffsetExtra = 0.0
|
||||
contour.Direction = 'CW'
|
||||
contour.UseComp = True
|
||||
contour.PlungeAngle = 90.0
|
||||
PathScripts.PathUtils.addToJob(contour)
|
||||
PathScripts.PathContour.ObjectContour.setDepths(contour.Proxy, contour)
|
||||
self.doc.recompute()
|
||||
|
||||
@@ -24,10 +24,11 @@
|
||||
|
||||
import TestApp
|
||||
|
||||
from PathTests.TestPathLog import TestPathLog
|
||||
from PathTests.TestPathCore import TestPathCore
|
||||
from PathTests.TestPathPost import PathPostTestCases
|
||||
|
||||
from PathTests.TestPathGeom import TestPathGeom
|
||||
from PathTests.TestPathDepthParams import depthTestCases
|
||||
|
||||
from PathTests.TestPathDressupHoldingTags import TestHoldingTags
|
||||
#from PathTests.TestPathDressupHoldingTags import TestHoldingTags
|
||||
|
||||
Reference in New Issue
Block a user