X3D Model Documentation: PushButtonPrototype.x3d

  1  <?xml version="1.0" encoding="UTF-8"?>
  2  <!DOCTYPE X3D PUBLIC "ISO//Web3D//DTD X3D 3.3//EN" "https://www.web3d.org/specifications/x3d-3.3.dtd">
  3  <X3D profile='Immersive' version='3.3'  xmlns:xsd='http://www.w3.org/2001/XMLSchema-instance' xsd:noNamespaceSchemaLocation='https://www.web3d.org/specifications/x3d-3.3.xsd'>
  4       <head>
  5            <meta name='title' content='PushButtonPrototype.x3d'/>
  6            <meta name='description' content='PushButton widget prototype declaration, inner button and outer shape can be round or square'/>
  7            <meta name='creator' content='Don Brutzman, Murat Onder and MV4205 class, Spring 2004 Quarter'/>
  8            <meta name='created' content='11 May 2004'/>
  9            <meta name='modified' content='2 January 2025'/>
 10            <meta name='reference' content='PushButtonExample.x3d'/>
 11            <meta name='identifier' content='https://savage.nps.edu/Savage/Tools/Animation/PushButtonPrototype.x3d'/>
 12            <meta name='generator' content='X3D-Edit 4.0, https://savage.nps.edu/X3D-Edit'/>
 13            <meta name='license' content='../../license.html'/>
 14       </head>
<!--

<!--
Event Graph ROUTE Table shows event connections.
-->
<!-- to top Index for DEF nodes: AnimationGroup, Blue, BooleanToggler, ClickAudio, Clock1, Clock2, DelayTimer, Green, InnerShapeSwitchRound, InnerShapeSwitchSquare, InnerShapeTransform, OuterShapeSwitchRound, OuterShapeSwitchSquare, OuterShapeTransform, PushButtonToggleMaterial, Red, SwitchAnimator1, SwitchAnimator2, Toucher

Index for ExternProtoDeclare definitions: MaterialToggle, TimeDelaySensor

