X3D Model Documentation: BathymetryGeneratorViaExtrusionPrototype.x3d

  1  <?xml version="1.0" encoding="UTF-8"?>
  2 
<!DOCTYPE X3D PUBLIC "ISO//Web3D//DTD X3D 3.0//EN" "https://www.web3d.org/specifications/x3d-3.0.dtd">
  3  <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'>
  4       <head>
  5            <meta name='title' content='BathymetryGeneratorViaExtrusionPrototype.x3d'/>
  6            <meta name='description' content='This prototype generates bathymetry based on the input data, and uses Extrusion as the output geometry (with some problems as a result).'/>
  7            <meta name='creator' content='Jane Wu'/>
  8            <meta name='created' content='8 January 2002'/>
  9            <meta name='modified' content='28 November 2019'/>
 10            <meta name='subject' content='bathymetry'/>
 11            <meta name='reference' content='https://www.web3d.org/technicalinfo/specifications/vrml97/part1/nodesRef.html#Extrusion'/>
 12            <meta name='identifier' content='https://savage.nps.edu/Savage/Tools/Animation/BathymetryGeneratorViaExtrusionPrototype.x3d'/>
 13            <meta name='generator' content='X3D-Edit 3.2, https://savage.nps.edu/X3D-Edit'/>
 14            <meta name='license' content='../../license.html'/>
 15       </head>
<!--

<!--
Event Graph ROUTE Table shows event connections.
-->
<!-- to top Index for DEF nodes: Bathymetry, BathymetryScript

Index for Viewpoint node: Viewpoint_1

