<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE X3D PUBLIC "ISO//Web3D//DTD X3D 3.3//EN" "https://www.web3d.org/specifications/x3d-3.3.dtd">
<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'>
  <head>
    <meta content='PushButtonPrototype.x3d' name='title'/>
    <meta content='PushButton widget prototype declaration, inner button and outer shape can be round or square' name='description'/>
    <meta content='Don Brutzman, Murat Onder and MV4205 class, Spring 2004 Quarter' name='creator'/>
    <meta content='11 May 2004' name='created'/>
    <meta content='2 January 2025' name='modified'/>
    <meta content='PushButtonExample.x3d' name='reference'/>
    <meta content='https://www.web3d.org/x3d/content/examples/Savage/Tools/Animation/PushButtonPrototype.x3d' name='identifier'/>
    <meta content='X3D-Edit 4.0, https://www.web3d.org/x3d/tools/X3D-Edit' name='generator'/>
    <meta content='../../license.html' name='license'/>
  </head>
  <Scene>
    <!-- Material Toggle Prototype is being used to be able to provide the color toggle. -->
    <WorldInfo title='PushButtonPrototype.x3d'/>
    <ExternProtoDeclare appinfo='MaterialToggle selects one of two different Material values' name='MaterialToggle' url='"MaterialTogglePrototype.x3d#MaterialToggle" "../../Tools/Animation/MaterialTogglePrototype.x3d#MaterialToggle" "https://www.web3d.org/x3d/content/examples/Savage/Tools/Animation/MaterialTogglePrototype.x3d#MaterialToggle" "../../Tools/Animation/MaterialTogglePrototype.wrl#MaterialToggle" "MaterialTogglePrototype.wrl#MaterialToggle" "https://www.web3d.org/x3d/content/examples/Savage/Tools/Animation/MaterialTogglePrototype.wrl#MaterialToggle"'>
      <field accessType='initializeOnly' appinfo='Material node that is enabled when toggle=false' name='defaultMaterial' type='SFNode'/>
      <field accessType='initializeOnly' appinfo='Material node that is enabled when toggle=true' name='toggleMaterial' type='SFNode'/>
      <field accessType='inputOnly' name='set_toggle' type='SFBool'/>
      <field accessType='inputOnly' appinfo='provide replacement default Material node' name='set_defaultMaterial' type='SFNode'/>
      <field accessType='initializeOnly' appinfo='whether to use DefaultMaterial or ToggleMaterial' name='toggle' type='SFBool'/>
      <field accessType='outputOnly' name='toggle_changed' type='SFBool'/>
      <field accessType='inputOnly' appinfo='provide replacement toggle Material node' name='set_toggleMaterial' type='SFNode'/>
    </ExternProtoDeclare>
    <!-- TimeDelaySensor is being used to provide the time delay for button's color change delay -->
    <ExternProtoDeclare appinfo='TimeSensor functionality commences after delayInterval pause' name='TimeDelaySensor' url='"TimeDelaySensorPrototype.x3d#TimeDelaySensor" "../../Tools/Animation/TimeDelaySensorPrototype.x3d#TimeDelaySensor" "https://www.web3d.org/x3d/content/examples/Savage/Tools/Animation/TimeDelaySensorPrototype.x3d#TimeDelaySensor" "../../Tools/Animation/TimeDelaySensorPrototype.wrl#TimeDelaySensor" "TimeDelaySensorPrototype.wrl#TimeDelaySensor" "https://www.web3d.org/x3d/content/examples/Savage/Tools/Animation/TimeDelaySensorPrototype.wrl#TimeDelaySensor"'>
      <field accessType='inputOutput' appinfo='when current time exceeds startTime, isActive becomes true and sensor becomes active' name='startTime' type='SFTime'/>
      <field accessType='inputOutput' appinfo='seconds' name='delayInterval' type='SFTime'/>
      <field accessType='outputOnly' name='delayCompleteTime' type='SFTime'/>
      <field accessType='inputOutput' appinfo='whether sensor is active' name='enabled' type='SFBool'/>
      <field accessType='inputOutput' appinfo='describe the purpose of this sensor' name='description' type='SFString'/>
      <field accessType='initializeOnly' name='traceEnabled' type='SFBool'/>
    </ExternProtoDeclare>
    <!-- PushButton Prototype definition starts here.. -->
    <ProtoDeclare appinfo='PushButton widget, inner button and outer shape can be round or square' name='PushButton'>
      <ProtoInterface>
        <field accessType='initializeOnly' appinfo='enables the console print-out in case of assigning wrong values default is true' name='traceEnabled' type='SFBool' value='true'/>
        <field accessType='initializeOnly' name='outerShapeMaterial' type='SFNode'>
          <Material DEF='Blue' diffuseColor='0 0 1'/>
        </field>
        <field accessType='initializeOnly' appinfo='defaultMaterial for inner pushbutton' name='defaultMaterial' type='SFNode'>
          <Material DEF='Red' diffuseColor='0.8 0.1 0'/>
        </field>
        <field accessType='initializeOnly' appinfo='toggleMaterial for inner pushbutton' name='toggleMaterial' type='SFNode'>
          <Material DEF='Green' diffuseColor='0.1 0.8 0'/>
        </field>
        <field accessType='outputOnly' appinfo='boolean output of button selection' name='value_changed' type='SFBool'/>
        <!-- switch values for combination of the button, default: round-round -->
        <field accessType='initializeOnly' appinfo='allowed values: round or square default is round' name='outerSwitchStyle' type='SFString' value='round'/>
        <field accessType='inputOnly' appinfo='allowed values: round or square default is round' name='set_outerSwitchStyle' type='SFString'/>
        <field accessType='initializeOnly' appinfo='allowed values: round square. default is round' name='innerSwitchStyle' type='SFString' value='round'/>
        <field accessType='inputOnly' appinfo='allowed values: round square. default is round' name='set_innerSwitchStyle' type='SFString'/>
        <field accessType='initializeOnly' appinfo='time delay for button movement so for color change default is 0.5 sec.' name='delayInterval' type='SFTime' value='.5'/>
      </ProtoInterface>
      <ProtoBody>
        <Group>
          <ProtoInstance DEF='DelayTimer' name='TimeDelaySensor'>
            <IS>
              <connect nodeField='delayInterval' protoField='delayInterval'/>
            </IS>
          </ProtoInstance>
          <Transform DEF='OuterShapeTransform' rotation='1 0 0 1.57'>
            <Switch DEF='OuterShapeSwitchRound' whichChoice='-1'>
              <Shape>
                <Cylinder height='.12' radius='.5'/>
                <Appearance>
                  <IS>
                    <connect nodeField='material' protoField='outerShapeMaterial'/>
                  </IS>
                </Appearance>
              </Shape>
            </Switch>
            <Switch DEF='OuterShapeSwitchSquare' whichChoice='-1'>
              <Shape>
                <Box size='1 .12 1'/>
                <Appearance>
                  <IS>
                    <connect nodeField='material' protoField='outerShapeMaterial'/>
                  </IS>
                </Appearance>
              </Shape>
            </Switch>
          </Transform>
          <Transform DEF='InnerShapeTransform' rotation='1 0 0 1.57' translation='0 0 .1'>
            <Switch DEF='InnerShapeSwitchRound' whichChoice='-1'>
              <Shape>
                <Cylinder height='.12' radius='.35'/>
                <Appearance>
                  <ProtoInstance DEF='PushButtonToggleMaterial' containerField='material' name='MaterialToggle'>
                    <IS>
                      <connect nodeField='defaultMaterial' protoField='defaultMaterial'/>
                      <connect nodeField='toggleMaterial' protoField='toggleMaterial'/>
                    </IS>
                  </ProtoInstance>
                </Appearance>
              </Shape>
            </Switch>
            <Switch DEF='InnerShapeSwitchSquare' whichChoice='-1'>
              <Shape>
                <Box size='.57 .12 .57'/>
                <Appearance>
                  <ProtoInstance USE='PushButtonToggleMaterial' containerField='material' name='MaterialToggle'/>
                </Appearance>
              </Shape>
            </Switch>
            <Sound>
              <AudioClip DEF='ClickAudio' description='click sound' url='"click.wav" "https://www.web3d.org/x3d/content/examples/Savage/Tools/Animation/click.wav"'/>
            </Sound>
            <TouchSensor DEF='Toucher' description='touch to push the button'/>
            <ROUTE fromField='touchTime' fromNode='Toucher' toField='set_startTime' toNode='ClickAudio'/>
            <ROUTE fromField='touchTime' fromNode='Toucher' toField='startTime' toNode='DelayTimer'/>
          </Transform>
          <Group DEF='AnimationGroup'>
            <!-- ============================= -->
            <TimeSensor DEF='Clock1'/>
            <ROUTE fromField='touchTime' fromNode='Toucher' toField='set_startTime' toNode='Clock1'/>
            <!-- ============================= -->
            <PositionInterpolator DEF='SwitchAnimator1' key='0 .5 1' keyValue='0 0 .1 0 0 .05 0 0 .007'/>
            <ROUTE fromField='fraction_changed' fromNode='Clock1' toField='set_fraction' toNode='SwitchAnimator1'/>
            <ROUTE fromField='value_changed' fromNode='SwitchAnimator1' toField='set_translation' toNode='InnerShapeTransform'/>
            <!-- ============================= -->
            <TimeSensor DEF='Clock2'/>
            <ROUTE fromField='delayCompleteTime' fromNode='DelayTimer' toField='set_startTime' toNode='Clock2'/>
            <!-- ============================= -->
            <PositionInterpolator DEF='SwitchAnimator2' key='0 .5 1' keyValue='0 0 .007 0 0 .05 0 0 .1'/>
            <ROUTE fromField='fraction_changed' fromNode='Clock2' toField='set_fraction' toNode='SwitchAnimator2'/>
            <ROUTE fromField='value_changed' fromNode='SwitchAnimator2' toField='set_translation' toNode='InnerShapeTransform'/>
            <!-- ============================= -->
            <BooleanToggle DEF='BooleanToggler'>
              <IS>
                <connect nodeField='toggle_changed' protoField='value_changed'/>
              </IS>
            </BooleanToggle>
            <ROUTE fromField='isActive' fromNode='Clock2' toField='set_boolean' toNode='BooleanToggler'/>
            <ROUTE fromField='toggle_changed' fromNode='BooleanToggler' toField='set_toggle' toNode='PushButtonToggleMaterial'/>
          </Group>
        </Group>
        <!-- Only first node in ProtoBody is rendered -->
        <Script directOutput='true'>
          <field accessType='initializeOnly' name='traceEnabled' type='SFBool'/>
          <field accessType='initializeOnly' name='delayInterval' type='SFTime'/>
          <field accessType='initializeOnly' appinfo='allowed values: round square. default is round' name='outerSwitchStyle' type='SFString'/>
          <field accessType='inputOnly' appinfo='allowed values: round square. default is round' name='set_outerSwitchStyle' type='SFString'/>
          <field accessType='initializeOnly' appinfo='allowed values: round square. default is round' name='innerSwitchStyle' type='SFString'/>
          <field accessType='inputOnly' appinfo='allowed values: round square. default is round' name='set_innerSwitchStyle' type='SFString'/>
          <field accessType='initializeOnly' name='switchOuterRound' type='SFNode'>
            <Switch USE='OuterShapeSwitchRound' whichChoice='-1'/>
          </field>
          <field accessType='initializeOnly' name='switchOuterSquare' type='SFNode'>
            <Switch USE='OuterShapeSwitchSquare' whichChoice='-1'/>
          </field>
          <field accessType='initializeOnly' name='switchInnerRound' type='SFNode'>
            <Switch USE='InnerShapeSwitchRound' whichChoice='-1'/>
          </field>
          <field accessType='initializeOnly' name='switchInnerSquare' type='SFNode'>
            <Switch USE='InnerShapeSwitchSquare' whichChoice='-1'/>
          </field>
          <field accessType='initializeOnly' name='clock1' type='SFNode'>
            <TimeSensor USE='Clock1'/>
          </field>
          <field accessType='initializeOnly' name='clock2' type='SFNode'>
            <TimeSensor USE='Clock2'/>
          </field>
          <IS>
            <connect nodeField='traceEnabled' protoField='traceEnabled'/>
            <connect nodeField='outerSwitchStyle' protoField='outerSwitchStyle'/>
            <connect nodeField='set_outerSwitchStyle' protoField='set_outerSwitchStyle'/>
            <connect nodeField='innerSwitchStyle' protoField='innerSwitchStyle'/>
            <connect nodeField='set_innerSwitchStyle' protoField='set_innerSwitchStyle'/>
            <connect nodeField='delayInterval' protoField='delayInterval'/>
          </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].');
    }
}
]]>
        </Script>
      </ProtoBody>
    </ProtoDeclare>
    <!-- ==================== -->
    <Anchor description='PushButtonExample' parameter='"target=_blank"' url='"PushButtonExample.x3d" "../../Tools/Animation/PushButtonExample.x3d" "https://www.web3d.org/x3d/content/examples/Savage/Tools/Animation/PushButtonExample.x3d" "../../Tools/Animation/PushButtonExample.wrl" "PushButtonExample.wrl" "https://www.web3d.org/x3d/content/examples/Savage/Tools/Animation/PushButtonExample.wrl"'>
      <Shape>
        <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)"'>
          <FontStyle justify='"MIDDLE" "MIDDLE"' size='0.7'/>
        </Text>
        <Appearance>
          <Material diffuseColor='.8 .7 0.2'/>
        </Appearance>
      </Shape>
    </Anchor>
  </Scene>
</X3D>