assembly: support freezing assembly
New property 'Freeze' added to assembly container to freeze any update of the assembly. AsmPartGroup and AsmElement has been change to bind to Part::FeaturePython instead of App::FeaturePython, in order to be able to hold its own shape when frozen. The new ChildViewProvider feature in ViewProviderLink is used to display the native shape.
This commit is contained in:
parent
9513b774a8
commit
e63118d6b5
374
assembly.py
374
assembly.py
|
@ -83,10 +83,14 @@ class ViewProviderAsmBase(object):
|
||||||
vobj.Proxy = self
|
vobj.Proxy = self
|
||||||
self.attach(vobj)
|
self.attach(vobj)
|
||||||
|
|
||||||
|
_addToSceneGraph = False
|
||||||
|
|
||||||
def attach(self,vobj):
|
def attach(self,vobj):
|
||||||
self.ViewObject = vobj
|
self.ViewObject = vobj
|
||||||
vobj.signalChangeIcon()
|
vobj.signalChangeIcon()
|
||||||
vobj.setPropertyStatus('Visibility','Hidden')
|
vobj.setPropertyStatus('Visibility','Hidden')
|
||||||
|
if not self._addToSceneGraph:
|
||||||
|
vobj.Document.ActiveView.getSceneGraph().removeChild(vobj.RootNode)
|
||||||
|
|
||||||
def __getstate__(self):
|
def __getstate__(self):
|
||||||
return None
|
return None
|
||||||
|
@ -138,6 +142,9 @@ class AsmGroup(AsmBase):
|
||||||
obj.addProperty("App::PropertyEnumeration","GroupMode","Base",'')
|
obj.addProperty("App::PropertyEnumeration","GroupMode","Base",'')
|
||||||
super(AsmGroup,self).attach(obj)
|
super(AsmGroup,self).attach(obj)
|
||||||
|
|
||||||
|
def allowDuplicateLabel(self,_obj):
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class ViewProviderAsmGroup(ViewProviderAsmBase):
|
class ViewProviderAsmGroup(ViewProviderAsmBase):
|
||||||
def claimChildren(self):
|
def claimChildren(self):
|
||||||
|
@ -166,10 +173,15 @@ class AsmPartGroup(AsmGroup):
|
||||||
def groupSetup(self):
|
def groupSetup(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def canLoadPartial(self,_obj):
|
||||||
|
return 1 if self.getAssembly().frozen else 0
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def make(parent,name='Parts'):
|
def make(parent,name='Parts'):
|
||||||
obj = parent.Document.addObject("App::FeaturePython",name,
|
obj = parent.Document.addObject("Part::FeaturePython",name,
|
||||||
AsmPartGroup(parent),None,True)
|
AsmPartGroup(parent),None,True)
|
||||||
|
obj.setPropertyStatus('Placement',('Output','Hidden'))
|
||||||
|
obj.setPropertyStatus('Shape','Output')
|
||||||
ViewProviderAsmPartGroup(obj.ViewObject)
|
ViewProviderAsmPartGroup(obj.ViewObject)
|
||||||
obj.purgeTouched()
|
obj.purgeTouched()
|
||||||
return obj
|
return obj
|
||||||
|
@ -193,11 +205,35 @@ class ViewProviderAsmPartGroup(ViewProviderAsmGroup):
|
||||||
def canDragAndDropObject(self,_obj):
|
def canDragAndDropObject(self,_obj):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def updateData(self,obj,prop):
|
||||||
|
if not hasattr(self,'ViewObject') or FreeCAD.isRestoring():
|
||||||
|
return
|
||||||
|
if prop == 'Shape':
|
||||||
|
cvp = obj.ViewObject.ChildViewProvider
|
||||||
|
if cvp:
|
||||||
|
cvp.mapShapeColors()
|
||||||
|
|
||||||
|
def showParts(self):
|
||||||
|
vobj = self.ViewObject
|
||||||
|
obj = vobj.Object
|
||||||
|
assembly = obj.Proxy.getAssembly().Object
|
||||||
|
if not assembly.ViewObject.ShowParts and \
|
||||||
|
(assembly.Freeze or (assembly.BuildShape!=BuildShapeNone and \
|
||||||
|
assembly.BuildShape!=BuildShapeCompound)):
|
||||||
|
mode = 1
|
||||||
|
else:
|
||||||
|
mode = 0
|
||||||
|
if not vobj.ChildViewProvider:
|
||||||
|
if not mode:
|
||||||
|
return
|
||||||
|
vobj.ChildViewProvider = 'PartGui::ViewProviderPartExt'
|
||||||
|
vobj.DefaultMode = mode
|
||||||
|
|
||||||
|
|
||||||
class AsmElement(AsmBase):
|
class AsmElement(AsmBase):
|
||||||
def __init__(self,parent):
|
def __init__(self,parent):
|
||||||
self._initializing = True
|
self._initializing = True
|
||||||
self.info = None
|
self.shape = None
|
||||||
self.parent = getProxy(parent,AsmElementGroup)
|
self.parent = getProxy(parent,AsmElementGroup)
|
||||||
super(AsmElement,self).__init__()
|
super(AsmElement,self).__init__()
|
||||||
|
|
||||||
|
@ -214,6 +250,7 @@ class AsmElement(AsmBase):
|
||||||
obj.setPropertyStatus('LinkTransform',['Immutable','Hidden'])
|
obj.setPropertyStatus('LinkTransform',['Immutable','Hidden'])
|
||||||
obj.configLinkProperty('LinkedObject','Placement','LinkTransform')
|
obj.configLinkProperty('LinkedObject','Placement','LinkTransform')
|
||||||
obj.setPropertyStatus('LinkedObject','ReadOnly')
|
obj.setPropertyStatus('LinkedObject','ReadOnly')
|
||||||
|
self.shape = None
|
||||||
|
|
||||||
def attach(self,obj):
|
def attach(self,obj):
|
||||||
obj.addProperty("App::PropertyXLink","LinkedObject"," Link",'')
|
obj.addProperty("App::PropertyXLink","LinkedObject"," Link",'')
|
||||||
|
@ -235,7 +272,7 @@ class AsmElement(AsmBase):
|
||||||
if not parent or FreeCAD.isRestoring():
|
if not parent or FreeCAD.isRestoring():
|
||||||
return
|
return
|
||||||
if prop=='Offset':
|
if prop=='Offset':
|
||||||
self.updatePlacement()
|
self.update()
|
||||||
elif prop == 'Label':
|
elif prop == 'Label':
|
||||||
parent.Object.cacheChildLabel()
|
parent.Object.cacheChildLabel()
|
||||||
if prop not in _IgnoredProperties and \
|
if prop not in _IgnoredProperties and \
|
||||||
|
@ -247,23 +284,29 @@ class AsmElement(AsmBase):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def updatePlacement(self):
|
def updatePlacement(self):
|
||||||
obj = getattr(self,'Object',None)
|
obj = self.Object
|
||||||
if not obj:
|
info = getElementInfo(self.getAssembly().getPartGroup(),
|
||||||
|
self.getElementSubname(),offset=self.Object.Offset)
|
||||||
|
if not info:
|
||||||
return
|
return
|
||||||
if obj.Offset.isIdentity():
|
self.shape = info.Shape
|
||||||
if obj.Placement.isIdentity():
|
if not utils.isSamePlacement(info.PlacementOffset,obj.Placement):
|
||||||
return
|
obj.setPropertyStatus('Placement','-Immutable')
|
||||||
pla = FreeCAD.Placement()
|
obj.Placement = info.PlacementOffset
|
||||||
else:
|
obj.setPropertyStatus('Placement','Immutable')
|
||||||
info = getElementInfo(self.getAssembly().getPartGroup(),
|
|
||||||
self.getElementSubname(),offset=self.Object.Offset)
|
def freeze(self):
|
||||||
if not info or \
|
obj = self.Object
|
||||||
utils.isSamePlacement(info.PlacementOffset,obj.Placement):
|
if self.getAssembly().Freeze:
|
||||||
return
|
if not self.shape:
|
||||||
pla = info.PlacementOffset
|
self.updatePlacement();
|
||||||
obj.setPropertyStatus('Placement','-Immutable')
|
if self.shape:
|
||||||
obj.Placement = pla
|
# make a compound to contain the shape's transformation
|
||||||
obj.setPropertyStatus('Placement','Immutable')
|
shape = Part.makeCompound(self.shape)
|
||||||
|
shape.ElementMap = self.shape.ElementMap
|
||||||
|
obj.Shape = shape
|
||||||
|
elif not obj.Shape.isNull():
|
||||||
|
obj.Shape = Part.Shape()
|
||||||
|
|
||||||
def getAssembly(self):
|
def getAssembly(self):
|
||||||
return self.parent.parent
|
return self.parent.parent
|
||||||
|
@ -383,8 +426,8 @@ class AsmElement(AsmBase):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls,name,elements):
|
def create(cls,name,elements):
|
||||||
element = elements.Document.addObject("App::FeaturePython",
|
element = elements.Document.addObject("Part::FeaturePython",
|
||||||
name,cls(elements),None,True)
|
name,cls(elements),None,True)
|
||||||
ViewProviderAsmElement(element.ViewObject)
|
ViewProviderAsmElement(element.ViewObject)
|
||||||
return element
|
return element
|
||||||
|
|
||||||
|
@ -560,6 +603,22 @@ class ViewProviderAsmElement(ViewProviderAsmOnTop):
|
||||||
AsmElement.make(AsmElement.Selection(Element=vobj.Object,
|
AsmElement.make(AsmElement.Selection(Element=vobj.Object,
|
||||||
Group=owner, Subname=subname+element),undo=True)
|
Group=owner, Subname=subname+element),undo=True)
|
||||||
|
|
||||||
|
def updateData(self,obj,prop):
|
||||||
|
if not hasattr(self,'ViewObject') or FreeCAD.isRestoring():
|
||||||
|
return
|
||||||
|
if prop == 'Shape':
|
||||||
|
vobj = obj.ViewObject
|
||||||
|
if obj.Shape.isNull():
|
||||||
|
vobj.ChildViewProvider = ''
|
||||||
|
elif not vobj.ChildViewProvider:
|
||||||
|
vobj.ChildViewProvider = 'PartGui::ViewProviderPartExt'
|
||||||
|
vobj.DefaultMode = 1
|
||||||
|
|
||||||
|
def onFinishRestoring(self):
|
||||||
|
vobj = self.ViewObject
|
||||||
|
if getattr(vobj,'ChildViewProvider',None):
|
||||||
|
vobj.DefaultMode = 1
|
||||||
|
|
||||||
|
|
||||||
class AsmElementSketch(AsmElement):
|
class AsmElementSketch(AsmElement):
|
||||||
def __init__(self,obj,parent):
|
def __init__(self,obj,parent):
|
||||||
|
@ -596,6 +655,9 @@ class AsmElementSketch(AsmElement):
|
||||||
ret[0] = obj
|
ret[0] = obj
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
def freeze(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ViewProviderAsmElementSketch(ViewProviderAsmElement):
|
class ViewProviderAsmElementSketch(ViewProviderAsmElement):
|
||||||
def getIcon(self):
|
def getIcon(self):
|
||||||
|
@ -612,6 +674,10 @@ class ViewProviderAsmElementSketch(ViewProviderAsmElement):
|
||||||
return subs[-1]
|
return subs[-1]
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
def updateData(self,obj,prop):
|
||||||
|
_ = obj
|
||||||
|
_ = prop
|
||||||
|
|
||||||
|
|
||||||
ElementInfo = namedtuple('AsmElementInfo', ('Parent','SubnameRef','Part',
|
ElementInfo = namedtuple('AsmElementInfo', ('Parent','SubnameRef','Part',
|
||||||
'PartName','Placement','Object','Subname','Shape','PlacementOffset'))
|
'PartName','Placement','Object','Subname','Shape','PlacementOffset'))
|
||||||
|
@ -625,6 +691,14 @@ def getElementInfo(parent,subname,checkPlacement=False,shape=None,offset=None):
|
||||||
|
|
||||||
subname: subname reference to the part element (i.e. edge, face, vertex)
|
subname: subname reference to the part element (i.e. edge, face, vertex)
|
||||||
|
|
||||||
|
shape: caller can pass in a pre-obtained element shape. The shape is
|
||||||
|
assumed ot be in the assembly coordinate space. This function will then
|
||||||
|
transform the shape into the its owner part's coordinate space. If
|
||||||
|
'shape' is not given, then the output shape will be obtained through
|
||||||
|
'parent' and 'subname'
|
||||||
|
|
||||||
|
offset: an optional offset in the element shape's coordinate space
|
||||||
|
|
||||||
Return a named tuple with the following fields:
|
Return a named tuple with the following fields:
|
||||||
|
|
||||||
Parent: set to the input parent object
|
Parent: set to the input parent object
|
||||||
|
@ -648,6 +722,10 @@ def getElementInfo(parent,subname,checkPlacement=False,shape=None,offset=None):
|
||||||
|
|
||||||
Shape: Part.Shape of the linked element. The shape's placement is relative
|
Shape: Part.Shape of the linked element. The shape's placement is relative
|
||||||
to the owner Part.
|
to the owner Part.
|
||||||
|
|
||||||
|
PlacementOffset: if 'offset' is given, then this field holds the necessary
|
||||||
|
placement offset in assembly coordinate space to achieve an equivalant
|
||||||
|
'offset', which is in element shape's coordinate space.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
subnameRef = subname
|
subnameRef = subname
|
||||||
|
@ -795,6 +873,7 @@ class AsmElementLink(AsmBase):
|
||||||
super(AsmElementLink,self).linkSetup(obj)
|
super(AsmElementLink,self).linkSetup(obj)
|
||||||
obj.configLinkProperty('LinkedObject')
|
obj.configLinkProperty('LinkedObject')
|
||||||
obj.setPropertyStatus('LinkedObject','ReadOnly')
|
obj.setPropertyStatus('LinkedObject','ReadOnly')
|
||||||
|
self.info = None
|
||||||
|
|
||||||
def attach(self,obj):
|
def attach(self,obj):
|
||||||
obj.addProperty("App::PropertyXLink","LinkedObject"," Link",'')
|
obj.addProperty("App::PropertyXLink","LinkedObject"," Link",'')
|
||||||
|
@ -898,6 +977,9 @@ class AsmElementLink(AsmBase):
|
||||||
return
|
return
|
||||||
self.info = getElementInfo(self.getAssembly().getPartGroup(),
|
self.info = getElementInfo(self.getAssembly().getPartGroup(),
|
||||||
self.getElementSubname(),shape=obj.getSubObject(''))
|
self.getElementSubname(),shape=obj.getSubObject(''))
|
||||||
|
if hasattr(obj,'Shape') and self.getAssembly().Freeze:
|
||||||
|
# make a compound to contain the shape's transformation
|
||||||
|
obj.Shape = Part.makeCompound(self.info.Shape)
|
||||||
return self.info
|
return self.info
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -1275,6 +1357,9 @@ class AsmConstraintGroup(AsmGroup):
|
||||||
def getAssembly(self):
|
def getAssembly(self):
|
||||||
return self.parent
|
return self.parent
|
||||||
|
|
||||||
|
def canLoadPartial(self,_obj):
|
||||||
|
return 2 if self.getAssembly().frozen else 0
|
||||||
|
|
||||||
def linkSetup(self,obj):
|
def linkSetup(self,obj):
|
||||||
super(AsmConstraintGroup,self).linkSetup(obj)
|
super(AsmConstraintGroup,self).linkSetup(obj)
|
||||||
for o in obj.Group:
|
for o in obj.Group:
|
||||||
|
@ -1380,8 +1465,9 @@ BuildShapeNone = 'None'
|
||||||
BuildShapeCompound = 'Compound'
|
BuildShapeCompound = 'Compound'
|
||||||
BuildShapeFuse = 'Fuse'
|
BuildShapeFuse = 'Fuse'
|
||||||
BuildShapeCut = 'Cut'
|
BuildShapeCut = 'Cut'
|
||||||
|
BuildShapeCommon = 'Common'
|
||||||
BuildShapeNames = (BuildShapeNone,BuildShapeCompound,
|
BuildShapeNames = (BuildShapeNone,BuildShapeCompound,
|
||||||
BuildShapeFuse,BuildShapeCut)
|
BuildShapeFuse,BuildShapeCut,BuildShapeCommon)
|
||||||
|
|
||||||
class Assembly(AsmGroup):
|
class Assembly(AsmGroup):
|
||||||
_Timer = QtCore.QTimer()
|
_Timer = QtCore.QTimer()
|
||||||
|
@ -1392,26 +1478,17 @@ class Assembly(AsmGroup):
|
||||||
self.parts = set()
|
self.parts = set()
|
||||||
self.partArrays = set()
|
self.partArrays = set()
|
||||||
self.constraints = None
|
self.constraints = None
|
||||||
|
self.frozen = False
|
||||||
super(Assembly,self).__init__()
|
super(Assembly,self).__init__()
|
||||||
|
|
||||||
|
def allowDuplicateLabel(self,_obj):
|
||||||
|
return False
|
||||||
|
|
||||||
def getSubObjects(self,_obj,reason):
|
def getSubObjects(self,_obj,reason):
|
||||||
partGroup = self.getPartGroup()
|
partGroup = self.getPartGroup()
|
||||||
return ['{}.{}'.format(partGroup.Name,name)
|
return ['{}.{}'.format(partGroup.Name,name)
|
||||||
for name in partGroup.getSubObjects(reason)]
|
for name in partGroup.getSubObjects(reason)]
|
||||||
|
|
||||||
def getSubObject(self,obj,subname,retType,mat,transform,depth):
|
|
||||||
if obj.BuildShape==BuildShapeNone:
|
|
||||||
if not subname or subname.startswith(';') or subname.find('.')<0:
|
|
||||||
_ = depth
|
|
||||||
if not retType:
|
|
||||||
return
|
|
||||||
if transform:
|
|
||||||
mat *= obj.Placement.toMatrix()
|
|
||||||
if retType==1:
|
|
||||||
return (obj,mat)
|
|
||||||
return (obj,mat,None)
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _collectParts(self,oldParts,newParts,partMap):
|
def _collectParts(self,oldParts,newParts,partMap):
|
||||||
for part in newParts:
|
for part in newParts:
|
||||||
try:
|
try:
|
||||||
|
@ -1422,22 +1499,24 @@ class Assembly(AsmGroup):
|
||||||
del partMap[part]
|
del partMap[part]
|
||||||
|
|
||||||
def execute(self,obj):
|
def execute(self,obj):
|
||||||
self.constraints = None
|
|
||||||
self.buildShape()
|
|
||||||
System.touch(obj)
|
|
||||||
obj.ViewObject.Proxy.onExecute()
|
|
||||||
|
|
||||||
# collect the part objects of this assembly
|
|
||||||
parts = set()
|
parts = set()
|
||||||
partArrays = set()
|
partArrays = set()
|
||||||
for cstr in self.getConstraints():
|
self.constraints = None
|
||||||
for element in cstr.Proxy.getElements():
|
|
||||||
info = element.Proxy.getInfo()
|
if not self.frozen:
|
||||||
if isinstance(info.Part,tuple):
|
self.buildShape()
|
||||||
partArrays.add(info.Part[0])
|
System.touch(obj)
|
||||||
parts.add(info.Part[0])
|
obj.ViewObject.Proxy.onExecute()
|
||||||
else:
|
|
||||||
parts.add(info.Part)
|
# collect the part objects of this assembly
|
||||||
|
for cstr in self.getConstraints():
|
||||||
|
for element in cstr.Proxy.getElements():
|
||||||
|
info = element.Proxy.getInfo()
|
||||||
|
if isinstance(info.Part,tuple):
|
||||||
|
partArrays.add(info.Part[0])
|
||||||
|
parts.add(info.Part[0])
|
||||||
|
else:
|
||||||
|
parts.add(info.Part)
|
||||||
|
|
||||||
# Update the global part object list for auto solving
|
# Update the global part object list for auto solving
|
||||||
#
|
#
|
||||||
|
@ -1523,48 +1602,104 @@ class Assembly(AsmGroup):
|
||||||
if not touched:
|
if not touched:
|
||||||
obj.purgeTouched()
|
obj.purgeTouched()
|
||||||
|
|
||||||
|
def upgrade(self):
|
||||||
|
'Upgrade old assembly objects to the new version'
|
||||||
|
partGroup = self.getPartGroup()
|
||||||
|
if hasattr(partGroup,'Shape'):
|
||||||
|
return
|
||||||
|
partGroup.setPropertyStatus('GroupMode','-Immutable')
|
||||||
|
partGroup.GroupMode = 0 # prevent auto delete children
|
||||||
|
newPartGroup = AsmPartGroup.make(self.Object)
|
||||||
|
newPartGroup.Group = partGroup.Group
|
||||||
|
newPartGroup.VisibilityList = partGroup.VisibilityList
|
||||||
|
|
||||||
|
elementGroup = self.getElementGroup()
|
||||||
|
vis = elementGroup.VisibilityList
|
||||||
|
elements = []
|
||||||
|
old = elementGroup.Group
|
||||||
|
for element in old:
|
||||||
|
copy = AsmElement.create('Element',elementGroup)
|
||||||
|
link = element.LinkedObject
|
||||||
|
if isinstance(link,tuple):
|
||||||
|
copy.LinkedObject = (newPartGroup,link[1])
|
||||||
|
copy.Label = element.Label
|
||||||
|
copy.Proxy._initializing = False
|
||||||
|
elements.append(copy)
|
||||||
|
|
||||||
|
elementGroup.Group = elements
|
||||||
|
elementGroup.setPropertyStatus('VisibilityList','-Immutable')
|
||||||
|
elementGroup.VisibilityList = vis
|
||||||
|
elementGroup.cacheChildLabel()
|
||||||
|
|
||||||
|
for element in old:
|
||||||
|
old.Document.removeObject(old)
|
||||||
|
|
||||||
|
self.Object.setLink({2:newPartGroup})
|
||||||
|
partGroup.Document.removeObject(partGroup)
|
||||||
|
|
||||||
def buildShape(self):
|
def buildShape(self):
|
||||||
obj = self.Object
|
obj = self.Object
|
||||||
if obj.BuildShape == BuildShapeNone:
|
partGroup = self.getPartGroup()
|
||||||
if not obj.Shape.isNull():
|
if not obj.Freeze and obj.BuildShape==BuildShapeNone:
|
||||||
obj.Shape = Part.Shape()
|
obj.Shape = Part.Shape();
|
||||||
|
if hasattr(partGroup, 'Shape'):
|
||||||
|
partGroup.Shape = Part.Shape()
|
||||||
return
|
return
|
||||||
|
|
||||||
shape = []
|
|
||||||
partGroup = self.getPartGroup()
|
|
||||||
group = partGroup.Group
|
group = partGroup.Group
|
||||||
if not group:
|
|
||||||
raise RuntimeError('no parts')
|
|
||||||
if obj.BuildShape == BuildShapeCut:
|
|
||||||
shape = Part.getShape(group[0]).Solids
|
|
||||||
if not shape:
|
|
||||||
raise RuntimeError('First part has no solid')
|
|
||||||
if len(shape)>1:
|
|
||||||
shape = [shape[0].fuse(shape[1:])]
|
|
||||||
group = group[1:]
|
|
||||||
|
|
||||||
for o in group:
|
shapes = []
|
||||||
if obj.isElementVisible(o.Name):
|
if obj.BuildShape == BuildShapeCompound:
|
||||||
shape += Part.getShape(o).Solids
|
for o in group:
|
||||||
if not shape:
|
if obj.isElementVisible(o.Name):
|
||||||
raise RuntimeError('No solids found in parts')
|
shape = Part.getShape(o)
|
||||||
if len(shape) == 1:
|
if not shape.isNull():
|
||||||
obj.Shape = shape[0]
|
shapes.append(shape)
|
||||||
elif obj.BuildShape == BuildShapeFuse:
|
|
||||||
obj.Shape = shape[0].fuse(shape[1:])
|
|
||||||
elif obj.BuildShape == BuildShapeCut:
|
|
||||||
if len(shape)>2:
|
|
||||||
obj.Shape = shape[0].cut(shape[1].fuse(shape[2:]))
|
|
||||||
else:
|
|
||||||
obj.Shape = shape[0].cut(shape[1])
|
|
||||||
else:
|
else:
|
||||||
obj.Shape = Part.makeCompound(shape)
|
# first shape is always included regardless of its visibility
|
||||||
|
solids = Part.getShape(group[0]).Solids
|
||||||
|
if solids:
|
||||||
|
if len(solids)>1 and obj.BuildShape!=BuildShapeFuse:
|
||||||
|
shapes.append(solids[0].fuse(solids[1:]))
|
||||||
|
else:
|
||||||
|
shapes += solids
|
||||||
|
group = group[1:]
|
||||||
|
for o in group:
|
||||||
|
if obj.isElementVisible(o.Name):
|
||||||
|
shape = Part.getShape(o)
|
||||||
|
# in case the first part have solids, we only include
|
||||||
|
# subsequent part containing solid
|
||||||
|
if solids:
|
||||||
|
shapes += shape.Solids
|
||||||
|
else:
|
||||||
|
shapes += shape
|
||||||
|
if not shapes:
|
||||||
|
raise RuntimeError('No shape found in parts')
|
||||||
|
if len(shapes) == 1:
|
||||||
|
shape = shapes[0]
|
||||||
|
# make sure the 'shape' has identity transform to prevent it from
|
||||||
|
# messing up with assembly's or partGroup's Placement
|
||||||
|
if not shape.Placement.isIdentity():
|
||||||
|
shape = Part.makeCompound(shape)
|
||||||
|
elif obj.BuildShape == BuildShapeFuse:
|
||||||
|
shape = shapes[0].fuse(shapes[1:])
|
||||||
|
elif obj.BuildShape == BuildShapeCut:
|
||||||
|
shape = shapes[0].cut(shapes[1:])
|
||||||
|
elif obj.BuildShape == BuildShapeCommon:
|
||||||
|
shape = shapes[0].common(shapes[1:])
|
||||||
|
else:
|
||||||
|
shape = Part.makeCompound(shapes)
|
||||||
|
|
||||||
|
if obj.Freeze or obj.BuildShape!=BuildShapeCompound:
|
||||||
|
partGroup.Shape = shape
|
||||||
|
elif hasattr(partGroup,'Shape'):
|
||||||
|
partGroup.Shape = Part.Shape()
|
||||||
|
|
||||||
|
shape.Placement = obj.Placement
|
||||||
|
obj.Shape = shape
|
||||||
|
|
||||||
def attach(self, obj):
|
def attach(self, obj):
|
||||||
obj.addProperty("App::PropertyEnumeration","BuildShape","Base",'')
|
obj.addProperty("App::PropertyEnumeration","BuildShape","Base",'')
|
||||||
obj.addProperty(
|
|
||||||
"App::PropertyLinkSubHidden","ColoredElements","Base",'')
|
|
||||||
obj.setPropertyStatus('ColoredElements',('Hidden','Immutable'))
|
|
||||||
obj.BuildShape = BuildShapeNames
|
obj.BuildShape = BuildShapeNames
|
||||||
super(Assembly,self).attach(obj)
|
super(Assembly,self).attach(obj)
|
||||||
|
|
||||||
|
@ -1573,13 +1708,14 @@ class Assembly(AsmGroup):
|
||||||
self.partArrays = set()
|
self.partArrays = set()
|
||||||
obj.configLinkProperty('Placement')
|
obj.configLinkProperty('Placement')
|
||||||
if not hasattr(obj,'ColoredElements'):
|
if not hasattr(obj,'ColoredElements'):
|
||||||
obj.addProperty(
|
obj.addProperty("App::PropertyLinkSubHidden",
|
||||||
"App::PropertyLinkSubHidden","ColoredElements","Base",'')
|
"ColoredElements","Base",'')
|
||||||
obj.setPropertyStatus('ColoredElements',('Hidden','Immutable'))
|
obj.setPropertyStatus('ColoredElements',('Hidden','Immutable'))
|
||||||
|
if not hasattr(obj,'Freeze'):
|
||||||
|
obj.addProperty('App::PropertyBool','Freeze','Base','')
|
||||||
obj.configLinkProperty('ColoredElements')
|
obj.configLinkProperty('ColoredElements')
|
||||||
super(Assembly,self).linkSetup(obj)
|
super(Assembly,self).linkSetup(obj)
|
||||||
System.attach(obj)
|
System.attach(obj)
|
||||||
self.onChanged(obj,'BuildShape')
|
|
||||||
|
|
||||||
# make sure all children are there, first constraint group, then element
|
# make sure all children are there, first constraint group, then element
|
||||||
# group, and finally part group. Call getPartGroup below will make sure
|
# group, and finally part group. Call getPartGroup below will make sure
|
||||||
|
@ -1587,19 +1723,45 @@ class Assembly(AsmGroup):
|
||||||
# correct rendering and picking behavior
|
# correct rendering and picking behavior
|
||||||
self.getPartGroup(True)
|
self.getPartGroup(True)
|
||||||
|
|
||||||
|
self.onChanged(obj,'BuildShape')
|
||||||
|
|
||||||
def onChanged(self, obj, prop):
|
def onChanged(self, obj, prop):
|
||||||
|
if not getattr(self,'Object',None) or FreeCAD.isRestoring():
|
||||||
|
return
|
||||||
if prop == 'BuildShape':
|
if prop == 'BuildShape':
|
||||||
if not obj.BuildShape or obj.BuildShape == BuildShapeCompound:
|
self.buildShape()
|
||||||
obj.setPropertyStatus('Shape','-Transient')
|
return
|
||||||
|
if prop == 'Freeze':
|
||||||
|
if obj.Freeze == self.frozen:
|
||||||
|
return
|
||||||
|
self.upgrade()
|
||||||
|
for o in self.getElementGroup().Group:
|
||||||
|
self.freeze(o)
|
||||||
|
if obj.BuildShape==BuildShapeNone:
|
||||||
|
self.buildShape()
|
||||||
|
elif obj.Freeze:
|
||||||
|
self.getPartGroup().Shape = obj.Shape
|
||||||
else:
|
else:
|
||||||
obj.setPropertyStatus('Shape','Transient')
|
self.getPartGroup().Shape = Part.Shape()
|
||||||
return
|
return
|
||||||
if prop not in _IgnoredProperties:
|
if prop not in _IgnoredProperties:
|
||||||
System.onChanged(obj,prop)
|
System.onChanged(obj,prop)
|
||||||
Assembly.autoSolve()
|
Assembly.autoSolve()
|
||||||
|
|
||||||
|
def onDocumentRestored(self,obj):
|
||||||
|
super(Assembly,self).onDocumentRestored(obj)
|
||||||
|
partGroup = self.getPartGroup()
|
||||||
|
self.frozen = obj.Freeze
|
||||||
|
if self.frozen or hasattr(partGroup,'Shape'):
|
||||||
|
obj.Shape = partGroup.Shape
|
||||||
|
elif obj.Shape.isNull() and \
|
||||||
|
obj.BuildShape == BuildShapeCompound:
|
||||||
|
self.buildShape()
|
||||||
|
|
||||||
def getConstraintGroup(self, create=False):
|
def getConstraintGroup(self, create=False):
|
||||||
obj = self.Object
|
obj = self.Object
|
||||||
|
if obj.Freeze:
|
||||||
|
return None
|
||||||
try:
|
try:
|
||||||
ret = obj.Group[0]
|
ret = obj.Group[0]
|
||||||
checkType(ret,AsmConstraintGroup)
|
checkType(ret,AsmConstraintGroup)
|
||||||
|
@ -1694,8 +1856,8 @@ class Assembly(AsmGroup):
|
||||||
if undo:
|
if undo:
|
||||||
FreeCAD.setActiveTransaction('Create assembly')
|
FreeCAD.setActiveTransaction('Create assembly')
|
||||||
try:
|
try:
|
||||||
obj = doc.addObject(
|
obj = doc.addObject("Part::FeaturePython",name,Assembly(),None,True)
|
||||||
"Part::FeaturePython",name,Assembly(),None,True)
|
obj.setPropertyStatus('Shape','Transient')
|
||||||
ViewProviderAssembly(obj.ViewObject)
|
ViewProviderAssembly(obj.ViewObject)
|
||||||
obj.Visibility = True
|
obj.Visibility = True
|
||||||
obj.purgeTouched()
|
obj.purgeTouched()
|
||||||
|
@ -1805,10 +1967,18 @@ class Assembly(AsmGroup):
|
||||||
|
|
||||||
|
|
||||||
class ViewProviderAssembly(ViewProviderAsmGroup):
|
class ViewProviderAssembly(ViewProviderAsmGroup):
|
||||||
|
_iconName = 'Assembly_Assembly_Frozen_Tree.svg'
|
||||||
|
_addToSceneGraph = True
|
||||||
|
|
||||||
def __init__(self,vobj):
|
def __init__(self,vobj):
|
||||||
self._movingPart = None
|
self._movingPart = None
|
||||||
super(ViewProviderAssembly,self).__init__(vobj)
|
super(ViewProviderAssembly,self).__init__(vobj)
|
||||||
|
|
||||||
|
def attach(self,vobj):
|
||||||
|
super(ViewProviderAssembly,self).attach(vobj)
|
||||||
|
if not hasattr(vobj,'ShowParts'):
|
||||||
|
vobj.addProperty("App::PropertyBool","ShowParts"," Link")
|
||||||
|
|
||||||
def onDelete(self,vobj,_subs):
|
def onDelete(self,vobj,_subs):
|
||||||
for o in vobj.Object.Proxy.getPartGroup().Group:
|
for o in vobj.Object.Proxy.getPartGroup().Group:
|
||||||
if o.TypeId == 'App::Origin':
|
if o.TypeId == 'App::Origin':
|
||||||
|
@ -1843,7 +2013,13 @@ class ViewProviderAssembly(ViewProviderAsmGroup):
|
||||||
partGroup,owner,subname = info
|
partGroup,owner,subname = info
|
||||||
partGroup.dropObject(obj,owner,subname)
|
partGroup.dropObject(obj,owner,subname)
|
||||||
|
|
||||||
|
def updateData(self,obj,prop):
|
||||||
|
if prop == 'Freeze':
|
||||||
|
obj.ViewObject.signalChangeIcon()
|
||||||
|
|
||||||
def getIcon(self):
|
def getIcon(self):
|
||||||
|
if getattr(self.ViewObject.Object,'Freeze',False):
|
||||||
|
return utils.getIcon(self.__class__)
|
||||||
return System.getIcon(self.ViewObject.Object)
|
return System.getIcon(self.ViewObject.Object)
|
||||||
|
|
||||||
def doubleClicked(self, _vobj):
|
def doubleClicked(self, _vobj):
|
||||||
|
@ -1897,6 +2073,24 @@ class ViewProviderAssembly(ViewProviderAsmGroup):
|
||||||
self._movingPart = None
|
self._movingPart = None
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def showParts(self):
|
||||||
|
self.ViewObject.Object.Proxy.getPartGroup().ViewObject.Proxy.showParts()
|
||||||
|
|
||||||
|
def updateData(self,_obj,prop):
|
||||||
|
if not hasattr(self,'ViewObject') or FreeCAD.isRestoring():
|
||||||
|
return
|
||||||
|
if prop=='Freeze' or prop=='BuildShape':
|
||||||
|
self.showParts()
|
||||||
|
|
||||||
|
def onChanged(self,_vobj,prop):
|
||||||
|
if not hasattr(self,'ViewObject') or FreeCAD.isRestoring():
|
||||||
|
return
|
||||||
|
if prop=='ShowParts':
|
||||||
|
self.showParts()
|
||||||
|
|
||||||
|
def onFinishRestoring(self):
|
||||||
|
self.showParts()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def isBusy(cls):
|
def isBusy(cls):
|
||||||
return cls._Busy
|
return cls._Busy
|
||||||
|
|
|
@ -276,7 +276,7 @@ def _solve(objs=None,recursive=None,reportFailed=True,
|
||||||
for obj in objs:
|
for obj in objs:
|
||||||
if not isTypeOf(obj,Assembly):
|
if not isTypeOf(obj,Assembly):
|
||||||
continue
|
continue
|
||||||
if System.isDisabled(obj):
|
if System.isDisabled(obj) or obj.Freeze:
|
||||||
logger.debug('bypass disabled assembly {}'.format(objName(obj)))
|
logger.debug('bypass disabled assembly {}'.format(objName(obj)))
|
||||||
continue
|
continue
|
||||||
logger.debug('adding assembly {}'.format(objName(obj)))
|
logger.debug('adding assembly {}'.format(objName(obj)))
|
||||||
|
@ -298,7 +298,7 @@ def _solve(objs=None,recursive=None,reportFailed=True,
|
||||||
for obj in objs:
|
for obj in objs:
|
||||||
if not isTypeOf(obj,Assembly):
|
if not isTypeOf(obj,Assembly):
|
||||||
continue
|
continue
|
||||||
if System.isDisabled(obj):
|
if System.isDisabled(obj) or obj.Freeze:
|
||||||
logger.debug('skip disabled assembly {}'.format(objName(obj)))
|
logger.debug('skip disabled assembly {}'.format(objName(obj)))
|
||||||
continue
|
continue
|
||||||
logger.debug('adding assembly {}'.format(objName(obj)))
|
logger.debug('adding assembly {}'.format(objName(obj)))
|
||||||
|
|
Loading…
Reference in New Issue
Block a user