[osg-users] CompositeNodeCallback : useful addition?

Jean-Sébastien Guay jean-sebastien.guay at polymtl.ca
Fri Oct 26 06:40:58 PDT 2007


Hello Robert,

I was wondering about something. I find the nesting of node callbacks  
a bit less intuitive and transparent than it could be, and I was  
wondering if there would be interest in a small class I made called  
CompositeNodeCallback. It's simply a vector of NodeCallbacks where the  
class's operator() calls the operator() of each NodeCallback in the  
vector. So in effect it implements the Composite and Decorator  
patterns for NodeCallbacks.

The other useful thing is that it has some static methods that allow  
you to add a NodeCallback to either the update, event or cull  
callbacks on a node, and it will transparently add a  
CompositeNodeCallback if needed, or add the callback to an existing  
CompositeNodeCallback if there is one. So in effect, you just replace  
any

node->setUpdateCallback(callback);

with

CompositeNodeCallback::addUpdateCallback(node, callback);

and it takes care of everything for you.

I know I find it useful, because I have been bitten a few times when  
setting a callback on a node that already had one, so it was replaced.  
And I find having to manage nested callbacks manually is tedious. So I  
appreciate this being automatic.

My question is, would this be useful in the OSG core. I'm including  
the code in case you want to see exactly what it does. If you decide  
it could be included, I'll gladly make the changes needed (osg  
namespace, comment copyright header, etc.) to make it a valid  
submission.

Thanks,

J-S
-- 
______________________________________________________
Jean-Sebastien Guay     jean-sebastien.guay at polymtl.ca
                         http://whitestar02.webhop.org/

----------------------------------------------------------------
This message was sent using IMP, the Internet Messaging Program.

-------------- next part --------------
// -*-c++-*- Copyright (c) 2007 Jean-S?stien Guay

#ifndef __COMPOSITE_NODE_CALLBACK_H__
#define __COMPOSITE_NODE_CALLBACK_H__ 1

#include <vector>

#include <osg/Node>
#include <osg/NodeCallback>

/** CompositeNodeCallback is a NodeCallback decorator to be able to attach 
 *  more than one callback to a node (either update, event or cull callbacks).
 *  It has static convenience methods to add a composite automatically if the
 *  given node already has a callback attached to it, instead of replacing the
 *  callback (which was the previous behavior if you didn't manage nested 
 *  callbacks correctly). 
 *  @author Jean-S?stien Guay <jean_seb at videotron.ca>
 */
class CompositeNodeCallback : public osg::NodeCallback
{
    public:
        // Interface to be able to manage the callbacks contained in the composite.

        /** Adds a callback to the group. */
        virtual bool addCallback(osg::NodeCallback* callback);
        /** Inserts a callback into the group at the specified position. */
        virtual bool insertCallback(unsigned int index, osg::NodeCallback* callback);
        /** Removes the callback from the group. */
        bool removeCallback(const osg::NodeCallback* callback);
        /** Removes the callback at the specified index. */
        bool removeCallback(unsigned int pos, unsigned int num = 1);
        /** Removes the num samples starting at the specified index. */
        virtual bool removeCallbacks(unsigned int pos, unsigned int num);

        /** Replaces the original callback with the new one if the original 
            callback was in the group. */
        virtual bool replaceCallback(const osg::NodeCallback* origCallback, osg::NodeCallback* newCallback);

        /** Sets the callback at the specified index. */
        virtual bool setCallback(unsigned int i, osg::NodeCallback* callback);

        /** Returns the number of samples in this group. */
        unsigned int getNumCallbacks() const;
        /** Get the index number of child, return a value between 0 and 
            getNumCallbacks()-1 if found, if not found then return 
            getNumCallbacks(). */
        unsigned int getCallbackIndex(const osg::NodeCallback* callback) const;
        /** Get the callback at the specified index. */
        const osg::NodeCallback* getCallback(unsigned int index) const;
        /** Get the callback at the specified index. */
        osg::NodeCallback* getCallback(unsigned int index);
        /** Returns true if the specified callback exists in the group. */
        bool containsCallback(const osg::NodeCallback* callback) const;


        // Implementation of the NodeCallback interface.

