|    
  Chapter 3:Node Reference
 
 Intro
 Anchor
 Appearance
 AudioClip
 Background
 Billboard
 Box
 Collision
 Color
 ColorInterpolator
 Cone
 Coordinate
 CoordinateInterpolator
 Cylinder
 CylinderSensor
 DirectionalLight
 ElevationGrid
 Extrusion
 Fog
 FontStyle
 Group
 ImageTexture
 IndexedFaceSet
 IndexedLineSet
 Inline
 LOD
 Material
 MovieTexture
 NavigationInfo
 Normal
 NormalInterpolator
 OrientationInterpolator
 PixelTexture
 PlaneSensor
 PointLight
 PointSet
 PositionInterpolator
 ProximitySensor
 ScalarInterpolator
 Script
 Shape
 Sound
 Sphere
 SphereSensor
 SpotLight
 Switch
 Text
 TextureCoordinate
 TextureTransform
 TimeSensor
 TouchSensor
 Transform
 Viewpoint
 VisibilitySensor
 WorldInfo
 |  
        
        
TimeSensor { 
  exposedField SFTime   cycleInterval 1       # (0, )
  exposedField SFBool   enabled       TRUE
  exposedField SFBool   loop          FALSE
  exposedField SFTime   startTime     0       # (-  ,  )
  exposedField SFTime   stopTime      0       # (-  ,  )
  eventOut     SFTime   cycleTime
  eventOut     SFFloat  fraction_changed
  eventOut     SFBool   isActive
  eventOut     SFTime   time
} TimeSensor nodes generate events as time passes. TimeSensor nodes 
          can be used for many purposes including: 
           driving continuous simulations and animations 
           controlling periodic activities (e.g., one per minute) 
           initiating single occurrence events such as an alarm clock 
          The TimeSensor node contains two discrete eventOuts: isActive 
          and cycleTime. The isActive eventOut sends TRUE when the 
          TimeSensor node begins running, and FALSE when it stops running. The 
          cycleTime eventOut sends a time event at startTime and 
          at the beginning of each new cycle (useful for synchronization with 
          other time-based objects). The remaining eventOuts generate continuous 
          events. The fraction_changed eventOut, an SFFloat in the closed 
          interval [0,1], sends the completed fraction of the current cycle. The 
          time eventOut sends the absolute time for a given simulation 
          tick.    
        
           
            | TECHNICAL 
                NOTE: More time was spent refining the design of the TimeSensor 
                node than any other node in the VRML 2.0 specification. That's 
                not unreasonable; TimeSensors are important. With the exception 
                of Sounds and MovieTextures, all animation in VRML worlds is driven 
                by TimeSensors, and TimeSensors implement VRML's model of time. It might 
                have been simpler to define two types of TimeSensors: one that 
                generated a (conceptually) continuous stream of events and one 
                that generated a series of discrete events. Much 
                of the work of defining the behavior of the TimeSensor was specifying 
                exactly when discrete (isActive, cycleTime) and 
                continuous (fraction_changed, time) eventOuts are 
                generated, relative to the events that come in and relative to 
                each other. TimeSensor generates both discrete and continuous 
                events because synchronizing discrete events (such as starting 
                an audio clip) with continuous events (such as animating the position 
                of an object) is very important. Even if two separate nodes had 
                been defined it would still be necessary to define precisely how 
                they interact, which would be as difficult as defining the behavior 
                of the combined TimeSensor. Daniel Woods rewrote and improved 
                the original TimeSensor node and time-dependent nodes sections 
                in the VRML specification. |  
          
             
              | TECHNICAL 
                  NOTE: If the enabled exposedField is TRUE, the TimeSensor 
                  node is enabled and may be running. If a set_enabled FALSE 
                  event is received while the TimeSensor node is running, the 
                  sensor performs the following actions: 
                   evaluates 
                    and sends all relevant outputs  
                   sends 
                    a FALSE value for isActive  
                   disables 
                    itself.  
                  Events 
                  on the exposedFields of the TimeSensor node (e.g., set_startTime) 
                  are processed and their corresponding eventOuts (e.g., startTime_changed) 
                  are sent regardless of the state of the enabled field. 
                  The remaining discussion assumes enabled is TRUE.  The loop, 
                  startTime, and stopTime exposedFields and the isActive 
                  eventOut and their effects on the TimeSensor node are discussed 
                  in detail in "2.6.9 Time dependent 
                  nodes". The "cycle" of a TimeSensor node lasts for 
                  cycleInterval seconds. The value of cycleInterval 
                  must be > 0. A value <= 0 produces undefined 
                  results.  A cycleTime 
                  eventOut can be used for synchronization purposes such as sound 
                  with animation. The value of a cycleTime eventOut will 
                  be equal to the time at the beginning of the current cycle. 
                  A cycleTime eventOut is generated at the beginning of 
                  every cycle, including the cycle starting at startTime. 
                  The first cycleTime eventOut for a TimeSensor node can 
                  be used as an alarm (single pulse at a specified time).  |     
            
               
                | TIP: 
                    The 
                    easiest way to set up a TimeSensor as an "alarm clock" that 
                    produces an event at a specific time in the future is to specify 
                    that time as the startTime, specify loop FALSE, 
                    and ROUTE from the TimeSensor's cycleTime eventOut. 
                    Theoretically, it doesn't matter what value you give for cycleInterval, 
                    since you're only using the cycleTime event generated 
                    at startTime. However, it is a good idea to use an 
                    arbitrarily small value as the cycleInterval (0.001 
                    s should work well), because some browsers may generate fraction_changed 
                    and time events during the cycleInterval regardless 
                    of whether or not they are being used. The easiest 
                    way to have one TimeSensor start when another has stopped 
                    is to write a little Script that sends the second TimeSensor 
                    a startTime event when it receives an isActive 
                    FALSE event from the first TimeSensor, like this:  
                   
     DEF TS1 TimeSensor { }
     DEF TS2 TimeSensor { }
     DEF S Script {
       eventIn SFBool isActive
       eventOut SFTime startTime_changed
       url "javascript:
         function isActive(value, timestamp) {
           if (value == false)
             startTime_changed = timestamp;
         }"
     }
     ROUTE TS1.isActive TO S.isActive
     ROUTE S.startTime_changed TO TS2.set_startTimeHowever, 
                    it is better to set the second TimeSensor's startTime 
                    as early as possible, so the browser knows in advance when 
                    it will start and thus it has a better chance of downloading 
                    any textures, sounds, or Inline geometry that might be needed 
                    once the second animation starts. This is also fairly easy, 
                    because the first TimeSensor will end at time startTime 
                    + cycleInterval:  
     DEF TS1 TimeSensor { }
     DEF TS2 TimeSensor { }
     DEF S Script {
       eventIn SFTime startTime_changed
       field SFTime start 0
       eventIn SFTime cycleInterval_changed
       field SFTime interval 0
       eventOut SFTime set_startTime
       url "javascript:
         function startTime_changed(value)
           { start = value; }
         function cycleInterval_changed(value)
           { interval = value; }
         function eventsProcessed()
           { set_startTime = start+interval; }"
     }
     ROUTE TS1.startTime_changed
       TO S.startTime_changed
     ROUTE TS1.cycleInterval_changed
       TO S.cycleInterval_changed
     ROUTE S.set_startTime TO TS2.set_startTime  |   
         When a TimeSensor node becomes active, it generates an isActive 
          = TRUE event and begins generating time, fraction_changed, 
          and cycleTime events which may be routed to other nodes to drive 
          animation or simulated behaviours. The behaviour at read time is described 
          below. The time event sends the absolute time for a given tick 
          of the TimeSensor node (time fields and 
          events represent the number of seconds since midnight GMT January 1, 
          1970).  fraction_changed events output a floating point value in the 
          closed interval [0, 1]. At startTime the value of fraction_changed 
          is 0. After startTime, the value of fraction_changed in 
          any cycle will progress through the range (0.0, 1.0]. At startTime + N × cycleInterval, 
          for N = 1, 2, ..., that is, at the end of every cycle, the value 
          of fraction_changed is 1.   Let now represent the time at the current simulation tick. 
          Then the time and fraction_changed eventOuts can 
          then be computed as:  
    time = now
    temp = (now - startTime) / cycleInterval
    f    = fractionalPart(temp)
    if (f == 0.0 && now > startTime) fraction_changed = 1.0
    else fraction_changed = f
 where fractionalPart(x) is a function that returns the fractional 
          part, that is, the digits to the right of the decimal point, of a nonnegative 
          floating point number.  A TimeSensor node can be set up to be active at read time by specifying 
          loop TRUE (not the default) and stopTime <= startTime 
          (satisfied by the default values). The time events output absolute 
          times for each tick of the TimeSensor node simulation. The time 
          events must start at the first simulation tick greater than or equal 
          to startTime. time events end at stopTime, or at 
          startTime + N × cycleInterval 
          for some positive integer value of N, or loop forever depending 
          on the values of the other fields. An active TimeSensor node shall stop 
          at the first simulation tick when now >= stopTime > startTime.    Figure 3-61: TimeSensor Node No guarantees are made with respect to how often a TimeSensor node 
          generates time events, but a TimeSensor node shall generate events at 
          least at every simulation tick. TimeSensor nodes are guaranteed to generate 
          final time and fraction_changed events. If loop is FALSE 
          at the end of the Nth cycleInterval and was TRUE at startTime + M × cycleInterval 
          for all 0 < M < N, then the final time 
          event will be generated with a value of (startTime + N × cycleInterval) 
          or stopTime (if stopTime > startTime), 
          whichever value is less. If loop is TRUE at the completion 
          of every cycle, the final event is generated as evaluated at stopTime 
          (if stopTime > startTime) or never.  An active TimeSensor node ignores set_cycleInterval and set_startTime 
          events. An active TimeSensor node also ignores set_stopTime events 
          for set_stopTime <= startTime. For example, if a 
          set_startTime event is received while a TimeSensor node is active, 
          that set_startTime event is ignored (the startTime field 
          is not changed, and a startTime_changed eventOut is not generated). 
          If an active TimeSensor node receives a set_stopTime event that 
          is less than the current time, and greater than startTime, it 
          behaves as if the stopTime requested is the current time and 
          sends the final events based on the current time (note that stopTime 
          is set as specified in the eventIn).  
           
          
             
              | TIP: 
                Ignoring set_ events while a TimeSensor 
                is running makes creating simple animations much easier, because 
                for most simple animations you want the animation played to completion 
                before it can be restarted. If you do need to stop and restart 
                a TimeSensor while it is running, send it both a stopTime 
                and a startTime event. The stopTime event will stop 
                the sensor and the startTime event will restart it immediately. 
                For example, this fragment will result in the TimeSensor immediately 
                restarting when the TouchSensor is activated: 
     DEF TOUCHS TouchSensor { ... }
     DEF TIMES TimeSensor { ... }
     ROUTE TOUCHS.touchTime TO TIMES.set_stopTime
     ROUTE TOUCHS.touchTime TO TIMES.set_startTime 
                   |     
          
             
              | TIP: 
                There are two cases of the TimeSensor that are most common. The 
                first case uses a TimeSensor to drive a single cycle of an animation 
                or behavior. Typically, another node that has a SFTime eventOut 
                (e.g., Script, TouchSensor, or ProximitySensor) routes to the 
                TimeSensor's startTime eventIn (setting it to now or now + delay), 
                which in turn routes its fraction_changed eventOut to another 
                node's set_fraction eventIn. The second common case of 
                a TimeSensor is a continuously looping animation or behavior. 
                In this case, the TimeSensor's loop field is TRUE, stopTime 
                is 0, startTime is 0, and cycleTime is the length 
                of the intended sequence. This has the effect of starting the 
                sequence in 1970 and looping forever. Be aware that looping TimeSensors 
                can slow down rendering performance if too many are active simultaneously, 
                and should be used only when necessary. It is recommended that 
                you restrict the effect of looping TimeSensors by coupling them 
                with a ProximitySensor, VisibilitySensor, Script, or LOD that 
                disables the TimeSensor when out of range or not relevant. |   
        
           
            | EXAMPLE 
                (click to run): The following example illustrates the 
                TimeSensor (see Figure 3-62). The first TimeSensor defines a continuously 
                running animation that is enabled and disabled by a ProximitySensor. 
                The second TimeSensor is triggered by a TouchSensor and fires 
                one cycle of an animation each time it is triggered:  #VRML V2.0 utf8
