@prefix :        <https://www.web3d.org/x3d/content/examples/Savage/Tools/Authoring/AnimatedViewpointRecorderPrototype.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.1.xsd#> .
@prefix x3do:    <https://www.web3d.org/specifications/X3dOntology4.1#> .

: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_9, :meta_1_10, :meta_1_11, :meta_1_12, :meta_1_13 .
:meta_1_1 a owl:NamedIndividual, x3do:meta ;
  x3do:hasParent :head ;
  x3do:content 'AnimatedViewpointRecorderPrototype.x3d' ;
  x3do:name 'title' .
:meta_1_2 a owl:NamedIndividual, x3do:meta ;
  x3do:hasParent :head ;
  x3do:content 'Record camera position and orientation as user navigates, then filter values and produce output, both into the console output window and as a replayable node group. Future work: further filtering.' ;
  x3do:name 'description' .
:meta_1_3 a owl:NamedIndividual, x3do:meta ;
  x3do:hasParent :head ;
  x3do:content 'Don Brutzman, Ken Curtin, Duane Davis, Christos Kalogrias' ;
  x3do:name 'creator' .
:meta_1_4 a owl:NamedIndividual, x3do:meta ;
  x3do:hasParent :head ;
  x3do:content '24 October 2003' ;
  x3do:name 'created' .
:meta_1_5 a owl:NamedIndividual, x3do:meta ;
  x3do:hasParent :head ;
  x3do:content '12 October 2023' ;
  x3do:name 'modified' .
:meta_1_6 a owl:NamedIndividual, x3do:meta ;
  x3do:hasParent :head ;
  x3do:content 'AnimatedViewpointRecorderExample.x3d' ;
  x3do:name 'reference' .
:meta_1_7 a owl:NamedIndividual, x3do:meta ;
  x3do:hasParent :head ;
  x3do:content 'AnimatedViewpointRecorderSample.x3d' ;
  x3do:name 'reference' .
:meta_1_8 a owl:NamedIndividual, x3do:meta ;
  x3do:hasParent :head ;
  x3do:content 'http://www.realism.com/Web3D/Examples#WhereAmI' ;
  x3do:name 'reference' .
:meta_1_9 a owl:NamedIndividual, x3do:meta ;
  x3do:hasParent :head ;
  x3do:content 'http://www.realism.com/vrml/Example/WhereAmI/WhereAmI_Proto.wrl' ;
  x3do:name 'reference' .
:meta_1_10 a owl:NamedIndividual, x3do:meta ;
  x3do:hasParent :head ;
  x3do:content 'recording animated viewpoint tour' ;
  x3do:name 'subject' .
:meta_1_11 a owl:NamedIndividual, x3do:meta ;
  x3do:hasParent :head ;
  x3do:content 'https://www.web3d.org/x3d/content/examples/Savage/Tools/Authoring/AnimatedViewpointRecorderPrototype.x3d' ;
  x3do:name 'identifier' .
:meta_1_12 a owl:NamedIndividual, x3do:meta ;
  x3do:hasParent :head ;
  x3do:content 'X3D-Edit 4.0, https://www.web3d.org/x3d/tools/X3D-Edit' ;
  x3do:name 'generator' .
:meta_1_13 a owl:NamedIndividual, x3do:meta ;
  x3do:hasParent :head ;
  x3do:content '../../license.html' ;
  x3do:name 'license' .
:meta dcterms:title "AnimatedViewpointRecorderPrototype.x3d" .
:meta dcterms:description "Record camera position and orientation as user navigates, then filter values and produce output, both into the console output window and as a replayable node group. Future work: further filtering." .
:meta dcterms:creator "Don Brutzman, Ken Curtin, Duane Davis, Christos Kalogrias" .
:meta dcterms:created "24 October 2003" .
:meta dcterms:modified "12 October 2023" .
:meta dcterms:reference "AnimatedViewpointRecorderExample.x3d" .
:meta dcterms:reference "AnimatedViewpointRecorderSample.x3d" .
:meta dcterms:reference "http://www.realism.com/Web3D/Examples#WhereAmI" .
:meta dcterms:reference "http://www.realism.com/vrml/Example/WhereAmI/WhereAmI_Proto.wrl" .
:meta dcterms:subject "recording animated viewpoint tour" .
:meta dcterms:identifier "https://www.web3d.org/x3d/content/examples/Savage/Tools/Authoring/AnimatedViewpointRecorderPrototype.x3d" .
:meta dcterms:generator "X3D-Edit 4.0, 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, :Background_2_3, :Viewpoint_2_4, :Anchor_2_5 ;
  x3do:hasProtoDeclare :ProtoDeclare_2_2 .
