<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE X3D PUBLIC "ISO//Web3D//DTD X3D 3.1//EN" "https://www.web3d.org/specifications/x3d-3.1.dtd">
<X3D profile='Immersive' version='3.1' xmlns:xsd='http://www.w3.org/2001/XMLSchema-instance' xsd:noNamespaceSchemaLocation='https://www.web3d.org/specifications/x3d-3.1.xsd'>
  <head>
    <meta content='BollardHydraulicSecurityPrototype.x3d' name='title'/>
    <meta content='Rising/retractable security bollard' name='description'/>
    <meta content='Don Brutzman and MV4205 Advanced XML class' name='creator'/>
    <meta content='30 May 2007' name='created'/>
    <meta content='20 October 2019' name='modified'/>
    <meta content='http://www.atgaccess.com' name='reference'/>
    <meta content='under development, initial functionality completed. TODO: add SMALL metadata. TODO: how to string together multiple bollards - add fields for spacing and count?' name='warning'/>
    <meta content='https://www.web3d.org/x3d/content/examples/Savage/Buildings/SecurityPerimeter/BollardHydraulicSecurityPrototype.x3d' name='identifier'/>
    <meta content='X3D-Edit 3.2, https://www.web3d.org/x3d/tools/X3D-Edit' name='generator'/>
    <meta content='../../license.html' name='license'/>
  </head>
  <Scene>
    <WorldInfo title='BollardHydraulicSecurityPrototype.x3d'/>
    <ProtoDeclare appinfo='A security bollard can be raised or lowered to prevent vehicle access by blocking a road. Usually multiple bollards are installed side by side raised and lowered together.' name='BollardHydraulicSecurity'>
      <ProtoInterface>
        <field accessType='initializeOnly' name='radius' type='SFFloat' value='0.2'/>
        <field accessType='initializeOnly' name='height' type='SFFloat' value='1'/>
        <field accessType='inputOutput' name='textureUrl' type='MFString'/>
        <field accessType='inputOutput' name='diffuseColor' type='SFColor' value='1 1 0'/>
        <field accessType='inputOnly' appinfo='Command to raise the bollard.' name='raise' type='SFBool'/>
        <field accessType='inputOnly' appinfo='Command to lower the bollard.' name='lower' type='SFBool'/>
        <!-- TODO: add TimeDelaySensor nodes to report completion by the following output fields -->
        <field accessType='outputOnly' appinfo='Notification event sent when bollard is fully raised.' name='isRaised' type='SFBool'/>
        <field accessType='outputOnly' appinfo='Notification event sent when bollard is fully lowered.' name='isLowered' type='SFBool'/>
      </ProtoInterface>
      <ProtoBody>
        <Transform DEF='BollardTransform'>
          <!-- first Cylinder displays only the sides, with an optional texture applied -->
          <Shape>
            <Cylinder bottom='false' top='false'>
              <IS>
                <connect nodeField='radius' protoField='radius'/>
                <connect nodeField='height' protoField='height'/>
              </IS>
            </Cylinder>
            <Appearance>
              <Material DEF='CylinderMaterial'>
                <IS>
                  <connect nodeField='diffuseColor' protoField='diffuseColor'/>
                </IS>
              </Material>
              <ImageTexture>
                <IS>
                  <connect nodeField='url' protoField='textureUrl'/>
                </IS>
              </ImageTexture>
            </Appearance>
          </Shape>
          <!-- second Cylinder displays only the top and bottom, which do not have the optional texture applied -->
          <Shape>
            <Cylinder side='false'>
              <IS>
                <connect nodeField='radius' protoField='radius'/>
                <connect nodeField='height' protoField='height'/>
              </IS>
            </Cylinder>
            <Appearance>
              <Material USE='CylinderMaterial'/>
            </Appearance>
          </Shape>
        </Transform>
        <!-- only the first node in a prototype body is rendered, the others are carried along for the ride -->
        <PositionInterpolator DEF='HeightInterpolator' key='0 1' keyValue='0 -0.5 0 0 0.5 0'/>
        <ROUTE fromField='value_changed' fromNode='HeightInterpolator' toField='set_translation' toNode='BollardTransform'/>
        <!-- ========= setup =========== -->
        <!-- this Script initializes our PositionInterpolator to proper start/stop heights. Also initializes BollardTransform translation to the correct height. -->
        <Script DEF='HeightTypeConversion' directOutput='true'>
          <field accessType='initializeOnly' name='height' type='SFFloat'/>
          <field accessType='outputOnly' name='initialTranslation' type='SFVec3f'/>
          <field accessType='initializeOnly' name='PI' type='SFNode'>
            <PositionInterpolator USE='HeightInterpolator'/>
          </field>
          <IS>
            <connect nodeField='height' protoField='height'/>
          </IS>
          <![CDATA[
ecmascript:

function initialize ()
{
	// set initial Transform translation value to the correct height
	initialTranslation = new SFVec3f (0, -(height / 2), 0); // output event
	finalTranslation   = new SFVec3f (0,  (height / 2), 0);
//	Browser.println ('initialTranslation=' + initialTranslation);

	// customize PositionInterpolator to move correct vertical distance
	PI.keyValue = new MFVec3f (initialTranslation, finalTranslation);
//	Browser.println ('PI.keyValue=' + PI.keyValue);
}
]]>
        </Script>
        <ROUTE fromField='initialTranslation' fromNode='HeightTypeConversion' toField='set_translation' toNode='BollardTransform'/>
        <!-- ========= raise =========== -->
        <TimeSensor DEF='RaiseBollardClock' cycleInterval='5'/>
        <!-- alternative approach (shown below) replaces this script with a BooleanFilter plus TimeTrigger (to convert SFBool to SFTime) -->
        <Script DEF='ClockScript'>
          <field accessType='inputOnly' name='raise' type='SFBool'/>
          <field accessType='outputOnly' name='startRaising' type='SFTime'/>
          <IS>
            <connect nodeField='raise' protoField='raise'/>
          </IS>
          <![CDATA[
ecmascript:

function raise (value, timestamp)
{
	startRaising = timestamp;
}
]]>
        </Script>
        <ROUTE fromField='fraction_changed' fromNode='RaiseBollardClock' toField='set_fraction' toNode='HeightInterpolator'/>
        <ROUTE fromField='startRaising' fromNode='ClockScript' toField='startTime' toNode='RaiseBollardClock'/>
        <!-- ========= lower =========== -->
        <ScalarInterpolator DEF='FractionReverser' key='0 1' keyValue='1 0'/>
        <TimeSensor DEF='LowerBollardClock' cycleInterval='5'/>
        <TimeTrigger DEF='LowerTimeTrigger'/>
        <BooleanFilter DEF='StartLoweringFilter'>
          <IS>
            <connect nodeField='set_boolean' protoField='lower'/>
          </IS>
        </BooleanFilter>
        <ROUTE fromField='value_changed' fromNode='FractionReverser' toField='set_fraction' toNode='HeightInterpolator'/>
        <ROUTE fromField='fraction_changed' fromNode='LowerBollardClock' toField='set_fraction' toNode='FractionReverser'/>
        <ROUTE fromField='triggerTime' fromNode='LowerTimeTrigger' toField='set_startTime' toNode='LowerBollardClock'/>
        <ROUTE fromField='inputTrue' fromNode='StartLoweringFilter' toField='set_boolean' toNode='LowerTimeTrigger'/>
      </ProtoBody>
    </ProtoDeclare>
    <!-- ==================== -->
    <Viewpoint description='Bollard view' position='0 0 6'/>
    <!-- test locally during development, once complete we split this out into a separate Example scene. -->
    <ProtoInstance DEF='Bollard' name='BollardHydraulicSecurity'>
      <fieldValue name='height' value='0.85'/>
      <fieldValue name='radius' value='0.2'/>
      <fieldValue name='textureUrl' value='"fan.png"'/>
    </ProtoInstance>
    <Shape DEF='GroundLevel'>
      <Box size='10 0.01 10'/>
    </Shape>
    <Transform DEF='RaiseInterface' translation='-2 -1 0'>
      <Shape>
        <Text string='"Click to" "raise"'>
          <FontStyle justify='"MIDDLE" "MIDDLE"' size='0.3'/>
        </Text>
      </Shape>
      <Shape>
        <Box size='3 2 0.01'/>
        <Appearance>
          <Material transparency='1'/>
        </Appearance>
      </Shape>
      <TouchSensor DEF='ClickToRaiseBollard' description='click to raise bollard'/>
      <ROUTE fromField='isActive' fromNode='ClickToRaiseBollard' toField='raise' toNode='Bollard'/>
    </Transform>
    <Transform DEF='LowerInterface' translation='2 -1 0'>
      <Shape>
        <Text string='"Click to" "lower"'>
          <FontStyle justify='"MIDDLE" "MIDDLE"' size='0.3'/>
        </Text>
      </Shape>
      <Shape>
        <Box size='3 2 0.01'/>
        <Appearance>
          <Material transparency='1'/>
        </Appearance>
      </Shape>
      <TouchSensor DEF='ClickToLowerBollard' description='click tolower bollard'/>
      <ROUTE fromField='isActive' fromNode='ClickToLowerBollard' toField='lower' toNode='Bollard'/>
    </Transform>
  </Scene>
</X3D>