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

View File

@ -978,23 +978,62 @@ class BaseMulti(Base):
if not count: if not count:
logger.warn('{} no first part shape'.format(cstrName(obj))) logger.warn('{} no first part shape'.format(cstrName(obj)))
return 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 idx = 0
for element in elements[1:]: for element in elements[1:]:
updates = [] 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: if idx >= count:
break break
info0 = firstInfo[idx] info0 = firstInfo[idx]
if i and solver.getArrayPartConstraintCount(info0.Part)==1: partInfo = solver.getPartInfo(info)
# We can safely skip those coplanar edges if the part
# array element is involved in one and only one 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). # constraint (i.e. this one).
updates.append((idx,i)) updates.append((info0,partInfo,info.Shape))
idx += 1 idx += 1
continue continue
partInfo0 = solver.getPartInfo(info0)
partInfo = solver.getPartInfo(info)
e0 = cls._entityDef[0]( e0 = cls._entityDef[0](
solver,partInfo0,info0.Subname,info0.Shape) solver,partInfo0,info0.Subname,info0.Shape)
e = cls._entityDef[0]( e = cls._entityDef[0](
@ -1009,7 +1048,7 @@ class BaseMulti(Base):
idx += 1 idx += 1
if updates: if updates:
partInfo0.Update.append((updates,element)) info0Ref.Update.append((infoRef,shapeRef,updates))
return ret return ret

View File

@ -44,6 +44,7 @@ class Solver(object):
self._cstrMap = {} self._cstrMap = {}
self._cstrArrayMap = defaultdict(int) self._cstrArrayMap = defaultdict(int)
self._fixedElements = set() self._fixedElements = set()
self._dragPart = dragPart
self.system.GroupHandle = self._fixedGroup self.system.GroupHandle = self._fixedGroup
@ -207,28 +208,18 @@ class Solver(object):
part.FirstAngle = v[1] part.FirstAngle = v[1]
part.LastAngle = v[2] part.LastAngle = v[2]
if recompute and touched:
assembly.recompute(True)
# Update parts with constraint multiplication, which auto expands # Update parts with constraint multiplication, which auto expands
# coplanar circular edges of the same radius. For performance sake, only # coplanar circular edges of the same radius. For performance sake, only
# the first edge of each expansion is used for constraint. We simply # the first edge of each expansion is used for constraint. We simply
# translate the rest of the parts with the same relative offset. # translate the rest of the parts with the same relative offset.
touched = False for partInfo0 in updates:
for partInfo in updates: for infoRef,shapeRef,pairs in partInfo0.Update:
for indices,element in partInfo.Update: refPos = infoRef.Placement.multVec(
element0 = element.Proxy.parent.Object.Group[0] utils.getElementPos(shapeRef))
infos0 = element0.Proxy.getInfo(expand=True) for info0,partInfo,shape in pairs:
infos = element.Proxy.getInfo(expand=True) pos = partInfo.Placement.multVec(utils.getElementPos(shape))
pos0 = infos[0].Placement.multVec( pla = partInfo0.Placement.copy()
utils.getElementPos(infos[0].Shape)) pla.Base += pos-refPos
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): if isSamePlacement(info0.Placement,pla):
self.system.log('not moving {}'.format(info0.PartName)) self.system.log('not moving {}'.format(info0.PartName))
else: else:
@ -240,9 +231,11 @@ class Solver(object):
info0.Part, info0.Part,
info0.Placement.copy())) info0.Placement.copy()))
setPlacement(info0.Part,pla) setPlacement(info0.Part,pla)
if touched:
if recompute and touched:
assembly.recompute(True) assembly.recompute(True)
def isFixedPart(self,part): def isFixedPart(self,part):
if isinstance(part,tuple) and part[0] in self._fixedParts: if isinstance(part,tuple) and part[0] in self._fixedParts:
return True return True
@ -255,6 +248,9 @@ class Solver(object):
def addFixedElement(self,part,subname): def addFixedElement(self,part,subname):
self._fixedElements.add((part,subname)) self._fixedElements.add((part,subname))
def getDragPart(self):
return self._dragPart
def countArrayPartConstraint(self,part): def countArrayPartConstraint(self,part):
self._cstrArrayMap[part] += 1 self._cstrArrayMap[part] += 1