:WorldInfo_2_1 a owl:NamedIndividual, x3do:WorldInfo ;
  x3do:hasParent :Scene ;
  x3do:title 'AnimatedViewpointRecorderPrototype.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 'AnimatedViewpointRecorder captures view position and orientation tour to create a guided tour animation. The recording output goes to the browser console where the .x3d (or .x3dv) output can be cut/pasted for further use.' ;
  x3do:name 'AnimatedViewpointRecorder' .
: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_1 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :ProtoInterface_2_2_1 ;
  x3do:accessType 'inputOnly' ;
  x3do:appinfo 'Set start=true to commence recording viewpoint position/orientation.' ;
  x3do:name 'start' ;
  x3do:type 'SFBool' .
:field_2_2_1_2 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :ProtoInterface_2_2_1 ;
  x3do:accessType 'inputOnly' ;
  x3do:appinfo 'Set stop=true to finish recording viewpoint position/orientation. Resulting VRML is added to scene resulting X3D and VRML is output to console.' ;
  x3do:name 'stop' ;
  x3do:type 'SFBool' .
:field_2_2_1_3 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :ProtoInterface_2_2_1 ;
  x3do:accessType 'initializeOnly' ;
  x3do:appinfo 'default 0.1 seconds' ;
  x3do:name 'samplingInterval' ;
  x3do:type 'SFTime' ;
  x3do:value '0.1' .
:field_2_2_1_4 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :ProtoInterface_2_2_1 ;
  x3do:accessType 'initializeOnly' ;
  x3do:appinfo 'whether to output .x3d syntax on browser console' ;
  x3do:name 'outputX3D' ;
  x3do:type 'SFBool' ;
  x3do:value true .
:field_2_2_1_5 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :ProtoInterface_2_2_1 ;
  x3do:accessType 'initializeOnly' ;
  x3do:appinfo 'whether to output .x3d syntax on browser console' ;
  x3do:name 'outputClassicVRML' ;
  x3do:type 'SFBool' ;
  x3do:value false .
:field_2_2_1_6 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :ProtoInterface_2_2_1 ;
  x3do:accessType 'initializeOnly' ;
  x3do:appinfo 'TODO not yet implemented' ;
  x3do:name 'filterDeadTime' ;
  x3do:type 'SFBool' ;
  x3do:value false .
:ProtoBody_2_2_2 a owl:NamedIndividual, x3do:ProtoBody ;
  x3do:hasParent :ProtoDeclare_2_2 ;
  x3do:hasChildren :Group_2_2_2_1 .
:Group_2_2_2_1 a owl:NamedIndividual, x3do:Group ;
  x3do:hasParent :ProtoBody_2_2_2 ;
  x3do:hasChildren :NewViewpointGroup, :WhereSensor, :RecordingScript, :RouteHolder .
:NewViewpointGroup a owl:NamedIndividual, x3do:Group ;
  x3do:hasParent :Group_2_2_2_1 ;
  x3do:DEF 'NewViewpointGroup' .
:WhereSensor a owl:NamedIndividual, x3do:ProximitySensor ;
  x3do:hasParent :Group_2_2_2_1 ;
  x3do:DEF 'WhereSensor' ;
  x3do:size ( 1000000000 1000000000 1000000000 ) .
