/**
* Copyright (c) 2003-2009, Xith3D Project Group all rights reserved.
*
* Portions based on the Java3D interface, Copyright by Sun Microsystems.
* Many thanks to the developers of Java3D and Sun Microsystems for their
* innovation and design.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the 'Xith3D Project Group' nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) A
* RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE
*/
package org.xith3d.scenegraph;
import java.util.BitSet;
import org.xith3d.scenegraph.traversal.DetailedTraversalCallback;
import org.xith3d.scenegraph.traversal.TraversalCallback;
/**
* Switch group nodes allow an application to choose dynamically
* amoung a number of subgraphs. The Switch node contains an ordered
* list of children and a switch value. The switch value determines
* which child or children will be rendered. The index order of children
* is only used for selecting the appropriate child or children,
* it does not specify rendering order.
*
* @author Marvin Froehlich (aka Qudus)
*/
public class Switch extends Group
{
/**
* indicates that no children are rendered
*/
public static final int CHILD_NONE = -1;
/**
* indicates that all children are rendered, switch node acts
* as an oridinary Group node.
*/
public static final int CHILD_ALL = -2;
/**
* indicates that the childMask BitSet is used to select
* the children that are rendered
*/
public static final int CHILD_MASK = -3;
private static boolean ignoreMaskForTraversal = true;
/**
* the BitSet defining the children to render
*/
private BitSet childMask;
/**
* the index of a specific child to render
*/
private int whichChild;
/**
* Sets the index of the child to render for this object. The value
* may be a non-negative integer, indicating a specific child,
* or it may be one of the following constants: CHILD_NONE,
* CHILD_ALL or CHILD_MASK.
*/
public void setWhichChild( int whichChild )
{
final int oldValue = this.whichChild;
this.whichChild = whichChild;
if ( ( getModListener() != null ) && ( whichChild != oldValue ) )
getModListener().onSwitchWhichChildChanged( this, oldValue, whichChild );
}
/**
* Gets the index of the child to render for this object.
*/
public final int getWhichChild()
{
return ( whichChild );
}
/**
* Sets the child mask for this object.
*/
public final void setChildMask( BitSet childMask )
{
//BitSet oldValue = this.childMask;
this.childMask = childMask;
/*
final int n = numChildren();
for ( int i = 0; i < n; i++ )
getChild( i ).setLive( childMask.get( i ) );
*/
/*
if (getModListener() != null)
getModListener().onSwitchMaskChanged( this, oldValue, childMask );
*/
}
/**
* Gets the child mask for this object
*/
public final BitSet getChildMask()
{
return ( childMask );
}
/**
* Gets the curently selected child. If whichChild is out
* of range or is set to CHILD_MASK, CHILD_ALL or CHILD_NONE
* then null is returned.
*/
public Node getCurrentChild()
{
if ( ( whichChild < 0 ) || ( whichChild > numChildren() - 1 ) )
return ( null );
return ( getChild( whichChild ) );
}
/**
* Tests if specified child is visible according current switch settings.
* @return true if child visible, false if not visible or child is not a member of this switch
*/
public final boolean isVisible( Node child )
{
if ( whichChild == CHILD_NONE )
return ( false );
final int idx = indexOf( child );
if ( idx < 0 )
return ( false );
if ( whichChild == CHILD_ALL )
return ( true );
if ( whichChild == CHILD_MASK )
{
if ( childMask == null )
return ( false );
return ( childMask.get( idx ) );
}
return ( idx == whichChild );
}
/**
* If this is true, all children will be traversed by the traverse() method.<br>
* If this is false, only the unmasked children will be traversed by the traverse() method.
*
* @param ignore
* @return the previous value
*/
public static boolean setIgnoreMaskForTraversal( boolean ignore )
{
final boolean b = ignoreMaskForTraversal;
ignoreMaskForTraversal = ignore;
return ( b );
}
/**
* If this is true, all children will be traversed by the traverse() method.<br>
* If this is false, only the unmasked children will be traversed by the traverse() method.
*/
public static boolean getIgnoreMaskForTraversal()
{
return ( ignoreMaskForTraversal );
}
/**
* {@inheritDoc}
*/
@Override
protected Switch newInstance()
{
boolean gib = Node.globalIgnoreBounds;
Node.globalIgnoreBounds = this.isIgnoreBounds();
Switch s = new Switch( this.whichChild, this.childMask );
Node.globalIgnoreBounds = gib;
return ( s );
}
/**
* Traverses the scenegraph from this node on.
* If this Node is a Group it will recusively run through each child.<br>
* <br>
* @see #getIgnoreMaskForTraversal()
*
* @param listener the listener is notified of any traversed Node on the way
* @return if false, the whole traversal will stop
*/
@Override
public boolean traverse( TraversalCallback listener )
{
if ( !listener.traversalOperation( this ) )
return ( false );
if ( listener.traversalCheckGroup( this ) )
{
if ( ( ignoreMaskForTraversal ) || ( whichChild == Switch.CHILD_ALL ) )
{
final int n = numChildren();
for ( int i = 0; i < n; i++ )
{
if ( !getChild( i ).traverse( listener ) )
return ( false );
}
}
else
{
switch ( whichChild )
{
case Switch.CHILD_MASK:
{
final int n = numChildren();
for ( int i = childMask.nextSetBit( 0 ); i >= 0 && i < n; i = childMask.nextSetBit( i + 1 ) )
{
if ( !getChild( i ).traverse( listener ) )
return ( false );
}
break;
}
case Switch.CHILD_NONE:
{
break;
}
default:
{
return ( getChild( whichChild ).traverse( listener ) );
}
}
}
}
return ( true );
}
/**
* Traverses the scenegraph from this node on.
* If this Node is a Group it will recusively run through every child.
*
* @param callback the listener is notified of any traversed Node on the way
*/
@Override
public boolean traverse( DetailedTraversalCallback callback )
{
if ( !callback.traversalOperationCommon( this ) )
return ( false );
if ( !callback.traversalOperation( this ) )
return ( false );
if ( callback.traversalCheckGroup( this ) )
{
if ( ( ignoreMaskForTraversal ) || ( whichChild == Switch.CHILD_ALL ) )
{
final int n = numChildren();
for ( int i = 0; i < n; i++ )
{
if ( !getChild( i ).traverse( callback ) )
return ( false );
}
}
else
{
switch ( whichChild )
{
case Switch.CHILD_MASK:
{
final int n = numChildren();
for ( int i = childMask.nextSetBit( 0 ); i >= 0 && i < n; i = childMask.nextSetBit( i + 1 ) )
{
if ( !getChild( i ).traverse( callback ) )
return ( false );
}
break;
}
case Switch.CHILD_NONE:
{
break;
}
default:
{
if ( !getChild( whichChild ).traverse( callback ) )
return ( false );
}
}
}
}
return ( callback.traversalOperationAfter( this ) && callback.traversalOperationCommonAfter( this ) );
}
/**
* Constructs a new Switch object with the specified childMask
* and the specified value for whichChild.
*/
public Switch( int whichChild, BitSet childMask )
{
super();
this.childMask = childMask;
this.whichChild = whichChild;
}
/**
* Constructs a new Switch object with an empty childMask
* and the specified value for whichChild.
*/
public Switch( int whichChild )
{
this( whichChild, new BitSet() );
}
/**
* Constructs a new Switch object with an empty childMask
* and whichChild set to CHILD_NONE.
*/
public Switch()
{
this( CHILD_NONE );
}
}