Index for ProtoDeclare definition: PushButton
-->
 15       <Scene>
 16            <!-- Material Toggle Prototype is being used to be able to provide the color toggle. -->
 17            <WorldInfo title='PushButtonPrototype.x3d'/>
 18            <ExternProtoDeclare name='MaterialToggle' appinfo='MaterialToggle selects one of two different Material values'   url=' "MaterialTogglePrototype.x3d#MaterialToggle" "../../Tools/Animation/MaterialTogglePrototype.x3d#MaterialToggle" "https://savage.nps.edu/Savage/Tools/Animation/MaterialTogglePrototype.x3d#MaterialToggle" "../../Tools/Animation/MaterialTogglePrototype.wrl#MaterialToggle" "MaterialTogglePrototype.wrl#MaterialToggle" "https://savage.nps.edu/Savage/Tools/Animation/MaterialTogglePrototype.wrl#MaterialToggle" '>
 19                 <field name='defaultMaterial' type='SFNode' accessType='initializeOnly'
                appinfo='Material node that is enabled when toggle=false'/>
 20                 <field name='toggleMaterial' type='SFNode' accessType='initializeOnly'
                appinfo='Material node that is enabled when toggle=true'/>
 21                 <field name='set_toggle' type='SFBool' accessType='inputOnly'/>
 22                 <field name='set_defaultMaterial' type='SFNode' accessType='inputOnly'
                appinfo='provide replacement default Material node'/>
 23                 <field name='toggle' type='SFBool' accessType='initializeOnly'
                appinfo='whether to use DefaultMaterial or ToggleMaterial'/>
 24                 <field name='toggle_changed' type='SFBool' accessType='outputOnly'/>
 25                 <field name='set_toggleMaterial' type='SFNode' accessType='inputOnly'
                appinfo='provide replacement toggle Material node'/>
 26            </ExternProtoDeclare>
 27            <!-- TimeDelaySensor is being used to provide the time delay for button's color change delay -->
 28            <ExternProtoDeclare name='TimeDelaySensor' appinfo='TimeSensor functionality commences after delayInterval pause'   url=' "TimeDelaySensorPrototype.x3d#TimeDelaySensor" "../../Tools/Animation/TimeDelaySensorPrototype.x3d#TimeDelaySensor" "https://savage.nps.edu/Savage/Tools/Animation/TimeDelaySensorPrototype.x3d#TimeDelaySensor" "../../Tools/Animation/TimeDelaySensorPrototype.wrl#TimeDelaySensor" "TimeDelaySensorPrototype.wrl#TimeDelaySensor" "https://savage.nps.edu/Savage/Tools/Animation/TimeDelaySensorPrototype.wrl#TimeDelaySensor" '>
 29                 <field name='startTime' type='SFTime' accessType='inputOutput'
                appinfo='when current time exceeds startTime, isActive becomes true and sensor becomes active'/>
 30                 <field name='delayInterval' type='SFTime' accessType='inputOutput'
                appinfo='seconds'/>
 31                 <field name='delayCompleteTime' type='SFTime' accessType='outputOnly'/>
 32                 <field name='enabled' type='SFBool' accessType='inputOutput'
                appinfo='whether sensor is active'/>
 33                 <field name='description' type='SFString' accessType='inputOutput'
                appinfo='describe the purpose of this sensor'/>
 34                 <field name='traceEnabled' type='SFBool' accessType='initializeOnly'/>
 35            </ExternProtoDeclare>
 36            <!-- PushButton Prototype definition starts here.. -->
 37            <ProtoDeclare name='PushButton' appinfo='PushButton widget, inner button and outer shape can be round or square'>
 38                 <ProtoInterface>
 39                      <field name='traceEnabled' type='SFBool' value='true' accessType='initializeOnly'
                     appinfo='enables the console print-out in case of assigning wrong values default is true'/>
 40                      <field name='outerShapeMaterial' type='SFNode' accessType='initializeOnly'>
 41                           <Material DEF='Blue' diffuseColor='0 0 1'/>
 42                      </field>
 43                      <field name='defaultMaterial' type='SFNode' accessType='initializeOnly'
                     appinfo='defaultMaterial for inner pushbutton'>
 44                           <Material DEF='Red' diffuseColor='0.8 0.1 0'/>
 45                      </field>
 46                      <field name='toggleMaterial' type='SFNode' accessType='initializeOnly'
                     appinfo='toggleMaterial for inner pushbutton'>
 47                           <Material DEF='Green' diffuseColor='0.1 0.8 0'/>
 48                      </field>
 49                      <field name='value_changed' type='SFBool' accessType='outputOnly'
                     appinfo='boolean output of button selection'/>
 50                      <!-- switch values for combination of the button, default: round-round -->
 51                      <field name='outerSwitchStyle' type='SFString' value='round' accessType='initializeOnly'
                     appinfo='allowed values: round or square default is round'/>
 52                      <field name='set_outerSwitchStyle' type='SFString' accessType='inputOnly'
                     appinfo='allowed values: round or square default is round'/>
 53                      <field name='innerSwitchStyle' type='SFString' value='round' accessType='initializeOnly'
                     appinfo='allowed values: round square. default is round'/>
 54                      <field name='set_innerSwitchStyle' type='SFString' accessType='inputOnly'
                     appinfo='allowed values: round square. default is round'/>
 55                      <field name='delayInterval' type='SFTime' value='.5' accessType='initializeOnly'
                     appinfo='time delay for button movement so for color change default is 0.5 sec.'/>
 56                 </ProtoInterface>
 57                 <ProtoBody>
 58                      <Group>
 59 
                         <!-- ROUTE information for DelayTimer node:  [from Toucher.touchTime to startTime ] [from delayCompleteTime to Clock2.set_startTime ] -->
                         <ProtoInstance name='TimeDelaySensor' DEF='DelayTimer'>
 60                                <IS>
 61                                     <connect nodeField='delayInterval' protoField='delayInterval'/>
 62                                </IS>
 63                           </ProtoInstance>
 64                           <Transform DEF='OuterShapeTransform' rotation='1 0 0 1.57'>
 65 
                              <!-- Switch OuterShapeSwitchRound is a DEF node that has 1 USE node: USE_1 -->
                              <Switch DEF='OuterShapeSwitchRound' whichChoice='-1'>
 66                                     <Shape>
 67                                          <Cylinder height='.12' radius='.5'/>
 68                                          <Appearance>
 69                                               <IS>
 70                                                    <connect nodeField='material' protoField='outerShapeMaterial'/>
 71                                               </IS>
 72                                          </Appearance>
 73                                     </Shape>
 74                                </Switch>
 75 
                              <!-- Switch OuterShapeSwitchSquare is a DEF node that has 1 USE node: USE_1 -->
                              <Switch DEF='OuterShapeSwitchSquare' whichChoice='-1'>
 76                                     <Shape>
 77                                          <Box size='1 .12 1'/>
 78                                          <Appearance>
 79                                               <IS>
 80                                                    <connect nodeField='material' protoField='outerShapeMaterial'/>
 81                                               </IS>
 82                                          </Appearance>
 83                                     </Shape>
 84                                </Switch>
 85                           </Transform>
 86 
                         <!-- ROUTE information for InnerShapeTransform node:  [from SwitchAnimator1.value_changed to set_translation ] [from SwitchAnimator2.value_changed to set_translation ] -->
                         <Transform DEF='InnerShapeTransform' rotation='1 0 0 1.57' translation='0 0 .1'>
 87 
                              <!-- Switch InnerShapeSwitchRound is a DEF node that has 1 USE node: USE_1 -->
                              <Switch DEF='InnerShapeSwitchRound' whichChoice='-1'>
 88                                     <Shape>
 89                                          <Cylinder height='.12' radius='.35'/>
 90                                          <Appearance>
 91 
                                             <!-- ProtoInstance PushButtonToggleMaterial is a DEF node that has 1 USE node: USE_1
                                             <!-- ROUTE information for PushButtonToggleMaterial node:  [from BooleanToggler.toggle_changed to set_toggle ] -->
                                             <ProtoInstance name='MaterialToggle' DEF='PushButtonToggleMaterial' containerField='material'>
 92                                                    <IS>
 93                                                         <connect nodeField='defaultMaterial' protoField='defaultMaterial'/>
 94                                                         <connect nodeField='toggleMaterial' protoField='toggleMaterial'/>
 95                                                    </IS>
 96                                               </ProtoInstance>
 97                                          </Appearance>
 98                                     </Shape>
 99                                </Switch>
