constraint: extend Angle locking to yaw-pitch-roll

Angle locking applies to PlaneCoincident, PlaneAlignment,
AxialAlignment, and MultiParallel
This commit is contained in:
Zheng, Lei 2018-05-07 17:23:32 +08:00
parent 61ccdf5bac
commit 094828561b
3 changed files with 150 additions and 131 deletions

View File

@ -7,6 +7,13 @@ 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'))
LineInfo = namedtuple('LineInfo', ('entity','p1','p2'))
NormalInfo = namedtuple('NormalInfo', ('entity','rot','params'))
PlaneInfo = namedtuple('PlaneInfo', ('entity','origin','normal'))
CircleInfo = namedtuple('CurcleInfo',('entity','radius','p0'))
ArcInfo = namedtuple('CurcleInfo',('entity','p1','p0','params'))
def _d(solver,partInfo,subname,shape,retAll=False): def _d(solver,partInfo,subname,shape,retAll=False):
'return a handle of any supported element of a draft object' 'return a handle of any supported element of a draft object'
if not solver: if not solver:
@ -45,7 +52,7 @@ def _p(solver,partInfo,subname,shape,retAll=False):
system = solver.system system = solver.system
if h: if h:
system.log('cache {}: {}'.format(key,h)) system.log('cache {}: {}'.format(key,h))
return h if retAll else h[0] return h if retAll else h.entity
v = utils.getElementPos(shape) v = utils.getElementPos(shape)
@ -58,30 +65,28 @@ 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 = [e, params] h = PointInfo(entity=e,params=params,vertex=v)
system.log('{}: add draft point {},{}'.format(key,h,v)) 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):
system.NameTag = nameTag + '.i' system.NameTag = nameTag + '.i'
e2 = system.addPointInPlane(e,system.sketchPlane[0], e2 = system.addPointInPlane(e,system.sketchPlane.entity,
group=partInfo.Group) group=partInfo.Group)
system.log('{}: add draft point in plane {},{}'.format( system.log('{}: add draft point in plane {},{}'.format(
partInfo.PartName,e2,system.sketchPlane[0])) partInfo.PartName,e2,system.sketchPlane.entity))
elif utils.isDraftCircle(part): elif utils.isDraftCircle(part):
requireArc = subname=='Vertex2' requireArc = subname=='Vertex2'
e = _prepareDraftCircle(solver,partInfo,requireArc) e = _prepareDraftCircle(solver,partInfo,requireArc)
if requireArc: if requireArc or subname=='Vertex1':
h = [e[1]] h = PointInfo(entity=e.p0,params=partInfo.Params,vertex=v)
elif subname=='Vertex1':
h = [e[2]]
elif subname=='Edge1': elif subname=='Edge1':
# center point # center point
h = [partInfo.Workplane[1]] h = partInfo.Workplane.origin
else: else:
raise RuntimeError('Invalid draft circle subname {} of ' raise RuntimeError('Invalid draft circle subname {} of '
'{}'.format(subname,partInfo.PartName)) '{}'.format(subname,partInfo.PartName))
system.log('{}: add circle point {},{}'.format(key,h,e)) system.log('{}: add circle point {}'.format(key,h))
else: else:
nameTag = partInfo.PartName + '.' + key nameTag = partInfo.PartName + '.' + key
@ -89,13 +94,11 @@ 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 = [h,e] h = PointInfo(entity=h, params=partInfo.Params,vertex=v)
system.log('{}: {},{}'.format(key,h,partInfo.Group)) system.log('{}: {},{}'.format(key,h,partInfo.Group))
# use the point entity as key to store its original position vector
partInfo.EntityMap[h[0]] = [v]
partInfo.EntityMap[key] = h partInfo.EntityMap[key] = h
return h if retAll else h[0] return h if retAll else h.entity
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'
@ -113,8 +116,6 @@ def _n(solver,partInfo,subname,shape,retAll=False):
if h: if h:
system.log('cache {}: {}'.format(key,h)) system.log('cache {}: {}'.format(key,h))
else: else:
h = []
if utils.isDraftCircle(partInfo.Part): if utils.isDraftCircle(partInfo.Part):
_prepareDraftCircle(solver,partInfo) _prepareDraftCircle(solver,partInfo)
@ -123,25 +124,13 @@ def _n(solver,partInfo,subname,shape,retAll=False):
system.NameTag = nameTag system.NameTag = nameTag
e = system.addNormal3dV(*utils.getNormal(rot)) e = system.addNormal3dV(*utils.getNormal(rot))
system.NameTag += 't' system.NameTag += 't'
h.append(system.addTransform(e,*partInfo.Params,group=partInfo.Group)) nz = system.addTransform(e,*partInfo.Params,group=partInfo.Group)
# also add x axis pointing quaterion for convenience h = NormalInfo(entity=nz,rot=rot,params=partInfo.Params)
xrot = FreeCAD.Rotation(FreeCAD.Vector(0,1,0),90).multiply(rot)
system.NameTag = nameTag + 'x'
e = system.addNormal3dV(*utils.getNormal(xrot))
system.NameTag = nameTag + 'xt'
h.append(system.addTransform(e,*partInfo.Params,group=partInfo.Group))
# also add local x pointing vector (1,0,0)
px = rot.multVec(FreeCAD.Vector(1,0,0))
hx = system.addPoint3dV(px.x,px.y,px.z)
h.append(system.addTransform(hx,*partInfo.Params,group=partInfo.Group))
system.log('{}: {},{}'.format(key,h,partInfo.Group)) system.log('{}: {},{}'.format(key,h,partInfo.Group))
# use the normal entity as key to store its original rotation
partInfo.EntityMap[h[0]] = [rot]
partInfo.EntityMap[key] = h partInfo.EntityMap[key] = h
return h if retAll else h[0] return h if retAll else h.entity
def _l(solver,partInfo,subname,shape,retAll=False): def _l(solver,partInfo,subname,shape,retAll=False):
'return a pair of handle of the end points of an edge in "shape"' 'return a pair of handle of the end points of an edge in "shape"'
@ -154,7 +143,7 @@ def _l(solver,partInfo,subname,shape,retAll=False):
if not vname1: if not vname1:
raise RuntimeError('Invalid draft subname {} or {}'.format( raise RuntimeError('Invalid draft subname {} or {}'.format(
subname,objName(partInfo))) subname,objName(partInfo)))
v = shape.Edges[0].Vertexes v = shape.Edge1.Vertexes
ret = _p(solver,partInfo,vname1,v[0]) ret = _p(solver,partInfo,vname1,v[0])
if ret: if ret:
return ret return ret
@ -168,7 +157,7 @@ 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.Edges[0].Vertexes v = shape.Edge1.Vertexes
if utils.isDraftWire(part): if utils.isDraftWire(part):
vname1,vname2 = utils.edge2VertexIndex(part,subname) vname1,vname2 = utils.edge2VertexIndex(part,subname)
if not vname1: if not vname1:
@ -188,11 +177,11 @@ def _l(solver,partInfo,subname,shape,retAll=False):
system.NameTag = nameTag system.NameTag = nameTag
h = system.addLineSegment(tp1,tp2,group=partInfo.Group) h = system.addLineSegment(tp1,tp2,group=partInfo.Group)
h = (h,tp1,tp2) h = LineInfo(entity=h,p1=tp1,p2=tp2)
system.log('{}: {},{}'.format(key,h,partInfo.Group)) system.log('{}: {},{}'.format(key,h,partInfo.Group))
partInfo.EntityMap[key] = h partInfo.EntityMap[key] = h
return h if retAll else h[0] return h if retAll else h.entity
def _la(solver,partInfo,subname,shape,retAll=False): def _la(solver,partInfo,subname,shape,retAll=False):
_ = retAll _ = retAll
@ -240,13 +229,13 @@ def _w(solver,partInfo,subname,shape,retAll=False):
if h: if h:
system.log('cache {}: {}'.format(key,h)) system.log('cache {}: {}'.format(key,h))
else: else:
p = _p(solver,partInfo,subname,shape) p = _p(solver,partInfo,subname,shape,True)
n = _n(solver,partInfo,subname,shape,True) n = _n(solver,partInfo,subname,shape,True)
system.NameTag = partInfo.PartName + '.' + key system.NameTag = partInfo.PartName + '.' + key
w = system.addWorkplane(p,n[0],group=partInfo.Group) w = system.addWorkplane(p.entity,n.entity,group=partInfo.Group)
h = [w,p,n] h = PlaneInfo(entity=w,origin=p,normal=n)
system.log('{}: {},{}'.format(key,h,partInfo.Group)) system.log('{}: {},{}'.format(key,h,partInfo.Group))
return h if retAll else h[0] return h if retAll else h.entity
def _wa(solver,partInfo,subname,shape,retAll=False): def _wa(solver,partInfo,subname,shape,retAll=False):
_ = retAll _ = retAll
@ -269,21 +258,22 @@ def _c(solver,partInfo,subname,shape,requireArc=False,retAll=False):
system = solver.system system = solver.system
if h: if h:
system.log('cache {}: {}'.format(key,h)) system.log('cache {}: {}'.format(key,h))
return h if retAll else h[0] return h if retAll else h.entity
g = partInfo.Group g = partInfo.Group
nameTag = partInfo.PartName + '.' + key nameTag = partInfo.PartName + '.' + key
if utils.isDraftCircle(partInfo.Part): if utils.isDraftCircle(partInfo.Part):
part = partInfo.Part part = partInfo.Part
w,p,n = partInfo.Workplane[:3] pln = partInfo.Workplane
n = n[0]
if system.sketchPlane and not solver.isFixedElement(part,subname): if system.sketchPlane and not solver.isFixedElement(part,subname):
system.NameTag = nameTag + '.o' system.NameTag = nameTag + '.o'
e1 = system.addSameOrientation(n,system.sketchPlane[2],group=g) e1 = system.addSameOrientation(pln.normal.entity,
system.sketchPlane.normal.entity, group=g)
system.NameTag = nameTag + '.i' system.NameTag = nameTag + '.i'
e2 = system.addPointInPlane(p,system.sketchPlane[0],group=g) e2 = system.addPointInPlane(
pln.origin.entity, system.sketchPlane.entity, group=g)
system.log('{}: fix draft circle in plane {},{}'.format( system.log('{}: fix draft circle in plane {},{}'.format(
partInfo.PartName,e1,e2)) partInfo.PartName,e1,e2))
@ -294,14 +284,15 @@ def _c(solver,partInfo,subname,shape,requireArc=False,retAll=False):
system.NameTag = nameTag + '.r' system.NameTag = nameTag + '.r'
r = system.addParamV(part.Radius.Value,group=g) r = system.addParamV(part.Radius.Value,group=g)
system.NameTag = nameTag + '.p0' system.NameTag = nameTag + '.p0'
p0 = system.addPoint2d(w,r,solver.v0,group=g) p0 = system.addPoint2d(pln.entity,r,solver.v0,group=g)
system.NameTag = nameTag system.NameTag = nameTag
e = system.addCircle(p,n,system.addDistance(r),group=g) e = system.addCircle(pln.origin.entity, pln.normal.entity,
h = [e,r,p0] system.addDistance(r), group=g)
h = CircleInfo(entity=e,radius=r,p0=p0)
system.log('{}: add draft circle {}, {}'.format(key,h,g)) system.log('{}: add draft circle {}, {}'.format(key,h,g))
else: else:
system.NameTag = nameTag + '.c' system.NameTag = nameTag + '.c'
center = system.addPoint2d(w,solver.v0,solver.v0,group=g) center = system.addPoint2d(pln.entity,solver.v0,solver.v0,group=g)
params = [] params = []
points = [] points = []
v = shape.Vertexes v = shape.Vertexes
@ -310,10 +301,11 @@ def _c(solver,partInfo,subname,shape,requireArc=False,retAll=False):
system.NameTag = nameTag+n.format(i) system.NameTag = nameTag+n.format(i)
params.append(system.addParamV(val,group=g)) params.append(system.addParamV(val,group=g))
system.NameTag = nameTag + '.p{}'.format(i) system.NameTag = nameTag + '.p{}'.format(i)
points.append(system.addPoint2d(w,*params[-2:],group=g)) pt = system.addPoint2d(pln.entity,*params[-2:],group=g)
points.append(pt)
system.NameTag = nameTag system.NameTag = nameTag
e = system.addArcOfCircle(w,center,*points,group=g) e = system.addArcOfCircle(pln.entity,center,*points,group=g)
h = [e,points[1],points[0],params] h = ArcInfo(entity=e,p1=points[1],p0=points[0],params=params)
system.log('{}: add draft arc {}, {}'.format(key,h,g)) system.log('{}: add draft arc {}, {}'.format(key,h,g))
# exhaust all possible keys from a draft circle to save # exhaust all possible keys from a draft circle to save
@ -321,8 +313,7 @@ def _c(solver,partInfo,subname,shape,requireArc=False,retAll=False):
sub = subname + '.c' if requireArc else '.a' sub = subname + '.c' if requireArc else '.a'
partInfo.EntityMap[sub] = h partInfo.EntityMap[sub] = h
else: else:
w,p,n = _w(solver,partInfo,subname,shape,True)[:3] pln = _w(solver,partInfo,subname,shape,True)
n = n[0]
r = utils.getElementCircular(shape) r = utils.getElementCircular(shape)
if not r: if not r:
raise RuntimeError('shape is not cicular') raise RuntimeError('shape is not cicular')
@ -331,16 +322,19 @@ def _c(solver,partInfo,subname,shape,requireArc=False,retAll=False):
if requireArc or isinstance(r,(list,tuple)): if requireArc or isinstance(r,(list,tuple)):
l = _l(solver,partInfo,subname,shape,True) l = _l(solver,partInfo,subname,shape,True)
system.NameTag = nameTag system.NameTag = nameTag
h = system.addArcOfCircle(w,p,l[1],l[2],group=g) 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)
else: else:
system.NameTag = nameTag system.NameTag = nameTag
h = system.addCircle(p,n,hr,group=g) h = system.addCircle(
h = (h,hr) pln.origin.entity, pln.normal.entity, hr, group=g)
h = CircleInfo(entity=h,radius=hr,p0=None)
system.log('{}: {},{}'.format(key,h,g)) system.log('{}: {},{}'.format(key,h,g))
partInfo.EntityMap[key] = h partInfo.EntityMap[key] = h
return h if retAll else h[0] return h if retAll else h.entity
def _dc(solver,partInfo,subname,shape,requireArc=False,retAll=False): def _dc(solver,partInfo,subname,shape,requireArc=False,retAll=False):
'return a handle of a draft circle' 'return a handle of a draft circle'
@ -519,21 +513,36 @@ class Constraint(ProxyType):
def _makeProp(name,tp,doc='',getter=propGet,internal=False,default=None): def _makeProp(name,tp,doc='',getter=propGet,internal=False,default=None):
PropertyInfo(Constraint,name,tp,doc,getter=getter, return PropertyInfo(Constraint,name,tp,doc,getter=getter,duplicate=True,
group='Constraint',internal=internal,default=default) group='Constraint',internal=internal,default=default).Key
_makeProp('Distance','App::PropertyDistance',getter=propGetValue) _makeProp('Distance','App::PropertyDistance',getter=propGetValue)
_makeProp('Length','App::PropertyDistance',getter=propGetValue,default=5.0) _makeProp('Length','App::PropertyDistance',getter=propGetValue,default=5.0)
_makeProp('Offset','App::PropertyDistance',getter=propGetValue) _makeProp('Offset','App::PropertyDistance',getter=propGetValue)
_makeProp('Cascade','App::PropertyBool',internal=True) _makeProp('Cascade','App::PropertyBool',internal=True)
_makeProp('Angle','App::PropertyAngle',getter=propGetValue) _makeProp('Angle','App::PropertyAngle',getter=propGetValue)
_makeProp('LockAngle','App::PropertyBool')
_AngleProps = [
_makeProp('LockAngle','App::PropertyBool',
doc='Enforce an angle offset defined as yaw-pitch-roll angle of the\n'
'second plane performed in the order of x-y-z'),
_makeProp('Angle','App::PropertyAngle',getter=propGetValue,
doc='The rotation angle of the second plane about its z-axis.\n'
'You need to enable LockAngle for this to take effect.'),
_makeProp('AnglePitch','App::PropertyAngle',getter=propGetValue,
doc='Rotation angle of the second plane about its y-axis.\n'
'You need to enable LockAngle for this to take effect.'),
_makeProp('AngleRoll','App::PropertyAngle',getter=propGetValue,
doc='Rotation angle of the second plane about its x-axis\n'
'You need to enable LockAngle for this to take effect.'),
]
_makeProp('Ratio','App::PropertyFloat',default=1.0) _makeProp('Ratio','App::PropertyFloat',default=1.0)
_makeProp('Difference','App::PropertyFloat') _makeProp('Difference','App::PropertyFloat')
_makeProp('Diameter','App::PropertyDistance',getter=propGetValue,default=10.0) _makeProp('Diameter','App::PropertyDistance',getter=propGetValue,default=10.0)
_makeProp('Radius','App::PropertyDistance',getter=propGetValue,default=5.0) _makeProp('Radius','App::PropertyDistance',getter=propGetValue,default=5.0)
_makeProp('Supplement','App::PropertyBool', _makeProp('Supplement','App::PropertyBool',
'If True, then the second angle is calculated as 180-angle') 'If True, then the angle is calculated as 180-angle')
_makeProp('AtEnd','App::PropertyBool', _makeProp('AtEnd','App::PropertyBool',
'If True, then tangent at the end point, or else at the start point') 'If True, then tangent at the end point, or else at the start point')
@ -624,7 +633,7 @@ class Base(object):
if cls._workplane and len(elements)==len(cls._entityDef): if cls._workplane and len(elements)==len(cls._entityDef):
if solver.system.sketchPlane: if solver.system.sketchPlane:
ret.append(solver.system.sketchPlane[0]) ret.append(solver.system.sketchPlane.entity)
elif int(cls._workplane)>1: elif int(cls._workplane)>1:
raise RuntimeError('Constraint "{}" requires a sketch plane ' raise RuntimeError('Constraint "{}" requires a sketch plane '
'or a {} element to define a projection plane'.format( 'or a {} element to define a projection plane'.format(
@ -755,7 +764,7 @@ class Locked(Base):
e0 = e1 e0 = e1
system.NameTag = nameTag + surfix system.NameTag = nameTag + surfix
if system.sketchPlane and utils.isDraftObject(info.Part): if system.sketchPlane and utils.isDraftObject(info.Part):
w = system.sketchPlane[0] w = system.sketchPlane.entity
else: else:
w = 0 w = 0
e = system.addPointsCoincident(e1,e2,w,group=solver.group) e = system.addPointsCoincident(e1,e2,w,group=solver.group)
@ -901,7 +910,7 @@ class BaseCascade(BaseMulti):
class PlaneCoincident(BaseCascade): class PlaneCoincident(BaseCascade):
_id = 35 _id = 35
_iconName = 'Assembly_ConstraintCoincidence.svg' _iconName = 'Assembly_ConstraintCoincidence.svg'
_props = ['Cascade','Offset','LockAngle','Angle'] _props = ['Cascade','Offset'] + _AngleProps
_tooltip = \ _tooltip = \
'Add a "{}" constraint to conincide planes of two or more parts.\n'\ 'Add a "{}" constraint to conincide planes of two or more parts.\n'\
'The planes are coincided at their centers with an optional distance.' 'The planes are coincided at their centers with an optional distance.'
@ -909,7 +918,7 @@ class PlaneCoincident(BaseCascade):
class PlaneAlignment(BaseCascade): class PlaneAlignment(BaseCascade):
_id = 37 _id = 37
_iconName = 'Assembly_ConstraintAlignment.svg' _iconName = 'Assembly_ConstraintAlignment.svg'
_props = ['Cascade','Offset','LockAngle','Angle'] _props = ['Cascade','Offset'] + _AngleProps
_tooltip = 'Add a "{}" constraint to rotate planes of two or more parts\n'\ _tooltip = 'Add a "{}" constraint to rotate planes of two or more parts\n'\
'into the same orientation' 'into the same orientation'
@ -917,7 +926,7 @@ class PlaneAlignment(BaseCascade):
class AxialAlignment(BaseMulti): class AxialAlignment(BaseMulti):
_id = 36 _id = 36
_iconName = 'Assembly_ConstraintAxial.svg' _iconName = 'Assembly_ConstraintAxial.svg'
_props = ['LockAngle','Angle'] _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'\
'The planes are aligned at the direction of their surface normal axis.' 'The planes are aligned at the direction of their surface normal axis.'
@ -934,8 +943,8 @@ class MultiParallel(BaseMulti):
_id = 291 _id = 291
_entityDef = (_lw,) _entityDef = (_lw,)
_iconName = 'Assembly_ConstraintMultiParallel.svg' _iconName = 'Assembly_ConstraintMultiParallel.svg'
_props = ['LockAngle','Angle'] _props = _AngleProps
_tooltip = 'Add a "{}" constraint to make planes normal or linear edges\n'\ _tooltip = 'Add a "{}" constraint to make planes ormal or linear edges\n'\
'of two or more parts parallel.' 'of two or more parts parallel.'

View File

@ -4,7 +4,8 @@ import FreeCAD, FreeCADGui
from .assembly import Assembly, isTypeOf, setPlacement from .assembly import Assembly, isTypeOf, setPlacement
from . import utils from . import utils
from .utils import syslogger as logger, objName, isSamePlacement from .utils import syslogger as logger, objName, isSamePlacement
from .constraint import Constraint, cstrName from .constraint import Constraint, cstrName, \
NormalInfo, PlaneInfo, PointInfo
from .system import System from .system import System
# Part: the part object # Part: the part object
@ -44,13 +45,17 @@ class Solver(object):
self.v0 = self.system.addParamV(0,group=self._fixedGroup) self.v0 = self.system.addParamV(0,group=self._fixedGroup)
self.v1 = self.system.addParamV(1,group=self._fixedGroup) self.v1 = self.system.addParamV(1,group=self._fixedGroup)
# convenience normals # convenience x normals
rotx = FreeCAD.Rotation(FreeCAD.Vector(0,1,0),-90) rotx = FreeCAD.Rotation(FreeCAD.Vector(0,1,0),-90)
self.nx = self.system.addNormal3dV(*utils.getNormal(rotx)) self.nx = self.system.addNormal3dV(*utils.getNormal(rotx))
# convenience x pointing vector # convenience x pointing vector
self.px = self.system.addPoint3d(self.v1,self.v0,self.v0) self.px = self.system.addPoint3d(self.v1,self.v0,self.v0)
# convenience y normals
roty = FreeCAD.Rotation(FreeCAD.Vector(1,0,0),90)
self.ny = self.system.addNormal3dV(*utils.getNormal(roty))
self._fixedParts = Constraint.getFixedParts(self,cstrs) self._fixedParts = Constraint.getFixedParts(self,cstrs)
for part in self._fixedParts: for part in self._fixedParts:
self._fixedElements.add((part,None)) self._fixedElements.add((part,None))
@ -115,10 +120,11 @@ class Solver(object):
changed = False changed = False
points = part.Points points = part.Points
for key,h in partInfo.EntityMap.items(): for key,h in partInfo.EntityMap.items():
if not key.endswith('.p') or\ if not isinstance(key, str) or\
not key.endswith('.p') or\
not key.startswith('Vertex'): not key.startswith('Vertex'):
continue continue
v = [ self.system.getParam(p).val for p in h[1] ] v = [ self.system.getParam(p).val for p in h.params ]
v = FreeCAD.Vector(*v) v = FreeCAD.Vector(*v)
v = partInfo.Placement.inverse().multVec(v) v = partInfo.Placement.inverse().multVec(v)
idx = utils.draftWireVertex2PointIndex(part,key[:-2]) idx = utils.draftWireVertex2PointIndex(part,key[:-2])
@ -163,9 +169,9 @@ class Solver(object):
part.FirstAngle.Value, part.FirstAngle.Value,
part.LastAngle.Value) part.LastAngle.Value)
if part.FirstAngle == part.LastAngle: if part.FirstAngle == part.LastAngle:
v = (self.system.getParam(h[1]).val,v0[1],v0[2]) v = (self.system.getParam(h.radius).val,v0[1],v0[2])
else: else:
params = [self.system.getParam(p).val for p in h[3]] params = [self.system.getParam(p).val for p in h.params]
p0 = FreeCAD.Vector(1,0,0) p0 = FreeCAD.Vector(1,0,0)
p1 = FreeCAD.Vector(params[0],params[1],0) p1 = FreeCAD.Vector(params[0],params[1],0)
p2 = FreeCAD.Vector(params[2],params[3],0) p2 = FreeCAD.Vector(params[2],params[3],0)
@ -222,14 +228,13 @@ 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 + '.nx'
nx = self.system.addTransform(self.nx,
self.v0,self.v0,self.v0,*params[3:],group=g)
px = self.system.addTransform(self.px,self.v0,self.v0,
self.v0,*params[3:],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 = (w,p,(n,nx,px)) h = PlaneInfo(entity=w,
origin=PointInfo(entity=p, params=None,
vertex=FreeCAD.Vector()),
normal=NormalInfo(entity=n,rot=FreeCAD.Rotation(),
params=params))
partInfo = PartInfo(Part = info.Part, partInfo = PartInfo(Part = info.Part,
PartName = info.PartName, PartName = info.PartName,

105
system.py
View File

@ -1,6 +1,7 @@
import os import os
import FreeCAD
from .constraint import cstrName from .constraint import cstrName
from .utils import getIcon, syslogger as logger, objName, project2D from .utils import getIcon, syslogger as logger, objName, project2D, getNormal
from .proxy import ProxyType, PropertyInfo from .proxy import ProxyType, PropertyInfo
class System(ProxyType): class System(ProxyType):
@ -130,22 +131,26 @@ class SystemExtension(object):
self.sketchPlane = args[0] if args else None self.sketchPlane = args[0] if args else None
return self.sketchPlane return self.sketchPlane
def setOrientation(self,h,lockAngle,angle,n1,n2,nx1,nx2,group): def setOrientation(self,h,lockAngle,yaw,pitch,roll,n1,n2,group):
if lockAngle and not angle: if not lockAngle:
h.append(self.addSameOrientation(n1,n2,group=group)) h.append(self.addParallel(n1.entity,n2.entity,group=group))
return
if not yaw and not pitch and not roll:
n = n2.entity
else: else:
h.append(self.addParallel(n1,n2,group=group)) rot = n2.rot.multiply(FreeCAD.Rotation(yaw,pitch,roll))
if lockAngle: e = self.addNormal3dV(*getNormal(rot))
h.append(self.addAngle(angle,False,nx1,nx2,group=group)) n = self.addTransform(e,*n2.params,group=group)
h.append(self.addSameOrientation(n1.entity,n,group=group))
return h return h
def reportRedundancy(self,warn=False): def reportRedundancy(self,warn=False):
msg = '{} between {} and {}'.format(cstrName(self.cstrObj), msg = '{} between {} and {}'.format(cstrName(self.cstrObj),
self.firstInfo.PartName, self.secondInfo.PartName) self.firstInfo.PartName, self.secondInfo.PartName)
if warn: if warn:
logger.warn('skip redundant ' + msg) logger.warn('skip redundant ' + msg, frame=1)
else: else:
logger.info('auto relax ' + msg) logger.info('auto relax ' + msg, frame=1)
def countConstraints(self,increment,count,*names): def countConstraints(self,increment,count,*names):
first,second = self.firstInfo,self.secondInfo first,second = self.firstInfo,self.secondInfo
@ -163,21 +168,18 @@ class SystemExtension(object):
if increment: if increment:
cstrs += [None]*increment cstrs += [None]*increment
ret += len(cstrs) ret += len(cstrs)
if count and ret >= count: if count and ret>=count:
if ret>count: if ret>count:
self.reportRedundancy(True) self.reportRedundancy(True)
return -1 return -1
else: elif ret!=len(cstrs):
self.reportRedundancy() self.reportRedundancy()
return ret return ret
def addPlaneCoincident(self,d,lockAngle,angle,e1,e2,group=0): def addPlaneCoincident(
self, d, lockAngle, yaw, pitch, roll, pln1, pln2, group=0):
if not group: if not group:
group = self.GroupHandle group = self.GroupHandle
w1,p1,n1 = e1[:3]
_,p2,n2 = e2[:3]
n1,nx1 = n1[:2]
n2,nx2 = n2[:2]
h = [] h = []
count = self.countConstraints(2 if lockAngle else 1,2,'Coincident') count = self.countConstraints(2 if lockAngle else 1,2,'Coincident')
if count<0: if count<0:
@ -191,28 +193,29 @@ 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(self.firstInfo.EntityMap[n1][0], v1,v2 = project2D(pln1.normal.rot,
self.firstInfo.EntityMap[p1][0], pln1.origin.vertex, pln2.origin.vertex)
self.secondInfo.EntityMap[p2][0])
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(p1,p2,w1,group=group)) h.append(self.addPointsHorizontal(pln1.origin.entity,
pln2.origin.entity, pln1.entity, group=group))
else: else:
h.append(self.addPointsVertical(p1,p2,w1,group=group)) h.append(self.addPointsVertical(pln1.origin.entity,
pln2.origin.entity, pln1.entity, group=group))
return h return h
if d: if d:
h.append(self.addPointPlaneDistance(d,p2,w1,group=group)) h.append(self.addPointPlaneDistance(
h.append(self.addPointsCoincident(p1,p2,w1,group=group)) d, pln2.origin.entity, pln1.entity, group=group))
h.append(self.addPointsCoincident(pln1.origin.entity,
pln2.origin.entity, pln1.entity, group=group))
else: else:
h.append(self.addPointsCoincident(p1,p2,group=group)) h.append(self.addPointsCoincident(
return self.setOrientation(h,lockAngle,angle,n1,n2,nx1,nx2,group) pln1.origin.entity, pln2.origin.entity, group=group))
return self.setOrientation(h, lockAngle, yaw, pitch, roll,
pln1.normal, pln2.normal, group)
def addPlaneAlignment(self,d,lockAngle,angle,e1,e2,group=0): def addPlaneAlignment(self,d,lockAngle,yaw,pitch,roll,pln1,pln2,group=0):
if not group: if not group:
group = self.GroupHandle group = self.GroupHandle
w1,_,n1 = e1[:4]
_,p2,n2 = e2[:4]
n1,nx1 = n1[:2]
n2,nx2 = n2[:2]
h = [] h = []
if self.relax: if self.relax:
count = self.countConstraints(2 if lockAngle else 1,3,'Alignment') count = self.countConstraints(2 if lockAngle else 1,3,'Alignment')
@ -221,49 +224,51 @@ class SystemExtension(object):
else: else:
count = 0 count = 0
if d: if d:
h.append(self.addPointPlaneDistance(d,p2,w1,group=group)) h.append(self.addPointPlaneDistance(
d, pln2.origin.entity, pln1.entity.entity, group=group))
else: else:
h.append(self.addPointInPlane(p2,w1,group=group)) h.append(self.addPointInPlane(
pln2.origin.entity, pln1.entity,group=group))
if count<=2: if count<=2:
if count==2 and not lockAngle: if count==2 and not lockAngle:
self.reportRedundancy() self.reportRedundancy()
h.append(self.setOrientation(h,lockAngle,angle,n1,n2,nx1,nx2,group)) self.setOrientation(h, lockAngle, yaw, pitch, roll,
pln1.normal, pln2.normal, group)
return h return h
def addAxialAlignment(self,lockAngle,angle,e1,e2,group=0): def addAxialAlignment(self,lockAngle,yaw,pitch,roll,pln1,pln2,group=0):
if not group: if not group:
group = self.GroupHandle group = self.GroupHandle
count = self.countConstraints(0,2,'Coincident') count = self.countConstraints(0,2,'Coincident')
if count<0: if count<0:
return return
if count: if count:
return self.addPlaneCoincident(False,0,0,e1,e2,group) return self.addPlaneCoincident(0,False,0,0,0,pln1,pln2,group)
w1,p1,n1 = e1[:3]
_,p2,n2 = e2[:3]
n1,nx1 = n1[:2]
n2,nx2 = n2[:2]
h = [] h = []
h.append(self.addPointsCoincident(p1,p2,w1,group=group)) h.append(self.addPointsCoincident(pln1.origin.entity,
return self.setOrientation(h,lockAngle,angle,n1,n2,nx1,nx2,group) pln2.origin.entity, pln2.entity, group=group))
return self.setOrientation(h, lockAngle, yaw, pitch, roll,
pln1.normal, pln2.normal, group)
def addMultiParallel(self,lockAngle,angle,e1,e2,group=0): def addMultiParallel(self,lockAngle,yaw,pitch,raw,e1,e2,group=0):
h = [] h = []
isPlane = isinstance(e1,list),isinstance(e2,list) isPlane = isinstance(e1,list),isinstance(e2,list)
if all(isPlane): if all(isPlane):
return self.setOrientation( return self.setOrientation(h, lockAngle, yaw, pitch, raw,
h,lockAngle,angle,e1[2],e2[2],e1[3],e2[3],group); e1.normal, e2.normal, group);
if not any(isPlane): if not any(isPlane):
h.append(self.addParallel(e1,e2,group=group)) h.append(self.addParallel(
e1.normal.entity, e2.normal.entity, group=group))
elif isPlane[0]: elif isPlane[0]:
h.append(self.addPerpendicular(e1[2],e2,group=group)) h.append(self.addPerpendicular(e1.normal.entity, e2, group=group))
else: else:
h.append(self.addPerpendicular(e1,e2[2],group=group)) h.append(self.addPerpendicular(e1, e2.normal.entity, group=group))
return h return h
def addColinear(self,e1,e2,wrkpln=0,group=0): def addColinear(self,l1,l2,wrkpln=0,group=0):
h = [] h = []
h.append(self.addParallel(e1[0],e2,wrkpln=wrkpln,group=group)) h.append(self.addParallel(l1.entity,l2,wrkpln=wrkpln,group=group))
h.append(self.addPointOnLine(e1[1],e2,wrkpln=wrkpln,group=group)) h.append(self.addPointOnLine(l1.entity,l2,wrkpln=wrkpln,group=group))
return h return h
def addPlacement(self,pla,group=0): def addPlacement(self,pla,group=0):