constraint: add Attachment constraint

This commit is contained in:
Zheng, Lei 2019-05-19 11:30:32 +08:00
parent c21b69901c
commit 0748352f4a
3 changed files with 136 additions and 5 deletions

View File

@ -497,14 +497,14 @@ class Constraint(ProxyType):
return mcs.getProxy(obj).prepare(obj,solver)
@classmethod
def getFixedParts(mcs,solver,cstrs,partGroup):
def getFixedParts(mcs,solver,cstrs,partGroup,rollback):
firstInfo = None
if partGroup.Proxy.derivedParts:
ret = set(partGroup.Proxy.derivedParts)
else:
ret = set()
from .assembly import isTypeOf, AsmWorkPlane
for obj in partGroup.Group:
from .assembly import isTypeOf, AsmWorkPlane, flattenGroup
for obj in flattenGroup(partGroup):
if not hasattr(obj,'Placement'):
ret.add(obj)
logger.debug('part without Placement {}',objName(obj))
@ -513,9 +513,13 @@ class Constraint(ProxyType):
logger.debug('fix workplane {}',objName(obj))
found = len(ret)
attachments = []
for obj in cstrs:
cstr = mcs.getProxy(obj)
if isinstance(cstr, Attachment):
attachments.append(obj)
# Build array constraint map for constraint multiplication
if mcs.canMultiply(obj):
element0 = obj.Proxy.getElements()[0].Proxy
@ -556,6 +560,106 @@ class Constraint(ProxyType):
logger.debug('\t{}.{}',o[0].Name,o[1])
else:
logger.debug('\t{}',o.Name)
if not ret:
return ret
# Early solving for Attachment constraint, which requires only simple
# matrix calculation. We only solve those directly or indirectly
# attached to some fixed part, and treat the rest as PlaneCoincident
# with locked angle.
from .assembly import setPlacement
handled = set()
while True:
nxt = []
for obj in attachments:
cstr = obj.Proxy
firstInfo = []
infos = []
if obj.Cascade:
# for cascading attachment, we should handle it if there is
# any fixed part inside. We also need to sort the element
# pairs properly so that they can be recognized as fixed one
# after another
prev = None
fixed = None
for e in cstr.getElements():
info = e.Proxy.getInfo()
if prev and prev.Part!=info.Part:
firstInfo.append(prev)
infos.append(info)
if fixed is None \
and (prev.Part in ret or info.Part in ret):
fixed = len(firstInfo)
prev = info
if fixed is None:
continue
if fixed:
# reorder the element pairs to put the pair with the
# fixed part first, and then reversed the ones that are
# before it.
firstInfo = firstInfo[fixed:] +\
list(reversed(firstInfo[:fixed]))
infos = infos[fixed:] + list(reversed(infos[:fixed]))
else:
if not mcs.canMultiply(obj):
infos = cstr.getElementsInfo()
firstInfo = [infos[0]]*(len(infos)-1)
infos = infos[1:]
else:
elements = cstr.getElements()
firstInfo = elements[0].Proxy.getInfo(expand=True)
infos = []
for element in elements[1:]:
infos += element.Proxy.getInfo(expand=True)
count = min(len(infos),len(firstInfo))
firstInfo = firstInfo[:count]
infos = infos[:count]
if any([ not (info0.Part in ret or info.Part in ret)
for info0,info in zip(firstInfo,infos)]):
# we only handle if all element pair has at least one
# part that are fixed
continue
handled.add(obj)
for info0,info in zip(firstInfo,infos):
if info.Part in ret:
if info0.Part in ret:
logger.warn('skip fixed part "{}" and "{}" in {}',
info.PartName,info0.PartName,cstrName(obj))
continue
info0,info = info,info0
ret.add(info.Part)
pla0 = info0.Placement.multiply(
utils.getElementPlacement(info0.Shape))
pla = pla0.multiply(
utils.getElementPlacement(info.Shape).inverse())
if not utils.isSamePlacement(pla,info.Placement):
solver.touched = True
solver.system.log('attaching "{}" -> "{}"',
info.PartName, info0.PartName)
if rollback is not None:
rollback.append((info.PartName,
info.Part,
info.Placement.copy()))
setPlacement(info.Part,pla)
if len(nxt) == len(attachments):
break
attachments = nxt
if handled:
cstrs[:] = [ obj for obj in cstrs if obj not in handled ]
return ret
@classmethod
@ -1087,8 +1191,10 @@ class BaseMulti(Base):
elements.append(e)
if len(elements)<=1:
logger.warn('{} has no effective constraint',cstrName(obj))
logger.warn('{} has no effective constraining element',
cstrName(obj))
return
e0 = None
e = None
info0 = None
@ -1171,6 +1277,16 @@ class PlaneCoincident(BaseCascade):
'The faces are coincided at their centers with an optional distance.'
class Attachment(BaseCascade):
_id = 45
_iconName = 'Assembly_ConstraintAttachment.svg'
_props = ['Multiply', 'Cascade']
_tooltip = \
'Add a "{}" constraint to attach two parts by the selected geometry\n'\
'elements. This constraint completely fixes the parts realtive to each\n'\
'other.'
class AxialAlignment(BaseMulti):
_id = 36
_entityDef = (_lna,)

View File

@ -43,8 +43,10 @@ class Solver(object):
self._partMap = {}
self._cstrMap = {}
self._cstrArrayMap = defaultdict(int)
self._fixedParts = set()
self._fixedElements = set()
self._dragPart = dragPart
self.touched = False
self.system.GroupHandle = self._fixedGroup
@ -64,10 +66,20 @@ class Solver(object):
self.ny = self.system.addNormal3dV(*utils.getNormal(roty))
partGroup = assembly.Proxy.getPartGroup()
self._fixedParts = Constraint.getFixedParts(self,cstrs,partGroup)
self._fixedParts = Constraint.getFixedParts(
self,cstrs,partGroup,rollback)
for part in self._fixedParts:
self._fixedElements.add((part,None))
if self.touched:
if not assembly.recompute(True):
raise RuntimeError(
'Failed to recompute {}'.format(objName(assembly)))
if not cstrs:
self.system.log('no constraints')
return
for cstr in cstrs:
self.system.log('preparing {}',cstrName(cstr))
self.system.GroupHandle += 1

View File

@ -221,6 +221,9 @@ class SystemExtension(object):
return self.setOrientation(h, lockAngle, yaw, pitch, roll,
pln1.normal, pln2.normal, group)
def addAttachment(self, pln1, pln2, group=0):
return self.addPlaneCoincident(0,0,0,False,0,0,0, pln1, pln2, group)
def addPlaneAlignment(self,d,lockAngle,yaw,pitch,roll,pln1,pln2,group=0):
if not group:
group = self.GroupHandle