:RecordingScript a owl:NamedIndividual, x3do:Script ;
  x3do:hasParent :Group_2_2_2_1 ;
  x3do:hasField :field_2_2_2_1_3_1, :field_2_2_2_1_3_2, :field_2_2_2_1_3_3, :field_2_2_2_1_3_4, :field_2_2_2_1_3_5, :field_2_2_2_1_3_6, :field_2_2_2_1_3_7, :field_2_2_2_1_3_8, :field_2_2_2_1_3_9, :field_2_2_2_1_3_10, :field_2_2_2_1_3_11, :field_2_2_2_1_3_12, :field_2_2_2_1_3_13, :field_2_2_2_1_3_14, :field_2_2_2_1_3_15, :field_2_2_2_1_3_16, :field_2_2_2_1_3_17, :field_2_2_2_1_3_18, :field_2_2_2_1_3_19, :field_2_2_2_1_3_20, :field_2_2_2_1_3_21, :field_2_2_2_1_3_22 ;
  x3do:hasIS :IS_2_2_2_1_3_23 ;
  x3do:DEF 'RecordingScript' ;
  x3do:directOutput true ;
  x3do:sourceCode """
ecmascript:

function initialize()
{
   positionArray        = new MFVec3f ();
   orientationArray     = new MFRotation ();
   positionTimeArray    = new MFTime ();
   orientationTimeArray = new MFTime ();

      positionEventsReceived = false;
   orientationEventsReceived = false;
}

function roundoff (value, digits)
{
	resolution = 1;
	for (i = 1; i <= digits; i++ )
	{
		resolution *= 10;
	}
	return Math.round (value*resolution) / resolution; // round to resolution
}
function filterPositions()
{
    // TODO
}

function filterOrientations()
{
    // TODO
}

function set_position (eventValue, timestamp)
{
// Browser.println ('position=' + eventValue);
   // we are counting on an initialization eventValue being sent by ProximitySensor
   positionEventsReceived = true;
   if ( positionArray.length == 0 )
   {
   	positionArray[0]     = eventValue; // initialize
   	positionTimeArray[0] = timestamp;  // initialize
   }
   precedingPositionSampleTime = positionTimeArray[ positionArray.length - 1 ];

   // seconds duration since last valid sample
   if ( (timestamp - precedingPositionSampleTime) > samplingInterval )
   {
	// append values to each array
	positionArray[positionArray.length] = eventValue;
	positionTimeArray[positionTimeArray.length] = timestamp;
   }
   precedingPosition = eventValue;
}

function set_orientation (eventValue, timestamp)
{
   // we are counting on an initialization eventValue being sent by ProximitySensor
   orientationEventsReceived = true;
   if ( orientationArray.length == 0 )
   {
     r = Math.sqrt (eventValue.x*eventValue.x + eventValue.y*eventValue.y + eventValue.z*eventValue.z);
//   Browser.println ('orientation=' + eventValue.toString() + ', r=' + r); // trace
     if (r != 0)
     {
        eventValue.x = eventValue.x / r;
        eventValue.y = eventValue.y / r;
        eventValue.z = eventValue.z / r;
     }
   	 orientationArray[0]     = eventValue; // initialize
   	 orientationTimeArray[0] = timestamp;  // initialize
   }
   precedingOrientationSampleTime = orientationTimeArray[ orientationTimeArray.length - 1 ];

   // append sample values to each array
   if ( (timestamp - precedingOrientationSampleTime) > samplingInterval )
   {
     orientationTimeArray[orientationTimeArray.length] = timestamp;
	 // normalize SFRotation axis if needed
     r = Math.sqrt (eventValue.x*eventValue.x + eventValue.y*eventValue.y + eventValue.z*eventValue.z);
//    Browser.println ('orientation=' + eventValue.toString() + ', r=' + r); // trace
     if (r != 0)
     {
        eventValue.x = eventValue.x / r;
        eventValue.y = eventValue.y / r;
        eventValue.z = eventValue.z / r;
        // auto append to array, no need to allocate
        orientationArray[orientationArray.length] = eventValue;
     }
     else // illegal zero-magnitude axis returned by browser, so just use previous rotation
     {
        // auto append to array, no need to allocate
        orientationArray[orientationArray.length] = precedingOrientation;
     }
   }
   precedingOrientation = eventValue;
}

function start (eventValue, timestamp)
{
	if (eventValue == false) return; // only accept start if eventValue == true
	if (recordingInProgress == true) return; // ignore repeated starts while already running
	recordingInProgress  = true;
    // arrays need to be reinitialized from previous run
	initialize();

    Browser.println ('    <!-- start recording ' + numberOfToursCreated + ' -->');
}

function stop (eventValue, timestamp)
{
	if (eventValue == false) return; // only accept stop  if eventValue == true
	if (recordingInProgress == false)
	{
	   Browser.println ('    <!-- stopped recording without first starting. -->');
       return;
	}

    // ensure legal array lengths in case some events were never sent due to not moving
    if (positionEventsReceived == false)
    {
       Browser.println ('<!-- warning:  no position values received! no action taken. -->');
       return;
    }
    if (orientationEventsReceived == false)
    {
       Browser.println ('<!-- warning:  no orientation values received! no action taken. -->');
       return;
    }
	recordingInProgress = false;

	// preceding last values were at last sampleInterval (either set_position or set_orientation)
	// add one more to each array since they are not sent values by sensor when not changing
       positionArray[   positionArray.length] = precedingPosition;
    orientationArray[orientationArray.length] = precedingOrientation;
       positionTimeArray[   positionTimeArray.length] = timestamp;
    orientationTimeArray[orientationTimeArray.length] = timestamp;

    if (positionArray.length != positionTimeArray.length)
    {
       Browser.println ('<!-- internal error:  positionArray.length (' + positionArray.length + ') != positionTimeArray.length (' + positionTimeArray.length + ') -->');
    }
    if (orientationArray.length != orientationTimeArray.length)
    {
       Browser.println ('<!-- internal error:  orientationArray.length (' + orientationArray.length + ') != orientationTimeArray.length (' + orientationTimeArray.length + ') -->');
    }

   filterPositions();
   filterOrientations();

   // iff events are sent simultaneously, could use either array with start/stop times synchronized
   // however that might be a bad assumption...  so reset start times to match
   if (positionTimeArray[0] > orientationTimeArray[0])    positionTimeArray[0] = orientationTimeArray[0];
   if (positionTimeArray[0] < orientationTimeArray[0]) orientationTimeArray[0] = positionTimeArray[0];

   startTime = positionTimeArray[0];
   stopTime  = positionTimeArray[positionTimeArray.length-1];
   interval  = stopTime - startTime;

   x3dString =
   '    <!-- ********** start recorded Animated Tour ' + numberOfToursCreated + ' using .x3d syntax ********** -->' +
   '    <Group>\n' +
   '      <Viewpoint DEF=\"AnimatedViewpointRecorderViewpoint' + numberOfToursCreated + '\" description=\"Animated Tour ' + numberOfToursCreated + '\"\n' +
   '         position=\"'    + positionArray[0].x + ' '    + positionArray[0].y + ' '    + positionArray[0].z + '\" \n' +
   '         orientation=\"' + orientationArray[0].x + ' ' + orientationArray[0].y + ' ' + orientationArray[0].z + ' ' + orientationArray[0].angle + '\"/>\n' +
   '      <!-- samplingInterval=' + samplingInterval + ' seconds, default TimeSensor loop=true -->' +
   '      <TimeSensor DEF=\"AnimatedViewpointRecorderTimer' + numberOfToursCreated + '\" cycleInterval=\"' + interval + '\"\n' +
   '        enabled=\"true\" loop=\"true\"/>\n' +
   '      <PositionInterpolator DEF=\"AnimatedViewpointRecorderPosition' + numberOfToursCreated + '\" key=\"\n' ;
   for (counter = 0; counter < positionTimeArray.length; counter++)
   {
    x3dString = x3dString +  roundoff(((positionTimeArray[counter] - positionTimeArray[0]) / interval),5) + ' \n';
   }
   x3dString = x3dString + '\"      keyValue=\"\n';
   for (counter = 0; counter < positionArray.length; counter++)
   {
      x3dString = x3dString +   positionArray[counter].x + ' ' + positionArray[counter].y + ' ' + positionArray[counter].z + ', \n';
   }
   x3dString = x3dString + '         \"/>\n' +
   '      <OrientationInterpolator DEF=\"AnimatedViewpointRecorderOrientation' + numberOfToursCreated + '\" key=\"\n';
   for (counter = 0; counter < orientationTimeArray.length; counter++)
   {
    x3dString = x3dString +   roundoff(((orientationTimeArray[counter] - orientationTimeArray[0]) / interval),5) + ' \n';
   }
   x3dString = x3dString +   '\"      keyValue=\"\n';
   for (counter = 0; counter < orientationArray.length; counter++)
   {
      var r = Math.sqrt(orientationArray[counter].x*orientationArray[counter].x + orientationArray[counter].y*orientationArray[counter].y + orientationArray[counter].z*orientationArray[counter].z); // normalize
      if (r == 0) r = 1; // avoid divide by zero
      x3dString = x3dString + (orientationArray[counter].x / r) + ' ' + (orientationArray[counter].y / r) + ' ' + (orientationArray[counter].z / r) + ' ' + orientationArray[counter].angle + ', \n';
   }
   x3dString = x3dString + '         \"/>\n' +
   '      <Group>\n' +
   '        <ROUTE fromField=\"bindTime\" fromNode=\"AnimatedViewpointRecorderViewpoint' + numberOfToursCreated + '\"\n' +
   '          toField=\"startTime\" toNode=\"AnimatedViewpointRecorderTimer' + numberOfToursCreated + '\"/>\n' +
   '        <ROUTE fromField=\"fraction_changed\" fromNode=\"AnimatedViewpointRecorderTimer' + numberOfToursCreated + '\"\n' +
   '          toField=\"set_fraction\" toNode=\"AnimatedViewpointRecorderPosition' + numberOfToursCreated + '\"/>\n' +
   '        <ROUTE fromField=\"fraction_changed\" fromNode=\"AnimatedViewpointRecorderTimer' + numberOfToursCreated + '\"\n' +
   '          toField=\"set_fraction\" toNode=\"AnimatedViewpointRecorderOrientation' + numberOfToursCreated + '\"/>\n' +
   '        <ROUTE fromField=\"value_changed\" fromNode=\"AnimatedViewpointRecorderPosition' + numberOfToursCreated + '\"\n' +
   '          toField=\"position\" toNode=\"AnimatedViewpointRecorderViewpoint' + numberOfToursCreated + '\"/>\n' +
   '        <ROUTE fromField=\"value_changed\"\n' +
   '          fromNode=\"AnimatedViewpointRecorderOrientation' + numberOfToursCreated + '\" toField=\"orientation\" toNode=\"AnimatedViewpointRecorderViewpoint' + numberOfToursCreated + '\"/>\n' +
   '      </Group>\n' +
   '    </Group>\n';

   if (outputX3D) Browser.println (x3dString);

   vrmlString =
      '# ********** start recorded Animated Tour ' + numberOfToursCreated + ' using .x3dv syntax ********** \n' +
      'Group {\n' +
      '  children [\n' +
      '      DEF AnimatedViewpointRecorderViewpoint' + numberOfToursCreated + ' Viewpoint {\n' +
      '        description \"Animated Tour ' + numberOfToursCreated + '\"\n' +
      '        orientation ' + orientationArray[0].x + ' ' + orientationArray[0].y + ' ' + orientationArray[0].z + ' ' + orientationArray[0].angle + '\n' +
      '        position ' + positionArray[0].x + ' '    + positionArray[0].y + ' '    + positionArray[0].z + '\n' +
      '      }\n' +
      '      DEF AnimatedViewpointRecorderTimer' + numberOfToursCreated + ' TimeSensor {\n' +
      '        cycleInterval ' + interval +  '\n' +
      '        loop TRUE\n' +
      '      }\n' +
      '      DEF AnimatedViewpointRecorderPosition' + numberOfToursCreated + ' PositionInterpolator {\n' +
      '        key [\n';
   for (counter = 0; counter < positionTimeArray.length; counter++)
   {
      vrmlString = vrmlString + roundoff(((positionTimeArray[counter] - positionTimeArray[0]) / interval),5) + ' \n';
   }
   vrmlString = vrmlString + '         ]\n' +
      '        keyValue [\n';
   for (counter = 0; counter < positionArray.length; counter++)
   {
      vrmlString = vrmlString +   positionArray[counter].x + ' ' + positionArray[counter].y + ' ' + positionArray[counter].z + ', \n';
   }
   vrmlString = vrmlString + '         ]\n' +
      '      }\n' +
      '      DEF AnimatedViewpointRecorderOrientation' + numberOfToursCreated + ' OrientationInterpolator {\n' +
      '        key [\n';
   for (counter = 0; counter < orientationTimeArray.length; counter++)
   {
    vrmlString = vrmlString +   roundoff(((orientationTimeArray[counter] - orientationTimeArray[0]) / interval),5) + ' \n';
   }
   vrmlString = vrmlString + '         ]\n' +
      '        keyValue [\n';
   for (counter = 0; counter < orientationArray.length; counter++)
   {
      vrmlString = vrmlString + orientationArray[counter].x + ' ' + orientationArray[counter].y + ' ' + orientationArray[counter].z + ' ' + orientationArray[counter].angle + ', \n';
   }
   vrmlString = vrmlString + '         ]\n' +
      '      }\n' +
      '      Group {\n' +
      '         ROUTE AnimatedViewpointRecorderViewpoint' + numberOfToursCreated + '.bindTime TO AnimatedViewpointRecorderTimer' + numberOfToursCreated + '.startTime\n' +
      '         ROUTE AnimatedViewpointRecorderTimer' + numberOfToursCreated + '.fraction_changed TO AnimatedViewpointRecorderPosition' + numberOfToursCreated + '.set_fraction\n' +
      '         ROUTE AnimatedViewpointRecorderTimer' + numberOfToursCreated + '.fraction_changed TO AnimatedViewpointRecorderOrientation' + numberOfToursCreated + '.set_fraction\n' +
      '         ROUTE AnimatedViewpointRecorderPosition' + numberOfToursCreated + '.value_changed TO AnimatedViewpointRecorderViewpoint' + numberOfToursCreated + '.position\n' +
      '         ROUTE AnimatedViewpointRecorderOrientation' + numberOfToursCreated + '.value_changed TO AnimatedViewpointRecorderViewpoint' + numberOfToursCreated + '.orientation\n' +
      '      }\n' +
      '   ]\n' +
      '}\n';

   Browser.println ();
   if (outputClassicVRML) Browser.println (vrmlString);

   numberOfToursCreated++;
   // TODO
   // newNode = new SFNode(vrmlString);
   // newViewpointGroup.children[numberOfToursCreated] = newNode;
}
""" .
:field_2_2_2_1_3_1 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :RecordingScript ;
  x3do:accessType 'inputOnly' ;
  x3do:name 'start' ;
  x3do:type 'SFBool' .
