#VRML V2.0 utf8

# based on 
# http://www.web3d.org/x3d/content/examples/Basic/development/IntegerSequencerPrototype.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" "IntegerSequencerPrototype.x3d"
# META "description" "This proto, modeled after a ScalarInterpolator, generates an array of integer values based on the input fraction and keys."
# META "warning" "MFInt32 keyValue accessType is listed as initializeOnly/field, since inputOutput cannot be translated to exposedField in VRML97 scripting."
# META "authors" "Don Brutzman, Estuko Lippi, Jeff Weekley, Jane Wu, Matthew Braun"
# META "created" "20 August 2001"
# META "modified" "16 August 2008"
# META "reference" "http://www.web3d.org/technicalinfo/specifications/vrml97/part1/nodesRef.html#ScalarInterpolator"
# META "subject" "integer sequencer"
# META "identifier" "http://www.web3d.org/x3d/content/examples/Basic/development/IntegerSequencerPrototype.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] ========== ========== ==========

NavigationInfo { type [ "EXAMINE" "ANY" ] } ###  Default X3D NavigationInfo

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

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

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

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

  eventIn      MFInt32	set_keyValue    # [appinfo] Array of integer values. Must have the same number of keys as keyValues.

  field        MFInt32	keyValue [ ]    # [appinfo] Array of integer values. Must have the same number of keys as keyValues.

  eventOut     MFInt32	keyValue_changed    # [appinfo] Array of integer values. Must have the same number of keys as keyValues.

  # Regular interpolator-style output
  eventOut     SFInt32	value_changed
  # Utility methods
  eventIn      SFBool	previous
  eventIn      SFBool	next
] {
  Group {
    children [
        Switch {
          choice [
            DEF KeyHolder ScalarInterpolator {
                              key IS key
            }
          ]
        }
        DEF SequencerScript Script {
          # Regular interpolator-style input
          eventIn      SFFloat	set_fraction IS set_fraction            # [appinfo] range [0..1]

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

          field        SFNode      keyHolderNode             USE KeyHolder
          eventOut     MFFloat	key_changed IS key_changed            # [appinfo] Array sequentially increasing [0..1]. Must have the same number of keys as keyValues.

          eventIn      MFInt32	set_keyValue IS set_keyValue            # [appinfo] Array of integer values. Must have the same number of keys as keyValues.

          field        MFInt32	keyValue IS keyValue
          eventOut     MFInt32	keyValue_changed IS keyValue_changed            # [appinfo] Array of integer values. Must have the same number of keys as keyValues.

          # Regular interpolator-style output
          eventOut     SFInt32	value_changed IS value_changed
          # Utility methods
          eventIn      SFBool	previous IS previous
          eventIn      SFBool	next IS next
           ### Warning:  Initializing SFBool value not provided for field 'traceEnabled' with @accessType='initializeOnly'
field        SFBool	traceEnabled FALSE            # [appinfo] For development use only not for inclusion in specification implementations.

          # Script-specific interfaces, not needed for node definition
          field        SFFloat	previousFraction 0.0
          field        SFInt32	nextIndex 0
          field        SFBool	valid TRUE
          field        SFBool	recheckValidity FALSE
          directOutput TRUE
          # Regular interpolator-style input
          # Regular interpolator-style output
          # Utility methods
          # Script-specific interfaces, not needed for node definition
                                                                                                                       ### 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 leftToRight;

function initialize()
{
	key      = keyHolderNode.key;
	tracePrint('Initializing a new IntegerSequencer.  key.length=' + key.length + '; keyValue.length=' + keyValue.length);
	tracePrint('key =' + key);
	tracePrint('keyValue =' + keyValue);

	validityCheck();
	setHalfKeyRange();

	// assume we start at first key, going left to right
	leftToRight = true;
	previousFraction = key[0];
	nextIndex = 1;  //validityCheck ensures minimum of 2 keys exist
}

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

	if (!valid) return; //IntegerSequencer ignored

	//Bounds checking
	if (newFraction < key[0])
	{
		tracePrint('*** warning: fraction is less than first key.  fraction set to first key ***');
		newFraction = key[0];
	}
	else if (newFraction > key[key.length-1])
	{
		tracePrint('*** warning: fraction is greater than last key.  fraction set to last key ***');
		newFraction = key[key.length -1];
	}

	//Check animation direction
	if (newFraction < previousFraction && leftToRight == true)
	{
		if ((previousFraction - newFraction) > halfKeyRange) //looped around
		{
			nextIndex = 1;
		}
		else //just changed direction
		{
			leftToRight = false;
			nextIndex = nextIndex - 1;
            }
	}
	else if (newFraction > previousFraction && leftToRight == false)
	{
		if ((newFraction - previousFraction) < halfKeyRange) //looped around
		{
			nextIndex = key.length - 2;
		}
		else //just changed direction
		{
			leftToRight = true;
			nextIndex = nextIndex + 1;
            }
	}
	else if (newFraction == previousFraction)
	{ //no change, so no processing required
		return;
	}
	previousFraction = newFraction;

	if (leftToRight) // moving left to right
	{
		while (newFraction > key[nextIndex]) nextIndex++;

		if (newFraction == key[nextIndex])
			value_changed = keyValue[nextIndex];
		else	value_changed = keyValue[nextIndex -1];

		tracePrint('forward animation, fraction =' + newFraction);
		tracePrint('value_changed eventOut is:' + value_changed);
	}
	else // moving right to left
	{
		while (newFraction < key[nextIndex]) nextIndex--;

		if (newFraction == key[nextIndex])
			value_changed = keyValue[nextIndex];
		else	value_changed = keyValue[nextIndex + 1];

		tracePrint('backward animation, fraction =' + newFraction);
		tracePrint('value_changed eventOut is:' + value_changed);
	}
}

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

function set_keyValue(newKeyValue, timeStamp)
{
	keyValue = newKeyValue;
	recheckValidity = true;
}

function setHalfKeyRange()
{
	halfKeyRange = (key[key.length - 1] - key[0])/2.0;
}

function previous (value, timeStamp)
{
  if (value==true) // trigger on true events only
  {
	leftToRight = true;
	nextIndex = nextIndex - 2; // reset to previous
	if (nextIndex < 0) nextIndex = nextIndex + key.length;
	value_changed = keyValue[nextIndex];
	previousFraction = key[nextIndex];
	nextIndex++; // setup for next time, leftToRight
	if (nextIndex > key.length - 1) nextIndex = 0;
  }
}
function next (value, timeStamp)
{
  if (value==true) // trigger on true events only
  {
	leftToRight = true;
	value_changed = keyValue[nextIndex];
	previousFraction = key[nextIndex];
	nextIndex++; // setup for next time,leftToRight
	if (nextIndex > key.length - 1) nextIndex = 0;
  }
}

function validityCheck()
{
	//Check if lengths of key & keyValue arrays match
	if (key.length != keyValue.length)
	{
		forcePrint('*** error: key and keyValue arrays must be of the same length.  IntegerSequencer ignored ***');
		valid = false;
		return;
	}
	//check to ensure minimum of 2 keys have been specified
	if (key.length < 2)
	{
		forcePrint('*** error: must contain at least 2 keys.  IntegerSequencer ignored ***');
		valid = false;
		return;
	}

	//Check if key array has values in an non-decreasing order
	for (i = 1; i < key.length; i++)
	{
		tracePrint('i=' + i);

		if (key[i] < key [i-1])
		{
			forcePrint('*** error: key array values must be listed in a non-decreasing order.  IntegerSequencer ignored ***');
			valid = false;
			return;
		}
	}
	valid = true
	recheckValidity = false;
	key_changed = key;
	keyValue_changed = keyValue;
	return;
}

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

function forcePrint(outputString)
{
	print ('[IntegerSequencer]' + outputString);
}
          
" ]
        }
    ]
  }
}
# ===============Example==============
Anchor {
  description "IntegerSequencerExample"
  parameter [ "target=_blank" ]
  url [ "IntegerSequencerExample.wrl" "https://savage.nps.edu/Savage/Tools/Animation/IntegerSequencerExample.wrl" "IntegerSequencerExample.x3d" "https://savage.nps.edu/Savage/Tools/Animation/IntegerSequencerExample.x3d" ]
  children [
      Shape {
        geometry Text {
          string [ "IntegerSequencerPrototype" "defines a prototype" "" "Click text to see example scene" "IntegerSequencerExample" ]
          fontStyle FontStyle {
            justify [ "MIDDLE" "MIDDLE"  ] 
            size 0.9
          }
        }
        appearance Appearance {
          material Material {
            diffuseColor 1 1 0.2
          }
        }
      }
  ]
}
