Refactor part move command
This commit is contained in:
parent
398b1fad86
commit
f16fbf52a8
713
assembly.py
713
assembly.py
|
@ -8,6 +8,8 @@ from asm3.constraint import Constraint
|
||||||
from asm3.system import System
|
from asm3.system import System
|
||||||
|
|
||||||
def setupUndo(doc,undoDocs,name):
|
def setupUndo(doc,undoDocs,name):
|
||||||
|
if undoDocs is None:
|
||||||
|
return
|
||||||
if doc.HasPendingTransaction or doc in undoDocs:
|
if doc.HasPendingTransaction or doc in undoDocs:
|
||||||
return
|
return
|
||||||
if not name:
|
if not name:
|
||||||
|
@ -110,7 +112,7 @@ class ViewProviderAsmGroup(ViewProviderAsmBase):
|
||||||
def claimChildren(self):
|
def claimChildren(self):
|
||||||
return self.ViewObject.Object.Group
|
return self.ViewObject.Object.Group
|
||||||
|
|
||||||
def doubleClicked(self):
|
def doubleClicked(self, _vobj):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@ -300,142 +302,83 @@ class ViewProviderAsmElement(ViewProviderAsmBase):
|
||||||
vobj.ShapeMaterial.EmissiveColor = self.getDefaultColor()
|
vobj.ShapeMaterial.EmissiveColor = self.getDefaultColor()
|
||||||
vobj.DrawStyle = 1
|
vobj.DrawStyle = 1
|
||||||
vobj.LineWidth = 4
|
vobj.LineWidth = 4
|
||||||
vobj.PointSize = 6
|
vobj.PointSize = 8
|
||||||
|
|
||||||
def getDefaultColor(self):
|
def getDefaultColor(self):
|
||||||
return (60.0/255.0,1.0,1.0)
|
return (60.0/255.0,1.0,1.0)
|
||||||
|
|
||||||
|
PartInfo = namedtuple('AsmPartInfo', ('Parent','SubnameRef','Part',
|
||||||
|
'PartName','Placement','Object','Subname','Shape'))
|
||||||
|
|
||||||
class AsmElementLink(AsmBase):
|
def getPartInfo(parent, subname):
|
||||||
def __init__(self,parent):
|
'''Return a named tuple containing the part object element information
|
||||||
super(AsmElementLink,self).__init__()
|
|
||||||
self.info = None
|
|
||||||
self.parent = getProxy(parent,AsmConstraint)
|
|
||||||
|
|
||||||
def linkSetup(self,obj):
|
Parameters:
|
||||||
super(AsmElementLink,self).linkSetup(obj)
|
|
||||||
obj.configLinkProperty('LinkedObject')
|
|
||||||
# obj.setPropertyStatus('LinkedObject','Immutable')
|
|
||||||
obj.setPropertyStatus('LinkedObject','ReadOnly')
|
|
||||||
|
|
||||||
def attach(self,obj):
|
parent: the parent document object, either an assembly, or a part group
|
||||||
obj.addProperty("App::PropertyXLink","LinkedObject"," Link",'')
|
|
||||||
super(AsmElementLink,self).attach(obj)
|
|
||||||
|
|
||||||
def execute(self,obj):
|
subname: subname reference to the part element (i.e. edge, face, vertex)
|
||||||
obj.ViewObject.Proxy.onExecute(self.getInfo(True))
|
|
||||||
return False
|
|
||||||
|
|
||||||
def getElement(self):
|
Return a named tuple with the following fields:
|
||||||
linked = self.Object.getLinkedObject(False)
|
|
||||||
if not linked:
|
|
||||||
raise RuntimeError('Element link broken')
|
|
||||||
if not isTypeOf(linked,AsmElement):
|
|
||||||
raise RuntimeError('Invalid element type')
|
|
||||||
return linked.Proxy
|
|
||||||
|
|
||||||
def getAssembly(self):
|
Parent: set to the input parent object
|
||||||
return self.parent.parent.parent
|
|
||||||
|
|
||||||
def getSubName(self):
|
SubnameRef: set to the input subname reference
|
||||||
link = self.Object.LinkedObject
|
|
||||||
if not isinstance(link,tuple):
|
|
||||||
raise RuntimeError('Invalid element link "{}"'.format(
|
|
||||||
objName(self.Object)))
|
|
||||||
return link[1]
|
|
||||||
|
|
||||||
def getShapeSubName(self):
|
Part: either the part object, or a tuple(obj, idx) to refer to an element in
|
||||||
element = self.getElement()
|
an link array,
|
||||||
assembly = element.getAssembly()
|
|
||||||
if assembly == self.getAssembly():
|
|
||||||
return element.getSubName()
|
|
||||||
# pop two names from back (i.e. element group, element)
|
|
||||||
subname = self.getSubName()
|
|
||||||
sub = subname.split('.')[:-3]
|
|
||||||
sub = '.'.join(sub) + '.' + assembly.getPartGroup().Name + \
|
|
||||||
'.' + element.getSubName()
|
|
||||||
logger.debug('shape subname {} -> {}'.format(subname,sub))
|
|
||||||
return sub
|
|
||||||
|
|
||||||
def prepareLink(self,owner,subname):
|
PartName: a string name for the part
|
||||||
assembly = self.getAssembly()
|
|
||||||
sobj = owner.getSubObject(subname,1)
|
|
||||||
if not sobj:
|
|
||||||
raise RuntimeError('invalid element link {} broken: {}'.format(
|
|
||||||
objName(owner),subname))
|
|
||||||
if isTypeOf(sobj,AsmElementLink):
|
|
||||||
# if it points to another AsElementLink that belongs the same
|
|
||||||
# assembly, simply return the same link
|
|
||||||
if sobj.Proxy.getAssembly() == assembly:
|
|
||||||
return sobj.LinkedObject
|
|
||||||
# If it is from another assembly (i.e. a nested assembly), convert
|
|
||||||
# the subname reference by poping three names (constraint group,
|
|
||||||
# constraint, element link) from the back, and then append with the
|
|
||||||
# element link's own subname reference
|
|
||||||
sub = subname.split('.')[:-4]
|
|
||||||
sub = '.'.join(subname)+'.'+sobj.Proxy.getSubName()
|
|
||||||
logger.debug('convert element link {} -> {}'.format(subname,sub))
|
|
||||||
return (owner,sub)
|
|
||||||
|
|
||||||
if isTypeOf(sobj,AsmElement):
|
Placement: the placement of the part
|
||||||
return (owner,subname)
|
|
||||||
|
|
||||||
# try to see if the reference comes from some nested assembly
|
Object: the object that owns the element. In case 'Part' is an assembly, we
|
||||||
ret = assembly.findChild(owner,subname,recursive=True)
|
the element owner will always be some (grand)child of the 'Part'
|
||||||
if not ret:
|
|
||||||
# It is from a non assembly child part, then use our own element
|
|
||||||
# group as the holder for elements
|
|
||||||
ret = [Assembly.Info(assembly.Object,owner,subname)]
|
|
||||||
|
|
||||||
if not isTypeOf(ret[-1].Object,AsmPartGroup):
|
Subname: the subname reference to the element owner object. The reference is
|
||||||
raise RuntimeError('Invalid element link ' + subname)
|
realtive to the 'Part', i.e. Object = Part.getSubObject(subname), or if
|
||||||
|
'Part' is a tuple, Object = Part[0].getSubObject(str(Part[1]) + '.' +
|
||||||
|
subname)
|
||||||
|
|
||||||
# call AsmElement.make to either create a new element, or an existing
|
Shape: Part.Shape of the linked element. The shape's placement is relative
|
||||||
# element if there is one
|
to the owner Part.
|
||||||
element = AsmElement.make(AsmElement.Selection(
|
'''
|
||||||
ret[-1].Assembly,None,ret[-1].Subname))
|
|
||||||
if ret[-1].Assembly == assembly.Object:
|
|
||||||
return (assembly.getElementGroup(),element.Name+'.')
|
|
||||||
|
|
||||||
elementSub = ret[-1].Object.Name + '.' + ret[-1].Subname
|
subnameRef = subname
|
||||||
sub = subname[:-(len(elementSub)+1)] + '.' + \
|
|
||||||
ret[-1].Assembly.Proxy.getElementGroup().Name + '.' + \
|
|
||||||
element.Name + '.'
|
|
||||||
logger.debug('generate new element {} -> {}'.format(subname,sub))
|
|
||||||
return (owner,sub)
|
|
||||||
|
|
||||||
def setLink(self,owner,subname):
|
|
||||||
obj = self.Object
|
|
||||||
obj.setLink(*self.prepareLink(owner,subname))
|
|
||||||
linked = obj.getLinkedObject(False)
|
|
||||||
if linked and linked!=obj:
|
|
||||||
label = linked.Label.split('_')
|
|
||||||
if label[-1].startswith('Element'):
|
|
||||||
label[-1] = 'Link'
|
|
||||||
obj.Label = '_'.join(label)
|
|
||||||
else:
|
|
||||||
obj.Label = obj.Name
|
|
||||||
|
|
||||||
Info = namedtuple('AsmElementLinkInfo',
|
|
||||||
('Part','PartName','Placement','Object','Subname','Shape'))
|
|
||||||
|
|
||||||
def getInfo(self,refresh=False):
|
|
||||||
if not refresh:
|
|
||||||
ret = getattr(self,'info',None)
|
|
||||||
if ret:
|
|
||||||
return ret
|
|
||||||
self.info = None
|
|
||||||
if not getattr(self,'Object',None):
|
|
||||||
return
|
|
||||||
assembly = self.getAssembly()
|
|
||||||
subname = self.getShapeSubName()
|
|
||||||
names = subname.split('.')
|
names = subname.split('.')
|
||||||
partGroup = assembly.getPartGroup()
|
if isTypeOf(parent,Assembly):
|
||||||
|
child = parent.getSubObject(names[0]+'.',1)
|
||||||
|
if not child:
|
||||||
|
raise RuntimeError('Invalid sub object {}, {}'.format(
|
||||||
|
objName(parent), subname))
|
||||||
|
|
||||||
|
if isTypeOf(child,AsmElementGroup):
|
||||||
|
raise RuntimeError('Element object cannot be moved directly')
|
||||||
|
|
||||||
|
if isTypeOf(child,AsmConstraintGroup):
|
||||||
|
child = parent.getSubObject(subname,1)
|
||||||
|
if not child:
|
||||||
|
raise RuntimeError('Invalid sub object {}, {}'.format(
|
||||||
|
objName(parent), subname))
|
||||||
|
if not isTypeOf(child,AsmElementLink):
|
||||||
|
raise RuntimeError('{} cannot be moved'.format(objName(child)))
|
||||||
|
return child.Proxy.getInfo()
|
||||||
|
|
||||||
|
partGroup = child
|
||||||
|
names = names[1:]
|
||||||
|
subname = '.'.join(names)
|
||||||
|
|
||||||
|
elif isTypeOf(parent,AsmPartGroup):
|
||||||
|
partGroup = parent
|
||||||
|
else:
|
||||||
|
raise RuntimeError('{} is not Assembly or PartGroup'.format(
|
||||||
|
objName(parent)))
|
||||||
|
|
||||||
part = partGroup.getSubObject(names[0]+'.',1)
|
part = partGroup.getSubObject(names[0]+'.',1)
|
||||||
if not part:
|
if not part:
|
||||||
raise RuntimeError('Eelement link "{}" borken: {}'.format(
|
raise RuntimeError('Invalid sub object {}, {}'.format(
|
||||||
objName(self.Object),subname))
|
objName(parent), subnameRef))
|
||||||
|
|
||||||
# For storing the shape of the element with proper transformation
|
# For storing the shape of the element with proper transformation
|
||||||
shape = None
|
shape = None
|
||||||
|
@ -445,9 +388,7 @@ class AsmElementLink(AsmBase):
|
||||||
# a link
|
# a link
|
||||||
obj = None
|
obj = None
|
||||||
|
|
||||||
if not isTypeOf(part,Assembly,True) and \
|
if not isTypeOf(part,Assembly,True):
|
||||||
not Constraint.isDisabled(self.parent.Object) and \
|
|
||||||
not Constraint.isLocked(self.parent.Object):
|
|
||||||
getter = getattr(part.getLinkedObject(True),
|
getter = getattr(part.getLinkedObject(True),
|
||||||
'getLinkExtProperty',None)
|
'getLinkExtProperty',None)
|
||||||
|
|
||||||
|
@ -488,8 +429,8 @@ class AsmElementLink(AsmBase):
|
||||||
part = (part[0],int(idx),part[1])
|
part = (part[0],int(idx),part[1])
|
||||||
pla = part[0].PlacementList[idx]
|
pla = part[0].PlacementList[idx]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise RuntimeError('invalid array subname of element '
|
raise RuntimeError('invalid array subname of element {}: '
|
||||||
'{}: {}'.format(objName(self.Object),subname))
|
'{}'.format(objName(parent),subnameRef))
|
||||||
|
|
||||||
partName = '{}.{}.'.format(part[0].Name,idx)
|
partName = '{}.{}.'.format(part[0].Name,idx)
|
||||||
|
|
||||||
|
@ -506,12 +447,145 @@ class AsmElementLink(AsmBase):
|
||||||
obj = part.getLinkedObject(False)
|
obj = part.getLinkedObject(False)
|
||||||
partName = part.Name
|
partName = part.Name
|
||||||
|
|
||||||
self.info = AsmElementLink.Info(Part = part,
|
return PartInfo(Parent = parent,
|
||||||
|
SubnameRef = subnameRef,
|
||||||
|
Part = part,
|
||||||
PartName = partName,
|
PartName = partName,
|
||||||
Placement = pla.copy(),
|
Placement = pla.copy(),
|
||||||
Object = obj,
|
Object = obj,
|
||||||
Subname = subname,
|
Subname = subname,
|
||||||
Shape = shape.copy())
|
Shape = shape.copy())
|
||||||
|
|
||||||
|
|
||||||
|
class AsmElementLink(AsmBase):
|
||||||
|
def __init__(self,parent):
|
||||||
|
super(AsmElementLink,self).__init__()
|
||||||
|
self.info = None
|
||||||
|
self.parent = getProxy(parent,AsmConstraint)
|
||||||
|
|
||||||
|
def linkSetup(self,obj):
|
||||||
|
super(AsmElementLink,self).linkSetup(obj)
|
||||||
|
obj.configLinkProperty('LinkedObject')
|
||||||
|
# obj.setPropertyStatus('LinkedObject','Immutable')
|
||||||
|
obj.setPropertyStatus('LinkedObject','ReadOnly')
|
||||||
|
|
||||||
|
def attach(self,obj):
|
||||||
|
obj.addProperty("App::PropertyXLink","LinkedObject"," Link",'')
|
||||||
|
super(AsmElementLink,self).attach(obj)
|
||||||
|
|
||||||
|
def execute(self,_obj):
|
||||||
|
self.getInfo(True)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def getElement(self):
|
||||||
|
linked = self.Object.getLinkedObject(False)
|
||||||
|
if not linked:
|
||||||
|
raise RuntimeError('Element link broken')
|
||||||
|
if not isTypeOf(linked,AsmElement):
|
||||||
|
raise RuntimeError('Invalid element type')
|
||||||
|
return linked.Proxy
|
||||||
|
|
||||||
|
def getAssembly(self):
|
||||||
|
return self.parent.parent.parent
|
||||||
|
|
||||||
|
def getSubName(self):
|
||||||
|
link = self.Object.LinkedObject
|
||||||
|
if not isinstance(link,tuple):
|
||||||
|
raise RuntimeError('Invalid element link "{}"'.format(
|
||||||
|
objName(self.Object)))
|
||||||
|
return link[1]
|
||||||
|
|
||||||
|
def getElementSubname(self):
|
||||||
|
'''Resolve element link subname
|
||||||
|
|
||||||
|
AsmElementLink links to elements of a constraint. It always link to an
|
||||||
|
AsmElement object belonging to the same parent assembly. AsmElement is
|
||||||
|
also a link, which links to an arbitarily nested part object. This
|
||||||
|
function is for resolving the AsmElementLink's subname reference to the
|
||||||
|
actual part object subname reference relative to the parent assembly's
|
||||||
|
part group
|
||||||
|
'''
|
||||||
|
element = self.getElement()
|
||||||
|
assembly = element.getAssembly()
|
||||||
|
if assembly == self.getAssembly():
|
||||||
|
return element.getSubName()
|
||||||
|
# pop two names from back (i.e. element group, element)
|
||||||
|
subname = self.getSubName()
|
||||||
|
sub = subname.split('.')[:-3]
|
||||||
|
sub = '.'.join(sub) + '.' + assembly.getPartGroup().Name + \
|
||||||
|
'.' + element.getSubName()
|
||||||
|
logger.debug('shape subname {} -> {}'.format(subname,sub))
|
||||||
|
return sub
|
||||||
|
|
||||||
|
def prepareLink(self,owner,subname):
|
||||||
|
assembly = self.getAssembly()
|
||||||
|
sobj = owner.getSubObject(subname,1)
|
||||||
|
if not sobj:
|
||||||
|
raise RuntimeError('invalid element link {} broken: {}'.format(
|
||||||
|
objName(owner),subname))
|
||||||
|
if isTypeOf(sobj,AsmElementLink):
|
||||||
|
# if it points to another AsElementLink that belongs the same
|
||||||
|
# assembly, simply return the same link
|
||||||
|
if sobj.Proxy.getAssembly() == assembly:
|
||||||
|
return sobj.LinkedObject
|
||||||
|
# If it is from another assembly (i.e. a nested assembly), convert
|
||||||
|
# the subname reference by poping three names (constraint group,
|
||||||
|
# constraint, element link) from the back, and then append with the
|
||||||
|
# element link's own subname reference
|
||||||
|
sub = subname.split('.')[:-4]
|
||||||
|
sub = '.'.join(subname)+'.'+sobj.Proxy.getSubName()
|
||||||
|
logger.debug('convert element link {} -> {}'.format(subname,sub))
|
||||||
|
return (owner,sub)
|
||||||
|
|
||||||
|
if isTypeOf(sobj,AsmElement):
|
||||||
|
return (owner,subname)
|
||||||
|
|
||||||
|
# try to see if the reference comes from some nested assembly
|
||||||
|
ret = assembly.find(owner,subname,recursive=True)
|
||||||
|
if not ret:
|
||||||
|
# It is from a non assembly child part, then use our own element
|
||||||
|
# group as the holder for elements
|
||||||
|
ret = [Assembly.Info(assembly.Object,owner,subname)]
|
||||||
|
|
||||||
|
if not isTypeOf(ret[-1].Object,AsmPartGroup):
|
||||||
|
raise RuntimeError('Invalid element link ' + subname)
|
||||||
|
|
||||||
|
# call AsmElement.make to either create a new element, or an existing
|
||||||
|
# element if there is one
|
||||||
|
element = AsmElement.make(AsmElement.Selection(
|
||||||
|
ret[-1].Assembly,None,ret[-1].Subname))
|
||||||
|
if ret[-1].Assembly == assembly.Object:
|
||||||
|
return (assembly.getElementGroup(),element.Name+'.')
|
||||||
|
|
||||||
|
elementSub = ret[-1].Object.Name + '.' + ret[-1].Subname
|
||||||
|
sub = subname[:-(len(elementSub)+1)] + '.' + \
|
||||||
|
ret[-1].Assembly.Proxy.getElementGroup().Name + '.' + \
|
||||||
|
element.Name + '.'
|
||||||
|
logger.debug('generate new element {} -> {}'.format(subname,sub))
|
||||||
|
return (owner,sub)
|
||||||
|
|
||||||
|
def setLink(self,owner,subname):
|
||||||
|
obj = self.Object
|
||||||
|
obj.setLink(*self.prepareLink(owner,subname))
|
||||||
|
linked = obj.getLinkedObject(False)
|
||||||
|
if linked and linked!=obj:
|
||||||
|
label = linked.Label.split('_')
|
||||||
|
if label[-1].startswith('Element'):
|
||||||
|
label[-1] = 'Link'
|
||||||
|
obj.Label = '_'.join(label)
|
||||||
|
else:
|
||||||
|
obj.Label = obj.Name
|
||||||
|
|
||||||
|
def getInfo(self,refresh=False):
|
||||||
|
if not refresh:
|
||||||
|
ret = getattr(self,'info',None)
|
||||||
|
if ret:
|
||||||
|
return ret
|
||||||
|
self.info = None
|
||||||
|
if not getattr(self,'Object',None):
|
||||||
|
return
|
||||||
|
self.info = getPartInfo(self.getAssembly().getPartGroup(),
|
||||||
|
self.getElementSubname())
|
||||||
return self.info
|
return self.info
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -548,84 +622,10 @@ class AsmElementLink(AsmBase):
|
||||||
def setPlacement(part,pla,undoDocs,undoName=None):
|
def setPlacement(part,pla,undoDocs,undoName=None):
|
||||||
AsmElementLink.setPlacement(part,pla,undoDocs,undoName)
|
AsmElementLink.setPlacement(part,pla,undoDocs,undoName)
|
||||||
|
|
||||||
class AsmDraggingContext(object):
|
|
||||||
def __init__(self,info):
|
|
||||||
self.undos = None
|
|
||||||
self.part = info.Part
|
|
||||||
rot = utils.getElementRotation(info.Shape)
|
|
||||||
if not rot:
|
|
||||||
# in case the shape has no normal, like a vertex, just use an empty
|
|
||||||
# rotation, which means having the same rotation has the owner part.
|
|
||||||
rot = FreeCAD.Rotation()
|
|
||||||
pla = FreeCAD.Placement(utils.getElementPos(info.Shape),rot)
|
|
||||||
self.offset = FreeCAD.Placement(pla.toMatrix())
|
|
||||||
self.offsetInv = FreeCAD.Placement(pla.toMatrix().inverse())
|
|
||||||
self.placement = info.Placement.multiply(pla)
|
|
||||||
self.tracePoint = self.placement.Base
|
|
||||||
self.trace = None
|
|
||||||
|
|
||||||
def update(self,info):
|
|
||||||
self.part = info.Part
|
|
||||||
pla = info.Placement.multiply(FreeCAD.Placement(self.offset))
|
|
||||||
self.placement = pla
|
|
||||||
if asm3.gui.AsmCmdManager.Trace and \
|
|
||||||
self.tracePoint.isEqual(pla.Base,1e5):
|
|
||||||
if not self.trace:
|
|
||||||
self.trace = FreeCAD.ActiveDocument.addObject(
|
|
||||||
'Part::Polygon','AsmTrace')
|
|
||||||
self.trace.Nodes = {-1:self.tracePoint}
|
|
||||||
self.tracePoint = pla.Base
|
|
||||||
self.trace.Nodes = {-1:pla.Base}
|
|
||||||
self.trace.recompute()
|
|
||||||
return pla
|
|
||||||
|
|
||||||
|
|
||||||
class ViewProviderAsmElementLink(ViewProviderAsmBase):
|
class ViewProviderAsmElementLink(ViewProviderAsmBase):
|
||||||
def __init__(self,vobj):
|
def doubleClicked(self,_vobj):
|
||||||
self._draggingContext = None
|
return movePart()
|
||||||
super(ViewProviderAsmElementLink,self).__init__(vobj)
|
|
||||||
|
|
||||||
def doubleClicked(self, vobj):
|
|
||||||
return vobj.Document.setEdit(vobj,1)
|
|
||||||
|
|
||||||
def onExecute(self,info):
|
|
||||||
if not getattr(self,'_draggingContext',None):
|
|
||||||
return
|
|
||||||
self.ViewObject.DraggingPlacement = self._draggingContext.update(info)
|
|
||||||
|
|
||||||
def initDraggingPlacement(self):
|
|
||||||
info = self.ViewObject.Object.Proxy.getInfo()
|
|
||||||
self._draggingContext = AsmDraggingContext(info)
|
|
||||||
return (FreeCADGui.editDocument().EditingTransform,
|
|
||||||
self._draggingContext.placement,
|
|
||||||
info.Shape.BoundBox)
|
|
||||||
|
|
||||||
def onDragStart(self):
|
|
||||||
self._draggingContext.undos = set()
|
|
||||||
|
|
||||||
def onDragMotion(self):
|
|
||||||
ctx = self._draggingContext
|
|
||||||
pla = self.ViewObject.DraggingPlacement.multiply(ctx.offsetInv)
|
|
||||||
setPlacement(ctx.part,pla,ctx.undos, 'Assembly drag')
|
|
||||||
|
|
||||||
from PySide import QtCore,QtGui
|
|
||||||
|
|
||||||
obj = self.ViewObject.Object
|
|
||||||
if QtGui.QApplication.keyboardModifiers() == QtCore.Qt.ControlModifier:
|
|
||||||
obj.getLinkedObject(False).recompute()
|
|
||||||
obj.recompute()
|
|
||||||
return
|
|
||||||
|
|
||||||
obj.Proxy.parent.solve()
|
|
||||||
return ctx.placement
|
|
||||||
|
|
||||||
def onDragEnd(self):
|
|
||||||
for doc in self._draggingContext.undos:
|
|
||||||
doc.commitTransaction()
|
|
||||||
|
|
||||||
def unsetEdit(self,_vobj,_mode):
|
|
||||||
self._draggingContext = None
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class AsmConstraint(AsmGroup):
|
class AsmConstraint(AsmGroup):
|
||||||
|
@ -636,14 +636,6 @@ class AsmConstraint(AsmGroup):
|
||||||
self.parent = getProxy(parent,AsmConstraintGroup)
|
self.parent = getProxy(parent,AsmConstraintGroup)
|
||||||
super(AsmConstraint,self).__init__()
|
super(AsmConstraint,self).__init__()
|
||||||
|
|
||||||
def solve(self, excp=False):
|
|
||||||
try:
|
|
||||||
asm3.solver.solve(self.getAssembly().Object)
|
|
||||||
except RuntimeError as e:
|
|
||||||
if excp:
|
|
||||||
raise e
|
|
||||||
logger.error(e)
|
|
||||||
|
|
||||||
def getAssembly(self):
|
def getAssembly(self):
|
||||||
return self.parent.parent
|
return self.parent.parent
|
||||||
|
|
||||||
|
@ -653,8 +645,7 @@ class AsmConstraint(AsmGroup):
|
||||||
obj = getattr(self,'Object',None)
|
obj = getattr(self,'Object',None)
|
||||||
if not obj:
|
if not obj:
|
||||||
return
|
return
|
||||||
if Constraint.isLocked(obj) or \
|
if Constraint.isDisabled(obj):
|
||||||
Constraint.isDisabled(obj):
|
|
||||||
return
|
return
|
||||||
parent = getattr(self,'parent',None)
|
parent = getattr(self,'parent',None)
|
||||||
if not parent:
|
if not parent:
|
||||||
|
@ -726,7 +717,7 @@ class AsmConstraint(AsmGroup):
|
||||||
'''
|
'''
|
||||||
sels = FreeCADGui.Selection.getSelectionEx('',False)
|
sels = FreeCADGui.Selection.getSelectionEx('',False)
|
||||||
if not sels:
|
if not sels:
|
||||||
return
|
raise RuntimeError('no selection')
|
||||||
if len(sels)>1:
|
if len(sels)>1:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
'The selections must have a common (grand)parent assembly')
|
'The selections must have a common (grand)parent assembly')
|
||||||
|
@ -737,7 +728,10 @@ class AsmConstraint(AsmGroup):
|
||||||
assembly = None
|
assembly = None
|
||||||
for sub in sel.SubElementNames:
|
for sub in sel.SubElementNames:
|
||||||
sobj = sel.Object.getSubObject(sub,1)
|
sobj = sel.Object.getSubObject(sub,1)
|
||||||
ret = Assembly.findChild(sel.Object,sub,recursive=True)
|
if not sobj:
|
||||||
|
raise RuntimeError('Cannot find sub-object "{}" of {}'.format(
|
||||||
|
sub,sel.Object))
|
||||||
|
ret = Assembly.find(sel.Object,sub,recursive=True)
|
||||||
if not ret:
|
if not ret:
|
||||||
raise RuntimeError('Selection {}.{} is not from an '
|
raise RuntimeError('Selection {}.{} is not from an '
|
||||||
'assembly'.format(sel.Object.Name,sub))
|
'assembly'.format(sel.Object.Name,sub))
|
||||||
|
@ -768,7 +762,7 @@ class AsmConstraint(AsmGroup):
|
||||||
typeid = Constraint.getTypeID(cstr)
|
typeid = Constraint.getTypeID(cstr)
|
||||||
info = cstr.Proxy.getInfo()
|
info = cstr.Proxy.getInfo()
|
||||||
check = [o.getShape() for o in info.Elements] + elements
|
check = [o.getShape() for o in info.Elements] + elements
|
||||||
elif typeid:
|
else:
|
||||||
check = elements
|
check = elements
|
||||||
if check:
|
if check:
|
||||||
Constraint.check(typeid,check)
|
Constraint.check(typeid,check)
|
||||||
|
@ -778,25 +772,39 @@ class AsmConstraint(AsmGroup):
|
||||||
Elements = elements)
|
Elements = elements)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def make(typeid, selection=None, name='Constraint'):
|
def make(typeid, selection=None, name='Constraint', undo=True):
|
||||||
if not selection:
|
if not selection:
|
||||||
selection = AsmConstraint.getSelection(typeid)
|
selection = AsmConstraint.getSelection(typeid)
|
||||||
if selection.Constraint:
|
if selection.Constraint:
|
||||||
cstr = selection.Constraint
|
cstr = selection.Constraint
|
||||||
|
if undo:
|
||||||
|
doc = cstr.Document
|
||||||
|
doc.openTransaction('Assembly change constraint')
|
||||||
else:
|
else:
|
||||||
constraints = selection.Assembly.Proxy.getConstraintGroup()
|
constraints = selection.Assembly.Proxy.getConstraintGroup()
|
||||||
|
if undo:
|
||||||
|
doc = constraints.Document
|
||||||
|
doc.openTransaction('Assembly make constraint')
|
||||||
cstr = constraints.Document.addObject("App::FeaturePython",
|
cstr = constraints.Document.addObject("App::FeaturePython",
|
||||||
name,AsmConstraint(constraints),None,True)
|
name,AsmConstraint(constraints),None,True)
|
||||||
ViewProviderAsmConstraint(cstr.ViewObject)
|
ViewProviderAsmConstraint(cstr.ViewObject)
|
||||||
constraints.setLink({-1:cstr})
|
constraints.setLink({-1:cstr})
|
||||||
Constraint.setTypeID(cstr,typeid)
|
Constraint.setTypeID(cstr,typeid)
|
||||||
|
|
||||||
|
try:
|
||||||
for e in selection.Elements:
|
for e in selection.Elements:
|
||||||
AsmElementLink.make(AsmElementLink.MakeInfo(cstr,*e))
|
AsmElementLink.make(AsmElementLink.MakeInfo(cstr,*e))
|
||||||
cstr.Proxy._initializing = False
|
cstr.Proxy._initializing = False
|
||||||
if cstr.recompute() and asm3.gui.AsmCmdManager.AutoRecompute:
|
if cstr.recompute() and asm3.gui.AsmCmdManager.AutoRecompute:
|
||||||
cstr.Proxy.solve()
|
logger.catch('solver exception when auto recompute',
|
||||||
|
asm3.solver.solve, selection.Assembly.Object, undo=undo)
|
||||||
|
if undo:
|
||||||
|
doc.commitTransaction()
|
||||||
return cstr
|
return cstr
|
||||||
|
except Exception:
|
||||||
|
if undo:
|
||||||
|
doc.abortTransaction()
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
class ViewProviderAsmConstraint(ViewProviderAsmGroup):
|
class ViewProviderAsmConstraint(ViewProviderAsmGroup):
|
||||||
|
@ -902,6 +910,7 @@ class Assembly(AsmGroup):
|
||||||
self.constraints = None
|
self.constraints = None
|
||||||
self.buildShape()
|
self.buildShape()
|
||||||
System.touch(obj)
|
System.touch(obj)
|
||||||
|
obj.ViewObject.Proxy.onExecute()
|
||||||
return False # return False to call LinkBaseExtension::execute()
|
return False # return False to call LinkBaseExtension::execute()
|
||||||
|
|
||||||
def onSolverChanged(self,setup=False):
|
def onSolverChanged(self,setup=False):
|
||||||
|
@ -1016,6 +1025,11 @@ class Assembly(AsmGroup):
|
||||||
logger.debug('skip constraint "{}" type '
|
logger.debug('skip constraint "{}" type '
|
||||||
'{}'.format(objName(o),o.Type))
|
'{}'.format(objName(o),o.Type))
|
||||||
continue
|
continue
|
||||||
|
if not System.isConstraintSupported(self.Object,
|
||||||
|
Constraint.getTypeName(o)):
|
||||||
|
logger.debug('skip unsupported constraint "{}" type '
|
||||||
|
'{}'.format(objName(o),o.Type))
|
||||||
|
continue
|
||||||
ret.append(o)
|
ret.append(o)
|
||||||
self.constraints = ret
|
self.constraints = ret
|
||||||
return self.constraints
|
return self.constraints
|
||||||
|
@ -1078,7 +1092,7 @@ class Assembly(AsmGroup):
|
||||||
Info = namedtuple('AssemblyInfo',('Assembly','Object','Subname'))
|
Info = namedtuple('AssemblyInfo',('Assembly','Object','Subname'))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def find(sels=None):
|
def getSelection(sels=None):
|
||||||
'Find all assembly objects among the current selection'
|
'Find all assembly objects among the current selection'
|
||||||
objs = set()
|
objs = set()
|
||||||
if sels is None:
|
if sels is None:
|
||||||
|
@ -1089,14 +1103,14 @@ class Assembly(AsmGroup):
|
||||||
objs.add(sel.Object)
|
objs.add(sel.Object)
|
||||||
continue
|
continue
|
||||||
for subname in sel.SubElementNames:
|
for subname in sel.SubElementNames:
|
||||||
ret = Assembly.findChild(sel.Object,subname,recursive=True)
|
ret = Assembly.find(sel.Object,subname,recursive=True)
|
||||||
if ret:
|
if ret:
|
||||||
objs.add(ret[-1].Assembly)
|
objs.add(ret[-1].Assembly)
|
||||||
return tuple(objs)
|
return tuple(objs)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def findChild(obj,subname,childType=None,
|
def find(obj,subname,childType=None,
|
||||||
recursive=False,relativeToChild=True):
|
recursive=False,relativeToChild=True,keepEmptyChild=False):
|
||||||
'''
|
'''
|
||||||
Find the immediate child of the first Assembly referenced in 'subs'
|
Find the immediate child of the first Assembly referenced in 'subs'
|
||||||
|
|
||||||
|
@ -1119,55 +1133,256 @@ class Assembly(AsmGroup):
|
||||||
'''
|
'''
|
||||||
assembly = None
|
assembly = None
|
||||||
child = None
|
child = None
|
||||||
idx = -1
|
|
||||||
if isTypeOf(obj,Assembly,True):
|
if isTypeOf(obj,Assembly,True):
|
||||||
assembly = obj
|
assembly = obj
|
||||||
subs = subname if isinstance(subname,list) else subname.split('.')
|
subs = subname if isinstance(subname,list) else subname.split('.')
|
||||||
|
i= 0
|
||||||
for i,name in enumerate(subs[:-1]):
|
for i,name in enumerate(subs[:-1]):
|
||||||
obj = obj.getSubObject(name+'.',1)
|
sobj = obj.getSubObject(name+'.',1)
|
||||||
if not obj:
|
if not sobj:
|
||||||
raise RuntimeError('Cannot find sub object {}'.format(name))
|
raise RuntimeError('Cannot find sub object {}, '
|
||||||
|
'{}'.format(objName(obj),name))
|
||||||
|
obj = sobj
|
||||||
if assembly and isTypeOf(obj,childType):
|
if assembly and isTypeOf(obj,childType):
|
||||||
child = obj
|
child = obj
|
||||||
if relativeToChild:
|
|
||||||
idx = i+1
|
|
||||||
else:
|
|
||||||
idx = i
|
|
||||||
break
|
break
|
||||||
assembly = obj if isTypeOf(obj,Assembly,True) else None
|
assembly = obj if isTypeOf(obj,Assembly,True) else None
|
||||||
|
|
||||||
if not child:
|
if not child:
|
||||||
|
if keepEmptyChild and assembly:
|
||||||
|
ret = Assembly.Info(Assembly=assembly,Object=None,Subname='')
|
||||||
|
return [ret] if recursive else ret
|
||||||
return
|
return
|
||||||
|
|
||||||
subs = subs[idx:]
|
ret = Assembly.Info(Assembly = assembly, Object = child,
|
||||||
ret = Assembly.Info(Assembly = assembly,
|
Subname = '.'.join(subs[i+1:] if relativeToChild else subs[i:]))
|
||||||
Object = child,
|
|
||||||
Subname = '.'.join(subs))
|
|
||||||
if not recursive:
|
if not recursive:
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
nret = Assembly.findChild(child,subs,childType,True)
|
nret = Assembly.find(child, subs[i+1:], childType, recursive,
|
||||||
|
relativeToChild, keepEmptyChild)
|
||||||
if nret:
|
if nret:
|
||||||
return [ret] + nret
|
return [ret] + nret
|
||||||
return [ret]
|
return [ret]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def findChildren(obj,subname,tp=None):
|
||||||
|
return Assembly.find(obj,subname,tp,True,False,True)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def findPartGroup(obj,subname='2.',recursive=False,relativeToChild=True):
|
def findPartGroup(obj,subname='2.',recursive=False,relativeToChild=True):
|
||||||
return Assembly.findChild(
|
return Assembly.find(
|
||||||
obj,subname,AsmPartGroup,recursive,relativeToChild)
|
obj,subname,AsmPartGroup,recursive,relativeToChild)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def findElementGroup(obj,subname='1.',relativeToChild=True):
|
def findElementGroup(obj,subname='1.',relativeToChild=True):
|
||||||
return Assembly.findChild(
|
return Assembly.find(
|
||||||
obj,subname,AsmElementGroup,False,relativeToChild)
|
obj,subname,AsmElementGroup,False,relativeToChild)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def findConstraintGroup(obj,subname='0.',relativeToChild=True):
|
def findConstraintGroup(obj,subname='0.',relativeToChild=True):
|
||||||
return Assembly.findChild(
|
return Assembly.find(
|
||||||
obj,subname,AsmConstraintGroup,False,relativeToChild)
|
obj,subname,AsmConstraintGroup,False,relativeToChild)
|
||||||
|
|
||||||
|
|
||||||
|
class AsmMovingPart(object):
|
||||||
|
def __init__(self,hierarchy,info):
|
||||||
|
self.objs = [h.Assembly for h in reversed(hierarchy)]
|
||||||
|
if isTypeOf(info.Parent,Assembly):
|
||||||
|
self.assembly = info.Parent.Proxy
|
||||||
|
elif isTypeOf(info.Parent,AsmPartGroup):
|
||||||
|
self.assembly = info.Parent.Proxy.parent
|
||||||
|
else:
|
||||||
|
raise RuntimeError('invalid moving part parent object {}'.format(
|
||||||
|
objName(info.Parent)))
|
||||||
|
|
||||||
|
self.parent = info.Parent
|
||||||
|
self.subname = info.SubnameRef
|
||||||
|
self.undos = None
|
||||||
|
self.part = info.Part
|
||||||
|
|
||||||
|
fixed = Constraint.getFixedTransform(self.assembly.getConstraints())
|
||||||
|
fixed = fixed.get(info.Part,None)
|
||||||
|
self.fixedTransform = fixed
|
||||||
|
if fixed and fixed.Shape:
|
||||||
|
shape = fixed.Shape
|
||||||
|
else:
|
||||||
|
shape = info.Shape
|
||||||
|
|
||||||
|
rot = utils.getElementRotation(shape)
|
||||||
|
if not rot:
|
||||||
|
# in case the shape has no normal, like a vertex, just use an empty
|
||||||
|
# rotation, which means having the same rotation has the owner part.
|
||||||
|
rot = FreeCAD.Rotation()
|
||||||
|
if not utils.isVertex(shape):
|
||||||
|
self.bbox = shape.BoundBox
|
||||||
|
else:
|
||||||
|
bbox = info.Object.ViewObject.getBoundingBox()
|
||||||
|
if bbox.isValid():
|
||||||
|
self.bbox = bbox
|
||||||
|
else:
|
||||||
|
logger.warn('empty bounding box of part {}'.format(
|
||||||
|
info.PartName))
|
||||||
|
self.bbox = FreeCAD.BoundBox(0,0,0,5,5,5)
|
||||||
|
|
||||||
|
pla = FreeCAD.Placement(utils.getElementPos(shape),rot)
|
||||||
|
|
||||||
|
self.offset = pla.copy()
|
||||||
|
self.offsetInv = pla.inverse()
|
||||||
|
self.draggerPlacement = info.Placement.multiply(pla)
|
||||||
|
self.tracePoint = self.draggerPlacement.Base
|
||||||
|
self.trace = None
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
info = getPartInfo(self.parent,self.subname)
|
||||||
|
self.part = info.Part
|
||||||
|
pla = info.Placement.multiply(FreeCAD.Placement(self.offset))
|
||||||
|
logger.trace('part move update {}: {}'.format(objName(self.parent),pla))
|
||||||
|
self.draggerPlacement = pla
|
||||||
|
if asm3.gui.AsmCmdManager.Trace and \
|
||||||
|
self.tracePoint.isEqual(pla.Base,1e5):
|
||||||
|
if not self.trace:
|
||||||
|
self.trace = FreeCAD.ActiveDocument.addObject(
|
||||||
|
'Part::Polygon','AsmTrace')
|
||||||
|
self.trace.Nodes = {-1:self.tracePoint}
|
||||||
|
self.tracePoint = pla.Base
|
||||||
|
self.trace.Nodes = {-1:pla.Base}
|
||||||
|
self.trace.recompute()
|
||||||
|
return pla
|
||||||
|
|
||||||
|
_undoName = 'Assembly move'
|
||||||
|
|
||||||
|
def move(self):
|
||||||
|
obj = self.assembly.Object
|
||||||
|
pla = obj.ViewObject.DraggingPlacement
|
||||||
|
|
||||||
|
update = True
|
||||||
|
if self.fixedTransform:
|
||||||
|
fixed = self.fixedTransform
|
||||||
|
movement = self.draggerPlacement.inverse().multiply(pla)
|
||||||
|
if not fixed.Shape:
|
||||||
|
# The moving part has completly fixed placement, so we move the
|
||||||
|
# parent assembly instead
|
||||||
|
pla = obj.Placement.multiply(movement)
|
||||||
|
setPlacement(obj,pla,self.undos,self._undoName)
|
||||||
|
update = False
|
||||||
|
else:
|
||||||
|
# fixed position, so reset translation
|
||||||
|
movement.Base = FreeCAD.Vector()
|
||||||
|
if not utils.isVertex(fixed.Shape):
|
||||||
|
yaw,_,_ = movement.Rotation.toEuler()
|
||||||
|
# when dragging with a fixed axis, we align the dragger Z
|
||||||
|
# axis with that fixed axis. So we shall only keep the yaw
|
||||||
|
# among the euler angles
|
||||||
|
movement.Rotation = FreeCAD.Rotation(yaw,0,0)
|
||||||
|
pla = self.draggerPlacement.multiply(movement)
|
||||||
|
|
||||||
|
if update:
|
||||||
|
# obtain and update the part placement
|
||||||
|
pla = pla.multiply(self.offsetInv)
|
||||||
|
setPlacement(self.part,pla,self.undos,self._undoName)
|
||||||
|
|
||||||
|
if not asm3.gui.AsmCmdManager.AutoRecompute:
|
||||||
|
# AsmCmdManager.AutoRecompute means auto re-solve the system. The
|
||||||
|
# recompute() call below is only for updating linked element and
|
||||||
|
# stuff
|
||||||
|
obj.recompute(True)
|
||||||
|
return
|
||||||
|
|
||||||
|
System.touch(obj)
|
||||||
|
|
||||||
|
# calls asm3.solver.solve(obj) and redirect all the exceptions message
|
||||||
|
# to logger only.
|
||||||
|
logger.catch('solver exception when moving part',
|
||||||
|
asm3.solver.solve,self.objs)
|
||||||
|
|
||||||
|
# self.draggerPlacement, which holds the intended dragger placement, is
|
||||||
|
# updated by the above solver call through the following chain,
|
||||||
|
# solver.solve() -> (triggers dependent objects recompute when done)
|
||||||
|
# Assembly.execute() ->
|
||||||
|
# ViewProviderAssembly.onExecute() ->
|
||||||
|
# AsmMovingPart.update()
|
||||||
|
return self.draggerPlacement
|
||||||
|
|
||||||
|
def getMovingPartInfo():
|
||||||
|
'''Extract information from current selection for part moving
|
||||||
|
|
||||||
|
It returns a tuple containing the selected assembly hierarchy (obtained from
|
||||||
|
Assembly.findChildren()), and AsmPartInfo of the selected child part object.
|
||||||
|
|
||||||
|
If there is only one selection, then the moving part will be one belong to
|
||||||
|
the deepest nested assembly object is selected hierarchy.
|
||||||
|
|
||||||
|
If there are two selections, then one selection must be a parent assembly
|
||||||
|
containing the other child object. The moving object will then be the
|
||||||
|
immediate child part object of the owner assembly. The actual selected sub
|
||||||
|
element, i.e. vertex, edge, face will determine the dragger placement
|
||||||
|
'''
|
||||||
|
|
||||||
|
sels = FreeCADGui.Selection.getSelectionEx('',False)
|
||||||
|
if not sels:
|
||||||
|
raise RuntimeError('no selection')
|
||||||
|
|
||||||
|
if len(sels)>1 or len(sels[0].SubElementNames)>2:
|
||||||
|
raise RuntimeError('too many selection')
|
||||||
|
|
||||||
|
ret = Assembly.findChildren(sels[0].Object,sels[0].SubElementNames[0])
|
||||||
|
if not ret:
|
||||||
|
raise RuntimeError('invalid selection {}, subname {}'.format(
|
||||||
|
objName(sels[0].Object),sels[0].SubElementNames[0]))
|
||||||
|
|
||||||
|
if len(sels[0].SubElementNames)==1:
|
||||||
|
info = getPartInfo(ret[-1].Assembly,ret[-1].Subname)
|
||||||
|
if not info and len(ret)>1:
|
||||||
|
info = getPartInfo(ret[-2].Assembly,ret[-2].Subname)
|
||||||
|
if not info:
|
||||||
|
return
|
||||||
|
return (ret, info)
|
||||||
|
|
||||||
|
ret2 = Assembly.findChildren(sels[0].Object,sels[0].SubElementNames[1])
|
||||||
|
if not ret2:
|
||||||
|
raise RuntimeError('invalid selection {}, subname {}'.format(
|
||||||
|
objName(sels[0].Object),sels[0].SubElementNames[1]))
|
||||||
|
|
||||||
|
if len(ret) == len(ret2):
|
||||||
|
if not ret2[-1].Object:
|
||||||
|
ret,ret2 = ret2,ret
|
||||||
|
elif len(ret) > len(ret2):
|
||||||
|
ret,ret2 = ret2,ret
|
||||||
|
|
||||||
|
assembly = ret[-1].Assembly
|
||||||
|
for r in ret2:
|
||||||
|
if assembly == r.Assembly:
|
||||||
|
return (ret2, getPartInfo(r.Assembly,r.Subname))
|
||||||
|
raise RuntimeError('not child parent selection')
|
||||||
|
|
||||||
|
def canMovePart():
|
||||||
|
return logger.catchTrace('',getMovingPartInfo) is not None
|
||||||
|
|
||||||
|
def movePart(useCenterballDragger=None):
|
||||||
|
ret = logger.catch('exception when moving part', getMovingPartInfo)
|
||||||
|
if not ret:
|
||||||
|
return False
|
||||||
|
|
||||||
|
info = ret[1]
|
||||||
|
doc = FreeCADGui.editDocument()
|
||||||
|
if doc:
|
||||||
|
doc.resetEdit()
|
||||||
|
if isTypeOf(info.Parent,AsmPartGroup):
|
||||||
|
vobj = info.Parent.Proxy.parent.Object.ViewObject
|
||||||
|
else:
|
||||||
|
vobj = info.Parent.ViewObject
|
||||||
|
if useCenterballDragger is not None:
|
||||||
|
vobj.UseCenterballDragger = useCenterballDragger
|
||||||
|
vobj.Proxy._movingPart = AsmMovingPart(*ret)
|
||||||
|
return vobj.Document.setEdit(vobj,1)
|
||||||
|
|
||||||
|
|
||||||
class ViewProviderAssembly(ViewProviderAsmGroup):
|
class ViewProviderAssembly(ViewProviderAsmGroup):
|
||||||
|
def __init__(self,vobj):
|
||||||
|
self._movingPart = None
|
||||||
|
super(ViewProviderAssembly,self).__init__(vobj)
|
||||||
|
|
||||||
def canDragObject(self,_child):
|
def canDragObject(self,_child):
|
||||||
return False
|
return False
|
||||||
|
@ -1191,3 +1406,39 @@ class ViewProviderAssembly(ViewProviderAsmGroup):
|
||||||
def getIcon(self):
|
def getIcon(self):
|
||||||
return System.getIcon(self.ViewObject.Object)
|
return System.getIcon(self.ViewObject.Object)
|
||||||
|
|
||||||
|
def doubleClicked(self, _vobj):
|
||||||
|
return movePart()
|
||||||
|
|
||||||
|
def onExecute(self):
|
||||||
|
if not getattr(self,'_movingPart',None):
|
||||||
|
return
|
||||||
|
|
||||||
|
pla = logger.catch('exception when update moving part',
|
||||||
|
self._movingPart.update)
|
||||||
|
if pla:
|
||||||
|
self.ViewObject.DraggingPlacement = pla
|
||||||
|
else:
|
||||||
|
doc = FreeCADGui.editDocument()
|
||||||
|
if doc:
|
||||||
|
doc.resetEdit()
|
||||||
|
|
||||||
|
def initDraggingPlacement(self):
|
||||||
|
if not getattr(self,'_movingPart',None):
|
||||||
|
return
|
||||||
|
return (FreeCADGui.editDocument().EditingTransform,
|
||||||
|
self._movingPart.draggerPlacement,
|
||||||
|
self._movingPart.bbox)
|
||||||
|
|
||||||
|
def onDragStart(self):
|
||||||
|
self._movingPart.undos = set()
|
||||||
|
|
||||||
|
def onDragMotion(self):
|
||||||
|
return self._movingPart.move()
|
||||||
|
|
||||||
|
def onDragEnd(self):
|
||||||
|
for doc in self._movingPart.undos:
|
||||||
|
doc.commitTransaction()
|
||||||
|
|
||||||
|
def unsetEdit(self,_vobj,_mode):
|
||||||
|
self._movingPart = None
|
||||||
|
return False
|
||||||
|
|
17
gui.py
17
gui.py
|
@ -141,26 +141,13 @@ class AsmCmdMove(AsmCmdBase):
|
||||||
_iconName = 'Assembly_Move.svg'
|
_iconName = 'Assembly_Move.svg'
|
||||||
_useCenterballDragger = True
|
_useCenterballDragger = True
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def getSelection(cls):
|
|
||||||
from asm3.assembly import isTypeOf,AsmElementLink
|
|
||||||
sels = FreeCADGui.Selection.getSelection()
|
|
||||||
if len(sels)==1 and isTypeOf(sels[0],AsmElementLink):
|
|
||||||
return sels[0].ViewObject
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def Activated(cls):
|
def Activated(cls):
|
||||||
vobj = cls.getSelection()
|
asm3.assembly.movePart(cls._useCenterballDragger)
|
||||||
if vobj:
|
|
||||||
doc = FreeCADGui.editDocument()
|
|
||||||
if doc:
|
|
||||||
doc.resetEdit()
|
|
||||||
vobj.UseCenterballDragger = cls._useCenterballDragger
|
|
||||||
vobj.doubleClicked()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def checkActive(cls):
|
def checkActive(cls):
|
||||||
cls._active = True if cls.getSelection() else False
|
cls._active = asm3.assembly.canMovePart()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def onClearSelection(cls):
|
def onClearSelection(cls):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user