:field_2_2_2_1_3_2 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :RecordingScript ;
  x3do:accessType 'inputOnly' ;
  x3do:name 'stop' ;
  x3do:type 'SFBool' .
:field_2_2_2_1_3_3 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :RecordingScript ;
  x3do:accessType 'initializeOnly' ;
  x3do:appinfo 'seconds' ;
  x3do:name 'samplingInterval' ;
  x3do:type 'SFTime' .
:field_2_2_2_1_3_4 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :RecordingScript ;
  x3do:accessType 'initializeOnly' ;
  x3do:appinfo 'whether to output .x3d syntax on browser console' ;
  x3do:name 'outputX3D' ;
  x3do:type 'SFBool' .
:field_2_2_2_1_3_5 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :RecordingScript ;
  x3do:accessType 'initializeOnly' ;
  x3do:appinfo 'whether to output .x3d syntax on browser console' ;
  x3do:name 'outputClassicVRML' ;
  x3do:type 'SFBool' .
:field_2_2_2_1_3_6 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :RecordingScript ;
  x3do:accessType 'outputOnly' ;
  x3do:appinfo 'persistent state variable' ;
  x3do:name 'recordingInProgress' ;
  x3do:type 'SFBool' .
:field_2_2_2_1_3_7 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :RecordingScript ;
  x3do:accessType 'inputOnly' ;
  x3do:name 'set_position' ;
  x3do:type 'SFVec3f' .
