diff --git a/constraint.py b/constraint.py index 503f8a7..1f5dbcf 100644 --- a/constraint.py +++ b/constraint.py @@ -28,7 +28,7 @@ def _p(solver,partInfo,subname,shape): partInfo.EntityMap[key] = h return h -def _n(solver,partInfo,subname,shape): +def _n(solver,partInfo,subname,shape,retAll=False): 'return a handle of a transformed normal quaterion derived from shape' if not solver: if utils.isPlanar(shape): @@ -40,13 +40,24 @@ def _n(solver,partInfo,subname,shape): if h: system.log('cache {}: {}'.format(key,h)) else: + h = [] + system.NameTag = subname - e = system.addNormal3dV(*utils.getElementNormal(shape)) + rot = utils.getElementRotation(shape) + e = system.addNormal3dV(*utils.getNormal(rot)) system.NameTag = partInfo.PartName - h = system.addTransform(e,*partInfo.Params,group=partInfo.Group) + 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 = subname + '.nx' + e = system.addNormal3dV(*utils.getNormal(rot)) + system.NameTag = partInfo.PartName + '.nx' + h.append(system.addTransform(e,*partInfo.Params,group=partInfo.Group)) + system.log('{}: {},{}'.format(key,h,partInfo.Group)) partInfo.EntityMap[key] = h - return 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"' @@ -97,10 +108,10 @@ def _w(solver,partInfo,subname,shape,retAll=False): system.log('cache {}: {}'.format(key,h)) else: p = _p(solver,partInfo,subname,shape) - n = _n(solver,partInfo,subname,shape) + n = _n(solver,partInfo,subname,shape,True) system.NameTag = partInfo.PartName - h = system.addWorkplane(p,n,group=partInfo.Group) - h = (h,p,n) + 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] @@ -134,13 +145,14 @@ def _c(solver,partInfo,subname,shape,requireArc=False): raise RuntimeError('shape is not an arc') else: system.NameTag = partInfo.PartName - h.append(solver.addDistanceV(r)) + h.append(system.addDistanceV(r)) h = system.addCircle(*h,group=partInfo.Group) system.log('{}: {},{}'.format(key,h,partInfo.Group)) partInfo.EntityMap[key] = h return h def _a(solver,partInfo,subname,shape): + 'return a handle of a transformed arc derived from "shape"' return _c(solver,partInfo,subname,shape,True) @@ -294,6 +306,7 @@ _makeProp('Distance','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') @@ -593,7 +606,7 @@ class BaseCascade(BaseMulti): class PlaneCoincident(BaseCascade): _id = 35 _iconName = 'Assembly_ConstraintCoincidence.svg' - _props = ['Cascade','Offset'] + _props = ['Cascade','Offset','LockAngle','Angle'] _menuItem = True _tooltip = \ 'Add a "{}" constraint to conincide planes of two or more parts.\n'\ @@ -603,7 +616,7 @@ class PlaneCoincident(BaseCascade): class PlaneAlignment(BaseCascade): _id = 37 _iconName = 'Assembly_ConstraintAlignment.svg' - _props = ['Cascade','Offset'] + _props = ['Cascade','Offset','LockAngle','Angle'] _menuItem = True _tooltip = 'Add a "{}" constraint to rotate planes of two or more parts\n'\ 'into the same orientation' @@ -612,6 +625,7 @@ class PlaneAlignment(BaseCascade): class AxialAlignment(BaseMulti): _id = 36 _iconName = 'Assembly_ConstraintAxial.svg' + _props = ['LockAngle','Angle'] _menuItem = True _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.' @@ -661,6 +675,7 @@ class MultiParallel(BaseMulti): _id = 291 _entityDef = (_ln,) _iconName = 'Assembly_ConstraintMultiParallel.svg' + _props = ['LockAngle','Angle'] _menuItem = True _tooltip = 'Add a "{}" constraint to make planes or linear edges of two\n'\ 'or more parts parallel.' diff --git a/system.py b/system.py index dcf330f..7700c75 100644 --- a/system.py +++ b/system.py @@ -107,12 +107,12 @@ class SystemExtension(object): def __init__(self): self.NameTag = '' - def addPlaneCoincident(self,d,e1,e2,group=0): + def addPlaneCoincident(self,d,lockAngle,angle,e1,e2,group=0): if not group: group = self.GroupHandle d = abs(d) - _,p1,n1 = e1 - w2,p2,n2 = e2 + _,p1,n1,nx1 = e1 + w2,p2,n2,nx2 = e2 h = [] if d>0.0: h.append(self.addPointPlaneDistance(d,p1,w2,group=group)) @@ -120,34 +120,44 @@ class SystemExtension(object): else: h.append(self.addPointsCoincident(p1,p2,group=group)) h.append(self.addParallel(n1,n2,group=group)) + if lockAngle: + h.append(self.addAngle(angle,False,nx1,nx2,group=group)) return h - def addPlaneAlignment(self,d,e1,e2,group=0): + def addPlaneAlignment(self,d,lockAngle,angle,e1,e2,group=0): if not group: group = self.GroupHandle d = abs(d) - _,p1,n1 = e1 - w2,_,n2 = e2 + _,p1,n1,nx1 = e1 + w2,_,n2,nx2 = e2 h = [] if d>0.0: h.append(self.addPointPlaneDistance(d,p1,w2,group=group)) else: h.append(self.addPointInPlane(p1,w2,group=group)) h.append(self.addParallel(n1,n2,group=group)) + if lockAngle: + h.append(self.addAngle(angle,False,nx1,nx2,group=group)) return h - def addAxialAlignment(self,e1,e2,group=0): + def addAxialAlignment(self,lockAngle,angle,e1,e2,group=0): if not group: group = self.GroupHandle - _,p1,n1 = e1 - w2,p2,n2 = e2 + _,p1,n1,nx1 = e1 + w2,p2,n2,nx2 = e2 h = [] h.append(self.addPointsCoincident(p1,p2,w2,group=group)) h.append(self.addParallel(n1,n2,group=group)) + if lockAngle: + h.append(self.addAngle(angle,False,nx1,nx2,group=group)) return h - def addMultiParallel(self,e1,e2,group=0): - return self.addParallel(e1,e2,group=group) + def addMultiParallel(self,lockAngle,angle,e1,e2,group=0): + h = [] + h.append(self.addParallel(e1,e2,group=group)) + if lockAngle: + h.append(self.addAngle(angle,False,e1,e2,group=group)) + return h def addPlacement(self,pla,group=0): q = pla.Rotation.Q diff --git a/utils.py b/utils.py index 40c95ca..453af35 100644 --- a/utils.py +++ b/utils.py @@ -285,14 +285,19 @@ def getElementRotation(obj,reverse=False): [L.tangent(0)[0] for L in lines]) #D(irections) if np.std( D, axis=0 ).max() < 10**-9: #then linear curve return D[0] - if axis: - return FreeCAD.Rotation(FreeCAD.Vector(0,0,-1 if reverse else 1),axis) + if not axis: + return FreeCAD.Rotation() + return FreeCAD.Rotation(FreeCAD.Vector(0,0,-1 if reverse else 1),axis) -def getElementNormal(obj,reverse=False): - rot = getElementRotation(obj,reverse) - if rot: - q = rot.Q - return q[3],q[0],q[1],q[2] +def getNormal(obj): + if isinstance(obj,FreeCAD.Rotation): + rot = obj + elif isinstance(obj,FreeCAD.Placement): + rot = obj.Rotation + else: + rot = getElementRotation(obj) + q = rot.Q + return q[3],q[0],q[1],q[2] def getElementCircular(obj): 'return radius if it is closed, or a list of two endpoints'