<?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='Immersive' version='3.0' xmlns:xsd='http://www.w3.org/2001/XMLSchema-instance' xsd:noNamespaceSchemaLocation=' http://www.web3d.org/specifications/x3d-3.0.xsd '>
<![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
]]>
<!-- Tag color codes: <Node DEF='idName' attribute='value'/> <Prototype name='ProtoName'> <field name='fieldName'/> </Prototype> -->