Interactive/DeleteEmptyGeometryCustom.py

# (C) Copyright 2012 Autodesk, Inc.  All rights reserved.
#
# This computer source code and related instructions and comments are
# the unpublished confidential and proprietary information of
# Autodesk, Inc. and are protected under applicable copyright and
# trade secret law.  They may not be disclosed to, copied or used by
# any third party without the prior written consent of Autodesk, Inc.

#           module = __import__(custom)
#           instance = module.instantiate()
#           self.__myCustoms.append(instance)

__all__ = ['DeleteEmptyGeometryCustom', 'instantiate']

from ActionRegistry      import theActionRegistry
from ConfirmDialog       import ConfirmDialog
from MessageInterpreter  import MessageInterpreter
from MessageRegistry     import theMessageRegistry
from RTFapi              import Event, Node
from RunInUiThread       import RunInUiThread
from SceneGraphUtilities import CollectNodes, CastToOriginalType
from UserCustomization   import UserCustomBase, CustomInfo

import ModelIO

DELETE_INVALID_NODES_Doc = \
[(
"""
Remove invalid geometry nodes in a file.
"""
),
[],
]

theMessageRegistry.register("DELETE_INVALID_NODES",
    (), 0, DELETE_INVALID_NODES_Doc)

class DeleteEmptyGeometryCustom( UserCustomBase ):
    def __init__( self ):
        self.__myDeleteMenu = None
        self.__myDeleteId = None
        self.__myInterpreter = LocalInterpreter()

    def getInterpreter( self, isInteractive ):
        if isInteractive:
            return self.__myInterpreter
        return None

    def appendMenuItems( self, id, menu ):
        if "Edit" == id:
            self.__myDeleteMenu = menu
            self.__myDeleteId = menu.appendItem(_( 'Delete Invalid Nodes\tQ' ),
                                             self.__onDeleteEmptyGeometry )

            # While at it, add the hotkey...
            theActionRegistry.register( 'Global',
                                        Event.BUTTON_q, (), True,
                                        Event.BUTTONUP,
                                        'DeleteEmptyGeometry' )
            theActionRegistry.register( 'Spectating',
                                        Event.BUTTON_q, (), True,
                                        Event.BUTTONUP,
                                        'DeleteEmptyGeometry' )

    def enableMenuStates( self, id, enableStates ):
        if "Edit" == id:
            enableStates[self.__myDeleteId] = True

    # ------------------------------------------------
    def __onDeleteEmptyGeometry( self, event ):
        self.__myInterpreter.DeleteEmptyGeometry( event )

# -----------------------------------------------------------------------

class LocalInterpreter( MessageInterpreter ):

    def __init__( self ):
        MessageInterpreter.__init__( self )
        self.__myModels = None
        self.__mySilentMode = False

    def showConfirmDialog( self, removedCount ):
        win = ConfirmDialog()
        win.show( None, "Autodesk Showcase 2012",
                  _("Removed %d invalid nodes") % (removedCount),
                  (_("OK"),) )

    def APPLICATION_CLOSE_SCENE( self, message ):
        self.__myModels = None

    def SET_DOCUMENT( self, message ):
        document, = message.data
        self.__myModels = document.get( ModelIO.id )

    def DeleteEmptyGeometry( self, event ):
        if self.__myModels is None:
            return

        def isInvalid( node ):
            return not node.hasChildren() \
                and ( node.getNodeType() == 'LodGroup' or node.getNodeType() == 'Group' ) \
                and not node.hasProperty( Node.kIsUserNode ) \
                and node != self.__myModels.root.get()

        invalidNodes = CollectNodes( self.__myModels.root, [isInvalid], idsOnly=False )
        removedCount = 0
        for node in invalidNodes:
            parent = node.getSingleParent()

            if parent:
                parent = CastToOriginalType( parent )
                parent.removeChild( node )

                print "Removing %s" % ( node.getUniqueId() )
                removedCount += 1

        if not self.__mySilentMode and removedCount > 0:
            RunInUiThread( self.showConfirmDialog, removedCount )

        #self.__myModels.root.updateBoundingBoxes()

    def DELETE_INVALID_NODES( self, message ):
        self.__mySilentMode = True
        self.DeleteEmptyGeometry( None )


# --------------------------------------------------------------------

def instantiate():
    # Are the relevant files elsewhere?
    # sys.path.append( "where the files are" )
    return DeleteEmptyGeometryCustom()


def info():
    customInfo = CustomInfo()
    customInfo.vendor = 'Autodesk'
    customInfo.version = '3.0'
    customInfo.api = '2013'
    customInfo.shortInfo = "Delete empty geometry."
    customInfo.longInfo = \
"""Delete empty geometry (LodGroup or Group nodes without children) as a sample of interacting with the scene.
"""
    return customInfo