100 
                              <!-- Switch InnerShapeSwitchSquare is a DEF node that has 1 USE node: USE_1 -->
                              <Switch DEF='InnerShapeSwitchSquare' whichChoice='-1'>
101                                     <Shape>
102                                          <Box size='.57 .12 .57'/>
103                                          <Appearance>
104                                               <ProtoInstance USE='PushButtonToggleMaterial' containerField='material'/>
105                                          </Appearance>
106                                     </Shape>
107                                </Switch>
108                                <Sound>
109 
                                   <!-- ROUTE information for ClickAudio node:  [from Toucher.touchTime to set_startTime ] -->
                                   <AudioClip DEF='ClickAudio' description='click sound'   url=' "click.wav" "https://savage.nps.edu/Savage/Tools/Animation/click.wav" '/>
110                                </Sound>
111 
                              <!-- ROUTE information for Toucher node:  [from touchTime to ClickAudio.set_startTime ] [from touchTime to DelayTimer.startTime ] [from touchTime to Clock1.set_startTime ] -->
                              <TouchSensor DEF='Toucher' description='touch to push the button'/>
112                                < ROUTE  fromNode='Toucher' fromField='touchTime' toNode='ClickAudio' toField='set_startTime'/>
113                                < ROUTE  fromNode='Toucher' fromField='touchTime' toNode='DelayTimer' toField='startTime'/>
114                           </Transform>
115                           <Group DEF='AnimationGroup'>
116                                <!-- ============================= -->
117 
                              <!-- TimeSensor Clock1 is a DEF node that has 1 USE node: USE_1
                              <!-- ROUTE information for Clock1 node:  [from Toucher.touchTime to set_startTime ] [from fraction_changed to SwitchAnimator1.set_fraction ] -->
                              <TimeSensor DEF='Clock1'/>
118                                < ROUTE  fromNode='Toucher' fromField='touchTime' toNode='Clock1' toField='set_startTime'/>
119                                <!-- ============================= -->
120 
                              <!-- ROUTE information for SwitchAnimator1 node:  [from Clock1.fraction_changed to set_fraction ] [from value_changed to InnerShapeTransform.set_translation ] -->
                              <PositionInterpolator DEF='SwitchAnimator1' key='0 .5 1' keyValue='0 0 .1 0 0 .05 0 0 .007'/>
