From 31f1f2b849223abbcd0c370dbc9b1bd97ed3c55e Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Tue, 8 May 2018 15:09:36 +0800 Subject: [PATCH] constraint: support cylindrical plane in AxialAlignment and Colinear --- constraint.py | 78 +++++++++++++++++++++++++++++++++------------------ solver.py | 10 +++++-- system.py | 61 +++++++++++++++++++++++++++------------- 3 files changed, 100 insertions(+), 49 deletions(-) diff --git a/constraint.py b/constraint.py index 226e977..50d97c3 100644 --- a/constraint.py +++ b/constraint.py @@ -7,9 +7,9 @@ from .proxy import ProxyType, PropertyInfo, propGet, propGetValue import os _iconPath = os.path.join(utils.iconPath,'constraints') -PointInfo = namedtuple('PointInfo', ('entity','params','vertex')) -LineInfo = namedtuple('LineInfo', ('entity','p1','p2')) -NormalInfo = namedtuple('NormalInfo', ('entity','rot','params')) +PointInfo = namedtuple('PointInfo', ('entity','params','vector')) +LineInfo = namedtuple('LineInfo', ('entity','p0','p1')) +NormalInfo = namedtuple('NormalInfo', ('entity','rot','params','p0','ln')) PlaneInfo = namedtuple('PlaneInfo', ('entity','origin','normal')) CircleInfo = namedtuple('CurcleInfo',('entity','radius','p0')) ArcInfo = namedtuple('CurcleInfo',('entity','p1','p0','params')) @@ -65,7 +65,7 @@ def _p(solver,partInfo,subname,shape,retAll=False): params.append(system.addParamV(val,group=partInfo.Group)) system.NameTag = nameTag e = system.addPoint3d(*params) - h = PointInfo(entity=e,params=params,vertex=v) + h = PointInfo(entity=e,params=params,vector=v) system.log('{}: add draft point {}'.format(key,h)) if system.sketchPlane and not solver.isFixedElement(part,subname): @@ -79,7 +79,7 @@ def _p(solver,partInfo,subname,shape,retAll=False): requireArc = subname=='Vertex2' e = _prepareDraftCircle(solver,partInfo,requireArc) if requireArc or subname=='Vertex1': - h = PointInfo(entity=e.p0,params=partInfo.Params,vertex=v) + h = PointInfo(entity=e.p0,params=partInfo.Params,vector=v) elif subname=='Edge1': # center point h = partInfo.Workplane.origin @@ -94,7 +94,7 @@ def _p(solver,partInfo,subname,shape,retAll=False): e = system.addPoint3dV(*v) system.NameTag = nameTag + 't' h = system.addTransform(e,*partInfo.Params,group=partInfo.Group) - h = PointInfo(entity=h, params=partInfo.Params,vertex=v) + h = PointInfo(entity=h, params=partInfo.Params,vector=v) system.log('{}: {},{}'.format(key,h,partInfo.Group)) partInfo.EntityMap[key] = h @@ -103,8 +103,8 @@ def _p(solver,partInfo,subname,shape,retAll=False): 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 not utils.isPlanar(shape) and not utils.isCylindricalPlane(shape): + return 'an edge or face with a planar or cylindrical surface' if utils.isDraftWire(partInfo): logger.warn('Use draft wire {} for normal. Draft wire placement' ' is not transformable'.format(partInfo.PartName)) @@ -126,7 +126,20 @@ def _n(solver,partInfo,subname,shape,retAll=False): system.NameTag += 't' nz = system.addTransform(e,*partInfo.Params,group=partInfo.Group) - h = NormalInfo(entity=nz,rot=rot,params=partInfo.Params) + p0 = _p(solver,partInfo,subname,shape,True) + v = rot.inverted().multVec(p0.vector) + v.z += 1 + v = rot.multVec(v) + system.NameTag = nameTag + 'p1' + e = system.addPoint3dV(*v) + system.NameTag += 't' + p1 = system.addTransform(e,*partInfo.Params,group=partInfo.Group) + + system.NameTag = nameTag + 'l' + ln = system.addLineSegment(p0.entity,p1,group=partInfo.Group) + + h = NormalInfo(entity=nz,rot=rot, + params=partInfo.Params, p0=p0.entity, ln=ln) system.log('{}: {},{}'.format(key,h,partInfo.Group)) partInfo.EntityMap[key] = h @@ -137,6 +150,7 @@ def _l(solver,partInfo,subname,shape,retAll=False): if not solver: if not utils.isLinearEdge(shape): return 'a linear edge' + if not utils.isDraftWire(partInfo): return vname1,vname2 = utils.edge2VertexIndex(partInfo,subname) @@ -157,27 +171,28 @@ def _l(solver,partInfo,subname,shape,retAll=False): system.log('cache {}: {}'.format(key,h)) else: nameTag = partInfo.PartName + '.' + key - v = shape.Edge1.Vertexes if utils.isDraftWire(part): + v = shape.Edge1.Vertexes vname1,vname2 = utils.edge2VertexIndex(part,subname) if not vname1: raise RuntimeError('Invalid draft subname {} or {}'.format( subname,partInfo.PartName)) - tp1 = _p(solver,partInfo,vname1,v[0]) - tp2 = _p(solver,partInfo,vname2,v[1]) + tp0 = _p(solver,partInfo,vname1,v[0]) + tp1 = _p(solver,partInfo,vname2,v[1]) else: + v = shape.Edge1.Vertexes + system.NameTag = nameTag + 'p0' + p0 = system.addPoint3dV(*v[0].Point) + system.NameTag = nameTag + 'p0t' + tp0 = system.addTransform(p0,*partInfo.Params,group=partInfo.Group) system.NameTag = nameTag + 'p1' - p1 = system.addPoint3dV(*v[0].Point) + p1 = system.addPoint3dV(*v[-1].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 = LineInfo(entity=h,p1=tp1,p2=tp2) + h = system.addLineSegment(tp0,tp1,group=partInfo.Group) + h = LineInfo(entity=h,p0=tp0,p1=tp1) system.log('{}: {},{}'.format(key,h,partInfo.Group)) partInfo.EntityMap[key] = h @@ -198,12 +213,18 @@ def _dl(solver,partInfo,subname,shape,retAll=False): 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): + if utils.isLinearEdge(shape) or \ + utils.isPlanar(shape) or \ + utils.isCylindricalPlane(shape): return - return 'a linear edge or edge/face with planar surface' + return 'a linear edge or edge/face with planar or cylindrical surface' if utils.isLinearEdge(shape): return _l(solver,partInfo,subname,shape,retAll) - return _n(solver,partInfo,subname,shape) + return _n(solver,partInfo,subname,shape,retAll) + +def _lna(solver,partInfo,subname,shape,retAll=False): + _ = retAll + return _ln(solver,partInfo,subname,shape,True) def _lw(solver,partInfo,subname,shape,retAll=False): 'return a handle for either a line or a plane depending on the shape' @@ -323,8 +344,8 @@ def _c(solver,partInfo,subname,shape,requireArc=False,retAll=False): l = _l(solver,partInfo,subname,shape,True) system.NameTag = nameTag h = system.addArcOfCircle( - pln.entity, pln.origin.entity, l.p1, l.p2, group=g) - h = ArcInfo(entity=h,p1=l.p2,p0=l.p1,params=None) + pln.entity, pln.origin.entity, l.p0, l.p1, group=g) + h = ArcInfo(entity=h,p1=l.p1,p0=l.p0,params=None) else: system.NameTag = nameTag h = system.addCircle( @@ -745,7 +766,7 @@ class Locked(Base): fixPoint = True names = utils.edge2VertexIndex(info.Part,info.Subname) else: - names = [info.Subname+'.fp1', info.Subname+'.fp2'] + names = [info.Subname+'.fp0', info.Subname+'.fp1'] nameTag = partInfo.PartName + '.' + info.Subname @@ -927,6 +948,7 @@ class PlaneAlignment(BaseCascade): class AxialAlignment(BaseMulti): _id = 36 + _entityDef = (_lna,) _iconName = 'Assembly_ConstraintAxial.svg' _props = _AngleProps _tooltip = 'Add a "{}" constraint to align planes of two or more parts.\n'\ @@ -1167,7 +1189,7 @@ class ArcLineTangent(Base2): class Colinear(Base2): _id = 39 - _entityDef = (_la, _l) + _entityDef = (_lna, _lna) _workplane = True _iconName = 'Assembly_ConstraintColinear.svg' _tooltip='Add a "{}" constraint to make to line colinear' @@ -1239,8 +1261,8 @@ class LineLength(BaseSketch): 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] + _,p0,p1 = cls.getEntities(obj,solver,retAll=True)[0] + params = cls.getPropertyValues(obj) + [p0,p1] ret = func(*params,group=solver.group) solver.system.log('{}: {}'.format(cstrName(obj),ret)) return ret diff --git a/solver.py b/solver.py index e7a71be..c725bdc 100644 --- a/solver.py +++ b/solver.py @@ -228,13 +228,19 @@ class Solver(object): p = self.system.addPoint3d(*params[:3],group=g) self.system.NameTag = info.PartName + '.n' n = self.system.addNormal3d(*params[3:],group=g) + self.system.NameTag = info.PartName + '.np0' + p0 = self.system.addPoint3d(self.v0,self.v0,self.v0,group=g) + self.system.NameTag = info.PartName + '.np1' + p1 = self.system.addPoint3d(self.v0,self.v0,self.v1,group=g) + self.system.NameTag = info.PartName + '.l' + ln = self.system.addLineSegment(p0,p1,group=g) self.system.NameTag = info.PartName + '.w' w = self.system.addWorkplane(p,n,group=g) h = PlaneInfo(entity=w, origin=PointInfo(entity=p, params=None, - vertex=FreeCAD.Vector()), + vector=FreeCAD.Vector()), normal=NormalInfo(entity=n,rot=FreeCAD.Rotation(), - params=params)) + params=params,p0=p0,ln=ln)) partInfo = PartInfo(Part = info.Part, PartName = info.PartName, diff --git a/system.py b/system.py index 3b55b7e..98fd077 100644 --- a/system.py +++ b/system.py @@ -1,6 +1,6 @@ import os import FreeCAD -from .constraint import cstrName +from .constraint import cstrName, PlaneInfo, NormalInfo from .utils import getIcon, syslogger as logger, objName, project2D, getNormal from .proxy import ProxyType, PropertyInfo @@ -134,7 +134,7 @@ class SystemExtension(object): def setOrientation(self,h,lockAngle,yaw,pitch,roll,n1,n2,group): if not lockAngle: h.append(self.addParallel(n1.entity,n2.entity,group=group)) - return + return h if not yaw and not pitch and not roll: n = n2.entity else: @@ -188,11 +188,11 @@ class SystemExtension(object): if d or dx or dy: dx,dy,d = pln2.normal.rot.multVec(FreeCAD.Vector(dx,dy,d)) - v = pln2.origin.vertex+FreeCAD.Vector(dx,dy,d) + v = pln2.origin.vector+FreeCAD.Vector(dx,dy,d) e = self.addTransform( self.addPoint3dV(*v),*pln2.origin.params,group=group) else: - v = pln2.origin.vertex + v = pln2.origin.vector e = pln2.origin.entity if not lockAngle and count==2: @@ -204,7 +204,7 @@ class SystemExtension(object): # We project the initial points to the first element plane, and # check for differences in x and y components of the points to # determine whether to use horizontal or vertical constraint. - v1,v2 = project2D(pln1.normal.rot, pln1.origin.vertex, v) + v1,v2 = project2D(pln1.normal.rot, pln1.origin.vector, v) if abs(v1.x-v2.x) < abs(v1.y-v2.y): h.append(self.addPointsHorizontal( pln1.origin.entity, e, pln1.entity, group=group)) @@ -241,29 +241,42 @@ class SystemExtension(object): pln1.normal, pln2.normal, group) return h - def addAxialAlignment(self,lockAngle,yaw,pitch,roll,pln1,pln2,group=0): + def addAxialAlignment(self,lockAngle,yaw,pitch,roll,ln1,ln2,group=0): if not group: group = self.GroupHandle - count = self.countConstraints(0,2,'Coincident') + h = [] + if not isinstance(ln1,NormalInfo): + if not isinstance(ln2,NormalInfo): + lockAngle = False + else: + ln1,ln2 = ln2,ln1 + + count = self.countConstraints(2 if lockAngle else 1,2,'Axial') if count < 0: return - if count: - return self.addPlaneCoincident(0,0,0,False,0,0,0,pln1,pln2,group) - h = [] - h.append(self.addPointsCoincident(pln1.origin.entity, - pln2.origin.entity, pln2.entity, group=group)) - return self.setOrientation(h, lockAngle, yaw, pitch, roll, - pln1.normal, pln2.normal, group) + relax = count==2 and not lockAngle + if isinstance(ln2,NormalInfo): + ln = ln2.ln + if not relax: + h = self.setOrientation( + h,lockAngle,yaw,pitch,roll,ln1,ln2,group) + else: + ln = ln2.entity + if not relax: + h.append(self.addParallel(ln1.entity,ln,group=group)) + h.append(self.addPointOnLine(ln1.p0,ln,group=group)) + return h def addMultiParallel(self,lockAngle,yaw,pitch,raw,e1,e2,group=0): + if not group: + group = self.GroupHandle h = [] - isPlane = isinstance(e1,list),isinstance(e2,list) + isPlane = isinstance(e1,PlaneInfo),isinstance(e2,PlaneInfo) if all(isPlane): return self.setOrientation(h, lockAngle, yaw, pitch, raw, e1.normal, e2.normal, group); if not any(isPlane): - h.append(self.addParallel( - e1.normal.entity, e2.normal.entity, group=group)) + h.append(self.addParallel(e1, e2, group=group)) elif isPlane[0]: h.append(self.addPerpendicular(e1.normal.entity, e2, group=group)) else: @@ -272,8 +285,18 @@ class SystemExtension(object): def addColinear(self,l1,l2,wrkpln=0,group=0): h = [] - h.append(self.addParallel(l1.entity,l2,wrkpln=wrkpln,group=group)) - h.append(self.addPointOnLine(l1.entity,l2,wrkpln=wrkpln,group=group)) + if isinstance(l1,NormalInfo): + pt = l1.p0 + l1 = l1.ln + else: + pt = l1.p0 + l1 = l1.entity + if isinstance(l2,NormalInfo): + l2 = l2.ln + else: + l2 = l2.entity + h.append(self.addParallel(l1,l2,wrkpln=wrkpln,group=group)) + h.append(self.addPointOnLine(pt,l2,wrkpln=wrkpln,group=group)) return h def addPlacement(self,pla,group=0):