Merge pull request #4003 from dubstar-04/feature/jobsetupplane

[Path] Enable Face selection for axis alignment (JobSetUp)
This commit is contained in:
sliptonic
2020-10-29 08:53:33 -05:00
committed by GitHub

View File

@@ -22,8 +22,18 @@
# * *
# ***************************************************************************
from collections import Counter
from contextlib import contextmanager
import math
import traceback
from pivy import coin
from PySide import QtCore, QtGui
import FreeCAD
import FreeCADGui
import PathScripts.PathJob as PathJob
import PathScripts.PathJobCmd as PathJobCmd
import PathScripts.PathJobDlg as PathJobDlg
@@ -37,8 +47,6 @@ import PathScripts.PathToolControllerGui as PathToolControllerGui
import PathScripts.PathToolLibraryEditor as PathToolLibraryEditor
import PathScripts.PathUtil as PathUtil
import PathScripts.PathUtils as PathUtils
import math
import traceback
# lazily loaded modules
from lazy_loader.lazy_loader import LazyLoader
@@ -46,21 +54,17 @@ Draft = LazyLoader('Draft', globals(), 'Draft')
Part = LazyLoader('Part', globals(), 'Part')
DraftVecUtils = LazyLoader('DraftVecUtils', globals(), 'DraftVecUtils')
from PySide import QtCore, QtGui
from collections import Counter
from contextlib import contextmanager
from pivy import coin
# Qt translation handling
def translate(context, text, disambig=None):
return QtCore.QCoreApplication.translate(context, text, disambig)
LOGLEVEL = False
if LOGLEVEL:
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
PathLog.trackModule(PathLog.thisModule())
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) # lgtm [py/unreachable-statement]
PathLog.trackModule(PathLog.thisModule()) # lgtm [py/unreachable-statement]
else:
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
@@ -303,7 +307,7 @@ class StockEdit(object):
widget.hide()
if select:
self.form.stock.setCurrentIndex(self.Index)
editor = self.editorFrame() # pylint: disable=assignment-from-none
editor = self.editorFrame() # pylint: disable=assignment-from-none
showHide(self.form.stockFromExisting, editor)
showHide(self.form.stockFromBase, editor)
showHide(self.form.stockCreateBox, editor)
@@ -365,7 +369,7 @@ class StockFromBaseBoundBoxEdit(StockEdit):
stock.ExtZneg = FreeCAD.Units.Quantity(self.form.stockExtZneg.text())
if 'zpos' in fields:
stock.ExtZpos = FreeCAD.Units.Quantity(self.form.stockExtZpos.text())
except Exception: # pylint: disable=broad-except
except Exception: # pylint: disable=broad-except
pass
def getFields(self, obj, fields=None):
@@ -462,7 +466,7 @@ class StockCreateBoxEdit(StockEdit):
obj.Stock.Height = FreeCAD.Units.Quantity(self.form.stockBoxHeight.text())
else:
PathLog.error(translate('PathJob', 'Stock not a box!'))
except Exception: # pylint: disable=broad-except
except Exception: # pylint: disable=broad-except
pass
def setFields(self, obj):
@@ -498,7 +502,7 @@ class StockCreateCylinderEdit(StockEdit):
obj.Stock.Height = FreeCAD.Units.Quantity(self.form.stockCylinderHeight.text())
else:
PathLog.error(translate('PathJob', 'Stock not a cylinder!'))
except Exception: # pylint: disable=broad-except
except Exception: # pylint: disable=broad-except
pass
def setFields(self, obj):
@@ -533,7 +537,7 @@ class StockFromExistingEdit(StockEdit):
def candidates(self, obj):
solids = [o for o in obj.Document.Objects if PathUtil.isSolid(o)]
if hasattr(obj, 'Model'):
if hasattr(obj, 'Model'):
job = obj
else:
job = PathUtils.findParentJob(obj)
@@ -672,7 +676,7 @@ class TaskPanel:
if self.form.wcslist.item(i).checkState() == QtCore.Qt.CheckState.Checked:
flist.append(self.form.wcslist.item(i).text())
self.obj.Fixtures = flist
except Exception: # pylint: disable=broad-except
except Exception: # pylint: disable=broad-except
FreeCAD.Console.PrintWarning("The Job was created without fixture support. Please delete and recreate the job\r\n")
self.updateTooltips()
@@ -878,7 +882,7 @@ class TaskPanel:
elif 'Number' == prop:
try:
tc.ToolNumber = int(item.text())
except Exception: # pylint: disable=broad-except
except Exception: # pylint: disable=broad-except
pass
item.setText("%d" % tc.ToolNumber)
elif 'Spindle' == prop:
@@ -890,7 +894,7 @@ class TaskPanel:
speed = -speed
tc.SpindleDir = rot
tc.SpindleSpeed = speed
except Exception: # pylint: disable=broad-except
except Exception: # pylint: disable=broad-except
pass
item.setText("%s%g" % ('+' if tc.SpindleDir == 'Forward' else '-', tc.SpindleSpeed))
elif 'HorizFeed' == prop or 'VertFeed' == prop:
@@ -902,32 +906,27 @@ class TaskPanel:
elif FreeCAD.Units.Unit() == val.Unit:
val = FreeCAD.Units.Quantity(item.text() + vUnit)
setattr(tc, prop, val)
except Exception: # pylint: disable=broad-except
except Exception: # pylint: disable=broad-except
pass
item.setText("%g" % getattr(tc, prop).getValueAs(vUnit))
else:
try:
val = FreeCAD.Units.Quantity(item.text())
setattr(tc, prop, val)
except Exception: # pylint: disable=broad-except
except Exception: # pylint: disable=broad-except
pass
item.setText("%g" % getattr(tc, prop).Value)
self.template.updateUI()
def modelSetAxis(self, axis):
def flipSel(sel):
PathLog.debug("flip")
p = sel.Object.Placement
loc = sel.Object.Placement.Base
rot = FreeCAD.Rotation(FreeCAD.Vector(1 - axis.x, 1 - axis.y, 1 - axis.z), 180)
sel.Object.Placement = FreeCAD.Placement(loc, p.Rotation.multiply(rot))
def rotateSel(sel, n):
# p = sel.Object.Placement
# loc = sel.Object.Placement.Base
def alignSel(sel, n, flip=False):
PathLog.debug("alignSel")
vector = axis
if flip:
vector = n.negative()
r = axis.cross(n) # rotation axis
a = DraftVecUtils.angle(n, axis, r) * 180 / math.pi
a = DraftVecUtils.angle(n, vector, r) * 180 / math.pi
PathLog.debug("oh boy: (%.2f, %.2f, %.2f) -> %.2f" % (r.x, r.y, r.z, a))
Draft.rotate(sel.Object, a, axis=r)
@@ -939,28 +938,26 @@ class TaskPanel:
selFeature = feature
sub = sel.Object.Shape.getElement(feature)
if 'Face' == sub.ShapeType:
n = sub.Surface.Axis
n = sub.normalAt(0, 0).negative()
if sub.Orientation == 'Reversed':
n = FreeCAD.Vector() - n
PathLog.debug("(%.2f, %.2f, %.2f) -> reversed (%s)" % (n.x, n.y, n.z, sub.Orientation))
else:
PathLog.debug("(%.2f, %.2f, %.2f) -> forward (%s)" % (n.x, n.y, n.z, sub.Orientation))
if PathGeom.pointsCoincide(axis, n):
PathLog.debug("face properly oriented (%.2f, %.2f, %.2f)" % (n.x, n.y, n.z))
if PathGeom.pointsCoincide(axis, n) or PathGeom.pointsCoincide(axis, FreeCAD.Vector() - n):
alignSel(sel, n, True)
else:
if PathGeom.pointsCoincide(axis, FreeCAD.Vector() - n):
flipSel(sel)
else:
rotateSel(sel, n)
alignSel(sel, n)
if 'Edge' == sub.ShapeType:
n = (sub.Vertexes[1].Point - sub.Vertexes[0].Point).normalize()
if PathGeom.pointsCoincide(axis, n) or PathGeom.pointsCoincide(axis, FreeCAD.Vector() - n):
# Don't really know the orientation of an edge, so let's just flip the object
# and if the user doesn't like it they can flip again
flipSel(sel)
alignSel(sel, n, True)
else:
rotateSel(sel, n)
alignSel(sel, n)
if selObject and selFeature:
FreeCADGui.Selection.clearSelection()
FreeCADGui.Selection.addSelection(selObject, selFeature)
@@ -988,9 +985,9 @@ class TaskPanel:
# if selection is not model, move the model too
# if the selection is not stock and there is a stock, move the stock too
for model in self.obj.Model.Group:
if model != selObject:
if model != selObject:
Draft.move(model, offset)
if selObject != self.obj.Stock and self.obj.Stock:
if selObject != self.obj.Stock and self.obj.Stock:
Draft.move(self.obj.Stock, offset)
def modelMove(self, axis):
@@ -1040,10 +1037,10 @@ class TaskPanel:
p = FreeCAD.Vector() - sub.Curve.Location
if 'Face' == sub.ShapeType:
p = FreeCAD.Vector() - sub.BoundBox.Center
if p:
Draft.move(sel.Object, p)
if selObject and selFeature:
FreeCADGui.Selection.clearSelection()
FreeCADGui.Selection.addSelection(selObject, selFeature)
@@ -1134,6 +1131,17 @@ class TaskPanel:
# no valid selection
return False
def isValidAxisSelection(self, sel):
if sel.ShapeType in ['Vertex', 'Edge', 'Face']:
if hasattr(sel, 'Curve') and type(sel.Curve) in [Part.Circle]:
return False
if hasattr(sel, 'Surface') and sel.Surface.curvature(0, 0, "Max") != 0:
return False
return True
# no valid selection
return False
def updateSelection(self):
# Remove Job object if present in Selection: source of phantom paths
if self.obj in FreeCADGui.Selection.getSelection():
@@ -1141,26 +1149,21 @@ class TaskPanel:
sel = FreeCADGui.Selection.getSelectionEx()
self.form.setOrigin.setEnabled(False)
self.form.moveToOrigin.setEnabled(False)
self.form.modelSetXAxis.setEnabled(False)
self.form.modelSetYAxis.setEnabled(False)
self.form.modelSetZAxis.setEnabled(False)
if len(sel) == 1 and len(sel[0].SubObjects) == 1:
subObj = sel[0].SubObjects[0]
if self.isValidDatumSelection(subObj):
self.form.modelSetXAxis.setEnabled(False)
self.form.modelSetYAxis.setEnabled(False)
self.form.modelSetZAxis.setEnabled(False)
self.form.setOrigin.setEnabled(True)
self.form.moveToOrigin.setEnabled(True)
else:
if self.isValidAxisSelection(subObj):
self.form.modelSetXAxis.setEnabled(True)
self.form.modelSetYAxis.setEnabled(True)
self.form.modelSetZAxis.setEnabled(True)
self.form.setOrigin.setEnabled(False)
self.form.moveToOrigin.setEnabled(False)
else:
self.form.modelSetXAxis.setEnabled(False)
self.form.modelSetYAxis.setEnabled(False)
self.form.modelSetZAxis.setEnabled(False)
self.form.setOrigin.setEnabled(False)
self.form.moveToOrigin.setEnabled(False)
if len(sel) == 0 or self.obj.Stock in [s.Object for s in sel]:
self.form.centerInStock.setEnabled(False)
@@ -1201,7 +1204,7 @@ class TaskPanel:
# first remove all obsolete base models
for model, count in PathUtil.keyValueIter(obsolete):
for i in range(count): # pylint: disable=unused-variable
for i in range(count): # pylint: disable=unused-variable
# it seems natural to remove the last of all the base objects for a given model
base = [b for b in obj.Model.Group if proxy.baseObject(obj, b) == model][-1]
self.vproxy.forgetBaseVisibility(obj, base)
@@ -1351,7 +1354,7 @@ def Create(base, template=None):
obj.Document.recompute()
obj.ViewObject.Proxy.editObject(obj.Stock)
return obj
except Exception as exc: # pylint: disable=broad-except
except Exception as exc: # pylint: disable=broad-except
PathLog.error(exc)
traceback.print_exc()
FreeCAD.ActiveDocument.abortTransaction()