Rebase onto current master
This commit is contained in:
parent
2d6ea99035
commit
32bcc0a579
|
@ -33,24 +33,17 @@ from .PathUtils import fmt
|
|||
|
||||
"""Helix Drill object and FreeCAD command"""
|
||||
|
||||
try:
|
||||
_encoding = QtGui.QApplication.UnicodeUTF8
|
||||
if FreeCAD.GuiUp:
|
||||
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)
|
||||
else:
|
||||
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 print_exceptions(func):
|
||||
from functools import wraps
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
except:
|
||||
import traceback
|
||||
raise
|
||||
return wrapper
|
||||
return text
|
||||
|
||||
def hollow_cylinder(cyl):
|
||||
"""Test if this is a hollow cylinder"""
|
||||
|
@ -99,22 +92,6 @@ def connected(edge, face):
|
|||
return True
|
||||
return False
|
||||
|
||||
def connected_cylinders(base, edge):
|
||||
from Part import Cylinder
|
||||
cylinders = []
|
||||
for n in range(len(base.Shape.Faces)):
|
||||
face = "Face{0}".format(n+1)
|
||||
subobj = base.Shape.Faces[n]
|
||||
if isinstance(subobj.Surface, Cylinder):
|
||||
if not connected(edge, subobj):
|
||||
continue
|
||||
if not z_cylinder(subobj):
|
||||
continue
|
||||
if not full_cylinder(subobj):
|
||||
continue
|
||||
cylinders.append(face)
|
||||
return cylinders
|
||||
|
||||
def cylinders_in_selection():
|
||||
from Part import Cylinder
|
||||
selections = FreeCADGui.Selection.getSelectionEx()
|
||||
|
@ -130,16 +107,6 @@ def cylinders_in_selection():
|
|||
if isinstance(subobj.Surface, Cylinder):
|
||||
if z_cylinder(subobj):
|
||||
cylinders[-1][1].append(feature)
|
||||
else:
|
||||
# brute force triple-loop as FreeCAD does not expose
|
||||
# any topology information...
|
||||
for edge in subobj.Edges:
|
||||
for face in connected_cylinders(base, edge):
|
||||
if hollow_cylinder(getattr(base.Shape, face)):
|
||||
cylinders[-1][1].append(face)
|
||||
|
||||
if subobj.ShapeType == 'Edge':
|
||||
cylinders.extend(connected_cylinders(base, (feature,)))
|
||||
|
||||
return cylinders
|
||||
|
||||
|
@ -282,8 +249,6 @@ class ObjectPathHelix(object):
|
|||
|
||||
obj.addProperty("App::PropertyLength", "DeltaR", "Helix Drill",
|
||||
translate("DeltaR", "Radius increment (must be smaller than tool diameter)"))
|
||||
obj.addProperty("App::PropertyBool", "Recursive", "Helix Drill",
|
||||
translate("Recursive", "If True, drill holes also in any subsequent smaller holes at the bottom of a hole"))
|
||||
|
||||
# Depth Properties
|
||||
obj.addProperty("App::PropertyDistance", "Clearance", "Depths",
|
||||
|
@ -301,12 +266,6 @@ class ObjectPathHelix(object):
|
|||
obj.addProperty("App::PropertyDistance", "ThroughDepth", "Depths",
|
||||
translate("Through Depth","Add this amount of additional cutting depth to open-ended holes. Only used if UseFinalDepth is False"))
|
||||
|
||||
# Feed Properties
|
||||
obj.addProperty("App::PropertySpeed", "VertFeed", "Feeds",
|
||||
translate("Vert Feed","Feed rate for vertical mill moves, this includes the actual arcs"))
|
||||
obj.addProperty("App::PropertySpeed", "HorizFeed", "Feeds",
|
||||
translate("Horiz Feed","Feed rate for horizontal mill moves, these are mostly retractions to the safe distance above the object"))
|
||||
|
||||
# The current tool number, read-only
|
||||
# this is apparently used internally, to keep track of tool chagnes
|
||||
obj.addProperty("App::PropertyIntegerConstraint","ToolNumber","Tool",translate("PathProfile","The current tool in use"))
|
||||
|
@ -322,33 +281,25 @@ class ObjectPathHelix(object):
|
|||
return None
|
||||
|
||||
def execute(self,obj):
|
||||
import cProfile, pstats, StringIO
|
||||
pr = cProfile.Profile()
|
||||
pr.enable()
|
||||
|
||||
from Part import Circle, Cylinder, Plane
|
||||
from math import sqrt
|
||||
|
||||
if obj.Features:
|
||||
if not obj.Active:
|
||||
obj.Path = Path.Path("(helix cut operation inactive)")
|
||||
obj.ViewObject.Visibility = False
|
||||
if obj.ViewObject:
|
||||
obj.ViewObject.Visibility = False
|
||||
return
|
||||
|
||||
if not len(obj.InList) > 0:
|
||||
FreeCAD.Console.PrintError("PathHelix: Operation is not part of a project\n")
|
||||
obj.Path = Path.Path("(helix cut operation not part of any project)")
|
||||
obj.ViewObject.Visibility = False
|
||||
return
|
||||
toolload = PathUtils.getLastToolLoad(obj)
|
||||
|
||||
project = obj.InList[0]
|
||||
obj.ToolNumber = int(PathUtils.changeTool(obj,project))
|
||||
tool = PathUtils.getTool(obj,obj.ToolNumber)
|
||||
|
||||
if not tool:
|
||||
if toolload is None or toolload.ToolNumber == 0:
|
||||
FreeCAD.Console.PrintError("PathHelix: No tool selected for helix cut operation, insert a tool change operation first\n")
|
||||
obj.Path = Path.Path("(ERROR: no tool selected for helix cut operation)")
|
||||
return
|
||||
|
||||
tool = PathUtils.getTool(obj, toolload.ToolNumber)
|
||||
|
||||
output = '(helix cut operation'
|
||||
if obj.Comment:
|
||||
output += ', '+ str(obj.Comment)+')\n'
|
||||
|
@ -360,16 +311,24 @@ class ObjectPathHelix(object):
|
|||
|
||||
drill_jobs = []
|
||||
|
||||
for base, feature in sum((list((obj, feature) for feature in features) for obj, features in obj.Features), []):
|
||||
cylinder = getattr(base.Shape, feature)
|
||||
zsafe = cylinder.BoundBox.ZMax + obj.Clearance.Value
|
||||
xc, yc, zc = cylinder.Surface.Center
|
||||
for base, features in obj.Features:
|
||||
centers = {}
|
||||
|
||||
if obj.Recursive:
|
||||
cur_z = cylinder.BoundBox.ZMax
|
||||
for feature in features:
|
||||
cylinder = getattr(base.Shape, feature)
|
||||
xc, yc, _ = cylinder.Surface.Center
|
||||
if (xc, yc) not in centers:
|
||||
centers[xc, yc] = {}
|
||||
centers[xc, yc][cylinder.Surface.Radius] = cylinder
|
||||
|
||||
for center, by_radius in centers.items():
|
||||
cylinders = sorted(by_radius.values(), key = lambda cyl : cyl.Surface.Radius, reverse=True)
|
||||
|
||||
zsafe = max(cyl.BoundBox.ZMax for cyl in cylinders) + obj.Clearance.Value
|
||||
cur_z = cylinders[0].BoundBox.ZMax
|
||||
jobs = []
|
||||
|
||||
while cylinder:
|
||||
for cylinder in cylinders:
|
||||
# Find other edge of current cylinder
|
||||
other_edge = None
|
||||
for edge in cylinder.Edges:
|
||||
|
@ -396,6 +355,7 @@ class ObjectPathHelix(object):
|
|||
if closed is None:
|
||||
raise Exception("Cannot determine if this cylinder is closed on the z = {0} side".format(next_z))
|
||||
|
||||
xc, yc, _ = cylinder.Surface.Center
|
||||
jobs.append(dict(xc=xc, yc=yc, zmin=next_z, zmax=cur_z, r_out=r, r_in=0.0, closed=closed, zsafe=zsafe))
|
||||
|
||||
elif dz > 0:
|
||||
|
@ -414,28 +374,10 @@ class ObjectPathHelix(object):
|
|||
new_jobs.append(job)
|
||||
jobs = new_jobs
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning("PathHelix: Encountered cylinder with zero height\n")
|
||||
FreeCAD.Console.PrintError("PathHelix: Encountered cylinder with zero height\n")
|
||||
break
|
||||
|
||||
cur_z = next_z
|
||||
cylinder = None
|
||||
faces = []
|
||||
for face in base.Shape.Faces:
|
||||
if connected(other_edge, face):
|
||||
if isinstance(face.Surface, Plane):
|
||||
faces.append(face)
|
||||
# should only be one
|
||||
face, = faces
|
||||
for edge in face.Edges:
|
||||
if not edge.isSame(other_edge):
|
||||
for other_face in connected_cylinders(base, edge):
|
||||
other_cylinder = getattr(base.Shape, other_face)
|
||||
xo = other_cylinder.Surface.Center.x
|
||||
yo = other_cylinder.Surface.Center.y
|
||||
center_dist = sqrt((xo - xc)**2 + (yo - yc)**2)
|
||||
if center_dist + other_cylinder.Surface.Radius < r:
|
||||
cylinder = other_cylinder
|
||||
break
|
||||
|
||||
if obj.UseStartDepth:
|
||||
jobs = [job for job in jobs if job["zmin"] < obj.StartDepth.Value]
|
||||
|
@ -450,36 +392,18 @@ class ObjectPathHelix(object):
|
|||
jobs[-1]["zmin"] -= obj.ThroughDepth.Value
|
||||
|
||||
drill_jobs.extend(jobs)
|
||||
else:
|
||||
if obj.UseStartDepth:
|
||||
zmax = obj.StartDepth.Value
|
||||
else:
|
||||
zmax = cylinder.BoundBox.ZMax
|
||||
if obj.UseFinalDepth:
|
||||
zmin = obj.FinalDepth.Value
|
||||
else:
|
||||
zmin = cylinder.BoundBox.ZMin - obj.ThroughDepth.Value
|
||||
drill_jobs.append(dict(xc=xc, yc=yc, zmin=zmin, zmax=zmax, r_out=cylinder.Surface.Radius, r_in=0.0, zsafe=zsafe))
|
||||
|
||||
for job in drill_jobs:
|
||||
output += helix_cut((job["xc"], job["yc"]), job["r_out"], job["r_in"], obj.DeltaR.Value,
|
||||
job["zmax"], job["zmin"], obj.StepDown.Value,
|
||||
job["zsafe"], tool.Diameter,
|
||||
obj.VertFeed.Value, obj.HorizFeed.Value, obj.Direction, obj.StartSide)
|
||||
toolload.VertFeed.Value, toolload.HorizFeed.Value, obj.Direction, obj.StartSide)
|
||||
output += '\n'
|
||||
|
||||
obj.Path = Path.Path(output)
|
||||
if obj.ViewObject:
|
||||
obj.ViewObject.Visibility = True
|
||||
|
||||
pr.disable()
|
||||
s = StringIO.StringIO()
|
||||
sortby = 'time' #cumulative'
|
||||
ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
|
||||
ps.print_stats(10)
|
||||
FreeCAD.Console.PrintError(s.getvalue() + "\n\n")
|
||||
|
||||
|
||||
taskpanels = {}
|
||||
|
||||
class ViewProviderPathHelix(object):
|
||||
|
@ -513,60 +437,52 @@ class CommandPathHelix(object):
|
|||
'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathHelix","Creates a helix cut from selected circles")}
|
||||
|
||||
def IsActive(self):
|
||||
return not FreeCAD.ActiveDocument is None
|
||||
if FreeCAD.ActiveDocument is not None:
|
||||
for o in FreeCAD.ActiveDocument.Objects:
|
||||
if o.Name[:3] == "Job":
|
||||
return True
|
||||
return False
|
||||
|
||||
def Activated(self):
|
||||
import FreeCADGui
|
||||
import Path
|
||||
from PathScripts import PathUtils
|
||||
|
||||
# register the transaction for the undo stack
|
||||
try:
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("PathHelix","Create a helix cut"))
|
||||
FreeCADGui.addModule("PathScripts.PathHelix")
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("PathHelix","Create a helix cut"))
|
||||
FreeCADGui.addModule("PathScripts.PathHelix")
|
||||
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","PathHelix")
|
||||
ObjectPathHelix(obj)
|
||||
ViewProviderPathHelix(obj.ViewObject)
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","PathHelix")
|
||||
ObjectPathHelix(obj)
|
||||
ViewProviderPathHelix(obj.ViewObject)
|
||||
|
||||
obj.Features = cylinders_in_selection()
|
||||
obj.DeltaR = 1.0
|
||||
obj.Features = cylinders_in_selection()
|
||||
obj.DeltaR = 1.0
|
||||
|
||||
project = PathUtils.addToProject(obj)
|
||||
tl = PathUtils.changeTool(obj,project)
|
||||
if tl:
|
||||
obj.ToolNumber = tl
|
||||
tool = PathUtils.getTool(obj,obj.ToolNumber)
|
||||
if tool:
|
||||
# start with 25% overlap
|
||||
obj.DeltaR = tool.Diameter * 0.75
|
||||
toolLoad = PathUtils.getLastToolLoad(obj)
|
||||
if toolLoad is not None:
|
||||
obj.ToolNumber = toolLoad.ToolNumber
|
||||
tool = PathUtils.getTool(obj, toolLoad.ToolNumber)
|
||||
if tool:
|
||||
# start with 25% overlap
|
||||
obj.DeltaR = tool.Diameter * 0.75
|
||||
|
||||
obj.Active = True
|
||||
obj.Comment = ""
|
||||
obj.Active = True
|
||||
obj.Comment = ""
|
||||
|
||||
obj.Direction = "CW"
|
||||
obj.StartSide = "inside"
|
||||
obj.Direction = "CW"
|
||||
obj.StartSide = "inside"
|
||||
|
||||
obj.Clearance = 10.0
|
||||
obj.StepDown = 1.0
|
||||
obj.UseStartDepth = False
|
||||
obj.StartDepth = 1.0
|
||||
obj.UseFinalDepth = False
|
||||
obj.FinalDepth = 0.0
|
||||
obj.ThroughDepth = 0.0
|
||||
obj.Recursive = True
|
||||
obj.Clearance = 10.0
|
||||
obj.StepDown = 1.0
|
||||
obj.UseStartDepth = False
|
||||
obj.StartDepth = 1.0
|
||||
obj.UseFinalDepth = False
|
||||
obj.FinalDepth = 0.0
|
||||
obj.ThroughDepth = 0.0
|
||||
|
||||
obj.VertFeed = 0.0
|
||||
obj.HorizFeed = 0.0
|
||||
PathUtils.addToJob(obj)
|
||||
|
||||
obj.ViewObject.startEditing()
|
||||
|
||||
# commit
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
|
||||
except:
|
||||
FreeCAD.ActiveDocument.abortTransaction()
|
||||
raise
|
||||
obj.ViewObject.startEditing()
|
||||
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
|
@ -692,7 +608,6 @@ class TaskPanel(object):
|
|||
|
||||
heading("Drill parameters")
|
||||
addCheckBox("Active", "Operation is active")
|
||||
addCheckBox("Recursive", "Also mill subsequent holes")
|
||||
tool = PathUtils.getTool(self.obj,self.obj.ToolNumber)
|
||||
if not tool:
|
||||
drmax = None
|
||||
|
@ -710,10 +625,6 @@ class TaskPanel(object):
|
|||
fdcheckbox, fdinput = addQuantity("FinalDepth", "Absolute final height", "UseFinalDepth")
|
||||
tdlabel, tdinput = addQuantity("ThroughDepth", "Extra drill depth\nfor open holes")
|
||||
|
||||
heading("Feeds")
|
||||
addQuantity("HorizFeed", "Horizontal Feed")
|
||||
addQuantity("VertFeed", "Vertical Feed")
|
||||
|
||||
# make ThroughDepth and FinalDepth mutually exclusive
|
||||
def fd_change(state):
|
||||
if fdcheckbox.isChecked():
|
||||
|
@ -734,7 +645,6 @@ class TaskPanel(object):
|
|||
widget.setLayout(layout)
|
||||
self.form = widget
|
||||
|
||||
@print_exceptions
|
||||
def addCylinders(self):
|
||||
features_per_base = {}
|
||||
for base, features in self.obj.Features:
|
||||
|
@ -754,7 +664,6 @@ class TaskPanel(object):
|
|||
self.obj.Proxy.execute(self.obj)
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
@print_exceptions
|
||||
def delCylinders(self):
|
||||
del_features = []
|
||||
for item in self.featureList.selectedItems():
|
||||
|
@ -768,15 +677,10 @@ class TaskPanel(object):
|
|||
if (obj, feature) not in del_features:
|
||||
new_features.append((obj, feature))
|
||||
|
||||
FreeCAD.Console.PrintError(del_features)
|
||||
FreeCAD.Console.PrintError("\n")
|
||||
FreeCAD.Console.PrintError(new_features)
|
||||
FreeCAD.Console.PrintError("\n")
|
||||
self.obj.Features = new_features
|
||||
self.obj.Proxy.execute(self.obj)
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
@print_exceptions
|
||||
def fillFeatureList(self):
|
||||
for obj, features in self.obj.Features:
|
||||
for feature in features:
|
||||
|
@ -786,7 +690,6 @@ class TaskPanel(object):
|
|||
item.setData(QtCore.Qt.UserRole, (obj, feature))
|
||||
self.featureList.addItem(item)
|
||||
|
||||
@print_exceptions
|
||||
def selectFeatures(self, selected, deselected):
|
||||
FreeCADGui.Selection.clearSelection()
|
||||
for item in self.featureList.selectedItems():
|
||||
|
|
Loading…
Reference in New Issue
Block a user