:field_2_2_2_1_3_8 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :RecordingScript ;
  x3do:accessType 'inputOnly' ;
  x3do:name 'set_orientation' ;
  x3do:type 'SFRotation' .
:field_2_2_2_1_3_9 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :RecordingScript ;
  x3do:accessType 'initializeOnly' ;
  x3do:name 'positionArray' ;
  x3do:type 'MFVec3f' .
:field_2_2_2_1_3_10 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :RecordingScript ;
  x3do:accessType 'initializeOnly' ;
  x3do:name 'positionTimeArray' ;
  x3do:type 'MFTime' .
:field_2_2_2_1_3_11 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :RecordingScript ;
  x3do:accessType 'initializeOnly' ;
  x3do:name 'orientationArray' ;
  x3do:type 'MFRotation' .
:field_2_2_2_1_3_12 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :RecordingScript ;
  x3do:accessType 'initializeOnly' ;
  x3do:name 'orientationTimeArray' ;
  x3do:type 'MFTime' .
:field_2_2_2_1_3_13 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :RecordingScript ;
  x3do:accessType 'initializeOnly' ;
  x3do:appinfo 'not yet implemented' ;
  x3do:name 'filterDeadTime' ;
  x3do:type 'SFBool' .
:field_2_2_2_1_3_14 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :RecordingScript ;
  x3do:hasChildren :NewViewpointGroup-USE-1 ;
  x3do:accessType 'initializeOnly' ;
  x3do:name 'newViewpointGroup' ;
  x3do:type 'SFNode' .
