#VRML V2.0 utf8

# based on 
# http://www.web3d.org/x3d/content/examples/development/BooleanSequencerPrototype.wrl

# X3D-to-VRML-97 XSL translation autogenerated by X3dToVrml97.xslt
# http://www.web3d.org/x3d/content/X3dToVrml97.xslt
# Generated using XSLT processor: SAXON 8.7 from Saxonica

# [X3D] VRML V3.0 utf8
# PROFILE Immersive
# [X3D] version=3.0
# [X3D] noNamespaceSchemaLocation=http://www.web3d.org/specifications/x3d-3.0.xsd
# [head]

# META "title" "BooleanSequencerPrototype.x3d"
# META "description" "BooleanSequencer is modeled after ScalarInterpolator and generates true or false values."
# META "creator" "Don Brutzman, Estuko Lippi, Jeff Weekley, Jane Wu"
# META "created" "7 August 2001"
# META "modified" "16 August 2008"
# META "reference" "http://www.web3d.org/technicalinfo/specifications/vrml97/part1/nodesRef.html#ScalarInterpolator"
# META "subject" "boolean sequencer"
# META "identifier" "http://www.web3d.org/x3d/content/examples/Basic/development/BooleanSequencerPrototype.x3d"
# META "generator" "X3D-Edit 3.2, https://savage.nps.edu/X3D-Edit"
# META "license" "../license.html"

# <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
# 
# <html>
# <head>
# 			<title>NPS MOVES Software License</title>
# 			<meta name="description" content="NPS MOVES Software License" />
# 			<meta name="author" content="Donald P. Brutzman" />
# 			<meta name="created" content="4 March 2005" />
# 			<meta name="revised" content="17 December 2006" />
# 			<meta name="reference" content="http://www.oreilly.com/catalog/osfreesoft" />
# 			<meta name="reference" content="http://opensource.org/licenses/bsd-license.php" />
# 			<meta name="reference" content="http://xchat.movesinstitute.org/bugzilla/show_bug.cgi?id=32" />
# 			<link rel="icon" href="http://www.web3D.org/x3d/content/examples/images/X3DtextIcon16.png" title="X3D" type="image/png"/>
# </head>
# <body>
# <p>Copyright (c) 1995-2008 held by the author(s).  All rights reserved.</p>
# 
# <p>Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:</p>
# 
# <ul>
# 		<li>
#       Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#     </li>  
# 		<li>	
# 			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.
# 		</li>	
# 		<li>	
#       Neither the names of the 
# 			<a href="http://www.nps.edu">Naval Postgraduate School (NPS)</a>
#       <a href="http://www.MovesInstitute.org">Modeling Virtual Environments and Simulation (MOVES) Institute</a>
#       nor the names of its contributors may be used to endorse or
#       promote products derived from this software without specific
#       prior written permission.
#     </li>
# </ul>			
# 
# <p>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) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.</p>
# 
# </body>
# </html>


# [Scene] ========== ========== ==========

