<?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 content='ProjectileInterpolatorPrototype.x3d' name='title'/>
    <meta content='A proto which simulates x-y plane projectile motion.' name='description'/>
    <meta content='Ozan APAYDIN' name='creator'/>
    <meta content='10 December 2001' name='created'/>
    <meta content='30 November 2024' name='modified'/>
    <meta content='https://www.web3d.org/x3d/content/examples/Basic/StudentProjects/ProjectileInterpolatorPrototype.x3d' name='identifier'/>
    <meta content='X3D-Edit 3.3, https://www.web3d.org/x3d/tools/X3D-Edit' name='generator'/>
    <meta content='../license.html' name='license'/>
  </head>
  <Scene>
    <WorldInfo title='ProjectileInterpolatorPrototype.x3d'/>
    <ProtoDeclare appinfo='This prototype is a Projectile Motion Interpolator. It takes Inputs : Initial Velocity and Angle then calculates trajectory of the projectile on x-y plane according to given dt time and B_m. It outputs value_changed values(SFVec3f).' name='ProjectileInterpolator'>
      <ProtoInterface>
        <field accessType='initializeOnly' appinfo='Initial Velocity value of the object.' name='Vi' type='SFFloat' value='0.0'/>
        <field accessType='initializeOnly' appinfo='Launch Angle. The angle between horizantal and launch direction' name='theta' type='SFFloat' value='0.0'/>
        <field accessType='initializeOnly' appinfo='Proportional to drag force = B2/m' name='B_m' type='SFFloat' value='0.00004'/>
        <field accessType='initializeOnly' appinfo='Time step.' name='dt' type='SFFloat' value='0.1'/>
        <field accessType='initializeOnly' appinfo='SFFloat Values ranging [0..1].' name='fraction' type='SFFloat' value='0.05'/>
        <field accessType='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.' name='set_fraction' type='SFFloat'/>
        <field accessType='inputOnly' appinfo='Sets theta to the value of eventIn.' name='set_theta' type='SFFloat'/>
        <field accessType='inputOnly' appinfo='Sets Vi to the value of eventIn.' name='set_Vi' type='SFFloat'/>
        <field accessType='outputOnly' appinfo='The interpolator function eventOut results as Vector3Float.' name='value_changed' type='SFVec3f'/>
      </ProtoInterface>
      <ProtoBody>
        <Script DEF='ProjectileMotionTrackerScript'>
          <field accessType='initializeOnly' name='Vi' type='SFFloat'/>
          <field accessType='initializeOnly' name='theta' type='SFFloat'/>
          <field accessType='initializeOnly' name='B_m' type='SFFloat'/>
          <field accessType='initializeOnly' name='dt' type='SFFloat'/>
          <field accessType='initializeOnly' appinfo='In range [01]' name='fraction' type='SFFloat'/>
          <field accessType='inputOnly' name='set_fraction' type='SFFloat'/>
          <field accessType='inputOnly' name='set_theta' type='SFFloat'/>
          <field accessType='inputOnly' name='set_Vi' type='SFFloat'/>
          <field accessType='outputOnly' name='value_changed' type='SFVec3f'/>
          <IS>
            <connect nodeField='Vi' protoField='Vi'/>
            <connect nodeField='theta' protoField='theta'/>
            <connect nodeField='B_m' protoField='B_m'/>
            <connect nodeField='dt' protoField='dt'/>
            <connect nodeField='fraction' protoField='fraction'/>
            <connect nodeField='set_fraction' protoField='set_fraction'/>
            <connect nodeField='set_theta' protoField='set_theta'/>
            <connect nodeField='set_Vi' protoField='set_Vi'/>
            <connect nodeField='value_changed' protoField='value_changed'/>
          </IS>
          <![CDATA[
ecmascript:
var x;
var y;
var Vx;
var Vy;
var B_m;
var dt;
var blocksize;
var Vi;
var theta;

var key;
var keyValue;
var previousFraction;
var previousFractionIndex;
var blockSize;
var outputArray;

function tracePrint (outputString)
{
	var traceEnabled = true;
	if (traceEnabled) Browser.println ('[WaypointInterpolator]' + outputString);
}
function alwaysPrint (outputString)
{
	Browser.println ('[WaypointInterpolator]' + outputString);
}



function initialize() {
   key = new Array();
   keyValue = new MFVec3f();
   x = new Array();
   y = new Array();
   calculateTrajectory();

   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 =  3; //keyValue.length/key.length;
	tracePrint ('blockSize=' + blockSize);
	if (blockSize != Math.round(blockSize))
	{
	  alwaysPrint ('*** warning:  blockSize not an integer multiple. check sizes of key and keyValue');
	}
	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 < blockSize; index++)
	{
		if ((key[index] < 0) || (key[index] > 1))
		{
		   alwaysPrint ('*** warning:  key[' + index + '] =' + key[index] + ', out of range [0..1]');
		}
	}
	// instantiate default array, later computations just update it
	outputArray = new SFVec3f();
	outputArray = keyValue[0];
	tracePrint ('initial outputArray=' + outputArray);

}

function set_fraction (inputFloat, timestamp) {
	fraction = inputFloat;
	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 = blockSize - 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)');


        	outputArray = keyValue[(keyValue.length -1)];

		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 outputArray - need to interpolate next
		outputArray = keyValue[previousFractionIndex];

	}
        else {

              delta = key[previousFractionIndex + 1] -  key[previousFractionIndex];
              differ = fraction - key[previousFractionIndex];
              percentDiffer = differ / delta;

              valueDelta = new SFVec3f();
              for(index = 0; index < blockSize; index++) {
                 valueDelta[index] =  keyValue[(previousFractionIndex + 1)][index] - keyValue[previousFractionIndex][index];
                 outputArray[index]  = keyValue[previousFractionIndex][index] + valueDelta[index] * percentDiffer;
		 Browser.println ('valueDelta' + valueDelta[index]);
                 Browser.println ('perDiffer' + percentDiffer);
              }

        }

	value_changed = outputArray;
	previousFraction = fraction;
	tracePrint ('value_changed=' + value_changed);

}