:NewViewpointGroup-USE-1 a owl:NamedIndividual, x3do:Group ;
  x3do:hasParent :field_2_2_2_1_3_14 ;
  owl:sameAs :NewViewpointGroup . # DEF matching this USE
:field_2_2_2_1_3_15 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :RecordingScript ;
  x3do:accessType 'initializeOnly' ;
  x3do:appinfo 'persistent holding variable' ;
  x3do:name 'numberOfToursCreated' ;
  x3do:type 'SFInt32' ;
  x3do:value 0 .
:field_2_2_2_1_3_16 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :RecordingScript ;
  x3do:accessType 'initializeOnly' ;
  x3do:appinfo 'persistent holding variable' ;
  x3do:name 'precedingPosition' ;
  x3do:type 'SFVec3f' ;
  x3do:value ( 0 0 0 ) .
:field_2_2_2_1_3_17 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :RecordingScript ;
  x3do:accessType 'initializeOnly' ;
  x3do:appinfo 'persistent holding variable' ;
  x3do:name 'precedingOrientation' ;
  x3do:type 'SFRotation' ;
  x3do:value ( 0 1 0 0 ) .
:field_2_2_2_1_3_18 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :RecordingScript ;
  x3do:accessType 'initializeOnly' ;
  x3do:appinfo 'persistent holding variable' ;
  x3do:name 'precedingPositionSampleTime' ;
  x3do:type 'SFTime' ;
  x3do:value '0' .
