From fc8f2f1a477fe9063fdfc945b699536c78781ec9 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Wed, 5 Sep 2018 20:41:15 +0800 Subject: [PATCH] constraint: fix constraint multiplication --- constraint.py | 74 ++++++++++++++++++++++++++++----------------------- solver.py | 69 ++++++++++++++++++++++++----------------------- 2 files changed, 75 insertions(+), 68 deletions(-) diff --git a/constraint.py b/constraint.py index fd091aa..a07e0a0 100644 --- a/constraint.py +++ b/constraint.py @@ -506,6 +506,17 @@ class Constraint(ProxyType): for obj in cstrs: cstr = mcs.getProxy(obj) + + # Build array constraint map for constraint multiplication + if mcs.canMultiply(obj): + element0 = obj.Proxy.getElements()[0].Proxy + for info in element0.getInfo(expand=True): + solver.countArrayPartConstraint(info.Part) + else: + for info in obj.Proxy.getElementsInfo(): + if isinstance(info.Part,tuple): + solver.countArrayPartConstraint(info.Part) + if cstr.hasFixedPart(obj): found = True for info in cstr.getFixedParts(solver,obj): @@ -934,42 +945,37 @@ class BaseMulti(Base): logger.warn('{} no first part shape'.format(cstrName(obj))) return idx = 0 - updates = [] for element in elements[1:]: - infos = element.Proxy.getInfo(expand=True) - if not infos: - continue - info0 = firstInfo[idx] - partInfo0 = solver.getPartInfo(info0) - info = infos[0] - partInfo = solver.getPartInfo(info) - e0 = cls._entityDef[0]( - solver,partInfo0,info0.Subname,info0.Shape) - e = cls._entityDef[0]( - solver,partInfo,info.Subname,info.Shape) - params = props + [e0,e] - solver.system.checkRedundancy(obj,partInfo0,partInfo) - h = func(*params,group=solver.group) - if isinstance(h,(list,tuple)): - ret += list(h) - else: - ret.append(h) - idx += 1 - if idx >= count: - return ret - if len(infos)>1: - updates.append((partInfo0,element,len(infos)-1)) + updates = [] + for i,info in enumerate(element.Proxy.getInfo(expand=True)): + if idx >= count: + break + info0 = firstInfo[idx] + if i and solver.getArrayPartConstraintCount(info0.Part)==1: + # We can safely skip those coplanar edges if the part + # array element is involved in one and only one + # constraint (i.e. this one). + updates.append((idx,i)) + idx += 1 + continue - for partInfo0,element,infoCount in updates: - if partInfo0.Update: - logger.warn('{} used in more than one constraint ' - 'multiplication'.format(partInfo0.PartName)) - continue - partInfo0.Update.append(idx) - partInfo0.Update.append(element) - idx += infoCount - if idx >= count: - break + partInfo0 = solver.getPartInfo(info0) + partInfo = solver.getPartInfo(info) + e0 = cls._entityDef[0]( + solver,partInfo0,info0.Subname,info0.Shape) + e = cls._entityDef[0]( + solver,partInfo,info.Subname,info.Shape) + params = props + [e0,e] + solver.system.checkRedundancy(obj,partInfo0,partInfo) + h = func(*params,group=solver.group) + if isinstance(h,(list,tuple)): + ret += list(h) + else: + ret.append(h) + idx += 1 + + if updates: + partInfo0.Update.append((updates,element)) return ret diff --git a/solver.py b/solver.py index 2623f37..c356e57 100644 --- a/solver.py +++ b/solver.py @@ -18,7 +18,7 @@ from .system import System # plane of the part. # EntityMap: string -> entity handle map, for caching # Group: transforming entity group handle -# CstrMap: map from other part to the constrains between this and the other part. +# CstrMap: map from other part to the constrain between this and the other part. # This is for auto constraint DOF reduction. Only some composite # constraints will be mapped. # Update: in case the constraint uses the `Multiplication` feature, only the @@ -41,6 +41,7 @@ class Solver(object): self.group = 1 # the solving group self._partMap = {} self._cstrMap = {} + self._cstrArrayMap = defaultdict(int) self._fixedElements = set() self.system.GroupHandle = self._fixedGroup @@ -211,40 +212,34 @@ class Solver(object): # coplanar circular edges of the same radius. For performance sake, only # the first edge of each expansion is used for constraint. We simply # translate the rest of the parts with the same relative offset. - touches = defaultdict(set) + touched = False for partInfo in updates: - idx,element = partInfo.Update - element0 = element.Proxy.parent.Object.Group[0] - infos0 = element0.Proxy.getInfo(expand=True) - count = len(infos0) - infos = element.Proxy.getInfo(expand=True) - pos0 = infos[0].Placement.multVec( - utils.getElementPos(infos[0].Shape)) - touched = False - for info in infos[1:]: - if idx >= count: - break - pos = info.Placement.multVec( - utils.getElementPos(info.Shape)) - pla = partInfo.Placement.copy() - pla.Base += pos-pos0 - info0 = infos0[idx] - idx += 1 - if isSamePlacement(info0.Placement,pla): - self.system.log('not moving {}'.format(info0.PartName)) - else: - self.system.log('moving {} {}'.format( - partInfo.PartName,pla)) - touched = True - if rollback is not None: - rollback.append((info0.PartName, - info0.Part, - info0.Placement.copy())) - setPlacement(info0.Part,pla) - if touched: - touches[partInfo.Part[0].Document].add(partInfo.Part[0]) - for doc,objs in touches.items(): - doc.recompute(list(objs)) + for indices,element in partInfo.Update: + element0 = element.Proxy.parent.Object.Group[0] + infos0 = element0.Proxy.getInfo(expand=True) + infos = element.Proxy.getInfo(expand=True) + pos0 = infos[0].Placement.multVec( + utils.getElementPos(infos[0].Shape)) + for idx0,idx in indices: + info = infos[idx] + pos = info.Placement.multVec( + utils.getElementPos(info.Shape)) + pla = partInfo.Placement.copy() + pla.Base += pos-pos0 + info0 = infos0[idx0] + if isSamePlacement(info0.Placement,pla): + self.system.log('not moving {}'.format(info0.PartName)) + else: + self.system.log('moving {} {}'.format( + partInfo.PartName,pla)) + touched = True + if rollback is not None: + rollback.append((info0.PartName, + info0.Part, + info0.Placement.copy())) + setPlacement(info0.Part,pla) + if touched: + assembly.recompute(True) def isFixedPart(self,part): if isinstance(part,tuple) and part[0] in self._fixedParts: @@ -258,6 +253,12 @@ class Solver(object): def addFixedElement(self,part,subname): self._fixedElements.add((part,subname)) + def countArrayPartConstraint(self,part): + self._cstrArrayMap[part] += 1 + + def getArrayPartConstraintCount(self,part): + return self._cstrArrayMap[part] + def getPartInfo(self,info,fixed=False,group=0): partInfo = self._partMap.get(info.Part,None) if partInfo: