<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE X3D PUBLIC "ISO//Web3D//DTD X3D 3.0//EN" "https://www.web3d.org/specifications/x3d-3.0.dtd">
<X3D profile='Immersive'  version='3.0 xmlns:xsd='http://www.w3.org/2001/XMLSchema-instance' xsd:noNamespaceSchemaLocation =' https://www.web3d.org/specifications/x3d-3.0.xsd ' >
<head>
<meta name='titlecontent=' BooleanSequencerPrototype.x3d '/>
<meta name='descriptioncontent='BooleanSequencer is modeled after ScalarInterpolator and generates true or false values.'/>
<meta name='creatorcontent='Don Brutzman, Estuko Lippi, Jeff Weekley, Jane Wu'/>
<meta name='createdcontent='7 August 2001'/>
<meta name='modifiedcontent='20 January 2020'/>
<meta name='referencecontent=' https://www.web3d.org/technicalinfo/specifications/vrml97/part1/nodesRef.html#ScalarInterpolator '/>
<meta name='subjectcontent='boolean sequencer'/>
<meta name='identifiercontent=' https://www.web3d.org/x3d/content/examples/Basic/development/BooleanSequencerPrototype.x3d '/>
<meta name='generatorcontent='X3D-Edit 3.3, https://savage.nps.edu/X3D-Edit'/>
<meta name='licensecontent=' ../license.html'/>
</head>
<!--

to top <!-- Event Graph ROUTE Table shows event connections -->
 
<!-- Index for DEF nodes: KeyHolder, KeyValueHolder, SequencerScript

Index for ProtoDeclare definition: BooleanSequencer
-->
<Scene>
<WorldInfo title='BooleanSequencerPrototype.x3d'/>
<ProtoDeclare name='BooleanSequencer'>
<ProtoInterface>
<field name='set_fractiontype='SFFloataccessType='inputOnly'
 appinfo='Regular interpolator-style input, typical range [0..1]' />

<field name='set_keytype='MFFloataccessType='inputOnly'/>
<field name='keytype='MFFloataccessType='inputOutput'
 appinfo='Array sequentially increasing typically [0..1]. Must have the same number of keys as keyValues.' >
<!-- NULL initialization value -->
</field>
<field name='key_changedtype='MFFloataccessType='outputOnly'/>
<field name='set_keyValuetype='MFBoolaccessType='inputOnly'/>
<field name='keyValuetype='MFBoolaccessType='inputOutput'
 appinfo='Array of true|false values. Must have the same number of keys as keyValues.' >
<!-- NULL initialization value -->
</field>
<field name='keyValue_changedtype='MFBoolaccessType='outputOnly'
 appinfo='Regular interpolator-style input' />

<field name='value_changedtype='SFBoolaccessType='outputOnly'
 appinfo='Regular interpolator-style input' />

<field name='previoustype='SFBoolaccessType='inputOnly'
 appinfo='Utility method' />

<field name='nexttype='SFBoolaccessType='inputOnly'
 appinfo='Utility method' />
</ProtoInterface>
<ProtoBody>
<Group>
<ScalarInterpolator DEF='KeyHolder'>
<IS>
<connect nodeField='keyprotoField='key'/>
</IS>
</ScalarInterpolator>
<Anchor DEF='KeyValueHolder'>
<IS>
<connect nodeField='descriptionprotoField='keyValue'/>
</IS>
</Anchor>
<Script DEF='SequencerScriptdirectOutput='true'>
<!-- Regular interpolator-style input -->
<field name='set_fractiontype='SFFloataccessType='inputOnly'
 appinfo='typical range [0..1]' />

<field name='set_keytype='MFFloataccessType='inputOnly'
 appinfo='Array sequentially increasing typically [0..1]. Must have the same number of keys as keyValues.' />

<field name='keyHolderNodetype='SFNodeaccessType='initializeOnly'>
<ScalarInterpolator USE=' KeyHolder'/>
</field>
<field name='key_changedtype='MFFloataccessType='outputOnly'/>
<field name='set_keyValuetype='MFBoolaccessType='inputOnly'
 appinfo='Array of true or false values. Must have the same number of keys as keyValues.' />