Group { children [
  DEF PS ProximitySensor { size 30 30 30 }
  DEF TS1 TimeSensor {
    enabled FALSE 
    loop TRUE
  }
  DEF T1 Transform {
    translation 0 0 -.5
    rotation .707 -.707 0 1.57
    children Shape {
      geometry Box {}
      appearance DEF A Appearance {
        material Material { diffuseColor 1 1 1 }
      }
    }
  }
  DEF OI OrientationInterpolator {
    key [ 0, 0.33, 0.66, 1.0 ]
    keyValue [ .707 .707 0 0,    .707 .707 0 2.09,
               .707 .707 0 4.18, .707 .707 0 6.28 ]
  }
  DEF T2 Transform {
    translation -4 0 0
    children [
      Shape {
        geometry Sphere { radius 0.5 }
        appearance USE A
      }
      DEF TOS TouchSensor {}
      DEF TS2 TimeSensor { cycleInterval 0.75 }
      DEF PI PositionInterpolator {
        key [ 0, .2, .5, .8, 1 ]
        keyValue [ -4 0 0, 0 4 0, 4 0 0, 0 -4 0, -4 0 0 ]
      }
    ]
  }
  Viewpoint { position 0 0 50 description "Animation off"}
  Viewpoint { position 0 0 10 description "Animation on"}
] }
ROUTE PS.isActive TO TS1.enabled
ROUTE TS1.fraction_changed TO OI.set_fraction
ROUTE OI.value_changed TO T1.rotation
ROUTE TOS.touchTime TO TS2.startTime
ROUTE TS2.fraction_changed TO PI.set_fraction
ROUTE PI.value_changed TO T2.translation |  
        
             Figure 3-62: TimeSensor Node Example |