from collections import namedtuple import FreeCAD, FreeCADGui from . import utils, gui from .utils import objName,cstrlogger as logger, guilogger from .proxy import ProxyType, PropertyInfo, propGet, propGetValue import os _iconPath = os.path.join(utils.iconPath,'constraints') def _p(solver,partInfo,subname,shape,retAll=False): 'return a handle of a transformed point derived from "shape"' if not solver: if not utils.hasCenter(shape): return 'a vertex or circular edge/face' if not utils.isDraftWire(partInfo): return if utils.draftWireVertex2PointIndex(partInfo,subname) is None: raise RuntimeError('Invalid draft wire vertex "{}" {}'.format( subname,objName(partInfo))) return key = subname+'.p' h = partInfo.EntityMap.get(key,None) system = solver.system if h: system.log('cache {}: {}'.format(key,h)) else: v = utils.getElementPos(shape) nameTag = partInfo.PartName + '.' + key if utils.isDraftWire(partInfo.Part): v = partInfo.Placement.multVec(v) params = [] for n,val in (('.x',v.x),('.y',v.y),('.z',v.z)): system.NameTag = nameTag+n params.append(system.addParamV(val,group=partInfo.Group)) system.NameTag = nameTag e = system.addPoint3d(*params) h = [e, params] system.log('{}: add draft point {},{}'.format(key,h,v)) else: system.NameTag = nameTag e = system.addPoint3dV(*v) system.NameTag = nameTag + 't' h = system.addTransform(e[0],*partInfo.Params,group=partInfo.Group) h = [h,e] system.log('{}: {},{}'.format(key,h,partInfo.Group)) partInfo.EntityMap[key] = h return h if retAll else h[0] def _n(solver,partInfo,subname,shape,retAll=False): 'return a handle of a transformed normal quaterion derived from shape' if not solver: if not utils.isPlanar(shape): return 'an edge or face with a surface normal' if utils.isDraftWire(partInfo): logger.warn('Use draft wire {} for normal. Draft wire placement' ' is not transformable'.format(objName(partInfo))) return key = subname+'.n' h = partInfo.EntityMap.get(key,None) system = solver.system if h: system.log('cache {}: {}'.format(key,h)) else: h = [] rot = utils.getElementRotation(shape) nameTag = partInfo.PartName + '.' + key system.NameTag = nameTag e = system.addNormal3dV(*utils.getNormal(rot)) system.NameTag += 't' h.append(system.addTransform(e,*partInfo.Params,group=partInfo.Group)) # also add x axis pointing quaterion for convenience rot = FreeCAD.Rotation(FreeCAD.Vector(0,1,0),90).multiply(rot) system.NameTag = nameTag + 'x' e = system.addNormal3dV(*utils.getNormal(rot)) system.NameTag = nameTag + 'xt' h.append(system.addTransform(e,*partInfo.Params,group=partInfo.Group)) system.log('{}: {},{}'.format(key,h,partInfo.Group)) partInfo.EntityMap[key] = h return h if retAll else h[0] def _l(solver,partInfo,subname,shape,retAll=False): 'return a pair of handle of the end points of an edge in "shape"' if not solver: if not utils.isLinearEdge(shape): return 'a linear edge' if not utils.isDraftWire(partInfo): return part = partInfo vname1,vname2 = utils.edge2VertexIndex(subname) if not vname1: raise RuntimeError('Invalid draft subname {} or {}'.format( subname,objName(part))) v = shape.Edges[0].Vertexes return _p(solver,partInfo,vname1,v[0]) or \ _p(solver,partInfo,vname2,v[1]) key = subname+'.l' h = partInfo.EntityMap.get(key,None) system = solver.system if h: system.log('cache {}: {}'.format(key,h)) else: nameTag = partInfo.PartName + '.' + key v = shape.Edges[0].Vertexes if utils.isDraftWire(partInfo.Part): vname1,vname2 = utils.edge2VertexIndex(subname) if not vname1: raise RuntimeError('Invalid draft subname {} or {}'.format( subname,objName(partInfo.Part))) tp1 = _p(solver,partInfo,vname1,v[0]) tp2 = _p(solver,partInfo,vname2,v[1]) else: system.NameTag = nameTag + 'p1' p1 = system.addPoint3dV(*v[0].Point) system.NameTag = nameTag + 'p1t' tp1 = system.addTransform(p1,*partInfo.Params,group=partInfo.Group) system.NameTag = nameTag + 'p2' p2 = system.addPoint3dV(*v[-1].Point) system.NameTag = nameTag + 'p2t' tp2 = system.addTransform(p2,*partInfo.Params,group=partInfo.Group) system.NameTag = nameTag h = system.addLineSegment(tp1,tp2,group=partInfo.Group) h = (h,tp1,tp2) system.log('{}: {},{}'.format(key,h,partInfo.Group)) partInfo.EntityMap[key] = h return h if retAll else h[0] def _ln(solver,partInfo,subname,shape,retAll=False): 'return a handle for either a line or a normal depends on the shape' if not solver: if utils.isLinearEdge(shape) or utils.isPlanar(shape): return return 'a linear edge or edge/face with planar surface' if utils.isLinearEdge(shape): return _l(solver,partInfo,subname,shape,retAll) return _n(solver,partInfo,subname,shape) def _w(solver,partInfo,subname,shape,retAll=False): 'return a handle of a transformed plane/workplane from "shape"' if not solver: if utils.isPlanar(shape): return return 'an edge/face with a planar surface' key = subname+'.w' h = partInfo.EntityMap.get(key,None) system = solver.system if h: system.log('cache {}: {}'.format(key,h)) else: p = _p(solver,partInfo,subname,shape) n = _n(solver,partInfo,subname,shape,True) system.NameTag = partInfo.PartName + '.' + key h = system.addWorkplane(p,n[0],group=partInfo.Group) h = [h,p] + n system.log('{}: {},{}'.format(key,h,partInfo.Group)) partInfo.EntityMap[key] = h return h if retAll else h[0] def _wa(solver,partInfo,subname,shape,retAll=False): _ = retAll return _w(solver,partInfo,subname,shape,True) def _c(solver,partInfo,subname,shape,requireArc=False,retAll=False): 'return a handle of a transformed circle/arc derived from "shape"' if not solver: r = utils.getElementCircular(shape) if r: return return 'an cicular edge' if requireArc: key = subname+'.a' else: key = subname+'.c' h = partInfo.EntityMap.get(key,None) system = solver.system if h: system.log('cache {}: {}'.format(key,h)) else: w,p,n,_ = _w(solver,partInfo,subname,shape,True) r = utils.getElementCircular(shape) if not r: raise RuntimeError('shape is not cicular') nameTag = partInfo.PartName + '.' + key system.NameTag = nameTag + '.r' hr = system.addDistanceV(r) if requireArc or isinstance(r,(list,tuple)): l = _l(solver,partInfo,subname,shape,True) system.NameTag = nameTag h = system.addArcOfCircle(w,p,l[1],l[2],group=partInfo.Group) else: system.NameTag = nameTag h = system.addCircle(p,n,hr,group=partInfo.Group) h = (h,hr) system.log('{}: {},{}'.format(key,h,partInfo.Group)) partInfo.EntityMap[key] = h return h if retAll else h[0] def _a(solver,partInfo,subname,shape,retAll=False): 'return a handle of a transformed arc derived from "shape"' return _c(solver,partInfo,subname,shape,True,retAll) class ConstraintCommand: _menuGroupName = '' def __init__(self,tp): self.tp = tp self._id = 100 + tp._id self._active = None @property def _toolbarName(self): return self.tp._toolbarName def workbenchActivated(self): pass def workbenchDeactivated(self): self._active = None def getContextMenuName(self): pass def getName(self): return 'asm3Add'+self.tp.getName() def GetResources(self): return self.tp.GetResources() def Activated(self): from .assembly import AsmConstraint guilogger.report('constraint "{}" command exception'.format( self.tp.getName()), AsmConstraint.make,self.tp._id) def IsActive(self): if not FreeCAD.ActiveDocument: return False if self._active is None: self.checkActive() return self._active def checkActive(self): from .assembly import AsmConstraint if guilogger.catchTrace('selection "{}" exception'.format( self.tp.getName()), AsmConstraint.getSelection, self.tp._id): self._active = True else: self._active = False def onClearSelection(self): self._active = False class Constraint(ProxyType): 'constraint meta class' _typeID = '_ConstraintType' _typeEnum = 'ConstraintType' _disabled = 'Disabled' @classmethod def register(mcs,cls): super(Constraint,mcs).register(cls) if cls._id>=0 and cls._iconName is not Base._iconName: gui.AsmCmdManager.register(ConstraintCommand(cls)) @classmethod def attach(mcs,obj,checkType=True): if checkType: if not mcs._disabled in obj.PropertiesList: obj.addProperty("App::PropertyBool",mcs._disabled,"Base",'') return super(Constraint,mcs).attach(obj,checkType) @classmethod def onChanged(mcs,obj,prop): if prop == mcs._disabled: obj.ViewObject.signalChangeIcon() return return super(Constraint,mcs).onChanged(obj,prop) @classmethod def isDisabled(mcs,obj): return getattr(obj,mcs._disabled,False) @classmethod def check(mcs,tp,group,checkCount=False): mcs.getType(tp).check(group,checkCount) @classmethod def prepare(mcs,obj,solver): return mcs.getProxy(obj).prepare(obj,solver) @classmethod def getFixedParts(mcs,cstrs): firstPart = None firstPartName = None found = False ret = set() for obj in cstrs: cstr = mcs.getProxy(obj) if cstr.hasFixedPart(obj): found = True for info in cstr.getFixedParts(obj): logger.debug('fixed part ' + info.PartName) ret.add(info.Part) if not found and not firstPart: elements = obj.Proxy.getElements() if elements: info = elements[0].Proxy.getInfo() firstPart = info.Part firstPartName = info.PartName if not found: if not firstPart: return None logger.debug('lock first part {}'.format(firstPartName)) ret.add(firstPart) return ret @classmethod def getFixedTransform(mcs,cstrs): firstPart = None found = False ret = {} for obj in cstrs: cstr = mcs.getProxy(obj) if cstr.hasFixedPart(obj): for info in cstr.getFixedTransform(obj): found = True ret[info.Part] = info if not found and not firstPart: elements = obj.Proxy.getElements() if elements: info = elements[0].Proxy.getInfo() firstPart = info.Part if not found and firstPart: ret[firstPart] = False return ret @classmethod def getIcon(mcs,obj): cstr = mcs.getProxy(obj) if cstr: return cstr.getIcon(obj) def _makeProp(name,tp,doc='',getter=propGet,internal=False): PropertyInfo(Constraint,name,tp,doc,getter=getter, group='Constraint',internal=internal) _makeProp('Distance','App::PropertyDistance',getter=propGetValue) _makeProp('Length','App::PropertyDistance',getter=propGetValue) _makeProp('Offset','App::PropertyDistance',getter=propGetValue) _makeProp('Cascade','App::PropertyBool',internal=True) _makeProp('Angle','App::PropertyAngle',getter=propGetValue) _makeProp('LockAngle','App::PropertyBool') _makeProp('Ratio','App::PropertyFloat') _makeProp('Difference','App::PropertyFloat') _makeProp('Diameter','App::PropertyFloat') _makeProp('Radius','App::PropertyFloat') _makeProp('Supplement','App::PropertyBool', 'If True, then the second angle is calculated as 180-angle') _makeProp('AtEnd','App::PropertyBool', 'If True, then tangent at the end point, or else at the start point') _ordinal = ('1st', '2nd', '3rd', '4th', '5th', '6th', '7th') def cstrName(obj): return '{}<{}>'.format(objName(obj),Constraint.getTypeName(obj)) class Base(object): __metaclass__ = Constraint _id = -1 _entityDef = () _workplane = False _props = [] _toolbarName = 'Assembly3 Constraints' _iconName = 'Assembly_ConstraintGeneral.svg' _menuText = 'Create "{}" constraint' def __init__(self,_obj): pass @classmethod def getPropertyInfoList(cls): return cls._props @classmethod def constraintFunc(cls,obj,solver): try: return getattr(solver.system,'add'+cls.getName()) except AttributeError: logger.warn('{} not supported in solver "{}"'.format( cstrName(obj),solver.getName())) @classmethod def getEntityDef(cls,group,checkCount,obj=None): entities = cls._entityDef if len(group) == len(entities): return entities if cls._workplane and len(group)==len(entities)+1: return list(entities) + [_w] if not checkCount and len(group)0 @classmethod def prepare(cls,obj,solver): ret = [] system = solver.system for element in obj.Proxy.getElements(): info = element.Proxy.getInfo() isVertex = utils.isVertex(info.Shape) if not isVertex and not utils.isLinearEdge(info.Shape): continue if solver.isFixedPart(info): logger.warn('redundant locking element "{}" in constraint ' '{}'.format(info.Subname,objName(obj))) continue partInfo = solver.getPartInfo(info) fixPoint = False if isVertex: names = [info.Subname] elif utils.isDraftObject(info): fixPoint = True names = utils.edge2VertexIndex(info.Subname) else: names = [info.Subname+'.fp1', info.Subname+'.fp2'] nameTag = partInfo.PartName + '.' + info.Subname for i,v in enumerate(info.Shape.Vertexes): surfix = '.fp{}'.format(i) system.NameTag = nameTag + surfix # Create an entity for the transformed constant point e1 = system.addPoint3dV(*info.Placement.multVec(v.Point)) # Get the entity for the point expressed in variable parameters e2 = _p(solver,partInfo,names[i],v) if i==0 or fixPoint: # We are fixing a vertex, or a linear edge. Either way, we # shall add a point coincidence constraint here. e0 = e1 system.NameTag = nameTag + surfix e = system.addPointsCoincident(e1,e2,group=solver.group) system.log('{}: fix point {},{},{}'.format( cstrName(obj),e,e1,e2)) else: # The second point, so we are fixing a linear edge. We can't # add a second coincidence constraint, which will cause # over-constraint. We constraint the second point to be on # the line defined by the linear edge. # # First, get an entity of the transformed constant line system.NameTag = nameTag + '.fl' l = system.addLineSegment(e0,e1) system.NameTag = nameTag # Now, constraint the second variable point to the line e = system.addPointOnLine(e2,l,group=solver.group) system.log('{}: fix line {},{}'.format(cstrName(obj),e,l)) ret.append(e) return ret @classmethod def check(cls,group,_checkCount=False): if not all([utils.isElement(o) for o in group]): raise RuntimeError('Constraint "{}" requires all children to be ' 'of element (Vertex, Edge or Face)'.format(cls.getName())) class BaseMulti(Base): _id = -1 _entityDef = (_wa,) @classmethod def check(cls,group,_checkCount=False): if len(group)<2: raise RuntimeError('Constraint "{}" requires at least two ' 'elements'.format(cls.getName())) for o in group: if isinstance(o,utils.PartInfo): msg = cls._entityDef[0](None,o.Part,o.Subname,o.Shape) else: msg = cls._entityDef[0](None,None,None,o) if msg: raise RuntimeError('Constraint "{}" requires all the element ' 'to be of {}'.format(cls.getName())) return @classmethod def prepare(cls,obj,solver): func = cls.constraintFunc(obj,solver); if not func: logger.warn('{} no constraint func'.format(cstrName(obj))) return parts = set() ref = None elements = [] props = cls.getPropertyValues(obj) for e in obj.Proxy.getElements(): info = e.Proxy.getInfo() if info.Part in parts: logger.warn('{} skip duplicate parts {}'.format( cstrName(obj),info.PartName)) continue parts.add(info.Part) if solver.isFixedPart(info): if ref: logger.warn('{} skip more than one fixed part {}'.format( cstrName(obj),info.PartName)) continue ref = info elements.insert(0,e) else: elements.append(e) if len(elements)<=1: logger.warn('{} has no effective constraint'.format(cstrName(obj))) return e0 = None ret = [] for e in elements: info = e.Proxy.getInfo() partInfo = solver.getPartInfo(info) if not e0: e0 = cls._entityDef[0](solver,partInfo,info.Subname,info.Shape) else: e = cls._entityDef[0](solver,partInfo,info.Subname,info.Shape) params = props + [e0,e] h = func(*params,group=solver.group) if isinstance(h,(list,tuple)): ret += list(h) else: ret.append(h) return ret class BaseCascade(BaseMulti): @classmethod def prepare(cls,obj,solver): if not getattr(obj,'Cascade',True): return super(BaseCascade,cls).prepare(obj,solver) func = cls.constraintFunc(obj,solver); if not func: logger.warn('{} no constraint func'.format(cstrName(obj))) return props = cls.getPropertyValues(obj) prev = None ret = [] for e in obj.Proxy.getElements(): info = e.Proxy.getInfo() if not prev or prev.Part==info.Part: prev = info continue prevInfo = solver.getPartInfo(prev) e1 = cls._entityDef[0](solver,prevInfo,prev.Subname,prev.Shape) partInfo = solver.getPartInfo(info) e2 = cls._entityDef[0](solver,partInfo,info.Subname,info.Shape) prev = info if solver.isFixedPart(info): params = props + [e1,e2] else: params = props + [e2,e1] h = func(*params,group=solver.group) if isinstance(h,(list,tuple)): ret += list(h) else: ret.append(h) if not ret: logger.warn('{} has no effective constraint'.format(cstrName(obj))) return ret class PlaneCoincident(BaseCascade): _id = 35 _iconName = 'Assembly_ConstraintCoincidence.svg' _props = ['Cascade','Offset','LockAngle','Angle'] _tooltip = \ 'Add a "{}" constraint to conincide planes of two or more parts.\n'\ 'The planes are coincided at their centers with an optional distance.' class PlaneAlignment(BaseCascade): _id = 37 _iconName = 'Assembly_ConstraintAlignment.svg' _props = ['Cascade','Offset','LockAngle','Angle'] _tooltip = 'Add a "{}" constraint to rotate planes of two or more parts\n'\ 'into the same orientation' class AxialAlignment(BaseMulti): _id = 36 _iconName = 'Assembly_ConstraintAxial.svg' _props = ['LockAngle','Angle'] _tooltip = 'Add a "{}" constraint to align planes of two or more parts.\n'\ 'The planes are aligned at the direction of their surface normal axis.' class SameOrientation(BaseMulti): _id = 2 _entityDef = (_n,) _iconName = 'Assembly_ConstraintOrientation.svg' _tooltip = 'Add a "{}" constraint to align planes of two or more parts.\n'\ 'The planes are aligned to have the same orientation (i.e. rotation)' class MultiParallel(BaseMulti): _id = 291 _entityDef = (_ln,) _iconName = 'Assembly_ConstraintMultiParallel.svg' _props = ['LockAngle','Angle'] _tooltip = 'Add a "{}" constraint to make planes or linear edges of two\n'\ 'or more parts parallel.' class Base2(Base): _id = -1 _toolbarName = 'Assembly3 Constraints2' class Angle(Base2): _id = 27 _entityDef = (_ln,_ln) _workplane = True _props = ["Angle","Supplement"] _iconName = 'Assembly_ConstraintAngle.svg' _tooltip = 'Add a "{}" constraint to set the angle of planes or linear\n'\ 'edges of two parts.' class Perpendicular(Base2): _id = 28 _entityDef = (_ln,_ln) _workplane = True _iconName = 'Assembly_ConstraintPerpendicular.svg' _tooltip = 'Add a "{}" constraint to make planes or linear edges of two\n'\ 'parts perpendicular.' class Parallel(Base2): _id = -1 _entityDef = (_ln,_ln) _workplane = True _iconName = 'Assembly_ConstraintParallel.svg' _tooltip = 'Add a "{}" constraint to make planes or linear edges of two\n'\ 'parts parallel.' class PointsCoincident(Base2): _id = 1 _entityDef = (_p,_p) _workplane = True _iconName = 'Assembly_ConstraintPointsCoincident.svg' _tooltip = 'Add a "{}" constraint to conincide two points.' class PointInPlane(Base2): _id = 3 _entityDef = (_p,_w) _iconName = 'Assembly_ConstraintPointInPlane.svg' _tooltip = 'Add a "{}" to constrain a point inside a plane.' class PointOnLine(Base2): _id = 4 _entityDef = (_p,_l) _workplane = True _iconName = 'Assembly_ConstraintPointOnLine.svg' _tooltip = 'Add a "{}" to constrain a point on to a line.' class PointsDistance(Base2): _id = 5 _entityDef = (_p,_p) _workplane = True _props = ["Distance"] _iconName = 'Assembly_ConstraintPointsDistance.svg' _tooltip = 'Add a "{}" to constrain the distance of two points.' class PointsProjectDistance(Base2): _id = 6 _entityDef = (_p,_p,_ln) _props = ["Distance"] _iconName = 'Assembly_ConstraintPointsProjectDistance.svg' _tooltip = 'Add a "{}" to constrain the distance of two points\n' \ 'projected on a line.' class PointPlaneDistance(Base2): _id = 7 _entityDef = (_p,_w) _props = ["Distance"] _iconName = 'Assembly_ConstraintPointPlaneDistance.svg' _tooltip='Add a "{}" to constrain the distance between a point and a plane' class PointLineDistance(Base2): _id = 8 _entityDef = (_p,_l) _workplane = True _props = ["Distance"] _iconName = 'Assembly_ConstraintPointLineDistance.svg' _tooltip='Add a "{}" to constrain the distance between a point and a line' class EqualPointLineDistance(Base2): _id = 13 _entityDef = (_p,_l,_p,_l) _workplane = True _iconName = 'Assembly_ConstraintEqualPointLineDistance.svg' _tooltip='Add a "{}" to constrain the distance between a point and a\n'\ 'line to be the same as the distance between another point\n'\ 'and line.' class EqualAngle(Base2): _id = 14 _entityDef = (_ln,_ln,_ln,_ln) _workplane = True _props = ["Supplement"] _iconName = 'Assembly_ConstraintEqualAngle.svg' _tooltip='Add a "{}" to equate the angles between two lines or normals.' class Symmetric(Base2): _id = 16 _entityDef = (_p,_p,_w) _workplane = True _iconName = 'Assembly_ConstraintSymmetric.svg' _tooltip='Add a "{}" constraint to make two points symmetric about a plane.' class SymmetricHorizontal(Base2): _id = 17 _entityDef = (_p,_p,_w) class SymmetricVertical(Base2): _id = 18 _entityDef = (_p,_p,_w) class SymmetricLine(Base2): _id = 19 _entityDef = (_p,_p,_l,_w) _iconName = 'Assembly_ConstraintSymmetricLine.svg' _tooltip='Add a "{}" constraint to make two points symmetric about a line.' class PointsHorizontal(Base2): _id = 21 _entityDef = (_p,_p,_w) _iconName = 'Assembly_ConstraintPointsHorizontal.svg' _tooltip='Add a "{}" constraint to make two points horizontal with each\n'\ 'other when projected onto a plane.' class PointsVertical(Base2): _id = 22 _entityDef = (_p,_p,_w) _iconName = 'Assembly_ConstraintPointsVertical.svg' _tooltip='Add a "{}" constraint to make two points vertical with each\n'\ 'other when projected onto a plane.' class LineHorizontal(Base2): _id = 23 _entityDef = (_l,_w) _iconName = 'Assembly_ConstraintLineHorizontal.svg' _tooltip='Add a "{}" constraint to make a line segment horizontal when\n'\ 'projected onto a plane.' class LineVertical(Base2): _id = 24 _entityDef = (_l,_w) _iconName = 'Assembly_ConstraintLineVertical.svg' _tooltip='Add a "{}" constraint to make a line segment vertical when\n'\ 'projected onto a plane.' class PointOnCircle(Base2): _id = 26 _entityDef = (_p,_c) _iconName = 'Assembly_ConstraintPointOnCircle.svg' _tooltip='Add a "{}" to constrain a point on to a clyndrical plane\n' \ 'defined by a cricle.' class ArcLineTangent(Base2): _id = 30 _entityDef = (_a,_l) _props = ["AtEnd"] _iconName = 'Assembly_ConstraintArcLineTangent.svg' _tooltip='Add a "{}" constraint to make a line tangent to an arc\n'\ 'at the start or end point of the arc.' class BaseSketch(Base): _id = -1 _toolbarName = 'Assembly3 Sketch Constraints' class BaseDraftWire(BaseSketch): _id = -1 @classmethod def check(cls,group,checkCount=False): super(BaseDraftWire,cls).check(group,checkCount) if not checkCount: return for o in group: if utils.isDraftWire(o): return raise RuntimeError('Constraint "{}" requires at least one linear edge ' 'from a non-closed-or-subdivided Draft.Wire'.format( cls.getName())) class LineLength(BaseDraftWire): _id = 34 _entityDef = (_l,) _workplane = True _props = ["Length"] _iconName = 'Assembly_ConstraintLineLength.svg' _tooltip='Add a "{}" constrain the length of a non-closed-or-subdivided '\ 'Draft.Wire' @classmethod def prepare(cls,obj,solver): func = PointsDistance.constraintFunc(obj,solver) if func: _,p1,p2 = cls.getEntities(obj,solver,retAll=True)[0] params = cls.getPropertyValues(obj) + [p1,p2] ret = func(*params,group=solver.group) solver.system.log('{}: {}'.format(cstrName(obj),ret)) else: logger.warn('{} no constraint func'.format(cstrName(obj))) class EqualLength(BaseDraftWire): _id = 9 _entityDef = (_l,_l) _workplane = True _iconName = 'Assembly_ConstraintEqualLength.svg' _tooltip='Add a "{}" constraint to make two lines of the same length.' class LengthRatio(BaseDraftWire): _id = 10 _entityDef = (_l,_l) _workplane = True _props = ["Ratio"] _iconName = 'Assembly_ConstraintLengthRatio.svg' _tooltip='Add a "{}" to constrain the length ratio of two lines.' class LengthDifference(BaseDraftWire): _id = 11 _entityDef = (_l,_l) _workplane = True _props = ["Difference"] _iconName = 'Assembly_ConstraintLengthDifference.svg' _tooltip='Add a "{}" to constrain the length difference of two lines.' class EqualLengthPointLineDistance(BaseSketch): _id = 12 _entityDef = (_p,_l,_l) _workplane = True _iconName = 'Assembly_ConstraintLengthEqualPointLineDistance.svg' _tooltip='Add a "{}" to constrain the distance between a point and a\n' \ 'line to be the same as the length of a another line.' class EqualLineArcLength(BaseSketch): _id = 15 _entityDef = (_l,_a) _workplane = True _tooltip='Add a "{}" constraint to make a line of the same length as an arc' @classmethod def check(cls,group,checkCount=False): super(EqualLineArcLength,cls).check(group,checkCount) if not checkCount: return for i,o in enumerate(group): if i: if utils.isDraftCircle(o): return elif utils.isDraftWire(o): return raise RuntimeError('Constraint "{}" requires at least one ' 'non-closed-or-subdivided Draft.Wire or one Draft.Circle'.format( cls.getName())) class MidPoint(BaseSketch): _id = 20 _entityDef = (_p,_l) _workplane = True _iconName = 'Assembly_ConstraintMidPoint.svg' _tooltip='Add a "{}" to constrain a point to the middle point of a line.' class Diameter(BaseSketch): _id = 25 _entityDef = (_c,) _prop = ("Diameter",) _iconName = 'Assembly_ConstraintDiameter.svg' _tooltip='Add a "{}" to constrain the diameter of a circle/arc' @classmethod def check(cls,group,checkCount=False): super(Diameter,cls).check(group,checkCount) if not checkCount: return if len(group): o = group[0] if utils.isDraftCircle(o): return raise RuntimeError('Constraint "{}" requires a ' 'Draft.Circle'.format(cls.getName())) class EqualRadius(BaseSketch): _id = 33 _entityDef = (_c,_c) _iconName = 'Assembly_ConstraintEqualRadius.svg' _tooltip='Add a "{}" constraint to make two circles/arcs of the same radius' @classmethod def check(cls,group,checkCount=False): super(EqualRadius,cls).check(group,checkCount) if not checkCount: return for o in group: if utils.isDraftCircle(o): return raise RuntimeError('Constraint "{}" requires at least one ' 'Draft.Circle'.format(cls.getName())) # class CubicLineTangent(BaseSketch): # _id = 31 # # # class CurvesTangent(BaseSketch): # _id = 32