Fix constraint multiplication
This commit is contained in:
parent
cb84709871
commit
6cff9d111b
106
assembly.py
106
assembly.py
|
@ -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:]:
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
34
solver.py
34
solver.py
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user