 	    /** Callback method called by the NodeVisitor when visiting a node. */
        virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);


        // Static methods.

        /** Static helper method that will add the callback to a node, with a 
            CompositeNodeCallback decorator if necessary (i.e. there is already
            a callback on the specified node). */
        static void addUpdateCallback(osg::Node* node, osg::NodeCallback* callback);
        /** Static helper method that will add the callback to a node, with a 
            CompositeNodeCallback decorator if necessary (i.e. there is already
            a callback on the specified node). */
        static void addEventCallback(osg::Node* node, osg::NodeCallback* callback);
        /** Static helper method that will add the callback to a node, with a 
            CompositeNodeCallback decorator if necessary (i.e. there is already
            a callback on the specified node). */
        static void addCullCallback(osg::Node* node, osg::NodeCallback* callback);

    protected:
        typedef std::vector<osg::ref_ptr<osg::NodeCallback> > CallbackVector;

        CallbackVector _callbacks;

        // Convenience methods
        enum CALLBACK_TYPE
        {
            CT_UPDATE = 0,
            CT_EVENT,
            CT_CULL
        };

        static void addCallback(osg::Node* node, const CALLBACK_TYPE type, osg::NodeCallback* callback);

        static osg::NodeCallback* getCallback(osg::Node* node, const CALLBACK_TYPE type);
        static const osg::NodeCallback* getCallback(const osg::Node* node, const CALLBACK_TYPE type);
        static void setCallback(osg::Node* node, const CALLBACK_TYPE type, osg::NodeCallback* callback);
};

#endif
-------------- next part --------------
// -*-c++-*- Copyright (c) 2007 Jean-S?stien Guay
#include "CompositeNodeCallback.h"

#include <cassert>


bool CompositeNodeCallback::addCallback(osg::NodeCallback* callback)
{
    return CompositeNodeCallback::insertCallback(_callbacks.size(), callback);
}

bool CompositeNodeCallback::insertCallback(unsigned int index, osg::NodeCallback* callback)
{
    if (!callback) return false;

    if (containsCallback(callback)) return false;

    if (index >= _callbacks.size()) _callbacks.push_back(callback);
    else _callbacks.insert(_callbacks.begin() + index, callback);

    return true;
}

bool CompositeNodeCallback::removeCallback(const osg::NodeCallback* callback)
{
    return removeCallbacks(getCallbackIndex(callback), 1);
}

bool CompositeNodeCallback::removeCallback(unsigned int pos, unsigned int num)
{
    return removeCallbacks(pos, num);
}

bool CompositeNodeCallback::removeCallbacks(unsigned int pos, unsigned int num)
{
    if (pos < _callbacks.size() && num > 0)
    {
        unsigned int end = pos + num;
        if (end > _callbacks.size()) end = _callbacks.size();

        for (unsigned int i = pos; i < end; ++i)
        {
            // Do any operation on the callback before it's removed.
        }

        _callbacks.erase(_callbacks.begin() + pos, _callbacks.begin() + end);

        return true;
    }
    return false;
}


bool CompositeNodeCallback::replaceCallback(const osg::NodeCallback* origCallback, osg::NodeCallback* newCallback)
{
    if (!newCallback || origCallback == newCallback) return false;

    unsigned int pos = getCallbackIndex(origCallback);
    if (pos < _callbacks.size())
    {
        return setCallback(pos, newCallback);
    }
    return false;
}


bool CompositeNodeCallback::setCallback(unsigned int i, osg::NodeCallback* callback)
{
    if (i < _callbacks.size() && callback)
    {
        // Do any operation on the _callbacks[i] before it's replaced.

        _callbacks[i] = callback;

        return true;
    }
    return false;
}


unsigned int CompositeNodeCallback::getNumCallbacks() const
{
    return _callbacks.size();
}

unsigned int CompositeNodeCallback::getCallbackIndex(const osg::NodeCallback* callback) const
{
    for (unsigned int i = 0; i < _callbacks.size(); ++i)
        if (_callbacks[i] == callback) return i;

    return _callbacks.size();
}

const osg::NodeCallback* CompositeNodeCallback::getCallback(unsigned int index) const
{
    return _callbacks[index].get();
}

osg::NodeCallback* CompositeNodeCallback::getCallback(unsigned int index)
{
    return _callbacks[index].get();
}