121                                < ROUTE  fromNode='Clock1' fromField='fraction_changed' toNode='SwitchAnimator1' toField='set_fraction'/>
122                                < ROUTE  fromNode='SwitchAnimator1' fromField='value_changed' toNode='InnerShapeTransform' toField='set_translation'/>
123                                <!-- ============================= -->
124 
                              <!-- TimeSensor Clock2 is a DEF node that has 1 USE node: USE_1
                              <!-- ROUTE information for Clock2 node:  [from DelayTimer.delayCompleteTime to set_startTime ] [from fraction_changed to SwitchAnimator2.set_fraction ] [from isActive to BooleanToggler.set_boolean ] -->
                              <TimeSensor DEF='Clock2'/>
125                                < ROUTE  fromNode='DelayTimer' fromField='delayCompleteTime' toNode='Clock2' toField='set_startTime'/>
126                                <!-- ============================= -->
127 
                              <!-- ROUTE information for SwitchAnimator2 node:  [from Clock2.fraction_changed to set_fraction ] [from value_changed to InnerShapeTransform.set_translation ] -->
                              <PositionInterpolator DEF='SwitchAnimator2' key='0 .5 1' keyValue='0 0 .007 0 0 .05 0 0 .1'/>
128                                < ROUTE  fromNode='Clock2' fromField='fraction_changed' toNode='SwitchAnimator2' toField='set_fraction'/>
129                                < ROUTE  fromNode='SwitchAnimator2' fromField='value_changed' toNode='InnerShapeTransform' toField='set_translation'/>
130                                <!-- ============================= -->
131 
                              <!-- ROUTE information for BooleanToggler node:  [from Clock2.isActive to set_boolean ] [from toggle_changed to PushButtonToggleMaterial.set_toggle ] -->
                              <BooleanToggle DEF='BooleanToggler' containerField='children'>
132                                     <IS>
133                                          <connect nodeField='toggle_changed' protoField='value_changed'/>
134                                     </IS>
135                                </BooleanToggle>
136                                < ROUTE  fromNode='Clock2' fromField='isActive' toNode='BooleanToggler' toField='set_boolean'/>
137                                < ROUTE  fromNode='BooleanToggler' fromField='toggle_changed' toNode='PushButtonToggleMaterial' toField='set_toggle'/>
138                           </Group>
139                      </Group>
140                      <!-- Only first node in ProtoBody is rendered -->
141                      <Script directOutput='true'>
142                           <field name='traceEnabled' type='SFBool' accessType='initializeOnly'/>
143                           <field name='delayInterval' type='SFTime' accessType='initializeOnly'/>
144                           <field name='outerSwitchStyle' type='SFString' accessType='initializeOnly'
                          appinfo='allowed values: round square. default is round'/>
145                           <field name='set_outerSwitchStyle' type='SFString' accessType='inputOnly'
                          appinfo='allowed values: round square. default is round'/>
146                           <field name='innerSwitchStyle' type='SFString' accessType='initializeOnly'
                          appinfo='allowed values: round square. default is round'/>
147                           <field name='set_innerSwitchStyle' type='SFString' accessType='inputOnly'
                          appinfo='allowed values: round square. default is round'/>
