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

View File

@ -831,8 +831,3 @@ class EqualRadius(Base):
_entityDef = (_c,_c) _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')) ('PartName','Placement','Params','Workplane','EntityMap','Group'))
class Solver(object): class Solver(object):
def __init__(self,assembly,reportFailed,undo): def __init__(self,assembly,reportFailed,undo,dragPart,recompute,rollback):
self.system = System.getSystem(assembly) self.system = System.getSystem(assembly)
cstrs = assembly.Proxy.getConstraints() cstrs = assembly.Proxy.getConstraints()
if not cstrs: if not cstrs:
@ -49,6 +49,22 @@ class Solver(object):
else: else:
self._cstrMap[ret] = cstr 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))) self.system.log('solving {}'.format(objName(assembly)))
try: try:
self.system.solve(group=self.group,reportFailed=reportFailed) self.system.solve(group=self.group,reportFailed=reportFailed)
@ -77,6 +93,7 @@ class Solver(object):
self.system.log('done sloving') self.system.log('done sloving')
undoDocs = set() if undo else None undoDocs = set() if undo else None
touched = False
for part,partInfo in self._partMap.items(): for part,partInfo in self._partMap.items():
if part in self._fixedParts: if part in self._fixedParts:
continue continue
@ -87,9 +104,17 @@ class Solver(object):
if isSamePlacement(partInfo.Placement,pla): if isSamePlacement(partInfo.Placement,pla):
self.system.log('not moving {}'.format(partInfo.PartName)) self.system.log('not moving {}'.format(partInfo.PartName))
else: else:
touched = True
self.system.log('moving {} {} {} {}'.format( self.system.log('moving {} {} {} {}'.format(
partInfo.PartName,partInfo.Params,params,pla)) partInfo.PartName,partInfo.Params,params,pla))
asm.setPlacement(part,pla,undoDocs) 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: if undo:
for doc in undoDocs: for doc in undoDocs:
@ -128,7 +153,8 @@ class Solver(object):
self._partMap[info.Part] = partInfo self._partMap[info.Part] = partInfo
return 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: if not objs:
sels = FreeCADGui.Selection.getSelectionEx('',False) sels = FreeCADGui.Selection.getSelectionEx('',False)
if len(sels): if len(sels):
@ -164,33 +190,35 @@ def solve(objs=None,recursive=None,reportFailed=True,recompute=True,undo=True):
# now # now
objs = FreeCAD.getDependentObjects(assemblies,False,True) objs = FreeCAD.getDependentObjects(assemblies,False,True)
assemblies = [] assemblies = []
touched = False
for obj in objs: for obj in objs:
if not asm.isTypeOf(obj,asm.Assembly): if not asm.isTypeOf(obj,asm.Assembly):
continue continue
if System.isDisabled(obj): if System.isDisabled(obj):
logger.debug('skip disabled assembly {}'.format(objName(obj))) logger.debug('skip disabled assembly {}'.format(objName(obj)))
continue 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))) logger.debug('adding assembly {}'.format(objName(obj)))
assemblies.append(obj) assemblies.append(obj)
if not assemblies: if not assemblies:
raise RuntimeError('no assembly need to be solved') raise RuntimeError('no assembly need to be solved')
assembly = None try:
for assembly in assemblies: for assembly in assemblies:
if recompute: if recompute:
assembly.recompute(True) assembly.recompute(True)
Solver(assembly,reportFailed,undo) if not System.isTouched(assembly):
System.touch(assembly,False) 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)