bool CompositeNodeCallback::containsCallback(const osg::NodeCallback* callback) const
{
    for (CallbackVector::const_iterator it = _callbacks.begin();
         it != _callbacks.end(); ++it)
    {
        if (*it == callback) return true;
    }

    return false;
}

void CompositeNodeCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
{
    for (CallbackVector::iterator it = _callbacks.begin();
         it != _callbacks.end();
         ++it)
    {
        // Call (*it)->operator()(node, nv) on each callback.
        (**it)(node, nv);
    }
}

void CompositeNodeCallback::addUpdateCallback(osg::Node* node, 
                                              osg::NodeCallback* callback)
{
    CompositeNodeCallback::addCallback(node, CT_UPDATE, callback);
}

void CompositeNodeCallback::addEventCallback(osg::Node* node, 
                                             osg::NodeCallback* callback)
{
    CompositeNodeCallback::addCallback(node, CT_EVENT, callback);
}

void CompositeNodeCallback::addCullCallback(osg::Node* node, 
                                            osg::NodeCallback* callback)
{
    CompositeNodeCallback::addCallback(node, CT_CULL, callback);
}


void CompositeNodeCallback::addCallback(osg::Node* node, 
                                        const CompositeNodeCallback::CALLBACK_TYPE type, 
                                        osg::NodeCallback* callback)
{
    // Possible cases:
    // 1. There is no callback on the node.
    //    ==> Add the new callback.
    // 2. There is already a callback on the node.
    //    2a. It is already a CompositeNodeCallback.
    //        ==> Add the new callback to the existing composite.
    //    2b. It is not a CompositeNodeCallback.
    //        ==> Create a new composite.
    //            Add the old callback and the new one to the composite.
    //            Add the composite onto the node.

    // Check if there's already a callback on the node.
    osg::NodeCallback* previousCallback = 
        CompositeNodeCallback::getCallback(node, type);
    if (previousCallback)
    {
        // If there is, check if it's a CompositeNodeCallback.
        CompositeNodeCallback* previousComposite = 
            dynamic_cast<CompositeNodeCallback*>(previousCallback);
        if (previousComposite)
        {
            // If it is, add the callback to the composite.
            previousComposite->addCallback(callback);
        }
        else
        {
            // If it isn't, create a composite, add both callbacks to it
            // and set it on the node.
            CompositeNodeCallback* composite = new CompositeNodeCallback;
            composite->addCallback(previousCallback);
            composite->addCallback(callback);
            CompositeNodeCallback::setCallback(node, type, composite);
        }
    }
    else
        // If there isn't, add the callback to the node.
        CompositeNodeCallback::setCallback(node, type, callback);
}


osg::NodeCallback* CompositeNodeCallback::getCallback(
                       osg::Node* node, 
                       const CompositeNodeCallback::CALLBACK_TYPE type)
{
    switch (type)
    {
        case CT_UPDATE:
            return node->getUpdateCallback();
            break;
        case CT_EVENT:
            return node->getEventCallback();
            break;
        case CT_CULL:
            return node->getCullCallback();
            break;
        default:
            assert(false && "Invalid callback type");
            return 0;
    }
}

const osg::NodeCallback* CompositeNodeCallback::getCallback(
                             const osg::Node* node, 
                             const CompositeNodeCallback::CALLBACK_TYPE type)
{
    switch (type)
    {
        case CT_UPDATE:
            return node->getUpdateCallback();
            break;
        case CT_EVENT:
            return node->getEventCallback();
            break;
        case CT_CULL:
            return node->getCullCallback();
            break;
        default:
            assert(false && "Invalid callback type");
            return 0;
    }
}

void CompositeNodeCallback::setCallback(
         osg::Node* node, 
         const CompositeNodeCallback::CALLBACK_TYPE type, 
         osg::NodeCallback* callback)
{
    switch (type)
    {
        case CT_UPDATE:
            node->setUpdateCallback(callback);
            break;
        case CT_EVENT:
            node->setEventCallback(callback);
            break;
        case CT_CULL:
            node->setCullCallback(callback);
            break;
        default:
            assert(false && "Invalid callback type");
    }
}


More information about the osg-users mailing list