from collections import namedtuple from PySide.QtCore import Qt from PySide.QtGui import QIcon, QPainter, QPixmap import FreeCAD, FreeCADGui import asm3.utils as utils import asm3.slvs as slvs from asm3.utils import logger, objName import os iconPath = os.path.join(utils.iconPath,'constraints') pixmapDisabled = QPixmap(os.path.join( iconPath,'Assembly_ConstraintDisabled.svg')) iconSize = (16,16) Types = [] TypeMap = {} TypeNameMap = {} class ConstraintType(type): def __init__(cls, name, bases, attrs): super(ConstraintType,cls).__init__(name,bases,attrs) if cls._id >= 0: if cls._id in TypeMap: raise RuntimeError( 'Duplicate constriant type id {}'.format(cls._id)) if cls.slvsFunc(): TypeMap[cls._id] = cls TypeNameMap[cls.getName()] = cls cls._idx = len(Types) logger.debug('register constraint "{}":{},{}'.format( cls.getName(),cls._id,cls._idx)) Types.append(cls) # PartName: text name of the part # Placement: the original placement of the part # Params: 7 parameters that defines the transformation # Workplane: a tuple of three entity handles, that is the workplane, the origin # point, and the normal. The workplane, defined by the origin and # norml, is essentially the XY reference plane of the part. # EntityMap: string -> entity handle map, for caching # Group: transforming entity group handle PartInfo = namedtuple('SolverPartInfo', ('PartName','Placement','Params','Workplane','EntityMap','Group')) def _addEntity(etype,system,partInfo,key,shape): key += '.{}'.format(etype) h = partInfo.EntityMap.get(key,None) if h: logger.debug('cache {}: {}'.format(key,h)) return h if etype == 'p': # point v = utils.getElementPos(shape) e = system.addPoint3dV(*v) elif etype == 'n': # normal v = utils.getElementNormal(shape) e = system.addNormal3dV(*v) else: raise RuntimeError('unknown entity type {}'.format(etype)) h = system.addTransform(e,*partInfo.Params,group=partInfo.Group) logger.debug('{}: {},{}, {}, {}'.format(key,h,partInfo.Group,e,v)) partInfo.EntityMap[key] = h return h def _p(system,partInfo,key,shape): 'return a slvs handle of a transformed point derived from "shape"' if not system: if utils.hasCenter(shape): return return 'a vertex or circular edge/face' return _addEntity('p',system,partInfo,key,shape) def _n(system,partInfo,key,shape): 'return a slvs handle of a transformed normal derived from "shape"' if not system: if utils.isAxisOfPlane(shape): return return 'an edge or face with a surface normal' return _addEntity('n',system,partInfo,key,shape) def _l(system,partInfo,key,shape,retAll=False): 'return a pair of slvs handle of the end points of an edge in "shape"' if not system: if utils.isLinearEdge(shape): return return 'a linear edge' key += '.l' h = partInfo.EntityMap.get(key,None) if h: logger.debug('cache {}: {}'.format(key,h)) else: v = shape.Edges[0].Vertexes p1 = system.addPoint3dV(*v[0].Point) p2 = system.addPoint3dV(*v[-1].Point) h = system.addLine(p1,p2,group=partInfo.Group) h = (h,p1,p2) logger.debug('{}: {},{}'.format(key,h,partInfo.Group)) partInfo.EntityMap[key] = h return h if retAll else h[0] def _w(system,partInfo,key,shape,retAll=False): 'return a slvs handle of a transformed plane/workplane from "shape"' if not system: if utils.isAxisOfPlane(shape): return return 'an edge or face with a planar surface' key2 = key+'.w' h = partInfo.EntityMap.get(key2,None) if h: logger.debug('cache {}: {}'.format(key,h)) else: p = _p(system,partInfo,key,shape) n = _n(system,partInfo,key,shape) h = system.addWorkplane(p,n,group=partInfo.Group) h = (h,p,n) logger.debug('{}: {},{}'.format(key,h,partInfo.Group)) partInfo.EntityMap[key2] = h return h if retAll else h[0] def _c(system,partInfo,key,shape,requireArc=False): 'return a slvs handle of a transformed circle/arc derived from "shape"' if not system: r = utils.getElementCircular(shape) if not r or (requireArc and not isinstance(r,list,tuple)): return return 'an cicular arc edge' if requireArc else 'a circular edge' key2 = key+'.c' h = partInfo.EntityMap.get(key2,None) if h: logger.debug('cache {}: {}'.format(key,h)) else: h = _w(system,partInfo,key,shape,True) r = utils.getElementCircular(shape) if not r: raise RuntimeError('shape is not cicular') if isinstance(r,(list,tuple)): l = _l(system,partInfo,key,shape,True) h += l[1:] h = system.addArcOfCircleV(*h,group=partInfo.Group) elif requireArc: raise RuntimeError('shape is not an arc') else: h = h[1:] h.append(system.addDistanceV(r)) h = system.addCircle(*h,group=partInfo.Group) logger.debug('{}: {},{} {}'.format(key,h,partInfo.Group,r)) partInfo.EntityMap[key2] = h return h def _a(system,partInfo,key,shape): return _c(system,partInfo,key,shape,True) _PropertyDistance = ('Value','Distance','PropertyDistance','Constraint') _PropertyAngle = ('Value','Angle','PropertyAngle','Constraint') _PropertyRatio = (None,'Ratio','PropertyFloat','Constraint') _PropertyDifference = (None,'Difference','PropertyFloat','Constraint') _PropertyDiameter = (None,'Diameter','PropertyFloat','Constraint') _PropertyRadius = (None,'Radius','PropertyFloat','Constraint') _PropertySupplement = (None,'Supplement','PropertyBool','Constraint', 'If True, then the second angle is calculated as 180-angle') _PropertyAtEnd = (None,'AtEnd','PropertyBool','Constraint', 'If True, then tangent at the end point, or else at the start point') _ordinal = ['1st', '2nd', '3rd', '4th', '5th', '6th', '7th' ] class Base: __metaclass__ = ConstraintType _id = -1 _entities = [] _workplane = False _props = [] _func = None _icon = None _iconDisabled = None _iconName = 'Assembly_ConstraintGeneral.svg' def __init__(self,obj,props): if obj._Type != self._id: if self._id < 0: raise RuntimeError('invalid constraint type {} id: ' '{}'.format(self.__class__,self._id)) obj._Type = self._id for prop in self.__class__._props: if prop[1] not in props: obj.addProperty(*prop[1:]) else: obj.setPropertyStatus(prop[1],'-Hidden') @classmethod def getName(cls): return cls.__name__ @classmethod def slvsFunc(cls): try: if not cls._func: cls._func = getattr(slvs.System,'add'+cls.getName()) return cls._func except AttributeError: logger.error('Invalid slvs constraint "{}"'.format(cls.getName())) @classmethod def getEntityDef(cls,group,checkCount,name=None): entities = cls._entities if len(group) != len(entities): if not checkCount and len(group)