:field_2_2_2_1_3_19 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :RecordingScript ;
  x3do:accessType 'initializeOnly' ;
  x3do:appinfo 'persistent holding variable' ;
  x3do:name 'precedingOrientationSampleTime' ;
  x3do:type 'SFTime' ;
  x3do:value '0' .
:field_2_2_2_1_3_20 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :RecordingScript ;
  x3do:accessType 'initializeOnly' ;
  x3do:appinfo 'normalization factor' ;
  x3do:name 'r' ;
  x3do:type 'SFFloat' ;
  x3do:value 1 .
:field_2_2_2_1_3_21 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :RecordingScript ;
  x3do:accessType 'initializeOnly' ;
  x3do:appinfo 'track output of ProximitySensor' ;
  x3do:name 'positionEventsReceived' ;
  x3do:type 'SFBool' ;
  x3do:value false .
:field_2_2_2_1_3_22 a owl:NamedIndividual, x3do:field ;
  x3do:hasParent :RecordingScript ;
  x3do:accessType 'initializeOnly' ;
  x3do:appinfo 'track output of ProximitySensor' ;
  x3do:name 'orientationEventsReceived' ;
  x3do:type 'SFBool' ;
  x3do:value false .
:IS_2_2_2_1_3_23 a owl:NamedIndividual, x3do:IS ;
  x3do:hasParent :RecordingScript ;
  x3do:hasConnect :connect_2_2_2_1_3_23_1, :connect_2_2_2_1_3_23_2, :connect_2_2_2_1_3_23_3, :connect_2_2_2_1_3_23_4, :connect_2_2_2_1_3_23_5, :connect_2_2_2_1_3_23_6 .
:connect_2_2_2_1_3_23_1 a owl:NamedIndividual, x3do:connect ;
  x3do:hasParent :IS_2_2_2_1_3_23 ;
  x3do:nodeField 'start' ;
  x3do:protoField 'start' .
:connect_2_2_2_1_3_23_2 a owl:NamedIndividual, x3do:connect ;
  x3do:hasParent :IS_2_2_2_1_3_23 ;
  x3do:nodeField 'stop' ;
  x3do:protoField 'stop' .
:connect_2_2_2_1_3_23_3 a owl:NamedIndividual, x3do:connect ;
  x3do:hasParent :IS_2_2_2_1_3_23 ;
  x3do:nodeField 'samplingInterval' ;
  x3do:protoField 'samplingInterval' .
:connect_2_2_2_1_3_23_4 a owl:NamedIndividual, x3do:connect ;
  x3do:hasParent :IS_2_2_2_1_3_23 ;
  x3do:nodeField 'outputX3D' ;
  x3do:protoField 'outputX3D' .
