Fix constraint multiplication

This commit is contained in:
Zheng, Lei 2018-10-08 21:30:57 +08:00
parent cb84709871
commit 6cff9d111b
3 changed files with 150 additions and 45 deletions

View File

@ -1181,7 +1181,7 @@ class AsmElementLink(AsmBase):
return self.infos if expand else self.info
self.info = None
self.infos *= 0 # clear the list
self.infos = []
obj = getattr(self,'Object',None)
if not obj:
return
@ -1286,19 +1286,32 @@ class AsmElementLink(AsmBase):
info.Constraint.setElementVisible(link.Name,False)
return link
def setPlacement(part,pla):
def setPlacement(part,pla,purgeTouched=False):
''' called by solver after solving to adjust the placement.
part: obtained by AsmConstraint.getInfo().Part pla: the new placement
'''
if isinstance(part,tuple):
if not isinstance(part,tuple):
if purgeTouched:
obj = part
touched = 'Touched' in obj.State
part.Placement = pla
else:
pla = part[0].Placement.inverse().multiply(pla)
if part[3]:
if purgeTouched:
obj = part[0]
touched = 'Touched' in obj.State
setLinkProperty(part[0],'PlacementList',{part[1]:pla})
else:
if purgeTouched:
obj = part[2]
touched = 'Touched' in obj.State
part[2].Placement = pla
else:
part.Placement = pla
if purgeTouched and not touched:
obj.purgeTouched()
class ViewProviderAsmElementLink(ViewProviderAsmOnTop):
def __init__(self,vobj):
@ -1418,21 +1431,29 @@ class AsmConstraint(AsmGroup):
name = 'Edge1'
if not elementCount:
shapes.append(None)
e.Proxy.infos = []
else:
count += elementCount
shapes.append(info.Shape.getElement(name))
# merge elements that are coplanar
poses = []
infos = []
for i,e in enumerate(children[1:]):
shape = shapes[i]
if not shape or not e.Proxy.infos:
if not shape:
continue
for j,e2 in enumerate(children[i+2:]):
shape2 = shapes[i+j+1]
if not shape2 or not e2.Proxy.infos:
if not shape2:
continue
if shape.isCoplanar(shape2):
e.Proxy.infos += e2.Proxy.infos
e2.Proxy.infos = []
for info in e.Proxy.infos:
infos.append(info)
poses.append(info.Placement.multVec(
utils.getElementPos(info.Shape)))
firstChild = children[0]
info = firstChild.Proxy.getInfo()
@ -1456,25 +1477,66 @@ class AsmConstraint(AsmGroup):
'ShowElement',ElementCount='Count')
if firstChild.AutoCount:
if getLinkProperty(info.Part[0],'ElementCount',None,True) is None:
oldCount = getLinkProperty(info.Part[0],'ElementCount',None,True)
if oldCount is None:
firstChild.AutoCount = False
else:
elif oldCount < count:
partTouched = 'Touched' in info.Part[0].State
setLinkProperty(info.Part[0],'ElementCount',count)
if not partTouched:
info.Part[0].purgeTouched()
if not firstChild.AutoCount:
count = getLinkProperty(info.Part[0],'ElementCount')
oldCount = getLinkProperty(info.Part[0],'ElementCount')
if count > oldCount:
count = oldCount
if firstChild.Count != count:
firstChild.Count = count
firstChild.Proxy.getInfo(True)
if not touched and 'Touched' in firstChild.State:
firstChild.Proxy.getInfo(True)
# purge touched to avoid recomputation multi-pass
firstChild.purgeTouched()
# To solve the problem of element index reordering, we shall reorder the
# linux array infos by its proximity to the corresponding constraining
# element shape
poses = poses[:count]
infos0 = firstChild.Proxy.getInfo(expand=True)[:count]
distances = []
for i,info0 in enumerate(infos0):
pos0 = info0.Placement.multVec(utils.getElementPos(info0.Shape))
for j,pos in enumerate(poses):
distances.append((pos0.distanceToPoint(pos),i,j))
distances.sort()
used = [-1]*count
order = [None]*count
for _,i,j in distances:
if used[i]>=0 or order[j]:
continue
used[i] = j
order[j] = infos0[i]
count -= 1
if not count:
break
firstChild.Proxy.infos = order
for i in used[oldCount:]:
info0 = order[i]
info = infos[i]
pla = info.Placement.multiply(
utils.getElementPlacement(info.Shape))
pla0 = info0.Placement.multiply(
utils.getElementPlacement(info0.Shape))
pla = info0.Placement.multiply(pla.multiply(pla0.inverse()))
info0.Placement.Rotation = pla.Rotation
info0.Placement.Base = pla.Base
setPlacement(info0.Part,pla,True)
def execute(self,obj):
if not getattr(self,'_initializing',False) and\
getattr(self,'parent',None):
@ -1500,16 +1562,23 @@ class AsmConstraint(AsmGroup):
group = obj.Group
if Constraint.canMultiply(obj):
firstInfo = group[0].Proxy.getInfo(expand=True)
if not firstInfo:
count = len(firstInfo)
if not count:
raise RuntimeError('invalid first element')
elements.append(group[0])
for o in group[1:]:
info = o.Proxy.getInfo(expand=True)
if not info:
infos = o.Proxy.getInfo(expand=True)
if not infos:
continue
elementInfo += info
elements.append(o)
for info in zip(firstInfo,elementInfo[:len(firstInfo)]):
if count <= len(infos):
infos = infos[:count]
o.Proxy.infos = infos
elementInfo += infos
break
elementInfo += infos
for info in zip(firstInfo,elementInfo):
Constraint.check(obj,info,True)
else:
for o in group:
@ -1722,10 +1791,11 @@ class AsmConstraint(AsmGroup):
partGroup = cstr.Proxy.getAssembly().getPartGroup()
if multiplied:
cstr.recompute(True)
subs = elements[0].Proxy.getElementSubname(True).split('.')
infos0 = []
for i in xrange(elements[0].Count):
subs[1] = str(i)
for info0 in elements[0].Proxy.getInfo(expand=True):
subs[1] = str(info0.Part[1])
infos0.append((partGroup,'.'.join(subs)))
infos = []
for element in elements[1:]:

View File

@ -978,23 +978,62 @@ class BaseMulti(Base):
if not count:
logger.warn('{} no first part shape'.format(cstrName(obj)))
return
dragPart = solver.getDragPart()
dragIndex = -1
if isinstance(dragPart,tuple) and \
dragPart[0]==firstInfo[0].Part[0] and \
solver.getArrayPartConstraintCount(dragPart)==1 and \
dragPart[1] < count:
dragIndex = dragPart[1]
idx = 0
for element in elements[1:]:
updates = []
for i,info in enumerate(element.Proxy.getInfo(expand=True)):
info0Ref = None
infoRef = None
shapeRef = None
refIdx = -1
infos = element.Proxy.getInfo(expand=True)
# make sure the dragging part is picked as reference for
# coplanar shortcut updating
if dragIndex>=0:
for i,info in enumerate(infos):
if idx+i >= count:
break
info0 = firstInfo[idx+i]
if info0.Part[1] == dragIndex:
dragIndex = -1
info0Ref = solver.getPartInfo(info0)
infoRef = solver.getPartInfo(info)
shapeRef = info.Shape
refIdx = i
break
for i,info in enumerate(infos):
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
partInfo = solver.getPartInfo(info)
if solver.getArrayPartConstraintCount(info0.Part)!=1:
partInfo0 = solver.getPartInfo(info0)
elif not infoRef:
partInfo0 = solver.getPartInfo(info0)
info0Ref = partInfo0
infoRef = partInfo
shapeRef = info.Shape
elif i == refIdx:
partInfo0 = info0Ref
else:
# 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))
updates.append((info0,partInfo,info.Shape))
idx += 1
continue
partInfo0 = solver.getPartInfo(info0)
partInfo = solver.getPartInfo(info)
e0 = cls._entityDef[0](
solver,partInfo0,info0.Subname,info0.Shape)
e = cls._entityDef[0](
@ -1009,7 +1048,7 @@ class BaseMulti(Base):
idx += 1
if updates:
partInfo0.Update.append((updates,element))
info0Ref.Update.append((infoRef,shapeRef,updates))
return ret

View File

@ -44,6 +44,7 @@ class Solver(object):
self._cstrMap = {}
self._cstrArrayMap = defaultdict(int)
self._fixedElements = set()
self._dragPart = dragPart
self.system.GroupHandle = self._fixedGroup
@ -207,28 +208,18 @@ class Solver(object):
part.FirstAngle = v[1]
part.LastAngle = v[2]
if recompute and touched:
assembly.recompute(True)
# Update parts with constraint multiplication, which auto expands
# 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.
touched = False
for partInfo in updates:
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]
for partInfo0 in updates:
for infoRef,shapeRef,pairs in partInfo0.Update:
refPos = infoRef.Placement.multVec(
utils.getElementPos(shapeRef))
for info0,partInfo,shape in pairs:
pos = partInfo.Placement.multVec(utils.getElementPos(shape))
pla = partInfo0.Placement.copy()
pla.Base += pos-refPos
if isSamePlacement(info0.Placement,pla):
self.system.log('not moving {}'.format(info0.PartName))
else:
@ -240,9 +231,11 @@ class Solver(object):
info0.Part,
info0.Placement.copy()))
setPlacement(info0.Part,pla)
if touched:
if recompute and touched:
assembly.recompute(True)
def isFixedPart(self,part):
if isinstance(part,tuple) and part[0] in self._fixedParts:
return True
@ -255,6 +248,9 @@ class Solver(object):
def addFixedElement(self,part,subname):
self._fixedElements.add((part,subname))
def getDragPart(self):
return self._dragPart
def countArrayPartConstraint(self,part):
self._cstrArrayMap[part] += 1