<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE X3D PUBLIC "ISO//Web3D//DTD X3D 3.0//EN"   "http://www.web3d.org/specifications/x3d-3.0.dtd">
<X3D profile='Immersiveversion='3.0xmlns:xsd='http://www.w3.org/2001/XMLSchema-instance' xsd:noNamespaceSchemaLocation=' http://www.web3d.org/specifications/x3d-3.0.xsd '>
<head>
<meta name='titlecontent='ExtrusionCrossSectionPrototype.x3d'/>
<meta name='creatorcontent='Don Brutzman'/>
<meta name='createdcontent='22 November 2000'/>
<meta name='modifiedcontent='3 April 2005'/>
<meta name='descriptioncontent='Shape prototype for Extrusion node that also draws spine line plus oriented, scaled cross sections at each spine point. Includes Torus example.'/>
<meta name='hintcontent='Utilize ExtrusionCrossSection ProtoInstance as if it is a Shape node (with containerField="children") rather than an Extrusion node (with containerField="geometry" and embedded Materials) under a Shape.'/>
<meta name='examplecontent=' http://www.web3d.org/x3d/content/examples/Basic/course/ExtrusionCrossSectionExampleShip.wrl '/>
<meta name='referencecontent='VRML 97 Specification, 6.18 Extrusion'/>
<meta name='referencecontent=' http://www.web3d.org/technicalinfo/specifications/vrml97/part1/nodesRef.html#Extrusion '/>
<meta name='imagecontent='VRML 97 Specification, Figure 6.6'/>
<meta name='imagecontent=' http://www.web3d.org/technicalinfo/specifications/vrml97/Images/Extrusion.gif '/>
<meta name='referencecontent=' http://realism.com/Web3D/Examples/Extrusion/_frame.html '/>
<meta name='referencecontent=' http://www.cs.brown.edu/~gss/vrml/drawpoly.html '/>
<meta name='referencecontent='ExtrusionXj3dGoldberg1999.java'/>
<meta name='referencecontent=' http://web3d.metrolink.com/cgi-bin/cvsweb.cgi/x3d/src/com/sun/j3d/loaders/vrml97/impl/Attic/Extrusion.java '/>
<meta name='identifiercontent=' http://www.web3d.org/x3d/content/examples/Basic/course/ExtrusionCrossSectionPrototype.x3d '/>
<meta name='generatorcontent='X3D-Edit 3.2, https://savage.nps.edu/X3D-Edit'/>
<meta name='licensecontent='../license.html'/>
</head>
<!--