<field name='keyValueHolderNodetype='SFNodeaccessType='initializeOnly'>
<Anchor USE=' KeyValueHolder'/>
</field>
<field name='keyValue_changedtype='MFBoolaccessType='outputOnly'/>
<!-- Regular interpolator-style output -->
<field name='value_changedtype='SFBoolaccessType='outputOnly'/>
<!-- Utility methods -->
<field name='previoustype='SFBoolaccessType='inputOnly'/>
<field name='nexttype='SFBoolaccessType='inputOnly'/>
<!-- Script-specific interfaces, not needed for node definition -->
<field name='traceEnabledtype='SFBoolvalue='falseaccessType='initializeOnly'/>
<field name='keyValueArraytype='MFInt32accessType='initializeOnly'>
<!-- NULL initialization value -->
</field>
<field name='previousFractiontype='SFFloatvalue='0.0accessType='initializeOnly'/>
<field name='nextIndextype='SFInt32value='0accessType='initializeOnly'/>
<field name='validtype='SFBoolvalue='trueaccessType='outputOnly'/>
<field name='recheckValiditytype='SFBoolvalue='falseaccessType='initializeOnly'/>
<field name='forwardtype='SFBoolvalue='trueaccessType='initializeOnly'
 appinfo='leftToRight' />

<field name='keytype='MFFloataccessType='inputOnly'>
<!-- NULL initialization value -->
</field>
<field name='keyValuetype='MFInt32accessType='inputOutput'>
<!-- NULL initialization value -->
</field>
<IS>
<connect nodeField='set_fractionprotoField='set_fraction'/>
<connect nodeField='set_keyprotoField='set_key'/>
<connect nodeField='key_changedprotoField='key_changed'/>
<connect nodeField='set_keyValueprotoField='set_keyValue'/>
<connect nodeField='keyValue_changedprotoField='keyValue_changed'/>
<connect nodeField='value_changedprotoField='value_changed'/>
<connect nodeField='previousprotoField='previous'/>
<connect nodeField='nextprotoField='next'/>
</IS>
<![CDATA[
            
ecmascript:

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)
		{
			alwaysPrint ('*** 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)
		{
			alwaysPrint ('*** 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)
	{
		alwaysPrint('*** warning: fraction is less than 0.  fraction reset to 0 ***');
		value = 0;
	}
	else if (value > 1)
	{
		alwaysPrint('*** 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)
	{
		alwaysPrint('*** 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)
	{
		alwaysPrint('*** 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)
		{
			alwaysPrint('*** error: key[' + i + '] value is NOT in the range of [0..1].  BooleanSequencer ignored ***');
			valid = false;
			return;
		}

		if (key[i] <= key [i-1])
		{
			alwaysPrint('*** 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) Browser.println ('[ BooleanSequencer ]' + outputString);
}

function alwaysPrint(outputString)
{
	Browser.println ('[ BooleanSequencer ]' + outputString);
}

          
]]>
</Script>
</Group>
</ProtoBody>
</ProtoDeclare>
<!-- ===============Example============== -->
<Anchor description='BooleanSequencerExampleparameter='"target=_blank"'
  url=' BooleanSequencerExample.x3d"https://www.web3d.org/x3d/content/examples/Basic/development/BooleanSequencerExample.x3d" "BooleanSequencerExample.wrl" "https://www.web3d.org/x3d/content/examples/Basic/development/BooleanSequencerExample.wrl" ' >
<Shape>
<Text string='"BooleanSequencerPrototype" "defines a prototype" "" "Click on this text to see" "BooleanSequencerExample" " scene"'>
<FontStyle justify='"MIDDLE" "MIDDLE"'/>
</Text>
<Appearance>
<Material diffuseColor='1 1 0.2'/>
</Appearance>
</Shape>
</Anchor>
</Scene>
</X3D>
<!--

to top <!-- Event Graph ROUTE Table shows event connections -->
 
<!-- Index for DEF nodes: KeyHolder, KeyValueHolder, SequencerScript

Index for ProtoDeclare definition: BooleanSequencer
-->

Event Graph ROUTE Table with 0 ROUTE total, showing X3D event-model relationships for this scene.

Each row shows an event cascade that may occur during a single timestamp interval between frame renderings, as part of the X3D execution model.

SequencerScript
Script
No ROUTE connection found for output from this node.
Contains SFNode field with indirect access to another node. 


-->

<!-- Online at
https://www.web3d.org/x3d/content/examples/Basic/development/BooleanSequencerPrototypeIndex.html -->
<!-- Version control at
https://sourceforge.net/p/x3d/code/HEAD/tree/www.web3d.org/x3d/content/examples/Basic/development/BooleanSequencerPrototype.x3d -->

<!-- Color legend: X3D terminology <X3dNode DEF='idName' field='value'/> matches XML terminology <XmlElement DEF='idName' attribute='value'/>
(Light-blue background: event-based behavior node or statement) (Grey background inside box: inserted documentation) (Magenta background: X3D Extensibility)
    <ProtoDeclare name='ProtoName'> <field name='fieldName'/> </ProtoDeclare> -->

to top <!-- For additional help information about X3D scenes, please see X3D Tooltips, X3D Resources, and X3D Scene Authoring Hints. -->