:connect_2_2_2_1_3_23_5 a owl:NamedIndividual, x3do:connect ;
  x3do:hasParent :IS_2_2_2_1_3_23 ;
  x3do:nodeField 'outputClassicVRML' ;
  x3do:protoField 'outputClassicVRML' .
:connect_2_2_2_1_3_23_6 a owl:NamedIndividual, x3do:connect ;
  x3do:hasParent :IS_2_2_2_1_3_23 ;
  x3do:nodeField 'filterDeadTime' ;
  x3do:protoField 'filterDeadTime' .
:RouteHolder a owl:NamedIndividual, x3do:Group ;
  x3do:hasParent :Group_2_2_2_1 ;
  x3do:hasROUTE :ROUTE_2_2_2_1_4_1, :ROUTE_2_2_2_1_4_2, :ROUTE_2_2_2_1_4_3 ;
  x3do:DEF 'RouteHolder' .
:ROUTE_2_2_2_1_4_1 a owl:NamedIndividual, x3do:ROUTE ;
  x3do:hasParent :RouteHolder ;
  x3do:fromField 'position_changed' ;
  x3do:fromNode 'WhereSensor' ;
  x3do:toField 'set_position' ;
  x3do:toNode 'RecordingScript' .
:ROUTE_2_2_2_1_4_2 a owl:NamedIndividual, x3do:ROUTE ;
  x3do:hasParent :RouteHolder ;
  x3do:fromField 'orientation_changed' ;
  x3do:fromNode 'WhereSensor' ;
  x3do:toField 'set_orientation' ;
  x3do:toNode 'RecordingScript' .
:ROUTE_2_2_2_1_4_3 a owl:NamedIndividual, x3do:ROUTE ;
  x3do:hasParent :RouteHolder ;
  x3do:fromField 'recordingInProgress' ;
  x3do:fromNode 'RecordingScript' ;
  x3do:toField 'enabled' ;
  x3do:toNode 'WhereSensor' .
:Background_2_3 a owl:NamedIndividual, x3do:Background ;
  x3do:hasParent :Scene ;
  x3do:groundColor ( 0.2 0.4 0.2 ) ;
  x3do:skyColor ( 0.2 0.2 0.4 ) .
:Viewpoint_2_4 a owl:NamedIndividual, x3do:Viewpoint ;
  x3do:hasParent :Scene ;
  x3do:description 'Animated Viewpoint Recorder' ;
  x3do:position ( 0 0 14 ) .
:Anchor_2_5 a owl:NamedIndividual, x3do:Anchor ;
  x3do:hasParent :Scene ;
  x3do:hasChildren :Shape_2_5_1 ;
  x3do:description 'AnimatedViewpointRecorder Example' ;
  x3do:url '"AnimatedViewpointRecorderExample.x3d" "https://www.web3d.org/x3d/content/examples/Savage/Tools/Authoring/AnimatedViewpointRecorderExample.x3d" "AnimatedViewpointRecorderExample.wrl" "https://www.web3d.org/x3d/content/examples/Savage/Tools/Authoring/AnimatedViewpointRecorderExample.wrl"' .
:Shape_2_5_1 a owl:NamedIndividual, x3do:Shape ;
  x3do:hasParent :Anchor_2_5 ;
  x3do:hasGeometry :Text_2_5_1_1 ;
  x3do:hasAppearance :Appearance_2_5_1_2 .
:Text_2_5_1_1 a owl:NamedIndividual, x3do:Text ;
  x3do:hasParent :Shape_2_5_1 ;
  x3do:hasFontStyle :FontStyle_2_5_1_1_1 ;
  x3do:string '"AnimatedViewpointRecorderPrototype" "is a prototype definition file" "" "Click this text to see" "AnimatedViewpointRecorderExample"' .
:FontStyle_2_5_1_1_1 a owl:NamedIndividual, x3do:FontStyle ;
  x3do:hasParent :Text_2_5_1_1 ;
  x3do:justify '"MIDDLE" "MIDDLE"' ;
  x3do:size 1.2 .
:Appearance_2_5_1_2 a owl:NamedIndividual, x3do:Appearance ;
  x3do:hasParent :Shape_2_5_1 ;
  x3do:hasMaterial :Material_2_5_1_2_1 .
:Material_2_5_1_2_1 a owl:NamedIndividual, x3do:Material ;
  x3do:hasParent :Appearance_2_5_1_2 ;
  x3do:diffuseColor ( 0.6 0.8 0.4 ) .
