<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE X3D PUBLIC "ISO//Web3D//DTD X3D 3.0//EN" "https://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='https://www.web3d.org/specifications/x3d-3.0.xsd'>
  <head>
    <meta content='ArbitraryAxisCylinderSensorPrototype.x3d' name='title'/>
    <meta content='Modified CylinderSensor oriented about an arbitrary axis, relative to peer/child geometry that remains oriented to its original axis. Originally authored by Don Brutzman&apos;s MV4204 class, with modifications by Maj James Breitinger USMC to include min/max angles of rotation and object center. Motivation: Modify example shown by Chapter 9 Figure 7 to build a PROTO for a CylinderSensor oriented about an arbitrary axis.' name='description'/>
    <meta content='ArbitraryAxisCylinderSensor operates on its children, NOT on its peers. This variation is necessary in order to accomplish the desired Transform rotation to a new orientation axis.' name='info'/>
    <meta content='Don Brutzman' name='creator'/>
    <meta content='1 October 1998' name='created'/>
    <meta content='28 November 2019' name='modified'/>
    <meta content='ArbitraryAxisCylinderSensorExamples.x3d' name='reference'/>
    <meta content='ArbitraryAxisCylinderSensorPrototypeOriginal.wrl' name='reference'/>
    <meta content='https://savage.nps.edu/Savage/Tools/Animation/ArbitraryAxisCylinderSensorPrototype.x3d' name='identifier'/>
    <meta content='The VRML 2.0 Sourcebook (Copyright 1997 By Andrea L. Ames, David R. Nadeau, and John L. Moreland)' name='reference'/>
    <meta content='X3D-Edit 3.2, https://savage.nps.edu/X3D-Edit' name='generator'/>
    <meta content='../../license.html' name='license'/>
  </head>
  <Scene>
    <WorldInfo title='ArbitraryAxisCylinderSensorPrototype.x3d'/>
    <ProtoDeclare appinfo='Modified CylinderSensor with children nodes oriented about an arbitrary axis. Warning: ArbitraryAxisCylinderSensor affects children, not peers.' name='ArbitraryAxisCylinderSensor'>
      <ProtoInterface>
        <!-- All default fields and events of a regular CylinderSensor are exposed without modification. -->
        <!-- Transparent viewing-assist geometry shows orientation and cylindrical mapping of mouse movements by sensor. Since viewCylinderSensorShape connects to a Script, it is a initialize-time field instead of a run-time exposedField. -->
        <field accessType='initializeOnly' appinfo='shifted axis of rotation from local vertical, default 1 0 0 0' name='shiftRotationAxis' type='SFRotation' value='1 0 0 0'/>
        <field accessType='initializeOnly' appinfo='local center for axis of rotation, default 0 0 0' name='center' type='SFVec3f' value='0 0 0'/>
        <field accessType='initializeOnly' appinfo='whether to show visualization shape to show orientation and cylindrical mapping of mouse movements by sensor, default true' name='showCylinderSensorShape' type='SFBool' value='true'/>
        <field accessType='inputOutput' appinfo='scale for visualization shape, default 1 1 1' name='scaleCylinderSensorShape' type='SFVec3f' value='1 1 1'/>
        <field accessType='inputOutput' appinfo='color for visualization shape, default 0.9 0.9 0.4' name='colorCylinderSensorShape' type='SFColor' value='0.9 0.9 0.4'/>
        <field accessType='inputOutput' appinfo='transparency for visualization shape' name='transparencyCylinderSensorShape' type='SFFloat' value='0.8'/>
        <field accessType='inputOutput' appinfo='children nodes affected by ArbitraryAxisCylinderSensor' name='children' type='MFNode'>
          <!-- initialization nodes (if any) go here -->
        </field>
        <field accessType='inputOutput' appinfo='determines whether previous offset values are remembered/accumulated, default true' name='autoOffset' type='SFBool' value='true'/>
        <field accessType='inputOutput' appinfo='Text tooltip displayed for user interaction' name='description' type='SFString' value='Select and drag to rotate ArbitraryAxisCylinderSensor'/>
        <field accessType='inputOutput' appinfo='diskAngle 0 forces disk-like behavior, diskAngle 1.57 (90 degrees) forces cylinder-like behavior, default 0.262, range [0,pi/2]' name='diskAngle' type='SFFloat' value='0.262'/>
        <field accessType='inputOutput' appinfo='enables/disables node operation, default true' name='enabled' type='SFBool' value='true'/>
        <field accessType='inputOutput' appinfo='clamps rotation_changed events, default 0, range [-2pi,2pi]' name='minAngle' type='SFFloat' value='0'/>
        <field accessType='inputOutput' appinfo='clamps rotation_changed events, default -1, range [-2pi,2pi]' name='maxAngle' type='SFFloat' value='-1'/>
        <field accessType='initializeOnly' appinfo='sends event and remembers last value sensed, default 0, range (-infinity,infinity)' name='offset' type='SFFloat' value='0'/>
        <field accessType='outputOnly' appinfo='output event isActive=true when primary mouse button is pressed, output event isActive=false when released.' name='isActive' type='SFBool'/>
        <field accessType='outputOnly' appinfo='rotation_changed events equal sum of relative bearing changes plus offset value about Y-axis in local coordinate system' name='rotation_changed' type='SFRotation'/>
        <field accessType='outputOnly' appinfo='trackPoint_changed events give intersection point of bearing with sensor&apos;s virtual geometry' name='trackPoint_changed' type='SFVec3f'/>
      </ProtoInterface>
      <ProtoBody>
        <!-- Prototype body follows. First rotate local frame about center to axis of interest, then perform the rotation about the desired center. -->
        <Transform DEF='ArbitraryAxisTransform'>
          <IS>
            <connect nodeField='rotation' protoField='shiftRotationAxis'/>
            <connect nodeField='center' protoField='center'/>
          </IS>
          <CylinderSensor DEF='RotatedCylinderSensor'>
            <IS>
              <connect nodeField='autoOffset' protoField='autoOffset'/>
              <connect nodeField='description' protoField='description'/>
              <connect nodeField='diskAngle' protoField='diskAngle'/>
              <connect nodeField='enabled' protoField='enabled'/>
              <connect nodeField='minAngle' protoField='minAngle'/>
              <connect nodeField='maxAngle' protoField='maxAngle'/>
              <connect nodeField='offset' protoField='offset'/>
              <connect nodeField='isActive' protoField='isActive'/>
              <connect nodeField='rotation_changed' protoField='rotation_changed'/>
              <connect nodeField='trackPoint_changed' protoField='trackPoint_changed'/>
            </IS>
          </CylinderSensor>
          <!-- CylinderSensorRotationTransform rotation value is overridden by RotatedCylinderSensor, so leave it alone! -->
          <Transform DEF='CylinderSensorRotationTransform'>
            <!-- ROUTEd rotation to RestorationTransform negates (and offsets) arbitrary-axis rotation in RestorationTransform, so children geometry is returned to original orientation. -->
            <!-- Perform the rotation about the same center. -->
            <Transform DEF='RestorationTransform'>
              <IS>
                <connect nodeField='center' protoField='center'/>
                <connect nodeField='children' protoField='children'/>
              </IS>
            </Transform>
            <!-- Prototype children field finally appears above... -->
            <Switch DEF='ScaleSensorSwitch' whichChoice='0'>
              <Transform DEF='ScaleSensorShape'>
                <IS>
                  <connect nodeField='scale' protoField='scaleCylinderSensorShape'/>
                </IS>
                <Shape>
                  <Cylinder height='2.5' radius='0.1'/>
                  <Appearance DEF='SensorShapeAppearance'>
                    <Material DEF='SensorShapeMaterial'>
                      <IS>
                        <connect nodeField='diffuseColor' protoField='colorCylinderSensorShape'/>
                        <connect nodeField='transparency' protoField='transparencyCylinderSensorShape'/>
                      </IS>
                    </Material>
                  </Appearance>
                </Shape>
                <Shape>
                  <Cylinder height='0.2' radius='1.5'/>
                  <Appearance USE='SensorShapeAppearance'/>
                </Shape>
              </Transform>
            </Switch>
          </Transform>
          <Script DEF='NegationScript' directOutput='true'>
            <field accessType='initializeOnly' name='shiftRotationAxis' type='SFRotation'/>
            <field accessType='initializeOnly' name='offset' type='SFFloat'/>
            <field accessType='outputOnly' name='rotationRestore' type='SFRotation'/>
            <field accessType='initializeOnly' name='CylinderSensorRotationTransform' type='SFNode'>
              <Transform USE='CylinderSensorRotationTransform'/>
            </field>
            <!-- local variables -->
            <field accessType='initializeOnly' name='rotationOffset' type='SFRotation' value='0 1 0 0'/>
            <field accessType='initializeOnly' name='rotationIntermediate' type='SFRotation' value='0 1 0 0'/>
            <IS>
              <connect nodeField='offset' protoField='offset'/>
              <connect nodeField='shiftRotationAxis' protoField='shiftRotationAxis'/>
            </IS>
            <![CDATA[
ecmascript:

function initialize () {
	rotationOffset = new SFRotation (0, 1, 0, offset);  // establish initial angle
	CylinderSensorRotationTransform.rotation =
	   CylinderSensorRotationTransform.rotation.multiply (rotationOffset);
	rotationIntermediate = shiftRotationAxis;
	rotationIntermediate = rotationIntermediate.inverse ();
	rotationRestore = rotationIntermediate;
	Browser.println ('   rotationOffset=' +    rotationOffset.toString());
	Browser.println ('shiftRotationAxis=' + shiftRotationAxis.toString());
	Browser.println ('  rotationRestore=' +   rotationRestore.toString());
}
]]>
          </Script>
          <Script DEF='HideSensorShapeScript'>
            <field accessType='initializeOnly' name='showCylinderSensorShape' type='SFBool'/>
            <field accessType='outputOnly' name='choiceScaleSensor' type='SFInt32'/>
            <IS>
              <connect nodeField='showCylinderSensorShape' protoField='showCylinderSensorShape'/>
            </IS>
            <![CDATA[
ecmascript:

function initialize () {
	if (showCylinderSensorShape == true)
		choiceScaleSensor =  0;
	else	choiceScaleSensor = -1;
}
]]>
          </Script>
        </Transform>
        <ROUTE fromField='rotation_changed' fromNode='RotatedCylinderSensor' toField='set_rotation' toNode='CylinderSensorRotationTransform'/>
        <ROUTE fromField='rotationRestore' fromNode='NegationScript' toField='rotation' toNode='RestorationTransform'/>
        <ROUTE fromField='choiceScaleSensor' fromNode='HideSensorShapeScript' toField='whichChoice' toNode='ScaleSensorSwitch'/>
        <!-- Add any ROUTEs here, going from Script to other nodes in ProtoBody -->
      </ProtoBody>
    </ProtoDeclare>
    <!-- Example use of this prototype is in ArbitraryAxisCylinderSensorExample.x3d, .wrl -->
    <Viewpoint description='click message to view example' position='0 0 8'/>
    <!-- Redirection text in case a user examines this PROTO file via a 3D browser: -->
    <Anchor description='Touch text for example' url='"ArbitraryAxisCylinderSensorExamples.x3d" "https://savage.nps.edu/Savage/Tools/Animation/ArbitraryAxisCylinderSensorExamples.x3d" "ArbitraryAxisCylinderSensorExamples.wrl" "https://savage.nps.edu/Savage/Tools/Animation/ArbitraryAxisCylinderSensorExamples.wrl"'>
      <Shape>
        <Appearance>
          <Material diffuseColor='0 1 1' emissiveColor='0 1 1'/>
        </Appearance>
        <Text string='"ArbitraryAxisCylinderSensorPrototype" "is a Prototype (PROTO) definition file." "" "To see an example scene using this new node" "click this text and view" "ArbitraryAxisCylinderSensorExample"'>
          <FontStyle justify='"MIDDLE" "MIDDLE"' size='0.5'/>
        </Text>
      </Shape>
      <Shape DEF='SelectionAssist'>
        <Box size='9 4 .001'/>
        <Appearance>
          <Material transparency='0.9'/>
        </Appearance>
      </Shape>
    </Anchor>
  </Scene>
</X3D>