@prefix :        <https://www.web3d.org/x3d/content/examples/Basic/StudentProjects/ProjectileInterpolatorPrototype.ttl#> .
@prefix owl:     <http://www.w3.org/2002/07/owl#> .
@prefix rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs:    <http://www.w3.org/2000/01/rdf-schema#> .
@prefix schema:  <http://schema.org/> .
@prefix dcterms: <http://purl.org/dc/terms/> .
@prefix xsd:     <http://www.w3.org/2001/XMLSchema#> .
@prefix x3d:     <https://www.web3d.org/specifications/x3d-4.0.xsd#> .
@prefix x3do:    <https://www.web3d.org/specifications/X3dOntology4.0#> .

:X3D a owl:NamedIndividual, x3do:X3D ;
  x3do:hasHead :head ;
  x3do:hasScene :Scene ;
  x3do:profile 'Immersive' ;
  x3do:version '3.0' ;
  x3do:noNamespaceSchemaLocation 'https://www.web3d.org/specifications/x3d-3.0.xsd' .
:head a owl:NamedIndividual, x3do:head ;
  x3do:hasParent :X3D ;
  x3do:hasMeta :meta_1_1, :meta_1_2, :meta_1_3, :meta_1_4, :meta_1_5, :meta_1_6, :meta_1_7, :meta_1_8 .
:meta_1_1 a owl:NamedIndividual, x3do:meta ;
  x3do:hasParent :head ;
  x3do:content 'ProjectileInterpolatorPrototype.x3d' ;
  x3do:name 'title' .
:meta_1_2 a owl:NamedIndividual, x3do:meta ;
  x3do:hasParent :head ;
  x3do:content 'A proto which simulates x-y plane projectile motion.' ;
  x3do:name 'description' .
:meta_1_3 a owl:NamedIndividual, x3do:meta ;
  x3do:hasParent :head ;
  x3do:content 'Ozan APAYDIN' ;
  x3do:name 'creator' .
:meta_1_4 a owl:NamedIndividual, x3do:meta ;
  x3do:hasParent :head ;
  x3do:content '10 December 2001' ;
  x3do:name 'created' .
:meta_1_5 a owl:NamedIndividual, x3do:meta ;
  x3do:hasParent :head ;
  x3do:content '30 November 2024' ;
  x3do:name 'modified' .
:meta_1_6 a owl:NamedIndividual, x3do:meta ;
  x3do:hasParent :head ;
  x3do:content 'https://www.web3d.org/x3d/content/examples/Basic/StudentProjects/ProjectileInterpolatorPrototype.x3d' ;
  x3do:name 'identifier' .
:meta_1_7 a owl:NamedIndividual, x3do:meta ;
  x3do:hasParent :head ;
  x3do:content 'X3D-Edit 3.3, https://www.web3d.org/x3d/tools/X3D-Edit' ;
  x3do:name 'generator' .
:meta_1_8 a owl:NamedIndividual, x3do:meta ;
  x3do:hasParent :head ;
  x3do:content '../license.html' ;
  x3do:name 'license' .
:meta dcterms:title "ProjectileInterpolatorPrototype.x3d" .
:meta dcterms:description "A proto which simulates x-y plane projectile motion." .
:meta dcterms:creator "Ozan APAYDIN" .
:meta dcterms:created "10 December 2001" .
:meta dcterms:modified "30 November 2024" .
:meta dcterms:identifier "https://www.web3d.org/x3d/content/examples/Basic/StudentProjects/ProjectileInterpolatorPrototype.x3d" .
:meta dcterms:generator "X3D-Edit 3.3, https://www.web3d.org/x3d/tools/X3D-Edit" .
:meta dcterms:license "../license.html" .
:Scene a owl:NamedIndividual, x3do:Scene ;
  x3do:hasParent :X3D ;
  x3do:hasChildren :WorldInfo_2_1, :Anchor_2_3, :PositionInterpolator_2_4 ;
  x3do:hasProtoDeclare :ProtoDeclare_2_2 .
:WorldInfo_2_1 a owl:NamedIndividual, x3do:WorldInfo ;
  x3do:hasParent :Scene ;
  x3do:title 'ProjectileInterpolatorPrototype.x3d' .
:ProtoDeclare_2_2 a owl:NamedIndividual, x3do:ProtoDeclare ;
  x3do:hasParent :Scene ;
  x3do:hasProtoInterface :ProtoInterface_2_2_1 ;
  x3do:hasProtoBody :ProtoBody_2_2_2 ;
  x3do: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).' ;
  x3do:name 'ProjectileInterpolator' .
:ProtoInterface_2_2_1 a owl:NamedIndividual, x3do:ProtoInterface ;
  x3do:hasParent :ProtoDeclare_2_2 ;
  x3do:hasField :field_2_2_1_1, :field_2_2_1_2, :field_2_2_1_3, :field_2_2_1_4, :field_2_2_1_5, :field_2_2_1_6, :field_2_2_1_7, :field_2_2_1_8, :field_2_2_1_9 .