Index for ProtoDeclare definition: BathymetryGenerator
-->
 16       <Scene>
 17            <WorldInfo title='BathymetryGeneratorViaExtrusionPrototype.x3d'/>
 18            <ProtoDeclare name='BathymetryGenerator'>
 19                 <ProtoInterface>
 20                      <field name='positionArray' type='MFVec3f' value='0 0 0 10 -4 0 25 -6 0 30 -8 5 38 -15 5 45 -18 5 55 -22 5 60 -25 15 60 -27 22 55 -30 35 48 -35 35 35 -35 35 25 -45 35 20 -55 35 15 -70 35 3 -70 35 -5 -72 40 -5 -75 50 0 -80 55 15 -75 55 30 -70 55 35 -60 55 40 -50 55 50 -34 55 65 -23 70' accessType='initializeOnly'/>
 21                      <field name='timeArray' type='MFTime' value='1 3 6 8 10 12 14 15 17 18 23 28 35 37 39 43 45 47 48 53 58 60 61 65 70' accessType='initializeOnly'
                     appinfo='for future development'/>
 22                      <field name='colorSchemeDepthRangeArray' type='MFVec2f' value='0 -10 -10 -20 -20 -30 -30 -40 -40 -50 -50 -60 -60 -70 -70 -999999' accessType='initializeOnly'/>
 23                      <field name='colorSchemeColorArray' type='MFColor' value='1 1 0.2 0.6 1 1 0 1 1 0.2 0.6 0.2 1 0 1 0.56 0 0.32 0.2 0.3 0.7 0 0 1' accessType='initializeOnly'/>
 24                      <field name='beamWidth' type='SFFloat' value='2' accessType='initializeOnly'/>
 25                      <field name='surfaceTransparency' type='SFFloat' value='0.25' accessType='initializeOnly'/>
 26                      <field name='traceEnabled' type='SFBool' value='false' accessType='initializeOnly'/>
 27                 </ProtoInterface>
 28                 <ProtoBody>
 29                      <Group>
 30 
                         <!-- ROUTE information for Bathymetry node:  [from BathymetryScript.bathyNodes to addChildren ] -->
                         <Transform DEF='Bathymetry'/>
 31 
                         <!-- ROUTE information for BathymetryScript node:  [from bathyNodes to Bathymetry.addChildren ] -->
                         <Script DEF='BathymetryScript' directOutput='true'>
 32                                <field name='positionArray' type='MFVec3f' accessType='initializeOnly'/>
 33                                <field name='timeArray' type='MFTime' accessType='initializeOnly'/>
 34                                <field name='colorSchemeDepthRangeArray' type='MFVec2f' accessType='initializeOnly'/>
 35                                <field name='colorSchemeColorArray' type='MFColor' accessType='initializeOnly'/>
 36                                <field name='beamWidth' type='SFFloat' accessType='initializeOnly'/>
 37                                <field name='transparency' type='SFFloat' accessType='initializeOnly'/>
 38                                <field name='spine' type='MFVec3f' value='0 0 0 0 1 0' accessType='initializeOnly'/>
 39                                <field name='scale' type='MFVec2f' value='1 1' accessType='initializeOnly'/>
 40                                <field name='orientation' type='MFRotation' value='0 0 1 0' accessType='initializeOnly'/>
 41                                <field name='bathyColor' type='SFColor' value='1 1 1' accessType='initializeOnly'/>
 42                                <field name='bathyNodes' type='MFNode' accessType='outputOnly'/>
 43                                <field name='traceEnabled' type='SFBool' accessType='initializeOnly'/>
 44                                <field name='coordinate' type='SFVec3f' value='0 0 0' accessType='initializeOnly'/>
 45                                <field name='previousPosition' type='SFVec3f' value='0 0 0' accessType='initializeOnly'/>
 46                                <field name='position' type='SFVec3f' value='0 0 0' accessType='initializeOnly'/>
 47                                <field name='bathyNodeIndex' type='SFInt32' value='0' accessType='initializeOnly'/>
 48                                <IS>
 49                                     <connect nodeField='positionArray' protoField='positionArray'/>
 50                                     <connect nodeField='timeArray' protoField='timeArray'/>
 51                                     <connect nodeField='colorSchemeDepthRangeArray' protoField='colorSchemeDepthRangeArray'/>
 52                                     <connect nodeField='colorSchemeColorArray' protoField='colorSchemeColorArray'/>
 53                                     <connect nodeField='beamWidth' protoField='beamWidth'/>
 54                                     <connect nodeField='transparency' protoField='surfaceTransparency'/>
 55                                     <connect nodeField='traceEnabled' protoField='traceEnabled'/>
 56                                </IS>
  <![CDATA[
            
ecmascript:

function initialize()
{
	bathyNodeIndex = 0;

	spineIndex = 0;
	position = positionArray[0];
	spine[spineIndex] = new SFVec3f(position.x, 0, position.z);
	scale[spineIndex] = new SFVec2f(1, Math.abs(position.y));
	spineIndex++;

	previousPosition = new SFVec3f(position.x, position.y, position.z);
	//Determine the initial depth range
	for (j = 0; j < colorSchemeDepthRangeArray.length; j++)
	{
		if (position.y >= colorSchemeDepthRangeArray[j].y)
			break;
	}
	currentDepthRangeIndex = j;

	for (i = 1; i < positionArray.length; i++)
	{			
		if (previousPosition.y == colorSchemeDepthRangeArray[currentDepthRangeIndex].y &&
		    positionArray[i].y != colorSchemeDepthRangeArray[currentDepthRangeIndex].y)
			terminateExtrusionSegmentWithCurrentPosition(currentDepthRangeIndex);

		//Update new position
		position = positionArray[i];

		//Determine the correct depth range
		if (position.y <= previousPosition.y)
		{
			for (j = currentDepthRangeIndex; j < colorSchemeDepthRangeArray.length; j++)
			{
				if (position.y >= colorSchemeDepthRangeArray[j].y)
					break;

				if (previousPosition.y != colorSchemeDepthRangeArray[currentDepthRangeIndex].y)
					terminateExtrusionSegmentWithDepthRangeBoundary(currentDepthRangeIndex);
			}
			currentDepthRangeIndex = j;
		}
		else
		{
			for (j = currentDepthRangeIndex; j > -1; j--)
			{
				if (position.y < colorSchemeDepthRangeArray[j-1].y)
					break;

				if (position.y > colorSchemeDepthRangeArray[j-1].y)
					terminateExtrusionSegmentWithDepthRangeBoundary(j-1);
			}
			currentDepthRangeIndex = j;
		}

		spine[spineIndex] = new SFVec3f(position.x, 0, position.z);
		scale[spineIndex] = new SFVec2f(1, Math.abs(position.y));
		spineIndex++;

		previousPosition = new SFVec3f(position.x, position.y, position.z);
	}
	terminateExtrusionSegmentWithCurrentPosition(currentDepthRangeIndex);
}

function terminateExtrusionSegmentWithDepthRangeBoundary(index)
{
	depthRange = colorSchemeDepthRangeArray[index];

	findCoordinate(previousPosition.x, position.x, previousPosition.y, position.y, depthRange.y);
	xPrime = coordinate;
	findCoordinate(previousPosition.z, position.z, previousPosition.y, position.y, depthRange.y);
	zPrime = coordinate;
	spine[spineIndex] = new SFVec3f(xPrime, 0, zPrime);
	scale[spineIndex] = new SFVec2f(1, Math.abs(depthRange.y));

	if (scale[scale.length-2].y > scale[scale.length-1].y)
		color = colorSchemeColorArray[index+1];
	else
		color = colorSchemeColorArray[index];

	createExtrusionShape(spine, scale, color);

	//Reset values to start the next extrustion segment
	spineIndex = 0;
	resetSpine();
	resetScale();

	//Update the current segment end as the start of the next segment
	spine[spineIndex] = new SFVec3f(xPrime, 0, zPrime);
	scale[spineIndex] = new SFVec2f(1, Math.abs(depthRange.y));
	spineIndex++;	
}

function terminateExtrusionSegmentWithCurrentPosition(index)
{
	if (scale[scale.length-1].y != Math.abs(colorSchemeDepthRangeArray[index].y))
		index--;

	if (scale[scale.length-2].y > scale[scale.length-1].y)
		color = colorSchemeColorArray[index+1];
	else
		color = colorSchemeColorArray[index];

	createExtrusionShape(spine, scale, color);

	//Reset values to start the next extrustion segment
	spineIndex = 0;
	resetSpine();
	resetScale();

	//Update the current segment end as the start of the next segment
	spine[spineIndex] = new SFVec3f(position.x, 0, position.z);
	scale[spineIndex] = new SFVec2f(1, Math.abs(position.y));
	spineIndex++;

	//Update the previousPosition
	previousPosition = new SFVec3f(position.x, position.y, position.z);
}

function findCoordinate(x1, x2, y1, y2, yPrime)
{
	coordinate = ((x1 - x2) / (y1 - y2)) * yPrime + ((x2*y1 - x1*y2) / (y1 - y2));
}

function createExtrusionShape(spine, scale, color)
{
	determineOrientation(spine);
	tracePrint('An extrusion is created whose spine is: ' + spine);
	tracePrint('and scale is: ' + scale);
	tracePrint('orientation is: ' + orientation);
	tracePrint('color is: ' + color);
	alwaysPrint('number of spine points is: ' + spine.length);
	alwaysPrint('orientation is: ' + orientation);

	//Build the VRML string
	extrusionSyntax  = 'Shape {\n';
	extrusionSyntax += '   appearance Appearance {' + '\n';
	extrusionSyntax += '      material Material {' + '\n';
	extrusionSyntax += '         diffuseColor ' + color + '\n';
	extrusionSyntax += '         transparency ' + transparency + '\n';
	extrusionSyntax += '      }' + '\n';
	extrusionSyntax += '   }' + '\n';
	extrusionSyntax += '   geometry Extrusion {' + '\n';
	extrusionSyntax += '      crossSection [' + (beamWidth/(-2)) + ', 1, ' + (beamWidth/2) + ', 1, ' + (beamWidth/(-2)) + ', 1]' + '\n';
	extrusionSyntax += '      scale ' + scale + '\n';
	extrusionSyntax += '      spine ' + spine + '\n';
	extrusionSyntax += '      orientation ' + orientation + '\n';
	extrusionSyntax += '      creaseAngle 1.57' + '\n';
	extrusionSyntax += '   }' + '\n';
	extrusionSyntax += '}';

	//Create Extrusion shape
	tracePrint (extrusionSyntax);
	bathySegment = new SFNode(extrusionSyntax);

	bathyNodes[bathyNodeIndex] = bathySegment;
	bathyNodeIndex++;
}

function determineOrientation(spine)
{
   previousZAxis = null;
   orientation = new MFRotation();
   //Special cases
   if (spine.length == 2)
   {
      if (spine[0].z == spine[1].z)
      {
         if (spine[0].x <= spine[1].x) //positive x direction
            orientation[0] = orientation[1] = new SFRotation(0, 1, 0, 1.57);
         else //negative x direction
            orientation[0] = orientation[1] = new SFRotation(0, 1, 0, -1.57);
      }
      else
      {
         if (spine[0].x == spine[1].x) //parallet to the z axis
            orientation[0] = orientation[1] = new SFRotation(0, 1, 0, 0);
         else
         {
            angleRadian = Math.atan((spine[0].x- spine[1].x) / (spine[0].z - spine[1].z));
//          angleRadian = Math.atan2((spine[0].x- spine[1].x), (spine[0].z - spine[1].z));
            
            orientation[0] = orientation[1] = new SFRotation(0, 1, 0, angleRadian);
         }
      }
      return;
   }

   for (n = 0; n < spine.length; n++)
   {
      //If spine is not closed, the Z axis used for the first spine point is the same as the Z axis for spine[1].
      //The Z axis used for the last spine point is the same as the Z axis for spine[spine.length - 2].   
      if (n == 0)
         si = 1;
      else if (n == (spine.length - 1))
         si = spine.length - 2;
      else
         si = n;

      zAxis = (spine[si+1].subtract(spine[si])).cross((spine[si-1].subtract(spine[si])));

      while (zAxis.x == 0 && zAxis.y == 0 && zAxis.z == 0)
      {
         if (previousZAxis == null)
         {
            ++si;
            if (si == (spine.length - 1)) //The entire spine is collinear
            {
               zAxis = new SFVec3f(1, 0, 0);
               break;
            }

            zAxis = (spine[si+1].subtract(spine[si])).cross((spine[si-1].subtract(spine[si])));
         }
         else
            zAxis = new SFVec3f(previousZAxis.x, previousZAxis.y, previousZAxis.z);
      }

      adjustedZAxis = zAxis;
      if (n == 0)
         previousZAxis = zAxis;
      else
      {
         dotProduct = zAxis.dot(previousZAxis);
         if (dotProduct < 0)
            adjustedZAxis = new SFVec3f(zAxis.multiply(-1).x, zAxis.multiply(-1).y, zAxis.multiply(-1).z);

         previousZAxis = adjustedZAxis;
      }
      
      zAxisNormalized = adjustedZAxis.normalize();
      theta = Math.acos(zAxisNormalized.dot(new SFVec3f(0, -1, 0)));
      if (spine[1].x < spine[0].x)
         orientation[n] = new SFRotation(0, -1, 0, theta);
      else
         orientation[n] = new SFRotation(0, 1, 0, theta);
   }
if (theta == 0)
   Browser.println ('rotation angle = ' + theta);
else if (theta > 1.57 && theta < 3.14)
   Browser.println ('rotation angle = ' + theta);
else if (theta > 3.14)
   Browser.println ('rotation angle = ' + theta);
}

function resetSpine()
{
	spine = new MFVec3f();
}

function resetScale()
{
	scale = new MFVec2f();
}

function tracePrint(string)
{
	if (traceEnabled)
		Browser.println ('[BathymetryGenerator] ' + string);
}

function alwaysPrint(string)
{
	Browser.println ('[BathymetryGenerator] ' + string);
}

          
]]>
 58                           </Script>
 59                           < ROUTE  fromNode='BathymetryScript' fromField='bathyNodes' toNode='Bathymetry' toField='addChildren'/>
 60                           <Shape>
 61                                <Extrusion/>
 62                           </Shape>
 63                      </Group>
 64                 </ProtoBody>
 65            </ProtoDeclare>
 66            <Viewpoint description='MainView' position='0 -50 200'/>
 67            <ProtoInstance name='BathymetryGenerator'>
 68                 <fieldValue name='positionArray' value='0 0 0 10 -4 0 25 -6 0 30 -8 5 38 -15 5 45 -18 5 55 -22 5 60 -25 15 60 -27 22 55 -30 35 48 -35 35 35 -35 35 25 -45 35 20 -55 35 15 -70 35 3 -70 35 -5 -72 40 -5 -75 50 0 -80 55 15 -75 55 30 -70 55 35 -60 55 40 -50 55 50 -34 55 65 -23 70'/>
 69                 <fieldValue name='surfaceTransparency' value='0.25'/>
 70                 <fieldValue name='traceEnabled' value='true'/>
 71            </ProtoInstance>
 72       </Scene>
 73  </X3D>
