constraint: support cylindrical plane in AxialAlignment and Colinear

This commit is contained in:
Zheng, Lei 2018-05-08 15:09:36 +08:00
parent fe7a1757cb
commit 31f1f2b849
3 changed files with 100 additions and 49 deletions

View File

@ -7,9 +7,9 @@ from .proxy import ProxyType, PropertyInfo, propGet, propGetValue
import os import os
_iconPath = os.path.join(utils.iconPath,'constraints') _iconPath = os.path.join(utils.iconPath,'constraints')
PointInfo = namedtuple('PointInfo', ('entity','params','vertex')) PointInfo = namedtuple('PointInfo', ('entity','params','vector'))
LineInfo = namedtuple('LineInfo', ('entity','p1','p2')) LineInfo = namedtuple('LineInfo', ('entity','p0','p1'))
NormalInfo = namedtuple('NormalInfo', ('entity','rot','params')) NormalInfo = namedtuple('NormalInfo', ('entity','rot','params','p0','ln'))
PlaneInfo = namedtuple('PlaneInfo', ('entity','origin','normal')) PlaneInfo = namedtuple('PlaneInfo', ('entity','origin','normal'))
CircleInfo = namedtuple('CurcleInfo',('entity','radius','p0')) CircleInfo = namedtuple('CurcleInfo',('entity','radius','p0'))
ArcInfo = namedtuple('CurcleInfo',('entity','p1','p0','params')) 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)) params.append(system.addParamV(val,group=partInfo.Group))
system.NameTag = nameTag system.NameTag = nameTag
e = system.addPoint3d(*params) 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)) system.log('{}: add draft point {}'.format(key,h))
if system.sketchPlane and not solver.isFixedElement(part,subname): if system.sketchPlane and not solver.isFixedElement(part,subname):
@ -79,7 +79,7 @@ def _p(solver,partInfo,subname,shape,retAll=False):
requireArc = subname=='Vertex2' requireArc = subname=='Vertex2'
e = _prepareDraftCircle(solver,partInfo,requireArc) e = _prepareDraftCircle(solver,partInfo,requireArc)
if requireArc or subname=='Vertex1': 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': elif subname=='Edge1':
# center point # center point
h = partInfo.Workplane.origin h = partInfo.Workplane.origin
@ -94,7 +94,7 @@ def _p(solver,partInfo,subname,shape,retAll=False):
e = system.addPoint3dV(*v) e = system.addPoint3dV(*v)
system.NameTag = nameTag + 't' system.NameTag = nameTag + 't'
h = system.addTransform(e,*partInfo.Params,group=partInfo.Group) 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)) system.log('{}: {},{}'.format(key,h,partInfo.Group))
partInfo.EntityMap[key] = h partInfo.EntityMap[key] = h
@ -103,8 +103,8 @@ def _p(solver,partInfo,subname,shape,retAll=False):
def _n(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' 'return a handle of a transformed normal quaterion derived from shape'
if not solver: if not solver:
if not utils.isPlanar(shape): if not utils.isPlanar(shape) and not utils.isCylindricalPlane(shape):
return 'an edge or face with a surface normal' return 'an edge or face with a planar or cylindrical surface'
if utils.isDraftWire(partInfo): if utils.isDraftWire(partInfo):
logger.warn('Use draft wire {} for normal. Draft wire placement' logger.warn('Use draft wire {} for normal. Draft wire placement'
' is not transformable'.format(partInfo.PartName)) ' is not transformable'.format(partInfo.PartName))
@ -126,7 +126,20 @@ def _n(solver,partInfo,subname,shape,retAll=False):
system.NameTag += 't' system.NameTag += 't'
nz = system.addTransform(e,*partInfo.Params,group=partInfo.Group) 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)) system.log('{}: {},{}'.format(key,h,partInfo.Group))
partInfo.EntityMap[key] = h partInfo.EntityMap[key] = h
@ -137,6 +150,7 @@ def _l(solver,partInfo,subname,shape,retAll=False):
if not solver: if not solver:
if not utils.isLinearEdge(shape): if not utils.isLinearEdge(shape):
return 'a linear edge' return 'a linear edge'
if not utils.isDraftWire(partInfo): if not utils.isDraftWire(partInfo):
return return
vname1,vname2 = utils.edge2VertexIndex(partInfo,subname) vname1,vname2 = utils.edge2VertexIndex(partInfo,subname)
@ -157,27 +171,28 @@ def _l(solver,partInfo,subname,shape,retAll=False):
system.log('cache {}: {}'.format(key,h)) system.log('cache {}: {}'.format(key,h))
else: else:
nameTag = partInfo.PartName + '.' + key nameTag = partInfo.PartName + '.' + key
v = shape.Edge1.Vertexes
if utils.isDraftWire(part): if utils.isDraftWire(part):
v = shape.Edge1.Vertexes
vname1,vname2 = utils.edge2VertexIndex(part,subname) vname1,vname2 = utils.edge2VertexIndex(part,subname)
if not vname1: if not vname1:
raise RuntimeError('Invalid draft subname {} or {}'.format( raise RuntimeError('Invalid draft subname {} or {}'.format(
subname,partInfo.PartName)) subname,partInfo.PartName))
tp1 = _p(solver,partInfo,vname1,v[0]) tp0 = _p(solver,partInfo,vname1,v[0])
tp2 = _p(solver,partInfo,vname2,v[1]) tp1 = _p(solver,partInfo,vname2,v[1])
else: 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' system.NameTag = nameTag + 'p1'
p1 = system.addPoint3dV(*v[0].Point) p1 = system.addPoint3dV(*v[-1].Point)
system.NameTag = nameTag + 'p1t' system.NameTag = nameTag + 'p1t'
tp1 = system.addTransform(p1,*partInfo.Params,group=partInfo.Group) 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 system.NameTag = nameTag
h = system.addLineSegment(tp1,tp2,group=partInfo.Group) h = system.addLineSegment(tp0,tp1,group=partInfo.Group)
h = LineInfo(entity=h,p1=tp1,p2=tp2) h = LineInfo(entity=h,p0=tp0,p1=tp1)
system.log('{}: {},{}'.format(key,h,partInfo.Group)) system.log('{}: {},{}'.format(key,h,partInfo.Group))
partInfo.EntityMap[key] = h partInfo.EntityMap[key] = h
@ -198,12 +213,18 @@ def _dl(solver,partInfo,subname,shape,retAll=False):
def _ln(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' 'return a handle for either a line or a normal depends on the shape'
if not solver: 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
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): if utils.isLinearEdge(shape):
return _l(solver,partInfo,subname,shape,retAll) 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): def _lw(solver,partInfo,subname,shape,retAll=False):
'return a handle for either a line or a plane depending on the shape' '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) l = _l(solver,partInfo,subname,shape,True)
system.NameTag = nameTag system.NameTag = nameTag
h = system.addArcOfCircle( h = system.addArcOfCircle(
pln.entity, pln.origin.entity, l.p1, l.p2, group=g) pln.entity, pln.origin.entity, l.p0, l.p1, group=g)
h = ArcInfo(entity=h,p1=l.p2,p0=l.p1,params=None) h = ArcInfo(entity=h,p1=l.p1,p0=l.p0,params=None)
else: else:
system.NameTag = nameTag system.NameTag = nameTag
h = system.addCircle( h = system.addCircle(
@ -745,7 +766,7 @@ class Locked(Base):
fixPoint = True fixPoint = True
names = utils.edge2VertexIndex(info.Part,info.Subname) names = utils.edge2VertexIndex(info.Part,info.Subname)
else: else:
names = [info.Subname+'.fp1', info.Subname+'.fp2'] names = [info.Subname+'.fp0', info.Subname+'.fp1']
nameTag = partInfo.PartName + '.' + info.Subname nameTag = partInfo.PartName + '.' + info.Subname
@ -927,6 +948,7 @@ class PlaneAlignment(BaseCascade):
class AxialAlignment(BaseMulti): class AxialAlignment(BaseMulti):
_id = 36 _id = 36
_entityDef = (_lna,)
_iconName = 'Assembly_ConstraintAxial.svg' _iconName = 'Assembly_ConstraintAxial.svg'
_props = _AngleProps _props = _AngleProps
_tooltip = 'Add a "{}" constraint to align planes of two or more parts.\n'\ _tooltip = 'Add a "{}" constraint to align planes of two or more parts.\n'\
@ -1167,7 +1189,7 @@ class ArcLineTangent(Base2):
class Colinear(Base2): class Colinear(Base2):
_id = 39 _id = 39
_entityDef = (_la, _l) _entityDef = (_lna, _lna)
_workplane = True _workplane = True
_iconName = 'Assembly_ConstraintColinear.svg' _iconName = 'Assembly_ConstraintColinear.svg'
_tooltip='Add a "{}" constraint to make to line colinear' _tooltip='Add a "{}" constraint to make to line colinear'
@ -1239,8 +1261,8 @@ class LineLength(BaseSketch):
def prepare(cls,obj,solver): def prepare(cls,obj,solver):
func = PointsDistance.constraintFunc(obj,solver) func = PointsDistance.constraintFunc(obj,solver)
if func: if func:
_,p1,p2 = cls.getEntities(obj,solver,retAll=True)[0] _,p0,p1 = cls.getEntities(obj,solver,retAll=True)[0]
params = cls.getPropertyValues(obj) + [p1,p2] params = cls.getPropertyValues(obj) + [p0,p1]
ret = func(*params,group=solver.group) ret = func(*params,group=solver.group)
solver.system.log('{}: {}'.format(cstrName(obj),ret)) solver.system.log('{}: {}'.format(cstrName(obj),ret))
return ret return ret

View File

@ -228,13 +228,19 @@ class Solver(object):
p = self.system.addPoint3d(*params[:3],group=g) p = self.system.addPoint3d(*params[:3],group=g)
self.system.NameTag = info.PartName + '.n' self.system.NameTag = info.PartName + '.n'
n = self.system.addNormal3d(*params[3:],group=g) 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' self.system.NameTag = info.PartName + '.w'
w = self.system.addWorkplane(p,n,group=g) w = self.system.addWorkplane(p,n,group=g)
h = PlaneInfo(entity=w, h = PlaneInfo(entity=w,
origin=PointInfo(entity=p, params=None, origin=PointInfo(entity=p, params=None,
vertex=FreeCAD.Vector()), vector=FreeCAD.Vector()),
normal=NormalInfo(entity=n,rot=FreeCAD.Rotation(), normal=NormalInfo(entity=n,rot=FreeCAD.Rotation(),
params=params)) params=params,p0=p0,ln=ln))
partInfo = PartInfo(Part = info.Part, partInfo = PartInfo(Part = info.Part,
PartName = info.PartName, PartName = info.PartName,

View File

@ -1,6 +1,6 @@
import os import os
import FreeCAD import FreeCAD
from .constraint import cstrName from .constraint import cstrName, PlaneInfo, NormalInfo
from .utils import getIcon, syslogger as logger, objName, project2D, getNormal from .utils import getIcon, syslogger as logger, objName, project2D, getNormal
from .proxy import ProxyType, PropertyInfo from .proxy import ProxyType, PropertyInfo
@ -134,7 +134,7 @@ class SystemExtension(object):
def setOrientation(self,h,lockAngle,yaw,pitch,roll,n1,n2,group): def setOrientation(self,h,lockAngle,yaw,pitch,roll,n1,n2,group):
if not lockAngle: if not lockAngle:
h.append(self.addParallel(n1.entity,n2.entity,group=group)) h.append(self.addParallel(n1.entity,n2.entity,group=group))
return return h
if not yaw and not pitch and not roll: if not yaw and not pitch and not roll:
n = n2.entity n = n2.entity
else: else:
@ -188,11 +188,11 @@ class SystemExtension(object):
if d or dx or dy: if d or dx or dy:
dx,dy,d = pln2.normal.rot.multVec(FreeCAD.Vector(dx,dy,d)) 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( e = self.addTransform(
self.addPoint3dV(*v),*pln2.origin.params,group=group) self.addPoint3dV(*v),*pln2.origin.params,group=group)
else: else:
v = pln2.origin.vertex v = pln2.origin.vector
e = pln2.origin.entity e = pln2.origin.entity
if not lockAngle and count==2: if not lockAngle and count==2:
@ -204,7 +204,7 @@ class SystemExtension(object):
# We project the initial points to the first element plane, and # We project the initial points to the first element plane, and
# check for differences in x and y components of the points to # check for differences in x and y components of the points to
# determine whether to use horizontal or vertical constraint. # 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): if abs(v1.x-v2.x) < abs(v1.y-v2.y):
h.append(self.addPointsHorizontal( h.append(self.addPointsHorizontal(
pln1.origin.entity, e, pln1.entity, group=group)) pln1.origin.entity, e, pln1.entity, group=group))
@ -241,29 +241,42 @@ class SystemExtension(object):
pln1.normal, pln2.normal, group) pln1.normal, pln2.normal, group)
return h 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: if not group:
group = self.GroupHandle 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: if count < 0:
return return
if count: relax = count==2 and not lockAngle
return self.addPlaneCoincident(0,0,0,False,0,0,0,pln1,pln2,group) if isinstance(ln2,NormalInfo):
h = [] ln = ln2.ln
h.append(self.addPointsCoincident(pln1.origin.entity, if not relax:
pln2.origin.entity, pln2.entity, group=group)) h = self.setOrientation(
return self.setOrientation(h, lockAngle, yaw, pitch, roll, h,lockAngle,yaw,pitch,roll,ln1,ln2,group)
pln1.normal, pln2.normal, 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): def addMultiParallel(self,lockAngle,yaw,pitch,raw,e1,e2,group=0):
if not group:
group = self.GroupHandle
h = [] h = []
isPlane = isinstance(e1,list),isinstance(e2,list) isPlane = isinstance(e1,PlaneInfo),isinstance(e2,PlaneInfo)
if all(isPlane): if all(isPlane):
return self.setOrientation(h, lockAngle, yaw, pitch, raw, return self.setOrientation(h, lockAngle, yaw, pitch, raw,
e1.normal, e2.normal, group); e1.normal, e2.normal, group);
if not any(isPlane): if not any(isPlane):
h.append(self.addParallel( h.append(self.addParallel(e1, e2, group=group))
e1.normal.entity, e2.normal.entity, group=group))
elif isPlane[0]: elif isPlane[0]:
h.append(self.addPerpendicular(e1.normal.entity, e2, group=group)) h.append(self.addPerpendicular(e1.normal.entity, e2, group=group))
else: else:
@ -272,8 +285,18 @@ class SystemExtension(object):
def addColinear(self,l1,l2,wrkpln=0,group=0): def addColinear(self,l1,l2,wrkpln=0,group=0):
h = [] h = []
h.append(self.addParallel(l1.entity,l2,wrkpln=wrkpln,group=group)) if isinstance(l1,NormalInfo):
h.append(self.addPointOnLine(l1.entity,l2,wrkpln=wrkpln,group=group)) 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 return h
def addPlacement(self,pla,group=0): def addPlacement(self,pla,group=0):