<?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='PositionInterpolator2dPrototype.x3d'/>
<meta name='descriptioncontent='PositionInterpolator2D prototype declaration to pairwise interpolate across an array of Vector2Float/SFVec2f values to produce a single Vector2Float value - click text to see example.'/>
<meta name='creatorcontent='Don Brutzman, Jeff Weekley, Jane Wu'/>
<meta name='createdcontent='16 October 2001'/>
<meta name='modifiedcontent='20 January 2020'/>
<meta name='referencecontent=' https://www.web3d.org/technicalinfo/specifications/vrml97/part1/concepts.html#4.6.8 '/>
<meta name='referencecontent=' https://www.web3d.org/technicalinfo/specifications/vrml97/part1/nodesRef.html#CoordinateInterpolator '/>
<meta name='subjectcontent='PositionInterpolator2D'/>
<meta name='identifiercontent=' https://www.web3d.org/x3d/content/examples/Basic/development/PositionInterpolator2dPrototype.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: InterpolationScript, KeyHolder, KeyValueHolder, SilenceWarnings

Index for ProtoDeclare definition: PositionInterpolator2D
-->
<Scene>
<WorldInfo title='PositionInterpolator2dPrototype.x3d'/>
<ProtoDeclare name='PositionInterpolator2Dappinfo='Provide interpolation capability for Vector2Float/SFVec2f values'  documentation=' https://www.web3d.org/technicalinfo/specifications/vrml97/part1/concepts.html#4.6.8 ' >
<ProtoInterface>
<field name='set_fractiontype='SFFloataccessType='inputOnly'
 appinfo='The set_fraction eventIn receives an SFFloat event and causes the interpolator function to evaluate resulting in a value_changed eventOut with the same timestamp as the set_fraction event.' >
<!-- Regular interpolator-style input -->
</field>
<field name='set_keytype='MFFloataccessType='inputOnly'/>
<field name='keytype='MFFloataccessType='inputOutput'
 appinfo='keyValue holds the array of Vector2FloatArrays that match each animation key.' >
<!-- initial value is [] null array -->
</field>
<field name='key_changedtype='MFFloataccessType='outputOnly'
 appinfo='Array sequentially increasing typically [0..1]. Must have the same number of keys as keyValues.' />

<field name='set_keyValuetype='MFVec2faccessType='inputOnly'
 appinfo='Array of integer values. Must have the same number of keys as keyValues.' />

<field name='keyValuetype='MFVec2faccessType='inputOutput'
 appinfo='keyValue holds the array of Vector2Float values that match each animation key.' >
<!-- initial value is [] null array -->
</field>
<field name='keyValue_changedtype='MFVec2faccessType='outputOnly'
 appinfo='Array of integer values. Must have the same number of keys as keyValues.' />

<field name='value_changedtype='SFVec2faccessType='outputOnly'
 appinfo='The interpolator function averages between respective keyValue Vector2Floats resulting in a Vector2Float value_changed eventOut with the same timestamp as the set_fraction event.' >
<!-- Regular interpolator-style output -->
</field>
</ProtoInterface>
<ProtoBody>
<Group>
<Switch whichChoice='-1'>
<ScalarInterpolator DEF='KeyHolder'>
<IS>
<connect nodeField='keyprotoField='key'/>
</IS>
</ScalarInterpolator>
<Shape>
<IndexedFaceSet>
<TextureCoordinate DEF='KeyValueHolder'>
<IS>
<connect nodeField='pointprotoField='keyValue'/>
</IS>
</TextureCoordinate>
</IndexedFaceSet>
<Appearance DEF='SilenceWarnings'/>
</Shape>
</Switch>
<Script DEF='InterpolationScriptdirectOutput='true'>
<!-- Regular interpolator-style input -->
<field name='set_fractiontype='SFFloataccessType='inputOnly'/>
<field name='fractiontype='SFFloatvalue='0accessType='initializeOnly'
 appinfo='local variable' />

