From eb06ef8fbd18fd6b8fc905d3ff39a3bb13141075 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Mon, 4 Dec 2017 16:10:43 +0800 Subject: [PATCH] solver: solver rollback on failure while dragging --- assembly.py | 15 ++++++++---- constraint.py | 5 ---- solver.py | 64 ++++++++++++++++++++++++++++++++++++--------------- 3 files changed, 57 insertions(+), 27 deletions(-) diff --git a/assembly.py b/assembly.py index 3caf399..aa725c9 100644 --- a/assembly.py +++ b/assembly.py @@ -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, diff --git a/constraint.py b/constraint.py index a5062ad..ec7c602 100644 --- a/constraint.py +++ b/constraint.py @@ -831,8 +831,3 @@ class EqualRadius(Base): _entityDef = (_c,_c) -class WhereDragged(Base): - _id = 34 - _entityDef = (_p,) - _workplane = True - diff --git a/solver.py b/solver.py index b219812..41c0d17 100644 --- a/solver.py +++ b/solver.py @@ -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)