constraint: init constraint parameter
Add init() api to calculate the some of the constraints' parameter (e.g. angle, distance, length, etc.) based on the existing elements. This can reduce excessive movement when auto recompute is on.
This commit is contained in:
parent
708668ed0b
commit
a2d639200f
|
@ -910,6 +910,9 @@ class AsmConstraint(AsmGroup):
|
||||||
self.elements = elements
|
self.elements = elements
|
||||||
return self.elements
|
return self.elements
|
||||||
|
|
||||||
|
def getElementsInfo(self):
|
||||||
|
return [ e.Proxy.getInfo() for e in self.getElements() ]
|
||||||
|
|
||||||
Selection = namedtuple('AsmConstraintSelection',
|
Selection = namedtuple('AsmConstraintSelection',
|
||||||
('SelObject','SelSubname','Assembly','Constraint','Elements'))
|
('SelObject','SelSubname','Assembly','Constraint','Elements'))
|
||||||
|
|
||||||
|
@ -1036,6 +1039,7 @@ class AsmConstraint(AsmGroup):
|
||||||
try:
|
try:
|
||||||
for e in sel.Elements:
|
for e in sel.Elements:
|
||||||
AsmElementLink.make(AsmElementLink.MakeInfo(cstr,*e))
|
AsmElementLink.make(AsmElementLink.MakeInfo(cstr,*e))
|
||||||
|
logger.catchDebug('init constraint', Constraint.init,cstr)
|
||||||
cstr.Proxy._initializing = False
|
cstr.Proxy._initializing = False
|
||||||
if undo:
|
if undo:
|
||||||
FreeCAD.closeActiveTransaction()
|
FreeCAD.closeActiveTransaction()
|
||||||
|
|
|
@ -411,8 +411,8 @@ class Constraint(ProxyType):
|
||||||
return getattr(obj,mcs._disabled,False)
|
return getattr(obj,mcs._disabled,False)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def check(mcs,tp,group,checkCount=False):
|
def check(mcs,tp,elements,checkCount=False):
|
||||||
mcs.getType(tp).check(group,checkCount)
|
mcs.getType(tp).check(elements,checkCount)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def prepare(mcs,obj,solver):
|
def prepare(mcs,obj,solver):
|
||||||
|
@ -476,6 +476,12 @@ class Constraint(ProxyType):
|
||||||
if cstr:
|
if cstr:
|
||||||
return cstr.getIcon(obj)
|
return cstr.getIcon(obj)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def init(mcs,obj):
|
||||||
|
cstr = mcs.getProxy(obj)
|
||||||
|
if cstr:
|
||||||
|
cstr.init(obj)
|
||||||
|
|
||||||
|
|
||||||
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,
|
PropertyInfo(Constraint,name,tp,doc,getter=getter,
|
||||||
|
@ -515,6 +521,10 @@ class Base(object):
|
||||||
def __init__(self,_obj):
|
def __init__(self,_obj):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def init(cls,_obj):
|
||||||
|
pass
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def getPropertyInfoList(cls):
|
def getPropertyInfoList(cls):
|
||||||
return cls._props
|
return cls._props
|
||||||
|
@ -528,30 +538,30 @@ class Base(object):
|
||||||
cstrName(obj),solver.getName()))
|
cstrName(obj),solver.getName()))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def getEntityDef(cls,group,checkCount,obj=None):
|
def getEntityDef(cls,elements,checkCount,obj=None):
|
||||||
entities = cls._entityDef
|
entities = cls._entityDef
|
||||||
if len(group) == len(entities):
|
if len(elements) == len(entities):
|
||||||
return entities
|
return entities
|
||||||
if cls._workplane and len(group)==len(entities)+1:
|
if cls._workplane and len(elements)==len(entities)+1:
|
||||||
return list(entities) + [_w]
|
return list(entities) + [_w]
|
||||||
if not checkCount and len(group)<len(entities):
|
if not checkCount and len(elements)<len(entities):
|
||||||
return entities[:len(group)]
|
return entities[:len(elements)]
|
||||||
if not obj:
|
if not obj:
|
||||||
name = cls.getName()
|
name = cls.getName()
|
||||||
else:
|
else:
|
||||||
name += cstrName(obj)
|
name += cstrName(obj)
|
||||||
if len(group)<len(entities):
|
if len(elements)<len(entities):
|
||||||
msg = entities[len(group)](None,None,None,None)
|
msg = entities[len(elements)](None,None,None,None)
|
||||||
raise RuntimeError('Constraint "{}" requires the {} element to be'
|
raise RuntimeError('Constraint "{}" requires the {} element to be'
|
||||||
' {}'.format(cls.getName(), _ordinal[len(group)], msg))
|
' {}'.format(cls.getName(), _ordinal[len(elements)], msg))
|
||||||
raise RuntimeError('Constraint {} has too many elements, expecting '
|
raise RuntimeError('Constraint {} has too many elements, expecting '
|
||||||
'only {}'.format(name,len(entities)))
|
'only {}'.format(name,len(entities)))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def check(cls,group,checkCount=False):
|
def check(cls,elements,checkCount=False):
|
||||||
entities = cls.getEntityDef(group,checkCount)
|
entities = cls.getEntityDef(elements,checkCount)
|
||||||
for i,e in enumerate(entities):
|
for i,e in enumerate(entities):
|
||||||
info = group[i]
|
info = elements[i]
|
||||||
msg = e(None,info.Part,info.Subname,info.Shape)
|
msg = e(None,info.Part,info.Subname,info.Shape)
|
||||||
if not msg:
|
if not msg:
|
||||||
continue
|
continue
|
||||||
|
@ -733,8 +743,8 @@ class Locked(Base):
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def check(cls,group,_checkCount=False):
|
def check(cls,elements,_checkCount=False):
|
||||||
if not all([utils.isElement(info.Shape) for info in group]):
|
if not all([utils.isElement(info.Shape) for info in elements]):
|
||||||
raise RuntimeError('Constraint "{}" requires all children to be '
|
raise RuntimeError('Constraint "{}" requires all children to be '
|
||||||
'of element (Vertex, Edge or Face)'.format(cls.getName()))
|
'of element (Vertex, Edge or Face)'.format(cls.getName()))
|
||||||
|
|
||||||
|
@ -744,11 +754,11 @@ class BaseMulti(Base):
|
||||||
_entityDef = (_wa,)
|
_entityDef = (_wa,)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def check(cls,group,checkCount=False):
|
def check(cls,elements,checkCount=False):
|
||||||
if checkCount and len(group)<2:
|
if checkCount and len(elements)<2:
|
||||||
raise RuntimeError('Constraint "{}" requires at least two '
|
raise RuntimeError('Constraint "{}" requires at least two '
|
||||||
'elements'.format(cls.getName()))
|
'elements'.format(cls.getName()))
|
||||||
for info in group:
|
for info in elements:
|
||||||
msg = cls._entityDef[0](None,info.Part,info.Subname,info.Shape)
|
msg = cls._entityDef[0](None,info.Part,info.Subname,info.Shape)
|
||||||
if msg:
|
if msg:
|
||||||
raise RuntimeError('Constraint "{}" requires all the element '
|
raise RuntimeError('Constraint "{}" requires all the element '
|
||||||
|
@ -896,6 +906,11 @@ class Angle(Base2):
|
||||||
_tooltip = 'Add a "{}" constraint to set the angle of planes or linear\n'\
|
_tooltip = 'Add a "{}" constraint to set the angle of planes or linear\n'\
|
||||||
'edges of two parts.'
|
'edges of two parts.'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def init(cls,obj):
|
||||||
|
shapes = [ info.Shape for info in obj.Proxy.getElementsInfo() ]
|
||||||
|
obj.Angle = utils.getElementsAngle(shapes[0],shapes[1])
|
||||||
|
|
||||||
|
|
||||||
class Perpendicular(Base2):
|
class Perpendicular(Base2):
|
||||||
_id = 28
|
_id = 28
|
||||||
|
@ -946,6 +961,12 @@ class PointsDistance(Base2):
|
||||||
_iconName = 'Assembly_ConstraintPointsDistance.svg'
|
_iconName = 'Assembly_ConstraintPointsDistance.svg'
|
||||||
_tooltip = 'Add a "{}" to constrain the distance of two points.'
|
_tooltip = 'Add a "{}" to constrain the distance of two points.'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def init(cls,obj):
|
||||||
|
points = [ info.Placement.multVec(info.Shape.Vertex1.Point)
|
||||||
|
for info in obj.Proxy.getElementsInfo() ]
|
||||||
|
obj.Distance = points[0].distanceToPoint(points[1])
|
||||||
|
|
||||||
|
|
||||||
class PointsProjectDistance(Base2):
|
class PointsProjectDistance(Base2):
|
||||||
_id = 6
|
_id = 6
|
||||||
|
@ -1093,28 +1114,36 @@ class SketchPlane(BaseSketch):
|
||||||
'undefine the previous work plane'
|
'undefine the previous work plane'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def getEntityDef(cls,group,checkCount,obj=None):
|
def getEntityDef(cls,elements,checkCount,obj=None):
|
||||||
_ = checkCount
|
_ = checkCount
|
||||||
_ = obj
|
_ = obj
|
||||||
if not group:
|
if not elements:
|
||||||
# If no element, then this constraint serves the prupose of clearing
|
# If no element, then this constraint serves the prupose of clearing
|
||||||
# the current sketch plane
|
# the current sketch plane
|
||||||
return []
|
return []
|
||||||
|
|
||||||
# if there is any child element in this constraint, we expect the first
|
# if there is any child element in this constraint, we expect the first
|
||||||
# one to be a planar face or edge to define the work plane. The rest of
|
# one to be a planar face or edge to define the work plane. The rest of
|
||||||
# entities must be from some draft wire or circle/arc.
|
# entities must be from some draft wire or circle/arc
|
||||||
return [_wa] + [_d]*(len(group)-1)
|
#
|
||||||
|
# Base.prepare() will call system.addSketchPlane() with all contained
|
||||||
|
# element below. However, the default implementation,
|
||||||
|
# SystemExtension.addSketchPlane(), only really uses the first one,
|
||||||
|
# i.e. the one obtained by _wa(), i.e. a tuple of entities
|
||||||
|
# (workplane,base,normal).
|
||||||
|
|
||||||
|
return [_wa] + [_d]*(len(elements)-1)
|
||||||
|
|
||||||
|
|
||||||
class BaseDraftWire(BaseSketch):
|
class BaseDraftWire(BaseSketch):
|
||||||
_id = -1
|
_id = -1
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def check(cls,group,checkCount=False):
|
def check(cls,elements,checkCount=False):
|
||||||
super(BaseDraftWire,cls).check(group,checkCount)
|
super(BaseDraftWire,cls).check(elements,checkCount)
|
||||||
if not checkCount:
|
if not checkCount:
|
||||||
return
|
return
|
||||||
for info in group:
|
for info in elements:
|
||||||
if utils.isDraftWire(info.Part):
|
if utils.isDraftWire(info.Part):
|
||||||
return
|
return
|
||||||
raise RuntimeError('Constraint "{}" requires at least one linear edge '
|
raise RuntimeError('Constraint "{}" requires at least one linear edge '
|
||||||
|
@ -1129,6 +1158,11 @@ class LineLength(BaseSketch):
|
||||||
_iconName = 'Assembly_ConstraintLineLength.svg'
|
_iconName = 'Assembly_ConstraintLineLength.svg'
|
||||||
_tooltip='Add a "{}" constrain the length of a none-subdivided Draft.Wire'
|
_tooltip='Add a "{}" constrain the length of a none-subdivided Draft.Wire'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def init(cls,obj):
|
||||||
|
info = obj.Proxy.getElementsInfo()[0]
|
||||||
|
obj.Length = info.Shape.Edge1.Length
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def prepare(cls,obj,solver):
|
def prepare(cls,obj,solver):
|
||||||
func = PointsDistance.constraintFunc(obj,solver)
|
func = PointsDistance.constraintFunc(obj,solver)
|
||||||
|
@ -1184,11 +1218,11 @@ class EqualLineArcLength(BaseSketch):
|
||||||
_tooltip='Add a "{}" constraint to make a line of the same length as an arc'
|
_tooltip='Add a "{}" constraint to make a line of the same length as an arc'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def check(cls,group,checkCount=False):
|
def check(cls,elements,checkCount=False):
|
||||||
super(EqualLineArcLength,cls).check(group,checkCount)
|
super(EqualLineArcLength,cls).check(elements,checkCount)
|
||||||
if not checkCount:
|
if not checkCount:
|
||||||
return
|
return
|
||||||
for i,info in enumerate(group):
|
for i,info in enumerate(elements):
|
||||||
if i:
|
if i:
|
||||||
if utils.isDraftCircle(info.Part):
|
if utils.isDraftCircle(info.Part):
|
||||||
return
|
return
|
||||||
|
@ -1222,11 +1256,11 @@ class EqualRadius(BaseSketch):
|
||||||
_tooltip='Add a "{}" constraint to make two circles/arcs of the same radius'
|
_tooltip='Add a "{}" constraint to make two circles/arcs of the same radius'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def check(cls,group,checkCount=False):
|
def check(cls,elements,checkCount=False):
|
||||||
super(EqualRadius,cls).check(group,checkCount)
|
super(EqualRadius,cls).check(elements,checkCount)
|
||||||
if not checkCount:
|
if not checkCount:
|
||||||
return
|
return
|
||||||
for info in group:
|
for info in elements:
|
||||||
if utils.isDraftCircle(info.Part):
|
if utils.isDraftCircle(info.Part):
|
||||||
return
|
return
|
||||||
raise RuntimeError('Constraint "{}" requires at least one '
|
raise RuntimeError('Constraint "{}" requires at least one '
|
||||||
|
|
18
utils.py
18
utils.py
|
@ -5,6 +5,7 @@ Most of the functions are borrowed directly from assembly2lib.py or lib3D.py in
|
||||||
assembly2
|
assembly2
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
import math
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
import FreeCAD, FreeCADGui, Part, Draft
|
import FreeCAD, FreeCADGui, Part, Draft
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
@ -367,6 +368,23 @@ def getNormal(obj):
|
||||||
q = rot.Q
|
q = rot.Q
|
||||||
return q[3],q[0],q[1],q[2]
|
return q[3],q[0],q[1],q[2]
|
||||||
|
|
||||||
|
def getElementDirection(obj,pla=None):
|
||||||
|
if isLinearEdge(obj):
|
||||||
|
shape = getElementShape(obj,Part.Edge)
|
||||||
|
vs = shape.Edge1.Vertexes
|
||||||
|
v = vs[0].Point - vs[1].Point
|
||||||
|
else:
|
||||||
|
rot = getElementRotation(obj)
|
||||||
|
v = rot.multVec(FreeCAD.Vector(0,0,1))
|
||||||
|
if pla:
|
||||||
|
v = pla.multVec(v)
|
||||||
|
return v
|
||||||
|
|
||||||
|
def getElementsAngle(o1,o2,pla1=None,pla2=None):
|
||||||
|
v1 = getElementDirection(o1,pla1)
|
||||||
|
v2 = getElementDirection(o2,pla2)
|
||||||
|
return math.degrees(v1.getAngle(v2))
|
||||||
|
|
||||||
def getElementCircular(obj):
|
def getElementCircular(obj):
|
||||||
'return radius if it is closed, or a list of two endpoints'
|
'return radius if it is closed, or a list of two endpoints'
|
||||||
edge = getElementShape(obj,Part.Edge)
|
edge = getElementShape(obj,Part.Edge)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user