<!--

<!--
Event Graph ROUTE Table shows event connections.
-->
<!-- to top Index for DEF nodes: Bathymetry, BathymetryScript

Index for Viewpoint node: Viewpoint_1

Index for ProtoDeclare definition: BathymetryGenerator
-->
X3D Tooltips element index: connect, Extrusion, field, fieldValue, Group, head, IS, meta, ProtoBody, ProtoDeclare, ProtoInstance, ProtoInterface, ROUTE, Scene, Script, Shape, Transform, Viewpoint, WorldInfo, X3D, plus documentation for accessType definitions, type definitions, XML data types, and field types

Event Graph ROUTE Table with 1 ROUTE connection 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.

BathymetryScript
Script
bathyNodes
MFNode
→
ROUTE
event to
(1)
Bathymetry
Transform
addChildren
MFNode

line 67
ProtoInstance
BathymetryGenerator
No ROUTE connection found for output events from this node.
This ProtoInstance contains SFNode/MFNode fieldValue declarations with
direct access to other nodes, and thus has potential to produce run-time animation. 

Additional guidance on X3D animation can be found in the 10-Step Animation Design Process and Event Tracing hint sheets. Have fun with X3D! 😀

-->
<!-- Online at
https://savage.nps.edu/Savage/Tools/Animation/BathymetryGeneratorViaExtrusionPrototypeIndex.html -->
<!-- Version control at
https://gitlab.nps.edu/Savage/Savage/Tools/Animation/BathymetryGeneratorViaExtrusionPrototype.x3d -->

<!-- Color-coding 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)
    <ProtoInstance name='ProtoName'> <field name='fieldName'/> </ProtoInstance> -->

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