Path logging interface and bulk transfer of HoldingTags dressup to use module.

This commit is contained in:
Markus Lampert
2017-01-19 15:48:32 -08:00
parent fdd34efa43
commit dfb1094318
6 changed files with 419 additions and 69 deletions

View File

@@ -27,14 +27,12 @@ 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 *
@@ -48,16 +46,13 @@ 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)
#PathLog.setLevel(PathLog.Level.DEBUG)
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() == 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 +60,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() == PathLog.Level.DEBUG:
obj = FreeCAD.ActiveDocument.addObject("Part::Sphere", label)
obj.Label = label
obj.Radius = radius
@@ -74,7 +69,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() == PathLog.Level.DEBUG:
obj = FreeCAD.ActiveDocument.addObject("Part::Cylinder", label)
obj.Label = label
obj.Radius = r
@@ -85,7 +80,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() == PathLog.Level.DEBUG:
obj = FreeCAD.ActiveDocument.addObject("Part::Cone", label)
obj.Label = label
obj.Radius1 = r1
@@ -162,7 +157,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 +193,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 +210,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 +307,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 +337,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 +408,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 +445,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 +541,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 +583,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 +603,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 +618,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 +636,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 +655,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 +691,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 +746,7 @@ class ObjectDressup:
return True
def createPath(self, obj, pathData, tags):
#print("createPath")
PathLog.track()
commands = []
lastEdge = 0
lastTag = 0
@@ -776,7 +766,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 +826,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 +846,7 @@ class ObjectDressup:
return (tags, positions, disabled)
def execute(self, obj):
#import cProfile
#pr = cProfile.Profile()
#pr.enable()
self.doExecute(obj)
@@ -881,7 +872,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 +885,11 @@ class ObjectDressup:
def processTags(self, obj):
tagID = 0
if debugDressup:
if PathLog.getLevel() == 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 +897,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 +928,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 +1028,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 +1056,7 @@ class TaskPanel:
self.obj.Proxy.execute(self.obj)
self.updateTagsView()
#if debugDressup:
#if PathLog.getLevel() == 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 +1349,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 +1391,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 +1420,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")

View 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