solver: solver rollback on failure while dragging

This commit is contained in:
Zheng, Lei 2017-12-04 16:10:43 +08:00
parent 5ac1dd7971
commit eb06ef8fbd
3 changed files with 57 additions and 27 deletions

View File

@ -1473,6 +1473,7 @@ class AsmMovingPart(object):
self.subname = info.SubnameRef
self.undos = None
self.part = info.Part
self.partName = info.PartName
fixed = Constraint.getFixedTransform(self.assembly.getConstraints())
fixed = fixed.get(info.Part,None)
@ -1509,6 +1510,7 @@ class AsmMovingPart(object):
pos = shape.Placement.Base
pla = FreeCAD.Placement(pos,rot)
self.oldPlacement = info.Placement.copy()
self.offset = pla.copy()
self.offsetInv = pla.inverse()
self.draggerPlacement = info.Placement.multiply(pla)
@ -1517,7 +1519,9 @@ class AsmMovingPart(object):
def update(self):
info = getPartInfo(self.parent,self.subname)
self.oldPlacement = info.Placement.copy()
self.part = info.Part
self.partName = info.PartName
pla = info.Placement.multiply(FreeCAD.Placement(self.offset))
logger.trace('part move update {}: {}'.format(objName(self.parent),pla))
self.draggerPlacement = pla
@ -1539,12 +1543,14 @@ class AsmMovingPart(object):
pla = obj.ViewObject.DraggingPlacement
update = True
rollback = []
if self.fixedTransform:
fixed = self.fixedTransform
movement = self.draggerPlacement.inverse().multiply(pla)
if not fixed.Shape:
# The moving part has completely fixed placement, so we move the
# parent assembly instead
rollback.append((obj.Name,obj,obj.Placement.copy()))
pla = obj.Placement.multiply(movement)
setPlacement(obj,pla,self.undos,self._undoName)
update = False
@ -1563,6 +1569,7 @@ class AsmMovingPart(object):
# obtain and update the part placement
pla = pla.multiply(self.offsetInv)
setPlacement(self.part,pla,self.undos,self._undoName)
rollback.append((self.partName,self.part,self.oldPlacement.copy()))
if not asm3.gui.AsmCmdManager.AutoRecompute:
# AsmCmdManager.AutoRecompute means auto re-solve the system. The
@ -1571,12 +1578,12 @@ class AsmMovingPart(object):
obj.recompute(True)
return
System.touch(obj)
# calls asm3.solver.solve(obj) and redirect all the exceptions message
# to logger only.
logger.catch('solver exception when moving part',
asm3.solver.solve,self.objs)
if not logger.catch('solver exception when moving part',
asm3.solver.solve,
self.objs, dragPart=self.part, rollback=rollback):
obj.recompute(True)
# self.draggerPlacement, which holds the intended dragger placement, is
# updated by the above solver call through the following chain,

View File

@ -831,8 +831,3 @@ class EqualRadius(Base):
_entityDef = (_c,_c)
class WhereDragged(Base):
_id = 34
_entityDef = (_p,)
_workplane = True

View File

@ -18,7 +18,7 @@ PartInfo = namedtuple('SolverPartInfo',
('PartName','Placement','Params','Workplane','EntityMap','Group'))
class Solver(object):
def __init__(self,assembly,reportFailed,undo):
def __init__(self,assembly,reportFailed,undo,dragPart,recompute,rollback):
self.system = System.getSystem(assembly)
cstrs = assembly.Proxy.getConstraints()
if not cstrs:
@ -49,6 +49,22 @@ class Solver(object):
else:
self._cstrMap[ret] = cstr
if dragPart:
# TODO: this is ugly, need a better way to expose dragging interface
addDragPoint = getattr(self.system,'addWhereDragged')
if addDragPoint:
if dragPart in self._fixedParts:
raise RuntimeError('cannot drag fixed part')
info = self._partMap.get(dragPart,None)
if not info:
raise RuntimeError('invalid dragging part')
# add dragging point
self.system.log('add drag point {}'.format(info.Workplane[1]))
# TODO: slvs addWhereDragged doesn't work as expected, need to
# investigate more
# addDragPoint(info.Workplane[1],group=self.group)
self.system.log('solving {}'.format(objName(assembly)))
try:
self.system.solve(group=self.group,reportFailed=reportFailed)
@ -77,6 +93,7 @@ class Solver(object):
self.system.log('done sloving')
undoDocs = set() if undo else None
touched = False
for part,partInfo in self._partMap.items():
if part in self._fixedParts:
continue
@ -87,9 +104,17 @@ class Solver(object):
if isSamePlacement(partInfo.Placement,pla):
self.system.log('not moving {}'.format(partInfo.PartName))
else:
touched = True
self.system.log('moving {} {} {} {}'.format(
partInfo.PartName,partInfo.Params,params,pla))
asm.setPlacement(part,pla,undoDocs)
if rollback is not None:
rollback.append((partInfo.PartName,
part,
partInfo.Placement.copy()))
if recompute and touched:
assembly.recompute(True)
if undo:
for doc in undoDocs:
@ -128,7 +153,8 @@ class Solver(object):
self._partMap[info.Part] = partInfo
return partInfo
def solve(objs=None,recursive=None,reportFailed=True,recompute=True,undo=True):
def solve(objs=None,recursive=None,reportFailed=True,
recompute=True,undo=True,dragPart=None,rollback=None):
if not objs:
sels = FreeCADGui.Selection.getSelectionEx('',False)
if len(sels):
@ -164,33 +190,35 @@ def solve(objs=None,recursive=None,reportFailed=True,recompute=True,undo=True):
# now
objs = FreeCAD.getDependentObjects(assemblies,False,True)
assemblies = []
touched = False
for obj in objs:
if not asm.isTypeOf(obj,asm.Assembly):
continue
if System.isDisabled(obj):
logger.debug('skip disabled assembly {}'.format(objName(obj)))
continue
if not touched:
if not System.isTouched(obj):
logger.debug('skip untouched assembly {}'.format(
objName(obj)))
continue
touched = True
logger.debug('adding assembly {}'.format(objName(obj)))
assemblies.append(obj)
if not assemblies:
raise RuntimeError('no assembly need to be solved')
assembly = None
for assembly in assemblies:
if recompute:
assembly.recompute(True)
Solver(assembly,reportFailed,undo)
System.touch(assembly,False)
try:
for assembly in assemblies:
if recompute:
assembly.recompute(True)
if not System.isTouched(assembly):
logger.debug('skip untouched assembly '
'{}'.format(objName(assembly)))
continue
Solver(assembly,reportFailed,undo,dragPart,recompute,rollback)
System.touch(assembly,False)
except Exception:
if rollback is not None:
for name,part,pla in reversed(rollback):
logger.debug('roll back {} to {}'.format(name,pla))
asm.setPlacement(part,pla,None)
raise
return True
if assembly and recompute:
assembly.recompute(True)
System.touch(assembly,False)