148                           <field name='switchOuterRound' type='SFNode' accessType='initializeOnly'>
149                                <Switch USE='OuterShapeSwitchRound'/>
150                           </field>
151                           <field name='switchOuterSquare' type='SFNode' accessType='initializeOnly'>
152                                <Switch USE='OuterShapeSwitchSquare'/>
153                           </field>
154                           <field name='switchInnerRound' type='SFNode' accessType='initializeOnly'>
155                                <Switch USE='InnerShapeSwitchRound'/>
156                           </field>
157                           <field name='switchInnerSquare' type='SFNode' accessType='initializeOnly'>
158                                <Switch USE='InnerShapeSwitchSquare'/>
159                           </field>
160                           <field name='clock1' type='SFNode' accessType='initializeOnly'>
161                                <TimeSensor USE='Clock1'/>
162                           </field>
163                           <field name='clock2' type='SFNode' accessType='initializeOnly'>
164                                <TimeSensor USE='Clock2'/>
165                           </field>
166                           <IS>
167                                <connect nodeField='traceEnabled' protoField='traceEnabled'/>
168                                <connect nodeField='outerSwitchStyle' protoField='outerSwitchStyle'/>
169                                <connect nodeField='set_outerSwitchStyle' protoField='set_outerSwitchStyle'/>
170                                <connect nodeField='innerSwitchStyle' protoField='innerSwitchStyle'/>
171                                <connect nodeField='set_innerSwitchStyle' protoField='set_innerSwitchStyle'/>
172                                <connect nodeField='delayInterval' protoField='delayInterval'/>
173                           </IS>
  <![CDATA[
          
ecmascript:

function initialize()
{
   clock1.cycleInterval = delayInterval;
   clock2.cycleInterval = delayInterval;

   set_innerSwitchStyle(innerSwitchStyle);
   set_outerSwitchStyle(outerSwitchStyle);
}

function set_innerSwitchStyle(stringValue)
{
   if (stringValue != null)  //security check
   {
      if (stringValue != 'round')
      {
         if (stringValue != 'square')
         {
            printError(stringValue, 'inner');
            innerSwitchStyle = 'round';
         }
         else
         {
            innerSwitchStyle = stringValue;
         }
      }
      else
      {
          innerSwitchStyle = stringValue;
      }
   }

   if      (innerSwitchStyle == 'round')
   {
        switchInnerSquare.whichChoice = -1;
         switchInnerRound.whichChoice =  0;
   }
   else // (innerSwitchStyle == 'square')
   {
        switchInnerSquare.whichChoice =  0;
         switchInnerRound.whichChoice = -1;
   }
}

function set_outerSwitchStyle(stringValue)
{
   if (stringValue != null)    //security check
   {
      if (stringValue != 'round')
      {
         if (stringValue != 'square')
         {
            printError(stringValue, 'outer');
            outerSwitchStyle = 'round';
         }
         else
         {
            outerSwitchStyle = stringValue;
         }
      }
      else
      {
          outerSwitchStyle = stringValue;
      }
   }

   if      (outerSwitchStyle == 'round')
   {
        switchOuterSquare.whichChoice = -1;
         switchOuterRound.whichChoice =  0;
   }
   else // (outerSwitchStyle == 'square')
   {
        switchOuterSquare.whichChoice =  0;
         switchOuterRound.whichChoice = -1;
   }
}

function printError(s1, s2)
{
    if (traceEnabled)
    {
        Browser.println ('\nAllowed values are [round] and [square].');
        Browser.println ('Assigned value [' + s1 + '] (The values are case/whitespace-sensitive).');
        Browser.println ('Default value  [round] will be used for [' + s2 + 'SwitchStyle].');
    }
}

        
]]>
175                      </Script>
176                 </ProtoBody>
177            </ProtoDeclare>
178            <!-- ==================== -->
179            <Anchor description='PushButtonExample' parameter='"target=_blank"'   url=' "PushButtonExample.x3d" "../../Tools/Animation/PushButtonExample.x3d" "https://savage.nps.edu/Savage/Tools/Animation/PushButtonExample.x3d" "../../Tools/Animation/PushButtonExample.wrl" "PushButtonExample.wrl" "https://savage.nps.edu/Savage/Tools/Animation/PushButtonExample.wrl" '>
180                 <Shape>
181                      <Text string='"PushButtonPrototype" "defines a prototype" "" "Click text to see example scene" "" "You can define 4 different buttons" "with the combination of round and square" "(default is round-round)"'>
182                           <FontStyle justify='"MIDDLE" "MIDDLE"' size='0.7'/>
183                      </Text>
184                      <Appearance>
185                           <Material diffuseColor='.8 .7 0.2'/>
186                      </Appearance>
187                 </Shape>
188            </Anchor>
189       </Scene>
190  </X3D>
<!--

<!--
Event Graph ROUTE Table shows event connections.
-->
<!-- to top Index for DEF nodes: AnimationGroup, Blue, BooleanToggler, ClickAudio, Clock1, Clock2, DelayTimer, Green, InnerShapeSwitchRound, InnerShapeSwitchSquare, InnerShapeTransform, OuterShapeSwitchRound, OuterShapeSwitchSquare, OuterShapeTransform, PushButtonToggleMaterial, Red, SwitchAnimator1, SwitchAnimator2, Toucher