:field_2_2_1_1 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :ProtoInterface_2_2_1 ;
  x3do:accessType 'initializeOnly' ;
  x3do:appinfo 'Initial Velocity value of the object.' ;
  x3do:name 'Vi' ;
  x3do:type 'SFFloat' ;
  x3do:value 0.0 .
:field_2_2_1_2 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :ProtoInterface_2_2_1 ;
  x3do:accessType 'initializeOnly' ;
  x3do:appinfo 'Launch Angle. The angle between horizantal and launch direction' ;
  x3do:name 'theta' ;
  x3do:type 'SFFloat' ;
  x3do:value 0.0 .
:field_2_2_1_3 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :ProtoInterface_2_2_1 ;
  x3do:accessType 'initializeOnly' ;
  x3do:appinfo 'Proportional to drag force = B2/m' ;
  x3do:name 'B_m' ;
  x3do:type 'SFFloat' ;
  x3do:value 0.00004 .
:field_2_2_1_4 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :ProtoInterface_2_2_1 ;
  x3do:accessType 'initializeOnly' ;
  x3do:appinfo 'Time step.' ;
  x3do:name 'dt' ;
  x3do:type 'SFFloat' ;
  x3do:value 0.1 .
:field_2_2_1_5 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :ProtoInterface_2_2_1 ;
  x3do:accessType 'initializeOnly' ;
  x3do:appinfo 'SFFloat Values ranging [0..1].' ;
  x3do:name 'fraction' ;
  x3do:type 'SFFloat' ;
  x3do:value 0.05 .
:field_2_2_1_6 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :ProtoInterface_2_2_1 ;
  x3do:accessType 'inputOnly' ;
  x3do: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.' ;
  x3do:name 'set_fraction' ;
  x3do:type 'SFFloat' .
:field_2_2_1_7 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :ProtoInterface_2_2_1 ;
  x3do:accessType 'inputOnly' ;
  x3do:appinfo 'Sets theta to the value of eventIn.' ;
  x3do:name 'set_theta' ;
  x3do:type 'SFFloat' .
:field_2_2_1_8 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :ProtoInterface_2_2_1 ;
  x3do:accessType 'inputOnly' ;
  x3do:appinfo 'Sets Vi to the value of eventIn.' ;
  x3do:name 'set_Vi' ;
  x3do:type 'SFFloat' .
:field_2_2_1_9 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :ProtoInterface_2_2_1 ;
  x3do:accessType 'outputOnly' ;
  x3do:appinfo 'The interpolator function eventOut results as Vector3Float.' ;
  x3do:name 'value_changed' ;
  x3do:type 'SFVec3f' .
:ProtoBody_2_2_2 a owl:NamedIndividual, x3do:ProtoBody ;
  x3do:hasParent :ProtoDeclare_2_2 ;
  x3do:hasChildren :ProjectileMotionTrackerScript .
:ProjectileMotionTrackerScript a owl:NamedIndividual, x3do:Script ;
  x3do:hasParent :ProtoBody_2_2_2 ;
  x3do:hasField :field_2_2_2_1_1, :field_2_2_2_1_2, :field_2_2_2_1_3, :field_2_2_2_1_4, :field_2_2_2_1_5, :field_2_2_2_1_6, :field_2_2_2_1_7, :field_2_2_2_1_8, :field_2_2_2_1_9 ;
  x3do:hasIS :IS_2_2_2_1_10 ;
  x3do:DEF 'ProjectileMotionTrackerScript' ;
  x3do:sourceCode """
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]);
   }

}
""" .
:field_2_2_2_1_1 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :ProjectileMotionTrackerScript ;
  x3do:accessType 'initializeOnly' ;
  x3do:name 'Vi' ;
  x3do:type 'SFFloat' .
:field_2_2_2_1_2 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :ProjectileMotionTrackerScript ;
  x3do:accessType 'initializeOnly' ;
  x3do:name 'theta' ;
  x3do:type 'SFFloat' .
:field_2_2_2_1_3 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :ProjectileMotionTrackerScript ;
  x3do:accessType 'initializeOnly' ;
  x3do:name 'B_m' ;
  x3do:type 'SFFloat' .
:field_2_2_2_1_4 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :ProjectileMotionTrackerScript ;
  x3do:accessType 'initializeOnly' ;
  x3do:name 'dt' ;
  x3do:type 'SFFloat' .
:field_2_2_2_1_5 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :ProjectileMotionTrackerScript ;
  x3do:accessType 'initializeOnly' ;
  x3do:appinfo 'In range [01]' ;
  x3do:name 'fraction' ;
  x3do:type 'SFFloat' .
:field_2_2_2_1_6 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :ProjectileMotionTrackerScript ;
  x3do:accessType 'inputOnly' ;
  x3do:name 'set_fraction' ;
  x3do:type 'SFFloat' .
:field_2_2_2_1_7 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :ProjectileMotionTrackerScript ;
  x3do:accessType 'inputOnly' ;
  x3do:name 'set_theta' ;
  x3do:type 'SFFloat' .