function set_Vi(initialVelocity, timeStamp) {
   Vi = initialVelocity;
   initialize(timeStamp);
}


function set_theta(angle, timeStamp) {
   theta = angle;
   initialize(timeStamp);
}


function calculateTrajectory() {

   x[0] = 0;
   y[0] = 0;

   var timeKeys = new Array();
   timeKeys[0] = 0.0;

   //convert degree to radian
   angle = Math.PI * theta / 180;

   Vx = Vi * Math.cos(angle);
   Vy = Vi * Math.sin(angle);

   var i = 0;

   do {
      i = i + 1;
      timeKeys[i] = timeKeys[i - 1] + dt;
      Browser.println ('timeKeys' + timeKeys[i]);
      x[i] = x[i - 1] + Vx * dt;
      y[i] = y[i - 1] + Vy * dt;


      f = B_m * Math.sqrt(Vx * Vx + Vy * Vy) * Math.exp(-y[i] / 0.0001);

      Vy = Vy - 9.8 * dt - f * Vy * dt;
      Vx = Vx - f * Vx * dt;
      Browser.println ('Vy' + Vy);
   }while(y[i] > 0);
   Browser.println ('Im here' + x.length);

   //interpolate to find landing point
   var a = -y[i] / y[i-1];
   x[i] = (x[i] + a * x[i-1]) / (1+a);
   y[i] = 0;

   //copy x, y values to keyValues
   copyToKeyValues();
   //finding keys
   for(j = 0; j < timeKeys.length; j++) {
      key[j] = timeKeys[j] / timeKeys[timeKeys.length - 1];
      Browser.println (' ' + key[j]);

   }

}

function copyToKeyValues() {
   for(i = 0; i < x.length; i++) {
     Browser.println ('x' + x[i]);
      keyValue[i][0] = x[i];
      keyValue[i][1] = y[i];
      keyValue[i][2] = 0;
   Browser.println ('keyValue' + i + ' ' + keyValue[i][0]);
   }

}
]]>
        </Script>
      </ProtoBody>
    </ProtoDeclare>
    <!-- ==================== -->
    <Anchor description='ProjectileInterpolatorArena' parameter='"target=_blank"' url='"ProjectileInterpolatorArena.wrl" "https://www.web3d.org/x3d/content/examples/Basic/StudentProjects/ProjectileInterpolatorArena.wrl" "ProjectileInterpolatorArena.x3d" "https://www.web3d.org/x3d/content/examples/Basic/StudentProjects/ProjectileInterpolatorArena.x3d"'>
      <Shape>
        <Text string='"ProjectileInterpolatorPrototype" "defines a prototype" "" "Click on this text to see" "ProjectileInterpolatorArena" " scene"'>
          <FontStyle justify='"MIDDLE" "MIDDLE"' size='0.7'/>
        </Text>
        <Appearance>
          <Material diffuseColor='1 1 0.2'/>
        </Appearance>
      </Shape>
    </Anchor>
    <PositionInterpolator/>
  </Scene>
</X3D>