Index for ExternProtoDeclare definitions: MaterialToggle, TimeDelaySensor

Index for ProtoDeclare definition: PushButton
-->
X3D Tooltips element index: Anchor, Appearance, AudioClip, BooleanToggle, Box, connect, Cylinder, ExternProtoDeclare, field, FontStyle, Group, head, IS, Material, meta, PositionInterpolator, ProtoBody, ProtoDeclare, ProtoInstance, ProtoInterface, ROUTE, Scene, Script, Shape, Sound, Switch, Text, TimeSensor, TouchSensor, Transform, WorldInfo, X3D, plus documentation for accessType definitions, type definitions, XML data types, and field types

Event Graph ROUTE Table entries with 10 ROUTE connections 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.

Toucher
TouchSensor
touchTime
SFTime
→
ROUTE
event to
(1)
ClickAudio
AudioClip
set_startTime
SFTime
Toucher
TouchSensor
touchTime
SFTime
→
ROUTE
event to
(1)
DelayTimer
ProtoInstance
startTime
SFTime
then
 
 
 
DelayTimer
ProtoInstance
delayCompleteTime
SFTime
→
ROUTE
event to
(2)
Clock2
TimeSensor
set_startTime
SFTime
then
 
 
 
Clock2
TimeSensor
fraction_changed
SFFloat
→
ROUTE
event to
(3)
SwitchAnimator2
PositionInterpolator
set_fraction
SFFloat
then
 
 
 
SwitchAnimator2
PositionInterpolator
value_changed
SFVec3f
→
ROUTE
event to
(4)
InnerShapeTransform
Transform
set_translation
SFVec3f
  then
 
 
 
Clock2
TimeSensor
isActive
SFBool
→
ROUTE
event to
(3)
BooleanToggler
BooleanToggle
set_boolean
SFBool
then
 
 
 
BooleanToggler
BooleanToggle
toggle_changed
SFBool
→
ROUTE
event to
(4)
PushButtonToggleMaterial
ProtoInstance
set_toggle
SFBool
Toucher
TouchSensor
touchTime
SFTime
→
ROUTE
event to
(1)
Clock1
TimeSensor
set_startTime
SFTime
then
 
 
 
Clock1
TimeSensor
fraction_changed
SFFloat
→
ROUTE
event to
(2)
SwitchAnimator1
PositionInterpolator
set_fraction
SFFloat
then
 
 
 
SwitchAnimator1
PositionInterpolator
value_changed
SFVec3f
→
ROUTE
event to
(3)
InnerShapeTransform
Transform
set_translation
SFVec3f

line 141
Script
No ROUTE connection found for output events from this node.
Contains SFNode fields with direct access to another node. 

      DelayTimer
ProtoInstance
delayCompleteTime
SFTime
→
ROUTE
event to
(1)
Clock2
TimeSensor
set_startTime
SFTime
then
 
 
 
Clock2
TimeSensor
fraction_changed
SFFloat
→
ROUTE
event to
(2)
SwitchAnimator2
PositionInterpolator
set_fraction
SFFloat
then
 
 
 
SwitchAnimator2
PositionInterpolator
value_changed
SFVec3f
→
ROUTE
event to
(3)
InnerShapeTransform
Transform
set_translation
SFVec3f
  then
 
 
 
Clock2
TimeSensor
isActive
SFBool
→
ROUTE
event to
(2)
BooleanToggler
BooleanToggle
set_boolean
SFBool
then
 
 
 
BooleanToggler
BooleanToggle
toggle_changed
SFBool
→
ROUTE
event to
(3)
PushButtonToggleMaterial
ProtoInstance
set_toggle
SFBool

      PushButtonToggleMaterial
ProtoInstance
MaterialToggle
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. 

line 179
Anchor
description='PushButtonExample' 
User-interaction hint for this node. 

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/PushButtonPrototypeIndex.html -->
<!-- Version control at
https://gitlab.nps.edu/Savage/Savage/Tools/Animation/PushButtonPrototype.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. -->