constraint: optimize constraint multiplication

This commit is contained in:
Zheng, Lei 2018-08-17 19:10:03 +08:00
parent 677ba607f1
commit 7d4b184394
4 changed files with 101 additions and 26 deletions

View File

@ -365,12 +365,20 @@ class AsmElement(AsmBase):
else:
parentShape = Part.getShape(info.Part, info.Subname,
transform=False, needSubElement=False)
shapes = []
found = False
shapes = [info.Shape]
pla = info.Shape.Placement
for edge in parentShape.Edges:
if info.Shape.isCoplanar(edge) and \
utils.isSameValue(
if not info.Shape.isCoplanar(edge) or \
not utils.isSameValue(
utils.getElementCircular(edge,True),obj.Radius):
edge.transformShape(mat,True)
continue
edge.transformShape(mat,True)
if not found and utils.isSamePlacement(pla,edge.Placement):
found = True
# make sure the direct referenced edge is the first one
shapes[0] = edge
else:
shapes.append(edge)
shape = shapes

View File

@ -934,25 +934,43 @@ class BaseMulti(Base):
logger.warn('{} no first part shape'.format(cstrName(obj)))
return
idx = 0
updates = []
for element in elements[1:]:
for info in element.Proxy.getInfo(expand=True):
info0 = firstInfo[idx]
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 idx >= count:
return ret
infos = element.Proxy.getInfo(expand=True)
if not infos:
continue
info0 = firstInfo[idx]
partInfo0 = solver.getPartInfo(info0,infos)
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))
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
return ret
parts = set()

View File

@ -1,5 +1,5 @@
import random, math
from collections import namedtuple
from collections import namedtuple,defaultdict
import FreeCAD, FreeCADGui
from .assembly import Assembly, isTypeOf, setPlacement
from . import utils
@ -21,8 +21,12 @@ from .system import System
# CstrMap: map from other part to the constrains between this and the othe 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
# first element of all the coplanar edges will be actually constrainted.
# The rest ElementInfo will be stored here for later update by matrix
# transformation.
PartInfo = namedtuple('SolverPartInfo', ('Part','PartName','Placement',
'Params','Workplane','EntityMap','Group','CstrMap'))
'Params','Workplane','EntityMap','Group','CstrMap','Update'))
class Solver(object):
def __init__(self,assembly,reportFailed,dragPart,recompute,rollback):
@ -116,6 +120,7 @@ class Solver(object):
self.system.log('done solving')
touched = False
updates = []
for part,partInfo in self._partMap.items():
if part in self._fixedParts:
continue
@ -147,6 +152,8 @@ class Solver(object):
touched = True
part.Points = points
else:
if partInfo.Update:
updates.append(partInfo)
params = [self.system.getParam(h).val for h in partInfo.Params]
p = params[:3]
q = (params[4],params[5],params[6],params[3])
@ -161,6 +168,8 @@ class Solver(object):
rollback.append((partInfo.PartName,
part,
partInfo.Placement.copy()))
partInfo.Placement.Base = pla.Base
partInfo.Placement.Rotation = pla.Rotation
setPlacement(part,pla)
if utils.isDraftCircle(part):
@ -198,6 +207,45 @@ class Solver(object):
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.
touches = defaultdict(set)
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))
def isFixedPart(self,part):
if isinstance(part,tuple) and part[0] in self._fixedParts:
return True
@ -257,7 +305,8 @@ class Solver(object):
Workplane = h,
EntityMap = {},
Group = group if group else g,
CstrMap = {})
CstrMap = {},
Update = [])
self.system.log('{}, {}'.format(partInfo,g))

View File

@ -138,7 +138,7 @@ class SystemExtension(object):
if not yaw and not pitch and not roll:
n = n2.entity
else:
rot = n2.rot.multiply(FreeCAD.Rotation(yaw,pitch,roll))
rot = FreeCAD.Rotation(yaw,pitch,roll).multiply(n2.rot)
e = self.addNormal3dV(*getNormal(rot))
n = self.addTransform(e,*n2.params,group=group)
h.append(self.addSameOrientation(n1.entity,n,group=group))