:field_2_2_2_1_8 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :ProjectileMotionTrackerScript ;
  x3do:accessType 'inputOnly' ;
  x3do:name 'set_Vi' ;
  x3do:type 'SFFloat' .
:field_2_2_2_1_9 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :ProjectileMotionTrackerScript ;
  x3do:accessType 'outputOnly' ;
  x3do:name 'value_changed' ;
  x3do:type 'SFVec3f' .
:IS_2_2_2_1_10 a owl:NamedIndividual, x3do:IS ;
  x3do:hasParent :ProjectileMotionTrackerScript ;
  x3do:hasConnect :connect_2_2_2_1_10_1, :connect_2_2_2_1_10_2, :connect_2_2_2_1_10_3, :connect_2_2_2_1_10_4, :connect_2_2_2_1_10_5, :connect_2_2_2_1_10_6, :connect_2_2_2_1_10_7, :connect_2_2_2_1_10_8, :connect_2_2_2_1_10_9 .
:connect_2_2_2_1_10_1 a owl:NamedIndividual, x3do:connect ;
  x3do:hasParent :IS_2_2_2_1_10 ;
  x3do:nodeField 'Vi' ;
  x3do:protoField 'Vi' .
:connect_2_2_2_1_10_2 a owl:NamedIndividual, x3do:connect ;
  x3do:hasParent :IS_2_2_2_1_10 ;
  x3do:nodeField 'theta' ;
  x3do:protoField 'theta' .
:connect_2_2_2_1_10_3 a owl:NamedIndividual, x3do:connect ;
  x3do:hasParent :IS_2_2_2_1_10 ;
  x3do:nodeField 'B_m' ;
  x3do:protoField 'B_m' .
:connect_2_2_2_1_10_4 a owl:NamedIndividual, x3do:connect ;
  x3do:hasParent :IS_2_2_2_1_10 ;
  x3do:nodeField 'dt' ;
  x3do:protoField 'dt' .
:connect_2_2_2_1_10_5 a owl:NamedIndividual, x3do:connect ;
  x3do:hasParent :IS_2_2_2_1_10 ;
  x3do:nodeField 'fraction' ;
  x3do:protoField 'fraction' .
:connect_2_2_2_1_10_6 a owl:NamedIndividual, x3do:connect ;
  x3do:hasParent :IS_2_2_2_1_10 ;
  x3do:nodeField 'set_fraction' ;
  x3do:protoField 'set_fraction' .
:connect_2_2_2_1_10_7 a owl:NamedIndividual, x3do:connect ;
  x3do:hasParent :IS_2_2_2_1_10 ;
  x3do:nodeField 'set_theta' ;
  x3do:protoField 'set_theta' .
:connect_2_2_2_1_10_8 a owl:NamedIndividual, x3do:connect ;
  x3do:hasParent :IS_2_2_2_1_10 ;
  x3do:nodeField 'set_Vi' ;
  x3do:protoField 'set_Vi' .
:connect_2_2_2_1_10_9 a owl:NamedIndividual, x3do:connect ;
  x3do:hasParent :IS_2_2_2_1_10 ;
  x3do:nodeField 'value_changed' ;
  x3do:protoField 'value_changed' .
:Anchor_2_3 a owl:NamedIndividual, x3do:Anchor ;
  x3do:hasParent :Scene ;
  x3do:hasChildren :Shape_2_3_1 ;
  x3do:description 'ProjectileInterpolatorArena' ;
  x3do:parameter '"target=_blank"' ;
  x3do: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_2_3_1 a owl:NamedIndividual, x3do:Shape ;
  x3do:hasParent :Anchor_2_3 ;
  x3do:hasGeometry :Text_2_3_1_1 ;
  x3do:hasAppearance :Appearance_2_3_1_2 .
:Text_2_3_1_1 a owl:NamedIndividual, x3do:Text ;
  x3do:hasParent :Shape_2_3_1 ;
  x3do:hasFontStyle :FontStyle_2_3_1_1_1 ;
  x3do:string '"ProjectileInterpolatorPrototype" "defines a prototype" "" "Click on this text to see" "ProjectileInterpolatorArena" " scene"' .
:FontStyle_2_3_1_1_1 a owl:NamedIndividual, x3do:FontStyle ;
  x3do:hasParent :Text_2_3_1_1 ;
  x3do:justify '"MIDDLE" "MIDDLE"' ;
  x3do:size 0.7 .
:Appearance_2_3_1_2 a owl:NamedIndividual, x3do:Appearance ;
  x3do:hasParent :Shape_2_3_1 ;
  x3do:hasMaterial :Material_2_3_1_2_1 .
:Material_2_3_1_2_1 a owl:NamedIndividual, x3do:Material ;
  x3do:hasParent :Appearance_2_3_1_2 ;
  x3do:diffuseColor ( 1 1 0.2 ) .
:PositionInterpolator_2_4 a owl:NamedIndividual, x3do:PositionInterpolator ;
  x3do:hasParent :Scene .