PROTO BooleanSequencer [
  # Regular interpolator-style input
  eventIn      SFFloat	set_fraction    # [appinfo] typical range [0..1]

#  eventIn      MFFloat	set_key
  exposedField MFFloat	key [ ]    # [appinfo] Array sequentially increasing typically [0..1]. Must have the same number of keys as keyValues.

    # NULL initialization value

  eventOut     MFFloat	key_changed
#  eventIn      SFString	set_keyValue # type MFBool 
  exposedField SFString	keyValue "" # type MFBool     # [appinfo] Array of true|false values. Must have the same number of keys as keyValues.

    # NULL initialization value

  eventOut     SFString	keyValue_changed # type MFBool 
  # Regular interpolator-style output
  eventOut     SFBool	value_changed
  # Utility methods
  eventIn      SFBool	previous
  eventIn      SFBool	next
] {
  Group {
    children [
        DEF KeyHolder ScalarInterpolator {
                      key IS key
        }
        DEF KeyValueHolder Anchor {
                      description IS keyValue
        }
        DEF SequencerScript Script {
          # Regular interpolator-style input
          eventIn      SFFloat	set_fraction IS set_fraction            # [appinfo] typical range [0..1]

#          eventIn      MFFloat	set_key IS set_key            # [appinfo] Array sequentially increasing typically [0..1]. Must have the same number of keys as keyValues.

          field        SFNode      keyHolderNode             USE KeyHolder
          eventOut     MFFloat	key_changed IS key_changed
#          eventIn      SFString	set_keyValue IS set_keyValue # type MFBool             # [appinfo] Array of true or false values. Must have the same number of keys as keyValues.

          field        SFNode      keyValueHolderNode             USE KeyValueHolder
          eventOut     SFString	keyValue_changed IS keyValue_changed # type MFBool 
          # Regular interpolator-style output
          eventOut     SFBool	value_changed IS value_changed
          # Utility methods
          eventIn      SFBool	previous IS previous
          eventIn      SFBool	next IS next
          # Script-specific interfaces, not needed for node definition
          field        SFBool	traceEnabled FALSE
          field        MFInt32	keyValueArray [ ]
            # NULL initialization value

          field        SFFloat	previousFraction 0.0
          field        SFInt32	nextIndex 0
          field        SFBool	valid TRUE
          field        SFBool	recheckValidity FALSE
          field        SFBool	forward TRUE            # [appinfo] leftToRight

          eventIn      MFFloat	key
            # NULL initialization value

          field        MFInt32	keyValue [ ]
            # NULL initialization value

          directOutput TRUE
          # Regular interpolator-style input
          # Regular interpolator-style output
          # Utility methods
          # Script-specific interfaces, not needed for node definition
                                                                                                           ### Warning:  inputOnly field 'key' has no input-event method definition 'function key (MFFloatValue, timestamp) { }' in contained ecmascript: code
 ### Warning:  'var' declarations of variables are not persistent in contained ecmascript: code, values are lost after each call. Use <field> definitions instead.

	url [ "javascript:
// ### X3D Browser.print() not supported by all VRML97 viewers, instead simply use print()


var key, keyValue;

function initialize()
{
	key      = keyHolderNode.key;
	keyValue = keyValueHolderNode.description;
	tracePrint('key =' + key);
	tracePrint('keyValue =' + keyValue);
	keyValueToKeyValueArray ();
	tracePrint('keyValueArray =' + keyValueArray);

	forward = true;
	tracePrint('Initializing a new BooleanSequencer.  key.length=' + key.length + '; keyValueArray.length=' + keyValueArray.length);
	validityCheck();
}

function keyValueToKeyValueArray ()
{
	tracePrint('keyValueToKeyValueArray starting');
	index = 0;
	complete = false;
	nextString = keyValue.toLowerCase();
	tracePrint('initial nextString=' + nextString);
	tokenCount=0;
	while ((complete != true) && (nextString.length > 0))
	{
		tracePrint('nextString=' + nextString);
		while ((nextString.substring(0,1) == ' ') || (nextString.substring(0,1) == ','))
		       nextString = nextString.slice(1);
		tracePrint('deblanked nextString=' + nextString);
		if (nextString.length == 0)
		{
			tracePrint ('nextString.length == 0');
			complete = true;
		}
		if (nextString.length < 4)
		{
			forcePrint ('*** illegal keyValue input=' + nextString);
			valid = false;
			complete = true;
		}
		else if (nextString.substring(0,4) =='true')
		{
			keyValueArray[keyValueArray.length] = 1; // append
			newString = nextString.slice(4);
			nextString = newString;
			tokenCount++;
			tracePrint('found true, nextString=' + nextString + ', tokenCount=' + tokenCount);
		}
		else if (nextString.length < 5)
		{
			forcePrint ('*** illegal keyValue input=' + nextString);
			valid = false;
			complete = true;
		}
		else if (nextString.substring(0,5) =='false')
		{
			keyValueArray[keyValueArray.length] = 0; // append
			newString = nextString.slice(5);
			nextString = newString;
			tokenCount++;
			tracePrint('found false, nextString=' + nextString + ', tokenCount=' + tokenCount);
		}
		tracePrint('  intermediate keyValueArray=' + keyValueArray);
	}
	tracePrint('keyValueToKeyValueArray complete');
}

function set_fraction(value, timeStamp)
{
	if (recheckValidity) validityCheck();

	if (!valid) return; //BooleanSequencer ignored

	tracePrint('fraction =' + value);
	//Bounds checking
	if (value < 0)
	{
		forcePrint('*** warning: fraction is less than 0.  fraction reset to 0 ***');
		value = 0;
	}
	else if (value > 1)
	{
		forcePrint('*** warning: fraction is greater than 1.  fraction reset to 1 ***');
		value = 1;
	}

	//Check animation direction
	if (value < previousFraction && forward == true)
	{
		forward = false;
		nextIndex = nextIndex - 1;
		tracePrint('Animate backward');
	}
	else if (value > previousFraction && forward == false)
	{
		forward = true;
		//nextIndex = 0;
		tracePrint('Animate forward');
	}

	previousFraction = value;

	if (forward == true)
	{
		for (i = nextIndex; i < key.length; i++)
		{
			if (value < key[i])
				return;

			nextIndex = i + 1;
			tracePrint('nextIndex =' + nextIndex);
			if (nextIndex < key.length)
			{
				if (value <= key[nextIndex])
				{
					//Fire event
					if (keyValueArray[nextIndex-1] == 0)
						value_changed = false;
					else
						value_changed = true;
					tracePrint('value_changed eventOut is:' + value_changed);
				}
			}
			else if (nextIndex == key.length)
			{
				//Fire event
				if (keyValueArray[nextIndex-1] == 0)
					value_changed = false;
				else
					value_changed = true;
				tracePrint('value_changed eventOut is:' + value_changed);
			}
			else //nextIndex > key.length
			{
				//nextIndex = 0;
				break;
			}
		}
	}
	else //backward
	{
		for (i = nextIndex; i > 0; i--)
		{
			if (value >= key[i])
				return;

			nextIndex = i - 1;
			tracePrint('nextIndex =' + nextIndex);
			if (nextIndex >= 0)
			{
				if (value >= key[nextIndex])
				{
					//Fire event
					if (keyValueArray[nextIndex] == 0)
						value_changed = false;
					else
						value_changed = true;
					tracePrint('value_changed eventOut is:' + value_changed);
				}
			}
			else //nextIndex < 0
			{
				//nextIndex = key.length;
				break;
			}
		}
	}
}

function set_key(value, timeStamp)
{
	key = value;
	keyHolderNode.key  = key;
	recheckValidity = true;
}

function set_keyValue(value, timeStamp)
{
	keyValue = value;
	keyValueHolderNode.description = keyValue;
	recheckValidity = true;
	keyValueToKeyValueArray ();
	keyValue_changed = keyValue;
}

function validityCheck()
{
	//Check if key & keyValueArray array length matches
	if (key.length != keyValueArray.length)
	{
		forcePrint('*** error: key and keyValue arrays must be of the same length.  BooleanSequencer ignored ***');
		valid = false;
		return;
	}

	//Check if key array has values in the range of [0..1] in an increasing order
	if (key[0] < 0 || key[0] > 1)
	{
		forcePrint('*** error: key[0] value is NOT in the range of [0..1].  BooleanSequencer ignored ***');
		valid = false;
		return;
	}
	for (i = 1; i < key.length; i++)
	{
		if (key[i] < 0 || key[i] > 1)
		{
			forcePrint('*** error: key[' + i + '] value is NOT in the range of [0..1].  BooleanSequencer ignored ***');
			valid = false;
			return;
		}

		if (key[i] <= key [i-1])
		{
			forcePrint('*** error: values for key[] array must be listed in an increasing order.  BooleanSequencer ignored ***');
			valid = false;
			return;
		}
	}
	recheckValidity = false;
	key_changed = key;
	return;
}
function previous (SFBoolValue, timestamp)
{
	nextIndex = nextIndex - 1;
	if (nextIndex == 0) nextIndex = key.length - 1;
}
function next (SFBoolValue, timestamp)
{
	nextIndex = nextIndex + 1;
	if (nextIndex == key.length) nextIndex = 0;
}

function tracePrint(outputString)
{
	if (traceEnabled) print ('[ BooleanSequencer ]' + outputString);
}

function forcePrint(outputString)
{
	print ('[ BooleanSequencer ]' + outputString);
}
          
" ]
        }
    ]
  }
}
