Add support for draft wire

Special treatment for non-closed-or-subdivided draft wire object
directly added into part group.  Instead of constraining on the object
placement, we shall constraint on individual points. In other word,
instead of making the draft wire's placement as free parameters, we
make the cooridnates of indvidual points as free parameters.
This commit is contained in:
Zheng, Lei 2018-01-19 14:10:43 +08:00
parent cd9a60c29d
commit 05595e3f41
5 changed files with 354 additions and 103 deletions

View File

@ -488,17 +488,14 @@ class ViewProviderAsmElement(ViewProviderAsmOnTop):
Group=owner, Subname=subname),undo=True)
PartInfo = namedtuple('AsmPartInfo', ('Parent','SubnameRef','Part',
'PartName','Placement','Object','Subname','Shape'))
def getPartInfo(parent, subname):
'''Return a named tuple containing the part object element information
Parameters:
parent: the parent document object, either an assembly, or a part group
parent: the parent document object, either an assembly, or a part group
subname: subname reference to the part element (i.e. edge, face, vertex)
subname: subname reference to the part element (i.e. edge, face, vertex)
Return a named tuple with the following fields:
@ -513,8 +510,8 @@ def getPartInfo(parent, subname):
Placement: the placement of the part
Object: the object that owns the element. In case 'Part' is an assembly, we
the element owner will always be some (grand)child of the 'Part'
Object: the object that owns the element. In case 'Part' is an assembly, the
element owner will always be some (grand)child of the 'Part'
Subname: the subname reference to the element owner object. The reference is
realtive to the 'Part', i.e. Object = Part.getSubObject(subname), or if
@ -633,7 +630,7 @@ def getPartInfo(parent, subname):
obj = part.getLinkedObject(False)
partName = part.Name
return PartInfo(Parent = parent,
return utils.PartInfo(Parent = parent,
SubnameRef = subnameRef,
Part = part,
PartName = partName,
@ -876,16 +873,16 @@ class AsmConstraint(AsmGroup):
ret = getattr(self,'elements',None)
if ret or Constraint.isDisabled(obj):
return ret
shapes = []
infos = []
elements = []
for o in obj.Group:
checkType(o,AsmElementLink)
info = o.Proxy.getInfo()
if not info:
return
shapes.append(info.Shape)
infos.append(info)
elements.append(o)
Constraint.check(obj,shapes,True)
Constraint.check(obj,infos,True)
self.elements = elements
return self.elements
@ -981,7 +978,7 @@ class AsmConstraint(AsmGroup):
if not Constraint.isDisabled(cstr):
if cstr:
typeid = Constraint.getTypeID(cstr)
check = [o.Proxy.getInfo().Shape for o in cstr.Group] + elements
check = [o.Proxy.getInfo() for o in cstr.Group] + elements
else:
check = elements
Constraint.check(typeid,check)
@ -1137,7 +1134,8 @@ class AsmElementGroup(AsmGroup):
return
for i,c in enumerate(reversed(label)):
if not c.isdigit():
label = label[:i+1]
if i:
label = label[:-i]
break;
i=0
while True:

View File

@ -7,12 +7,18 @@ from .proxy import ProxyType, PropertyInfo, propGet, propGetValue
import os
_iconPath = os.path.join(utils.iconPath,'constraints')
def _p(solver,partInfo,subname,shape):
def _p(solver,partInfo,subname,shape,retAll=False):
'return a handle of a transformed point derived from "shape"'
if not solver:
if utils.hasCenter(shape):
if not utils.hasCenter(shape):
return 'a vertex or circular edge/face'
if not utils.isDraftWire(partInfo):
return
return 'a vertex or circular edge/face'
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
@ -20,20 +26,37 @@ def _p(solver,partInfo,subname,shape):
system.log('cache {}: {}'.format(key,h))
else:
v = utils.getElementPos(shape)
system.NameTag = key
e = system.addPoint3dV(*v)
system.NameTag = partInfo.PartName + '.' + key
h = system.addTransform(e,*partInfo.Params,group=partInfo.Group)
system.log('{}: {},{}'.format(key,h,partInfo.Group))
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
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 utils.isPlanar(shape):
return
return 'an edge or face with a surface normal'
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
@ -43,17 +66,17 @@ def _n(solver,partInfo,subname,shape,retAll=False):
h = []
rot = utils.getElementRotation(shape)
system.NameTag = key
e = system.addNormal3dV(*utils.getNormal(rot))
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 = key + 'x'
e = system.addNormal3dV(*utils.getNormal(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))
@ -63,29 +86,50 @@ def _n(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"'
if not solver:
if utils.isLinearEdge(shape):
if not utils.isLinearEdge(shape):
return 'a linear edge'
if not utils.isDraftWire(partInfo):
return
return 'a linear edge'
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:
system.NameTag = key
v = shape.Edges[0].Vertexes
p1 = system.addPoint3dV(*v[0].Point)
p2 = system.addPoint3dV(*v[-1].Point)
nameTag = partInfo.PartName + '.' + key
system.NameTag = nameTag + '.p1'
tp1 = system.addTransform(p1,*partInfo.Params,group=partInfo.Group)
system.NameTag = nameTag + '.p2'
tp2 = system.addTransform(p2,*partInfo.Params,group=partInfo.Group)
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,p1,p2)
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):
@ -120,10 +164,11 @@ def _w(solver,partInfo,subname,shape,retAll=False):
partInfo.EntityMap[key] = h
return h if retAll else h[0]
def _wa(solver,partInfo,subname,shape):
def _wa(solver,partInfo,subname,shape,retAll=False):
_ = retAll
return _w(solver,partInfo,subname,shape,True)
def _c(solver,partInfo,subname,shape,requireArc=False):
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)
@ -143,23 +188,24 @@ def _c(solver,partInfo,subname,shape,requireArc=False):
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 = partInfo.PartName + '.' + key
system.NameTag = nameTag
h = system.addArcOfCircle(w,p,l[1],l[2],group=partInfo.Group)
else:
nameTag = partInfo.PartName + '.' + key
system.NameTag = nameTag + '.r'
hr = system.addDistanceV(r)
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
return h if retAll else h[0]
def _a(solver,partInfo,subname,shape):
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)
return _c(solver,partInfo,subname,shape,True,retAll)
class ConstraintCommand:
@ -312,6 +358,7 @@ def _makeProp(name,tp,doc='',getter=propGet,internal=False):
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)
@ -381,7 +428,10 @@ class Base(object):
entities = cls.getEntityDef(group,checkCount)
for i,e in enumerate(entities):
o = group[i]
msg = e(None,None,None,o)
if isinstance(o,utils.PartInfo):
msg = e(None,o.Part,o.Subname,o.Shape)
else:
msg = e(None,None,None,o)
if not msg:
continue
if i == len(cls._entityDef):
@ -396,7 +446,7 @@ class Base(object):
return utils.getIcon(cls,Constraint.isDisabled(obj),_iconPath)
@classmethod
def getEntities(cls,obj,solver):
def getEntities(cls,obj,solver,retAll=False):
'''maps fcad element shape to entities'''
elements = obj.Proxy.getElements()
entities = cls.getEntityDef(elements,True,obj)
@ -404,7 +454,7 @@ class Base(object):
for e,o in zip(entities,elements):
info = o.Proxy.getInfo()
partInfo = solver.getPartInfo(info)
ret.append(e(solver,partInfo,info.Subname,info.Shape))
ret.append(e(solver,partInfo,info.Subname,info.Shape,retAll=retAll))
solver.system.log('{} entities: {}'.format(cstrName(obj),ret))
return ret
@ -476,32 +526,64 @@ class Locked(Base):
@classmethod
def prepare(cls,obj,solver):
ret = []
system = solver.system
for element in obj.Proxy.getElements():
info = element.Proxy.getInfo()
if not utils.isVertex(info.Shape) and \
not utils.isLinearEdge(info.Shape):
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)
system = solver.system
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):
subname = info.Subname+'.'+str(i)
system.NameTag = subname + '.tp'
surfix = '.fp{}'.format(i)
system.NameTag = nameTag + surfix
# Create an entity for the transformed constant point
e1 = system.addPoint3dV(*info.Placement.multVec(v.Point))
e2 = _p(solver,partInfo,subname,v)
if i==0:
# 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 = partInfo.PartName + '.' + info.Subname
ret.append(system.addPointsCoincident(
e1,e2,group=solver.group))
system.NameTag = nameTag + surfix
e = system.addPointsCoincident(e1,e2,group=solver.group)
system.log('{}: fix point {},{},{}'.format(
cstrName(obj),e,e1,e2))
else:
system.NameTag = info.Subname + 'tl'
# 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 = partInfo.PartName + '.' + info.Subname
ret.append(system.addPointOnLine(e2,l,group=solver.group))
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
@ -522,7 +604,10 @@ class BaseMulti(Base):
raise RuntimeError('Constraint "{}" requires at least two '
'elements'.format(cls.getName()))
for o in group:
msg = cls._entityDef[0](None,None,None,o)
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()))
@ -842,7 +927,43 @@ class BaseSketch(Base):
_toolbarName = 'Assembly3 Sketch Constraints'
class EqualLength(BaseSketch):
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
@ -850,7 +971,7 @@ class EqualLength(BaseSketch):
_tooltip='Add a "{}" constraint to make two lines of the same length.'
class LengthRatio(BaseSketch):
class LengthRatio(BaseDraftWire):
_id = 10
_entityDef = (_l,_l)
_workplane = True
@ -859,7 +980,7 @@ class LengthRatio(BaseSketch):
_tooltip='Add a "{}" to constrain the length ratio of two lines.'
class LengthDifference(BaseSketch):
class LengthDifference(BaseDraftWire):
_id = 11
_entityDef = (_l,_l)
_workplane = True
@ -877,13 +998,27 @@ class EqualLengthPointLineDistance(BaseSketch):
'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
@ -893,13 +1028,25 @@ class MidPoint(BaseSketch):
_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
@ -907,6 +1054,17 @@ class EqualRadius(BaseSketch):
_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

View File

@ -2,10 +2,12 @@ import random
from collections import namedtuple
import FreeCAD, FreeCADGui
from .assembly import Assembly, isTypeOf, setPlacement
from . import utils
from .utils import syslogger as logger, objName, isSamePlacement
from .constraint import Constraint, cstrName
from .system import System
# Part: the part object
# PartName: text name of the part
# Placement: the original placement of the part
# Params: 7 parameters that defines the transformation of this part
@ -14,8 +16,8 @@ from .system import System
# norml, is essentially the XY reference plane of the part.
# EntityMap: string -> entity handle map, for caching
# Group: transforming entity group handle
PartInfo = namedtuple('SolverPartInfo',
('PartName','Placement','Params','Workplane','EntityMap','Group'))
PartInfo = namedtuple('SolverPartInfo', ('Part','PartName','Placement',
'Params','Workplane','EntityMap','Group'))
class Solver(object):
def __init__(self,assembly,reportFailed,dragPart,recompute,rollback):
@ -93,21 +95,48 @@ class Solver(object):
for part,partInfo in self._partMap.items():
if part in self._fixedParts:
continue
params = [ self.system.getParam(h).val for h in partInfo.Params ]
p = params[:3]
q = (params[4],params[5],params[6],params[3])
pla = FreeCAD.Placement(FreeCAD.Vector(*p),FreeCAD.Rotation(*q))
if isSamePlacement(partInfo.Placement,pla):
self.system.log('not moving {}'.format(partInfo.PartName))
if utils.isDraftWire(part):
pointChanged = False
points = part.Points
for subname,h in partInfo.EntityMap.items():
if not subname.endswith('.p') or\
not subname.startswith('Vertex'):
continue
v = [ self.system.getParam(p).val for p in h[1] ]
v = FreeCAD.Vector(*v)
v = partInfo.Placement.inverse().multVec(v)
idx = utils.draftWireVertex2PointIndex(part,subname[:-2])
if utils.isSamePos(points[idx],v):
self.system.log('not moving {} point {}'.format(
partInfo.PartName,idx))
else:
pointChanged = True
self.system.log('moving {} point{} from {}->{}'.format(
partInfo.PartName,idx,points[idx],v))
if rollback is not None:
rollback.append((partInfo.PartName,
part,
(idx, points[idx])))
points[idx] = v
if pointChanged:
touched = True
part.Points = points
else:
touched = True
self.system.log('moving {} {} {} {}'.format(
partInfo.PartName,partInfo.Params,params,pla))
setPlacement(part,pla)
if rollback is not None:
rollback.append((partInfo.PartName,
part,
partInfo.Placement.copy()))
params = [self.system.getParam(h).val for h in partInfo.Params]
p = params[:3]
q = (params[4],params[5],params[6],params[3])
pla = FreeCAD.Placement(FreeCAD.Vector(*p),FreeCAD.Rotation(*q))
if isSamePlacement(partInfo.Placement,pla):
self.system.log('not moving {}'.format(partInfo.PartName))
else:
touched = True
self.system.log('moving {} {} {} {}'.format(
partInfo.PartName,partInfo.Params,params,pla))
setPlacement(part,pla)
if rollback is not None:
rollback.append((partInfo.PartName,
part,
partInfo.Placement.copy()))
if recompute and touched:
assembly.recompute(True)
@ -125,18 +154,25 @@ class Solver(object):
else:
g = self.group
self.system.NameTag = info.PartName
params = self.system.addPlacement(info.Placement,group=g)
if utils.isDraftWire(info):
# Special treatment for draft wire. We do not change its placement,
# but individual point position, instead.
params = None
h = None
else:
self.system.NameTag = info.PartName
params = self.system.addPlacement(info.Placement,group=g)
self.system.NameTag = info.PartName + '.p'
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 + '.w'
w = self.system.addWorkplane(p,n,group=g)
h = (w,p,n)
self.system.NameTag = info.PartName + '.p'
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 + '.w'
w = self.system.addWorkplane(p,n,group=g)
h = (w,p,n)
partInfo = PartInfo(PartName = info.PartName,
partInfo = PartInfo(Part = info.Part,
PartName = info.PartName,
Placement = info.Placement.copy(),
Params = params,
Workplane = h,
@ -211,7 +247,11 @@ def _solve(objs=None,recursive=None,reportFailed=True,
if rollback is not None:
for name,part,pla in reversed(rollback):
logger.debug('roll back {} to {}'.format(name,pla))
setPlacement(part,pla)
if utils.isDraftWire(part):
idx,pt = pla
part.Points[idx] = pt
else:
setPlacement(part,pla)
raise
return True

View File

@ -455,7 +455,7 @@ def _directionConsine(wrkpln,l1,l2,supplement=False):
v1,v2 = _project(wrkpln,l1,l2)
if supplement:
v1 = v1 * -1.0
return v1.cross(v2)/(v1.magnitude()*v2.magnitude())
return v1.dot(v2)/(v1.magnitude()*v2.magnitude())
_x = 'i'
_y = 'j'
@ -1016,7 +1016,7 @@ class _Angle(_ProjectingConstraint):
return _directionConsine(self.wrkpln,self.l1,self.l2,self.supplement)
def getEq(self):
return self.DirectionCosine - sp.cos(self.degree.SymObj*sp.pi/180.0)
return self.DirectionCosine - sp.cos(sp.pi*self.degree/180.0)
class _Perpendicular(_Angle):
_args = ('l1', 'l2',)

View File

@ -5,7 +5,8 @@ Most of the functions are borrowed directly from assembly2lib.py or lib3D.py in
assembly2
'''
import FreeCAD, FreeCADGui, Part
from collections import namedtuple
import FreeCAD, FreeCADGui, Part, Draft
import numpy as np
from .FCADLogger import FCADLogger
rootlogger = FCADLogger('asm3')
@ -125,8 +126,30 @@ def getElementShape(obj,tp):
if len(f)==1:
return f[0]
PartInfo = namedtuple('AsmPartInfo', ('Parent','SubnameRef','Part',
'PartName','Placement','Object','Subname','Shape'))
def isDraftWire(obj):
if isinstance(obj,PartInfo):
obj = obj.Part
proxy = getattr(obj,'Proxy',None)
return isinstance(proxy,Draft._Wire) and \
not obj.Closed and \
not obj.Subdivisions
def isDraftCircle(obj):
if isinstance(obj,PartInfo):
obj = obj.Part
proxy = getattr(obj,'Proxy',None)
return isinstance(proxy,Draft._Circle)
def isDraftObject(obj):
return isDraftWire(obj) or isDraftCircle(obj)
def isElement(obj):
if not isinstance(obj,(tuple,list)):
if isinstance(obj,PartInfo):
shape = obj.Shape
elif not isinstance(obj,(tuple,list)):
shape = obj
else:
sobj,_,shape = obj[0].getSubObject(obj[1],2)
@ -446,6 +469,38 @@ def fit_rotation_axis_to_surface1( surface, n_u=3, n_v=3 ):
_tol = 10e-7
def isSamePos(p1,p2):
return p1.distanceToPoint(p2) < _tol
def isSamePlacement(pla1,pla2):
return pla1.Base.distanceToPoint(pla2.Base) < _tol and \
return isSamePos(pla1.Base,pla2.Base) and \
np.linalg.norm(np.array(pla1.Rotation.Q)-np.array(pla2.Rotation.Q))<_tol
def getElementIndex(name,check=None):
'Return element index, 0 if invalid'
for i,c in enumerate(reversed(name)):
if not c.isdigit():
if not i:
break
idx = int(name[-i:])
if check and '{}{}'.format(check,idx)!=name:
break
return idx
return 0
def draftWireVertex2PointIndex(obj,name):
'Convert vertex index to draft wire point index, None if invalid'
idx = getElementIndex(name,'Vertex')
# We don't support subdivision yet (checked in isDraftWire())
if idx <= 0 or not isDraftWire(obj):
return
idx -= 1
if idx < len(obj.Points):
return idx
def edge2VertexIndex(name):
'deduct the vertex index from the edge index'
idx = getElementIndex(name,'Edge')
if not idx:
return None,None
return 'Vertex{}'.format(idx),'Vertex{}'.format(idx+1)