Index for ProtoDeclare definition: ExtrusionCrossSection
Index for DEF nodes: CrossSectionAppearance, CrossSectionFaces, CrossSectionFacesCoordinates, CrossSectionScript, EnclosingExtrusion, ExternalExtrusion, ExtrusionAppearance, FontCenter, InternalCrossSections, LineColorNode, Spine, SpineCoordinates, SpineLine
-->
<Scene>
<ProtoDeclare name='ExtrusionCrossSection'>
<ProtoInterface>
<field name='nametype='SFStringaccessType='initializeOnly'/>
<field name='crossSectiontype='MFVec2fvalue='1 1 1 -1 -1 -1 -1 1 1 1accessType='initializeOnly'/>
<field name='spinetype='MFVec3fvalue='0 0 0 0 1 0accessType='initializeOnly'/>
<field name='scaletype='MFVec2fvalue='1 1accessType='initializeOnly'/>
<field name='orientationtype='MFRotationvalue='0 0 1 0accessType='initializeOnly'/>
<field name='beginCaptype='SFBoolvalue='falseaccessType='initializeOnly'/>
<field name='endCaptype='SFBoolvalue='falseaccessType='initializeOnly'/>
<field name='ccwtype='SFBoolvalue='trueaccessType='initializeOnly'/>
<field name='convextype='SFBoolvalue='trueaccessType='initializeOnly'/>
<field name='creaseAngletype='SFFloatvalue='0accessType='initializeOnly'/>
<field name='solidtype='SFBoolvalue='trueaccessType='initializeOnly'/>
<field name='lineColortype='MFColorvalue='0 0.9 0.8accessType='inputOutput'/>
<field name='crossSectionMaterialtype='SFNodeaccessType='inputOutput'>
<!-- default material -->
<Material diffuseColor='0.2 0.8 0.3'/>
</field>
<field name='extrusionMaterialtype='SFNodeaccessType='inputOutput'>
<!-- default material -->
<Material diffuseColor='0.3 0.3 0.3transparency='0.7'/>
</field>
<field name='extrusionImageTexturetype='SFNodeaccessType='initializeOnly'>
<!-- no default texture -->
</field>
<field name='extrusionTextureTransformtype='SFNodeaccessType='initializeOnly'>
<!-- no default textureTransform -->
</field>
<!-- run-time eventIn Extrusion-modification interfaces for VRML97 compatibility -->
<field name='set_crossSectiontype='MFVec2faccessType='inputOnly'/>
<field name='set_spinetype='MFVec3faccessType='inputOnly'/>
<field name='set_scaletype='MFVec2faccessType='inputOnly'/>
<field name='set_orientationtype='MFRotationaccessType='inputOnly'/>
<field name='traceEnabledtype='SFBoolvalue='falseaccessType='initializeOnly'/>
</ProtoInterface>
<ProtoBody>
<Group>
<Shape DEF='Spine'>
<!-- SpineLine ROUTE:  [from CrossSectionScript.spineIndex to set_coordIndex ] -->
<IndexedLineSet DEF='SpineLinecolorPerVertex='false'>
<Coordinate DEF='SpineCoordinates'>
<IS>
<connect nodeField='pointprotoField='spine'/>
</IS>
</Coordinate>
<Color DEF='LineColorNode'>
<IS>
<connect nodeField='colorprotoField='lineColor'/>
</IS>
</Color>
</IndexedLineSet>
</Shape>
<Shape DEF='InternalCrossSections'>
<!-- CrossSectionFaces ROUTE:  [from CrossSectionScript.facesIndex to set_coordIndex ] -->
<IndexedFaceSet DEF='CrossSectionFacesconvex='falsesolid='false'>
<!-- CrossSectionFacesCoordinates ROUTE:  [from CrossSectionScript.facePoints to point ] -->
<Coordinate DEF='CrossSectionFacesCoordinates'/>
</IndexedFaceSet>
<Appearance DEF='CrossSectionAppearance'>
<IS>
<connect nodeField='materialprotoField='crossSectionMaterial'/>
</IS>
</Appearance>
</Shape>
<Shape DEF='ExternalExtrusion'>
<Extrusion DEF='EnclosingExtrusion'>
<IS>
<connect nodeField='crossSectionprotoField='crossSection'/>
<connect nodeField='spineprotoField='spine'/>
<connect nodeField='scaleprotoField='scale'/>
<connect nodeField='orientationprotoField='orientation'/>
<connect nodeField='set_crossSectionprotoField='set_crossSection'/>
<connect nodeField='set_spineprotoField='set_spine'/>
<connect nodeField='set_scaleprotoField='set_scale'/>
<connect nodeField='set_orientationprotoField='set_orientation'/>
<connect nodeField='beginCapprotoField='beginCap'/>
<connect nodeField='endCapprotoField='endCap'/>
<connect nodeField='ccwprotoField='ccw'/>
<connect nodeField='convexprotoField='convex'/>
<connect nodeField='creaseAngleprotoField='creaseAngle'/>
<connect nodeField='solidprotoField='solid'/>
</IS>
</Extrusion>
<Appearance DEF='ExtrusionAppearance'>
<IS>
<connect nodeField='materialprotoField='extrusionMaterial'/>
<connect nodeField='textureprotoField='extrusionImageTexture'/>
<connect nodeField='textureTransformprotoField='extrusionTextureTransform'/>
</IS>
</Appearance>
</Shape>
</Group>
<!-- CrossSectionScript ROUTEs:  [from spineIndex to SpineLine.set_coordIndex ] [from facesIndex to CrossSectionFaces.set_coordIndex ] [from facePoints to CrossSectionFacesCoordinates.point ] -->
<Script DEF='CrossSectionScript'>
<field name='nametype='SFStringaccessType='initializeOnly'/>
<field name='crossSectiontype='MFVec2faccessType='initializeOnly'/>
<field name='spinetype='MFVec3faccessType='initializeOnly'/>
<field name='scaletype='MFVec2faccessType='initializeOnly'/>
<field name='orientationtype='MFRotationaccessType='initializeOnly'/>
<field name='set_crossSectiontype='MFVec2faccessType='inputOnly'/>
<field name='set_spinetype='MFVec3faccessType='inputOnly'/>
<field name='set_scaletype='MFVec2faccessType='inputOnly'/>
<field name='set_orientationtype='MFRotationaccessType='inputOnly'/>
<field name='spineIndextype='MFInt32accessType='outputOnly'/>
<field name='facesIndextype='MFInt32accessType='outputOnly'/>
<field name='facePointstype='MFVec3faccessType='outputOnly'/>
<field name='traceEnabledtype='SFBoolaccessType='initializeOnly'/>
<IS>
<connect nodeField='nameprotoField='name'/>
<connect nodeField='crossSectionprotoField='crossSection'/>
<connect nodeField='spineprotoField='spine'/>
<connect nodeField='scaleprotoField='scale'/>
<connect nodeField='orientationprotoField='orientation'/>
<connect nodeField='set_crossSectionprotoField='set_crossSection'/>
<connect nodeField='set_spineprotoField='set_spine'/>
<connect nodeField='set_scaleprotoField='set_scale'/>
<connect nodeField='set_orientationprotoField='set_orientation'/>
<connect nodeField='traceEnabledprotoField='traceEnabled'/>
</IS>
<![CDATA[
          ecmascript:

function tracePrint (value)
{
	if (traceEnabled == true)
	{
		if (!name || (name==''))
			Browser.print ('[ExtrusionCrossSection]' + value);
		else	Browser.print ('[ExtrusionCrossSection' + name + ']' + value);
	}
}
function forcePrint (value)
{
		if (!name || (name==''))
			Browser.print ('[ExtrusionCrossSection]' + value);
		else	Browser.print ('[ExtrusionCrossSection' + name + ']' + value);
}
function initialize ()
{
  tracePrint ('==============================================================');
  tracePrint ('===== initialize() - calculate extrusion representations =====');
  errorFound = false;
  if (spine.length < 2)
  {
  	errorFound = true;
	forcePrint ('[Error] spine.length' + spine.length + ' < 2');
  }
  // single scale value is applied uniformly across all spine points
  if ((spine.length != scale.length) && (scale.length > 1))
  {
  	errorFound = true;
	forcePrint ('[Error] spine.length' + spine.length + ' != scale.length' + scale.length);
  }
  // single orientation value is applied uniformly across all spine points
  if ((spine.length != orientation.length) && (orientation.length > 1))
  {
  	errorFound = true;
	forcePrint ('[Error] spine.length' + spine.length + ' != orientation.length' + orientation.length);
  }
  if (errorFound) return;

  tracePrint ('spine.length=' + spine.length);
  tracePrint ('spine=' + spine);
  for ( sp = 0; sp <= spine.length - 1; sp++ ) {
	spineIndex[sp] =  sp;
  }
  tracePrint ('crossSection.length=' + crossSection.length);
  tracePrint ('crossSection=' + crossSection);

  tracePrint ('scale.length=' + scale.length);
  if (scale.length == 0)
  {
	scale[0] = new SFVec2f(1, 1);
	tracePrint ('reset scale=' + scale);
  }
  else tracePrint ('scale=' + scale);

  tracePrint ('orientation.length=' + orientation.length);
  if (orientation.length == 0)
  {
	orientation[0] = new SFRotation(0, 0, 1, 0);
	tracePrint ('reset orientation=' + orientation);
  }
  else tracePrint ('orientation=' + orientation);

  SCPxAxis = new SFVec3f ();
  SCPyAxis = new SFVec3f ();
  SCPzAxis = new SFVec3f ();

  n = spine.length;
  for ( sp = 0; sp < n ; sp++ ) {

	tracePrint ('=== sp spineIndex==' + sp + ' ===');
	// so far, these crossSection faces are perpendicular to x axis.
	// must align each crossSection with spine-aligned cross-section plane (SCP),
	// which bisects incoming and outgoing spine tangents at each control point

	// apparently all three SCP axes are defined in the specification for thoroughness,
	// but only one will be needed to actually rotate the SCP from the default x-axis perpendicular.

	if ((sp == 0) && (spine[n-1] == spine[0]))	// first, closed
	{
		tracePrint ('SCP axis:  first [sp==0], closed');
		SCPyAxis = (spine[1].subtract(spine[n-2])).normalize();
		SCPzAxis = (spine[1].subtract(spine[0])).cross(spine[n-2].subtract(spine[0])).normalize();
	}
	else if (sp == 0)				// first, open
	{
		tracePrint ('SCP axis:  first [sp==0], open');
		SCPyAxis = (spine[1].subtract(spine[0])).normalize();
		SCPzAxis = (spine[2].subtract(spine[1])).cross(spine[0].subtract(spine[1]));
	}
//	else if ((sp == 1) && (spine.length = 2))	// 2-element spine:  second is last, same SCP as first
//	{
//		tracePrint ('2-element spine:  second is last, same SCP as first');
//		SCPyAxis = (spine[1].subtract(spine[0])).normalize();
//		SCPzAxis = (spine[2].subtract(spine[1])).cross(spine[0].subtract(spine[1]));
//	}
	else if ((sp == n-1) && (spine[n-1] == spine[0])) // last, closed
	{
		tracePrint ('SCP axis:  last [sp==' + (n-1) + '], closed');
		SCPyAxis = (spine[1].subtract(spine[n-2])).normalize();
		SCPzAxis = (spine[1].subtract(spine[0])).cross(spine[n-2].subtract(spine[0])).normalize();
	}
	else if ((sp == n-1) && (spine.length > 2))	// last, open
	{
		tracePrint ('SCP axis:  last [sp==' + (n-1) + '], open');
		SCPyAxis = (spine[n-1].subtract(spine[n-2])).normalize();
		SCPzAxis = (spine[n-1].subtract(spine[n-2])).cross(spine[n-3].subtract(spine[n-2])).normalize();
	}
	else if (sp < n - 1)				// intermediate
	{
		tracePrint ('SCP axis:  intermediate');
		SCPyAxis = (spine[sp+1].subtract(spine[sp-1])).normalize();
		SCPzAxis = (spine[sp+1].subtract(spine[sp])).cross(spine[sp-1].subtract(spine[sp])).normalize();
	}
	else Browser.print ('!!! SCP axis:  error, unexpected spine case');

	if ((sp > 0) && (spine.length > 2))
	{
		// prevent small changes in spine segment angles from flipping cross-section 180 degrees
		if (SCPzAxis.dot(SCPzAxisPrevious) < 0)
		{
			SCPzAxis = SCPzAxis.multiply (-1);
			tracePrint ('SCPzAxis.multiply (-1), sp=' + sp);
		}
	}
	// zero SCP axis means collinear spines, so must compute one axis from plane of acceptable values.
	// thus use prior value for smooth continuity.
	if ((SCPyAxis.x==0) && (SCPyAxis.y==0) && (SCPyAxis.z==0))
	{
	   if (sp == 0)
	   {
		tracePrint ('*** indeterminate SCPyAxis=(0, 0, 0), rotating 90 degrees from spine');
		firstSpine = new SFVec3f((spine[1].subtract(spine[0])).normalize());
		if ((Math.abs(firstSpine.x)!=1) || (firstSpine.y!=0) || (firstSpine.z!=0))
			SCPyAxis = (new SFRotation (1, 0, 0, Math.PI/2)).multVec(firstSpine);
		else
			SCPyAxis = (new SFRotation (0, 1, 0, Math.PI/2)).multVec(firstSpine);
	   }
	   else
	   {
		tracePrint ('*** indeterminate SCPyAxis=(0, 0, 0), reset to previous value');
		SCPyAxis = SCPyAxisPrevious;
	   }
	}
	if ((SCPzAxis.x==0) && (SCPzAxis.y==0) && (SCPzAxis.z==0))
	{
	   if (sp == 0)
	   {
		tracePrint ('*** indeterminate SCPzAxis=(0, 0, 0), rotating 90 degrees from SCPyAxis');
		if ((Math.abs(SCPyAxis.x)!=1) || (SCPyAxis.y!=0) || (SCPyAxis.z!=0))
			SCPzAxis = (new SFRotation (1, 0, 0, Math.PI/2)).multVec(SCPyAxis);
		else
			SCPzAxis = (new SFRotation (0, 1, 0, Math.PI/2)).multVec(SCPyAxis);
	   }
	   else
	   {
		tracePrint ('*** indeterminate SCPzAxis=(0, 0, 0), reset to previous value');
		SCPzAxis = SCPzAxisPrevious;
	   }
	}
	SCPxAxis = SCPyAxis.cross(SCPzAxis);

	SCPyAxisPrevious = SCPyAxis;
	SCPzAxisPrevious = SCPzAxis;
	tracePrint ('SCPxAxis=' + SCPxAxis);
	tracePrint ('SCPyAxis=' + SCPyAxis);
	tracePrint ('SCPzAxis=' + SCPzAxis);
	if ((SCPxAxis.x==0) && (SCPxAxis.y==0) && (SCPxAxis.z==0))
	{
		tracePrint ('*** indeterminate SCPxAxis=(0, 0, 0), unexpected error');
	}

	startAxis = new SFVec3f (1, 0, 0); // should be -1, with corresponding fix later?
	if (Math.abs(startAxis.dot(SCPyAxis)) != 1)
	{
		// spec calls local negative x-axis the y-axis of SCP (i.e. SCP perpendicular)
		// we want to rotate default y axis of current cross section to align with SCPyAxis
		SCPnewAxisRotation = new SFRotation (startAxis, SCPyAxis);
		yRotated = true;
		tracePrint ('[a] yRotated=' + yRotated + ', SCPnewAxisRotation=' + SCPnewAxisRotation);
	}
	else if (startAxis.dot(SCPyAxis) == 1) // aligned, same direction
	{
		// dot-product==1 means already aligned with y-axis
		SCPnewAxisRotation = new SFRotation (1, 0, 0, 0);
		tracePrint ('*** SCPyAxis' + SCPyAxis + ' colinear with startAxis' + startAxis + ' same direction');
		yRotated = false;
		tracePrint ('[b] yRotated=' + yRotated + ', SCPnewAxisRotation=' + SCPnewAxisRotation);
	}
	else // aligned, opposite direction
	{
		// dot-product==1 means already aligned with y-axis
		SCPnewAxisRotation = new SFRotation (0, 0, 1, -Math.PI);
		tracePrint ('*** SCPyAxis' + SCPyAxis + ' colinear with startAxis' + startAxis + ' opposite direction');
		yRotated = false;
		tracePrint ('[c] yRotated=' + yRotated + ', SCPnewAxisRotation=' + SCPnewAxisRotation);
	}
	// generate crossSection points + repeated initial point + [-1] flag for each loop
	for ( i = 0; i <= crossSection.length - 1; i++ )
	{
	   j = sp * (crossSection.length + 2) + i;
	   facesIndex[j] = j;

	   offset = new SFVec3f();
	   if (scale.length > 1)
	   {	// not sure why crossSection coefficient is negative, maybe an SCP factor?
		offset[0] =   0;				// x
		offset[1] = - crossSection[i][0]*scale[sp][0];	// y
		offset[2] = - crossSection[i][1]*scale[sp][1] * (-1);	// z
	   }
	   else
	   {	// not sure why crossSection coefficient is negative, maybe an SCP factor?
		offset[0] =   0;				// x
		offset[1] = - crossSection[i][0]*scale[0][0];	// y
		offset[2] = - crossSection[i][1]*scale[0][1] * (-1);	// z
	   }

	   if (orientation.length > 1)
	   {
		orientationFix = new SFRotation(
			orientation[sp].y,
			orientation[sp].x * (-1),
			orientation[sp].z,
			orientation[sp].angle);
		// rotate this point by fixed orientation value
		orientedOffset = orientationFix.multVec (offset);
		tracePrint ('[e] orientedOffset =' + orientedOffset);
	   }
	   else	// single orientation to apply throughout
	   {
		orientationFix = new SFRotation(
			orientation[0].y,
			orientation[0].x * (-1),
			orientation[0].z,
			orientation[0].angle);
		// rotate this point by fixed orientation value
		orientedOffset = orientationFix.multVec (offset);
		tracePrint ('[f] orientedOffset =' + orientedOffset);
	   }
	   // rotate this point by SCPnewAxisRotation correction
	   SCPoffset = SCPnewAxisRotation.multVec (orientedOffset);

	   facePoints[j][0] =  spine[sp][0] + SCPoffset[0];	// x
	   facePoints[j][1] =  spine[sp][1] + SCPoffset[1];	// y
	   facePoints[j][2] =  spine[sp][2] + SCPoffset[2];	// z

//	   tracePrint ('offset=' + offset + ', orientedOffset=' + orientedOffset + ', SCPoffset=' + SCPoffset);
  	}
	// Computation algorithm example provided next to illustrate steps.
	// crossSection.length=9  ==>  point sequence as follows:
	//  0..8,  initial point 0 repeat at 9, terminator 10,
	// 11..19, initial point 11 repeat at 20, terminator 21,
	// 22..30, initial point 22 repeat at 31, terminator 32, etc.
	j = (sp + 1) * (crossSection.length + 2) - 2;
	// first close polygon with repeated initial point, then [-1] terminator
  	facesIndex[j]   = facesIndex[j - crossSection.length];
	facesIndex[j+1] = -1;
	facePoints[j]  [0] = 0; // unused, corresponds to repeated polygon point
	facePoints[j]  [1] = 0; // unused
	facePoints[j]  [2] = 0; // unused
	facePoints[j+1][0] = 0; // unused, corresponds to [-1] end-of-face flag
	facePoints[j+1][1] = 0; // unused
	facePoints[j+1][2] = 0; // unused
  }
  tracePrint ('facesIndex.length=' + facesIndex.length);
  tracePrint ('facePoints.length=' + facePoints.length);
  tracePrint ('facesIndex=' + facesIndex);
  tracePrint ('facePoints=' + facePoints);
}
// run-time event updates:  recalculate everything
function set_crossSection (value, timeStamp)
{
	tracePrint ('set_crossSection=' + value);
	crossSection = value;
	initialize ();
}
function set_orientation  (value, timeStamp)
{
	tracePrint ('set_orientation=' + value);
	orientation = value;
	initialize ();
}
function set_scale        (value, timeStamp)
{
	tracePrint ('set_scale=' + value);
	scale = value;
	initialize ();
}
function set_spine        (value, timeStamp)
{
	tracePrint ('set_spine=' + value);
	spine = value;
	initialize ();
}
//	ExtrusionCrossSectionPrototype
        
]]>
</Script>
<ROUTE fromNode='CrossSectionScriptfromField='spineIndextoNode='SpineLinetoField='set_coordIndex'/>
<ROUTE fromNode='CrossSectionScriptfromField='facesIndextoNode='CrossSectionFacestoField='set_coordIndex'/>
<ROUTE fromNode='CrossSectionScriptfromField='facePointstoNode='CrossSectionFacesCoordinatestoField='point'/>
</ProtoBody>
</ProtoDeclare>
<!-- ==================== -->
<!-- Example instance adapted from Figure15.12Torus.x3d -->
<!-- http://www.web3d.org/x3d/content/examples/Basic/Vrml2.0Sourcebook/Chapter15-Extrusion/Figure15.12Torus.x3d -->
<!-- Redirection text in case a user examines this PROTO file via a 3D browser: -->
<Group>
<Transform translation='0 1.5 0'>
<Shape>
<Appearance>
<Material diffuseColor='0 1 1emissiveColor='0 1 1'/>
</Appearance>
<Text string='"ExtrusionCrossSectionPrototype" "is a Prototype definition scene" "" "Example scenes using this node:"solid='true'>
<FontStyle justify='"MIDDLE" "MIDDLE"size='0.9'/>
</Text>
</Shape>
</Transform>
<Transform translation='-2.5 -2 0'>
<Anchor description='Touch text for Torus exampleparameter='target=_blank'
  url=' "ExtrusionCrossSectionExampleTorus.wrl" "/www.web3d.org/x3d/content/examples/Basic/course/ExtrusionCrossSectionExampleTorus.wrl" "http://www.web3d.org/x3d/content/examples/Basic/course/ExtrusionCrossSectionExampleTorus.wrl" "ExtrusionCrossSectionExampleTorus.x3d" "/www.web3d.org/x3d/content/examples/Basic/course/ExtrusionCrossSectionExampleTorus.x3d" "http://www.web3d.org/x3d/content/examples/Basic/course/ExtrusionCrossSectionExampleTorus.x3d" '>
<Shape>
<Appearance>
<Material diffuseColor='1 0 1transparency='0.2'/>
</Appearance>
<Text string='Torussolid='true'>
<FontStyle DEF='FontCenterjustify='"MIDDLE" "MIDDLE"size='1.2style='BOLD'/>
</Text>
</Shape>
</Anchor>
</Transform>
<Transform translation='2.5 -2 0'> </Transform>
</Group>
</Scene>
</X3D>
<!--

Index for ProtoDeclare definition: ExtrusionCrossSection
Index for DEF nodes: CrossSectionAppearance, CrossSectionFaces, CrossSectionFacesCoordinates, CrossSectionScript, EnclosingExtrusion, ExternalExtrusion, ExtrusionAppearance, FontCenter, InternalCrossSections, LineColorNode, Spine, SpineCoordinates, SpineLine
-->

<!-- Tag color codes: <Node DEF='idName' attribute='value'/> <Prototype name='ProtoName'> <field name='fieldName'/> </Prototype> -->