FreeCAD_assembly3/solver.py
Zheng, Lei b5a91ec889 Basic function working
Tested PointsCoincident constraint
2017-10-31 04:05:01 +08:00

154 lines
5.6 KiB
Python

import FreeCAD, FreeCADGui
import asm3.slvs as slvs
import asm3.assembly as asm
from asm3.utils import logger, objName, isSamePlacement
import asm3.constraint as constraint
class AsmSolver(object):
def __init__(self,assembly,reportFailed):
cstrs = assembly.Proxy.getConstraints()
if not cstrs:
logger.debug('no constraint found in assembly '
'{}'.format(objName(assembly)))
return
parts = assembly.Proxy.getPartGroup().Group
if len(parts)<=1:
logger.debug('not enough parts in {}'.format(objName(assembly)))
return
self.system = slvs.System()
self._fixedGroup = 2
self.system.GroupHandle = 3 # the import group
self.group = 1 # the solving group
self._partMap = {}
self._cstrMap = {}
self._entityMap = {}
self._fixedPart = parts[0]
for cstr in cstrs:
logger.debug('preparing {}, type {}'.format(
objName(cstr),cstr.Type))
self._cstrMap[constraint.prepare(cstr,self)] = cstr
logger.debug('solving {}'.format(objName(assembly)))
ret = self.system.solve(group=self.group,reportFailed=reportFailed)
if ret:
if reportFailed:
msg = 'List of failed constraint:'
for f in self.system.Failed:
msg += '\n' + objName(self._cstrMap[f])
logger.error(msg)
raise RuntimeError('Failed to solve the constraints ({}) '
'in {}'.format(ret,objName(assembly)))
logger.debug('done sloving, dof {}'.format(self.system.Dof))
undoDocs = set()
for part,partInfo in self._partMap.items():
if part == self._fixedPart:
continue
params = [ self.system.getParam(h).val for h in partInfo.Params ]
p = params[:3]
q = [params[4],params[5],params[6],params[3]]
pla = FreeCAD.Placement(FreeCAD.Vector(*p),FreeCAD.Rotation(*q))
if isSamePlacement(partInfo.Placement,pla):
logger.debug('not moving {}'.format(partInfo.PartName))
else:
logger.debug('moving {} {} {} {}'.format(
partInfo.PartName,partInfo.Params,params,pla))
asm.AsmElementLink.setPlacement(part,pla,undoDocs)
for doc in undoDocs:
doc.commitTransaction()
def getPartInfo(self,info):
partInfo = self._partMap.get(info.Part,None)
if partInfo:
return partInfo
# info.Object below is supposed to be the actual part geometry, while
# info.Part may be a link to that object. We use info.Object as a key so
# that multiple info.Part can share the same entity map.
#
# TODO: It is actually more complicated than that. Becuase info.Object
# itself may be a link, and followed by a chain of multiple links. It's
# complicated because each link can either have a linked placement or
# not, depends on its LinkTransform property, meaning that their
# Placement may be chained or independent. Ideally, we should explode
# the link chain, and recreate the transformation dependency using slvs
# transfomation entity. We'll leave that for another day, maybe...
#
# The down side for now is that we may have redundant entities, and
# worse, the solver may not be able to get correct result if there are
# placement dependent links among parts. So, one should avoid using
# LinkTransform in most cases.
#
entityMap = self._entityMap.get(info.Object,None)
if not entityMap:
entityMap = {}
self._entityMap[info.Object] = entityMap
if info.Part == self._fixedPart:
g = self._fixedGroup
else:
g = self.group
q = info.Placement.Rotation.Q
vals = list(info.Placement.Base) + [q[3],q[0],q[1],q[2]]
params = [self.system.addParamV(v,g) for v in vals]
p = self.system.addPoint3d(*params[:3],group=g)
n = self.system.addNormal3d(*params[3:],group=g)
w = self.system.addWorkplane(p,n,group=g)
h = (w,p,n)
logger.debug('{} {}, {}, {}, {}'.format(
info.PartName,info.Placement,h,params,vals))
partInfo = constraint.PartInfo(
info.PartName, info.Placement.copy(),params,h,entityMap)
self._partMap[info.Part] = partInfo
return partInfo
def solve(objs=None,recursive=True,reportFailed=True,recompute=True):
if not objs:
objs = FreeCAD.ActiveDocument.Objects
elif not isinstance(objs,(list,tuple)):
objs = [objs]
if not objs:
logger.error('no objects')
return
if recompute:
docs = set()
for o in objs:
docs.add(o.Document)
for d in docs:
logger.debug('recomputing {}'.format(d.Name))
d.recompute()
if recursive:
# Get all dependent object, including external ones, and return as a
# topologically sorted list.
objs = FreeCAD.getDependentObjects(objs,False,True)
assemblies = []
for obj in objs:
if asm.isTypeOf(obj,asm.Assembly):
logger.debug('adding assembly {}'.format(objName(obj)))
assemblies.append(obj)
if not assemblies:
logger.error('no assembly found')
return
for assembly in assemblies:
logger.debug('solving assembly {}'.format(objName(assembly)))
AsmSolver(assembly,reportFailed)
if recompute:
assembly.Document.recompute()