Add SketchPlane constraint for easy 2D sketch

The SketchPlane expects the first element to be a planar face/edge to
define a work plane for any following draft wire/circle/arc elements.
The 2D constrainted elements can either be inside the defining
SketchPlane constraint, or other constrains following the SketchPlane
in the constraint group.

Add a new SketchPlane to define a new work plane for any following
draft elements. Add an empty SketchPlane to undefine the work plane,
effectively making any following draft elements free in 3D space.
This commit is contained in:
Zheng, Lei 2018-01-22 19:14:58 +08:00
parent c56a9d85d7
commit cc412bdc22
8 changed files with 312 additions and 96 deletions

View File

@ -0,0 +1,136 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="svg2816"
height="64px"
width="64px"
inkscape:version="0.91 r13725"
sodipodi:docname="Assembly_ConstraintSketchPlane.svg">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1375"
inkscape:window-height="876"
id="namedview8"
showgrid="true"
inkscape:snap-global="true"
inkscape:zoom="7.375"
inkscape:cx="27.11081"
inkscape:cy="40.388169"
inkscape:window-x="65"
inkscape:window-y="24"
inkscape:window-maximized="1"
inkscape:current-layer="svg2816"
inkscape:snap-bbox="true">
<inkscape:grid
type="xygrid"
id="grid2985"
empspacing="2"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<defs
id="defs2818">
<linearGradient
inkscape:collect="always"
id="linearGradient3783">
<stop
style="stop-color:#a40000;stop-opacity:1"
offset="0"
id="stop3785" />
<stop
style="stop-color:#ef2929;stop-opacity:1"
offset="1"
id="stop3787" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3783"
id="linearGradient3789"
x1="35.55761"
y1="28.313709"
x2="29.113543"
y2="5.5904126"
gradientUnits="userSpaceOnUse" />
</defs>
<metadata
id="metadata2821">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:title>Path-Heights</dc:title>
<dc:date>2016-05-15</dc:date>
<dc:relation>http://www.freecadweb.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Path/Gui/Resources/icons/Path-Heights.svg</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
<dc:contributor>
<cc:Agent>
<dc:title>[agryson] Alexander Gryson</dc:title>
</cc:Agent>
</dc:contributor>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="g4242"
transform="translate(-0.38351502,2.1894493)"
style="opacity:1">
<g
transform="matrix(0.97634224,0.21623093,-0.21623093,0.97634224,4.6115742,5.372201)"
style="opacity:0.8"
id="g3809">
<path
style="opacity:1;fill:url(#linearGradient3789);fill-opacity:1;fill-rule:evenodd;stroke:#280000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 27.000001,3 61,9 61,9 39,31 3,25 Z"
id="path4225-1-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
<path
style="fill:none;stroke:#ef2929;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 27.610169,5.1440678 56.868167,10.287637 38.322034,28.855932 7.3728814,23.711864 Z"
id="path4225-1-7-1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
</g>
<g
transform="matrix(0.97650191,0.21550873,-0.21550873,0.97650191,4.6183399,5.2009574)"
style="opacity:0.8;fill:none"
id="g3809-1">
<path
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#280000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 27.000001,3 61,9 61,9 39,31 3,25 Z"
id="path4225-1-7-15"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -487,6 +487,8 @@ class ViewProviderAsmElement(ViewProviderAsmOnTop):
AsmElement.make(AsmElement.Selection(Element=vobj.Object,
Group=owner, Subname=subname),undo=True)
ElementInfo = namedtuple('AsmElementInfo', ('Parent','SubnameRef','Part',
'PartName','Placement','Object','Subname','Shape'))
def getElementInfo(parent, subname):
'''Return a named tuple containing the part object element information
@ -630,7 +632,7 @@ def getElementInfo(parent, subname):
obj = part.getLinkedObject(False)
partName = part.Name
return utils.ElementInfo(Parent = parent,
return ElementInfo(Parent = parent,
SubnameRef = subnameRef,
Part = part,
PartName = partName,
@ -703,7 +705,7 @@ class AsmElementLink(AsmBase):
return '{}.{}.{}'.format(prefix, assembly.getPartGroup().Name,
element.getElementSubname())
def setLink(self,owner,subname):
def setLink(self,owner,subname,checkOnly=False):
# check if there is any sub assembly in the reference
ret = Assembly.find(owner,subname)
if not ret:
@ -739,7 +741,8 @@ class AsmElementLink(AsmBase):
linked[0]==owner and linked[1]==subname:
raise RuntimeError('duplicate element link {} in constraint '
'{}'.format(objName(sibling),objName(self.parent.Object)))
self.Object.setLink(owner,subname)
if not checkOnly:
self.Object.setLink(owner,subname)
def getInfo(self,refresh=False):
if not refresh:
@ -802,7 +805,7 @@ class ViewProviderAsmElementLink(ViewProviderAsmOnTop):
def canDropObjectEx(self,_obj,owner,subname):
if logger.catchTrace('Cannot drop to AsmLink {}'.format(
objName(self.ViewObject.Object)),
self.ViewObject.Object.Proxy.prepareLink,
self.ViewObject.Object.Proxy.setLink,
owner, subname, True):
return True
return False
@ -874,16 +877,16 @@ class AsmConstraint(AsmGroup):
ret = getattr(self,'elements',None)
if ret or Constraint.isDisabled(obj):
return ret
infos = []
elementInfo = []
elements = []
for o in obj.Group:
checkType(o,AsmElementLink)
info = o.Proxy.getInfo()
if not info:
return
infos.append(info)
elementInfo.append(info)
elements.append(o)
Constraint.check(obj,infos,True)
Constraint.check(obj,elementInfo,True)
self.elements = elements
return self.elements
@ -919,6 +922,7 @@ class AsmConstraint(AsmGroup):
sel = sels[0]
cstr = None
elements = []
elementInfo = []
assembly = None
selSubname = None
for sub in subs:
@ -976,13 +980,18 @@ class AsmConstraint(AsmGroup):
elements.append((found.Object,sub))
elementInfo.append(getElementInfo(
assembly,found.Object.Name+'.'+sub))
if not Constraint.isDisabled(cstr):
if cstr:
typeid = Constraint.getTypeID(cstr)
check = [o.Proxy.getInfo() for o in cstr.Group] + elements
else:
check = elements
Constraint.check(typeid,check)
check = []
for o in cstr.Group:
check.append(o.Proxy.getInfo())
elementInfo = check + elementInfo
Constraint.check(typeid,elementInfo)
return AsmConstraint.Selection(SelObject=sel.Object,
SelSubname=selSubname,

View File

@ -7,6 +7,21 @@ from .proxy import ProxyType, PropertyInfo, propGet, propGetValue
import os
_iconPath = os.path.join(utils.iconPath,'constraints')
def _d(solver,partInfo,subname,shape,retAll=False):
'return a handle of any supported element of a draft object'
if not solver:
if utils.isDraftObject(partInfo):
return
raise RuntimeError('Expects only elements from a draft wire or '
'draft circle/arc')
if subname.startswith('Vertex'):
return _p(solver,partInfo,subname,shape,retAll)
elif subname.startswith('Edge'):
return _l(solver,partInfo,subname,shape,retAll)
else:
raise RuntimeError('Invalid element {} of object {}'.format(subname,
partInfo.PartName))
def _p(solver,partInfo,subname,shape,retAll=False):
'return a handle of a transformed point derived from "shape"'
if not solver:
@ -18,6 +33,7 @@ def _p(solver,partInfo,subname,shape,retAll=False):
subname,objName(partInfo)))
return
part = partInfo.Part
key = subname+'.p'
h = partInfo.EntityMap.get(key,None)
system = solver.system
@ -25,7 +41,7 @@ def _p(solver,partInfo,subname,shape,retAll=False):
system.log('cache {}: {}'.format(key,h))
return h if retAll else h[0]
if utils.isDraftWire(partInfo.Part):
if utils.isDraftWire(part):
v = utils.getElementPos(shape)
nameTag = partInfo.PartName + '.' + key
v = partInfo.Placement.multVec(v)
@ -38,8 +54,15 @@ def _p(solver,partInfo,subname,shape,retAll=False):
h = [e, params]
system.log('{}: add draft point {},{}'.format(key,h,v))
elif utils.isDraftCircle(partInfo.Part):
shape = utils.getElementShape((partInfo.Part,'Edge1'),Part.Edge)
if system.sketchPlane and not solver.isFixedElement(part,subname):
system.NameTag = nameTag + '.i'
e2 = system.addPointInPlane(e,system.sketchPlane[0],
group=partInfo.Group)
system.log('{}: add draft point in plane {},{}'.format(
partInfo.PartName,e2,system.sketchPlane[0]))
elif utils.isDraftCircle(part):
shape = utils.getElementShape((part,'Edge1'),Part.Edge)
if subname == 'Vertex1':
e = _c(solver,partInfo,'Edge1',shape,retAll=True)
h = [e[2]]
@ -48,7 +71,7 @@ def _p(solver,partInfo,subname,shape,retAll=False):
h = [e[1]]
else:
raise RuntimeError('Invalid draft circle vertex {} of '
'{}'.format(subname,objName(partInfo.Part)))
'{}'.format(subname,partInfo.PartName))
system.log('{}: add circle point {},{}'.format(key,h,e))
@ -58,7 +81,7 @@ def _p(solver,partInfo,subname,shape,retAll=False):
system.NameTag = nameTag
e = system.addPoint3dV(*v)
system.NameTag = nameTag + 't'
h = system.addTransform(e[0],*partInfo.Params,group=partInfo.Group)
h = system.addTransform(e,*partInfo.Params,group=partInfo.Group)
h = [h,e]
system.log('{}: {},{}'.format(key,h,partInfo.Group))
@ -70,7 +93,7 @@ def _n(solver,partInfo,subname,shape,retAll=False):
if not solver:
if not utils.isPlanar(shape):
return 'an edge or face with a surface normal'
if utils.isDraftWire(partInfo.Part):
if utils.isDraftWire(partInfo):
logger.warn('Use draft wire {} for normal. Draft wire placement'
' is not transformable'.format(partInfo.PartName))
return
@ -106,17 +129,19 @@ 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.Part):
if not utils.isDraftWire(partInfo):
return
part = partInfo
vname1,vname2 = utils.edge2VertexIndex(subname)
vname1,vname2 = utils.edge2VertexIndex(partInfo,subname)
if not vname1:
raise RuntimeError('Invalid draft subname {} or {}'.format(
subname,objName(part)))
subname,objName(partInfo)))
v = shape.Edges[0].Vertexes
return _p(solver,partInfo,vname1,v[0]) or \
_p(solver,partInfo,vname2,v[1])
ret = _p(solver,partInfo,vname1,v[0])
if ret:
return ret
return _p(solver,partInfo,vname2,v[1])
part = partInfo.Part
key = subname+'.l'
h = partInfo.EntityMap.get(key,None)
system = solver.system
@ -125,11 +150,11 @@ def _l(solver,partInfo,subname,shape,retAll=False):
else:
nameTag = partInfo.PartName + '.' + key
v = shape.Edges[0].Vertexes
if utils.isDraftWire(partInfo.Part):
vname1,vname2 = utils.edge2VertexIndex(subname)
if utils.isDraftWire(part):
vname1,vname2 = utils.edge2VertexIndex(part,subname)
if not vname1:
raise RuntimeError('Invalid draft subname {} or {}'.format(
subname,objName(partInfo.Part)))
subname,partInfo.PartName))
tp1 = _p(solver,partInfo,vname1,v[0])
tp2 = _p(solver,partInfo,vname2,v[1])
else:
@ -155,7 +180,7 @@ def _dl(solver,partInfo,subname,shape,retAll=False):
if not solver:
if utils.isDraftWire(partInfo):
return
raise RuntimeError('Requires a non-closed-or-subdivided draft wire')
raise RuntimeError('Requires a non-subdivided draft wire')
return _l(solver,partInfo,subname,shape,retAll)
def _ln(solver,partInfo,subname,shape,retAll=False):
@ -219,6 +244,15 @@ def _c(solver,partInfo,subname,shape,requireArc=False,retAll=False):
if utils.isDraftCircle(partInfo.Part):
part = partInfo.Part
w,p,n = partInfo.Workplane
if system.sketchPlane and not solver.isFixedElement(part,subname):
system.NameTag = nameTag + '.o'
e1 = system.addSameOrientation(n,system.sketchPlane[2],group=g)
system.NameTag = nameTag + '.i'
e2 = system.addPointInPlane(p,system.sketchPlane[0],group=g)
system.log('{}: fix draft circle in plane {},{}'.format(
partInfo.PartName,e1,e2))
if part.FirstAngle == part.LastAngle:
if requireArc:
raise RuntimeError('expecting an arc from {}'.format(
@ -347,7 +381,11 @@ class Constraint(ProxyType):
def register(mcs,cls):
super(Constraint,mcs).register(cls)
if cls._id>=0 and cls._iconName is not Base._iconName:
gui.AsmCmdManager.register(ConstraintCommand(cls))
try:
gui.AsmCmdManager.register(ConstraintCommand(cls))
except Exception:
logger.error('failed to register {}'.format(cls.getName()))
raise
@classmethod
def attach(mcs,obj,checkType=True):
@ -508,11 +546,8 @@ class Base(object):
def check(cls,group,checkCount=False):
entities = cls.getEntityDef(group,checkCount)
for i,e in enumerate(entities):
o = group[i]
if isinstance(o,utils.ElementInfo):
msg = e(None,o.Part,o.Subname,o.Shape)
else:
msg = e(None,None,None,o)
info = group[i]
msg = e(None,info.Part,info.Subname,info.Shape)
if not msg:
continue
if i == len(cls._entityDef):
@ -546,6 +581,7 @@ class Base(object):
params = cls.getPropertyValues(obj) + cls.getEntities(obj,solver)
ret = func(*params,group=solver.group)
solver.system.log('{}: {}'.format(cstrName(obj),ret))
return ret
else:
logger.warn('{} no constraint func'.format(cstrName(obj)))
@ -583,7 +619,7 @@ class Locked(Base):
info = e.Proxy.getInfo()
if not utils.isVertex(info.Shape) and \
not utils.isLinearEdge(info.Shape) and \
not utils.isDraftCircle(info):
not utils.isDraftCircle(info.Part):
ret.append(info)
return ret
@ -594,7 +630,7 @@ class Locked(Base):
ret = []
for e in obj.Proxy.getElements():
info = e.Proxy.getInfo()
if utils.isDraftObject(info):
if utils.isDraftObject(info.Part):
continue
shape = None
if utils.isVertex(info.Shape) or \
@ -613,26 +649,31 @@ class Locked(Base):
system = solver.system
isVertex = utils.isVertex(info.Shape)
if not isVertex and utils.isDraftCircle(info):
solver.getPartInfo(info,True,solver.group)
if solver.isFixedElement(info.Part,info.Subname):
return ret
if not isVertex and utils.isDraftCircle(info.Part):
if solver.sketchPlane:
_c(solver,solver.getPartInfo(info),info.Subname,info.Shape)
else:
solver.getPartInfo(info,True,solver.group)
solver.addFixedElement(info.Part,info.Subname)
return ret
if not isVertex and not utils.isLinearEdge(info.Shape):
return ret
if solver.isFixedPart(info):
logger.warn('redundant locking element "{}" in constraint '
'{}'.format(info.Subname,info.PartName))
return ret
partInfo = solver.getPartInfo(info)
fixPoint = False
if isVertex:
names = [info.Subname]
elif utils.isDraftObject(info):
if utils.isDraftCircle(info.Part):
_c(solver,partInfo,'Edge1',info.Shape)
solver.addFixedElement(info.Part,'Edge1')
elif utils.isDraftWire(info.Part):
fixPoint = True
names = utils.edge2VertexIndex(info.Subname)
names = utils.edge2VertexIndex(info.Part,info.Subname)
else:
names = [info.Subname+'.fp1', info.Subname+'.fp2']
@ -647,13 +688,18 @@ class Locked(Base):
# Get the entity for the point expressed in variable parameters
e2 = _p(solver,partInfo,names[i],v)
solver.addFixedElement(info.Part,names[i])
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 = nameTag + surfix
e = system.addPointsCoincident(e1,e2,group=solver.group)
if system.sketchPlane and utils.isDraftObject(info.Part):
w = system.sketchPlane[0]
else:
w = 0
e = system.addPointsCoincident(e1,e2,w,group=solver.group)
system.log('{}: fix point {},{},{}'.format(
info.PartName,e,e1,e2))
else:
@ -683,7 +729,7 @@ class Locked(Base):
@classmethod
def check(cls,group,_checkCount=False):
if not all([utils.isElement(o) for o in group]):
if not all([utils.isElement(info.Shape) for info in group]):
raise RuntimeError('Constraint "{}" requires all children to be '
'of element (Vertex, Edge or Face)'.format(cls.getName()))
@ -697,11 +743,8 @@ class BaseMulti(Base):
if len(group)<2:
raise RuntimeError('Constraint "{}" requires at least two '
'elements'.format(cls.getName()))
for o in group:
if isinstance(o,utils.ElementInfo):
msg = cls._entityDef[0](None,o.Part,o.Subname,o.Shape)
else:
msg = cls._entityDef[0](None,None,None,o)
for info in group:
msg = cls._entityDef[0](None,info.Part,info.Subname,info.Shape)
if msg:
raise RuntimeError('Constraint "{}" requires all the element '
'to be of {}'.format(cls.getName()))
@ -725,7 +768,7 @@ class BaseMulti(Base):
cstrName(obj),info.PartName))
continue
parts.add(info.Part)
if solver.isFixedPart(info):
if solver.isFixedPart(info.Part):
if ref:
logger.warn('{} skip more than one fixed part {}'.format(
cstrName(obj),info.PartName))
@ -777,7 +820,7 @@ class BaseCascade(BaseMulti):
partInfo = solver.getPartInfo(info)
e2 = cls._entityDef[0](solver,partInfo,info.Subname,info.Shape)
prev = info
if solver.isFixedPart(info):
if solver.isFixedPart(info.Part):
params = props + [e1,e2]
else:
params = props + [e2,e1]
@ -1021,6 +1064,23 @@ class BaseSketch(Base):
_toolbarName = 'Assembly3 Sketch Constraints'
class SketchPlane(BaseSketch):
_id = 38
_iconName = 'Assembly_ConstraintSketchPlane.svg'
_tooltip='Add a "{0}" to define the work plane of any draft element\n'\
'inside or following this constraint. Add an empty "{0}" to\n'\
'undefine the previous work plane'
@classmethod
def getEntityDef(cls,group,checkCount,obj=None):
# 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
# entities must be from some draft wire or circle/arc.
if not group:
return
return [_wa] + [_d]*(len(group)-1)
class BaseDraftWire(BaseSketch):
_id = -1
@ -1029,11 +1089,11 @@ class BaseDraftWire(BaseSketch):
super(BaseDraftWire,cls).check(group,checkCount)
if not checkCount:
return
for o in group:
if utils.isDraftWire(o):
for info in group:
if utils.isDraftWire(info.Part):
return
raise RuntimeError('Constraint "{}" requires at least one linear edge '
'from a non-closed-or-subdivided Draft.Wire'.format(
'from a none-subdivided Draft.Wire'.format(
cls.getName()))
class LineLength(BaseSketch):
@ -1042,8 +1102,7 @@ class LineLength(BaseSketch):
_workplane = True
_props = ["Length"]
_iconName = 'Assembly_ConstraintLineLength.svg'
_tooltip='Add a "{}" constrain the length of a non-closed-or-subdivided '\
'Draft.Wire'
_tooltip='Add a "{}" constrain the length of a none-subdivided Draft.Wire'
@classmethod
def prepare(cls,obj,solver):
@ -1053,6 +1112,7 @@ class LineLength(BaseSketch):
params = cls.getPropertyValues(obj) + [p1,p2]
ret = func(*params,group=solver.group)
solver.system.log('{}: {}'.format(cstrName(obj),ret))
return ret
else:
logger.warn('{} no constraint func'.format(cstrName(obj)))
@ -1103,14 +1163,14 @@ class EqualLineArcLength(BaseSketch):
super(EqualLineArcLength,cls).check(group,checkCount)
if not checkCount:
return
for i,o in enumerate(group):
for i,info in enumerate(group):
if i:
if utils.isDraftCircle(o):
if utils.isDraftCircle(info.Part):
return
elif utils.isDraftWire(o):
elif utils.isDraftWire(info.Part):
return
raise RuntimeError('Constraint "{}" requires at least one '
'non-closed-or-subdivided Draft.Wire or one Draft.Circle'.format(
'non-subdivided Draft.Wire or one Draft.Circle'.format(
cls.getName()))
@ -1141,8 +1201,8 @@ class EqualRadius(BaseSketch):
super(EqualRadius,cls).check(group,checkCount)
if not checkCount:
return
for o in group:
if utils.isDraftCircle(o):
for info in group:
if utils.isDraftCircle(info.Part):
return
raise RuntimeError('Constraint "{}" requires at least one '
'Draft.Circle'.format(cls.getName()))

View File

@ -71,7 +71,7 @@ class AsmMovingPart(object):
def update(self):
info = getElementInfo(self.info.Parent,self.info.SubnameRef)
self.info = info
if utils.isDraftObject(info):
if utils.isDraftObject(info.Part):
pos = utils.getElementPos(info.Shape)
rot = utils.getElementRotation(info.Shape)
pla = info.Placement.multiply(FreeCAD.Placement(pos,rot))
@ -105,7 +105,7 @@ class AsmMovingPart(object):
return
change = [idx]
else:
change = utils.draftWireEdge2PointIndex(part,info.Subname)
change = utils.edge2VertexIndex(part,info.Subname,True)
if change[0] is None or change[1] is None:
logger.error('Invalid draft wire edge {} {}'.format(
info.Subname, info.PartName))

View File

@ -207,8 +207,9 @@ class ProxyType(type):
return
info = mcs.getInfo()
if cls._id in info.TypeMap:
raise RuntimeError('Duplicate {} type id {}'.format(
mcs.getMetaName(),cls._id))
raise RuntimeError('Duplicate {} type id {}, {} conflict with '
'{}'.format(mcs.getMetaName(),cls._id,cls.getName(),
info.TypeMap[cls._id].getName()))
info.TypeMap[cls._id] = cls
info.TypeNameMap[cls.getName()] = cls
info.TypeNames.append(cls.getName())

View File

@ -32,6 +32,7 @@ class Solver(object):
self.group = 1 # the solving group
self._partMap = {}
self._cstrMap = {}
self._fixedElements = set()
self.system.GroupHandle = self._fixedGroup
@ -176,8 +177,14 @@ class Solver(object):
if recompute and touched:
assembly.recompute(True)
def isFixedPart(self,info):
return info.Part in self._fixedParts
def isFixedPart(self,part):
return part in self._fixedParts
def isFixedElement(self,part,subname):
return self.isFixedPart(part) or (part,subname) in self._fixedElements
def addFixedElement(self,part,subname):
self._fixedElements.add((part,subname))
def getPartInfo(self,info,fixed=False,group=0):
partInfo = self._partMap.get(info.Part,None)

View File

@ -110,6 +110,12 @@ class SystemBase(object):
class SystemExtension(object):
def __init__(self):
self.NameTag = ''
self.sketchPlane = None
def addSketchPlane(self,*args,**kargs):
_ = kargs
self.sketchPlane = args[0] if args else None
return self.sketchPlane
def addPlaneCoincident(self,d,lockAngle,angle,e1,e2,group=0):
if not group:

View File

@ -126,30 +126,24 @@ def getElementShape(obj,tp):
if len(f)==1:
return f[0]
ElementInfo = namedtuple('AsmElementInfo', ('Parent','SubnameRef','Part',
'PartName','Placement','Object','Subname','Shape'))
def isDraftWire(obj):
if isinstance(obj,ElementInfo):
obj = obj.Part
proxy = getattr(obj,'Proxy',None)
return isinstance(proxy,Draft._Wire) and \
not obj.Closed and \
not obj.Subdivisions
if isinstance(proxy,Draft._Wire) and not obj.Subdivisions:
return obj
def isDraftCircle(obj):
if isinstance(obj,ElementInfo):
obj = obj.Part
proxy = getattr(obj,'Proxy',None)
return isinstance(proxy,Draft._Circle)
if isinstance(proxy,Draft._Circle):
return obj
def isDraftObject(obj):
return isDraftWire(obj) or isDraftCircle(obj)
o = isDraftWire(obj)
if o:
return o
return isDraftCircle(obj)
def isElement(obj):
if isinstance(obj,ElementInfo):
shape = obj.Shape
elif not isinstance(obj,(tuple,list)):
if not isinstance(obj,(tuple,list)):
shape = obj
else:
sobj,_,shape = obj[0].getSubObject(obj[1],2)
@ -490,7 +484,7 @@ def isSamePlacement(pla1,pla2):
isSameValue(pla1.Rotation.Q,pla2.Rotation.Q)
def getElementIndex(name,check=None):
'Return element index, 0 if invalid'
'Return element index (starting with 1), 0 if invalid'
for i,c in enumerate(reversed(name)):
if not c.isdigit():
if not i:
@ -503,24 +497,27 @@ def getElementIndex(name,check=None):
def draftWireVertex2PointIndex(obj,name):
'Convert vertex index to draft wire point index, None if invalid'
obj = isDraftWire(obj)
if not obj:
return
idx = getElementIndex(name,'Vertex')
# We don't support subdivision yet (checked in isDraftWire())
if idx <= 0 or not isDraftWire(obj):
if idx <= 0:
return
idx -= 1
if idx < len(obj.Points):
return idx
def draftWireEdge2PointIndex(obj,name):
vname1,vname2 = edge2VertexIndex(name)
if not vname1:
return None,None
return (draftWireVertex2PointIndex(obj,vname1),
draftWireVertex2PointIndex(obj,vname2))
def edge2VertexIndex(name):
def edge2VertexIndex(obj,name,retInteger=False):
'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)
dwire = isDraftWire(obj)
if dwire and dwire.Closed and idx==len(dwire.Points):
idx2 = 1
else:
idx2 = idx+1
if retInteger:
return idx-1,idx2-1
return 'Vertex{}'.format(idx),'Vertex{}'.format(idx2)