<field name='set_keytype='MFFloataccessType='inputOnly'/>
<field name='keyHolderNodetype='SFNodeaccessType='initializeOnly'>
<ScalarInterpolator USE=' KeyHolder'/>
</field>
<field name='key_changedtype='MFFloataccessType='outputOnly'/>
<field name='set_keyValuetype='MFVec2faccessType='inputOnly'/>
<field name='keyValueHolderNodetype='SFNodeaccessType='initializeOnly'>
<TextureCoordinate USE=' KeyValueHolder'/>
</field>
<field name='keyValue_changedtype='MFVec2faccessType='outputOnly'/>
<!-- Regular interpolator-style output -->
<field name='value_changedtype='SFVec2faccessType='outputOnly'/>
<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'/>
</IS>
<![CDATA[
            
ecmascript:

// internal global persistent variables
var previousFraction;
var previousFractionIndex;
var blockSize;
var outputArray;

var traceEnabled = false;

function tracePrint (outputString)
{
	if (traceEnabled) Browser.println ('[PositionInterpolator2d]' + outputString);
}
function alwaysPrint (outputString)
{
	Browser.println ('[PositionInterpolator2d]' + outputString);
}
function initialize ()
{
	key      = keyHolderNode.key;
	keyValue = keyValueHolderNode.point;
	previousFractionIndex = -1;
	previousFraction = 0;
	// check key array ranges [0..1] and is monotonically increasing
	// check that size of keyValue array is integer multiple of size of key array
	tracePrint ('key            =' + key);
	tracePrint ('key.length=' + key.length);
	tracePrint ('keyValue=  ' + keyValue);
	tracePrint ('keyValue.length=' + keyValue.length);
	blockSize =  keyValue.length/key.length;
	tracePrint ('blockSize=' + blockSize);
	if (blockSize != 1)
	{
	  alwaysPrint ('*** warning:  check sizes of key and keyValue, should be identical!');
	}
	if (key[0] != 0)
	{
	  alwaysPrint ('*** warning:  key[0] != 0');
	}
	if (key[key.length-1] != 1)
	{
	  alwaysPrint ('*** warning:  key[' + (key.length - 1) + '] != 1, reset from' + key[key.length-1] + ' to 1');
	  key[key.length-1] = 1;
	}
	for (index = 0; index < key.length; index++)
	{
		if ((key[index] < 0) || (key[index] > 1))
		{
		   alwaysPrint ('*** warning:  key[' + index + '] =' + key[index] + ', out of range [0..1]');
		}
	}
}

function set_fraction (inputFloat, timestamp) {
	fraction = inputFloat;
	outputResult = new SFVec2f ();
	tracePrint ('previousFractionIndex=' + previousFractionIndex
		 + ', fraction=' + fraction + ', previousFraction=' + previousFraction);

	if (fraction < 0)
	{
		tracePrint ('*** illegal fraction' + fraction + ' set to 0');
		fraction = 0;
		previousFractionIndex = 0; // first
	}
	else if (fraction > 1)
	{
		alwaysPrint ('*** illegal fraction' + fraction + ' set to 1');
		fraction = 1;
		previousFractionIndex = key.length - 1; // last
	}
	else if (previousFractionIndex == -1)
	{
		previousFractionIndex = 0; // first
		tracePrint ('previousFractionIndex initialized for first event');
	}
	else if ((fraction >= previousFraction) && (fraction >= key[previousFractionIndex+1]))
	{
		previousFractionIndex++;
	}
	else if (fraction < previousFraction) // regress, or loop repeat without reaching one
	{
		previousFractionIndex = 0;
		while ((fraction >= key[previousFractionIndex+1]) && (previousFractionIndex < blockSize))
		{
			previousFractionIndex++;
		}
		tracePrint ('reset/reincrement previousFractionIndex to' + previousFractionIndex);
	}

	if (fraction == 1) // use final block
	{
		tracePrint ('(fraction == 1)');
		// update outputResult with final keyValue
		outputResult = keyValue[keyValue.length];

		previousFractionIndex = -1; // setup for restart
		tracePrint ('finished final fraction==1 block');
	}
	// when fraction matches index, calculate value_changed from corresponding keyValue array
	else if (fraction == key[previousFractionIndex])
	{
		tracePrint ('(fraction == key[previousFractionIndex])');
		// update outputResult with corresponding keyValue
		outputResult = keyValue[previousFractionIndex];
	}
	else // calculate value_changed by interpolating between adjacent keyValue arrays
	{
		partialFraction = fraction                     - key[previousFractionIndex];
		deltaFraction   = key[previousFractionIndex+1] - key[previousFractionIndex];
		percentFraction = partialFraction / deltaFraction;
	//	tracePrint ('deltaFraction   =' + deltaFraction);
	//	tracePrint ('partialFraction =' + partialFraction);
		tracePrint ('percentFraction =' + percentFraction);
		// no arithmetic operators provided for SFVec2f, treat element by element
		nextKeyValue  = keyValue[previousFractionIndex + 1];
		priorKeyValue = keyValue[previousFractionIndex];
		tracePrint (' nextKeyValue =' + nextKeyValue);
		tracePrint ('priorKeyValue =' + priorKeyValue);
		deltaKeyValue = new SFVec2f (	nextKeyValue[0] - priorKeyValue[0],
						nextKeyValue[1] - priorKeyValue[1]);
		//	tracePrint ('deltaKeyValue =' + deltaKeyValue);
		// update outputResult
		outputResult[0] = keyValue[previousFractionIndex][0]
			        + percentFraction * deltaKeyValue[0];
		outputResult[1] = keyValue[previousFractionIndex][1]
			        + percentFraction * deltaKeyValue[1];
	}
	value_changed = outputResult;
	previousFraction = fraction;
	tracePrint ('value_changed=' + value_changed);
}

function set_key (inputArray, timestamp) {
	key = inputArray;       // update key Vector2FloatArray
	keyHolderNode.key = key; // update holder
	initialize (timestamp); // reverify key, keyValue sizes
}

function set_keyValue (inputArray, timestamp) {
	keyValue = inputArray;  // update keyValue Vector2FloatArray
	keyValueHolderNode.point = keyValue; // update holder
	initialize (timestamp); // reverify key, keyValue sizes
}

          
]]>
</Script>
</Group>
</ProtoBody>
</ProtoDeclare>
<!-- ====================================== -->
<!-- Example use -->
<Anchor description='PositionInterpolator2D Example'
  url=' "PositionInterpolator2dExample.x3d" "https://www.web3d.org/x3d/content/examples/Basic/development/PositionInterpolator2dExample.x3d" "PositionInterpolator2dExample.wrl" "https://www.web3d.org/x3d/content/examples/Basic/development/PositionInterpolator2dExample.wrl" ' >
<Shape>
<Text string='"PositionInterpolator2dPrototype" "defines a prototype" "" "Click text to see" "PositionInterpolator2dExample" " scene"'>
<FontStyle justify='"MIDDLE" "MIDDLE"size='0.7'/>
</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: InterpolationScript, KeyHolder, KeyValueHolder, SilenceWarnings

Index for ProtoDeclare definition: PositionInterpolator2D
-->

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.

InterpolationScript
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/PositionInterpolator2dPrototypeIndex.html -->
<!-- Version control at
https://sourceforge.net/p/x3d/code/HEAD/tree/www.web3d.org/x3d/content/examples/Basic/development/PositionInterpolator2dPrototype.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. -->