Browse Source

animation & interpolation

Nikolay Suslov 6 years ago
parent
commit
74722264ca
29 changed files with 10964 additions and 19 deletions
  1. 500 0
      public/defaults/animation/animation.vwf.yaml
  2. 420 0
      public/defaults/animation/animationNode.vwf.yaml
  3. 57 0
      public/defaults/animation/position.vwf.yaml
  4. 23 1
      public/defaults/proxy/vwf.example.com/aframe/aentity.js
  5. 17 1
      public/defaults/proxy/vwf.example.com/aframe/aentity.vwf.yaml
  6. 4 3
      public/defaults/proxy/vwf.example.com/aframe/avatar.js
  7. 500 0
      public/defaults/proxy/vwf.example.com/animation/animation.vwf.yaml
  8. 420 0
      public/defaults/proxy/vwf.example.com/animation/animationNode.vwf.yaml
  9. 57 0
      public/defaults/proxy/vwf.example.com/animation/position.vwf.yaml
  10. 7 0
      public/index.html
  11. 1479 0
      public/lib/closure/base.js
  12. 293 0
      public/lib/closure/deps.js
  13. 109 0
      public/lib/closure/vec/float32array.js
  14. 109 0
      public/lib/closure/vec/float64array.js
  15. 896 0
      public/lib/closure/vec/mat3.js
  16. 1615 0
      public/lib/closure/vec/mat4.js
  17. 722 0
      public/lib/closure/vec/matrix3.js
  18. 1405 0
      public/lib/closure/vec/matrix4.js
  19. 513 0
      public/lib/closure/vec/quaternion.js
  20. 94 0
      public/lib/closure/vec/ray.js
  21. 65 0
      public/lib/closure/vec/vec.js
  22. 375 0
      public/lib/closure/vec/vec2.js
  23. 473 0
      public/lib/closure/vec/vec3.js
  24. 405 0
      public/lib/closure/vec/vec4.js
  25. 43 4
      public/vwf/model/aframe.js
  26. 16 7
      public/vwf/model/aframe/addon/aframe-interpolation.js
  27. 246 0
      public/vwf/model/aframe/addon/aframe-interpolation_from_vle.js
  28. 98 0
      public/vwf/view/aframe.js
  29. 3 3
      public/vwf/view/aframeComponent.js

+ 500 - 0
public/defaults/animation/animation.vwf.yaml

@@ -0,0 +1,500 @@
+# Copyright 2012 United States Government, as represented by the Secretary of Defense, Under
+# Secretary of Defense (Personnel & Readiness).
+# 
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+# in compliance with the License. You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software distributed under the License
+# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+# or implied. See the License for the specific language governing permissions and limitations under
+# the License.
+
+## `animation.vwf` adds standard animation functions to a node.
+## 
+## Animations play over a given `animationDuration` and at a given `animationRate` with respect to
+## simulation time. `animationTime` tracks time as the animation plays. Setting `animationTime`
+## moves the animation. The animation plays once to the end, or with `animationLoop` repeats
+## indefinitely.
+## 
+## Events are sent to indicate `animationStarted`, `animationStopped` and `animationLooped`. Changes
+## to the animation time are announced with `animationTimeChanged.`
+##
+## A node may provide an `animationUpdate` method to calculate its own animation state, or it may
+## use a specialized behavior that implements a standard animation.
+## 
+## @name animation.vwf
+## @namespace
+
+# The duration must be a non-negative number, times are always in the range [ `0`, `duration` ], and
+# the rate may be any number (although there is little protection against rates very near zero).
+# 
+#   0 <= animationDuration
+#   0 <= animationTime <= animationDuration
+#   -Infinity < animationRate < Infinity
+# 
+# The animation play state and time index are maintained in `animationStart$` and
+# `animationPause$`. `animationPause$` is `null` while the animation is playing. The
+# distance from `animationStart$` to the current time (when playing) or `animationPause$`
+# (when paused) gives the time index, after accounting for the play rate.
+# 
+#   animationTime = ( ( animationPause$ || time ) - ( animationStart$ || time ) ) *
+#     animationRate (positive rates)
+#   animationTime = ( ( animationPause$ || time ) - ( animationStart$ || time ) ) *
+#     animationRate + animationDuration (negative rates)
+# 
+#   animationPlaying = this.animationStart$ != null && this.animationPause$ == null
+# 
+# `animationStart$` and `animationPause$` are `null` before the animation is first played.
+# This state is interpreted as `animationPlaying` == `false` and `animationTime` == `0`.
+# 
+#   animationStart$ == null => animationPause$ == null
+#   animationStart$ == null => animationStop$ == null
+# 
+#   animationStart$ != null => animationStart$ <= time
+#   animationPause$ != null => animationStart$ <= animationPause$ <= time
+#   animationStop$ != null => animationStart$ + animationDuration$ == animationStop$
+# 
+#   animationDuration$ == animationDuration / animationRate (positive rates)
+#   animationDuration$ == -animationDuration / animationRate (negative rates)
+# 
+# Properties named with a`$` suffix refer to times or durations on the simulation timeline. All
+# other time-related properties refer to the animation timeline.
+
+---
+
+properties:
+
+  ## The current animation position in seconds.
+  ## 
+  ## `animationTime` automatically moves with the simulation time while the animation is playing,
+  ## tracking the change in time multiplied by `animationRate`. A negative `animationRate` will
+  ## cause `animationTime` to move backwards. Setting `animationTime` updates the postion whether or
+  ## not the animation is currently playing.
+
+  animationTime:
+
+    set: |
+      if(this.animationStartTime == null) {
+        this.animationStartTime = 0;
+      }
+      if(this.animationStopTime == null) {
+        this.animationStopTime = this.animationDuration;
+      }
+      // Save copies to avoid repeated reads.
+      var duration = this.animationStopTime - this.animationStartTime,
+        rate = this.animationRate;
+      // Range limit the incoming value.
+      value = Math.min( Math.max( this.animationStartTime, value ), this.animationStopTime );
+      // Keep paused if updating start/pause from null/null. Use t=0 instead of `this.time` so that
+      // setting `node.animationTime` during initialization is consistent across multiple clients.
+      if ( this.animationStart$ == null ) {
+        this.animationPause$ = 0;
+      }
+      // Calculate the start and stop times that makes the new time work.
+      this.animationStart$ =
+        ( this.animationPause$ != null ? this.animationPause$ : this.time ) -
+        ( rate >= 0 ? value - this.animationStartTime : value - duration ) / rate;
+      this.animationStop$ =
+        this.animationStart$ +
+        ( rate >= 0 ? duration : -duration ) / rate;
+      // Update the node and fire the changed event.
+      if ( value !== this.animationTimeUpdated ) {
+        this.animationTimeUpdated = value;
+        this.animationUpdate( value, this.animationDuration );
+        this.animationTimeChanged( value );
+      } //@ sourceURL=animation.animationTime.set.vwf
+
+    get: |
+      // Save copies to avoid repeated reads.
+      var startTime = this.animationStartTime;
+      var stopTime = this.animationStopTime;
+      var rate = this.animationRate;
+      var animationPause$ = this.animationPause$;
+      var animationStart$ = this.animationStart$;
+      var time = this.time;
+      // Calculate the time from the start and current/pause times.
+      var value = (
+        ( animationPause$ != null ? animationPause$ : time ) -
+        ( animationStart$ != null ? animationStart$ : time )
+      ) * rate + ( rate >= 0 ? startTime : stopTime );
+      // Range limit the value.
+      value = Math.min( Math.max( startTime, value ), stopTime );
+      // If changed since last seen, update and fire the changed event.
+      if ( value !== this.animationTimeUpdated ) {
+        this.animationTimeUpdated = value;
+        this.animationUpdate( value, this.animationDuration );
+        this.animationTimeChanged( value );
+      }
+      return value; //@ sourceURL=animation.animationTime.get.vwf
+
+  ## The length of the animation in seconds.
+  ## 
+  ## `animationTime` will always be within the range [ `0`, `animationDuration` ]. Animations
+  ## automatically stop playing once `animationTime` reaches `animationDuration` unless
+  ## `animationLoop` is set. Animations with a negative `animationRate` start at
+  ## `animationDuration` and stop at `0`.
+
+  animationDuration:  # TODO: allow changes while playing
+    set: |
+      var duration = value, rate = this.animationRate;
+      this.animationDuration = duration;
+      this.animationDuration$ = ( rate >= 0 ? duration : -duration ) / rate;
+    value: 1
+
+  ## The animation playback rate.
+  ## 
+  ## Set `animationRate` to a value greater than `1` to increase the rate. Set a value between `0`
+  ## and `1` to decrease it. Negative rates will cause the animation to play backwards.
+
+  animationRate:  # TODO: allow changes while playing
+    set: |
+      var duration = this.animationDuration, rate = value;
+      this.animationRate = rate;
+      this.animationDuration$ = ( rate >= 0 ? duration : -duration ) / rate;
+    value: 1
+
+  ## Automatically replay the animation from the start after reaching the end.
+
+  animationLoop: false
+
+  ## The animation's play/pause control.
+
+  animationPlaying:
+
+    set: |
+      if(this.animationStartTime == null) {
+        this.animationStartTime = 0;
+      }
+      if(this.animationStopTime == null) {
+        this.animationStopTime = this.animationDuration;
+      }
+      if ( this.animationStart$ != null && this.animationPause$ == null ) {
+        if ( ! value ) {
+          // Mark as paused at the current time.
+          this.animationPause$ = this.time;
+          // Send the `animationStopped` event if stopping at the end.
+          if ( this.time == this.animationStop$ ) {
+            this.animationStopped();
+          }
+        }
+      } else {
+        if ( value ) {
+          // Save copies to avoid repeated reads.
+          var duration = this.animationStopTime - this.animationStartTime,
+            rate = this.animationRate;
+          // Start from the beginning if resuming from the end.
+          if ( this.animationPause$ == this.animationStop$ ) {
+            this.animationPause$ = this.animationStart$;
+          }
+          // Recalculate the start and stop times to keep paused time unchanged, then resume.
+          this.animationStart$ =
+            ( this.animationStart$ != null ? this.animationStart$ : this.time ) -
+            ( this.animationPause$ != null ? this.animationPause$ : this.time ) +
+            this.time;
+          this.animationStop$ =
+            this.animationStart$ +
+            ( rate >= 0 ? duration : -duration ) / rate;
+          this.animationPause$ = null;
+          // Send the `animationStarted` event if starting from the beginning.
+          if ( this.time == this.animationStart$ ) {
+            this.animationStarted();
+          }
+          // Start the animation worker.
+          this.logger.debug( "scheduling animationTick" );
+          this.animationTick();
+        }
+      } //@ sourceURL=animation.animationPlaying.set.vwf
+
+    get: |
+      return this.animationStart$ != null && this.animationPause$ == null;
+
+  ## The most recent value of `animationTime` recognized by this behavior.
+  ## 
+  ## Each `animationTime` change causes `animationUpdate` to be called. `animationTimeUpdated`
+  ## records the value of `animationTime` from the last time this occurred.
+  ## 
+  ## `animationTimeUpdated` is for internal use. Do not set this property.
+
+  animationTimeUpdated: null
+
+  ## The simulation time corresponding to the start of the animation.
+  ## 
+  ## `animationStart$` is `null` until the animation is first played. `animationPlaying` is
+  ## `false` and `animationTime` is `0` while `animationStart$` is `null`.
+  ## 
+  ## `animationStart$` is for internal use. Do not set this property.
+
+  animationStart$: null
+
+  ## The simulation time corresponding to the animation's pause position.
+  ## 
+  ## `animationPause$` is `null` while the animation is playing and before the animation is
+  ## first played.
+  ## 
+  ## `animationPause$` is for internal use. Do not set this property.
+
+  animationPause$: null
+
+  ## The simulation time corresponding to the end of the animation. The animation worker function
+  ## loops or stops the animation when `time` reaches this value.
+  ## 
+  ## `animationStop$` is for internal use. Do not set this property.
+
+  animationStop$: null
+
+  ## The animation's duration in simulation time, after considering `animationRate`.
+  ## 
+  ## `animationDuration$` is for internal use. Do not set this property.
+
+  animationDuration$: null
+
+  ## The time that the animation should start at. Used with animationStopTime to play
+  ## a subsection of the animation. Defaults to 0 when the animation starts to play
+  ## if it is not assigned another value. 'animationStartTime' will always be within 
+  ## the range [ `0`, `animationDuration` ]
+
+  animationStartTime: 
+    set: |
+      this.animationStartTime = value ? Math.min( Math.max( 0, value ), this.animationDuration ) : value;
+    value: null
+
+  ## The time that the animation should stop at. Used with animationStartTime to play
+  ## a subsection of the animation. Defaults to 'animationDuration' when the animation
+  ## starts to play if it is not assigned another value. 'animationStopTime' will always
+  ## be within the range [ `0`, `animationDuration` ]
+
+  animationStopTime:  
+    set: |
+      this.animationStopTime = value ? Math.min( Math.max( 0, value ), this.animationDuration ) : value;
+    value: null
+
+  ## The frame that the animation should start at. Setting this value automatically updates the "animationStartTime"
+  ## to the "animationStartFrame" / "fps"
+
+  animationStartFrame:
+    set: |
+      this.animationStartTime = value / this.animationFPS;
+    get: |
+      return Math.floor(this.animationStartTime * this.animationFPS);
+
+  ## The frame that the animation should stop at. Setting this value automatically updates the "animationStopTime"
+  ## to the "animationStopFrame" / "fps"
+
+  animationStopFrame:
+    set: |
+      this.animationStopTime = value / this.animationFPS;
+    get: |
+      return Math.floor(this.animationStopTime * this.animationFPS);
+
+  ## The frames per second of the animation loaded from the source model.
+
+  animationFPS: 30
+
+  ## The number of frames of the animation loaded from the source model. 
+  animationFrames:
+    set: |
+      this.animationDuration = value / this.animationFPS;
+    get: |
+      return Math.ceil(this.animationFPS * this.animationDuration); 
+
+  ## The current frame the animation is on. Equivalent to animationTime.
+  animationFrame:
+    set: |
+      if(this.animationFPS) {
+        this.animationTime = value / this.animationFPS;
+      }
+    get: |
+      if(this.animationFPS) {
+        return Math.floor(this.animationTime * this.animationFPS);
+      }
+
+  ## The animation update rate in ticks per second of simulation time.
+
+  animationTPS: 60
+
+methods:
+
+  # Play the animation from the start.
+
+  animationPlay:
+    parameters:
+      - startTime
+      - stopTime
+    body: |
+      if(!isNaN(stopTime)) {
+        this.animationStopTime = stopTime;
+      }
+      if(!isNaN(startTime)) {
+        this.animationStartTime = startTime;
+      }
+      this.animationPlaying = true;
+
+  # Pause the animation at the current position.
+
+  animationPause: |
+    this.animationPlaying = false;
+
+  # Resume the animation from the current position.
+
+  animationResume: |
+    this.animationPlaying = true;
+
+  # Stop the animation and reset the position to the start.
+
+  animationStop: |
+    this.animationPlaying = false;
+    this.animationTime = 0;
+
+  ## Update the animation. If `animationTime` has reached the end, stop or loop depending on the
+  ## `animationLoop` setting. Schedule the next tick if the animation did not stop.
+  ## 
+  ## `animationTick` is for internal use. Do not call this method directly.
+
+  animationTick: |
+    if ( this.animationPlaying ) {
+      // Read the time to recognize the current time and update.
+      // TODO: move side effects out of the getter!!! (says Kevin)
+      this.animationTime; 
+      // Loop or stop after reaching the end.
+      if ( this.time === this.animationStop$ ) {
+        if ( this.animationLoop ) {
+          this.animationLooped();
+          this.animationTime = this.animationRate >= 0 ?
+            this.animationStartTime : this.animationStopTime;
+        } else {
+          this.animationPlaying = false;
+        }
+      }
+      // Schedule the next tick if still playing.
+      if ( this.animationPlaying ) {
+        if ( this.animationStop$ - this.time > 1 / this.animationTPS ) {
+          this.in( 1 / this.animationTPS ).animationTick(); // next interval
+        } else {
+
+          // TODO: When animationStop$ is 0 (usually when a model does not actually have an 
+          //       animation on it), we schedule a method call for a time in the past (at time 0).
+          //       That immediately calls animationTick again, but this.time does not equal 
+          //       animationStop$ as we would expect.  So, it doesn't stop the animation and we get
+          //       caught in an infinite loop.
+          //       Ideally we should catch the case where animationStop$ is 0 before this point.
+          //       But for now, we catch it here.  
+          if ( this.animationStop$ > 0 ) {
+            this.at( this.animationStop$ ).animationTick(); // exactly at end
+          } else {
+            this.animationPlaying = false;
+          }
+        }
+      } else {
+        this.logger.debug( "canceling animationTick" );
+      }
+    } //@ sourceURL=animation.animationTick.vwf
+
+  ## `animation.vwf` calls `animationUpdate` each time `animationTime` changes. Nodes that
+  ## implement `animation.vwf` should provide an `animationUpdate` method to calculate the animation
+  ## state appropriate for the node.
+  ## 
+  ## Since this event is sent within the `animationTime` getter function, references to
+  ## `this.animationTime` will return `undefined` due to reentrancy protections. Use the provided
+  ## `time` parameter instead.
+  ## 
+  ## @param {Number} time
+  ##   The animation's `animationTime`.
+  ## @param {Number} duration
+  ##   The animation's `animationDuration`.
+
+  animationUpdate:
+    parameters:
+      - time
+      - duration
+
+events:
+
+  # Sent when the animation starts playing from the beginning.
+
+  animationStarted:
+
+  # Sent when the animation reaches the end and `animationLoop` is not set.
+
+  animationStopped:
+
+  # Sent when the animation reaches the end and `animationLoop` is set.
+
+  animationLooped:
+
+  ## Sent each time `animationTime` changes.
+  ## 
+  ## Since this event is sent within the `animationTime` getter function, references to
+  ## `this.animationTime` will return `undefined` due to reentrancy protections. Use the provided
+  ## `time` parameter instead.
+  ## 
+  ## @param {Number} time
+  ##   The animation's `animationTime`.
+
+  animationTimeChanged:
+    parameters:
+      - time
+scripts:
+- |
+  this.initialize = function() {
+
+    // Locate child nodes that extend or implement "http://vwf.example.com/animation/position.vwf"
+    // to identify themselves as animation key positions.
+
+    var positions = this.find( "./element(*,'http://vwf.example.com/animation/position.vwf')" );
+
+    // Fill in missing `animationTime` properties, distributing evenly between the left and right
+    // positions that define `animationTime`.
+
+    // 1: [ - ] => [ 0 ]
+    // 1: [ 0, - ] => [ 0, 1 ]
+    // 1: [ -, 1 ] => [ 0, 1 ]
+    // 1: [ 0, -, - ] => [ 0, 1/2, 1 ]
+    // 1: [ -, -, 1 ] => [ 0, 1/2, 1 ]
+    // 1: [ 0, - , -, 1 ] => [ 0, 1/3 , 2/3, 1 ]
+
+    var leftTime, leftIndex;
+    var rightTime, rightIndex = -Infinity;
+
+    if ( positions.length > 0 ) {
+      
+      positions.sort(function(a, b) {
+        return a.sequence - b.sequence;
+      });
+
+      if ( positions[0].animationTime === null ) {
+        positions[0].animationTime = 0;
+      }
+
+      if ( positions[positions.length-1].animationTime === null ) {
+        positions[positions.length-1].animationTime = this.animationDuration;
+      }
+
+      positions.forEach( function( position, index ) {
+
+        if ( position.animationTime !== null ) {
+
+          leftTime = position.animationTime;
+          leftIndex = index;
+
+        } else {
+
+          if ( index > rightIndex ) {
+            for ( rightIndex = index + 1; rightIndex < positions.length; rightIndex++ ) {
+              if ( ( rightTime = /* assignment! */ positions[rightIndex].animationTime ) !== null ) {
+                break;
+              }
+            }
+          }
+
+          position.animationTime = leftTime + ( rightTime - leftTime ) *
+            ( index - leftIndex )  / ( rightIndex - leftIndex );
+
+        }
+
+      }, this );
+
+    }
+
+  } //@ sourceURL=http://vwf.example.com/animation.vwf/scripts~initialize

+ 420 - 0
public/defaults/animation/animationNode.vwf.yaml

@@ -0,0 +1,420 @@
+# Copyright 2012 United States Government, as represented by the Secretary of Defense, Under
+# Secretary of Defense (Personnel & Readiness).
+# 
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+# in compliance with the License. You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software distributed under the License
+# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+# or implied. See the License for the specific language governing permissions and limitations under
+# the License.
+
+## @name node3.animation.vwf
+## @namespace
+
+---
+extends:
+  http://vwf.example.com/animation/animation.vwf
+methods:
+  animationUpdate:
+    parameters:
+      - time
+      - duration
+  ## Translate by given translation over duration.
+  ## 
+  ## @name node3.animation.vwf#translateBy
+  ## @function
+  ##
+  ## @param {Array} translation
+  ## @param {Number} duration
+  ##
+  ## @returns undefined
+  translateBy:
+    parameters:
+      - translation
+      - duration
+    body: |
+      this.startTranslation$ = this.translation || goog.vec.Vec3.create();
+      var deltaTranslation = this.translationFromValue( translation );
+      this.stopTranslation$ = goog.vec.Vec3.add(
+        this.startTranslation$,
+        deltaTranslation,
+        goog.vec.Vec3.create()
+      );
+      if(duration > 0) {
+        this.animationDuration = duration;
+        this.animationUpdate = function(time, duration) {
+          this.translation = goog.vec.Vec3.lerp(
+            this.startTranslation$, this.stopTranslation$,
+            time >= duration ? 1 : time / duration,
+            goog.vec.Vec3.create()
+          );
+        }
+        this.animationPlay(0, duration);
+      }
+      else {
+        this.translation = this.stopTranslation$;
+      } //@ sourceURL=node3.animation.translateBy.vwf
+
+  ## Translate to given translation over duration.
+  ## 
+  ## @name node3.animation.vwf#translateTo
+  ## @function
+  ##
+  ## @param {Array} translation
+  ## @param {Number} duration
+  ##
+  ## @returns undefined
+  translateTo:
+    parameters:
+      - translation
+      - duration
+    body: |
+      this.startTranslation$ = this.translation || goog.vec.Vec3.create();
+      this.stopTranslation$ = this.translationFromValue( translation );
+      if(duration > 0) {
+        this.animationDuration = duration;
+        this.animationUpdate = function(time, duration) {
+          this.translation = goog.vec.Vec3.lerp(
+            this.startTranslation$, this.stopTranslation$,
+            duration == 0 ? duration : time / duration,
+            goog.vec.Vec3.create()
+          );
+        }
+        this.animationPlay(0, duration);
+      }
+      else {
+        this.translation = this.stopTranslation$;
+      } //@ sourceURL=node3.animation.translateTo.vwf
+
+  ## Rotate by given rotation over duration.
+  ## 
+  ## @name node3.animation.vwf#rotateBy
+  ## @function
+  ## 
+  ## @param {Array} rotation
+  ## @param {Number} [duration]
+  ## @param {String} [frame]
+  ## 
+  ## @returns undefined
+  rotateBy:
+    parameters:
+      - rotation
+      - duration
+      - frame
+    body: |
+      var rotation = this.rotationFromValue( rotation );
+      var deltaQuaternion = goog.vec.Quaternion.fromAngleAxis(
+        rotation[3] * Math.PI / 180,
+        goog.vec.Vec3.createFromValues( rotation[0], rotation[1], rotation[2] ),
+        goog.vec.Quaternion.create()
+      );
+      this.quaterniateBy( deltaQuaternion, duration, frame ); //@ sourceURL=node3.animation.rotateBy.vwf
+
+  ## Rotate to given rotation over duration.
+  ## 
+  ## @name node3.animation.vwf#rotateTo
+  ## @function
+  ## 
+  ## @param {Array} rotation
+  ## @param {Number} duration
+  ## 
+  ## @returns undefined
+  rotateTo:
+    parameters:
+      - rotation
+      - duration
+    body: |
+      var rotation = this.rotationFromValue( rotation );
+      var stopQuaternion = goog.vec.Quaternion.fromAngleAxis(
+        rotation[3] * Math.PI / 180,
+        goog.vec.Vec3.createFromValues( rotation[0], rotation[1], rotation[2] ),
+        goog.vec.Quaternion.create()
+      );
+      this.quaterniateTo( stopQuaternion, duration ); //@ sourceURL=node3.animation.rotateTo.vwf
+
+  ## Rotate by given quaternion over duration.
+  ## 
+  ## @name node3.animation.vwf#quaterniateBy
+  ## @function
+  ## 
+  ## @param {Array} quaternion
+  ## @param {Number} [duration]
+  ## @param {String} [frame]
+  ## 
+  ## @returns undefined
+  quaterniateBy:
+    parameters:
+      - quaternion
+      - duration
+      - frame
+    body: |
+      this.startQuaternion$ = this.quaternion || goog.vec.Quaternion.createFromValues( 0, 0, 0, 1 );
+      var deltaQuaternion = this.quaternionFromValue( quaternion );
+      if ( ! frame || frame == "rotated" ) {
+        this.stopQuaternion$ = goog.vec.Quaternion.concat(
+          deltaQuaternion,
+          this.startQuaternion$,
+          goog.vec.Quaternion.create()
+        );
+      } else if ( frame == "scaled" ) {
+        this.stopQuaternion$ = goog.vec.Quaternion.concat(
+          this.startQuaternion$,
+          deltaQuaternion,
+          goog.vec.Quaternion.create()
+        );
+      }
+      if(duration > 0) {
+        this.animationDuration = duration;
+        this.animationUpdate = function(time, duration) {
+          this.quaternion = goog.vec.Quaternion.slerp(
+            this.startQuaternion$, this.stopQuaternion$,
+            time >= duration ? 1 : time / duration,
+            goog.vec.Quaternion.create()
+          );
+        }
+        this.animationPlay(0, duration);
+      }
+      else {
+        this.quaternion = this.stopQuaternion$;
+      } //@ sourceURL=node3.animation.quaterniateBy.vwf
+
+  ## Rotate to given quaternion over duration.
+  ## 
+  ## @name node3.animation.vwf#quaterniateTo
+  ## @function
+  ## 
+  ## @param {Array} quaternion
+  ## @param {Number} duration
+  ## 
+  ## @returns undefined
+  quaterniateTo:
+    parameters:
+      - quaternion
+      - duration
+    body: |
+      this.startQuaternion$ = this.quaternion || goog.vec.Quaternion.createFromValues( 0, 0, 0, 1 );
+      this.stopQuaternion$ = this.quaternionFromValue( quaternion );
+      if(duration > 0) {
+        this.animationDuration = duration;
+        this.animationUpdate = function(time, duration) {
+          this.quaternion = goog.vec.Quaternion.slerp(
+            this.startQuaternion$, this.stopQuaternion$,
+            duration == 0 ? duration : time / duration,
+            goog.vec.Quaternion.create()
+          );
+        }
+        this.animationPlay(0, duration);
+      }
+      else {
+        this.quaternion = this.stopQuaternion$;
+      } //@ sourceURL=node3.animation.quaterniateTo.vwf
+
+  ## Scale by given scale over duration.
+  ## 
+  ## @name node3.animation.vwf#scaleBy
+  ## @function
+  ## 
+  ## @param {Array} scale
+  ## @param {Number} duration
+  ## 
+  ## @returns undefined
+  scaleBy:
+    parameters:
+      - scale
+      - duration
+    body: |
+      this.startScale$ = this.scale || goog.vec.Vec3.createFromValues( 1, 1, 1 );
+      var deltaScale = this.scaleFromValue( scale );
+      this.stopScale$ = goog.vec.Vec3.createFromValues(
+        this.startScale$[0] * deltaScale[0],
+        this.startScale$[1] * deltaScale[1],
+        this.startScale$[2] * deltaScale[2]
+      );
+      if(duration > 0) {
+        this.animationDuration = duration;
+        this.animationUpdate = function(time, duration) {
+          this.scale = goog.vec.Vec3.lerp(  // TODO: should be geometric interpolation
+            this.startScale$, this.stopScale$,
+            duration == 0 ? duration : time / duration,
+            goog.vec.Vec3.create()
+          );
+        }
+        this.animationPlay(0, duration);
+      }
+      else {
+        this.scale = this.stopScale$;
+      } //@ sourceURL=node3.animation.scaleBy.vwf
+
+  ## Scale to given scale over duration.
+  ## 
+  ## @name node3.animation.vwf#scaleTo
+  ## @function
+  ## 
+  ## @param {Array} scale
+  ## @param {Number} duration
+  ## 
+  ## @returns undefined
+  scaleTo:
+    parameters:
+      - scale
+      - duration
+    body: |
+      this.startScale$ = this.scale || goog.vec.Vec3.createFromValues( 1, 1, 1 );
+      this.stopScale$ = this.scaleFromValue( scale );
+      if(duration > 0) {
+        this.animationDuration = duration;
+        this.animationUpdate = function(time, duration) {
+          this.scale = goog.vec.Vec3.lerp(  // TODO: should be geometric interpolation
+            this.startScale$, this.stopScale$,
+            duration == 0 ? duration : time / duration,
+            goog.vec.Vec3.create()
+          );
+        }
+        this.animationPlay(0, duration); 
+      }
+      else {
+        this.scale = this.stopScale$;
+      }//@ sourceURL=node3.animation.scaleTo.vwf
+
+  ## Transform by the given amount over duration.
+  ## 
+  ## @name node3.animation.vwf#transformBy
+  ## @function
+  ##
+  ## @param {Array} transform
+  ## @param {Number} duration
+  ##
+  ## @returns undefined
+  transformBy:
+    parameters:
+      - transform
+      - duration
+    body: |
+      var startTransform = this.transform || goog.vec.Vec3.create();
+      var deltaTransform = this.transformFromValue( transform );
+      // Left multiply by the delta
+      var stopTransform = goog.vec.Mat4.multMat( deltaTransform, startTransform, goog.vec.Mat4.createFloat32() );
+      this.transformTo( stopTransform, duration ); //@ sourceURL=node3.animation.transformBy.vwf
+
+  ## Transform to given transform over duration.
+  ## 
+  ## @name node3.animation.vwf#transformTo
+  ## @function
+  ##
+  ## @param {Array} transform
+  ## @param {Number} duration
+  ##
+  ## @returns undefined
+  transformTo:
+    parameters:
+      - transform
+      - duration
+    body: |
+      var stopTransform = this.transformFromValue( transform ) || goog.vec.Mat4.createIdentity();
+
+      if ( duration > 0 ) {
+
+        // Calculate the start and stop translations
+        this.startTranslation$ = this.translation || goog.vec.Vec3.create();
+        this.stopTranslation$ = goog.vec.Vec3.create();
+        goog.vec.Mat4.getColumn( stopTransform, 3, this.stopTranslation$ );
+        // Calculate the start and stop quaternion and scale
+        this.startScale$ = this.scale || goog.vec.Vec3.createFromValues( 1, 1, 1 );
+        this.stopScale$ = goog.vec.Vec3.create();
+        this.startQuaternion$ = this.quaternion || goog.vec.Quaternion.createFromValues( 0, 0, 0, 1 );
+        this.stopQuaternion$ = goog.vec.Quaternion.fromRotationMatrix4(
+          this.unscaledTransform( stopTransform || goog.vec.Mat4.createIdentity(), this.stopScale$, goog.vec.Mat4.create() ),
+          goog.vec.Quaternion.create()
+        );
+
+        this.animationDuration = duration;
+        // Call the appropriate functions to do the translation and quaterniation (that is totally a word)
+        this.animationUpdate = function(time, duration) {
+          this.translation = goog.vec.Vec3.lerp(
+            this.startTranslation$, this.stopTranslation$,
+            duration == 0 ? duration : time / duration,
+            goog.vec.Vec3.create()
+          );
+          this.quaternion = goog.vec.Quaternion.slerp(
+            this.startQuaternion$, this.stopQuaternion$,
+            duration == 0 ? duration : time / duration,
+            goog.vec.Quaternion.create()
+          );
+          this.scale = goog.vec.Vec3.lerp(  // TODO: should be geometric interpolation
+            this.startScale$, this.stopScale$,
+            duration == 0 ? duration : time / duration,
+            goog.vec.Vec3.create()
+          );
+        }
+        this.animationPlay(0, duration);
+      }
+      else {
+        this.transform = stopTransform;
+      } //@ sourceURL=node3.animation.transformTo.vwf
+
+  ## Transform the world transform by the given amount over duration.
+  ## 
+  ## @name node3.animation.vwf#worldTransformBy
+  ## @function
+  ##
+  ## @param {Array} transform
+  ## @param {Number} duration
+  ##
+  ## @returns undefined
+  worldTransformBy:
+    parameters:
+      - transform
+      - duration
+    body: |
+      var startWorldTransform = this.worldTransform || goog.vec.Mat4.create();
+      var deltaTransform = this.transformFromValue( transform );
+      // Left multiply by the delta
+      var stopWorldTransform = goog.vec.Mat4.multMat( deltaTransform, startWorldTransform, 
+                                                      goog.vec.Mat4.createFloat32() );
+      this.worldTransformTo( stopWorldTransform, duration ) //@ sourceURL=node3.animation.worldTransformBy.vwf
+
+  ## Transform the world transform to given transform over duration.
+  ## 
+  ## @name node3.animation.vwf#worldTransformTo
+  ## @function
+  ##
+  ## @param {Array} transform
+  ## @param {Number} duration
+  ##
+  ## @returns undefined
+  worldTransformTo:
+    parameters:
+      - transform
+      - duration
+    body: |
+      var stopWorldTransform = this.transformFromValue( transform );
+      var stopTransform;
+      if ( this.parent && this.parent.worldTransform ) {
+        // We need to find the local transform that will bring about the desired world transform
+        // The math for this looks like -
+        // (new worldTransform) = (parentWorldTransform) * (transform)
+        // So, if we left multiply both sides by the inverse of the parentWorldTransform...
+        // inv(parentWorldTransform) * (new worldTransform) = (transform)
+        // Find the inverse parent worldTransform
+        var inverseParentWorldTransform = goog.vec.Mat4.createFloat32();
+        if ( goog.vec.Mat4.invert( this.parent.worldTransform, inverseParentWorldTransform ) ) {
+          // Left multiply the new worldTransform by the inverse parent worldTransform
+          stopTransform = goog.vec.Mat4.multMat( inverseParentWorldTransform, stopWorldTransform,
+                                                 goog.vec.Mat4.createFloat32() );
+        }
+        else {
+          stopTransform = goog.vec.Mat4.createIdentity();
+          this.logger.error( "Parent '" + this.parent.id + "' transform matrix is not invertible: " + 
+                             this.parent.transform );
+        }
+      }
+      else {
+        stopTransform = stopWorldTransform;
+      }
+      this.transformTo( stopTransform, duration ); //@ sourceURL=node3.animation.worldTransformTo.vwf
+
+event: 
+  changingTransformFromView:

+ 57 - 0
public/defaults/animation/position.vwf.yaml

@@ -0,0 +1,57 @@
+# Copyright 2012 United States Government, as represented by the Secretary of Defense, Under
+# Secretary of Defense (Personnel & Readiness).
+# 
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+# in compliance with the License. You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software distributed under the License
+# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+# or implied. See the License for the specific language governing permissions and limitations under
+# the License.
+
+## `animation.position.vwf` defines a key value for an animation.
+##
+## @name animation.position.vwf
+## @namespace
+
+---
+
+properties:
+
+  ## This position's location on the animation timeline. Valid values are in the range from `0` to
+  ## the animation's duration.
+  ## 
+  ## Positions that don't provide `time` will be automatically distributed along the range between
+  ## the siblings that do. If none of a node's animation positions provide `time`, then the
+  ## positions will be distributed evenly throughout the animation--in the range [`0`, *duration*].
+  ## 
+  ## @name animation.position.vwf#animationTime
+  ## @property
+
+  animationTime:
+
+  ## The animated node's transform at this position. The node's transform will be set to this value
+  ## when the animations reaches this position. Transforms for intermediate postions will be
+  ## interpolated from the transforms of the adjacent positions.
+  ##
+  ## if no times, distribute evenly over number of children
+  ## transforms optional; only for node3 animations, and may be provided by asset or by node's update
+  ## 
+  ## @name animation.position.vwf#animationTransform
+  ## @property
+
+  animationTransform:
+
+  ## animation positions and control positions are not necessarily coincident, although they typically are
+  ## animation data may be missing from control positions if no transform is necessary
+  ## control data may be missing from animation positions if value/time == 1, attraction does not change
+  ##
+  ## The sequence of the animation key value. Key values will be sorted on the sequence to keep the order correct,
+  ## because Ruby 1.8.7 doesn't preserve child order in component objects.
+  ## 
+  ## @name animation.position.vwf#sequence
+  ## @property
+
+  sequence: 0

+ 23 - 1
public/defaults/proxy/vwf.example.com/aframe/aentity.js

@@ -21,4 +21,26 @@ this.showCloseGizmo = function () {
             this.children.delete(this.gizmo)
         }
     }
-}
+}
+
+  // Parse a parameter as a translation specification.
+  this.translationFromValue = function( propertyValue ) {
+
+      var value = goog.vec.Vec3.create();
+    if (propertyValue.hasOwnProperty('x')) {
+        value = goog.vec.Vec3.createFromValues(propertyValue.x, propertyValue.y,  propertyValue.z)
+    } 
+    else if (Array.isArray(propertyValue) || propertyValue instanceof Float32Array ) {
+        value = goog.vec.Vec3.createFromArray(propertyValue);}
+    else if (typeof propertyValue === 'string') {
+        let val = AFRAME.utils.coordinates.parse(propertyValue);
+        value = goog.vec.Vec3.createFromValues(val.x, val.y, val.z)
+        }
+
+    return value
+
+
+    // return value && value.length >= 3 ?
+    //   value :
+    //   goog.vec.Vec3.create();
+  };

+ 17 - 1
public/defaults/proxy/vwf.example.com/aframe/aentity.vwf.yaml

@@ -2,8 +2,19 @@
 --- 
 extends: http://vwf.example.com/aframe/node.vwf
 type: "a-entity"
+implements:
+- http://vwf.example.com/animation/animation.vwf
+- http://vwf.example.com/animation/animationNode.vwf
 properties:
   position:
+    set: |
+      var position = this.translationFromValue( value ); // parse incoming value
+      if ( ! goog.vec.Vec3.equals( this.position || goog.vec.Vec3.create(), position ) ) {
+        this.position = position;
+        this.positionChanged( position);
+      }  
+    get: |
+      return this.position || goog.vec.Vec3.create();
   rotation:
   scale:
   clickable:
@@ -11,6 +22,8 @@ properties:
   visible:
   edit:
   osc:
+events:
+  positionChanged:
 methods:
   setGizmoMode:
     parameters:
@@ -21,5 +34,8 @@ methods:
       - nodeID
   worldRotation:
   worldPosition:
+  translationFromValue:
+    parameters:
+      - propertyValue
 scripts:
-  - source: "http://vwf.example.com/aframe/aentity.js"
+  - source: "http://vwf.example.com/aframe/aentity.js"

+ 4 - 3
public/defaults/proxy/vwf.example.com/aframe/avatar.js

@@ -72,7 +72,7 @@ this.createAvatarBody = function (nodeDef, modelSrc) {
                         "extends": "http://vwf.example.com/aframe/interpolation-component.vwf",
                         "type": "component",
                         "properties": {
-                            "enabled": true
+                            "enabled": false
                         }
                     },
                     "visual": {
@@ -228,7 +228,7 @@ this.createAvatarBody = function (nodeDef, modelSrc) {
         "extends": "http://vwf.example.com/aframe/interpolation-component.vwf",
         "type": "component",
         "properties": {
-            "enabled": true
+            "enabled": false
         }
     }
 
@@ -268,7 +268,8 @@ this.followAvatarControl = function (position, rotation) {
     // this.rotation = AFRAME.utils.coordinates.stringify(rotation);
 //debugger;
 
-    this.position = AFRAME.utils.coordinates.stringify(position);
+   // this.position = AFRAME.utils.coordinates.stringify(position);
+   this.position = position;
     let myRot = AFRAME.utils.coordinates.parse(this.rotation);
     let myHeadRot = AFRAME.utils.coordinates.parse(this.avatarNode.myHead.rotation);
     let myBodyRot = AFRAME.utils.coordinates.parse(this.avatarNode.myBody.rotation);

+ 500 - 0
public/defaults/proxy/vwf.example.com/animation/animation.vwf.yaml

@@ -0,0 +1,500 @@
+# Copyright 2012 United States Government, as represented by the Secretary of Defense, Under
+# Secretary of Defense (Personnel & Readiness).
+# 
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+# in compliance with the License. You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software distributed under the License
+# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+# or implied. See the License for the specific language governing permissions and limitations under
+# the License.
+
+## `animation.vwf` adds standard animation functions to a node.
+## 
+## Animations play over a given `animationDuration` and at a given `animationRate` with respect to
+## simulation time. `animationTime` tracks time as the animation plays. Setting `animationTime`
+## moves the animation. The animation plays once to the end, or with `animationLoop` repeats
+## indefinitely.
+## 
+## Events are sent to indicate `animationStarted`, `animationStopped` and `animationLooped`. Changes
+## to the animation time are announced with `animationTimeChanged.`
+##
+## A node may provide an `animationUpdate` method to calculate its own animation state, or it may
+## use a specialized behavior that implements a standard animation.
+## 
+## @name animation.vwf
+## @namespace
+
+# The duration must be a non-negative number, times are always in the range [ `0`, `duration` ], and
+# the rate may be any number (although there is little protection against rates very near zero).
+# 
+#   0 <= animationDuration
+#   0 <= animationTime <= animationDuration
+#   -Infinity < animationRate < Infinity
+# 
+# The animation play state and time index are maintained in `animationStartSIM` and
+# `animationPauseSIM`. `animationPauseSIM` is `null` while the animation is playing. The
+# distance from `animationStartSIM` to the current time (when playing) or `animationPauseSIM`
+# (when paused) gives the time index, after accounting for the play rate.
+# 
+#   animationTime = ( ( animationPauseSIM || time ) - ( animationStartSIM || time ) ) *
+#     animationRate (positive rates)
+#   animationTime = ( ( animationPauseSIM || time ) - ( animationStartSIM || time ) ) *
+#     animationRate + animationDuration (negative rates)
+# 
+#   animationPlaying = this.animationStartSIM != null && this.animationPauseSIM == null
+# 
+# `animationStartSIM` and `animationPauseSIM` are `null` before the animation is first played.
+# This state is interpreted as `animationPlaying` == `false` and `animationTime` == `0`.
+# 
+#   animationStartSIM == null => animationPauseSIM == null
+#   animationStartSIM == null => animationStopSIM == null
+# 
+#   animationStartSIM != null => animationStartSIM <= time
+#   animationPauseSIM != null => animationStartSIM <= animationPauseSIM <= time
+#   animationStopSIM != null => animationStartSIM + animationDurationSIM == animationStopSIM
+# 
+#   animationDurationSIM == animationDuration / animationRate (positive rates)
+#   animationDurationSIM == -animationDuration / animationRate (negative rates)
+# 
+# Properties named with a`SIM` suffix refer to times or durations on the simulation timeline. All
+# other time-related properties refer to the animation timeline.
+
+---
+
+properties:
+
+  ## The current animation position in seconds.
+  ## 
+  ## `animationTime` automatically moves with the simulation time while the animation is playing,
+  ## tracking the change in time multiplied by `animationRate`. A negative `animationRate` will
+  ## cause `animationTime` to move backwards. Setting `animationTime` updates the postion whether or
+  ## not the animation is currently playing.
+
+  animationTime:
+
+    set: |
+      if(this.animationStartTime == null) {
+        this.animationStartTime = 0;
+      }
+      if(this.animationStopTime == null) {
+        this.animationStopTime = this.animationDuration;
+      }
+      // Save copies to avoid repeated reads.
+      var duration = this.animationStopTime - this.animationStartTime,
+        rate = this.animationRate;
+      // Range limit the incoming value.
+      value = Math.min( Math.max( this.animationStartTime, value ), this.animationStopTime );
+      // Keep paused if updating start/pause from null/null. Use t=0 instead of `this.time` so that
+      // setting `node.animationTime` during initialization is consistent across multiple clients.
+      if ( this.animationStartSIM == null ) {
+        this.animationPauseSIM = 0;
+      }
+      // Calculate the start and stop times that makes the new time work.
+      this.animationStartSIM =
+        ( this.animationPauseSIM != null ? this.animationPauseSIM : this.time ) -
+        ( rate >= 0 ? value - this.animationStartTime : value - duration ) / rate;
+      this.animationStopSIM =
+        this.animationStartSIM +
+        ( rate >= 0 ? duration : -duration ) / rate;
+      // Update the node and fire the changed event.
+      if ( value !== this.animationTimeUpdated ) {
+        this.animationTimeUpdated = value;
+        this.animationUpdate( value, this.animationDuration );
+        this.animationTimeChanged( value );
+      } //@ sourceURL=animation.animationTime.set.vwf
+
+    get: |
+      // Save copies to avoid repeated reads.
+      var startTime = this.animationStartTime;
+      var stopTime = this.animationStopTime;
+      var rate = this.animationRate;
+      var animationPauseSIM = this.animationPauseSIM;
+      var animationStartSIM = this.animationStartSIM;
+      var time = this.time;
+      // Calculate the time from the start and current/pause times.
+      var value = (
+        ( animationPauseSIM != null ? animationPauseSIM : time ) -
+        ( animationStartSIM != null ? animationStartSIM : time )
+      ) * rate + ( rate >= 0 ? startTime : stopTime );
+      // Range limit the value.
+      value = Math.min( Math.max( startTime, value ), stopTime );
+      // If changed since last seen, update and fire the changed event.
+      if ( value !== this.animationTimeUpdated ) {
+        this.animationTimeUpdated = value;
+        this.animationUpdate( value, this.animationDuration );
+        this.animationTimeChanged( value );
+      }
+      return value; //@ sourceURL=animation.animationTime.get.vwf
+
+  ## The length of the animation in seconds.
+  ## 
+  ## `animationTime` will always be within the range [ `0`, `animationDuration` ]. Animations
+  ## automatically stop playing once `animationTime` reaches `animationDuration` unless
+  ## `animationLoop` is set. Animations with a negative `animationRate` start at
+  ## `animationDuration` and stop at `0`.
+
+  animationDuration:  # TODO: allow changes while playing
+    set: |
+      var duration = value, rate = this.animationRate;
+      this.animationDuration = duration;
+      this.animationDurationSIM = ( rate >= 0 ? duration : -duration ) / rate;
+    value: 1
+
+  ## The animation playback rate.
+  ## 
+  ## Set `animationRate` to a value greater than `1` to increase the rate. Set a value between `0`
+  ## and `1` to decrease it. Negative rates will cause the animation to play backwards.
+
+  animationRate:  # TODO: allow changes while playing
+    set: |
+      var duration = this.animationDuration, rate = value;
+      this.animationRate = rate;
+      this.animationDurationSIM = ( rate >= 0 ? duration : -duration ) / rate;
+    value: 1
+
+  ## Automatically replay the animation from the start after reaching the end.
+
+  animationLoop: false
+
+  ## The animation's play/pause control.
+
+  animationPlaying:
+
+    set: |
+      if(this.animationStartTime == null) {
+        this.animationStartTime = 0;
+      }
+      if(this.animationStopTime == null) {
+        this.animationStopTime = this.animationDuration;
+      }
+      if ( this.animationStartSIM != null && this.animationPauseSIM == null ) {
+        if ( ! value ) {
+          // Mark as paused at the current time.
+          this.animationPauseSIM = this.time;
+          // Send the `animationStopped` event if stopping at the end.
+          if ( this.time == this.animationStopSIM ) {
+            this.animationStopped();
+          }
+        }
+      } else {
+        if ( value ) {
+          // Save copies to avoid repeated reads.
+          var duration = this.animationStopTime - this.animationStartTime,
+            rate = this.animationRate;
+          // Start from the beginning if resuming from the end.
+          if ( this.animationPauseSIM == this.animationStopSIM ) {
+            this.animationPauseSIM = this.animationStartSIM;
+          }
+          // Recalculate the start and stop times to keep paused time unchanged, then resume.
+          this.animationStartSIM =
+            ( this.animationStartSIM != null ? this.animationStartSIM : this.time ) -
+            ( this.animationPauseSIM != null ? this.animationPauseSIM : this.time ) +
+            this.time;
+          this.animationStopSIM =
+            this.animationStartSIM +
+            ( rate >= 0 ? duration : -duration ) / rate;
+          this.animationPauseSIM = null;
+          // Send the `animationStarted` event if starting from the beginning.
+          if ( this.time == this.animationStartSIM ) {
+            this.animationStarted();
+          }
+          // Start the animation worker.
+          this.logger.debug( "scheduling animationTick" );
+          this.animationTick();
+        }
+      } //@ sourceURL=animation.animationPlaying.set.vwf
+
+    get: |
+      return this.animationStartSIM != null && this.animationPauseSIM == null;
+
+  ## The most recent value of `animationTime` recognized by this behavior.
+  ## 
+  ## Each `animationTime` change causes `animationUpdate` to be called. `animationTimeUpdated`
+  ## records the value of `animationTime` from the last time this occurred.
+  ## 
+  ## `animationTimeUpdated` is for internal use. Do not set this property.
+
+  animationTimeUpdated: null
+
+  ## The simulation time corresponding to the start of the animation.
+  ## 
+  ## `animationStartSIM` is `null` until the animation is first played. `animationPlaying` is
+  ## `false` and `animationTime` is `0` while `animationStartSIM` is `null`.
+  ## 
+  ## `animationStartSIM` is for internal use. Do not set this property.
+
+  animationStartSIM: null
+
+  ## The simulation time corresponding to the animation's pause position.
+  ## 
+  ## `animationPauseSIM` is `null` while the animation is playing and before the animation is
+  ## first played.
+  ## 
+  ## `animationPauseSIM` is for internal use. Do not set this property.
+
+  animationPauseSIM: null
+
+  ## The simulation time corresponding to the end of the animation. The animation worker function
+  ## loops or stops the animation when `time` reaches this value.
+  ## 
+  ## `animationStopSIM` is for internal use. Do not set this property.
+
+  animationStopSIM: null
+
+  ## The animation's duration in simulation time, after considering `animationRate`.
+  ## 
+  ## `animationDurationSIM` is for internal use. Do not set this property.
+
+  animationDurationSIM: null
+
+  ## The time that the animation should start at. Used with animationStopTime to play
+  ## a subsection of the animation. Defaults to 0 when the animation starts to play
+  ## if it is not assigned another value. 'animationStartTime' will always be within 
+  ## the range [ `0`, `animationDuration` ]
+
+  animationStartTime: 
+    set: |
+      this.animationStartTime = value ? Math.min( Math.max( 0, value ), this.animationDuration ) : value;
+    value: null
+
+  ## The time that the animation should stop at. Used with animationStartTime to play
+  ## a subsection of the animation. Defaults to 'animationDuration' when the animation
+  ## starts to play if it is not assigned another value. 'animationStopTime' will always
+  ## be within the range [ `0`, `animationDuration` ]
+
+  animationStopTime:  
+    set: |
+      this.animationStopTime = value ? Math.min( Math.max( 0, value ), this.animationDuration ) : value;
+    value: null
+
+  ## The frame that the animation should start at. Setting this value automatically updates the "animationStartTime"
+  ## to the "animationStartFrame" / "fps"
+
+  animationStartFrame:
+    set: |
+      this.animationStartTime = value / this.animationFPS;
+    get: |
+      return Math.floor(this.animationStartTime * this.animationFPS);
+
+  ## The frame that the animation should stop at. Setting this value automatically updates the "animationStopTime"
+  ## to the "animationStopFrame" / "fps"
+
+  animationStopFrame:
+    set: |
+      this.animationStopTime = value / this.animationFPS;
+    get: |
+      return Math.floor(this.animationStopTime * this.animationFPS);
+
+  ## The frames per second of the animation loaded from the source model.
+
+  animationFPS: 30
+
+  ## The number of frames of the animation loaded from the source model. 
+  animationFrames:
+    set: |
+      this.animationDuration = value / this.animationFPS;
+    get: |
+      return Math.ceil(this.animationFPS * this.animationDuration); 
+
+  ## The current frame the animation is on. Equivalent to animationTime.
+  animationFrame:
+    set: |
+      if(this.animationFPS) {
+        this.animationTime = value / this.animationFPS;
+      }
+    get: |
+      if(this.animationFPS) {
+        return Math.floor(this.animationTime * this.animationFPS);
+      }
+
+  ## The animation update rate in ticks per second of simulation time.
+
+  animationTPS: 60
+
+methods:
+
+  # Play the animation from the start.
+
+  animationPlay:
+    parameters:
+      - startTime
+      - stopTime
+    body: |
+      if(!isNaN(stopTime)) {
+        this.animationStopTime = stopTime;
+      }
+      if(!isNaN(startTime)) {
+        this.animationStartTime = startTime;
+      }
+      this.animationPlaying = true;
+
+  # Pause the animation at the current position.
+
+  animationPause: |
+    this.animationPlaying = false;
+
+  # Resume the animation from the current position.
+
+  animationResume: |
+    this.animationPlaying = true;
+
+  # Stop the animation and reset the position to the start.
+
+  animationStop: |
+    this.animationPlaying = false;
+    this.animationTime = 0;
+
+  ## Update the animation. If `animationTime` has reached the end, stop or loop depending on the
+  ## `animationLoop` setting. Schedule the next tick if the animation did not stop.
+  ## 
+  ## `animationTick` is for internal use. Do not call this method directly.
+
+  animationTick: |
+    if ( this.animationPlaying ) {
+      // Read the time to recognize the current time and update.
+      // TODO: move side effects out of the getter!!! (says Kevin)
+      this.animationTime; 
+      // Loop or stop after reaching the end.
+      if ( this.time === this.animationStopSIM ) {
+        if ( this.animationLoop ) {
+          this.animationLooped();
+          this.animationTime = this.animationRate >= 0 ?
+            this.animationStartTime : this.animationStopTime;
+        } else {
+          this.animationPlaying = false;
+        }
+      }
+      // Schedule the next tick if still playing.
+      if ( this.animationPlaying ) {
+        if ( this.animationStopSIM - this.time > 1 / this.animationTPS ) {
+          this.in( 1 / this.animationTPS ).animationTick(); // next interval
+        } else {
+
+          // TODO: When animationStopSIM is 0 (usually when a model does not actually have an 
+          //       animation on it), we schedule a method call for a time in the past (at time 0).
+          //       That immediately calls animationTick again, but this.time does not equal 
+          //       animationStopSIM as we would expect.  So, it doesn't stop the animation and we get
+          //       caught in an infinite loop.
+          //       Ideally we should catch the case where animationStopSIM is 0 before this point.
+          //       But for now, we catch it here.  
+          if ( this.animationStopSIM > 0 ) {
+            this.at( this.animationStopSIM ).animationTick(); // exactly at end
+          } else {
+            this.animationPlaying = false;
+          }
+        }
+      } else {
+        this.logger.debug( "canceling animationTick" );
+      }
+    } //@ sourceURL=animation.animationTick.vwf
+
+  ## `animation.vwf` calls `animationUpdate` each time `animationTime` changes. Nodes that
+  ## implement `animation.vwf` should provide an `animationUpdate` method to calculate the animation
+  ## state appropriate for the node.
+  ## 
+  ## Since this event is sent within the `animationTime` getter function, references to
+  ## `this.animationTime` will return `undefined` due to reentrancy protections. Use the provided
+  ## `time` parameter instead.
+  ## 
+  ## @param {Number} time
+  ##   The animation's `animationTime`.
+  ## @param {Number} duration
+  ##   The animation's `animationDuration`.
+
+  animationUpdate:
+    parameters:
+      - time
+      - duration
+
+events:
+
+  # Sent when the animation starts playing from the beginning.
+
+  animationStarted:
+
+  # Sent when the animation reaches the end and `animationLoop` is not set.
+
+  animationStopped:
+
+  # Sent when the animation reaches the end and `animationLoop` is set.
+
+  animationLooped:
+
+  ## Sent each time `animationTime` changes.
+  ## 
+  ## Since this event is sent within the `animationTime` getter function, references to
+  ## `this.animationTime` will return `undefined` due to reentrancy protections. Use the provided
+  ## `time` parameter instead.
+  ## 
+  ## @param {Number} time
+  ##   The animation's `animationTime`.
+
+  animationTimeChanged:
+    parameters:
+      - time
+scripts:
+- |
+  this.initialize = function() {
+
+    // Locate child nodes that extend or implement "http://vwf.example.com/animation/position.vwf"
+    // to identify themselves as animation key positions.
+
+    var positions = this.find( "./element(*,'http://vwf.example.com/animation/position.vwf')" );
+
+    // Fill in missing `animationTime` properties, distributing evenly between the left and right
+    // positions that define `animationTime`.
+
+    // 1: [ - ] => [ 0 ]
+    // 1: [ 0, - ] => [ 0, 1 ]
+    // 1: [ -, 1 ] => [ 0, 1 ]
+    // 1: [ 0, -, - ] => [ 0, 1/2, 1 ]
+    // 1: [ -, -, 1 ] => [ 0, 1/2, 1 ]
+    // 1: [ 0, - , -, 1 ] => [ 0, 1/3 , 2/3, 1 ]
+
+    var leftTime, leftIndex;
+    var rightTime, rightIndex = -Infinity;
+
+    if ( positions.length > 0 ) {
+      
+      positions.sort(function(a, b) {
+        return a.sequence - b.sequence;
+      });
+
+      if ( positions[0].animationTime === null ) {
+        positions[0].animationTime = 0;
+      }
+
+      if ( positions[positions.length-1].animationTime === null ) {
+        positions[positions.length-1].animationTime = this.animationDuration;
+      }
+
+      positions.forEach( function( position, index ) {
+
+        if ( position.animationTime !== null ) {
+
+          leftTime = position.animationTime;
+          leftIndex = index;
+
+        } else {
+
+          if ( index > rightIndex ) {
+            for ( rightIndex = index + 1; rightIndex < positions.length; rightIndex++ ) {
+              if ( ( rightTime = /* assignment! */ positions[rightIndex].animationTime ) !== null ) {
+                break;
+              }
+            }
+          }
+
+          position.animationTime = leftTime + ( rightTime - leftTime ) *
+            ( index - leftIndex )  / ( rightIndex - leftIndex );
+
+        }
+
+      }, this );
+
+    }
+
+  } //@ sourceURL=http://vwf.example.com/animation.vwf/scripts~initialize

+ 420 - 0
public/defaults/proxy/vwf.example.com/animation/animationNode.vwf.yaml

@@ -0,0 +1,420 @@
+# Copyright 2012 United States Government, as represented by the Secretary of Defense, Under
+# Secretary of Defense (Personnel & Readiness).
+# 
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+# in compliance with the License. You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software distributed under the License
+# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+# or implied. See the License for the specific language governing permissions and limitations under
+# the License.
+
+## @name node3.animation.vwf
+## @namespace
+
+---
+extends:
+  http://vwf.example.com/animation/animation.vwf
+methods:
+  animationUpdate:
+    parameters:
+      - time
+      - duration
+  ## Translate by given translation over duration.
+  ## 
+  ## @name node3.animation.vwf#translateBy
+  ## @function
+  ##
+  ## @param {Array} translation
+  ## @param {Number} duration
+  ##
+  ## @returns undefined
+  translateBy:
+    parameters:
+      - translation
+      - duration
+    body: |
+      this.startTranslationSIM = this.position || goog.vec.Vec3.create();
+      var deltaTranslation = this.translationFromValue( translation );
+      this.stopTranslationSIM = goog.vec.Vec3.add(
+        this.startTranslationSIM,
+        deltaTranslation,
+        goog.vec.Vec3.create()
+      );
+      if(duration > 0) {
+        this.animationDuration = duration;
+        this.animationUpdate = function(time, duration) {
+          this.position = goog.vec.Vec3.lerp(
+            this.startTranslationSIM, this.stopTranslationSIM,
+            time >= duration ? 1 : time / duration,
+            goog.vec.Vec3.create()
+          );
+        }
+        this.animationPlay(0, duration);
+      }
+      else {
+        this.position = this.stopTranslationSIM;
+      } //@ sourceURL=node3.animation.translateBy.vwf
+
+  ## Translate to given translation over duration.
+  ## 
+  ## @name node3.animation.vwf#translateTo
+  ## @function
+  ##
+  ## @param {Array} translation
+  ## @param {Number} duration
+  ##
+  ## @returns undefined
+  translateTo:
+    parameters:
+      - translation
+      - duration
+    body: |
+      this.startTranslationSIM = this.position || goog.vec.Vec3.create();
+      this.stopTranslationSIM = this.translationFromValue( translation );
+      if(duration > 0) {
+        this.animationDuration = duration;
+        this.animationUpdate = function(time, duration) {
+          this.position = goog.vec.Vec3.lerp(
+            this.startTranslationSIM, this.stopTranslationSIM,
+            duration == 0 ? duration : time / duration,
+            goog.vec.Vec3.create()
+          );
+        }
+        this.animationPlay(0, duration);
+      }
+      else {
+        this.position = this.stopTranslationSIM;
+      } //@ sourceURL=node3.animation.translateTo.vwf
+
+  ## Rotate by given rotation over duration.
+  ## 
+  ## @name node3.animation.vwf#rotateBy
+  ## @function
+  ## 
+  ## @param {Array} rotation
+  ## @param {Number} [duration]
+  ## @param {String} [frame]
+  ## 
+  ## @returns undefined
+  rotateBy:
+    parameters:
+      - rotation
+      - duration
+      - frame
+    body: |
+      var rotation = this.rotationFromValue( rotation );
+      var deltaQuaternion = goog.vec.Quaternion.fromAngleAxis(
+        rotation[3] * Math.PI / 180,
+        goog.vec.Vec3.createFromValues( rotation[0], rotation[1], rotation[2] ),
+        goog.vec.Quaternion.create()
+      );
+      this.quaterniateBy( deltaQuaternion, duration, frame ); //@ sourceURL=node3.animation.rotateBy.vwf
+
+  ## Rotate to given rotation over duration.
+  ## 
+  ## @name node3.animation.vwf#rotateTo
+  ## @function
+  ## 
+  ## @param {Array} rotation
+  ## @param {Number} duration
+  ## 
+  ## @returns undefined
+  rotateTo:
+    parameters:
+      - rotation
+      - duration
+    body: |
+      var rotation = this.rotationFromValue( rotation );
+      var stopQuaternion = goog.vec.Quaternion.fromAngleAxis(
+        rotation[3] * Math.PI / 180,
+        goog.vec.Vec3.createFromValues( rotation[0], rotation[1], rotation[2] ),
+        goog.vec.Quaternion.create()
+      );
+      this.quaterniateTo( stopQuaternion, duration ); //@ sourceURL=node3.animation.rotateTo.vwf
+
+  ## Rotate by given quaternion over duration.
+  ## 
+  ## @name node3.animation.vwf#quaterniateBy
+  ## @function
+  ## 
+  ## @param {Array} quaternion
+  ## @param {Number} [duration]
+  ## @param {String} [frame]
+  ## 
+  ## @returns undefined
+  quaterniateBy:
+    parameters:
+      - quaternion
+      - duration
+      - frame
+    body: |
+      this.startQuaternionSIM = this.quaternion || goog.vec.Quaternion.createFromValues( 0, 0, 0, 1 );
+      var deltaQuaternion = this.quaternionFromValue( quaternion );
+      if ( ! frame || frame == "rotated" ) {
+        this.stopQuaternionSIM = goog.vec.Quaternion.concat(
+          deltaQuaternion,
+          this.startQuaternionSIM,
+          goog.vec.Quaternion.create()
+        );
+      } else if ( frame == "scaled" ) {
+        this.stopQuaternionSIM = goog.vec.Quaternion.concat(
+          this.startQuaternionSIM,
+          deltaQuaternion,
+          goog.vec.Quaternion.create()
+        );
+      }
+      if(duration > 0) {
+        this.animationDuration = duration;
+        this.animationUpdate = function(time, duration) {
+          this.quaternion = goog.vec.Quaternion.slerp(
+            this.startQuaternionSIM, this.stopQuaternionSIM,
+            time >= duration ? 1 : time / duration,
+            goog.vec.Quaternion.create()
+          );
+        }
+        this.animationPlay(0, duration);
+      }
+      else {
+        this.quaternion = this.stopQuaternionSIM;
+      } //@ sourceURL=node3.animation.quaterniateBy.vwf
+
+  ## Rotate to given quaternion over duration.
+  ## 
+  ## @name node3.animation.vwf#quaterniateTo
+  ## @function
+  ## 
+  ## @param {Array} quaternion
+  ## @param {Number} duration
+  ## 
+  ## @returns undefined
+  quaterniateTo:
+    parameters:
+      - quaternion
+      - duration
+    body: |
+      this.startQuaternionSIM = this.quaternion || goog.vec.Quaternion.createFromValues( 0, 0, 0, 1 );
+      this.stopQuaternionSIM = this.quaternionFromValue( quaternion );
+      if(duration > 0) {
+        this.animationDuration = duration;
+        this.animationUpdate = function(time, duration) {
+          this.quaternion = goog.vec.Quaternion.slerp(
+            this.startQuaternionSIM, this.stopQuaternionSIM,
+            duration == 0 ? duration : time / duration,
+            goog.vec.Quaternion.create()
+          );
+        }
+        this.animationPlay(0, duration);
+      }
+      else {
+        this.quaternion = this.stopQuaternionSIM;
+      } //@ sourceURL=node3.animation.quaterniateTo.vwf
+
+  ## Scale by given scale over duration.
+  ## 
+  ## @name node3.animation.vwf#scaleBy
+  ## @function
+  ## 
+  ## @param {Array} scale
+  ## @param {Number} duration
+  ## 
+  ## @returns undefined
+  scaleBy:
+    parameters:
+      - scale
+      - duration
+    body: |
+      this.startScaleSIM = this.scale || goog.vec.Vec3.createFromValues( 1, 1, 1 );
+      var deltaScale = this.scaleFromValue( scale );
+      this.stopScaleSIM = goog.vec.Vec3.createFromValues(
+        this.startScaleSIM[0] * deltaScale[0],
+        this.startScaleSIM[1] * deltaScale[1],
+        this.startScaleSIM[2] * deltaScale[2]
+      );
+      if(duration > 0) {
+        this.animationDuration = duration;
+        this.animationUpdate = function(time, duration) {
+          this.scale = goog.vec.Vec3.lerp(  // TODO: should be geometric interpolation
+            this.startScaleSIM, this.stopScaleSIM,
+            duration == 0 ? duration : time / duration,
+            goog.vec.Vec3.create()
+          );
+        }
+        this.animationPlay(0, duration);
+      }
+      else {
+        this.scale = this.stopScaleSIM;
+      } //@ sourceURL=node3.animation.scaleBy.vwf
+
+  ## Scale to given scale over duration.
+  ## 
+  ## @name node3.animation.vwf#scaleTo
+  ## @function
+  ## 
+  ## @param {Array} scale
+  ## @param {Number} duration
+  ## 
+  ## @returns undefined
+  scaleTo:
+    parameters:
+      - scale
+      - duration
+    body: |
+      this.startScaleSIM = this.scale || goog.vec.Vec3.createFromValues( 1, 1, 1 );
+      this.stopScaleSIM = this.scaleFromValue( scale );
+      if(duration > 0) {
+        this.animationDuration = duration;
+        this.animationUpdate = function(time, duration) {
+          this.scale = goog.vec.Vec3.lerp(  // TODO: should be geometric interpolation
+            this.startScaleSIM, this.stopScaleSIM,
+            duration == 0 ? duration : time / duration,
+            goog.vec.Vec3.create()
+          );
+        }
+        this.animationPlay(0, duration); 
+      }
+      else {
+        this.scale = this.stopScaleSIM;
+      }//@ sourceURL=node3.animation.scaleTo.vwf
+
+  ## Transform by the given amount over duration.
+  ## 
+  ## @name node3.animation.vwf#transformBy
+  ## @function
+  ##
+  ## @param {Array} transform
+  ## @param {Number} duration
+  ##
+  ## @returns undefined
+  transformBy:
+    parameters:
+      - transform
+      - duration
+    body: |
+      var startTransform = this.transform || goog.vec.Vec3.create();
+      var deltaTransform = this.transformFromValue( transform );
+      // Left multiply by the delta
+      var stopTransform = goog.vec.Mat4.multMat( deltaTransform, startTransform, goog.vec.Mat4.createFloat32() );
+      this.transformTo( stopTransform, duration ); //@ sourceURL=node3.animation.transformBy.vwf
+
+  ## Transform to given transform over duration.
+  ## 
+  ## @name node3.animation.vwf#transformTo
+  ## @function
+  ##
+  ## @param {Array} transform
+  ## @param {Number} duration
+  ##
+  ## @returns undefined
+  transformTo:
+    parameters:
+      - transform
+      - duration
+    body: |
+      var stopTransform = this.transformFromValue( transform ) || goog.vec.Mat4.createIdentity();
+
+      if ( duration > 0 ) {
+
+        // Calculate the start and stop translations
+        this.startTranslationSIM = this.translation || goog.vec.Vec3.create();
+        this.stopTranslationSIM = goog.vec.Vec3.create();
+        goog.vec.Mat4.getColumn( stopTransform, 3, this.stopTranslationSIM );
+        // Calculate the start and stop quaternion and scale
+        this.startScaleSIM = this.scale || goog.vec.Vec3.createFromValues( 1, 1, 1 );
+        this.stopScaleSIM = goog.vec.Vec3.create();
+        this.startQuaternionSIM = this.quaternion || goog.vec.Quaternion.createFromValues( 0, 0, 0, 1 );
+        this.stopQuaternionSIM = goog.vec.Quaternion.fromRotationMatrix4(
+          this.unscaledTransform( stopTransform || goog.vec.Mat4.createIdentity(), this.stopScaleSIM, goog.vec.Mat4.create() ),
+          goog.vec.Quaternion.create()
+        );
+
+        this.animationDuration = duration;
+        // Call the appropriate functions to do the translation and quaterniation (that is totally a word)
+        this.animationUpdate = function(time, duration) {
+          this.translation = goog.vec.Vec3.lerp(
+            this.startTranslationSIM, this.stopTranslationSIM,
+            duration == 0 ? duration : time / duration,
+            goog.vec.Vec3.create()
+          );
+          this.quaternion = goog.vec.Quaternion.slerp(
+            this.startQuaternionSIM, this.stopQuaternionSIM,
+            duration == 0 ? duration : time / duration,
+            goog.vec.Quaternion.create()
+          );
+          this.scale = goog.vec.Vec3.lerp(  // TODO: should be geometric interpolation
+            this.startScaleSIM, this.stopScaleSIM,
+            duration == 0 ? duration : time / duration,
+            goog.vec.Vec3.create()
+          );
+        }
+        this.animationPlay(0, duration);
+      }
+      else {
+        this.transform = stopTransform;
+      } //@ sourceURL=node3.animation.transformTo.vwf
+
+  ## Transform the world transform by the given amount over duration.
+  ## 
+  ## @name node3.animation.vwf#worldTransformBy
+  ## @function
+  ##
+  ## @param {Array} transform
+  ## @param {Number} duration
+  ##
+  ## @returns undefined
+  worldTransformBy:
+    parameters:
+      - transform
+      - duration
+    body: |
+      var startWorldTransform = this.worldTransform || goog.vec.Mat4.create();
+      var deltaTransform = this.transformFromValue( transform );
+      // Left multiply by the delta
+      var stopWorldTransform = goog.vec.Mat4.multMat( deltaTransform, startWorldTransform, 
+                                                      goog.vec.Mat4.createFloat32() );
+      this.worldTransformTo( stopWorldTransform, duration ) //@ sourceURL=node3.animation.worldTransformBy.vwf
+
+  ## Transform the world transform to given transform over duration.
+  ## 
+  ## @name node3.animation.vwf#worldTransformTo
+  ## @function
+  ##
+  ## @param {Array} transform
+  ## @param {Number} duration
+  ##
+  ## @returns undefined
+  worldTransformTo:
+    parameters:
+      - transform
+      - duration
+    body: |
+      var stopWorldTransform = this.transformFromValue( transform );
+      var stopTransform;
+      if ( this.parent && this.parent.worldTransform ) {
+        // We need to find the local transform that will bring about the desired world transform
+        // The math for this looks like -
+        // (new worldTransform) = (parentWorldTransform) * (transform)
+        // So, if we left multiply both sides by the inverse of the parentWorldTransform...
+        // inv(parentWorldTransform) * (new worldTransform) = (transform)
+        // Find the inverse parent worldTransform
+        var inverseParentWorldTransform = goog.vec.Mat4.createFloat32();
+        if ( goog.vec.Mat4.invert( this.parent.worldTransform, inverseParentWorldTransform ) ) {
+          // Left multiply the new worldTransform by the inverse parent worldTransform
+          stopTransform = goog.vec.Mat4.multMat( inverseParentWorldTransform, stopWorldTransform,
+                                                 goog.vec.Mat4.createFloat32() );
+        }
+        else {
+          stopTransform = goog.vec.Mat4.createIdentity();
+          this.logger.error( "Parent '" + this.parent.id + "' transform matrix is not invertible: " + 
+                             this.parent.transform );
+        }
+      }
+      else {
+        stopTransform = stopWorldTransform;
+      }
+      this.transformTo( stopTransform, duration ); //@ sourceURL=node3.animation.worldTransformTo.vwf
+
+event: 
+  changingTransformFromView:

+ 57 - 0
public/defaults/proxy/vwf.example.com/animation/position.vwf.yaml

@@ -0,0 +1,57 @@
+# Copyright 2012 United States Government, as represented by the Secretary of Defense, Under
+# Secretary of Defense (Personnel & Readiness).
+# 
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+# in compliance with the License. You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software distributed under the License
+# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+# or implied. See the License for the specific language governing permissions and limitations under
+# the License.
+
+## `animation.position.vwf` defines a key value for an animation.
+##
+## @name animation.position.vwf
+## @namespace
+
+---
+
+properties:
+
+  ## This position's location on the animation timeline. Valid values are in the range from `0` to
+  ## the animation's duration.
+  ## 
+  ## Positions that don't provide `time` will be automatically distributed along the range between
+  ## the siblings that do. If none of a node's animation positions provide `time`, then the
+  ## positions will be distributed evenly throughout the animation--in the range [`0`, *duration*].
+  ## 
+  ## @name animation.position.vwf#animationTime
+  ## @property
+
+  animationTime:
+
+  ## The animated node's transform at this position. The node's transform will be set to this value
+  ## when the animations reaches this position. Transforms for intermediate postions will be
+  ## interpolated from the transforms of the adjacent positions.
+  ##
+  ## if no times, distribute evenly over number of children
+  ## transforms optional; only for node3 animations, and may be provided by asset or by node's update
+  ## 
+  ## @name animation.position.vwf#animationTransform
+  ## @property
+
+  animationTransform:
+
+  ## animation positions and control positions are not necessarily coincident, although they typically are
+  ## animation data may be missing from control positions if no transform is necessary
+  ## control data may be missing from animation positions if value/time == 1, attraction does not change
+  ##
+  ## The sequence of the animation key value. Key values will be sorted on the sequence to keep the order correct,
+  ## because Ruby 1.8.7 doesn't preserve child order in component objects.
+  ## 
+  ## @name animation.position.vwf#sequence
+  ## @property
+
+  sequence: 0

+ 7 - 0
public/index.html

@@ -59,6 +59,13 @@ Copyright (c) 2014-2018 Nikolai Suslov and the Krestianstvo.org project contribu
     <script type="text/javascript" src="/lib/mash.js"></script>
     <script type="text/javascript" src="/lib/ace/ace.js"></script>
 
+
+    <script type="text/javascript" src="/lib/closure/base.js"></script>
+    <script type="text/javascript">goog.require('goog.vec.Vec2')</script>
+    <script type="text/javascript">goog.require('goog.vec.Mat4')</script>
+    <script type="text/javascript">goog.require('goog.vec.Quaternion')</script>
+
+
     <script type="text/javascript" src="/vwf.js"></script>
 
     <link rel="stylesheet" type="text/css" href="/lib/index.css" />

+ 1479 - 0
public/lib/closure/base.js

@@ -0,0 +1,1479 @@
+// Copyright 2006 The Closure Library Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * @fileoverview Bootstrap for the Google JS Library (Closure).
+ *
+ * In uncompiled mode base.js will write out Closure's deps file, unless the
+ * global <code>CLOSURE_NO_DEPS</code> is set to true.  This allows projects to
+ * include their own deps file(s) from different locations.
+ *
+ */
+
+
+/**
+ * @define {boolean} Overridden to true by the compiler when --closure_pass
+ *     or --mark_as_compiled is specified.
+ */
+var COMPILED = false;
+
+
+/**
+ * Base namespace for the Closure library.  Checks to see goog is
+ * already defined in the current scope before assigning to prevent
+ * clobbering if base.js is loaded more than once.
+ *
+ * @const
+ */
+var goog = goog || {}; // Identifies this file as the Closure base.
+
+
+/**
+ * Reference to the global context.  In most cases this will be 'window'.
+ */
+goog.global = this;
+
+
+/**
+ * @define {boolean} DEBUG is provided as a convenience so that debugging code
+ * that should not be included in a production js_binary can be easily stripped
+ * by specifying --define goog.DEBUG=false to the JSCompiler. For example, most
+ * toString() methods should be declared inside an "if (goog.DEBUG)" conditional
+ * because they are generally used for debugging purposes and it is difficult
+ * for the JSCompiler to statically determine whether they are used.
+ */
+goog.DEBUG = true;
+
+
+/**
+ * @define {string} LOCALE defines the locale being used for compilation. It is
+ * used to select locale specific data to be compiled in js binary. BUILD rule
+ * can specify this value by "--define goog.LOCALE=<locale_name>" as JSCompiler
+ * option.
+ *
+ * Take into account that the locale code format is important. You should use
+ * the canonical Unicode format with hyphen as a delimiter. Language must be
+ * lowercase, Language Script - Capitalized, Region - UPPERCASE.
+ * There are few examples: pt-BR, en, en-US, sr-Latin-BO, zh-Hans-CN.
+ *
+ * See more info about locale codes here:
+ * http://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers
+ *
+ * For language codes you should use values defined by ISO 693-1. See it here
+ * http://www.w3.org/WAI/ER/IG/ert/iso639.htm. There is only one exception from
+ * this rule: the Hebrew language. For legacy reasons the old code (iw) should
+ * be used instead of the new code (he), see http://wiki/Main/IIISynonyms.
+ */
+goog.LOCALE = 'en';  // default to en
+
+
+/**
+ * Creates object stubs for a namespace.  The presence of one or more
+ * goog.provide() calls indicate that the file defines the given
+ * objects/namespaces.  Build tools also scan for provide/require statements
+ * to discern dependencies, build dependency files (see deps.js), etc.
+ * @see goog.require
+ * @param {string} name Namespace provided by this file in the form
+ *     "goog.package.part".
+ */
+goog.provide = function(name) {
+  if (!COMPILED) {
+    // Ensure that the same namespace isn't provided twice. This is intended
+    // to teach new developers that 'goog.provide' is effectively a variable
+    // declaration. And when JSCompiler transforms goog.provide into a real
+    // variable declaration, the compiled JS should work the same as the raw
+    // JS--even when the raw JS uses goog.provide incorrectly.
+    if (goog.isProvided_(name)) {
+      throw Error('Namespace "' + name + '" already declared.');
+    }
+    delete goog.implicitNamespaces_[name];
+
+    var namespace = name;
+    while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) {
+      if (goog.getObjectByName(namespace)) {
+        break;
+      }
+      goog.implicitNamespaces_[namespace] = true;
+    }
+  }
+
+  goog.exportPath_(name);
+};
+
+
+/**
+ * Marks that the current file should only be used for testing, and never for
+ * live code in production.
+ * @param {string=} opt_message Optional message to add to the error that's
+ *     raised when used in production code.
+ */
+goog.setTestOnly = function(opt_message) {
+  if (COMPILED && !goog.DEBUG) {
+    opt_message = opt_message || '';
+    throw Error('Importing test-only code into non-debug environment' +
+                opt_message ? ': ' + opt_message : '.');
+  }
+};
+
+
+if (!COMPILED) {
+
+  /**
+   * Check if the given name has been goog.provided. This will return false for
+   * names that are available only as implicit namespaces.
+   * @param {string} name name of the object to look for.
+   * @return {boolean} Whether the name has been provided.
+   * @private
+   */
+  goog.isProvided_ = function(name) {
+    return !goog.implicitNamespaces_[name] && !!goog.getObjectByName(name);
+  };
+
+  /**
+   * Namespaces implicitly defined by goog.provide. For example,
+   * goog.provide('goog.events.Event') implicitly declares
+   * that 'goog' and 'goog.events' must be namespaces.
+   *
+   * @type {Object}
+   * @private
+   */
+  goog.implicitNamespaces_ = {};
+}
+
+
+/**
+ * Builds an object structure for the provided namespace path,
+ * ensuring that names that already exist are not overwritten. For
+ * example:
+ * "a.b.c" -> a = {};a.b={};a.b.c={};
+ * Used by goog.provide and goog.exportSymbol.
+ * @param {string} name name of the object that this file defines.
+ * @param {*=} opt_object the object to expose at the end of the path.
+ * @param {Object=} opt_objectToExportTo The object to add the path to; default
+ *     is |goog.global|.
+ * @private
+ */
+goog.exportPath_ = function(name, opt_object, opt_objectToExportTo) {
+  var parts = name.split('.');
+  var cur = opt_objectToExportTo || goog.global;
+
+  // Internet Explorer exhibits strange behavior when throwing errors from
+  // methods externed in this manner.  See the testExportSymbolExceptions in
+  // base_test.html for an example.
+  if (!(parts[0] in cur) && cur.execScript) {
+    cur.execScript('var ' + parts[0]);
+  }
+
+  // Certain browsers cannot parse code in the form for((a in b); c;);
+  // This pattern is produced by the JSCompiler when it collapses the
+  // statement above into the conditional loop below. To prevent this from
+  // happening, use a for-loop and reserve the init logic as below.
+
+  // Parentheses added to eliminate strict JS warning in Firefox.
+  for (var part; parts.length && (part = parts.shift());) {
+    if (!parts.length && goog.isDef(opt_object)) {
+      // last part and we have an object; use it
+      cur[part] = opt_object;
+    } else if (cur[part]) {
+      cur = cur[part];
+    } else {
+      cur = cur[part] = {};
+    }
+  }
+};
+
+
+/**
+ * Returns an object based on its fully qualified external name.  If you are
+ * using a compilation pass that renames property names beware that using this
+ * function will not find renamed properties.
+ *
+ * @param {string} name The fully qualified name.
+ * @param {Object=} opt_obj The object within which to look; default is
+ *     |goog.global|.
+ * @return {?} The value (object or primitive) or, if not found, null.
+ */
+goog.getObjectByName = function(name, opt_obj) {
+  var parts = name.split('.');
+  var cur = opt_obj || goog.global;
+  for (var part; part = parts.shift(); ) {
+    if (goog.isDefAndNotNull(cur[part])) {
+      cur = cur[part];
+    } else {
+      return null;
+    }
+  }
+  return cur;
+};
+
+
+/**
+ * Globalizes a whole namespace, such as goog or goog.lang.
+ *
+ * @param {Object} obj The namespace to globalize.
+ * @param {Object=} opt_global The object to add the properties to.
+ * @deprecated Properties may be explicitly exported to the global scope, but
+ *     this should no longer be done in bulk.
+ */
+goog.globalize = function(obj, opt_global) {
+  var global = opt_global || goog.global;
+  for (var x in obj) {
+    global[x] = obj[x];
+  }
+};
+
+
+/**
+ * Adds a dependency from a file to the files it requires.
+ * @param {string} relPath The path to the js file.
+ * @param {Array} provides An array of strings with the names of the objects
+ *                         this file provides.
+ * @param {Array} requires An array of strings with the names of the objects
+ *                         this file requires.
+ */
+goog.addDependency = function(relPath, provides, requires) {
+  if (!COMPILED) {
+    var provide, require;
+    var path = relPath.replace(/\\/g, '/');
+    var deps = goog.dependencies_;
+    for (var i = 0; provide = provides[i]; i++) {
+      deps.nameToPath[provide] = path;
+      if (!(path in deps.pathToNames)) {
+        deps.pathToNames[path] = {};
+      }
+      deps.pathToNames[path][provide] = true;
+    }
+    for (var j = 0; require = requires[j]; j++) {
+      if (!(path in deps.requires)) {
+        deps.requires[path] = {};
+      }
+      deps.requires[path][require] = true;
+    }
+  }
+};
+
+
+
+
+// NOTE(nnaze): The debug DOM loader was included in base.js as an orignal
+// way to do "debug-mode" development.  The dependency system can sometimes
+// be confusing, as can the debug DOM loader's asyncronous nature.
+//
+// With the DOM loader, a call to goog.require() is not blocking -- the
+// script will not load until some point after the current script.  If a
+// namespace is needed at runtime, it needs to be defined in a previous
+// script, or loaded via require() with its registered dependencies.
+// User-defined namespaces may need their own deps file.  See http://go/js_deps,
+// http://go/genjsdeps, or, externally, DepsWriter.
+// http://code.google.com/closure/library/docs/depswriter.html
+//
+// Because of legacy clients, the DOM loader can't be easily removed from
+// base.js.  Work is being done to make it disableable or replaceable for
+// different environments (DOM-less JavaScript interpreters like Rhino or V8,
+// for example). See bootstrap/ for more information.
+
+
+/**
+ * @define {boolean} Whether to enable the debug loader.
+ *
+ * If enabled, a call to goog.require() will attempt to load the namespace by
+ * appending a script tag to the DOM (if the namespace has been registered).
+ *
+ * If disabled, goog.require() will simply assert that the namespace has been
+ * provided (and depend on the fact that some outside tool correctly ordered
+ * the script).
+ */
+goog.ENABLE_DEBUG_LOADER = true;
+
+
+/**
+ * Implements a system for the dynamic resolution of dependencies
+ * that works in parallel with the BUILD system. Note that all calls
+ * to goog.require will be stripped by the JSCompiler when the
+ * --closure_pass option is used.
+ * @see goog.provide
+ * @param {string} name Namespace to include (as was given in goog.provide())
+ *     in the form "goog.package.part".
+ */
+goog.require = function(name) {
+
+  // if the object already exists we do not need do do anything
+  // TODO(arv): If we start to support require based on file name this has
+  //            to change
+  // TODO(arv): If we allow goog.foo.* this has to change
+  // TODO(arv): If we implement dynamic load after page load we should probably
+  //            not remove this code for the compiled output
+  if (!COMPILED) {
+    if (goog.isProvided_(name)) {
+      return;
+    }
+
+    if (goog.ENABLE_DEBUG_LOADER) {
+      var path = goog.getPathFromDeps_(name);
+      if (path) {
+        goog.included_[path] = true;
+        goog.writeScripts_();
+        return;
+      }
+    }
+
+    var errorMessage = 'goog.require could not find: ' + name;
+    if (goog.global.console) {
+      goog.global.console['error'](errorMessage);
+    }
+
+
+      throw Error(errorMessage);
+
+  }
+};
+
+
+/**
+ * Path for included scripts
+ * @type {string}
+ */
+goog.basePath = '';
+
+
+/**
+ * A hook for overriding the base path.
+ * @type {string|undefined}
+ */
+goog.global.CLOSURE_BASE_PATH;
+
+
+/**
+ * Whether to write out Closure's deps file. By default,
+ * the deps are written.
+ * @type {boolean|undefined}
+ */
+goog.global.CLOSURE_NO_DEPS;
+
+
+/**
+ * A function to import a single script. This is meant to be overridden when
+ * Closure is being run in non-HTML contexts, such as web workers. It's defined
+ * in the global scope so that it can be set before base.js is loaded, which
+ * allows deps.js to be imported properly.
+ *
+ * The function is passed the script source, which is a relative URI. It should
+ * return true if the script was imported, false otherwise.
+ */
+goog.global.CLOSURE_IMPORT_SCRIPT;
+
+
+/**
+ * Null function used for default values of callbacks, etc.
+ * @return {void} Nothing.
+ */
+goog.nullFunction = function() {};
+
+
+/**
+ * The identity function. Returns its first argument.
+ *
+ * @param {...*} var_args The arguments of the function.
+ * @return {*} The first argument.
+ * @deprecated Use goog.functions.identity instead.
+ */
+goog.identityFunction = function(var_args) {
+  return arguments[0];
+};
+
+
+/**
+ * When defining a class Foo with an abstract method bar(), you can do:
+ *
+ * Foo.prototype.bar = goog.abstractMethod
+ *
+ * Now if a subclass of Foo fails to override bar(), an error
+ * will be thrown when bar() is invoked.
+ *
+ * Note: This does not take the name of the function to override as
+ * an argument because that would make it more difficult to obfuscate
+ * our JavaScript code.
+ *
+ * @type {!Function}
+ * @throws {Error} when invoked to indicate the method should be
+ *   overridden.
+ */
+goog.abstractMethod = function() {
+  throw Error('unimplemented abstract method');
+};
+
+
+/**
+ * Adds a {@code getInstance} static method that always return the same instance
+ * object.
+ * @param {!Function} ctor The constructor for the class to add the static
+ *     method to.
+ */
+goog.addSingletonGetter = function(ctor) {
+  ctor.getInstance = function() {
+    return ctor.instance_ || (ctor.instance_ = new ctor());
+  };
+};
+
+
+if (!COMPILED && goog.ENABLE_DEBUG_LOADER) {
+  /**
+   * Object used to keep track of urls that have already been added. This
+   * record allows the prevention of circular dependencies.
+   * @type {Object}
+   * @private
+   */
+  goog.included_ = {};
+
+
+  /**
+   * This object is used to keep track of dependencies and other data that is
+   * used for loading scripts
+   * @private
+   * @type {Object}
+   */
+  goog.dependencies_ = {
+    pathToNames: {}, // 1 to many
+    nameToPath: {}, // 1 to 1
+    requires: {}, // 1 to many
+    // used when resolving dependencies to prevent us from
+    // visiting the file twice
+    visited: {},
+    written: {} // used to keep track of script files we have written
+  };
+
+
+  /**
+   * Tries to detect whether is in the context of an HTML document.
+   * @return {boolean} True if it looks like HTML document.
+   * @private
+   */
+  goog.inHtmlDocument_ = function() {
+    var doc = goog.global.document;
+    return typeof doc != 'undefined' &&
+           'write' in doc;  // XULDocument misses write.
+  };
+
+
+  /**
+   * Tries to detect the base path of the base.js script that bootstraps Closure
+   * @private
+   */
+  goog.findBasePath_ = function() {
+    if (goog.global.CLOSURE_BASE_PATH) {
+      goog.basePath = goog.global.CLOSURE_BASE_PATH;
+      return;
+    } else if (!goog.inHtmlDocument_()) {
+      return;
+    }
+    var doc = goog.global.document;
+    var scripts = doc.getElementsByTagName('script');
+    // Search backwards since the current script is in almost all cases the one
+    // that has base.js.
+    for (var i = scripts.length - 1; i >= 0; --i) {
+      var src = scripts[i].src;
+      var qmark = src.lastIndexOf('?');
+      var l = qmark == -1 ? src.length : qmark;
+      if (src.substr(l - 7, 7) == 'base.js') {
+        goog.basePath = src.substr(0, l - 7);
+        return;
+      }
+    }
+  };
+
+
+  /**
+   * Imports a script if, and only if, that script hasn't already been imported.
+   * (Must be called at execution time)
+   * @param {string} src Script source.
+   * @private
+   */
+  goog.importScript_ = function(src) {
+    var importScript = goog.global.CLOSURE_IMPORT_SCRIPT ||
+        goog.writeScriptTag_;
+    if (!goog.dependencies_.written[src] && importScript(src)) {
+      goog.dependencies_.written[src] = true;
+    }
+  };
+
+
+  /**
+   * The default implementation of the import function. Writes a script tag to
+   * import the script.
+   *
+   * @param {string} src The script source.
+   * @return {boolean} True if the script was imported, false otherwise.
+   * @private
+   */
+  goog.writeScriptTag_ = function(src) {
+    if (goog.inHtmlDocument_()) {
+      var doc = goog.global.document;
+      doc.write(
+          '<script type="text/javascript" src="' + src + '"></' + 'script>');
+      return true;
+    } else {
+      return false;
+    }
+  };
+
+
+  /**
+   * Resolves dependencies based on the dependencies added using addDependency
+   * and calls importScript_ in the correct order.
+   * @private
+   */
+  goog.writeScripts_ = function() {
+    // the scripts we need to write this time
+    var scripts = [];
+    var seenScript = {};
+    var deps = goog.dependencies_;
+
+    function visitNode(path) {
+      if (path in deps.written) {
+        return;
+      }
+
+      // we have already visited this one. We can get here if we have cyclic
+      // dependencies
+      if (path in deps.visited) {
+        if (!(path in seenScript)) {
+          seenScript[path] = true;
+          scripts.push(path);
+        }
+        return;
+      }
+
+      deps.visited[path] = true;
+
+      if (path in deps.requires) {
+        for (var requireName in deps.requires[path]) {
+          // If the required name is defined, we assume that it was already
+          // bootstrapped by other means.
+          if (!goog.isProvided_(requireName)) {
+            if (requireName in deps.nameToPath) {
+              visitNode(deps.nameToPath[requireName]);
+            } else {
+              throw Error('Undefined nameToPath for ' + requireName);
+            }
+          }
+        }
+      }
+
+      if (!(path in seenScript)) {
+        seenScript[path] = true;
+        scripts.push(path);
+      }
+    }
+
+    for (var path in goog.included_) {
+      if (!deps.written[path]) {
+        visitNode(path);
+      }
+    }
+
+    for (var i = 0; i < scripts.length; i++) {
+      if (scripts[i]) {
+        goog.importScript_(goog.basePath + scripts[i]);
+      } else {
+        throw Error('Undefined script input');
+      }
+    }
+  };
+
+
+  /**
+   * Looks at the dependency rules and tries to determine the script file that
+   * fulfills a particular rule.
+   * @param {string} rule In the form goog.namespace.Class or project.script.
+   * @return {?string} Url corresponding to the rule, or null.
+   * @private
+   */
+  goog.getPathFromDeps_ = function(rule) {
+    if (rule in goog.dependencies_.nameToPath) {
+      return goog.dependencies_.nameToPath[rule];
+    } else {
+      return null;
+    }
+  };
+
+  goog.findBasePath_();
+
+  // Allow projects to manage the deps files themselves.
+  if (!goog.global.CLOSURE_NO_DEPS) {
+    goog.importScript_(goog.basePath + 'deps.js');
+  }
+}
+
+
+
+//==============================================================================
+// Language Enhancements
+//==============================================================================
+
+
+/**
+ * This is a "fixed" version of the typeof operator.  It differs from the typeof
+ * operator in such a way that null returns 'null' and arrays return 'array'.
+ * @param {*} value The value to get the type of.
+ * @return {string} The name of the type.
+ */
+goog.typeOf = function(value) {
+  var s = typeof value;
+  if (s == 'object') {
+    if (value) {
+      // Check these first, so we can avoid calling Object.prototype.toString if
+      // possible.
+      //
+      // IE improperly marshals tyepof across execution contexts, but a
+      // cross-context object will still return false for "instanceof Object".
+      if (value instanceof Array) {
+        return 'array';
+      } else if (value instanceof Object) {
+        return s;
+      }
+
+      // HACK: In order to use an Object prototype method on the arbitrary
+      //   value, the compiler requires the value be cast to type Object,
+      //   even though the ECMA spec explicitly allows it.
+      var className = Object.prototype.toString.call(
+          /** @type {Object} */ (value));
+      // In Firefox 3.6, attempting to access iframe window objects' length
+      // property throws an NS_ERROR_FAILURE, so we need to special-case it
+      // here.
+      if (className == '[object Window]') {
+        return 'object';
+      }
+
+      // We cannot always use constructor == Array or instanceof Array because
+      // different frames have different Array objects. In IE6, if the iframe
+      // where the array was created is destroyed, the array loses its
+      // prototype. Then dereferencing val.splice here throws an exception, so
+      // we can't use goog.isFunction. Calling typeof directly returns 'unknown'
+      // so that will work. In this case, this function will return false and
+      // most array functions will still work because the array is still
+      // array-like (supports length and []) even though it has lost its
+      // prototype.
+      // Mark Miller noticed that Object.prototype.toString
+      // allows access to the unforgeable [[Class]] property.
+      //  15.2.4.2 Object.prototype.toString ( )
+      //  When the toString method is called, the following steps are taken:
+      //      1. Get the [[Class]] property of this object.
+      //      2. Compute a string value by concatenating the three strings
+      //         "[object ", Result(1), and "]".
+      //      3. Return Result(2).
+      // and this behavior survives the destruction of the execution context.
+      if ((className == '[object Array]' ||
+           // In IE all non value types are wrapped as objects across window
+           // boundaries (not iframe though) so we have to do object detection
+           // for this edge case
+           typeof value.length == 'number' &&
+           typeof value.splice != 'undefined' &&
+           typeof value.propertyIsEnumerable != 'undefined' &&
+           !value.propertyIsEnumerable('splice')
+
+          )) {
+        return 'array';
+      }
+      // HACK: There is still an array case that fails.
+      //     function ArrayImpostor() {}
+      //     ArrayImpostor.prototype = [];
+      //     var impostor = new ArrayImpostor;
+      // this can be fixed by getting rid of the fast path
+      // (value instanceof Array) and solely relying on
+      // (value && Object.prototype.toString.vall(value) === '[object Array]')
+      // but that would require many more function calls and is not warranted
+      // unless closure code is receiving objects from untrusted sources.
+
+      // IE in cross-window calls does not correctly marshal the function type
+      // (it appears just as an object) so we cannot use just typeof val ==
+      // 'function'. However, if the object has a call property, it is a
+      // function.
+      if ((className == '[object Function]' ||
+          typeof value.call != 'undefined' &&
+          typeof value.propertyIsEnumerable != 'undefined' &&
+          !value.propertyIsEnumerable('call'))) {
+        return 'function';
+      }
+
+
+    } else {
+      return 'null';
+    }
+
+  } else if (s == 'function' && typeof value.call == 'undefined') {
+    // In Safari typeof nodeList returns 'function', and on Firefox
+    // typeof behaves similarly for HTML{Applet,Embed,Object}Elements
+    // and RegExps.  We would like to return object for those and we can
+    // detect an invalid function by making sure that the function
+    // object has a call method.
+    return 'object';
+  }
+  return s;
+};
+
+
+/**
+ * Returns true if the specified value is not |undefined|.
+ * WARNING: Do not use this to test if an object has a property. Use the in
+ * operator instead.  Additionally, this function assumes that the global
+ * undefined variable has not been redefined.
+ * @param {*} val Variable to test.
+ * @return {boolean} Whether variable is defined.
+ */
+goog.isDef = function(val) {
+  return val !== undefined;
+};
+
+
+/**
+ * Returns true if the specified value is |null|
+ * @param {*} val Variable to test.
+ * @return {boolean} Whether variable is null.
+ */
+goog.isNull = function(val) {
+  return val === null;
+};
+
+
+/**
+ * Returns true if the specified value is defined and not null
+ * @param {*} val Variable to test.
+ * @return {boolean} Whether variable is defined and not null.
+ */
+goog.isDefAndNotNull = function(val) {
+  // Note that undefined == null.
+  return val != null;
+};
+
+
+/**
+ * Returns true if the specified value is an array
+ * @param {*} val Variable to test.
+ * @return {boolean} Whether variable is an array.
+ */
+goog.isArray = function(val) {
+  return goog.typeOf(val) == 'array';
+};
+
+
+/**
+ * Returns true if the object looks like an array. To qualify as array like
+ * the value needs to be either a NodeList or an object with a Number length
+ * property.
+ * @param {*} val Variable to test.
+ * @return {boolean} Whether variable is an array.
+ */
+goog.isArrayLike = function(val) {
+  var type = goog.typeOf(val);
+  return type == 'array' || type == 'object' && typeof val.length == 'number';
+};
+
+
+/**
+ * Returns true if the object looks like a Date. To qualify as Date-like
+ * the value needs to be an object and have a getFullYear() function.
+ * @param {*} val Variable to test.
+ * @return {boolean} Whether variable is a like a Date.
+ */
+goog.isDateLike = function(val) {
+  return goog.isObject(val) && typeof val.getFullYear == 'function';
+};
+
+
+/**
+ * Returns true if the specified value is a string
+ * @param {*} val Variable to test.
+ * @return {boolean} Whether variable is a string.
+ */
+goog.isString = function(val) {
+  return typeof val == 'string';
+};
+
+
+/**
+ * Returns true if the specified value is a boolean
+ * @param {*} val Variable to test.
+ * @return {boolean} Whether variable is boolean.
+ */
+goog.isBoolean = function(val) {
+  return typeof val == 'boolean';
+};
+
+
+/**
+ * Returns true if the specified value is a number
+ * @param {*} val Variable to test.
+ * @return {boolean} Whether variable is a number.
+ */
+goog.isNumber = function(val) {
+  return typeof val == 'number';
+};
+
+
+/**
+ * Returns true if the specified value is a function
+ * @param {*} val Variable to test.
+ * @return {boolean} Whether variable is a function.
+ */
+goog.isFunction = function(val) {
+  return goog.typeOf(val) == 'function';
+};
+
+
+/**
+ * Returns true if the specified value is an object.  This includes arrays
+ * and functions.
+ * @param {*} val Variable to test.
+ * @return {boolean} Whether variable is an object.
+ */
+goog.isObject = function(val) {
+  var type = typeof val;
+  return type == 'object' && val != null || type == 'function';
+  // return Object(val) === val also works, but is slower, especially if val is
+  // not an object.
+};
+
+
+/**
+ * Gets a unique ID for an object. This mutates the object so that further
+ * calls with the same object as a parameter returns the same value. The unique
+ * ID is guaranteed to be unique across the current session amongst objects that
+ * are passed into {@code getUid}. There is no guarantee that the ID is unique
+ * or consistent across sessions. It is unsafe to generate unique ID for
+ * function prototypes.
+ *
+ * @param {Object} obj The object to get the unique ID for.
+ * @return {number} The unique ID for the object.
+ */
+goog.getUid = function(obj) {
+  // TODO(arv): Make the type stricter, do not accept null.
+
+  // In Opera window.hasOwnProperty exists but always returns false so we avoid
+  // using it. As a consequence the unique ID generated for BaseClass.prototype
+  // and SubClass.prototype will be the same.
+  return obj[goog.UID_PROPERTY_] ||
+      (obj[goog.UID_PROPERTY_] = ++goog.uidCounter_);
+};
+
+
+/**
+ * Removes the unique ID from an object. This is useful if the object was
+ * previously mutated using {@code goog.getUid} in which case the mutation is
+ * undone.
+ * @param {Object} obj The object to remove the unique ID field from.
+ */
+goog.removeUid = function(obj) {
+  // TODO(arv): Make the type stricter, do not accept null.
+
+  // DOM nodes in IE are not instance of Object and throws exception
+  // for delete. Instead we try to use removeAttribute
+  if ('removeAttribute' in obj) {
+    obj.removeAttribute(goog.UID_PROPERTY_);
+  }
+  /** @preserveTry */
+  try {
+    delete obj[goog.UID_PROPERTY_];
+  } catch (ex) {
+  }
+};
+
+
+/**
+ * Name for unique ID property. Initialized in a way to help avoid collisions
+ * with other closure javascript on the same page.
+ * @type {string}
+ * @private
+ */
+goog.UID_PROPERTY_ = 'closure_uid_' +
+    Math.floor(Math.random() * 2147483648).toString(36);
+
+
+/**
+ * Counter for UID.
+ * @type {number}
+ * @private
+ */
+goog.uidCounter_ = 0;
+
+
+/**
+ * Adds a hash code field to an object. The hash code is unique for the
+ * given object.
+ * @param {Object} obj The object to get the hash code for.
+ * @return {number} The hash code for the object.
+ * @deprecated Use goog.getUid instead.
+ */
+goog.getHashCode = goog.getUid;
+
+
+/**
+ * Removes the hash code field from an object.
+ * @param {Object} obj The object to remove the field from.
+ * @deprecated Use goog.removeUid instead.
+ */
+goog.removeHashCode = goog.removeUid;
+
+
+/**
+ * Clones a value. The input may be an Object, Array, or basic type. Objects and
+ * arrays will be cloned recursively.
+ *
+ * WARNINGS:
+ * <code>goog.cloneObject</code> does not detect reference loops. Objects that
+ * refer to themselves will cause infinite recursion.
+ *
+ * <code>goog.cloneObject</code> is unaware of unique identifiers, and copies
+ * UIDs created by <code>getUid</code> into cloned results.
+ *
+ * @param {*} obj The value to clone.
+ * @return {*} A clone of the input value.
+ * @deprecated goog.cloneObject is unsafe. Prefer the goog.object methods.
+ */
+goog.cloneObject = function(obj) {
+  var type = goog.typeOf(obj);
+  if (type == 'object' || type == 'array') {
+    if (obj.clone) {
+      return obj.clone();
+    }
+    var clone = type == 'array' ? [] : {};
+    for (var key in obj) {
+      clone[key] = goog.cloneObject(obj[key]);
+    }
+    return clone;
+  }
+
+  return obj;
+};
+
+
+/**
+ * Forward declaration for the clone method. This is necessary until the
+ * compiler can better support duck-typing constructs as used in
+ * goog.cloneObject.
+ *
+ * TODO(brenneman): Remove once the JSCompiler can infer that the check for
+ * proto.clone is safe in goog.cloneObject.
+ *
+ * @type {Function}
+ */
+Object.prototype.clone;
+
+
+/**
+ * A native implementation of goog.bind.
+ * @param {Function} fn A function to partially apply.
+ * @param {Object|undefined} selfObj Specifies the object which |this| should
+ *     point to when the function is run.
+ * @param {...*} var_args Additional arguments that are partially
+ *     applied to the function.
+ * @return {!Function} A partially-applied form of the function bind() was
+ *     invoked as a method of.
+ * @private
+ * @suppress {deprecated} The compiler thinks that Function.prototype.bind
+ *     is deprecated because some people have declared a pure-JS version.
+ *     Only the pure-JS version is truly deprecated.
+ */
+goog.bindNative_ = function(fn, selfObj, var_args) {
+  return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments));
+};
+
+
+/**
+ * A pure-JS implementation of goog.bind.
+ * @param {Function} fn A function to partially apply.
+ * @param {Object|undefined} selfObj Specifies the object which |this| should
+ *     point to when the function is run.
+ * @param {...*} var_args Additional arguments that are partially
+ *     applied to the function.
+ * @return {!Function} A partially-applied form of the function bind() was
+ *     invoked as a method of.
+ * @private
+ */
+goog.bindJs_ = function(fn, selfObj, var_args) {
+  if (!fn) {
+    throw new Error();
+  }
+
+  if (arguments.length > 2) {
+    var boundArgs = Array.prototype.slice.call(arguments, 2);
+    return function() {
+      // Prepend the bound arguments to the current arguments.
+      var newArgs = Array.prototype.slice.call(arguments);
+      Array.prototype.unshift.apply(newArgs, boundArgs);
+      return fn.apply(selfObj, newArgs);
+    };
+
+  } else {
+    return function() {
+      return fn.apply(selfObj, arguments);
+    };
+  }
+};
+
+
+/**
+ * Partially applies this function to a particular 'this object' and zero or
+ * more arguments. The result is a new function with some arguments of the first
+ * function pre-filled and the value of |this| 'pre-specified'.<br><br>
+ *
+ * Remaining arguments specified at call-time are appended to the pre-
+ * specified ones.<br><br>
+ *
+ * Also see: {@link #partial}.<br><br>
+ *
+ * Usage:
+ * <pre>var barMethBound = bind(myFunction, myObj, 'arg1', 'arg2');
+ * barMethBound('arg3', 'arg4');</pre>
+ *
+ * @param {Function} fn A function to partially apply.
+ * @param {Object|undefined} selfObj Specifies the object which |this| should
+ *     point to when the function is run.
+ * @param {...*} var_args Additional arguments that are partially
+ *     applied to the function.
+ * @return {!Function} A partially-applied form of the function bind() was
+ *     invoked as a method of.
+ * @suppress {deprecated} See above.
+ */
+goog.bind = function(fn, selfObj, var_args) {
+  // TODO(nicksantos): narrow the type signature.
+  if (Function.prototype.bind &&
+      // NOTE(nicksantos): Somebody pulled base.js into the default
+      // Chrome extension environment. This means that for Chrome extensions,
+      // they get the implementation of Function.prototype.bind that
+      // calls goog.bind instead of the native one. Even worse, we don't want
+      // to introduce a circular dependency between goog.bind and
+      // Function.prototype.bind, so we have to hack this to make sure it
+      // works correctly.
+      Function.prototype.bind.toString().indexOf('native code') != -1) {
+    goog.bind = goog.bindNative_;
+  } else {
+    goog.bind = goog.bindJs_;
+  }
+  return goog.bind.apply(null, arguments);
+};
+
+
+/**
+ * Like bind(), except that a 'this object' is not required. Useful when the
+ * target function is already bound.
+ *
+ * Usage:
+ * var g = partial(f, arg1, arg2);
+ * g(arg3, arg4);
+ *
+ * @param {Function} fn A function to partially apply.
+ * @param {...*} var_args Additional arguments that are partially
+ *     applied to fn.
+ * @return {!Function} A partially-applied form of the function bind() was
+ *     invoked as a method of.
+ */
+goog.partial = function(fn, var_args) {
+  var args = Array.prototype.slice.call(arguments, 1);
+  return function() {
+    // Prepend the bound arguments to the current arguments.
+    var newArgs = Array.prototype.slice.call(arguments);
+    newArgs.unshift.apply(newArgs, args);
+    return fn.apply(this, newArgs);
+  };
+};
+
+
+/**
+ * Copies all the members of a source object to a target object. This method
+ * does not work on all browsers for all objects that contain keys such as
+ * toString or hasOwnProperty. Use goog.object.extend for this purpose.
+ * @param {Object} target Target.
+ * @param {Object} source Source.
+ */
+goog.mixin = function(target, source) {
+  for (var x in source) {
+    target[x] = source[x];
+  }
+
+  // For IE7 or lower, the for-in-loop does not contain any properties that are
+  // not enumerable on the prototype object (for example, isPrototypeOf from
+  // Object.prototype) but also it will not include 'replace' on objects that
+  // extend String and change 'replace' (not that it is common for anyone to
+  // extend anything except Object).
+};
+
+
+/**
+ * @return {number} An integer value representing the number of milliseconds
+ *     between midnight, January 1, 1970 and the current time.
+ */
+goog.now = Date.now || (function() {
+  // Unary plus operator converts its operand to a number which in the case of
+  // a date is done by calling getTime().
+  return +new Date();
+});
+
+
+/**
+ * Evals javascript in the global scope.  In IE this uses execScript, other
+ * browsers use goog.global.eval. If goog.global.eval does not evaluate in the
+ * global scope (for example, in Safari), appends a script tag instead.
+ * Throws an exception if neither execScript or eval is defined.
+ * @param {string} script JavaScript string.
+ */
+goog.globalEval = function(script) {
+  if (goog.global.execScript) {
+    goog.global.execScript(script, 'JavaScript');
+  } else if (goog.global.eval) {
+    // Test to see if eval works
+    if (goog.evalWorksForGlobals_ == null) {
+      goog.global.eval('var _et_ = 1;');
+      if (typeof goog.global['_et_'] != 'undefined') {
+        delete goog.global['_et_'];
+        goog.evalWorksForGlobals_ = true;
+      } else {
+        goog.evalWorksForGlobals_ = false;
+      }
+    }
+
+    if (goog.evalWorksForGlobals_) {
+      goog.global.eval(script);
+    } else {
+      var doc = goog.global.document;
+      var scriptElt = doc.createElement('script');
+      scriptElt.type = 'text/javascript';
+      scriptElt.defer = false;
+      // Note(user): can't use .innerHTML since "t('<test>')" will fail and
+      // .text doesn't work in Safari 2.  Therefore we append a text node.
+      scriptElt.appendChild(doc.createTextNode(script));
+      doc.body.appendChild(scriptElt);
+      doc.body.removeChild(scriptElt);
+    }
+  } else {
+    throw Error('goog.globalEval not available');
+  }
+};
+
+
+/**
+ * Indicates whether or not we can call 'eval' directly to eval code in the
+ * global scope. Set to a Boolean by the first call to goog.globalEval (which
+ * empirically tests whether eval works for globals). @see goog.globalEval
+ * @type {?boolean}
+ * @private
+ */
+goog.evalWorksForGlobals_ = null;
+
+
+/**
+ * Optional map of CSS class names to obfuscated names used with
+ * goog.getCssName().
+ * @type {Object|undefined}
+ * @private
+ * @see goog.setCssNameMapping
+ */
+goog.cssNameMapping_;
+
+
+/**
+ * Optional obfuscation style for CSS class names. Should be set to either
+ * 'BY_WHOLE' or 'BY_PART' if defined.
+ * @type {string|undefined}
+ * @private
+ * @see goog.setCssNameMapping
+ */
+goog.cssNameMappingStyle_;
+
+
+/**
+ * Handles strings that are intended to be used as CSS class names.
+ *
+ * This function works in tandem with @see goog.setCssNameMapping.
+ *
+ * Without any mapping set, the arguments are simple joined with a
+ * hyphen and passed through unaltered.
+ *
+ * When there is a mapping, there are two possible styles in which
+ * these mappings are used. In the BY_PART style, each part (i.e. in
+ * between hyphens) of the passed in css name is rewritten according
+ * to the map. In the BY_WHOLE style, the full css name is looked up in
+ * the map directly. If a rewrite is not specified by the map, the
+ * compiler will output a warning.
+ *
+ * When the mapping is passed to the compiler, it will replace calls
+ * to goog.getCssName with the strings from the mapping, e.g.
+ *     var x = goog.getCssName('foo');
+ *     var y = goog.getCssName(this.baseClass, 'active');
+ *  becomes:
+ *     var x= 'foo';
+ *     var y = this.baseClass + '-active';
+ *
+ * If one argument is passed it will be processed, if two are passed
+ * only the modifier will be processed, as it is assumed the first
+ * argument was generated as a result of calling goog.getCssName.
+ *
+ * @param {string} className The class name.
+ * @param {string=} opt_modifier A modifier to be appended to the class name.
+ * @return {string} The class name or the concatenation of the class name and
+ *     the modifier.
+ */
+goog.getCssName = function(className, opt_modifier) {
+  var getMapping = function(cssName) {
+    return goog.cssNameMapping_[cssName] || cssName;
+  };
+
+  var renameByParts = function(cssName) {
+    // Remap all the parts individually.
+    var parts = cssName.split('-');
+    var mapped = [];
+    for (var i = 0; i < parts.length; i++) {
+      mapped.push(getMapping(parts[i]));
+    }
+    return mapped.join('-');
+  };
+
+  var rename;
+  if (goog.cssNameMapping_) {
+    rename = goog.cssNameMappingStyle_ == 'BY_WHOLE' ?
+        getMapping : renameByParts;
+  } else {
+    rename = function(a) {
+      return a;
+    };
+  }
+
+  if (opt_modifier) {
+    return className + '-' + rename(opt_modifier);
+  } else {
+    return rename(className);
+  }
+};
+
+
+/**
+ * Sets the map to check when returning a value from goog.getCssName(). Example:
+ * <pre>
+ * goog.setCssNameMapping({
+ *   "goog": "a",
+ *   "disabled": "b",
+ * });
+ *
+ * var x = goog.getCssName('goog');
+ * // The following evaluates to: "a a-b".
+ * goog.getCssName('goog') + ' ' + goog.getCssName(x, 'disabled')
+ * </pre>
+ * When declared as a map of string literals to string literals, the JSCompiler
+ * will replace all calls to goog.getCssName() using the supplied map if the
+ * --closure_pass flag is set.
+ *
+ * @param {!Object} mapping A map of strings to strings where keys are possible
+ *     arguments to goog.getCssName() and values are the corresponding values
+ *     that should be returned.
+ * @param {string=} opt_style The style of css name mapping. There are two valid
+ *     options: 'BY_PART', and 'BY_WHOLE'.
+ * @see goog.getCssName for a description.
+ */
+goog.setCssNameMapping = function(mapping, opt_style) {
+  goog.cssNameMapping_ = mapping;
+  goog.cssNameMappingStyle_ = opt_style;
+};
+
+
+/**
+ * To use CSS renaming in compiled mode, one of the input files should have a
+ * call to goog.setCssNameMapping() with an object literal that the JSCompiler
+ * can extract and use to replace all calls to goog.getCssName(). In uncompiled
+ * mode, JavaScript code should be loaded before this base.js file that declares
+ * a global variable, CLOSURE_CSS_NAME_MAPPING, which is used below. This is
+ * to ensure that the mapping is loaded before any calls to goog.getCssName()
+ * are made in uncompiled mode.
+ *
+ * A hook for overriding the CSS name mapping.
+ * @type {Object|undefined}
+ */
+goog.global.CLOSURE_CSS_NAME_MAPPING;
+
+
+if (!COMPILED && goog.global.CLOSURE_CSS_NAME_MAPPING) {
+  // This does not call goog.setCssNameMapping() because the JSCompiler
+  // requires that goog.setCssNameMapping() be called with an object literal.
+  goog.cssNameMapping_ = goog.global.CLOSURE_CSS_NAME_MAPPING;
+}
+
+
+/**
+ * Abstract implementation of goog.getMsg for use with localized messages.
+ * @param {string} str Translatable string, places holders in the form {$foo}.
+ * @param {Object=} opt_values Map of place holder name to value.
+ * @return {string} message with placeholders filled.
+ */
+goog.getMsg = function(str, opt_values) {
+  var values = opt_values || {};
+  for (var key in values) {
+    var value = ('' + values[key]).replace(/\$/g, '$$$$');
+    str = str.replace(new RegExp('\\{\\$' + key + '\\}', 'gi'), value);
+  }
+  return str;
+};
+
+
+/**
+ * Exposes an unobfuscated global namespace path for the given object.
+ * Note that fields of the exported object *will* be obfuscated,
+ * unless they are exported in turn via this function or
+ * goog.exportProperty
+ *
+ * <p>Also handy for making public items that are defined in anonymous
+ * closures.
+ *
+ * ex. goog.exportSymbol('Foo', Foo);
+ *
+ * ex. goog.exportSymbol('public.path.Foo.staticFunction',
+ *                       Foo.staticFunction);
+ *     public.path.Foo.staticFunction();
+ *
+ * ex. goog.exportSymbol('public.path.Foo.prototype.myMethod',
+ *                       Foo.prototype.myMethod);
+ *     new public.path.Foo().myMethod();
+ *
+ * @param {string} publicPath Unobfuscated name to export.
+ * @param {*} object Object the name should point to.
+ * @param {Object=} opt_objectToExportTo The object to add the path to; default
+ *     is |goog.global|.
+ */
+goog.exportSymbol = function(publicPath, object, opt_objectToExportTo) {
+  goog.exportPath_(publicPath, object, opt_objectToExportTo);
+};
+
+
+/**
+ * Exports a property unobfuscated into the object's namespace.
+ * ex. goog.exportProperty(Foo, 'staticFunction', Foo.staticFunction);
+ * ex. goog.exportProperty(Foo.prototype, 'myMethod', Foo.prototype.myMethod);
+ * @param {Object} object Object whose static property is being exported.
+ * @param {string} publicName Unobfuscated name to export.
+ * @param {*} symbol Object the name should point to.
+ */
+goog.exportProperty = function(object, publicName, symbol) {
+  object[publicName] = symbol;
+};
+
+
+/**
+ * Inherit the prototype methods from one constructor into another.
+ *
+ * Usage:
+ * <pre>
+ * function ParentClass(a, b) { }
+ * ParentClass.prototype.foo = function(a) { }
+ *
+ * function ChildClass(a, b, c) {
+ *   goog.base(this, a, b);
+ * }
+ * goog.inherits(ChildClass, ParentClass);
+ *
+ * var child = new ChildClass('a', 'b', 'see');
+ * child.foo(); // works
+ * </pre>
+ *
+ * In addition, a superclass' implementation of a method can be invoked
+ * as follows:
+ *
+ * <pre>
+ * ChildClass.prototype.foo = function(a) {
+ *   ChildClass.superClass_.foo.call(this, a);
+ *   // other code
+ * };
+ * </pre>
+ *
+ * @param {Function} childCtor Child class.
+ * @param {Function} parentCtor Parent class.
+ */
+goog.inherits = function(childCtor, parentCtor) {
+  /** @constructor */
+  function tempCtor() {};
+  tempCtor.prototype = parentCtor.prototype;
+  childCtor.superClass_ = parentCtor.prototype;
+  childCtor.prototype = new tempCtor();
+  childCtor.prototype.constructor = childCtor;
+};
+
+
+/**
+ * Call up to the superclass.
+ *
+ * If this is called from a constructor, then this calls the superclass
+ * contructor with arguments 1-N.
+ *
+ * If this is called from a prototype method, then you must pass
+ * the name of the method as the second argument to this function. If
+ * you do not, you will get a runtime error. This calls the superclass'
+ * method with arguments 2-N.
+ *
+ * This function only works if you use goog.inherits to express
+ * inheritance relationships between your classes.
+ *
+ * This function is a compiler primitive. At compile-time, the
+ * compiler will do macro expansion to remove a lot of
+ * the extra overhead that this function introduces. The compiler
+ * will also enforce a lot of the assumptions that this function
+ * makes, and treat it as a compiler error if you break them.
+ *
+ * @param {!Object} me Should always be "this".
+ * @param {*=} opt_methodName The method name if calling a super method.
+ * @param {...*} var_args The rest of the arguments.
+ * @return {*} The return value of the superclass method.
+ */
+goog.base = function(me, opt_methodName, var_args) {
+  var caller = arguments.callee.caller;
+  if (caller.superClass_) {
+    // This is a constructor. Call the superclass constructor.
+    return caller.superClass_.constructor.apply(
+        me, Array.prototype.slice.call(arguments, 1));
+  }
+
+  var args = Array.prototype.slice.call(arguments, 2);
+  var foundCaller = false;
+  for (var ctor = me.constructor;
+       ctor; ctor = ctor.superClass_ && ctor.superClass_.constructor) {
+    if (ctor.prototype[opt_methodName] === caller) {
+      foundCaller = true;
+    } else if (foundCaller) {
+      return ctor.prototype[opt_methodName].apply(me, args);
+    }
+  }
+
+  // If we did not find the caller in the prototype chain,
+  // then one of two things happened:
+  // 1) The caller is an instance method.
+  // 2) This method was not called by the right caller.
+  if (me[opt_methodName] === caller) {
+    return me.constructor.prototype[opt_methodName].apply(me, args);
+  } else {
+    throw Error(
+        'goog.base called from a method of one name ' +
+        'to a method of a different name');
+  }
+};
+
+
+/**
+ * Allow for aliasing within scope functions.  This function exists for
+ * uncompiled code - in compiled code the calls will be inlined and the
+ * aliases applied.  In uncompiled code the function is simply run since the
+ * aliases as written are valid JavaScript.
+ * @param {function()} fn Function to call.  This function can contain aliases
+ *     to namespaces (e.g. "var dom = goog.dom") or classes
+ *    (e.g. "var Timer = goog.Timer").
+ */
+goog.scope = function(fn) {
+  fn.call(goog.global);
+};
+
+

File diff suppressed because it is too large
+ 293 - 0
public/lib/closure/deps.js


+ 109 - 0
public/lib/closure/vec/float32array.js

@@ -0,0 +1,109 @@
+// Copyright 2011 The Closure Library Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+/**
+ * @fileoverview Supplies a Float32Array implementation that implements
+ *     most of the Float32Array spec and that can be used when a built-in
+ *     implementation is not available.
+ *
+ *     Note that if no existing Float32Array implementation is found then
+ *     this class and all its public properties are exported as Float32Array.
+ *
+ *     Adding support for the other TypedArray classes here does not make sense
+ *     since this vector math library only needs Float32Array.
+ *
+ */
+goog.provide('goog.vec.Float32Array');
+
+
+
+/**
+ * Constructs a new Float32Array. The new array is initialized to all zeros.
+ *
+ * @param {goog.vec.Float32Array|Array|ArrayBuffer|number} p0
+ *     The length of the array, or an array to initialize the contents of the
+ *     new Float32Array.
+ * @constructor
+ */
+goog.vec.Float32Array = function(p0) {
+  this.length = p0.length || p0;
+  for (var i = 0; i < this.length; i++) {
+    this[i] = p0[i] || 0;
+  }
+};
+
+
+/**
+ * The number of bytes in an element (as defined by the Typed Array
+ * specification).
+ *
+ * @type {number}
+ */
+goog.vec.Float32Array.BYTES_PER_ELEMENT = 4;
+
+
+/**
+ * The number of bytes in an element (as defined by the Typed Array
+ * specification).
+ *
+ * @type {number}
+ */
+goog.vec.Float32Array.prototype.BYTES_PER_ELEMENT = 4;
+
+
+/**
+ * Sets elements of the array.
+ * @param {Array.<number>|Float32Array} values The array of values.
+ * @param {number=} opt_offset The offset in this array to start.
+ */
+goog.vec.Float32Array.prototype.set = function(values, opt_offset) {
+  opt_offset = opt_offset || 0;
+  for (var i = 0; i < values.length && opt_offset + i < this.length; i++) {
+    this[opt_offset + i] = values[i];
+  }
+};
+
+
+/**
+ * Creates a string representation of this array.
+ * @return {string} The string version of this array.
+ */
+goog.vec.Float32Array.prototype.toString = Array.prototype.join;
+
+
+/**
+ * Note that we cannot implement the subarray() or (deprecated) slice()
+ * methods properly since doing so would require being able to overload
+ * the [] operator which is not possible in javascript.  So we leave
+ * them unimplemented.  Any attempt to call these methods will just result
+ * in a javascript error since we leave them undefined.
+ */
+
+
+/**
+ * If no existing Float32Array implementation is found then we export
+ * goog.vec.Float32Array as Float32Array.
+ */
+if (typeof Float32Array == 'undefined') {
+  goog.exportProperty(goog.vec.Float32Array, 'BYTES_PER_ELEMENT',
+                      goog.vec.Float32Array.BYTES_PER_ELEMENT);
+  goog.exportProperty(goog.vec.Float32Array.prototype, 'BYTES_PER_ELEMENT',
+                      goog.vec.Float32Array.prototype.BYTES_PER_ELEMENT);
+  goog.exportProperty(goog.vec.Float32Array.prototype, 'set',
+                      goog.vec.Float32Array.prototype.set);
+  goog.exportProperty(goog.vec.Float32Array.prototype, 'toString',
+                      goog.vec.Float32Array.prototype.toString);
+  goog.exportSymbol('Float32Array', goog.vec.Float32Array);
+}

+ 109 - 0
public/lib/closure/vec/float64array.js

@@ -0,0 +1,109 @@
+// Copyright 2011 The Closure Library Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+/**
+ * @fileoverview Supplies a Float64Array implementation that implements
+ * most of the Float64Array spec and that can be used when a built-in
+ * implementation is not available.
+ *
+ * Note that if no existing Float64Array implementation is found then this
+ * class and all its public properties are exported as Float64Array.
+ *
+ * Adding support for the other TypedArray classes here does not make sense
+ * since this vector math library only needs Float32Array and Float64Array.
+ *
+ */
+goog.provide('goog.vec.Float64Array');
+
+
+
+/**
+ * Constructs a new Float64Array. The new array is initialized to all zeros.
+ *
+ * @param {goog.vec.Float64Array|Array|ArrayBuffer|number} p0
+ *     The length of the array, or an array to initialize the contents of the
+ *     new Float64Array.
+ * @constructor
+ */
+goog.vec.Float64Array = function(p0) {
+  this.length = p0.length || p0;
+  for (var i = 0; i < this.length; i++) {
+    this[i] = p0[i] || 0;
+  }
+};
+
+
+/**
+ * The number of bytes in an element (as defined by the Typed Array
+ * specification).
+ *
+ * @type {number}
+ */
+goog.vec.Float64Array.BYTES_PER_ELEMENT = 8;
+
+
+/**
+ * The number of bytes in an element (as defined by the Typed Array
+ * specification).
+ *
+ * @type {number}
+ */
+goog.vec.Float64Array.prototype.BYTES_PER_ELEMENT = 8;
+
+
+/**
+ * Sets elements of the array.
+ * @param {Array.<number>|Float64Array} values The array of values.
+ * @param {number=} opt_offset The offset in this array to start.
+ */
+goog.vec.Float64Array.prototype.set = function(values, opt_offset) {
+  opt_offset = opt_offset || 0;
+  for (var i = 0; i < values.length && opt_offset + i < this.length; i++) {
+    this[opt_offset + i] = values[i];
+  }
+};
+
+
+/**
+ * Creates a string representation of this array.
+ * @return {string} The string version of this array.
+ */
+goog.vec.Float64Array.prototype.toString = Array.prototype.join;
+
+
+/**
+ * Note that we cannot implement the subarray() or (deprecated) slice()
+ * methods properly since doing so would require being able to overload
+ * the [] operator which is not possible in javascript.  So we leave
+ * them unimplemented.  Any attempt to call these methods will just result
+ * in a javascript error since we leave them undefined.
+ */
+
+
+/**
+ * If no existing Float64Array implementation is found then we export
+ * goog.vec.Float64Array as Float64Array.
+ */
+if (typeof Float64Array == 'undefined') {
+  goog.exportProperty(goog.vec.Float64Array, 'BYTES_PER_ELEMENT',
+                      goog.vec.Float64Array.BYTES_PER_ELEMENT);
+  goog.exportProperty(goog.vec.Float64Array.prototype, 'BYTES_PER_ELEMENT',
+                      goog.vec.Float64Array.prototype.BYTES_PER_ELEMENT);
+  goog.exportProperty(goog.vec.Float64Array.prototype, 'set',
+                      goog.vec.Float64Array.prototype.set);
+  goog.exportProperty(goog.vec.Float64Array.prototype, 'toString',
+                      goog.vec.Float64Array.prototype.toString);
+  goog.exportSymbol('Float64Array', goog.vec.Float64Array);
+}

+ 896 - 0
public/lib/closure/vec/mat3.js

@@ -0,0 +1,896 @@
+// Copyright 2011 The Closure Library Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * @fileoverview Implements 3x3 matrices and their related functions which are
+ * compatible with WebGL. The API is structured to avoid unnecessary memory
+ * allocations.  The last parameter will typically be the output vector and
+ * an object can be both an input and output parameter to all methods except
+ * where noted. Matrix operations follow the mathematical form when multiplying
+ * vectors as follows: resultVec = matrix * vec.
+ *
+ */
+goog.provide('goog.vec.Mat3');
+
+goog.require('goog.vec');
+goog.require('goog.vec.Vec3');
+
+
+/** @typedef {goog.vec.Float32} */ goog.vec.Mat3.Float32;
+/** @typedef {goog.vec.Float64} */ goog.vec.Mat3.Float64;
+/** @typedef {goog.vec.Number} */ goog.vec.Mat3.Number;
+/** @typedef {goog.vec.AnyType} */ goog.vec.Mat3.AnyType;
+
+// The following two types are deprecated - use the above types instead.
+/** @typedef {Float32Array} */ goog.vec.Mat3.Type;
+/** @typedef {goog.vec.ArrayType} */ goog.vec.Mat3.Mat3Like;
+
+
+/**
+ * Creates the array representation of a 3x3 matrix of Float32.
+ * The use of the array directly instead of a class reduces overhead.
+ * The returned matrix is cleared to all zeros.
+ *
+ * @return {!goog.vec.Mat3.Float32} The new matrix.
+ */
+goog.vec.Mat3.createFloat32 = function() {
+  return new Float32Array(9);
+};
+
+
+/**
+ * Creates the array representation of a 3x3 matrix of Float64.
+ * The returned matrix is cleared to all zeros.
+ *
+ * @return {!goog.vec.Mat3.Float64} The new matrix.
+ */
+goog.vec.Mat3.createFloat64 = function() {
+  return new Float64Array(9);
+};
+
+
+/**
+ * Creates the array representation of a 3x3 matrix of Number.
+ * The returned matrix is cleared to all zeros.
+ *
+ * @return {!goog.vec.Mat3.Number} The new matrix.
+ */
+goog.vec.Mat3.createNumber = function() {
+  var a = new Array(9);
+  goog.vec.Mat3.setFromValues(a,
+                              0, 0, 0,
+                              0, 0, 0,
+                              0, 0, 0);
+  return a;
+};
+
+
+/**
+ * Creates the array representation of a 3x3 matrix of Float32.
+ * The returned matrix is cleared to all zeros.
+ *
+ * @deprecated Use createFloat32.
+ * @return {!goog.vec.Mat3.Type} The new matrix.
+ */
+goog.vec.Mat3.create = function() {
+  return goog.vec.Mat3.createFloat32();
+};
+
+
+/**
+ * Creates a 3x3 identity matrix of Float32.
+ *
+ * @return {!goog.vec.Mat3.Float32} The new 9 element array.
+ */
+goog.vec.Mat3.createFloat32Identity = function() {
+  var mat = goog.vec.Mat3.createFloat32();
+  mat[0] = mat[4] = mat[8] = 1;
+  return mat;
+};
+
+
+/**
+ * Creates a 3x3 identity matrix of Float64.
+ *
+ * @return {!goog.vec.Mat3.Float64} The new 9 element array.
+ */
+goog.vec.Mat3.createFloat64Identity = function() {
+  var mat = goog.vec.Mat3.createFloat64();
+  mat[0] = mat[4] = mat[8] = 1;
+  return mat;
+};
+
+
+/**
+ * Creates a 3x3 identity matrix of Number.
+ * The returned matrix is cleared to all zeros.
+ *
+ * @return {!goog.vec.Mat3.Number} The new 9 element array.
+ */
+goog.vec.Mat3.createNumberIdentity = function() {
+  var a = new Array(9);
+  goog.vec.Mat3.setFromValues(a,
+                              1, 0, 0,
+                              0, 1, 0,
+                              0, 0, 1);
+  return a;
+};
+
+
+/**
+ * Creates the array representation of a 3x3 matrix of Float32.
+ * The returned matrix is cleared to all zeros.
+ *
+ * @deprecated Use createFloat32Identity.
+ * @return {!goog.vec.Mat3.Type} The new 9 element array.
+ */
+goog.vec.Mat3.createIdentity = function() {
+  return goog.vec.Mat3.createFloat32Identity();
+};
+
+
+/**
+ * Creates a 3x3 matrix of Float32 initialized from the given array.
+ *
+ * @param {goog.vec.Mat3.AnyType} matrix The array containing the
+ *     matrix values in column major order.
+ * @return {!goog.vec.Mat3.Float32} The new, nine element array.
+ */
+goog.vec.Mat3.createFloat32FromArray = function(matrix) {
+  var newMatrix = goog.vec.Mat3.createFloat32();
+  goog.vec.Mat3.setFromArray(newMatrix, matrix);
+  return newMatrix;
+};
+
+
+/**
+ * Creates a 3x3 matrix of Float32 initialized from the given values.
+ *
+ * @param {number} v00 The values at (0, 0).
+ * @param {number} v10 The values at (1, 0).
+ * @param {number} v20 The values at (2, 0).
+ * @param {number} v01 The values at (0, 1).
+ * @param {number} v11 The values at (1, 1).
+ * @param {number} v21 The values at (2, 1).
+ * @param {number} v02 The values at (0, 2).
+ * @param {number} v12 The values at (1, 2).
+ * @param {number} v22 The values at (2, 2).
+ * @return {!goog.vec.Mat3.Float32} The new, nine element array.
+ */
+goog.vec.Mat3.createFloat32FromValues = function(
+    v00, v10, v20, v01, v11, v21, v02, v12, v22) {
+  var newMatrix = goog.vec.Mat3.createFloat32();
+  goog.vec.Mat3.setFromValues(
+      newMatrix, v00, v10, v20, v01, v11, v21, v02, v12, v22);
+  return newMatrix;
+};
+
+
+/**
+ * Creates a clone of a 3x3 matrix of Float32.
+ *
+ * @param {goog.vec.Mat3.Float32} matrix The source 3x3 matrix.
+ * @return {!goog.vec.Mat3.Float32} The new 3x3 element matrix.
+ */
+goog.vec.Mat3.cloneFloat32 = goog.vec.Mat3.createFloat32FromArray;
+
+
+/**
+ * Creates a 3x3 matrix of Float64 initialized from the given array.
+ *
+ * @param {goog.vec.Mat3.AnyType} matrix The array containing the
+ *     matrix values in column major order.
+ * @return {!goog.vec.Mat3.Float64} The new, nine element array.
+ */
+goog.vec.Mat3.createFloat64FromArray = function(matrix) {
+  var newMatrix = goog.vec.Mat3.createFloat64();
+  goog.vec.Mat3.setFromArray(newMatrix, matrix);
+  return newMatrix;
+};
+
+
+/**
+ * Creates a 3x3 matrix of Float64 initialized from the given values.
+ *
+ * @param {number} v00 The values at (0, 0).
+ * @param {number} v10 The values at (1, 0).
+ * @param {number} v20 The values at (2, 0).
+ * @param {number} v01 The values at (0, 1).
+ * @param {number} v11 The values at (1, 1).
+ * @param {number} v21 The values at (2, 1).
+ * @param {number} v02 The values at (0, 2).
+ * @param {number} v12 The values at (1, 2).
+ * @param {number} v22 The values at (2, 2).
+ * @return {!goog.vec.Mat3.Float64} The new, nine element array.
+ */
+goog.vec.Mat3.createFloat64FromValues = function(
+    v00, v10, v20, v01, v11, v21, v02, v12, v22) {
+  var newMatrix = goog.vec.Mat3.createFloat64();
+  goog.vec.Mat3.setFromValues(
+      newMatrix, v00, v10, v20, v01, v11, v21, v02, v12, v22);
+  return newMatrix;
+};
+
+
+/**
+ * Creates a clone of a 3x3 matrix of Float64.
+ *
+ * @param {goog.vec.Mat3.Float64} matrix The source 3x3 matrix.
+ * @return {!goog.vec.Mat3.Float64} The new 3x3 element matrix.
+ */
+goog.vec.Mat3.cloneFloat64 = goog.vec.Mat3.createFloat64FromArray;
+
+
+/**
+ * Creates a 3x3 matrix of Float32 initialized from the given array.
+ *
+ * @deprecated Use createFloat32FromArray.
+ * @param {goog.vec.Mat3.Mat3Like} matrix The array containing the
+ *     matrix values in column major order.
+ * @return {!goog.vec.Mat3.Type} The new, nine element array.
+ */
+goog.vec.Mat3.createFromArray = function(matrix) {
+  var newMatrix = goog.vec.Mat3.createFloat32();
+  goog.vec.Mat3.setFromArray(newMatrix, matrix);
+  return newMatrix;
+};
+
+
+/**
+ * Creates a 3x3 matrix of Float32 initialized from the given values.
+ *
+ * @deprecated Use createFloat32FromValues.
+ * @param {number} v00 The values at (0, 0).
+ * @param {number} v10 The values at (1, 0).
+ * @param {number} v20 The values at (2, 0).
+ * @param {number} v01 The values at (0, 1).
+ * @param {number} v11 The values at (1, 1).
+ * @param {number} v21 The values at (2, 1).
+ * @param {number} v02 The values at (0, 2).
+ * @param {number} v12 The values at (1, 2).
+ * @param {number} v22 The values at (2, 2).
+ * @return {!goog.vec.Mat3.Type} The new, nine element array.
+ */
+goog.vec.Mat3.createFromValues = function(
+    v00, v10, v20, v01, v11, v21, v02, v12, v22) {
+  var newMatrix = goog.vec.Mat3.create();
+  goog.vec.Mat3.setFromValues(
+      newMatrix, v00, v10, v20, v01, v11, v21, v02, v12, v22);
+  return newMatrix;
+};
+
+
+/**
+ * Creates a clone of a 3x3 matrix of Float32.
+ *
+ * @deprecated Use cloneFloat32.
+ * @param {goog.vec.Mat3.Mat3Like} matrix The source 3x3 matrix.
+ * @return {!goog.vec.Mat3.Type} The new 3x3 element matrix.
+ */
+goog.vec.Mat3.clone = goog.vec.Mat3.createFromArray;
+
+
+/**
+ * Retrieves the element at the requested row and column.
+ *
+ * @param {goog.vec.Mat3.AnyType} mat The matrix containing the value to
+ *     retrieve.
+ * @param {number} row The row index.
+ * @param {number} column The column index.
+ * @return {number} The element value at the requested row, column indices.
+ */
+goog.vec.Mat3.getElement = function(mat, row, column) {
+  return mat[row + column * 3];
+};
+
+
+/**
+ * Sets the element at the requested row and column.
+ *
+ * @param {goog.vec.Mat3.AnyType} mat The matrix containing the value to
+ *     retrieve.
+ * @param {number} row The row index.
+ * @param {number} column The column index.
+ * @param {number} value The value to set at the requested row, column.
+ */
+goog.vec.Mat3.setElement = function(mat, row, column, value) {
+  mat[row + column * 3] = value;
+};
+
+
+/**
+ * Initializes the matrix from the set of values. Note the values supplied are
+ * in column major order.
+ *
+ * @param {goog.vec.Mat3.AnyType} mat The matrix to receive the
+ *     values.
+ * @param {number} v00 The values at (0, 0).
+ * @param {number} v10 The values at (1, 0).
+ * @param {number} v20 The values at (2, 0).
+ * @param {number} v01 The values at (0, 1).
+ * @param {number} v11 The values at (1, 1).
+ * @param {number} v21 The values at (2, 1).
+ * @param {number} v02 The values at (0, 2).
+ * @param {number} v12 The values at (1, 2).
+ * @param {number} v22 The values at (2, 2).
+ * @return {!goog.vec.Mat3.AnyType} return mat so that operations can be
+ *     chained together.
+ */
+goog.vec.Mat3.setFromValues = function(
+    mat, v00, v10, v20, v01, v11, v21, v02, v12, v22) {
+  mat[0] = v00;
+  mat[1] = v10;
+  mat[2] = v20;
+  mat[3] = v01;
+  mat[4] = v11;
+  mat[5] = v21;
+  mat[6] = v02;
+  mat[7] = v12;
+  mat[8] = v22;
+  return mat;
+};
+
+
+/**
+ * Sets the matrix from the array of values stored in column major order.
+ *
+ * @param {goog.vec.Mat3.AnyType} mat The matrix to receive the values.
+ * @param {goog.vec.Mat3.AnyType} values The column major ordered
+ *     array of values to store in the matrix.
+ * @return {!goog.vec.Mat3.AnyType} return mat so that operations can be
+ *     chained together.
+ */
+goog.vec.Mat3.setFromArray = function(mat, values) {
+  mat[0] = values[0];
+  mat[1] = values[1];
+  mat[2] = values[2];
+  mat[3] = values[3];
+  mat[4] = values[4];
+  mat[5] = values[5];
+  mat[6] = values[6];
+  mat[7] = values[7];
+  mat[8] = values[8];
+  return mat;
+};
+
+
+/**
+ * Sets the matrix from the array of values stored in row major order.
+ *
+ * @param {goog.vec.Mat3.AnyType} mat The matrix to receive the values.
+ * @param {goog.vec.Mat3.AnyType} values The row major ordered array
+ *     of values to store in the matrix.
+ * @return {!goog.vec.Mat3.AnyType} return mat so that operations can be
+ *     chained together.
+ */
+goog.vec.Mat3.setFromRowMajorArray = function(mat, values) {
+  mat[0] = values[0];
+  mat[1] = values[3];
+  mat[2] = values[6];
+  mat[3] = values[1];
+  mat[4] = values[4];
+  mat[5] = values[7];
+  mat[6] = values[2];
+  mat[7] = values[5];
+  mat[8] = values[8];
+  return mat;
+};
+
+
+/**
+ * Sets the diagonal values of the matrix from the given values.
+ *
+ * @param {goog.vec.Mat3.AnyType} mat The matrix to receive the values.
+ * @param {number} v00 The values for (0, 0).
+ * @param {number} v11 The values for (1, 1).
+ * @param {number} v22 The values for (2, 2).
+ * @return {!goog.vec.Mat3.AnyType} return mat so that operations can be
+ *     chained together.
+ */
+goog.vec.Mat3.setDiagonalValues = function(mat, v00, v11, v22) {
+  mat[0] = v00;
+  mat[4] = v11;
+  mat[8] = v22;
+  return mat;
+};
+
+
+/**
+ * Sets the diagonal values of the matrix from the given vector.
+ *
+ * @param {goog.vec.Mat3.AnyType} mat The matrix to receive the values.
+ * @param {goog.vec.Vec3.AnyType} vec The vector containing the values.
+ */
+goog.vec.Mat3.setDiagonal = function(mat, vec) {
+  mat[0] = vec[0];
+  mat[4] = vec[1];
+  mat[8] = vec[2];
+};
+
+
+/**
+ * Sets the specified column with the supplied values.
+ *
+ * @param {goog.vec.Mat3.AnyType} mat The matrix to recieve the values.
+ * @param {number} column The column index to set the values on.
+ * @param {number} v0 The value for row 0.
+ * @param {number} v1 The value for row 1.
+ * @param {number} v2 The value for row 2.
+ */
+goog.vec.Mat3.setColumnValues = function(mat, column, v0, v1, v2) {
+  var i = column * 3;
+  mat[i] = v0;
+  mat[i + 1] = v1;
+  mat[i + 2] = v2;
+};
+
+
+/**
+ * Sets the specified column with the value from the supplied array.
+ *
+ * @param {goog.vec.Mat3.AnyType} mat The matrix to receive the values.
+ * @param {number} column The column index to set the values on.
+ * @param {goog.vec.Vec3.AnyType} vec The vector elements for the column.
+ */
+goog.vec.Mat3.setColumn = function(mat, column, vec) {
+  var i = column * 3;
+  mat[i] = vec[0];
+  mat[i + 1] = vec[1];
+  mat[i + 2] = vec[2];
+};
+
+
+/**
+ * Retrieves the specified column from the matrix into the given vector
+ * array.
+ *
+ * @param {goog.vec.Mat3.AnyType} mat The matrix supplying the values.
+ * @param {number} column The column to get the values from.
+ * @param {goog.vec.Vec3.AnyType} vec The vector elements to receive the
+ *     column.
+ */
+goog.vec.Mat3.getColumn = function(mat, column, vec) {
+  var i = column * 3;
+  vec[0] = mat[i];
+  vec[1] = mat[i + 1];
+  vec[2] = mat[i + 2];
+};
+
+
+/**
+ * Sets the columns of the matrix from the set of vector elements.
+ *
+ * @param {goog.vec.Mat3.AnyType} mat The matrix to receive the values.
+ * @param {goog.vec.Vec3.AnyType} vec0 The values for column 0.
+ * @param {goog.vec.Vec3.AnyType} vec1 The values for column 1.
+ * @param {goog.vec.Vec3.AnyType} vec2 The values for column 2.
+ */
+goog.vec.Mat3.setColumns = function(mat, vec0, vec1, vec2) {
+  goog.vec.Mat3.setColumn(mat, 0, vec0);
+  goog.vec.Mat3.setColumn(mat, 1, vec1);
+  goog.vec.Mat3.setColumn(mat, 2, vec2);
+};
+
+
+/**
+ * Retrieves the column values from the given matrix into the given vector
+ * elements.
+ *
+ * @param {goog.vec.Mat3.AnyType} mat The matrix supplying the columns.
+ * @param {goog.vec.Vec3.AnyType} vec0 The vector to receive column 0.
+ * @param {goog.vec.Vec3.AnyType} vec1 The vector to receive column 1.
+ * @param {goog.vec.Vec3.AnyType} vec2 The vector to receive column 2.
+ */
+goog.vec.Mat3.getColumns = function(mat, vec0, vec1, vec2) {
+  goog.vec.Mat3.getColumn(mat, 0, vec0);
+  goog.vec.Mat3.getColumn(mat, 1, vec1);
+  goog.vec.Mat3.getColumn(mat, 2, vec2);
+};
+
+
+/**
+ * Sets the row values from the supplied values.
+ *
+ * @param {goog.vec.Mat3.AnyType} mat The matrix to receive the values.
+ * @param {number} row The index of the row to receive the values.
+ * @param {number} v0 The value for column 0.
+ * @param {number} v1 The value for column 1.
+ * @param {number} v2 The value for column 2.
+ */
+goog.vec.Mat3.setRowValues = function(mat, row, v0, v1, v2) {
+  mat[row] = v0;
+  mat[row + 3] = v1;
+  mat[row + 6] = v2;
+};
+
+
+/**
+ * Sets the row values from the supplied vector.
+ *
+ * @param {goog.vec.Mat3.AnyType} mat The matrix to receive the row values.
+ * @param {number} row The index of the row.
+ * @param {goog.vec.Vec3.AnyType} vec The vector containing the values.
+ */
+goog.vec.Mat3.setRow = function(mat, row, vec) {
+  mat[row] = vec[0];
+  mat[row + 3] = vec[1];
+  mat[row + 6] = vec[2];
+};
+
+
+/**
+ * Retrieves the row values into the given vector.
+ *
+ * @param {goog.vec.Mat3.AnyType} mat The matrix supplying the values.
+ * @param {number} row The index of the row supplying the values.
+ * @param {goog.vec.Vec3.AnyType} vec The vector to receive the row.
+ */
+goog.vec.Mat3.getRow = function(mat, row, vec) {
+  vec[0] = mat[row];
+  vec[1] = mat[row + 3];
+  vec[2] = mat[row + 6];
+};
+
+
+/**
+ * Sets the rows of the matrix from the supplied vectors.
+ *
+ * @param {goog.vec.Mat3.AnyType} mat The matrix to receive the values.
+ * @param {goog.vec.Vec3.AnyType} vec0 The values for row 0.
+ * @param {goog.vec.Vec3.AnyType} vec1 The values for row 1.
+ * @param {goog.vec.Vec3.AnyType} vec2 The values for row 2.
+ */
+goog.vec.Mat3.setRows = function(mat, vec0, vec1, vec2) {
+  goog.vec.Mat3.setRow(mat, 0, vec0);
+  goog.vec.Mat3.setRow(mat, 1, vec1);
+  goog.vec.Mat3.setRow(mat, 2, vec2);
+};
+
+
+/**
+ * Retrieves the rows of the matrix into the supplied vectors.
+ *
+ * @param {goog.vec.Mat3.AnyType} mat The matrix to supplying the values.
+ * @param {goog.vec.Vec3.AnyType} vec0 The vector to receive row 0.
+ * @param {goog.vec.Vec3.AnyType} vec1 The vector to receive row 1.
+ * @param {goog.vec.Vec3.AnyType} vec2 The vector to receive row 2.
+ */
+goog.vec.Mat3.getRows = function(mat, vec0, vec1, vec2) {
+  goog.vec.Mat3.getRow(mat, 0, vec0);
+  goog.vec.Mat3.getRow(mat, 1, vec1);
+  goog.vec.Mat3.getRow(mat, 2, vec2);
+};
+
+
+/**
+ * Makes the given 3x3 matrix the zero matrix.
+ *
+ * @param {goog.vec.Mat3.AnyType} mat The matrix.
+ * @return {!goog.vec.Mat3.AnyType} return mat so operations can be chained.
+ */
+goog.vec.Mat3.makeZero = function(mat) {
+  mat[0] = 0;
+  mat[1] = 0;
+  mat[2] = 0;
+  mat[3] = 0;
+  mat[4] = 0;
+  mat[5] = 0;
+  mat[6] = 0;
+  mat[7] = 0;
+  mat[8] = 0;
+  return mat;
+};
+
+
+/**
+ * Makes the given 3x3 matrix the identity matrix.
+ *
+ * @param {goog.vec.Mat3.AnyType} mat The matrix.
+ * @return {!goog.vec.Mat3.AnyType} return mat so operations can be chained.
+ */
+goog.vec.Mat3.makeIdentity = function(mat) {
+  mat[0] = 1;
+  mat[1] = 0;
+  mat[2] = 0;
+  mat[3] = 0;
+  mat[4] = 1;
+  mat[5] = 0;
+  mat[6] = 0;
+  mat[7] = 0;
+  mat[8] = 1;
+  return mat;
+};
+
+
+/**
+ * Performs a per-component addition of the matrices mat0 and mat1, storing
+ * the result into resultMat.
+ *
+ * @param {goog.vec.Mat3.AnyType} mat0 The first addend.
+ * @param {goog.vec.Mat3.AnyType} mat1 The second addend.
+ * @param {goog.vec.Mat3.AnyType} resultMat The matrix to
+ *     receive the results (may be either mat0 or mat1).
+ * @return {!goog.vec.Mat3.AnyType} return resultMat so that operations can be
+ *     chained together.
+ */
+goog.vec.Mat3.addMat = function(mat0, mat1, resultMat) {
+  resultMat[0] = mat0[0] + mat1[0];
+  resultMat[1] = mat0[1] + mat1[1];
+  resultMat[2] = mat0[2] + mat1[2];
+  resultMat[3] = mat0[3] + mat1[3];
+  resultMat[4] = mat0[4] + mat1[4];
+  resultMat[5] = mat0[5] + mat1[5];
+  resultMat[6] = mat0[6] + mat1[6];
+  resultMat[7] = mat0[7] + mat1[7];
+  resultMat[8] = mat0[8] + mat1[8];
+  return resultMat;
+};
+
+
+/**
+ * Performs a per-component subtraction of the matrices mat0 and mat1,
+ * storing the result into resultMat.
+ *
+ * @param {goog.vec.Mat3.AnyType} mat0 The minuend.
+ * @param {goog.vec.Mat3.AnyType} mat1 The subtrahend.
+ * @param {goog.vec.Mat3.AnyType} resultMat The matrix to receive
+ *     the results (may be either mat0 or mat1).
+ * @return {!goog.vec.Mat3.AnyType} return resultMat so that operations can be
+ *     chained together.
+ */
+goog.vec.Mat3.subMat = function(mat0, mat1, resultMat) {
+  resultMat[0] = mat0[0] - mat1[0];
+  resultMat[1] = mat0[1] - mat1[1];
+  resultMat[2] = mat0[2] - mat1[2];
+  resultMat[3] = mat0[3] - mat1[3];
+  resultMat[4] = mat0[4] - mat1[4];
+  resultMat[5] = mat0[5] - mat1[5];
+  resultMat[6] = mat0[6] - mat1[6];
+  resultMat[7] = mat0[7] - mat1[7];
+  resultMat[8] = mat0[8] - mat1[8];
+  return resultMat;
+};
+
+
+/**
+ * Multiplies matrix mat0 with the given scalar, storing the result
+ * into resultMat.
+ *
+ * @param {goog.vec.Mat3.AnyType} mat The matrix.
+ * @param {number} scalar The scalar value to multiple to each element of mat.
+ * @param {goog.vec.Mat3.AnyType} resultMat The matrix to receive
+ *     the results (may be mat).
+ * @return {!goog.vec.Mat3.AnyType} return resultMat so that operations can be
+ *     chained together.
+ */
+goog.vec.Mat3.multScalar = function(mat, scalar, resultMat) {
+  resultMat[0] = mat[0] * scalar;
+  resultMat[1] = mat[1] * scalar;
+  resultMat[2] = mat[2] * scalar;
+  resultMat[3] = mat[3] * scalar;
+  resultMat[4] = mat[4] * scalar;
+  resultMat[5] = mat[5] * scalar;
+  resultMat[6] = mat[6] * scalar;
+  resultMat[7] = mat[7] * scalar;
+  resultMat[8] = mat[8] * scalar;
+  return resultMat;
+};
+
+
+/**
+ * Multiplies the two matrices mat0 and mat1 using matrix multiplication,
+ * storing the result into resultMat.
+ *
+ * @param {goog.vec.Mat3.AnyType} mat0 The first (left hand) matrix.
+ * @param {goog.vec.Mat3.AnyType} mat1 The second (right hand) matrix.
+ * @param {goog.vec.Mat3.AnyType} resultMat The matrix to receive
+ *     the results (may be either mat0 or mat1).
+ * @return {!goog.vec.Mat3.AnyType} return resultMat so that operations can be
+ *     chained together.
+ */
+goog.vec.Mat3.multMat = function(mat0, mat1, resultMat) {
+  var a00 = mat0[0], a10 = mat0[1], a20 = mat0[2];
+  var a01 = mat0[3], a11 = mat0[4], a21 = mat0[5];
+  var a02 = mat0[6], a12 = mat0[7], a22 = mat0[8];
+
+  var b00 = mat1[0], b10 = mat1[1], b20 = mat1[2];
+  var b01 = mat1[3], b11 = mat1[4], b21 = mat1[5];
+  var b02 = mat1[6], b12 = mat1[7], b22 = mat1[8];
+
+  resultMat[0] = a00 * b00 + a01 * b10 + a02 * b20;
+  resultMat[1] = a10 * b00 + a11 * b10 + a12 * b20;
+  resultMat[2] = a20 * b00 + a21 * b10 + a22 * b20;
+  resultMat[3] = a00 * b01 + a01 * b11 + a02 * b21;
+  resultMat[4] = a10 * b01 + a11 * b11 + a12 * b21;
+  resultMat[5] = a20 * b01 + a21 * b11 + a22 * b21;
+  resultMat[6] = a00 * b02 + a01 * b12 + a02 * b22;
+  resultMat[7] = a10 * b02 + a11 * b12 + a12 * b22;
+  resultMat[8] = a20 * b02 + a21 * b12 + a22 * b22;
+  return resultMat;
+};
+
+
+/**
+ * Transposes the given matrix mat storing the result into resultMat.
+ *
+ * @param {goog.vec.Mat3.AnyType} mat The matrix to transpose.
+ * @param {goog.vec.Mat3.AnyType} resultMat The matrix to receive
+ *     the results (may be mat).
+ * @return {!goog.vec.Mat3.AnyType} return resultMat so that operations can be
+ *     chained together.
+ */
+goog.vec.Mat3.transpose = function(mat, resultMat) {
+  if (resultMat == mat) {
+    var a10 = mat[1], a20 = mat[2], a21 = mat[5];
+    resultMat[1] = mat[3];
+    resultMat[2] = mat[6];
+    resultMat[3] = a10;
+    resultMat[5] = mat[7];
+    resultMat[6] = a20;
+    resultMat[7] = a21;
+  } else {
+    resultMat[0] = mat[0];
+    resultMat[1] = mat[3];
+    resultMat[2] = mat[6];
+    resultMat[3] = mat[1];
+    resultMat[4] = mat[4];
+    resultMat[5] = mat[7];
+    resultMat[6] = mat[2];
+    resultMat[7] = mat[5];
+    resultMat[8] = mat[8];
+  }
+  return resultMat;
+};
+
+
+/**
+ * Computes the inverse of mat0 storing the result into resultMat. If the
+ * inverse is defined, this function returns true, false otherwise.
+ *
+ * @param {goog.vec.Mat3.AnyType} mat0 The matrix to invert.
+ * @param {goog.vec.Mat3.AnyType} resultMat The matrix to receive
+ *     the result (may be mat0).
+ * @return {boolean} True if the inverse is defined. If false is returned,
+ *     resultMat is not modified.
+ */
+goog.vec.Mat3.invert = function(mat0, resultMat) {
+  var a00 = mat0[0], a10 = mat0[1], a20 = mat0[2];
+  var a01 = mat0[3], a11 = mat0[4], a21 = mat0[5];
+  var a02 = mat0[6], a12 = mat0[7], a22 = mat0[8];
+
+  var t00 = a11 * a22 - a12 * a21;
+  var t10 = a12 * a20 - a10 * a22;
+  var t20 = a10 * a21 - a11 * a20;
+  var det = a00 * t00 + a01 * t10 + a02 * t20;
+  if (det == 0) {
+    return false;
+  }
+
+  var idet = 1 / det;
+  resultMat[0] = t00 * idet;
+  resultMat[3] = (a02 * a21 - a01 * a22) * idet;
+  resultMat[6] = (a01 * a12 - a02 * a11) * idet;
+
+  resultMat[1] = t10 * idet;
+  resultMat[4] = (a00 * a22 - a02 * a20) * idet;
+  resultMat[7] = (a02 * a10 - a00 * a12) * idet;
+
+  resultMat[2] = t20 * idet;
+  resultMat[5] = (a01 * a20 - a00 * a21) * idet;
+  resultMat[8] = (a00 * a11 - a01 * a10) * idet;
+  return true;
+};
+
+
+/**
+ * Returns true if the components of mat0 are equal to the components of mat1.
+ *
+ * @param {goog.vec.Mat3.AnyType} mat0 The first matrix.
+ * @param {goog.vec.Mat3.AnyType} mat1 The second matrix.
+ * @return {boolean} True if the the two matrices are equivalent.
+ */
+goog.vec.Mat3.equals = function(mat0, mat1) {
+  return mat0.length == mat1.length &&
+      mat0[0] == mat1[0] && mat0[1] == mat1[1] && mat0[2] == mat1[2] &&
+      mat0[3] == mat1[3] && mat0[4] == mat1[4] && mat0[5] == mat1[5] &&
+      mat0[6] == mat1[6] && mat0[7] == mat1[7] && mat0[8] == mat1[8];
+};
+
+
+/**
+ * Transforms the given vector with the given matrix storing the resulting,
+ * transformed matrix into resultVec.
+ *
+ * @param {!goog.vec.Mat3.AnyType} mat The matrix supplying the transformation.
+ * @param {goog.vec.Vec3.AnyType} vec The vector to transform.
+ * @param {goog.vec.Vec3.AnyType} resultVec The vector to
+ *     receive the results (may be vec).
+ * @return {!goog.vec.Vec3.AnyType} return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Mat3.multVec3 = function(mat, vec, resultVec) {
+  var x = vec[0], y = vec[1], z = vec[2];
+  resultVec[0] = x * mat[0] + y * mat[3] + z * mat[6];
+  resultVec[1] = x * mat[1] + y * mat[4] + z * mat[7];
+  resultVec[2] = x * mat[2] + y * mat[5] + z * mat[8];
+  return resultVec;
+};
+
+
+/**
+ * Makes the given 3x3 matrix a translation matrix with x and y
+ * translation values.
+ *
+ * @param {!goog.vec.Mat3.AnyType} mat The matrix.
+ * @param {number} x The translation along the x axis.
+ * @param {number} y The translation along the y axis.
+ * @return {!goog.vec.Mat3.AnyType} return mat so that operations can be
+ *     chained.
+ */
+goog.vec.Mat3.makeTranslate = function(mat, x, y) {
+  goog.vec.Mat3.makeIdentity(mat);
+  goog.vec.Mat3.setColumnValues(mat, 2, x, y, 1);
+  return mat;
+};
+
+
+/**
+ * Makes the given 3x3 matrix a scale matrix with x, y, and z scale factors.
+ *
+ * @param {!goog.vec.Mat3.AnyType} mat The 3x3 (9-element) matrix
+ *     array to receive the new scale matrix.
+ * @param {number} x The scale along the x axis.
+ * @param {number} y The scale along the y axis.
+ * @param {number} z The scale along the z axis.
+ * @return {!goog.vec.Mat3.AnyType} return mat so that operations can be
+ *     chained.
+ */
+goog.vec.Mat3.makeScale = function(mat, x, y, z) {
+  goog.vec.Mat3.makeIdentity(mat);
+  goog.vec.Mat3.setDiagonalValues(mat, x, y, z);
+  return mat;
+};
+
+
+/**
+ * Makes the given 3x3 matrix a rotation matrix with the given rotation
+ * angle about the axis defined by the vector (ax, ay, az).
+ *
+ * @param {!goog.vec.Mat3.AnyType} mat The matrix.
+ * @param {number} angle The rotation angle in radians.
+ * @param {number} ax The x component of the rotation axis.
+ * @param {number} ay The y component of the rotation axis.
+ * @param {number} az The z component of the rotation axis.
+ * @return {!goog.vec.Mat3.AnyType} return mat so that operations can be
+ *     chained.
+ */
+goog.vec.Mat3.makeRotate = function(mat, angle, ax, ay, az) {
+  var c = Math.cos(angle);
+  var d = 1 - c;
+  var s = Math.sin(angle);
+
+  goog.vec.Mat3.setFromValues(mat,
+      ax * ax * d + c,
+      ax * ay * d + az * s,
+      ax * az * d - ay * s,
+
+      ax * ay * d - az * s,
+      ay * ay * d + c,
+      ay * az * d + ax * s,
+
+      ax * az * d + ay * s,
+      ay * az * d - ax * s,
+      az * az * d + c);
+  return mat;
+};

+ 1615 - 0
public/lib/closure/vec/mat4.js

@@ -0,0 +1,1615 @@
+// Copyright 2011 The Closure Library Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * @fileoverview Implements 4x4 matrices and their related functions which are
+ * compatible with WebGL. The API is structured to avoid unnecessary memory
+ * allocations.  The last parameter will typically be the output vector and
+ * an object can be both an input and output parameter to all methods except
+ * where noted. Matrix operations follow the mathematical form when multiplying
+ * vectors as follows: resultVec = matrix * vec.
+ *
+ */
+goog.provide('goog.vec.Mat4');
+
+goog.require('goog.vec');
+goog.require('goog.vec.Vec3');
+goog.require('goog.vec.Vec4');
+
+
+/** @typedef {goog.vec.Float32} */ goog.vec.Mat4.Float32;
+/** @typedef {goog.vec.Float64} */ goog.vec.Mat4.Float64;
+/** @typedef {goog.vec.Number} */ goog.vec.Mat4.Number;
+/** @typedef {goog.vec.AnyType} */ goog.vec.Mat4.AnyType;
+
+// The following two types are deprecated - use the above types instead.
+/** @typedef {Float32Array} */ goog.vec.Mat4.Type;
+/** @typedef {goog.vec.ArrayType} */ goog.vec.Mat4.Mat4Like;
+
+
+/**
+ * Creates the array representation of a 4x4 matrix of Float32.
+ * The use of the array directly instead of a class reduces overhead.
+ * The returned matrix is cleared to all zeros.
+ *
+ * @return {!goog.vec.Mat4.Float32} The new matrix.
+ */
+goog.vec.Mat4.createFloat32 = function() {
+  return new Float32Array(16);
+};
+
+
+/**
+ * Creates the array representation of a 4x4 matrix of Float64.
+ * The returned matrix is cleared to all zeros.
+ *
+ * @return {!goog.vec.Mat4.Float64} The new matrix.
+ */
+goog.vec.Mat4.createFloat64 = function() {
+  return new Float64Array(16);
+};
+
+
+/**
+ * Creates the array representation of a 4x4 matrix of Number.
+ * The returned matrix is cleared to all zeros.
+ *
+ * @return {!goog.vec.Mat4.Number} The new matrix.
+ */
+goog.vec.Mat4.createNumber = function() {
+  var a = new Array(16);
+  goog.vec.Mat4.setFromValues(a,
+                              0, 0, 0, 0,
+                              0, 0, 0, 0,
+                              0, 0, 0, 0,
+                              0, 0, 0, 0);
+  return a;
+};
+
+
+/**
+ * Creates the array representation of a 4x4 matrix of Float32.
+ * The returned matrix is cleared to all zeros.
+ *
+ * @deprecated Use createFloat32.
+ * @return {!goog.vec.Mat4.Type} The new matrix.
+ */
+goog.vec.Mat4.create = function() {
+  return goog.vec.Mat4.createFloat32();
+};
+
+
+/**
+ * Creates a 4x4 identity matrix of Float32.
+ *
+ * @return {!goog.vec.Mat4.Float32} The new 16 element array.
+ */
+goog.vec.Mat4.createFloat32Identity = function() {
+  var mat = goog.vec.Mat4.createFloat32();
+  mat[0] = mat[5] = mat[10] = mat[15] = 1;
+  return mat;
+};
+
+
+/**
+ * Creates a 4x4 identity matrix of Float64.
+ *
+ * @return {!goog.vec.Mat4.Float64} The new 16 element array.
+ */
+goog.vec.Mat4.createFloat64Identity = function() {
+  var mat = goog.vec.Mat4.createFloat64();
+  mat[0] = mat[5] = mat[10] = mat[15] = 1;
+  return mat;
+};
+
+
+/**
+ * Creates a 4x4 identity matrix of Number.
+ * The returned matrix is cleared to all zeros.
+ *
+ * @return {!goog.vec.Mat4.Number} The new 16 element array.
+ */
+goog.vec.Mat4.createNumberIdentity = function() {
+  var a = new Array(16);
+  goog.vec.Mat4.setFromValues(a,
+                              1, 0, 0, 0,
+                              0, 1, 0, 0,
+                              0, 0, 1, 0,
+                              0, 0, 0, 1);
+  return a;
+};
+
+
+/**
+ * Creates the array representation of a 4x4 matrix of Float32.
+ * The returned matrix is cleared to all zeros.
+ *
+ * @deprecated Use createFloat32Identity.
+ * @return {!goog.vec.Mat4.Type} The new 16 element array.
+ */
+goog.vec.Mat4.createIdentity = function() {
+  return goog.vec.Mat4.createFloat32Identity();
+};
+
+
+/**
+ * Creates a 4x4 matrix of Float32 initialized from the given array.
+ *
+ * @param {goog.vec.Mat4.AnyType} matrix The array containing the
+ *     matrix values in column major order.
+ * @return {!goog.vec.Mat4.Float32} The new, 16 element array.
+ */
+goog.vec.Mat4.createFloat32FromArray = function(matrix) {
+  var newMatrix = goog.vec.Mat4.createFloat32();
+  goog.vec.Mat4.setFromArray(newMatrix, matrix);
+  return newMatrix;
+};
+
+
+/**
+ * Creates a 4x4 matrix of Float32 initialized from the given values.
+ *
+ * @param {number} v00 The values at (0, 0).
+ * @param {number} v10 The values at (1, 0).
+ * @param {number} v20 The values at (2, 0).
+ * @param {number} v30 The values at (3, 0).
+ * @param {number} v01 The values at (0, 1).
+ * @param {number} v11 The values at (1, 1).
+ * @param {number} v21 The values at (2, 1).
+ * @param {number} v31 The values at (3, 1).
+ * @param {number} v02 The values at (0, 2).
+ * @param {number} v12 The values at (1, 2).
+ * @param {number} v22 The values at (2, 2).
+ * @param {number} v32 The values at (3, 2).
+ * @param {number} v03 The values at (0, 3).
+ * @param {number} v13 The values at (1, 3).
+ * @param {number} v23 The values at (2, 3).
+ * @param {number} v33 The values at (3, 3).
+ * @return {!goog.vec.Mat4.Float32} The new, 16 element array.
+ */
+goog.vec.Mat4.createFloat32FromValues = function(
+    v00, v10, v20, v30,
+    v01, v11, v21, v31,
+    v02, v12, v22, v32,
+    v03, v13, v23, v33) {
+  var newMatrix = goog.vec.Mat4.createFloat32();
+  goog.vec.Mat4.setFromValues(
+      newMatrix, v00, v10, v20, v30, v01, v11, v21, v31, v02, v12, v22, v32,
+      v03, v13, v23, v33);
+  return newMatrix;
+};
+
+
+/**
+ * Creates a clone of a 4x4 matrix of Float32.
+ *
+ * @param {goog.vec.Mat4.Float32} matrix The source 4x4 matrix.
+ * @return {!goog.vec.Mat4.Float32} The new 4x4 element matrix.
+ */
+goog.vec.Mat4.cloneFloat32 = goog.vec.Mat4.createFloat32FromArray;
+
+
+/**
+ * Creates a 4x4 matrix of Float64 initialized from the given array.
+ *
+ * @param {goog.vec.Mat4.AnyType} matrix The array containing the
+ *     matrix values in column major order.
+ * @return {!goog.vec.Mat4.Float64} The new, nine element array.
+ */
+goog.vec.Mat4.createFloat64FromArray = function(matrix) {
+  var newMatrix = goog.vec.Mat4.createFloat64();
+  goog.vec.Mat4.setFromArray(newMatrix, matrix);
+  return newMatrix;
+};
+
+
+/**
+ * Creates a 4x4 matrix of Float64 initialized from the given values.
+ *
+ * @param {number} v00 The values at (0, 0).
+ * @param {number} v10 The values at (1, 0).
+ * @param {number} v20 The values at (2, 0).
+ * @param {number} v30 The values at (3, 0).
+ * @param {number} v01 The values at (0, 1).
+ * @param {number} v11 The values at (1, 1).
+ * @param {number} v21 The values at (2, 1).
+ * @param {number} v31 The values at (3, 1).
+ * @param {number} v02 The values at (0, 2).
+ * @param {number} v12 The values at (1, 2).
+ * @param {number} v22 The values at (2, 2).
+ * @param {number} v32 The values at (3, 2).
+ * @param {number} v03 The values at (0, 3).
+ * @param {number} v13 The values at (1, 3).
+ * @param {number} v23 The values at (2, 3).
+ * @param {number} v33 The values at (3, 3).
+ * @return {!goog.vec.Mat4.Float64} The new, 16 element array.
+ */
+goog.vec.Mat4.createFloat64FromValues = function(
+    v00, v10, v20, v30,
+    v01, v11, v21, v31,
+    v02, v12, v22, v32,
+    v03, v13, v23, v33) {
+  var newMatrix = goog.vec.Mat4.createFloat64();
+  goog.vec.Mat4.setFromValues(
+      newMatrix, v00, v10, v20, v30, v01, v11, v21, v31, v02, v12, v22, v32,
+      v03, v13, v23, v33);
+  return newMatrix;
+};
+
+
+/**
+ * Creates a clone of a 4x4 matrix of Float64.
+ *
+ * @param {goog.vec.Mat4.Float64} matrix The source 4x4 matrix.
+ * @return {!goog.vec.Mat4.Float64} The new 4x4 element matrix.
+ */
+goog.vec.Mat4.cloneFloat64 = goog.vec.Mat4.createFloat64FromArray;
+
+
+/**
+ * Creates a 4x4 matrix of Float32 initialized from the given array.
+ *
+ * @deprecated Use createFloat32FromArray.
+ * @param {goog.vec.Mat4.Mat4Like} matrix The array containing the
+ *     matrix values in column major order.
+ * @return {!goog.vec.Mat4.Type} The new, nine element array.
+ */
+goog.vec.Mat4.createFromArray = function(matrix) {
+  var newMatrix = goog.vec.Mat4.createFloat32();
+  goog.vec.Mat4.setFromArray(newMatrix, matrix);
+  return newMatrix;
+};
+
+
+/**
+ * Creates a 4x4 matrix of Float32 initialized from the given values.
+ *
+ * @deprecated Use createFloat32FromValues.
+ * @param {number} v00 The values at (0, 0).
+ * @param {number} v10 The values at (1, 0).
+ * @param {number} v20 The values at (2, 0).
+ * @param {number} v30 The values at (3, 0).
+ * @param {number} v01 The values at (0, 1).
+ * @param {number} v11 The values at (1, 1).
+ * @param {number} v21 The values at (2, 1).
+ * @param {number} v31 The values at (3, 1).
+ * @param {number} v02 The values at (0, 2).
+ * @param {number} v12 The values at (1, 2).
+ * @param {number} v22 The values at (2, 2).
+ * @param {number} v32 The values at (3, 2).
+ * @param {number} v03 The values at (0, 3).
+ * @param {number} v13 The values at (1, 3).
+ * @param {number} v23 The values at (2, 3).
+ * @param {number} v33 The values at (3, 3).
+ * @return {!goog.vec.Mat4.Type} The new, 16 element array.
+ */
+goog.vec.Mat4.createFromValues = function(
+    v00, v10, v20, v30,
+    v01, v11, v21, v31,
+    v02, v12, v22, v32,
+    v03, v13, v23, v33) {
+  return goog.vec.Mat4.createFloat32FromValues(
+      v00, v10, v20, v30, v01, v11, v21, v31, v02, v12, v22, v32,
+      v03, v13, v23, v33);
+};
+
+
+/**
+ * Creates a clone of a 4x4 matrix of Float32.
+ *
+ * @deprecated Use cloneFloat32.
+ * @param {goog.vec.Mat4.Mat4Like} matrix The source 4x4 matrix.
+ * @return {!goog.vec.Mat4.Type} The new 4x4 element matrix.
+ */
+goog.vec.Mat4.clone = goog.vec.Mat4.createFromArray;
+
+
+/**
+ * Retrieves the element at the requested row and column.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix containing the
+ *     value to retrieve.
+ * @param {number} row The row index.
+ * @param {number} column The column index.
+ * @return {number} The element value at the requested row, column indices.
+ */
+goog.vec.Mat4.getElement = function(mat, row, column) {
+  return mat[row + column * 4];
+};
+
+
+/**
+ * Sets the element at the requested row and column.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix to set the value on.
+ * @param {number} row The row index.
+ * @param {number} column The column index.
+ * @param {number} value The value to set at the requested row, column.
+ */
+goog.vec.Mat4.setElement = function(mat, row, column, value) {
+  mat[row + column * 4] = value;
+};
+
+
+/**
+ * Initializes the matrix from the set of values. Note the values supplied are
+ * in column major order.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the
+ *     values.
+ * @param {number} v00 The values at (0, 0).
+ * @param {number} v10 The values at (1, 0).
+ * @param {number} v20 The values at (2, 0).
+ * @param {number} v30 The values at (3, 0).
+ * @param {number} v01 The values at (0, 1).
+ * @param {number} v11 The values at (1, 1).
+ * @param {number} v21 The values at (2, 1).
+ * @param {number} v31 The values at (3, 1).
+ * @param {number} v02 The values at (0, 2).
+ * @param {number} v12 The values at (1, 2).
+ * @param {number} v22 The values at (2, 2).
+ * @param {number} v32 The values at (3, 2).
+ * @param {number} v03 The values at (0, 3).
+ * @param {number} v13 The values at (1, 3).
+ * @param {number} v23 The values at (2, 3).
+ * @param {number} v33 The values at (3, 3).
+ */
+goog.vec.Mat4.setFromValues = function(
+    mat, v00, v10, v20, v30, v01, v11, v21, v31, v02, v12, v22, v32,
+    v03, v13, v23, v33) {
+  mat[0] = v00;
+  mat[1] = v10;
+  mat[2] = v20;
+  mat[3] = v30;
+  mat[4] = v01;
+  mat[5] = v11;
+  mat[6] = v21;
+  mat[7] = v31;
+  mat[8] = v02;
+  mat[9] = v12;
+  mat[10] = v22;
+  mat[11] = v32;
+  mat[12] = v03;
+  mat[13] = v13;
+  mat[14] = v23;
+  mat[15] = v33;
+};
+
+
+/**
+ * Sets the matrix from the array of values stored in column major order.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the values.
+ * @param {goog.vec.Mat4.AnyType} values The column major ordered
+ *     array of values to store in the matrix.
+ */
+goog.vec.Mat4.setFromArray = function(mat, values) {
+  mat[0] = values[0];
+  mat[1] = values[1];
+  mat[2] = values[2];
+  mat[3] = values[3];
+  mat[4] = values[4];
+  mat[5] = values[5];
+  mat[6] = values[6];
+  mat[7] = values[7];
+  mat[8] = values[8];
+  mat[9] = values[9];
+  mat[10] = values[10];
+  mat[11] = values[11];
+  mat[12] = values[12];
+  mat[13] = values[13];
+  mat[14] = values[14];
+  mat[15] = values[15];
+};
+
+
+/**
+ * Sets the matrix from the array of values stored in row major order.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the values.
+ * @param {goog.vec.Mat4.AnyType} values The row major ordered array of
+ *     values to store in the matrix.
+ */
+goog.vec.Mat4.setFromRowMajorArray = function(mat, values) {
+  mat[0] = values[0];
+  mat[1] = values[4];
+  mat[2] = values[8];
+  mat[3] = values[12];
+
+  mat[4] = values[1];
+  mat[5] = values[5];
+  mat[6] = values[9];
+  mat[7] = values[13];
+
+  mat[8] = values[2];
+  mat[9] = values[6];
+  mat[10] = values[10];
+  mat[11] = values[14];
+
+  mat[12] = values[3];
+  mat[13] = values[7];
+  mat[14] = values[11];
+  mat[15] = values[15];
+};
+
+
+/**
+ * Sets the diagonal values of the matrix from the given values.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the values.
+ * @param {number} v00 The values for (0, 0).
+ * @param {number} v11 The values for (1, 1).
+ * @param {number} v22 The values for (2, 2).
+ * @param {number} v33 The values for (3, 3).
+ */
+goog.vec.Mat4.setDiagonalValues = function(mat, v00, v11, v22, v33) {
+  mat[0] = v00;
+  mat[5] = v11;
+  mat[10] = v22;
+  mat[15] = v33;
+};
+
+
+/**
+ * Sets the diagonal values of the matrix from the given vector.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the values.
+ * @param {goog.vec.Vec4.AnyType} vec The vector containing the values.
+ */
+goog.vec.Mat4.setDiagonal = function(mat, vec) {
+  mat[0] = vec[0];
+  mat[5] = vec[1];
+  mat[10] = vec[2];
+  mat[15] = vec[3];
+};
+
+
+/**
+ * Gets the diagonal values of the matrix into the given vector.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix containing the values.
+ * @param {goog.vec.Vec4.AnyType} vec The vector to receive the values.
+ * @param {number=} opt_diagonal Which diagonal to get. A value of 0 selects the
+ *     main diagonal, a positive number selects a super diagonal and a negative
+ *     number selects a sub diagonal.
+ */
+goog.vec.Mat4.getDiagonal = function(mat, vec, opt_diagonal) {
+  if (!opt_diagonal) {
+    // This is the most common case, so we avoid the for loop.
+    vec[0] = mat[0];
+    vec[1] = mat[5];
+    vec[2] = mat[10];
+    vec[3] = mat[15];
+  } else {
+    var offset = opt_diagonal > 0 ? 4 * opt_diagonal : -opt_diagonal;
+    for (var i = 0; i < 4 - Math.abs(opt_diagonal); i++) {
+      vec[i] = mat[offset + 5 * i];
+    }
+  }
+};
+
+
+/**
+ * Sets the specified column with the supplied values.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix to recieve the values.
+ * @param {number} column The column index to set the values on.
+ * @param {number} v0 The value for row 0.
+ * @param {number} v1 The value for row 1.
+ * @param {number} v2 The value for row 2.
+ * @param {number} v3 The value for row 3.
+ */
+goog.vec.Mat4.setColumnValues = function(mat, column, v0, v1, v2, v3) {
+  var i = column * 4;
+  mat[i] = v0;
+  mat[i + 1] = v1;
+  mat[i + 2] = v2;
+  mat[i + 3] = v3;
+};
+
+
+/**
+ * Sets the specified column with the value from the supplied vector.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the values.
+ * @param {number} column The column index to set the values on.
+ * @param {goog.vec.Vec4.AnyType} vec The vector of elements for the column.
+ */
+goog.vec.Mat4.setColumn = function(mat, column, vec) {
+  var i = column * 4;
+  mat[i] = vec[0];
+  mat[i + 1] = vec[1];
+  mat[i + 2] = vec[2];
+  mat[i + 3] = vec[3];
+};
+
+
+/**
+ * Retrieves the specified column from the matrix into the given vector.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix supplying the values.
+ * @param {number} column The column to get the values from.
+ * @param {goog.vec.Vec4.AnyType} vec The vector of elements to
+ *     receive the column.
+ */
+goog.vec.Mat4.getColumn = function(mat, column, vec) {
+  var i = column * 4;
+  vec[0] = mat[i];
+  vec[1] = mat[i + 1];
+  vec[2] = mat[i + 2];
+  vec[3] = mat[i + 3];
+};
+
+
+/**
+ * Sets the columns of the matrix from the given vectors.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the values.
+ * @param {goog.vec.Vec4.AnyType} vec0 The values for column 0.
+ * @param {goog.vec.Vec4.AnyType} vec1 The values for column 1.
+ * @param {goog.vec.Vec4.AnyType} vec2 The values for column 2.
+ * @param {goog.vec.Vec4.AnyType} vec3 The values for column 3.
+ */
+goog.vec.Mat4.setColumns = function(mat, vec0, vec1, vec2, vec3) {
+  goog.vec.Mat4.setColumn(mat, 0, vec0);
+  goog.vec.Mat4.setColumn(mat, 1, vec1);
+  goog.vec.Mat4.setColumn(mat, 2, vec2);
+  goog.vec.Mat4.setColumn(mat, 3, vec3);
+};
+
+
+/**
+ * Retrieves the column values from the given matrix into the given vectors.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix supplying the columns.
+ * @param {goog.vec.Vec4.AnyType} vec0 The vector to receive column 0.
+ * @param {goog.vec.Vec4.AnyType} vec1 The vector to receive column 1.
+ * @param {goog.vec.Vec4.AnyType} vec2 The vector to receive column 2.
+ * @param {goog.vec.Vec4.AnyType} vec3 The vector to receive column 3.
+ */
+goog.vec.Mat4.getColumns = function(mat, vec0, vec1, vec2, vec3) {
+  goog.vec.Mat4.getColumn(mat, 0, vec0);
+  goog.vec.Mat4.getColumn(mat, 1, vec1);
+  goog.vec.Mat4.getColumn(mat, 2, vec2);
+  goog.vec.Mat4.getColumn(mat, 3, vec3);
+};
+
+
+/**
+ * Sets the row values from the supplied values.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the values.
+ * @param {number} row The index of the row to receive the values.
+ * @param {number} v0 The value for column 0.
+ * @param {number} v1 The value for column 1.
+ * @param {number} v2 The value for column 2.
+ * @param {number} v3 The value for column 3.
+ */
+goog.vec.Mat4.setRowValues = function(mat, row, v0, v1, v2, v3) {
+  mat[row] = v0;
+  mat[row + 4] = v1;
+  mat[row + 8] = v2;
+  mat[row + 12] = v3;
+};
+
+
+/**
+ * Sets the row values from the supplied vector.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the row values.
+ * @param {number} row The index of the row.
+ * @param {goog.vec.Vec4.AnyType} vec The vector containing the values.
+ */
+goog.vec.Mat4.setRow = function(mat, row, vec) {
+  mat[row] = vec[0];
+  mat[row + 4] = vec[1];
+  mat[row + 8] = vec[2];
+  mat[row + 12] = vec[3];
+};
+
+
+/**
+ * Retrieves the row values into the given vector.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix supplying the values.
+ * @param {number} row The index of the row supplying the values.
+ * @param {goog.vec.Vec4.AnyType} vec The vector to receive the row.
+ */
+goog.vec.Mat4.getRow = function(mat, row, vec) {
+  vec[0] = mat[row];
+  vec[1] = mat[row + 4];
+  vec[2] = mat[row + 8];
+  vec[3] = mat[row + 12];
+};
+
+
+/**
+ * Sets the rows of the matrix from the supplied vectors.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the values.
+ * @param {goog.vec.Vec4.AnyType} vec0 The values for row 0.
+ * @param {goog.vec.Vec4.AnyType} vec1 The values for row 1.
+ * @param {goog.vec.Vec4.AnyType} vec2 The values for row 2.
+ * @param {goog.vec.Vec4.AnyType} vec3 The values for row 3.
+ */
+goog.vec.Mat4.setRows = function(mat, vec0, vec1, vec2, vec3) {
+  goog.vec.Mat4.setRow(mat, 0, vec0);
+  goog.vec.Mat4.setRow(mat, 1, vec1);
+  goog.vec.Mat4.setRow(mat, 2, vec2);
+  goog.vec.Mat4.setRow(mat, 3, vec3);
+};
+
+
+/**
+ * Retrieves the rows of the matrix into the supplied vectors.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix to supply the values.
+ * @param {goog.vec.Vec4.AnyType} vec0 The vector to receive row 0.
+ * @param {goog.vec.Vec4.AnyType} vec1 The vector to receive row 1.
+ * @param {goog.vec.Vec4.AnyType} vec2 The vector to receive row 2.
+ * @param {goog.vec.Vec4.AnyType} vec3 The vector to receive row 3.
+ */
+goog.vec.Mat4.getRows = function(mat, vec0, vec1, vec2, vec3) {
+  goog.vec.Mat4.getRow(mat, 0, vec0);
+  goog.vec.Mat4.getRow(mat, 1, vec1);
+  goog.vec.Mat4.getRow(mat, 2, vec2);
+  goog.vec.Mat4.getRow(mat, 3, vec3);
+};
+
+
+/**
+ * Makes the given 4x4 matrix the zero matrix.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix.
+ * @return {!goog.vec.Mat4.AnyType} return mat so operations can be chained.
+ */
+goog.vec.Mat4.makeZero = function(mat) {
+  mat[0] = 0;
+  mat[1] = 0;
+  mat[2] = 0;
+  mat[3] = 0;
+  mat[4] = 0;
+  mat[5] = 0;
+  mat[6] = 0;
+  mat[7] = 0;
+  mat[8] = 0;
+  mat[9] = 0;
+  mat[10] = 0;
+  mat[11] = 0;
+  mat[12] = 0;
+  mat[13] = 0;
+  mat[14] = 0;
+  mat[15] = 0;
+  return mat;
+};
+
+
+/**
+ * Makes the given 4x4 matrix the identity matrix.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix.
+ * @return {!goog.vec.Mat4.AnyType} return mat so operations can be chained.
+ */
+goog.vec.Mat4.makeIdentity = function(mat) {
+  mat[0] = 1;
+  mat[1] = 0;
+  mat[2] = 0;
+  mat[3] = 0;
+  mat[4] = 0;
+  mat[5] = 1;
+  mat[6] = 0;
+  mat[7] = 0;
+  mat[8] = 0;
+  mat[9] = 0;
+  mat[10] = 1;
+  mat[11] = 0;
+  mat[12] = 0;
+  mat[13] = 0;
+  mat[14] = 0;
+  mat[15] = 1;
+  return mat;
+};
+
+
+/**
+ * Performs a per-component addition of the matrix mat0 and mat1, storing
+ * the result into resultMat.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat0 The first addend.
+ * @param {goog.vec.Mat4.AnyType} mat1 The second addend.
+ * @param {goog.vec.Mat4.AnyType} resultMat The matrix to
+ *     receive the results (may be either mat0 or mat1).
+ * @return {!goog.vec.Mat4.AnyType} return resultMat so that operations can be
+ *     chained together.
+ */
+goog.vec.Mat4.addMat = function(mat0, mat1, resultMat) {
+  resultMat[0] = mat0[0] + mat1[0];
+  resultMat[1] = mat0[1] + mat1[1];
+  resultMat[2] = mat0[2] + mat1[2];
+  resultMat[3] = mat0[3] + mat1[3];
+  resultMat[4] = mat0[4] + mat1[4];
+  resultMat[5] = mat0[5] + mat1[5];
+  resultMat[6] = mat0[6] + mat1[6];
+  resultMat[7] = mat0[7] + mat1[7];
+  resultMat[8] = mat0[8] + mat1[8];
+  resultMat[9] = mat0[9] + mat1[9];
+  resultMat[10] = mat0[10] + mat1[10];
+  resultMat[11] = mat0[11] + mat1[11];
+  resultMat[12] = mat0[12] + mat1[12];
+  resultMat[13] = mat0[13] + mat1[13];
+  resultMat[14] = mat0[14] + mat1[14];
+  resultMat[15] = mat0[15] + mat1[15];
+  return resultMat;
+};
+
+
+/**
+ * Performs a per-component subtraction of the matrix mat0 and mat1,
+ * storing the result into resultMat.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat0 The minuend.
+ * @param {goog.vec.Mat4.AnyType} mat1 The subtrahend.
+ * @param {goog.vec.Mat4.AnyType} resultMat The matrix to receive
+ *     the results (may be either mat0 or mat1).
+ * @return {!goog.vec.Mat4.AnyType} return resultMat so that operations can be
+ *     chained together.
+ */
+goog.vec.Mat4.subMat = function(mat0, mat1, resultMat) {
+  resultMat[0] = mat0[0] - mat1[0];
+  resultMat[1] = mat0[1] - mat1[1];
+  resultMat[2] = mat0[2] - mat1[2];
+  resultMat[3] = mat0[3] - mat1[3];
+  resultMat[4] = mat0[4] - mat1[4];
+  resultMat[5] = mat0[5] - mat1[5];
+  resultMat[6] = mat0[6] - mat1[6];
+  resultMat[7] = mat0[7] - mat1[7];
+  resultMat[8] = mat0[8] - mat1[8];
+  resultMat[9] = mat0[9] - mat1[9];
+  resultMat[10] = mat0[10] - mat1[10];
+  resultMat[11] = mat0[11] - mat1[11];
+  resultMat[12] = mat0[12] - mat1[12];
+  resultMat[13] = mat0[13] - mat1[13];
+  resultMat[14] = mat0[14] - mat1[14];
+  resultMat[15] = mat0[15] - mat1[15];
+  return resultMat;
+};
+
+
+/**
+ * Multiplies matrix mat with the given scalar, storing the result
+ * into resultMat.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix.
+ * @param {number} scalar The scalar value to multiply to each element of mat.
+ * @param {goog.vec.Mat4.AnyType} resultMat The matrix to receive
+ *     the results (may be mat).
+ * @return {!goog.vec.Mat4.AnyType} return resultMat so that operations can be
+ *     chained together.
+ */
+goog.vec.Mat4.multScalar = function(mat, scalar, resultMat) {
+  resultMat[0] = mat[0] * scalar;
+  resultMat[1] = mat[1] * scalar;
+  resultMat[2] = mat[2] * scalar;
+  resultMat[3] = mat[3] * scalar;
+  resultMat[4] = mat[4] * scalar;
+  resultMat[5] = mat[5] * scalar;
+  resultMat[6] = mat[6] * scalar;
+  resultMat[7] = mat[7] * scalar;
+  resultMat[8] = mat[8] * scalar;
+  resultMat[9] = mat[9] * scalar;
+  resultMat[10] = mat[10] * scalar;
+  resultMat[11] = mat[11] * scalar;
+  resultMat[12] = mat[12] * scalar;
+  resultMat[13] = mat[13] * scalar;
+  resultMat[14] = mat[14] * scalar;
+  resultMat[15] = mat[15] * scalar;
+  return resultMat;
+};
+
+
+/**
+ * Multiplies the two matrices mat0 and mat1 using matrix multiplication,
+ * storing the result into resultMat.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat0 The first (left hand) matrix.
+ * @param {goog.vec.Mat4.AnyType} mat1 The second (right hand) matrix.
+ * @param {goog.vec.Mat4.AnyType} resultMat The matrix to receive
+ *     the results (may be either mat0 or mat1).
+ * @return {!goog.vec.Mat4.AnyType} return resultMat so that operations can be
+ *     chained together.
+ */
+goog.vec.Mat4.multMat = function(mat0, mat1, resultMat) {
+  var a00 = mat0[0], a10 = mat0[1], a20 = mat0[2], a30 = mat0[3];
+  var a01 = mat0[4], a11 = mat0[5], a21 = mat0[6], a31 = mat0[7];
+  var a02 = mat0[8], a12 = mat0[9], a22 = mat0[10], a32 = mat0[11];
+  var a03 = mat0[12], a13 = mat0[13], a23 = mat0[14], a33 = mat0[15];
+
+  var b00 = mat1[0], b10 = mat1[1], b20 = mat1[2], b30 = mat1[3];
+  var b01 = mat1[4], b11 = mat1[5], b21 = mat1[6], b31 = mat1[7];
+  var b02 = mat1[8], b12 = mat1[9], b22 = mat1[10], b32 = mat1[11];
+  var b03 = mat1[12], b13 = mat1[13], b23 = mat1[14], b33 = mat1[15];
+
+  resultMat[0] = a00 * b00 + a01 * b10 + a02 * b20 + a03 * b30;
+  resultMat[1] = a10 * b00 + a11 * b10 + a12 * b20 + a13 * b30;
+  resultMat[2] = a20 * b00 + a21 * b10 + a22 * b20 + a23 * b30;
+  resultMat[3] = a30 * b00 + a31 * b10 + a32 * b20 + a33 * b30;
+
+  resultMat[4] = a00 * b01 + a01 * b11 + a02 * b21 + a03 * b31;
+  resultMat[5] = a10 * b01 + a11 * b11 + a12 * b21 + a13 * b31;
+  resultMat[6] = a20 * b01 + a21 * b11 + a22 * b21 + a23 * b31;
+  resultMat[7] = a30 * b01 + a31 * b11 + a32 * b21 + a33 * b31;
+
+  resultMat[8] = a00 * b02 + a01 * b12 + a02 * b22 + a03 * b32;
+  resultMat[9] = a10 * b02 + a11 * b12 + a12 * b22 + a13 * b32;
+  resultMat[10] = a20 * b02 + a21 * b12 + a22 * b22 + a23 * b32;
+  resultMat[11] = a30 * b02 + a31 * b12 + a32 * b22 + a33 * b32;
+
+  resultMat[12] = a00 * b03 + a01 * b13 + a02 * b23 + a03 * b33;
+  resultMat[13] = a10 * b03 + a11 * b13 + a12 * b23 + a13 * b33;
+  resultMat[14] = a20 * b03 + a21 * b13 + a22 * b23 + a23 * b33;
+  resultMat[15] = a30 * b03 + a31 * b13 + a32 * b23 + a33 * b33;
+  return resultMat;
+};
+
+
+/**
+ * Transposes the given matrix mat storing the result into resultMat.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix to transpose.
+ * @param {goog.vec.Mat4.AnyType} resultMat The matrix to receive
+ *     the results (may be mat).
+ * @return {!goog.vec.Mat4.AnyType} return resultMat so that operations can be
+ *     chained together.
+ */
+goog.vec.Mat4.transpose = function(mat, resultMat) {
+  if (resultMat == mat) {
+    var a10 = mat[1], a20 = mat[2], a30 = mat[3];
+    var a21 = mat[6], a31 = mat[7];
+    var a32 = mat[11];
+    resultMat[1] = mat[4];
+    resultMat[2] = mat[8];
+    resultMat[3] = mat[12];
+    resultMat[4] = a10;
+    resultMat[6] = mat[9];
+    resultMat[7] = mat[13];
+    resultMat[8] = a20;
+    resultMat[9] = a21;
+    resultMat[11] = mat[14];
+    resultMat[12] = a30;
+    resultMat[13] = a31;
+    resultMat[14] = a32;
+  } else {
+    resultMat[0] = mat[0];
+    resultMat[1] = mat[4];
+    resultMat[2] = mat[8];
+    resultMat[3] = mat[12];
+
+    resultMat[4] = mat[1];
+    resultMat[5] = mat[5];
+    resultMat[6] = mat[9];
+    resultMat[7] = mat[13];
+
+    resultMat[8] = mat[2];
+    resultMat[9] = mat[6];
+    resultMat[10] = mat[10];
+    resultMat[11] = mat[14];
+
+    resultMat[12] = mat[3];
+    resultMat[13] = mat[7];
+    resultMat[14] = mat[11];
+    resultMat[15] = mat[15];
+  }
+  return resultMat;
+};
+
+
+/**
+ * Computes the determinant of the matrix.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix to compute the matrix for.
+ * @return {number} The determinant of the matrix.
+ */
+goog.vec.Mat4.determinant = function(mat) {
+  var m00 = mat[0], m10 = mat[1], m20 = mat[2], m30 = mat[3];
+  var m01 = mat[4], m11 = mat[5], m21 = mat[6], m31 = mat[7];
+  var m02 = mat[8], m12 = mat[9], m22 = mat[10], m32 = mat[11];
+  var m03 = mat[12], m13 = mat[13], m23 = mat[14], m33 = mat[15];
+
+  var a0 = m00 * m11 - m10 * m01;
+  var a1 = m00 * m21 - m20 * m01;
+  var a2 = m00 * m31 - m30 * m01;
+  var a3 = m10 * m21 - m20 * m11;
+  var a4 = m10 * m31 - m30 * m11;
+  var a5 = m20 * m31 - m30 * m21;
+  var b0 = m02 * m13 - m12 * m03;
+  var b1 = m02 * m23 - m22 * m03;
+  var b2 = m02 * m33 - m32 * m03;
+  var b3 = m12 * m23 - m22 * m13;
+  var b4 = m12 * m33 - m32 * m13;
+  var b5 = m22 * m33 - m32 * m23;
+
+  return a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0;
+};
+
+
+/**
+ * Computes the inverse of mat storing the result into resultMat. If the
+ * inverse is defined, this function returns true, false otherwise.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix to invert.
+ * @param {goog.vec.Mat4.AnyType} resultMat The matrix to receive
+ *     the result (may be mat).
+ * @return {boolean} True if the inverse is defined. If false is returned,
+ *     resultMat is not modified.
+ */
+goog.vec.Mat4.invert = function(mat, resultMat) {
+  var m00 = mat[0], m10 = mat[1], m20 = mat[2], m30 = mat[3];
+  var m01 = mat[4], m11 = mat[5], m21 = mat[6], m31 = mat[7];
+  var m02 = mat[8], m12 = mat[9], m22 = mat[10], m32 = mat[11];
+  var m03 = mat[12], m13 = mat[13], m23 = mat[14], m33 = mat[15];
+
+  var a0 = m00 * m11 - m10 * m01;
+  var a1 = m00 * m21 - m20 * m01;
+  var a2 = m00 * m31 - m30 * m01;
+  var a3 = m10 * m21 - m20 * m11;
+  var a4 = m10 * m31 - m30 * m11;
+  var a5 = m20 * m31 - m30 * m21;
+  var b0 = m02 * m13 - m12 * m03;
+  var b1 = m02 * m23 - m22 * m03;
+  var b2 = m02 * m33 - m32 * m03;
+  var b3 = m12 * m23 - m22 * m13;
+  var b4 = m12 * m33 - m32 * m13;
+  var b5 = m22 * m33 - m32 * m23;
+
+  var det = a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0;
+  if (det == 0) {
+    return false;
+  }
+
+  var idet = 1.0 / det;
+  resultMat[0] = (m11 * b5 - m21 * b4 + m31 * b3) * idet;
+  resultMat[1] = (-m10 * b5 + m20 * b4 - m30 * b3) * idet;
+  resultMat[2] = (m13 * a5 - m23 * a4 + m33 * a3) * idet;
+  resultMat[3] = (-m12 * a5 + m22 * a4 - m32 * a3) * idet;
+  resultMat[4] = (-m01 * b5 + m21 * b2 - m31 * b1) * idet;
+  resultMat[5] = (m00 * b5 - m20 * b2 + m30 * b1) * idet;
+  resultMat[6] = (-m03 * a5 + m23 * a2 - m33 * a1) * idet;
+  resultMat[7] = (m02 * a5 - m22 * a2 + m32 * a1) * idet;
+  resultMat[8] = (m01 * b4 - m11 * b2 + m31 * b0) * idet;
+  resultMat[9] = (-m00 * b4 + m10 * b2 - m30 * b0) * idet;
+  resultMat[10] = (m03 * a4 - m13 * a2 + m33 * a0) * idet;
+  resultMat[11] = (-m02 * a4 + m12 * a2 - m32 * a0) * idet;
+  resultMat[12] = (-m01 * b3 + m11 * b1 - m21 * b0) * idet;
+  resultMat[13] = (m00 * b3 - m10 * b1 + m20 * b0) * idet;
+  resultMat[14] = (-m03 * a3 + m13 * a1 - m23 * a0) * idet;
+  resultMat[15] = (m02 * a3 - m12 * a1 + m22 * a0) * idet;
+  return true;
+};
+
+
+/**
+ * Returns true if the components of mat0 are equal to the components of mat1.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat0 The first matrix.
+ * @param {goog.vec.Mat4.AnyType} mat1 The second matrix.
+ * @return {boolean} True if the the two matrices are equivalent.
+ */
+goog.vec.Mat4.equals = function(mat0, mat1) {
+  return mat0.length == mat1.length &&
+      mat0[0] == mat1[0] &&
+      mat0[1] == mat1[1] &&
+      mat0[2] == mat1[2] &&
+      mat0[3] == mat1[3] &&
+      mat0[4] == mat1[4] &&
+      mat0[5] == mat1[5] &&
+      mat0[6] == mat1[6] &&
+      mat0[7] == mat1[7] &&
+      mat0[8] == mat1[8] &&
+      mat0[9] == mat1[9] &&
+      mat0[10] == mat1[10] &&
+      mat0[11] == mat1[11] &&
+      mat0[12] == mat1[12] &&
+      mat0[13] == mat1[13] &&
+      mat0[14] == mat1[14] &&
+      mat0[15] == mat1[15];
+};
+
+
+/**
+ * Transforms the given vector with the given matrix storing the resulting,
+ * transformed vector into resultVec. The input vector is multiplied against the
+ * upper 3x4 matrix omitting the projective component.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix supplying the transformation.
+ * @param {goog.vec.Vec3.AnyType} vec The 3 element vector to transform.
+ * @param {goog.vec.Vec3.AnyType} resultVec The 3 element vector to
+ *     receive the results (may be vec).
+ * @return {!goog.vec.Vec3.AnyType} return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Mat4.multVec3 = function(mat, vec, resultVec) {
+  var x = vec[0], y = vec[1], z = vec[2];
+  resultVec[0] = x * mat[0] + y * mat[4] + z * mat[8] + mat[12];
+  resultVec[1] = x * mat[1] + y * mat[5] + z * mat[9] + mat[13];
+  resultVec[2] = x * mat[2] + y * mat[6] + z * mat[10] + mat[14];
+  return resultVec;
+};
+
+
+/**
+ * Transforms the given vector with the given matrix storing the resulting,
+ * transformed vector into resultVec. The input vector is multiplied against the
+ * upper 3x3 matrix omitting the projective component and translation
+ * components.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix supplying the transformation.
+ * @param {goog.vec.Vec3.AnyType} vec The 3 element vector to transform.
+ * @param {goog.vec.Vec3.AnyType} resultVec The 3 element vector to
+ *     receive the results (may be vec).
+ * @return {!goog.vec.Vec3.AnyType} return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Mat4.multVec3NoTranslate = function(mat, vec, resultVec) {
+  var x = vec[0], y = vec[1], z = vec[2];
+  resultVec[0] = x * mat[0] + y * mat[4] + z * mat[8];
+  resultVec[1] = x * mat[1] + y * mat[5] + z * mat[9];
+  resultVec[2] = x * mat[2] + y * mat[6] + z * mat[10];
+  return resultVec;
+};
+
+
+/**
+ * Transforms the given vector with the given matrix storing the resulting,
+ * transformed vector into resultVec. The input vector is multiplied against the
+ * full 4x4 matrix with the homogeneous divide applied to reduce the 4 element
+ * vector to a 3 element vector.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix supplying the transformation.
+ * @param {goog.vec.Vec3.AnyType} vec The 3 element vector to transform.
+ * @param {goog.vec.Vec3.AnyType} resultVec The 3 element vector
+ *     to receive the results (may be vec).
+ * @return {!goog.vec.Vec3.AnyType} return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Mat4.multVec3Projective = function(mat, vec, resultVec) {
+  var x = vec[0], y = vec[1], z = vec[2];
+  var invw = 1 / (x * mat[3] + y * mat[7] + z * mat[11] + mat[15]);
+  resultVec[0] = (x * mat[0] + y * mat[4] + z * mat[8] + mat[12]) * invw;
+  resultVec[1] = (x * mat[1] + y * mat[5] + z * mat[9] + mat[13]) * invw;
+  resultVec[2] = (x * mat[2] + y * mat[6] + z * mat[10] + mat[14]) * invw;
+  return resultVec;
+};
+
+
+/**
+ * Transforms the given vector with the given matrix storing the resulting,
+ * transformed vector into resultVec.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix supplying the transformation.
+ * @param {goog.vec.Vec4.AnyType} vec The vector to transform.
+ * @param {goog.vec.Vec4.AnyType} resultVec The vector to
+ *     receive the results (may be vec).
+ * @return {!goog.vec.Vec4.AnyType} return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Mat4.multVec4 = function(mat, vec, resultVec) {
+  var x = vec[0], y = vec[1], z = vec[2], w = vec[3];
+  resultVec[0] = x * mat[0] + y * mat[4] + z * mat[8] + w * mat[12];
+  resultVec[1] = x * mat[1] + y * mat[5] + z * mat[9] + w * mat[13];
+  resultVec[2] = x * mat[2] + y * mat[6] + z * mat[10] + w * mat[14];
+  resultVec[3] = x * mat[3] + y * mat[7] + z * mat[11] + w * mat[15];
+  return resultVec;
+};
+
+
+/**
+ * Makes the given 4x4 matrix a translation matrix with x, y and z
+ * translation factors.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix.
+ * @param {number} x The translation along the x axis.
+ * @param {number} y The translation along the y axis.
+ * @param {number} z The translation along the z axis.
+ * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
+ *     chained.
+ */
+goog.vec.Mat4.makeTranslate = function(mat, x, y, z) {
+  goog.vec.Mat4.makeIdentity(mat);
+  goog.vec.Mat4.setColumnValues(mat, 3, x, y, z, 1);
+  return mat;
+};
+
+
+/**
+ * Makes the given 4x4 matrix as a scale matrix with x, y and z scale factors.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix.
+ * @param {number} x The scale along the x axis.
+ * @param {number} y The scale along the y axis.
+ * @param {number} z The scale along the z axis.
+ * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
+ *     chained.
+ */
+goog.vec.Mat4.makeScale = function(mat, x, y, z) {
+  goog.vec.Mat4.makeIdentity(mat);
+  goog.vec.Mat4.setDiagonalValues(mat, x, y, z, 1);
+  return mat;
+};
+
+
+/**
+ * Makes the given 4x4 matrix a rotation matrix with the given rotation
+ * angle about the axis defined by the vector (ax, ay, az).
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix.
+ * @param {number} angle The rotation angle in radians.
+ * @param {number} ax The x component of the rotation axis.
+ * @param {number} ay The y component of the rotation axis.
+ * @param {number} az The z component of the rotation axis.
+ * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
+ *     chained.
+ */
+goog.vec.Mat4.makeRotate = function(mat, angle, ax, ay, az) {
+  var c = Math.cos(angle);
+  var d = 1 - c;
+  var s = Math.sin(angle);
+
+  goog.vec.Mat4.setFromValues(mat,
+      ax * ax * d + c,
+      ax * ay * d + az * s,
+      ax * az * d - ay * s,
+      0,
+
+      ax * ay * d - az * s,
+      ay * ay * d + c,
+      ay * az * d + ax * s,
+      0,
+
+      ax * az * d + ay * s,
+      ay * az * d - ax * s,
+      az * az * d + c,
+      0,
+
+      0, 0, 0, 1);
+  return mat;
+};
+
+
+/**
+ * Makes the given 4x4 matrix a perspective projection matrix.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix.
+ * @param {number} left The coordinate of the left clipping plane.
+ * @param {number} right The coordinate of the right clipping plane.
+ * @param {number} bottom The coordinate of the bottom clipping plane.
+ * @param {number} top The coordinate of the top clipping plane.
+ * @param {number} near The distance to the near clipping plane.
+ * @param {number} far The distance to the far clipping plane.
+ * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
+ *     chained.
+ */
+goog.vec.Mat4.makeFrustum = function(mat, left, right, bottom, top, near, far) {
+  var x = (2 * near) / (right - left);
+  var y = (2 * near) / (top - bottom);
+  var a = (right + left) / (right - left);
+  var b = (top + bottom) / (top - bottom);
+  var c = -(far + near) / (far - near);
+  var d = -(2 * far * near) / (far - near);
+
+  goog.vec.Mat4.setFromValues(mat,
+      x, 0, 0, 0,
+      0, y, 0, 0,
+      a, b, c, -1,
+      0, 0, d, 0
+  );
+  return mat;
+};
+
+
+/**
+ * Makse the given 4x4 matrix  perspective projection matrix given a
+ * field of view and aspect ratio.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix.
+ * @param {number} fovy The field of view along the y (vertical) axis in
+ *     radians.
+ * @param {number} aspect The x (width) to y (height) aspect ratio.
+ * @param {number} near The distance to the near clipping plane.
+ * @param {number} far The distance to the far clipping plane.
+ * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
+ *     chained.
+ */
+goog.vec.Mat4.makePerspective = function(mat, fovy, aspect, near, far) {
+  var angle = fovy / 2;
+  var dz = far - near;
+  var sinAngle = Math.sin(angle);
+  if (dz == 0 || sinAngle == 0 || aspect == 0) {
+    return /** @type {!goog.vec.Mat4.AnyType} */ (mat);
+  }
+
+  var cot = Math.cos(angle) / sinAngle;
+  goog.vec.Mat4.setFromValues(mat,
+      cot / aspect, 0, 0, 0,
+      0, cot, 0, 0,
+      0, 0, -(far + near) / dz, -1,
+      0, 0, -(2 * near * far) / dz, 0
+  );
+  return /** @type {!goog.vec.Mat4.AnyType} */ (mat);
+};
+
+
+/**
+ * Makes the given 4x4 matrix an orthographic projection matrix.
+ *
+ * @param {!goog.vec.Mat4.AnyType} mat The matrix.
+ * @param {number} left The coordinate of the left clipping plane.
+ * @param {number} right The coordinate of the right clipping plane.
+ * @param {number} bottom The coordinate of the bottom clipping plane.
+ * @param {number} top The coordinate of the top clipping plane.
+ * @param {number} near The distance to the near clipping plane.
+ * @param {number} far The distance to the far clipping plane.
+ * @return {!goog.vec.Mat4.AnyType} return mat so that operations can be
+ *     chained.
+ */
+goog.vec.Mat4.makeOrtho = function(mat, left, right, bottom, top, near, far) {
+  var x = 2 / (right - left);
+  var y = 2 / (top - bottom);
+  var z = -2 / (far - near);
+  var a = -(right + left) / (right - left);
+  var b = -(top + bottom) / (top - bottom);
+  var c = -(far + near) / (far - near);
+
+  goog.vec.Mat4.setFromValues(mat,
+      x, 0, 0, 0,
+      0, y, 0, 0,
+      0, 0, z, 0,
+      a, b, c, 1
+  );
+  return /** @type {!goog.vec.Mat4.AnyType} */ (mat);
+};
+
+
+/**
+ * Makes the given 4x4 matrix a modelview matrix of a camera so that
+ * the camera is 'looking at' the given center point.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix.
+ * @param {goog.vec.Vec3.AnyType} eyePt The position of the eye point
+ *     (camera origin).
+ * @param {goog.vec.Vec3.AnyType} centerPt The point to aim the camera at.
+ * @param {goog.vec.Vec3.AnyType} worldUpVec The vector that identifies
+ *     the up direction for the camera.
+ * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
+ *     chained.
+ */
+goog.vec.Mat4.makeLookAt = function(mat, eyePt, centerPt, worldUpVec) {
+  // Compute the direction vector from the eye point to the center point and
+  // normalize.
+  var fwdVec = goog.vec.Mat4.tmpVec4_[0];
+  goog.vec.Vec3.subtract(centerPt, eyePt, fwdVec);
+  goog.vec.Vec3.normalize(fwdVec, fwdVec);
+  fwdVec[3] = 0;
+
+  // Compute the side vector from the forward vector and the input up vector.
+  var sideVec = goog.vec.Mat4.tmpVec4_[1];
+  goog.vec.Vec3.cross(fwdVec, worldUpVec, sideVec);
+  goog.vec.Vec3.normalize(sideVec, sideVec);
+  sideVec[3] = 0;
+
+  // Now the up vector to form the orthonormal basis.
+  var upVec = goog.vec.Mat4.tmpVec4_[2];
+  goog.vec.Vec3.cross(sideVec, fwdVec, upVec);
+  goog.vec.Vec3.normalize(upVec, upVec);
+  upVec[3] = 0;
+
+  // Update the view matrix with the new orthonormal basis and position the
+  // camera at the given eye point.
+  goog.vec.Vec3.negate(fwdVec, fwdVec);
+  goog.vec.Mat4.setRow(mat, 0, sideVec);
+  goog.vec.Mat4.setRow(mat, 1, upVec);
+  goog.vec.Mat4.setRow(mat, 2, fwdVec);
+  goog.vec.Mat4.setRowValues(mat, 3, 0, 0, 0, 1);
+  goog.vec.Mat4.translate(
+      mat, -eyePt[0], -eyePt[1], -eyePt[2]);
+
+  return mat;
+};
+
+
+/**
+ * Decomposes a matrix into the lookAt vectors eyePt, fwdVec and worldUpVec.
+ * The matrix represents the modelview matrix of a camera. It is the inverse
+ * of lookAt except for the output of the fwdVec instead of centerPt.
+ * The centerPt itself cannot be recovered from a modelview matrix.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix.
+ * @param {goog.vec.Vec3.AnyType} eyePt The position of the eye point
+ *     (camera origin).
+ * @param {goog.vec.Vec3.AnyType} fwdVec The vector describing where
+ *     the camera points to.
+ * @param {goog.vec.Vec3.AnyType} worldUpVec The vector that
+ *     identifies the up direction for the camera.
+ * @return {boolean} True if the method succeeds, false otherwise.
+ *     The method can only fail if the inverse of viewMatrix is not defined.
+ */
+goog.vec.Mat4.toLookAt = function(mat, eyePt, fwdVec, worldUpVec) {
+  // Get eye of the camera.
+  var matInverse = goog.vec.Mat4.tmpMat4_[0];
+  if (!goog.vec.Mat4.invert(mat, matInverse)) {
+    // The input matrix does not have a valid inverse.
+    return false;
+  }
+
+  if (eyePt) {
+    eyePt[0] = matInverse[12];
+    eyePt[1] = matInverse[13];
+    eyePt[2] = matInverse[14];
+  }
+
+  // Get forward vector from the definition of lookAt.
+  if (fwdVec || worldUpVec) {
+    if (!fwdVec) {
+      fwdVec = goog.vec.Mat4.tmpVec3_[0];
+    }
+    fwdVec[0] = -mat[2];
+    fwdVec[1] = -mat[6];
+    fwdVec[2] = -mat[10];
+    // Normalize forward vector.
+    goog.vec.Vec3.normalize(fwdVec, fwdVec);
+  }
+
+  if (worldUpVec) {
+    // Get side vector from the definition of gluLookAt.
+    var side = goog.vec.Mat4.tmpVec3_[1];
+    side[0] = mat[0];
+    side[1] = mat[4];
+    side[2] = mat[8];
+    // Compute up vector as a up = side x forward.
+    goog.vec.Vec3.cross(side, fwdVec, worldUpVec);
+    // Normalize up vector.
+    goog.vec.Vec3.normalize(worldUpVec, worldUpVec);
+  }
+  return true;
+};
+
+
+/**
+ * Makes the given 4x4 matrix a rotation matrix given Euler angles using
+ * the ZXZ convention.
+ * Given the euler angles [theta1, theta2, theta3], the rotation is defined as
+ * rotation = rotation_z(theta1) * rotation_x(theta2) * rotation_z(theta3),
+ * with theta1 in [0, 2 * pi], theta2 in [0, pi] and theta3 in [0, 2 * pi].
+ * rotation_x(theta) means rotation around the X axis of theta radians,
+ *
+ * @param {!goog.vec.Mat4.AnyType} mat The matrix.
+ * @param {number} theta1 The angle of rotation around the Z axis in radians.
+ * @param {number} theta2 The angle of rotation around the X axis in radians.
+ * @param {number} theta3 The angle of rotation around the Z axis in radians.
+ * @return {!goog.vec.Mat4.AnyType} return mat so that operations can be
+ *     chained.
+ */
+goog.vec.Mat4.makeEulerZXZ = function(mat, theta1, theta2, theta3) {
+  var c1 = Math.cos(theta1);
+  var s1 = Math.sin(theta1);
+
+  var c2 = Math.cos(theta2);
+  var s2 = Math.sin(theta2);
+
+  var c3 = Math.cos(theta3);
+  var s3 = Math.sin(theta3);
+
+  mat[0] = c1 * c3 - c2 * s1 * s3;
+  mat[1] = c2 * c1 * s3 + c3 * s1;
+  mat[2] = s3 * s2;
+  mat[3] = 0;
+
+  mat[4] = -c1 * s3 - c3 * c2 * s1;
+  mat[5] = c1 * c2 * c3 - s1 * s3;
+  mat[6] = c3 * s2;
+  mat[7] = 0;
+
+  mat[8] = s2 * s1;
+  mat[9] = -c1 * s2;
+  mat[10] = c2;
+  mat[11] = 0;
+
+  mat[12] = 0;
+  mat[13] = 0;
+  mat[14] = 0;
+  mat[15] = 1;
+
+  return mat;
+};
+
+
+/**
+ * Decomposes a rotation matrix into Euler angles using the ZXZ convention so
+ * that rotation = rotation_z(theta1) * rotation_x(theta2) * rotation_z(theta3),
+ * with theta1 in [0, 2 * pi], theta2 in [0, pi] and theta3 in [0, 2 * pi].
+ * rotation_x(theta) means rotation around the X axis of theta radians.
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix.
+ * @param {goog.vec.Mat4.AnyType} euler The ZXZ Euler angles in
+ *     radians as [theta1, theta2, theta3].
+ * @param {boolean=} opt_theta2IsNegative Whether theta2 is in [-pi, 0] instead
+ *     of the default [0, pi].
+ */
+goog.vec.Mat4.toEulerZXZ = function(mat, euler, opt_theta2IsNegative) {
+  // There is an ambiguity in the sign of sinTheta2 because of the sqrt.
+  var sinTheta2 = Math.sqrt(mat[2] * mat[2] + mat[6] * mat[6]);
+
+  // By default we explicitely constrain theta2 to be in [0, pi],
+  // so sinTheta2 is always positive. We can change the behavior and specify
+  // theta2 to be negative in [-pi, 0] with opt_Theta2IsNegative.
+  var signTheta2 = opt_theta2IsNegative ? -1 : 1;
+
+  if (sinTheta2 > goog.vec.EPSILON) {
+    euler[2] = Math.atan2(mat[2] * signTheta2, mat[6] * signTheta2);
+    euler[1] = Math.atan2(sinTheta2 * signTheta2, mat[10]);
+    euler[0] = Math.atan2(mat[8] * signTheta2, -mat[9] * signTheta2);
+  } else {
+    // There is also an arbitrary choice for roll = 0 or pan = 0 in this case.
+    // We assume roll = 0 as some applications do not allow the camera to roll.
+    euler[0] = 0;
+    euler[1] = Math.atan2(sinTheta2 * signTheta2, mat[10]);
+    euler[2] = Math.atan2(mat[1], mat[0]);
+  }
+
+  // Atan2 outputs angles in [-pi, pi] so we bring them back to [0, 2 * pi].
+  euler[0] = (euler[0] + Math.PI * 2) % (Math.PI * 2);
+  euler[2] = (euler[2] + Math.PI * 2) % (Math.PI * 2);
+  // For theta2 we want the angle to be in [0, pi] or [-pi, 0] depending on
+  // signTheta2.
+  euler[1] = ((euler[1] * signTheta2 + Math.PI * 2) % (Math.PI * 2)) *
+      signTheta2;
+};
+
+
+/**
+ * Translates the given matrix by x,y,z.  Equvialent to:
+ * goog.vec.Mat4.multMat(
+ *     mat,
+ *     goog.vec.Mat4.makeTranslate(goog.vec.Mat4.create(), x, y, z),
+ *     mat);
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix.
+ * @param {number} x The translation along the x axis.
+ * @param {number} y The translation along the y axis.
+ * @param {number} z The translation along the z axis.
+ * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
+ *     chained.
+ */
+goog.vec.Mat4.translate = function(mat, x, y, z) {
+  goog.vec.Mat4.setColumnValues(
+      mat, 3,
+      mat[0] * x + mat[4] * y + mat[8] * z + mat[12],
+      mat[1] * x + mat[5] * y + mat[9] * z + mat[13],
+      mat[2] * x + mat[6] * y + mat[10] * z + mat[14],
+      mat[3] * x + mat[7] * y + mat[11] * z + mat[15]);
+  return mat;
+};
+
+
+/**
+ * Scales the given matrix by x,y,z.  Equivalent to:
+ * goog.vec.Mat4.multMat(
+ *     mat,
+ *     goog.vec.Mat4.makeScale(goog.vec.Mat4.create(), x, y, z),
+ *     mat);
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix.
+ * @param {number} x The x scale factor.
+ * @param {number} y The y scale factor.
+ * @param {number} z The z scale factor.
+ * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
+ *     chained.
+ */
+goog.vec.Mat4.scale = function(mat, x, y, z) {
+  goog.vec.Mat4.setFromValues(
+      mat,
+      mat[0] * x, mat[1] * x, mat[2] * x, mat[3] * x,
+      mat[4] * y, mat[5] * y, mat[6] * y, mat[7] * y,
+      mat[8] * z, mat[9] * z, mat[10] * z, mat[11] * z,
+      mat[12], mat[13], mat[14], mat[15]);
+  return mat;
+};
+
+
+/**
+ * Rotation the given matrix by angle about the x,y,z axis.  Equivalent to:
+ * goog.vec.Mat4.multMat(
+ *     mat,
+ *     goog.vec.Mat4.makeRotate(goog.vec.Mat4.create(), angle, x, y, z),
+ *     mat);
+ *
+ * @param {goog.vec.Mat4.AnyType} mat The matrix.
+ * @param {number} angle The angle in radians.
+ * @param {number} x The x component of the rotation axis.
+ * @param {number} y The y component of the rotation axis.
+ * @param {number} z The z component of the rotation axis.
+ * @return {goog.vec.Mat4.AnyType} return mat so that operations can be
+ *     chained.
+ */
+goog.vec.Mat4.rotate = function(mat, angle, x, y, z) {
+  var m00 = mat[0], m10 = mat[1], m20 = mat[2], m30 = mat[3];
+  var m01 = mat[4], m11 = mat[5], m21 = mat[6], m31 = mat[7];
+  var m02 = mat[8], m12 = mat[9], m22 = mat[10], m32 = mat[11];
+  var m03 = mat[12], m13 = mat[13], m23 = mat[14], m33 = mat[15];
+
+  var cosAngle = Math.cos(angle);
+  var sinAngle = Math.sin(angle);
+  var diffCosAngle = 1 - cosAngle;
+  var r00 = x * x * diffCosAngle + cosAngle;
+  var r10 = x * y * diffCosAngle + z * sinAngle;
+  var r20 = x * z * diffCosAngle - y * sinAngle;
+
+  var r01 = x * y * diffCosAngle - z * sinAngle;
+  var r11 = y * y * diffCosAngle + cosAngle;
+  var r21 = y * z * diffCosAngle + x * sinAngle;
+
+  var r02 = x * z * diffCosAngle + y * sinAngle;
+  var r12 = y * z * diffCosAngle - x * sinAngle;
+  var r22 = z * z * diffCosAngle + cosAngle;
+
+  goog.vec.Mat4.setFromValues(
+      mat,
+      m00 * r00 + m01 * r10 + m02 * r20,
+      m10 * r00 + m11 * r10 + m12 * r20,
+      m20 * r00 + m21 * r10 + m22 * r20,
+      m30 * r00 + m31 * r10 + m32 * r20,
+
+      m00 * r01 + m01 * r11 + m02 * r21,
+      m10 * r01 + m11 * r11 + m12 * r21,
+      m20 * r01 + m21 * r11 + m22 * r21,
+      m30 * r01 + m31 * r11 + m32 * r21,
+
+      m00 * r02 + m01 * r12 + m02 * r22,
+      m10 * r02 + m11 * r12 + m12 * r22,
+      m20 * r02 + m21 * r12 + m22 * r22,
+      m30 * r02 + m31 * r12 + m32 * r22,
+
+      m03, m13, m23, m33);
+
+  return mat;
+};
+
+
+/**
+ * @type {Array.<goog.vec.Vec3.Type>}
+ * @private
+ */
+goog.vec.Mat4.tmpVec3_ = [
+  goog.vec.Vec3.createFloat64(),
+  goog.vec.Vec3.createFloat64()
+];
+
+
+/**
+ * @type {Array.<goog.vec.Vec4.Type>}
+ * @private
+ */
+goog.vec.Mat4.tmpVec4_ = [
+  goog.vec.Vec4.createFloat64(),
+  goog.vec.Vec4.createFloat64(),
+  goog.vec.Vec4.createFloat64()
+];
+
+
+/**
+ * @type {Array.<goog.vec.Mat4.Type>}
+ * @private
+ */
+goog.vec.Mat4.tmpMat4_ = [
+  goog.vec.Mat4.createFloat64()
+];

+ 722 - 0
public/lib/closure/vec/matrix3.js

@@ -0,0 +1,722 @@
+// Copyright 2011 The Closure Library Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * @fileoverview WARNING: DEPRECATED.  Use Mat3 instead.
+ * Implements 3x3 matrices and their related functions which are
+ * compatible with WebGL. The API is structured to avoid unnecessary memory
+ * allocations.  The last parameter will typically be the output vector and
+ * an object can be both an input and output parameter to all methods except
+ * where noted. Matrix operations follow the mathematical form when multiplying
+ * vectors as follows: resultVec = matrix * vec.
+ *
+ */
+goog.provide('goog.vec.Matrix3');
+
+goog.require('goog.vec');
+
+
+/**
+ * @typedef {goog.vec.ArrayType}
+ */
+goog.vec.Matrix3.Type;
+
+
+/**
+ * Creates the array representation of a 3x3 matrix. The use of the array
+ * directly eliminates any overhead associated with the class representation
+ * defined above. The returned matrix is cleared to all zeros.
+ *
+ * @return {goog.vec.Matrix3.Type} The new, nine element array.
+ */
+goog.vec.Matrix3.create = function() {
+  return new Float32Array(9);
+};
+
+
+/**
+ * Creates the array representation of a 3x3 matrix. The use of the array
+ * directly eliminates any overhead associated with the class representation
+ * defined above. The returned matrix is initialized with the identity.
+ *
+ * @return {goog.vec.Matrix3.Type} The new, nine element array.
+ */
+goog.vec.Matrix3.createIdentity = function() {
+  var mat = goog.vec.Matrix3.create();
+  mat[0] = mat[4] = mat[8] = 1;
+  return mat;
+};
+
+
+/**
+ * Creates a 3x3 matrix initialized from the given array.
+ *
+ * @param {goog.vec.ArrayType} matrix The array containing the
+ *     matrix values in column major order.
+ * @return {goog.vec.Matrix3.Type} The new, nine element array.
+ */
+goog.vec.Matrix3.createFromArray = function(matrix) {
+  var newMatrix = goog.vec.Matrix3.create();
+  goog.vec.Matrix3.setFromArray(newMatrix, matrix);
+  return newMatrix;
+};
+
+
+/**
+ * Creates a 3x3 matrix initialized from the given values.
+ *
+ * @param {number} v00 The values at (0, 0).
+ * @param {number} v10 The values at (1, 0).
+ * @param {number} v20 The values at (2, 0).
+ * @param {number} v01 The values at (0, 1).
+ * @param {number} v11 The values at (1, 1).
+ * @param {number} v21 The values at (2, 1).
+ * @param {number} v02 The values at (0, 2).
+ * @param {number} v12 The values at (1, 2).
+ * @param {number} v22 The values at (2, 2).
+ * @return {goog.vec.Matrix3.Type} The new, nine element array.
+ */
+goog.vec.Matrix3.createFromValues = function(
+    v00, v10, v20, v01, v11, v21, v02, v12, v22) {
+  var newMatrix = goog.vec.Matrix3.create();
+  goog.vec.Matrix3.setFromValues(
+      newMatrix, v00, v10, v20, v01, v11, v21, v02, v12, v22);
+  return newMatrix;
+};
+
+
+/**
+ * Creates a clone of a 3x3 matrix.
+ *
+ * @param {goog.vec.Matrix3.Type} matrix The source 3x3 matrix.
+ * @return {goog.vec.Matrix3.Type} The new 3x3 element matrix.
+ */
+goog.vec.Matrix3.clone =
+    goog.vec.Matrix3.createFromArray;
+
+
+/**
+ * Retrieves the element at the requested row and column.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix containing the
+ *     value to retrieve.
+ * @param {number} row The row index.
+ * @param {number} column The column index.
+ * @return {number} The element value at the requested row, column indices.
+ */
+goog.vec.Matrix3.getElement = function(mat, row, column) {
+  return mat[row + column * 3];
+};
+
+
+/**
+ * Sets the element at the requested row and column.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix containing the
+ *     value to retrieve.
+ * @param {number} row The row index.
+ * @param {number} column The column index.
+ * @param {number} value The value to set at the requested row, column.
+ */
+goog.vec.Matrix3.setElement = function(mat, row, column, value) {
+  mat[row + column * 3] = value;
+};
+
+
+/**
+ * Initializes the matrix from the set of values. Note the values supplied are
+ * in column major order.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to receive the
+ *     values.
+ * @param {number} v00 The values at (0, 0).
+ * @param {number} v10 The values at (1, 0).
+ * @param {number} v20 The values at (2, 0).
+ * @param {number} v01 The values at (0, 1).
+ * @param {number} v11 The values at (1, 1).
+ * @param {number} v21 The values at (2, 1).
+ * @param {number} v02 The values at (0, 2).
+ * @param {number} v12 The values at (1, 2).
+ * @param {number} v22 The values at (2, 2).
+ */
+goog.vec.Matrix3.setFromValues = function(
+    mat, v00, v10, v20, v01, v11, v21, v02, v12, v22) {
+  mat[0] = v00;
+  mat[1] = v10;
+  mat[2] = v20;
+  mat[3] = v01;
+  mat[4] = v11;
+  mat[5] = v21;
+  mat[6] = v02;
+  mat[7] = v12;
+  mat[8] = v22;
+};
+
+
+/**
+ * Sets the matrix from the array of values stored in column major order.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to receive the
+ *     values.
+ * @param {goog.vec.ArrayType} values The column major ordered
+ *     array of values to store in the matrix.
+ */
+goog.vec.Matrix3.setFromArray = function(mat, values) {
+  mat[0] = values[0];
+  mat[1] = values[1];
+  mat[2] = values[2];
+  mat[3] = values[3];
+  mat[4] = values[4];
+  mat[5] = values[5];
+  mat[6] = values[6];
+  mat[7] = values[7];
+  mat[8] = values[8];
+};
+
+
+/**
+ * Sets the matrix from the array of values stored in row major order.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to receive the
+ *     values.
+ * @param {goog.vec.ArrayType} values The row major ordered array
+ *     of values to store in the matrix.
+ */
+goog.vec.Matrix3.setFromRowMajorArray = function(mat, values) {
+  mat[0] = values[0];
+  mat[1] = values[3];
+  mat[2] = values[6];
+  mat[3] = values[1];
+  mat[4] = values[4];
+  mat[5] = values[7];
+  mat[6] = values[2];
+  mat[7] = values[5];
+  mat[8] = values[8];
+};
+
+
+/**
+ * Sets the diagonal values of the matrix from the given values.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to receive the
+ *     values.
+ * @param {number} v00 The values for (0, 0).
+ * @param {number} v11 The values for (1, 1).
+ * @param {number} v22 The values for (2, 2).
+ */
+goog.vec.Matrix3.setDiagonalValues = function(mat, v00, v11, v22) {
+  mat[0] = v00;
+  mat[4] = v11;
+  mat[8] = v22;
+};
+
+
+/**
+ * Sets the diagonal values of the matrix from the given vector.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to receive the
+ *     values.
+ * @param {goog.vec.ArrayType} vec The vector containing the
+ *     values.
+ */
+goog.vec.Matrix3.setDiagonal = function(mat, vec) {
+  mat[0] = vec[0];
+  mat[4] = vec[1];
+  mat[8] = vec[2];
+};
+
+
+/**
+ * Sets the specified column with the supplied values.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to recieve the
+ *     values.
+ * @param {number} column The column index to set the values on.
+ * @param {number} v0 The value for row 0.
+ * @param {number} v1 The value for row 1.
+ * @param {number} v2 The value for row 2.
+ */
+goog.vec.Matrix3.setColumnValues = function(
+    mat, column, v0, v1, v2) {
+  var i = column * 3;
+  mat[i] = v0;
+  mat[i + 1] = v1;
+  mat[i + 2] = v2;
+};
+
+
+/**
+ * Sets the specified column with the value from the supplied array.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to receive the
+ *     values.
+ * @param {number} column The column index to set the values on.
+ * @param {goog.vec.ArrayType} vec The vector elements for the
+ *     column.
+ */
+goog.vec.Matrix3.setColumn = function(mat, column, vec) {
+  var i = column * 3;
+  mat[i] = vec[0];
+  mat[i + 1] = vec[1];
+  mat[i + 2] = vec[2];
+};
+
+
+/**
+ * Retrieves the specified column from the matrix into the given vector
+ * array.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix supplying the
+ *     values.
+ * @param {number} column The column to get the values from.
+ * @param {goog.vec.ArrayType} vec The vector elements to receive
+ *     the column.
+ */
+goog.vec.Matrix3.getColumn = function(mat, column, vec) {
+  var i = column * 3;
+  vec[0] = mat[i];
+  vec[1] = mat[i + 1];
+  vec[2] = mat[i + 2];
+};
+
+
+/**
+ * Sets the columns of the matrix from the set of vector elements.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to receive the
+ *     values.
+ * @param {goog.vec.ArrayType} vec0 The values for column 0.
+ * @param {goog.vec.ArrayType} vec1 The values for column 1.
+ * @param {goog.vec.ArrayType} vec2 The values for column 2.
+ */
+goog.vec.Matrix3.setColumns = function(
+    mat, vec0, vec1, vec2) {
+  goog.vec.Matrix3.setColumn(mat, 0, vec0);
+  goog.vec.Matrix3.setColumn(mat, 1, vec1);
+  goog.vec.Matrix3.setColumn(mat, 2, vec2);
+};
+
+
+/**
+ * Retrieves the column values from the given matrix into the given vector
+ * elements.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix containing the
+ *     columns to retrieve.
+ * @param {goog.vec.ArrayType} vec0 The vector elements to receive
+ *     column 0.
+ * @param {goog.vec.ArrayType} vec1 The vector elements to receive
+ *     column 1.
+ * @param {goog.vec.ArrayType} vec2 The vector elements to receive
+ *     column 2.
+ */
+goog.vec.Matrix3.getColumns = function(
+    mat, vec0, vec1, vec2) {
+  goog.vec.Matrix3.getColumn(mat, 0, vec0);
+  goog.vec.Matrix3.getColumn(mat, 1, vec1);
+  goog.vec.Matrix3.getColumn(mat, 2, vec2);
+};
+
+
+/**
+ * Sets the row values from the supplied values.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to receive the
+ *     values.
+ * @param {number} row The index of the row to receive the values.
+ * @param {number} v0 The value for column 0.
+ * @param {number} v1 The value for column 1.
+ * @param {number} v2 The value for column 2.
+ */
+goog.vec.Matrix3.setRowValues = function(mat, row, v0, v1, v2) {
+  mat[row] = v0;
+  mat[row + 3] = v1;
+  mat[row + 6] = v2;
+};
+
+
+/**
+ * Sets the row values from the supplied vector.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to receive the
+ *     row values.
+ * @param {number} row The index of the row.
+ * @param {goog.vec.ArrayType} vec The vector containing the values.
+ */
+goog.vec.Matrix3.setRow = function(mat, row, vec) {
+  mat[row] = vec[0];
+  mat[row + 3] = vec[1];
+  mat[row + 6] = vec[2];
+};
+
+
+/**
+ * Retrieves the row values into the given vector.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix supplying the
+ *     values.
+ * @param {number} row The index of the row supplying the values.
+ * @param {goog.vec.ArrayType} vec The vector to receive the row.
+ */
+goog.vec.Matrix3.getRow = function(mat, row, vec) {
+  vec[0] = mat[row];
+  vec[1] = mat[row + 3];
+  vec[2] = mat[row + 6];
+};
+
+
+/**
+ * Sets the rows of the matrix from the supplied vectors.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to receive the
+ *     values.
+ * @param {goog.vec.ArrayType} vec0 The values for row 0.
+ * @param {goog.vec.ArrayType} vec1 The values for row 1.
+ * @param {goog.vec.ArrayType} vec2 The values for row 2.
+ */
+goog.vec.Matrix3.setRows = function(
+    mat, vec0, vec1, vec2) {
+  goog.vec.Matrix3.setRow(mat, 0, vec0);
+  goog.vec.Matrix3.setRow(mat, 1, vec1);
+  goog.vec.Matrix3.setRow(mat, 2, vec2);
+};
+
+
+/**
+ * Retrieves the rows of the matrix into the supplied vectors.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to supplying
+ *     the values.
+ * @param {goog.vec.ArrayType} vec0 The vector to receive row 0.
+ * @param {goog.vec.ArrayType} vec1 The vector to receive row 1.
+ * @param {goog.vec.ArrayType} vec2 The vector to receive row 2.
+ */
+goog.vec.Matrix3.getRows = function(
+    mat, vec0, vec1, vec2) {
+  goog.vec.Matrix3.getRow(mat, 0, vec0);
+  goog.vec.Matrix3.getRow(mat, 1, vec1);
+  goog.vec.Matrix3.getRow(mat, 2, vec2);
+};
+
+
+/**
+ * Clears the given matrix to zero.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to clear.
+ */
+goog.vec.Matrix3.setZero = function(mat) {
+  mat[0] = 0;
+  mat[1] = 0;
+  mat[2] = 0;
+  mat[3] = 0;
+  mat[4] = 0;
+  mat[5] = 0;
+  mat[6] = 0;
+  mat[7] = 0;
+  mat[8] = 0;
+};
+
+
+/**
+ * Sets the given matrix to the identity matrix.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to set.
+ */
+goog.vec.Matrix3.setIdentity = function(mat) {
+  mat[0] = 1;
+  mat[1] = 0;
+  mat[2] = 0;
+  mat[3] = 0;
+  mat[4] = 1;
+  mat[5] = 0;
+  mat[6] = 0;
+  mat[7] = 0;
+  mat[8] = 1;
+};
+
+
+/**
+ * Performs a per-component addition of the matrices mat0 and mat1, storing
+ * the result into resultMat.
+ *
+ * @param {goog.vec.ArrayType} mat0 The first addend.
+ * @param {goog.vec.ArrayType} mat1 The second addend.
+ * @param {goog.vec.ArrayType} resultMat The matrix to
+ *     receive the results (may be either mat0 or mat1).
+ * @return {goog.vec.ArrayType} return resultMat so that operations can be
+ *     chained together.
+ */
+goog.vec.Matrix3.add = function(mat0, mat1, resultMat) {
+  resultMat[0] = mat0[0] + mat1[0];
+  resultMat[1] = mat0[1] + mat1[1];
+  resultMat[2] = mat0[2] + mat1[2];
+  resultMat[3] = mat0[3] + mat1[3];
+  resultMat[4] = mat0[4] + mat1[4];
+  resultMat[5] = mat0[5] + mat1[5];
+  resultMat[6] = mat0[6] + mat1[6];
+  resultMat[7] = mat0[7] + mat1[7];
+  resultMat[8] = mat0[8] + mat1[8];
+  return resultMat;
+};
+
+
+/**
+ * Performs a per-component subtraction of the matrices mat0 and mat1,
+ * storing the result into resultMat.
+ *
+ * @param {goog.vec.ArrayType} mat0 The minuend.
+ * @param {goog.vec.ArrayType} mat1 The subtrahend.
+ * @param {goog.vec.ArrayType} resultMat The matrix to receive
+ *     the results (may be either mat0 or mat1).
+ * @return {goog.vec.ArrayType} return resultMat so that operations can be
+ *     chained together.
+ */
+goog.vec.Matrix3.subtract = function(mat0, mat1, resultMat) {
+  resultMat[0] = mat0[0] - mat1[0];
+  resultMat[1] = mat0[1] - mat1[1];
+  resultMat[2] = mat0[2] - mat1[2];
+  resultMat[3] = mat0[3] - mat1[3];
+  resultMat[4] = mat0[4] - mat1[4];
+  resultMat[5] = mat0[5] - mat1[5];
+  resultMat[6] = mat0[6] - mat1[6];
+  resultMat[7] = mat0[7] - mat1[7];
+  resultMat[8] = mat0[8] - mat1[8];
+  return resultMat;
+};
+
+
+/**
+ * Performs a component-wise multiplication of mat0 with the given scalar
+ * storing the result into resultMat.
+ *
+ * @param {goog.vec.ArrayType} mat0 The matrix to scale.
+ * @param {number} scalar The scalar value to multiple to each element of mat0.
+ * @param {goog.vec.ArrayType} resultMat The matrix to receive
+ *     the results (may be mat0).
+ * @return {goog.vec.ArrayType} return resultMat so that operations can be
+ *     chained together.
+ */
+goog.vec.Matrix3.scale = function(mat0, scalar, resultMat) {
+  resultMat[0] = mat0[0] * scalar;
+  resultMat[1] = mat0[1] * scalar;
+  resultMat[2] = mat0[2] * scalar;
+  resultMat[3] = mat0[3] * scalar;
+  resultMat[4] = mat0[4] * scalar;
+  resultMat[5] = mat0[5] * scalar;
+  resultMat[6] = mat0[6] * scalar;
+  resultMat[7] = mat0[7] * scalar;
+  resultMat[8] = mat0[8] * scalar;
+  return resultMat;
+};
+
+
+/**
+ * Multiplies the two matrices mat0 and mat1 using matrix multiplication,
+ * storing the result into resultMat.
+ *
+ * @param {goog.vec.ArrayType} mat0 The first (left hand) matrix.
+ * @param {goog.vec.ArrayType} mat1 The second (right hand)
+ *     matrix.
+ * @param {goog.vec.ArrayType} resultMat The matrix to receive
+ *     the results (may be either mat0 or mat1).
+ * @return {goog.vec.ArrayType} return resultMat so that operations can be
+ *     chained together.
+ */
+goog.vec.Matrix3.multMat = function(mat0, mat1, resultMat) {
+  var a00 = mat0[0], a10 = mat0[1], a20 = mat0[2];
+  var a01 = mat0[3], a11 = mat0[4], a21 = mat0[5];
+  var a02 = mat0[6], a12 = mat0[7], a22 = mat0[8];
+
+  var b00 = mat1[0], b10 = mat1[1], b20 = mat1[2];
+  var b01 = mat1[3], b11 = mat1[4], b21 = mat1[5];
+  var b02 = mat1[6], b12 = mat1[7], b22 = mat1[8];
+
+  resultMat[0] = a00 * b00 + a01 * b10 + a02 * b20;
+  resultMat[1] = a10 * b00 + a11 * b10 + a12 * b20;
+  resultMat[2] = a20 * b00 + a21 * b10 + a22 * b20;
+  resultMat[3] = a00 * b01 + a01 * b11 + a02 * b21;
+  resultMat[4] = a10 * b01 + a11 * b11 + a12 * b21;
+  resultMat[5] = a20 * b01 + a21 * b11 + a22 * b21;
+  resultMat[6] = a00 * b02 + a01 * b12 + a02 * b22;
+  resultMat[7] = a10 * b02 + a11 * b12 + a12 * b22;
+  resultMat[8] = a20 * b02 + a21 * b12 + a22 * b22;
+  return resultMat;
+};
+
+
+/**
+ * Transposes the given matrix mat storing the result into resultMat.
+ * @param {goog.vec.ArrayType} mat The matrix to transpose.
+ * @param {goog.vec.ArrayType} resultMat The matrix to receive
+ *     the results (may be mat).
+ * @return {goog.vec.ArrayType} return resultMat so that operations can be
+ *     chained together.
+ */
+goog.vec.Matrix3.transpose = function(mat, resultMat) {
+  if (resultMat == mat) {
+    var a10 = mat[1], a20 = mat[2], a21 = mat[5];
+    resultMat[1] = mat[3];
+    resultMat[2] = mat[6];
+    resultMat[3] = a10;
+    resultMat[5] = mat[7];
+    resultMat[6] = a20;
+    resultMat[7] = a21;
+  } else {
+    resultMat[0] = mat[0];
+    resultMat[1] = mat[3];
+    resultMat[2] = mat[6];
+    resultMat[3] = mat[1];
+    resultMat[4] = mat[4];
+    resultMat[5] = mat[7];
+    resultMat[6] = mat[2];
+    resultMat[7] = mat[5];
+    resultMat[8] = mat[8];
+  }
+  return resultMat;
+};
+
+
+/**
+ * Computes the inverse of mat0 storing the result into resultMat. If the
+ * inverse is defined, this function returns true, false otherwise.
+ * @param {goog.vec.ArrayType} mat0 The matrix to invert.
+ * @param {goog.vec.ArrayType} resultMat The matrix to receive
+ *     the result (may be mat0).
+ * @return {boolean} True if the inverse is defined. If false is returned,
+ *     resultMat is not modified.
+ */
+goog.vec.Matrix3.invert = function(mat0, resultMat) {
+  var a00 = mat0[0], a10 = mat0[1], a20 = mat0[2];
+  var a01 = mat0[3], a11 = mat0[4], a21 = mat0[5];
+  var a02 = mat0[6], a12 = mat0[7], a22 = mat0[8];
+
+  var t00 = a11 * a22 - a12 * a21;
+  var t10 = a12 * a20 - a10 * a22;
+  var t20 = a10 * a21 - a11 * a20;
+  var det = a00 * t00 + a01 * t10 + a02 * t20;
+  if (det == 0) {
+    return false;
+  }
+
+  var idet = 1 / det;
+  resultMat[0] = t00 * idet;
+  resultMat[3] = (a02 * a21 - a01 * a22) * idet;
+  resultMat[6] = (a01 * a12 - a02 * a11) * idet;
+
+  resultMat[1] = t10 * idet;
+  resultMat[4] = (a00 * a22 - a02 * a20) * idet;
+  resultMat[7] = (a02 * a10 - a00 * a12) * idet;
+
+  resultMat[2] = t20 * idet;
+  resultMat[5] = (a01 * a20 - a00 * a21) * idet;
+  resultMat[8] = (a00 * a11 - a01 * a10) * idet;
+  return true;
+};
+
+
+/**
+ * Returns true if the components of mat0 are equal to the components of mat1.
+ *
+ * @param {goog.vec.ArrayType} mat0 The first matrix.
+ * @param {goog.vec.ArrayType} mat1 The second matrix.
+ * @return {boolean} True if the the two matrices are equivalent.
+ */
+goog.vec.Matrix3.equals = function(mat0, mat1) {
+  return mat0.length == mat1.length &&
+      mat0[0] == mat1[0] && mat0[1] == mat1[1] && mat0[2] == mat1[2] &&
+      mat0[3] == mat1[3] && mat0[4] == mat1[4] && mat0[5] == mat1[5] &&
+      mat0[6] == mat1[6] && mat0[7] == mat1[7] && mat0[8] == mat1[8];
+};
+
+
+/**
+ * Transforms the given vector with the given matrix storing the resulting,
+ * transformed matrix into resultVec.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix supplying the
+ *     transformation.
+ * @param {goog.vec.ArrayType} vec The vector to transform.
+ * @param {goog.vec.ArrayType} resultVec The vector to
+ *     receive the results (may be vec).
+ * @return {goog.vec.ArrayType} return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Matrix3.multVec3 = function(mat, vec, resultVec) {
+  var x = vec[0], y = vec[1], z = vec[2];
+  resultVec[0] = x * mat[0] + y * mat[3] + z * mat[6];
+  resultVec[1] = x * mat[1] + y * mat[4] + z * mat[7];
+  resultVec[2] = x * mat[2] + y * mat[5] + z * mat[8];
+  return resultVec;
+};
+
+
+/**
+ * Initializes the given 3x3 matrix as a translation matrix with x and y
+ * translation values.
+ *
+ * @param {goog.vec.ArrayType} mat The 3x3 (9-element) matrix
+ *     array to receive the new translation matrix.
+ * @param {number} x The translation along the x axis.
+ * @param {number} y The translation along the y axis.
+ */
+goog.vec.Matrix3.makeTranslate = function(mat, x, y) {
+  goog.vec.Matrix3.setIdentity(mat);
+  goog.vec.Matrix3.setColumnValues(mat, 2, x, y, 1);
+};
+
+
+/**
+ * Initializes the given 3x3 matrix as a scale matrix with x, y and z scale
+ * factors.
+ * @param {goog.vec.ArrayType} mat The 3x3 (9-element) matrix
+ *     array to receive the new scale matrix.
+ * @param {number} x The scale along the x axis.
+ * @param {number} y The scale along the y axis.
+ * @param {number} z The scale along the z axis.
+ */
+goog.vec.Matrix3.makeScale = function(mat, x, y, z) {
+  goog.vec.Matrix3.setIdentity(mat);
+  goog.vec.Matrix3.setDiagonalValues(mat, x, y, z);
+};
+
+
+/**
+ * Initializes the given 3x3 matrix as a rotation matrix with the given rotation
+ * angle about the axis defined by the vector (ax, ay, az).
+ * @param {goog.vec.ArrayType} mat The 3x3 (9-element) matrix
+ *     array to receive the new scale matrix.
+ * @param {number} angle The rotation angle in radians.
+ * @param {number} ax The x component of the rotation axis.
+ * @param {number} ay The y component of the rotation axis.
+ * @param {number} az The z component of the rotation axis.
+ */
+goog.vec.Matrix3.makeAxisAngleRotate = function(
+    mat, angle, ax, ay, az) {
+  var c = Math.cos(angle);
+  var d = 1 - c;
+  var s = Math.sin(angle);
+
+  goog.vec.Matrix3.setFromValues(mat,
+      ax * ax * d + c,
+      ax * ay * d + az * s,
+      ax * az * d - ay * s,
+
+      ax * ay * d - az * s,
+      ay * ay * d + c,
+      ay * az * d + ax * s,
+
+      ax * az * d + ay * s,
+      ay * az * d - ax * s,
+      az * az * d + c);
+};

+ 1405 - 0
public/lib/closure/vec/matrix4.js

@@ -0,0 +1,1405 @@
+// Copyright 2011 The Closure Library Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * @fileoverview WARNING: DEPRECATED.  Use Mat4 instead.
+ * Implements 4x4 matrices and their related functions which are
+ * compatible with WebGL. The API is structured to avoid unnecessary memory
+ * allocations.  The last parameter will typically be the output vector and
+ * an object can be both an input and output parameter to all methods except
+ * where noted. Matrix operations follow the mathematical form when multiplying
+ * vectors as follows: resultVec = matrix * vec.
+ *
+ */
+goog.provide('goog.vec.Matrix4');
+
+goog.require('goog.vec');
+goog.require('goog.vec.Vec3');
+goog.require('goog.vec.Vec4');
+
+
+/**
+ * @typedef {goog.vec.ArrayType}
+ */
+goog.vec.Matrix4.Type;
+
+
+/**
+ * Creates the array representation of a 4x4 matrix. The use of the array
+ * directly eliminates any overhead associated with the class representation
+ * defined above. The returned matrix is cleared to all zeros.
+ *
+ * @return {goog.vec.Matrix4.Type} The new, sixteen element array.
+ */
+goog.vec.Matrix4.create = function() {
+  return new Float32Array(16);
+};
+
+
+/**
+ * Creates the array representation of a 4x4 matrix. The use of the array
+ * directly eliminates any overhead associated with the class representation
+ * defined above. The returned matrix is initialized with the identity
+ *
+ * @return {goog.vec.Matrix4.Type} The new, sixteen element array.
+ */
+goog.vec.Matrix4.createIdentity = function() {
+  var mat = goog.vec.Matrix4.create();
+  mat[0] = mat[5] = mat[10] = mat[15] = 1;
+  return mat;
+};
+
+
+/**
+ * Creates a 4x4 matrix initialized from the given array.
+ *
+ * @param {goog.vec.ArrayType} matrix The array containing the
+ *     matrix values in column major order.
+ * @return {goog.vec.Matrix4.Type} The new, 16 element array.
+ */
+goog.vec.Matrix4.createFromArray = function(matrix) {
+  var newMatrix = goog.vec.Matrix4.create();
+  goog.vec.Matrix4.setFromArray(newMatrix, matrix);
+  return newMatrix;
+};
+
+
+/**
+ * Creates a 4x4 matrix initialized from the given values.
+ *
+ * @param {number} v00 The values at (0, 0).
+ * @param {number} v10 The values at (1, 0).
+ * @param {number} v20 The values at (2, 0).
+ * @param {number} v30 The values at (3, 0).
+ * @param {number} v01 The values at (0, 1).
+ * @param {number} v11 The values at (1, 1).
+ * @param {number} v21 The values at (2, 1).
+ * @param {number} v31 The values at (3, 1).
+ * @param {number} v02 The values at (0, 2).
+ * @param {number} v12 The values at (1, 2).
+ * @param {number} v22 The values at (2, 2).
+ * @param {number} v32 The values at (3, 2).
+ * @param {number} v03 The values at (0, 3).
+ * @param {number} v13 The values at (1, 3).
+ * @param {number} v23 The values at (2, 3).
+ * @param {number} v33 The values at (3, 3).
+ * @return {goog.vec.Matrix4.Type} The new, 16 element array.
+ */
+goog.vec.Matrix4.createFromValues = function(
+    v00, v10, v20, v30, v01, v11, v21, v31, v02, v12, v22, v32,
+    v03, v13, v23, v33) {
+  var newMatrix = goog.vec.Matrix4.create();
+  goog.vec.Matrix4.setFromValues(
+      newMatrix, v00, v10, v20, v30, v01, v11, v21, v31, v02, v12, v22, v32,
+      v03, v13, v23, v33);
+  return newMatrix;
+};
+
+
+/**
+ * Creates a clone of a 4x4 matrix.
+ *
+ * @param {goog.vec.Matrix4.Type} matrix The source 4x4 matrix.
+ * @return {goog.vec.Matrix4.Type} The new, 16 element matrix.
+ */
+goog.vec.Matrix4.clone =
+    goog.vec.Matrix4.createFromArray;
+
+
+/**
+ * Retrieves the element at the requested row and column.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix containing the
+ *     value to retrieve.
+ * @param {number} row The row index.
+ * @param {number} column The column index.
+ * @return {number} The element value at the requested row, column indices.
+ */
+goog.vec.Matrix4.getElement = function(mat, row, column) {
+  return mat[row + column * 4];
+};
+
+
+/**
+ * Sets the element at the requested row and column.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix containing the
+ *     value to retrieve.
+ * @param {number} row The row index.
+ * @param {number} column The column index.
+ * @param {number} value The value to set at the requested row, column.
+ */
+goog.vec.Matrix4.setElement = function(mat, row, column, value) {
+  mat[row + column * 4] = value;
+};
+
+
+/**
+ * Initializes the matrix from the set of values. Note the values supplied are
+ * in column major order.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to receive the
+ *     values.
+ * @param {number} v00 The values at (0, 0).
+ * @param {number} v10 The values at (1, 0).
+ * @param {number} v20 The values at (2, 0).
+ * @param {number} v30 The values at (3, 0).
+ * @param {number} v01 The values at (0, 1).
+ * @param {number} v11 The values at (1, 1).
+ * @param {number} v21 The values at (2, 1).
+ * @param {number} v31 The values at (3, 1).
+ * @param {number} v02 The values at (0, 2).
+ * @param {number} v12 The values at (1, 2).
+ * @param {number} v22 The values at (2, 2).
+ * @param {number} v32 The values at (3, 2).
+ * @param {number} v03 The values at (0, 3).
+ * @param {number} v13 The values at (1, 3).
+ * @param {number} v23 The values at (2, 3).
+ * @param {number} v33 The values at (3, 3).
+ */
+goog.vec.Matrix4.setFromValues = function(
+    mat, v00, v10, v20, v30, v01, v11, v21, v31, v02, v12, v22, v32,
+    v03, v13, v23, v33) {
+  mat[0] = v00;
+  mat[1] = v10;
+  mat[2] = v20;
+  mat[3] = v30;
+  mat[4] = v01;
+  mat[5] = v11;
+  mat[6] = v21;
+  mat[7] = v31;
+  mat[8] = v02;
+  mat[9] = v12;
+  mat[10] = v22;
+  mat[11] = v32;
+  mat[12] = v03;
+  mat[13] = v13;
+  mat[14] = v23;
+  mat[15] = v33;
+};
+
+
+/**
+ * Sets the matrix from the array of values stored in column major order.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to receive the
+ *     values.
+ * @param {goog.vec.ArrayType} values The column major ordered
+ *     array of values to store in the matrix.
+ */
+goog.vec.Matrix4.setFromArray = function(mat, values) {
+  mat[0] = values[0];
+  mat[1] = values[1];
+  mat[2] = values[2];
+  mat[3] = values[3];
+  mat[4] = values[4];
+  mat[5] = values[5];
+  mat[6] = values[6];
+  mat[7] = values[7];
+  mat[8] = values[8];
+  mat[9] = values[9];
+  mat[10] = values[10];
+  mat[11] = values[11];
+  mat[12] = values[12];
+  mat[13] = values[13];
+  mat[14] = values[14];
+  mat[15] = values[15];
+};
+
+
+/**
+ * Sets the matrix from the array of values stored in row major order.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to receive the
+ *     values.
+ * @param {goog.vec.ArrayType} values The row major ordered array of
+ *     values to store in the matrix.
+ */
+goog.vec.Matrix4.setFromRowMajorArray = function(mat, values) {
+  mat[0] = values[0];
+  mat[1] = values[4];
+  mat[2] = values[8];
+  mat[3] = values[12];
+
+  mat[4] = values[1];
+  mat[5] = values[5];
+  mat[6] = values[9];
+  mat[7] = values[13];
+
+  mat[8] = values[2];
+  mat[9] = values[6];
+  mat[10] = values[10];
+  mat[11] = values[14];
+
+  mat[12] = values[3];
+  mat[13] = values[7];
+  mat[14] = values[11];
+  mat[15] = values[15];
+};
+
+
+/**
+ * Sets the diagonal values of the matrix from the given values.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to receive the
+ *     values.
+ * @param {number} v00 The values for (0, 0).
+ * @param {number} v11 The values for (1, 1).
+ * @param {number} v22 The values for (2, 2).
+ * @param {number} v33 The values for (3, 3).
+ */
+goog.vec.Matrix4.setDiagonalValues = function(
+    mat, v00, v11, v22, v33) {
+  mat[0] = v00;
+  mat[5] = v11;
+  mat[10] = v22;
+  mat[15] = v33;
+};
+
+
+/**
+ * Sets the diagonal values of the matrix from the given vector.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to receive the
+ *     values.
+ * @param {goog.vec.ArrayType} vec The vector containing the
+ *     values.
+ */
+goog.vec.Matrix4.setDiagonal = function(mat, vec) {
+  mat[0] = vec[0];
+  mat[5] = vec[1];
+  mat[10] = vec[2];
+  mat[15] = vec[3];
+};
+
+
+/**
+ * Sets the specified column with the supplied values.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to recieve the
+ *     values.
+ * @param {number} column The column index to set the values on.
+ * @param {number} v0 The value for row 0.
+ * @param {number} v1 The value for row 1.
+ * @param {number} v2 The value for row 2.
+ * @param {number} v3 The value for row 3.
+ */
+goog.vec.Matrix4.setColumnValues = function(
+    mat, column, v0, v1, v2, v3) {
+  var i = column * 4;
+  mat[i] = v0;
+  mat[i + 1] = v1;
+  mat[i + 2] = v2;
+  mat[i + 3] = v3;
+};
+
+
+/**
+ * Sets the specified column with the value from the supplied array.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to receive the
+ *     values.
+ * @param {number} column The column index to set the values on.
+ * @param {goog.vec.ArrayType} vec The vector of elements for the
+ *     column.
+ */
+goog.vec.Matrix4.setColumn = function(mat, column, vec) {
+  var i = column * 4;
+  mat[i] = vec[0];
+  mat[i + 1] = vec[1];
+  mat[i + 2] = vec[2];
+  mat[i + 3] = vec[3];
+};
+
+
+/**
+ * Retrieves the specified column from the matrix into the given vector
+ * array.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix supplying the
+ *     values.
+ * @param {number} column The column to get the values from.
+ * @param {goog.vec.ArrayType} vec The vector of elements to
+ *     receive the column.
+ */
+goog.vec.Matrix4.getColumn = function(mat, column, vec) {
+  var i = column * 4;
+  vec[0] = mat[i];
+  vec[1] = mat[i + 1];
+  vec[2] = mat[i + 2];
+  vec[3] = mat[i + 3];
+};
+
+
+/**
+ * Sets the columns of the matrix from the set of vector elements.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to receive the
+ *     values.
+ * @param {goog.vec.ArrayType} vec0 The values for column 0.
+ * @param {goog.vec.ArrayType} vec1 The values for column 1.
+ * @param {goog.vec.ArrayType} vec2 The values for column 2.
+ * @param {goog.vec.ArrayType} vec3 The values for column 3.
+ */
+goog.vec.Matrix4.setColumns = function(
+    mat, vec0, vec1, vec2, vec3) {
+  goog.vec.Matrix4.setColumn(mat, 0, vec0);
+  goog.vec.Matrix4.setColumn(mat, 1, vec1);
+  goog.vec.Matrix4.setColumn(mat, 2, vec2);
+  goog.vec.Matrix4.setColumn(mat, 3, vec3);
+};
+
+
+/**
+ * Retrieves the column values from the given matrix into the given vector
+ * elements.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix containing the
+ *     columns to retrieve.
+ * @param {goog.vec.ArrayType} vec0 The vector elements to receive
+ *     column 0.
+ * @param {goog.vec.ArrayType} vec1 The vector elements to receive
+ *     column 1.
+ * @param {goog.vec.ArrayType} vec2 The vector elements to receive
+ *     column 2.
+ * @param {goog.vec.ArrayType} vec3 The vector elements to receive
+ *     column 3.
+ */
+goog.vec.Matrix4.getColumns = function(
+    mat, vec0, vec1, vec2, vec3) {
+  goog.vec.Matrix4.getColumn(mat, 0, vec0);
+  goog.vec.Matrix4.getColumn(mat, 1, vec1);
+  goog.vec.Matrix4.getColumn(mat, 2, vec2);
+  goog.vec.Matrix4.getColumn(mat, 3, vec3);
+};
+
+
+/**
+ * Sets the row values from the supplied values.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to receive the
+ *     values.
+ * @param {number} row The index of the row to receive the values.
+ * @param {number} v0 The value for column 0.
+ * @param {number} v1 The value for column 1.
+ * @param {number} v2 The value for column 2.
+ * @param {number} v3 The value for column 3.
+ */
+goog.vec.Matrix4.setRowValues = function(mat, row, v0, v1, v2, v3) {
+  mat[row] = v0;
+  mat[row + 4] = v1;
+  mat[row + 8] = v2;
+  mat[row + 12] = v3;
+};
+
+
+/**
+ * Sets the row values from the supplied vector.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to receive the
+ *     row values.
+ * @param {number} row The index of the row.
+ * @param {goog.vec.ArrayType} vec The vector containing the
+ *     values.
+ */
+goog.vec.Matrix4.setRow = function(mat, row, vec) {
+  mat[row] = vec[0];
+  mat[row + 4] = vec[1];
+  mat[row + 8] = vec[2];
+  mat[row + 12] = vec[3];
+};
+
+
+/**
+ * Retrieves the row values into the given vector.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix supplying the
+ *     values.
+ * @param {number} row The index of the row supplying the values.
+ * @param {goog.vec.ArrayType} vec The vector to receive the
+ *     row.
+ */
+goog.vec.Matrix4.getRow = function(mat, row, vec) {
+  vec[0] = mat[row];
+  vec[1] = mat[row + 4];
+  vec[2] = mat[row + 8];
+  vec[3] = mat[row + 12];
+};
+
+
+/**
+ * Sets the rows of the matrix from the supplied vectors.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to receive the
+ *     values.
+ * @param {goog.vec.ArrayType} vec0 The values for row 0.
+ * @param {goog.vec.ArrayType} vec1 The values for row 1.
+ * @param {goog.vec.ArrayType} vec2 The values for row 2.
+ * @param {goog.vec.ArrayType} vec3 The values for row 3.
+ */
+goog.vec.Matrix4.setRows = function(
+    mat, vec0, vec1, vec2, vec3) {
+  goog.vec.Matrix4.setRow(mat, 0, vec0);
+  goog.vec.Matrix4.setRow(mat, 1, vec1);
+  goog.vec.Matrix4.setRow(mat, 2, vec2);
+  goog.vec.Matrix4.setRow(mat, 3, vec3);
+};
+
+
+/**
+ * Retrieves the rows of the matrix into the supplied vectors.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to supply the
+ *     values.
+ * @param {goog.vec.ArrayType} vec0 The vector to receive row 0.
+ * @param {goog.vec.ArrayType} vec1 The vector to receive row 1.
+ * @param {goog.vec.ArrayType} vec2 The vector to receive row 2.
+ * @param {goog.vec.ArrayType} vec3 The vector to receive row 3.
+ */
+goog.vec.Matrix4.getRows = function(
+    mat, vec0, vec1, vec2, vec3) {
+  goog.vec.Matrix4.getRow(mat, 0, vec0);
+  goog.vec.Matrix4.getRow(mat, 1, vec1);
+  goog.vec.Matrix4.getRow(mat, 2, vec2);
+  goog.vec.Matrix4.getRow(mat, 3, vec3);
+};
+
+
+/**
+ * Clears the given matrix to zero.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to clear.
+ */
+goog.vec.Matrix4.setZero = function(mat) {
+  mat[0] = 0;
+  mat[1] = 0;
+  mat[2] = 0;
+  mat[3] = 0;
+  mat[4] = 0;
+  mat[5] = 0;
+  mat[6] = 0;
+  mat[7] = 0;
+  mat[8] = 0;
+  mat[9] = 0;
+  mat[10] = 0;
+  mat[11] = 0;
+  mat[12] = 0;
+  mat[13] = 0;
+  mat[14] = 0;
+  mat[15] = 0;
+};
+
+
+/**
+ * Sets the given matrix to the identity matrix.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to set.
+ */
+goog.vec.Matrix4.setIdentity = function(mat) {
+  mat[0] = 1;
+  mat[1] = 0;
+  mat[2] = 0;
+  mat[3] = 0;
+  mat[4] = 0;
+  mat[5] = 1;
+  mat[6] = 0;
+  mat[7] = 0;
+  mat[8] = 0;
+  mat[9] = 0;
+  mat[10] = 1;
+  mat[11] = 0;
+  mat[12] = 0;
+  mat[13] = 0;
+  mat[14] = 0;
+  mat[15] = 1;
+};
+
+
+/**
+ * Performs a per-component addition of the matrix mat0 and mat1, storing
+ * the result into resultMat.
+ *
+ * @param {goog.vec.ArrayType} mat0 The first addend.
+ * @param {goog.vec.ArrayType} mat1 The second addend.
+ * @param {goog.vec.ArrayType} resultMat The matrix to
+ *     receive the results (may be either mat0 or mat1).
+ * @return {goog.vec.ArrayType} return resultMat so that operations can be
+ *     chained together.
+ */
+goog.vec.Matrix4.add = function(mat0, mat1, resultMat) {
+  resultMat[0] = mat0[0] + mat1[0];
+  resultMat[1] = mat0[1] + mat1[1];
+  resultMat[2] = mat0[2] + mat1[2];
+  resultMat[3] = mat0[3] + mat1[3];
+  resultMat[4] = mat0[4] + mat1[4];
+  resultMat[5] = mat0[5] + mat1[5];
+  resultMat[6] = mat0[6] + mat1[6];
+  resultMat[7] = mat0[7] + mat1[7];
+  resultMat[8] = mat0[8] + mat1[8];
+  resultMat[9] = mat0[9] + mat1[9];
+  resultMat[10] = mat0[10] + mat1[10];
+  resultMat[11] = mat0[11] + mat1[11];
+  resultMat[12] = mat0[12] + mat1[12];
+  resultMat[13] = mat0[13] + mat1[13];
+  resultMat[14] = mat0[14] + mat1[14];
+  resultMat[15] = mat0[15] + mat1[15];
+  return resultMat;
+};
+
+
+/**
+ * Performs a per-component subtraction of the matrix mat0 and mat1,
+ * storing the result into resultMat.
+ *
+ * @param {goog.vec.ArrayType} mat0 The minuend.
+ * @param {goog.vec.ArrayType} mat1 The subtrahend.
+ * @param {goog.vec.ArrayType} resultMat The matrix to receive
+ *     the results (may be either mat0 or mat1).
+ * @return {goog.vec.ArrayType} return resultMat so that operations can be
+ *     chained together.
+ */
+goog.vec.Matrix4.subtract = function(mat0, mat1, resultMat) {
+  resultMat[0] = mat0[0] - mat1[0];
+  resultMat[1] = mat0[1] - mat1[1];
+  resultMat[2] = mat0[2] - mat1[2];
+  resultMat[3] = mat0[3] - mat1[3];
+  resultMat[4] = mat0[4] - mat1[4];
+  resultMat[5] = mat0[5] - mat1[5];
+  resultMat[6] = mat0[6] - mat1[6];
+  resultMat[7] = mat0[7] - mat1[7];
+  resultMat[8] = mat0[8] - mat1[8];
+  resultMat[9] = mat0[9] - mat1[9];
+  resultMat[10] = mat0[10] - mat1[10];
+  resultMat[11] = mat0[11] - mat1[11];
+  resultMat[12] = mat0[12] - mat1[12];
+  resultMat[13] = mat0[13] - mat1[13];
+  resultMat[14] = mat0[14] - mat1[14];
+  resultMat[15] = mat0[15] - mat1[15];
+  return resultMat;
+};
+
+
+/**
+ * Performs a component-wise multiplication of mat0 with the given scalar
+ * storing the result into resultMat.
+ *
+ * @param {goog.vec.ArrayType} mat0 The matrix to scale.
+ * @param {number} scalar The scalar value to multiple to each element of mat0.
+ * @param {goog.vec.ArrayType} resultMat The matrix to receive
+ *     the results (may be mat0).
+ * @return {goog.vec.ArrayType} return resultMat so that operations can be
+ *     chained together.
+ */
+goog.vec.Matrix4.scale = function(mat0, scalar, resultMat) {
+  resultMat[0] = mat0[0] * scalar;
+  resultMat[1] = mat0[1] * scalar;
+  resultMat[2] = mat0[2] * scalar;
+  resultMat[3] = mat0[3] * scalar;
+  resultMat[4] = mat0[4] * scalar;
+  resultMat[5] = mat0[5] * scalar;
+  resultMat[6] = mat0[6] * scalar;
+  resultMat[7] = mat0[7] * scalar;
+  resultMat[8] = mat0[8] * scalar;
+  resultMat[9] = mat0[9] * scalar;
+  resultMat[10] = mat0[10] * scalar;
+  resultMat[11] = mat0[11] * scalar;
+  resultMat[12] = mat0[12] * scalar;
+  resultMat[13] = mat0[13] * scalar;
+  resultMat[14] = mat0[14] * scalar;
+  resultMat[15] = mat0[15] * scalar;
+  return resultMat;
+};
+
+
+/**
+ * Multiplies the two matrices mat0 and mat1 using matrix multiplication,
+ * storing the result into resultMat.
+ *
+ * @param {goog.vec.ArrayType} mat0 The first (left hand) matrix.
+ * @param {goog.vec.ArrayType} mat1 The second (right hand)
+ *     matrix.
+ * @param {goog.vec.ArrayType} resultMat The matrix to receive
+ *     the results (may be either mat0 or mat1).
+ * @return {goog.vec.ArrayType} return resultMat so that operations can be
+ *     chained together.
+ */
+goog.vec.Matrix4.multMat = function(mat0, mat1, resultMat) {
+  var a00 = mat0[0], a10 = mat0[1], a20 = mat0[2], a30 = mat0[3];
+  var a01 = mat0[4], a11 = mat0[5], a21 = mat0[6], a31 = mat0[7];
+  var a02 = mat0[8], a12 = mat0[9], a22 = mat0[10], a32 = mat0[11];
+  var a03 = mat0[12], a13 = mat0[13], a23 = mat0[14], a33 = mat0[15];
+
+  var b00 = mat1[0], b10 = mat1[1], b20 = mat1[2], b30 = mat1[3];
+  var b01 = mat1[4], b11 = mat1[5], b21 = mat1[6], b31 = mat1[7];
+  var b02 = mat1[8], b12 = mat1[9], b22 = mat1[10], b32 = mat1[11];
+  var b03 = mat1[12], b13 = mat1[13], b23 = mat1[14], b33 = mat1[15];
+
+  resultMat[0] = a00 * b00 + a01 * b10 + a02 * b20 + a03 * b30;
+  resultMat[1] = a10 * b00 + a11 * b10 + a12 * b20 + a13 * b30;
+  resultMat[2] = a20 * b00 + a21 * b10 + a22 * b20 + a23 * b30;
+  resultMat[3] = a30 * b00 + a31 * b10 + a32 * b20 + a33 * b30;
+
+  resultMat[4] = a00 * b01 + a01 * b11 + a02 * b21 + a03 * b31;
+  resultMat[5] = a10 * b01 + a11 * b11 + a12 * b21 + a13 * b31;
+  resultMat[6] = a20 * b01 + a21 * b11 + a22 * b21 + a23 * b31;
+  resultMat[7] = a30 * b01 + a31 * b11 + a32 * b21 + a33 * b31;
+
+  resultMat[8] = a00 * b02 + a01 * b12 + a02 * b22 + a03 * b32;
+  resultMat[9] = a10 * b02 + a11 * b12 + a12 * b22 + a13 * b32;
+  resultMat[10] = a20 * b02 + a21 * b12 + a22 * b22 + a23 * b32;
+  resultMat[11] = a30 * b02 + a31 * b12 + a32 * b22 + a33 * b32;
+
+  resultMat[12] = a00 * b03 + a01 * b13 + a02 * b23 + a03 * b33;
+  resultMat[13] = a10 * b03 + a11 * b13 + a12 * b23 + a13 * b33;
+  resultMat[14] = a20 * b03 + a21 * b13 + a22 * b23 + a23 * b33;
+  resultMat[15] = a30 * b03 + a31 * b13 + a32 * b23 + a33 * b33;
+  return resultMat;
+};
+
+
+/**
+ * Transposes the given matrix mat storing the result into resultMat.
+ * @param {goog.vec.ArrayType} mat The matrix to transpose.
+ * @param {goog.vec.ArrayType} resultMat The matrix to receive
+ *     the results (may be mat).
+ * @return {goog.vec.ArrayType} return resultMat so that operations can be
+ *     chained together.
+ */
+goog.vec.Matrix4.transpose = function(mat, resultMat) {
+  if (resultMat == mat) {
+    var a10 = mat[1], a20 = mat[2], a30 = mat[3];
+    var a21 = mat[6], a31 = mat[7];
+    var a32 = mat[11];
+    resultMat[1] = mat[4];
+    resultMat[2] = mat[8];
+    resultMat[3] = mat[12];
+    resultMat[4] = a10;
+    resultMat[6] = mat[9];
+    resultMat[7] = mat[13];
+    resultMat[8] = a20;
+    resultMat[9] = a21;
+    resultMat[11] = mat[14];
+    resultMat[12] = a30;
+    resultMat[13] = a31;
+    resultMat[14] = a32;
+  } else {
+    resultMat[0] = mat[0];
+    resultMat[1] = mat[4];
+    resultMat[2] = mat[8];
+    resultMat[3] = mat[12];
+
+    resultMat[4] = mat[1];
+    resultMat[5] = mat[5];
+    resultMat[6] = mat[9];
+    resultMat[7] = mat[13];
+
+    resultMat[8] = mat[2];
+    resultMat[9] = mat[6];
+    resultMat[10] = mat[10];
+    resultMat[11] = mat[14];
+
+    resultMat[12] = mat[3];
+    resultMat[13] = mat[7];
+    resultMat[14] = mat[11];
+    resultMat[15] = mat[15];
+  }
+  return resultMat;
+};
+
+
+/**
+ * Computes the determinant of the matrix.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to compute the
+ *     matrix for.
+ * @return {number} The determinant of the matrix.
+ */
+goog.vec.Matrix4.determinant = function(mat) {
+  var m00 = mat[0], m10 = mat[1], m20 = mat[2], m30 = mat[3];
+  var m01 = mat[4], m11 = mat[5], m21 = mat[6], m31 = mat[7];
+  var m02 = mat[8], m12 = mat[9], m22 = mat[10], m32 = mat[11];
+  var m03 = mat[12], m13 = mat[13], m23 = mat[14], m33 = mat[15];
+
+  var a0 = m00 * m11 - m10 * m01;
+  var a1 = m00 * m21 - m20 * m01;
+  var a2 = m00 * m31 - m30 * m01;
+  var a3 = m10 * m21 - m20 * m11;
+  var a4 = m10 * m31 - m30 * m11;
+  var a5 = m20 * m31 - m30 * m21;
+  var b0 = m02 * m13 - m12 * m03;
+  var b1 = m02 * m23 - m22 * m03;
+  var b2 = m02 * m33 - m32 * m03;
+  var b3 = m12 * m23 - m22 * m13;
+  var b4 = m12 * m33 - m32 * m13;
+  var b5 = m22 * m33 - m32 * m23;
+
+  return a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0;
+};
+
+
+/**
+ * Computes the inverse of mat storing the result into resultMat. If the
+ * inverse is defined, this function returns true, false otherwise.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix to invert.
+ * @param {goog.vec.ArrayType} resultMat The matrix to receive
+ *     the result (may be mat).
+ * @return {boolean} True if the inverse is defined. If false is returned,
+ *     resultMat is not modified.
+ */
+goog.vec.Matrix4.invert = function(mat, resultMat) {
+  var m00 = mat[0], m10 = mat[1], m20 = mat[2], m30 = mat[3];
+  var m01 = mat[4], m11 = mat[5], m21 = mat[6], m31 = mat[7];
+  var m02 = mat[8], m12 = mat[9], m22 = mat[10], m32 = mat[11];
+  var m03 = mat[12], m13 = mat[13], m23 = mat[14], m33 = mat[15];
+
+  var a0 = m00 * m11 - m10 * m01;
+  var a1 = m00 * m21 - m20 * m01;
+  var a2 = m00 * m31 - m30 * m01;
+  var a3 = m10 * m21 - m20 * m11;
+  var a4 = m10 * m31 - m30 * m11;
+  var a5 = m20 * m31 - m30 * m21;
+  var b0 = m02 * m13 - m12 * m03;
+  var b1 = m02 * m23 - m22 * m03;
+  var b2 = m02 * m33 - m32 * m03;
+  var b3 = m12 * m23 - m22 * m13;
+  var b4 = m12 * m33 - m32 * m13;
+  var b5 = m22 * m33 - m32 * m23;
+
+  var det = a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0;
+  if (det == 0) {
+    return false;
+  }
+
+  var idet = 1.0 / det;
+  resultMat[0] = (m11 * b5 - m21 * b4 + m31 * b3) * idet;
+  resultMat[1] = (-m10 * b5 + m20 * b4 - m30 * b3) * idet;
+  resultMat[2] = (m13 * a5 - m23 * a4 + m33 * a3) * idet;
+  resultMat[3] = (-m12 * a5 + m22 * a4 - m32 * a3) * idet;
+  resultMat[4] = (-m01 * b5 + m21 * b2 - m31 * b1) * idet;
+  resultMat[5] = (m00 * b5 - m20 * b2 + m30 * b1) * idet;
+  resultMat[6] = (-m03 * a5 + m23 * a2 - m33 * a1) * idet;
+  resultMat[7] = (m02 * a5 - m22 * a2 + m32 * a1) * idet;
+  resultMat[8] = (m01 * b4 - m11 * b2 + m31 * b0) * idet;
+  resultMat[9] = (-m00 * b4 + m10 * b2 - m30 * b0) * idet;
+  resultMat[10] = (m03 * a4 - m13 * a2 + m33 * a0) * idet;
+  resultMat[11] = (-m02 * a4 + m12 * a2 - m32 * a0) * idet;
+  resultMat[12] = (-m01 * b3 + m11 * b1 - m21 * b0) * idet;
+  resultMat[13] = (m00 * b3 - m10 * b1 + m20 * b0) * idet;
+  resultMat[14] = (-m03 * a3 + m13 * a1 - m23 * a0) * idet;
+  resultMat[15] = (m02 * a3 - m12 * a1 + m22 * a0) * idet;
+  return true;
+};
+
+
+/**
+ * Returns true if the components of mat0 are equal to the components of mat1.
+ *
+ * @param {goog.vec.ArrayType} mat0 The first matrix.
+ * @param {goog.vec.ArrayType} mat1 The second matrix.
+ * @return {boolean} True if the the two matrices are equivalent.
+ */
+goog.vec.Matrix4.equals = function(mat0, mat1) {
+  return mat0.length == mat1.length &&
+      mat0[0] == mat1[0] &&
+      mat0[1] == mat1[1] &&
+      mat0[2] == mat1[2] &&
+      mat0[3] == mat1[3] &&
+      mat0[4] == mat1[4] &&
+      mat0[5] == mat1[5] &&
+      mat0[6] == mat1[6] &&
+      mat0[7] == mat1[7] &&
+      mat0[8] == mat1[8] &&
+      mat0[9] == mat1[9] &&
+      mat0[10] == mat1[10] &&
+      mat0[11] == mat1[11] &&
+      mat0[12] == mat1[12] &&
+      mat0[13] == mat1[13] &&
+      mat0[14] == mat1[14] &&
+      mat0[15] == mat1[15];
+};
+
+
+/**
+ * Transforms the given vector with the given matrix storing the resulting,
+ * transformed vector into resultVec. The input vector is multiplied against the
+ * upper 3x4 matrix omitting the projective component.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix supplying the
+ *     transformation.
+ * @param {goog.vec.ArrayType} vec The 3 element vector to
+ *     transform.
+ * @param {goog.vec.ArrayType} resultVec The 3 element vector to
+ *     receive the results (may be vec).
+ * @return {goog.vec.ArrayType} return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Matrix4.multVec3 = function(mat, vec, resultVec) {
+  var x = vec[0], y = vec[1], z = vec[2];
+  resultVec[0] = x * mat[0] + y * mat[4] + z * mat[8] + mat[12];
+  resultVec[1] = x * mat[1] + y * mat[5] + z * mat[9] + mat[13];
+  resultVec[2] = x * mat[2] + y * mat[6] + z * mat[10] + mat[14];
+  return resultVec;
+};
+
+
+/**
+ * Transforms the given vector with the given matrix storing the resulting,
+ * transformed vector into resultVec. The input vector is multiplied against the
+ * upper 3x3 matrix omitting the projective component and translation
+ * components.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix supplying the
+ *     transformation.
+ * @param {goog.vec.ArrayType} vec The 3 element vector to
+ *     transform.
+ * @param {goog.vec.ArrayType} resultVec The 3 element vector to
+ *     receive the results (may be vec).
+ * @return {goog.vec.ArrayType} return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Matrix4.multVec3NoTranslate = function(
+    mat, vec, resultVec) {
+  var x = vec[0], y = vec[1], z = vec[2];
+  resultVec[0] = x * mat[0] + y * mat[4] + z * mat[8];
+  resultVec[1] = x * mat[1] + y * mat[5] + z * mat[9];
+  resultVec[2] = x * mat[2] + y * mat[6] + z * mat[10];
+  return resultVec;
+};
+
+
+/**
+ * Transforms the given vector with the given matrix storing the resulting,
+ * transformed vector into resultVec. The input vector is multiplied against the
+ * full 4x4 matrix with the homogeneous divide applied to reduce the 4 element
+ * vector to a 3 element vector.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix supplying the
+ *     transformation.
+ * @param {goog.vec.ArrayType} vec The 3 element vector to
+ *     transform.
+ * @param {goog.vec.ArrayType} resultVec The 3 element vector
+ *     to receive the results (may be vec).
+ * @return {goog.vec.ArrayType} return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Matrix4.multVec3Projective = function(
+    mat, vec, resultVec) {
+  var x = vec[0], y = vec[1], z = vec[2];
+  var invw = 1 / (x * mat[3] + y * mat[7] + z * mat[11] + mat[15]);
+  resultVec[0] = (x * mat[0] + y * mat[4] + z * mat[8] + mat[12]) * invw;
+  resultVec[1] = (x * mat[1] + y * mat[5] + z * mat[9] + mat[13]) * invw;
+  resultVec[2] = (x * mat[2] + y * mat[6] + z * mat[10] + mat[14]) * invw;
+  return resultVec;
+};
+
+
+/**
+ * Transforms the given vector with the given matrix storing the resulting,
+ * transformed vector into resultVec.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix supplying the
+ *     transformation.
+ * @param {goog.vec.ArrayType} vec The vector to transform.
+ * @param {goog.vec.ArrayType} resultVec The vector to
+ *     receive the results (may be vec).
+ * @return {goog.vec.ArrayType} return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Matrix4.multVec4 = function(mat, vec, resultVec) {
+  var x = vec[0], y = vec[1], z = vec[2], w = vec[3];
+  resultVec[0] = x * mat[0] + y * mat[4] + z * mat[8] + w * mat[12];
+  resultVec[1] = x * mat[1] + y * mat[5] + z * mat[9] + w * mat[13];
+  resultVec[2] = x * mat[2] + y * mat[6] + z * mat[10] + w * mat[14];
+  resultVec[3] = x * mat[3] + y * mat[7] + z * mat[11] + w * mat[15];
+  return resultVec;
+};
+
+
+/**
+ * Transforms the given vector with the given matrix storing the resulting,
+ * transformed vector into resultVec. The input matrix is multiplied against the
+ * upper 3x4 matrix omitting the projective component.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix supplying the
+ *     transformation.
+ * @param {goog.vec.ArrayType} vec The 3 element vector to
+ *     transform.
+ * @param {goog.vec.ArrayType} resultVec The 3 element vector to
+ *     receive the results (may be vec).
+ * @return {goog.vec.ArrayType} return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Matrix4.multVec3ToArray = function(mat, vec, resultVec) {
+  goog.vec.Matrix4.multVec3(
+      mat, vec, (/** @type {goog.vec.ArrayType} */ resultVec));
+  return resultVec;
+};
+
+
+/**
+ * Transforms the given vector with the given matrix storing the resulting,
+ * transformed vector into resultVec.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix supplying the
+ *     transformation.
+ * @param {goog.vec.ArrayType} vec The vector to transform.
+ * @param {goog.vec.ArrayType} resultVec The vector to
+ *     receive the results (may be vec).
+ * @return {goog.vec.ArrayType} return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Matrix4.multVec4ToArray = function(mat, vec, resultVec) {
+  goog.vec.Matrix4.multVec4(
+      mat, vec, (/** @type {goog.vec.ArrayType} */ resultVec));
+  return resultVec;
+};
+
+
+/**
+ * Initializes the given 4x4 matrix as a translation matrix with x, y and z
+ * translation factors.
+ * @param {goog.vec.ArrayType} mat The 4x4 (16-element) matrix
+ *     array to receive the new translation matrix.
+ * @param {number} x The translation along the x axis.
+ * @param {number} y The translation along the y axis.
+ * @param {number} z The translation along the z axis.
+ */
+goog.vec.Matrix4.makeTranslate = function(mat, x, y, z) {
+  goog.vec.Matrix4.setIdentity(mat);
+  goog.vec.Matrix4.setColumnValues(mat, 3, x, y, z, 1);
+};
+
+
+/**
+ * Initializes the given 4x4 matrix as a scale matrix with x, y and z scale
+ * factors.
+ * @param {goog.vec.ArrayType} mat The 4x4 (16-element) matrix
+ *     array to receive the new translation matrix.
+ * @param {number} x The scale along the x axis.
+ * @param {number} y The scale along the y axis.
+ * @param {number} z The scale along the z axis.
+ */
+goog.vec.Matrix4.makeScale = function(mat, x, y, z) {
+  goog.vec.Matrix4.setIdentity(mat);
+  goog.vec.Matrix4.setDiagonalValues(mat, x, y, z, 1);
+};
+
+
+/**
+ * Initializes the given 4x4 matrix as a rotation matrix with the given rotation
+ * angle about the axis defined by the vector (ax, ay, az).
+ * @param {goog.vec.ArrayType} mat The 4x4 (16-element) matrix
+ *     array to receive the new translation matrix.
+ * @param {number} angle The rotation angle in radians.
+ * @param {number} ax The x component of the rotation axis.
+ * @param {number} ay The y component of the rotation axis.
+ * @param {number} az The z component of the rotation axis.
+ */
+goog.vec.Matrix4.makeAxisAngleRotate = function(
+    mat, angle, ax, ay, az) {
+  var c = Math.cos(angle);
+  var d = 1 - c;
+  var s = Math.sin(angle);
+
+  goog.vec.Matrix4.setFromValues(mat,
+      ax * ax * d + c,
+      ax * ay * d + az * s,
+      ax * az * d - ay * s,
+      0,
+
+      ax * ay * d - az * s,
+      ay * ay * d + c,
+      ay * az * d + ax * s,
+      0,
+
+      ax * az * d + ay * s,
+      ay * az * d - ax * s,
+      az * az * d + c,
+      0,
+
+      0, 0, 0, 1);
+};
+
+
+/**
+ * Initializes the given 4x4 matrix as a perspective projection matrix.
+ * @param {goog.vec.ArrayType} mat The 4x4 (16-element) matrix
+ *     array to receive the new translation matrix.
+ * @param {number} left The coordinate of the left clipping plane.
+ * @param {number} right The coordinate of the right clipping plane.
+ * @param {number} bottom The coordinate of the bottom clipping plane.
+ * @param {number} top The coordinate of the top clipping plane.
+ * @param {number} near The distance to the near clipping plane.
+ * @param {number} far The distance to the far clipping plane.
+ */
+goog.vec.Matrix4.makeFrustum = function(
+    mat, left, right, bottom, top, near, far) {
+  var x = (2 * near) / (right - left);
+  var y = (2 * near) / (top - bottom);
+  var a = (right + left) / (right - left);
+  var b = (top + bottom) / (top - bottom);
+  var c = -(far + near) / (far - near);
+  var d = -(2 * far * near) / (far - near);
+
+  goog.vec.Matrix4.setFromValues(mat,
+      x, 0, 0, 0,
+      0, y, 0, 0,
+      a, b, c, -1,
+      0, 0, d, 0
+  );
+};
+
+
+/**
+ * Initializes the given 4x4 matrix as a perspective projection matrix given a
+ * field of view and aspect ratio.
+ * @param {goog.vec.ArrayType} mat The 4x4 (16-element) matrix
+ *     array to receive the new translation matrix.
+ * @param {number} fovy The field of view along the y (vertical) axis in
+ *     radians.
+ * @param {number} aspect The x (width) to y (height) aspect ratio.
+ * @param {number} near The distance to the near clipping plane.
+ * @param {number} far The distance to the far clipping plane.
+ */
+goog.vec.Matrix4.makePerspective = function(
+    mat, fovy, aspect, near, far) {
+  var angle = fovy / 2;
+  var dz = far - near;
+  var sinAngle = Math.sin(angle);
+  if (dz == 0 || sinAngle == 0 || aspect == 0) return;
+
+  var cot = Math.cos(angle) / sinAngle;
+  goog.vec.Matrix4.setFromValues(mat,
+      cot / aspect, 0, 0, 0,
+      0, cot, 0, 0,
+      0, 0, -(far + near) / dz, -1,
+      0, 0, -(2 * near * far) / dz, 0
+  );
+};
+
+
+/**
+ * Initializes the given 4x4 matrix as an orthographic projection matrix.
+ * @param {goog.vec.ArrayType} mat The 4x4 (16-element) matrix
+ *     array to receive the new translation matrix.
+ * @param {number} left The coordinate of the left clipping plane.
+ * @param {number} right The coordinate of the right clipping plane.
+ * @param {number} bottom The coordinate of the bottom clipping plane.
+ * @param {number} top The coordinate of the top clipping plane.
+ * @param {number} near The distance to the near clipping plane.
+ * @param {number} far The distance to the far clipping plane.
+ */
+goog.vec.Matrix4.makeOrtho = function(
+    mat, left, right, bottom, top, near, far) {
+  var x = 2 / (right - left);
+  var y = 2 / (top - bottom);
+  var z = -2 / (far - near);
+  var a = -(right + left) / (right - left);
+  var b = -(top + bottom) / (top - bottom);
+  var c = -(far + near) / (far - near);
+
+  goog.vec.Matrix4.setFromValues(mat,
+      x, 0, 0, 0,
+      0, y, 0, 0,
+      0, 0, z, 0,
+      a, b, c, 1
+  );
+};
+
+
+/**
+ * Updates a matrix representing the modelview matrix of a camera so that
+ * the camera is 'looking at' the given center point.
+ * @param {goog.vec.ArrayType} viewMatrix The matrix.
+ * @param {goog.vec.ArrayType} eyePt The position of the eye point
+ *     (camera origin).
+ * @param {goog.vec.ArrayType} centerPt The point to aim the camera
+ *     at.
+ * @param {goog.vec.ArrayType} worldUpVec The vector that
+ *     identifies the up direction for the camera.
+ */
+goog.vec.Matrix4.lookAt = function(
+    viewMatrix, eyePt, centerPt, worldUpVec) {
+  // Compute the direction vector from the eye point to the center point and
+  // normalize.
+  var fwdVec = goog.vec.Matrix4.tmpVec4_[0];
+  goog.vec.Vec3.subtract(centerPt, eyePt, fwdVec);
+  goog.vec.Vec3.normalize(fwdVec, fwdVec);
+  fwdVec[3] = 0;
+
+  // Compute the side vector from the forward vector and the input up vector.
+  var sideVec = goog.vec.Matrix4.tmpVec4_[1];
+  goog.vec.Vec3.cross(fwdVec, worldUpVec, sideVec);
+  goog.vec.Vec3.normalize(sideVec, sideVec);
+  sideVec[3] = 0;
+
+  // Now the up vector to form the orthonormal basis.
+  var upVec = goog.vec.Matrix4.tmpVec4_[2];
+  goog.vec.Vec3.cross(sideVec, fwdVec, upVec);
+  goog.vec.Vec3.normalize(upVec, upVec);
+  upVec[3] = 0;
+
+  // Update the view matrix with the new orthonormal basis and position the
+  // camera at the given eye point.
+  goog.vec.Vec3.negate(fwdVec, fwdVec);
+  goog.vec.Matrix4.setRow(viewMatrix, 0, sideVec);
+  goog.vec.Matrix4.setRow(viewMatrix, 1, upVec);
+  goog.vec.Matrix4.setRow(viewMatrix, 2, fwdVec);
+  goog.vec.Matrix4.setRowValues(viewMatrix, 3, 0, 0, 0, 1);
+  goog.vec.Matrix4.applyTranslate(
+      viewMatrix, -eyePt[0], -eyePt[1], -eyePt[2]);
+};
+
+
+/**
+ * Decomposes a matrix into the lookAt vectors eyePt, fwdVec and worldUpVec.
+ * The matrix represents the modelview matrix of a camera. It is the inverse
+ * of lookAt except for the output of the fwdVec instead of centerPt.
+ * The centerPt itself cannot be recovered from a modelview matrix.
+ * @param {goog.vec.ArrayType} viewMatrix The matrix.
+ * @param {goog.vec.ArrayType} eyePt The position of the eye point
+ *     (camera origin).
+ * @param {goog.vec.ArrayType} fwdVec The vector describing where
+ *     the camera points to.
+ * @param {goog.vec.ArrayType} worldUpVec The vector that
+ *     identifies the up direction for the camera.
+ * @return {boolean} True if the method succeeds, false otherwise.
+ *     The method can only fail if the inverse of viewMatrix is not defined.
+ */
+goog.vec.Matrix4.toLookAt = function(
+    viewMatrix, eyePt, fwdVec, worldUpVec) {
+  // Get eye of the camera.
+  var viewMatrixInverse = goog.vec.Matrix4.tmpMatrix4_[0];
+  if (!goog.vec.Matrix4.invert(viewMatrix, viewMatrixInverse)) {
+    // The input matrix does not have a valid inverse.
+    return false;
+  }
+
+  if (eyePt) {
+    eyePt[0] = viewMatrixInverse[12];
+    eyePt[1] = viewMatrixInverse[13];
+    eyePt[2] = viewMatrixInverse[14];
+  }
+
+  // Get forward vector from the definition of lookAt.
+  if (fwdVec || worldUpVec) {
+    if (!fwdVec) {
+      fwdVec = goog.vec.Matrix4.tmpVec3_[0];
+    }
+    fwdVec[0] = -viewMatrix[2];
+    fwdVec[1] = -viewMatrix[6];
+    fwdVec[2] = -viewMatrix[10];
+    // Normalize forward vector.
+    goog.vec.Vec3.normalize(fwdVec, fwdVec);
+  }
+
+  if (worldUpVec) {
+    // Get side vector from the definition of gluLookAt.
+    var side = goog.vec.Matrix4.tmpVec3_[1];
+    side[0] = viewMatrix[0];
+    side[1] = viewMatrix[4];
+    side[2] = viewMatrix[8];
+    // Compute up vector as a up = side x forward.
+    goog.vec.Vec3.cross(side, fwdVec, worldUpVec);
+    // Normalize up vector.
+    goog.vec.Vec3.normalize(worldUpVec, worldUpVec);
+  }
+  return true;
+};
+
+
+/**
+ * Constructs a rotation matrix from its Euler angles using the ZXZ convention.
+ * Given the euler angles [theta1, theta2, theta3], the rotation is defined as
+ * rotation = rotation_z(theta1) * rotation_x(theta2) * rotation_z(theta3),
+ * where rotation_x(theta) means rotation around the X axis of theta radians.
+ * @param {goog.vec.ArrayType} matrix The rotation matrix.
+ * @param {number} theta1 The angle of rotation around the Z axis in radians.
+ * @param {number} theta2 The angle of rotation around the X axis in radians.
+ * @param {number} theta3 The angle of rotation around the Z axis in radians.
+ */
+goog.vec.Matrix4.fromEulerZXZ = function(
+    matrix, theta1, theta2, theta3) {
+  var c1 = Math.cos(theta1);
+  var s1 = Math.sin(theta1);
+
+  var c2 = Math.cos(theta2);
+  var s2 = Math.sin(theta2);
+
+  var c3 = Math.cos(theta3);
+  var s3 = Math.sin(theta3);
+
+  matrix[0] = c1 * c3 - c2 * s1 * s3;
+  matrix[1] = c2 * c1 * s3 + c3 * s1;
+  matrix[2] = s3 * s2;
+  matrix[3] = 0;
+
+  matrix[4] = -c1 * s3 - c3 * c2 * s1;
+  matrix[5] = c1 * c2 * c3 - s1 * s3;
+  matrix[6] = c3 * s2;
+  matrix[7] = 0;
+
+  matrix[8] = s2 * s1;
+  matrix[9] = -c1 * s2;
+  matrix[10] = c2;
+  matrix[11] = 0;
+
+  matrix[12] = 0;
+  matrix[13] = 0;
+  matrix[14] = 0;
+  matrix[15] = 1;
+};
+
+
+/**
+ * Decomposes a rotation matrix into Euler angles using the ZXZ convention.
+ * @param {goog.vec.ArrayType} matrix The rotation matrix.
+ * @param {goog.vec.ArrayType} euler The ZXZ Euler angles in
+ *     radians. euler = [roll, tilt, pan].
+ */
+goog.vec.Matrix4.toEulerZXZ = function(matrix, euler) {
+  var s2 = Math.sqrt(matrix[2] * matrix[2] + matrix[6] * matrix[6]);
+
+  // There is an ambiguity in the sign of s2. We assume the tilt value
+  // is between [-pi/2, 0], so s2 is always negative.
+  if (s2 > goog.vec.EPSILON) {
+    euler[2] = Math.atan2(-matrix[2], -matrix[6]);
+    euler[1] = Math.atan2(-s2, matrix[10]);
+    euler[0] = Math.atan2(-matrix[8], matrix[9]);
+  } else {
+    // There is also an arbitrary choice for roll = 0 or pan = 0 in this case.
+    // We assume roll = 0 as some applications do not allow the camera to roll.
+    euler[0] = 0;
+    euler[1] = Math.atan2(-s2, matrix[10]);
+    euler[2] = Math.atan2(matrix[1], matrix[0]);
+  }
+};
+
+
+/**
+ * Applies a translation by x,y,z to the given matrix.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix.
+ * @param {number} x The translation along the x axis.
+ * @param {number} y The translation along the y axis.
+ * @param {number} z The translation along the z axis.
+ */
+goog.vec.Matrix4.applyTranslate = function(mat, x, y, z) {
+  goog.vec.Matrix4.setColumnValues(
+      mat, 3,
+      mat[0] * x + mat[4] * y + mat[8] * z + mat[12],
+      mat[1] * x + mat[5] * y + mat[9] * z + mat[13],
+      mat[2] * x + mat[6] * y + mat[10] * z + mat[14],
+      mat[3] * x + mat[7] * y + mat[11] * z + mat[15]);
+};
+
+
+/**
+ * Applies an x,y,z scale to the given matrix.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix.
+ * @param {number} x The x scale factor.
+ * @param {number} y The y scale factor.
+ * @param {number} z The z scale factor.
+ */
+goog.vec.Matrix4.applyScale = function(mat, x, y, z) {
+  goog.vec.Matrix4.setFromValues(
+      mat,
+      mat[0] * x, mat[1] * x, mat[2] * x, mat[3] * x,
+      mat[4] * y, mat[5] * y, mat[6] * y, mat[7] * y,
+      mat[8] * z, mat[9] * z, mat[10] * z, mat[11] * z,
+      mat[12], mat[13], mat[14], mat[15]);
+};
+
+
+/**
+ * Applies a rotation by angle about the x,y,z axis to the given matrix.
+ *
+ * @param {goog.vec.ArrayType} mat The matrix.
+ * @param {number} angle The angle in radians.
+ * @param {number} x The x component of the rotation axis.
+ * @param {number} y The y component of the rotation axis.
+ * @param {number} z The z component of the rotation axis.
+ */
+goog.vec.Matrix4.applyRotate = function(mat, angle, x, y, z) {
+  var m00 = mat[0], m10 = mat[1], m20 = mat[2], m30 = mat[3];
+  var m01 = mat[4], m11 = mat[5], m21 = mat[6], m31 = mat[7];
+  var m02 = mat[8], m12 = mat[9], m22 = mat[10], m32 = mat[11];
+  var m03 = mat[12], m13 = mat[13], m23 = mat[14], m33 = mat[15];
+
+  var cosAngle = Math.cos(angle);
+  var sinAngle = Math.sin(angle);
+  var diffCosAngle = 1 - cosAngle;
+  var r00 = x * x * diffCosAngle + cosAngle;
+  var r10 = x * y * diffCosAngle + z * sinAngle;
+  var r20 = x * z * diffCosAngle - y * sinAngle;
+
+  var r01 = x * y * diffCosAngle - z * sinAngle;
+  var r11 = y * y * diffCosAngle + cosAngle;
+  var r21 = y * z * diffCosAngle + x * sinAngle;
+
+  var r02 = x * z * diffCosAngle + y * sinAngle;
+  var r12 = y * z * diffCosAngle - x * sinAngle;
+  var r22 = z * z * diffCosAngle + cosAngle;
+
+  goog.vec.Matrix4.setFromValues(
+      mat,
+      m00 * r00 + m01 * r10 + m02 * r20,
+      m10 * r00 + m11 * r10 + m12 * r20,
+      m20 * r00 + m21 * r10 + m22 * r20,
+      m30 * r00 + m31 * r10 + m32 * r20,
+
+      m00 * r01 + m01 * r11 + m02 * r21,
+      m10 * r01 + m11 * r11 + m12 * r21,
+      m20 * r01 + m21 * r11 + m22 * r21,
+      m30 * r01 + m31 * r11 + m32 * r21,
+
+      m00 * r02 + m01 * r12 + m02 * r22,
+      m10 * r02 + m11 * r12 + m12 * r22,
+      m20 * r02 + m21 * r12 + m22 * r22,
+      m30 * r02 + m31 * r12 + m32 * r22,
+
+      m03, m13, m23, m33);
+};
+
+
+/**
+ * @type {Array.<goog.vec.Vec3.Type>}
+ * @private
+ */
+goog.vec.Matrix4.tmpVec3_ = [
+  goog.vec.Vec3.createNumber(),
+  goog.vec.Vec3.createNumber()
+];
+
+
+/**
+ * @type {Array.<goog.vec.Vec4.Type>}
+ * @private
+ */
+goog.vec.Matrix4.tmpVec4_ = [
+  goog.vec.Vec4.createNumber(),
+  goog.vec.Vec4.createNumber(),
+  goog.vec.Vec4.createNumber()
+];
+
+
+/**
+ * @type {Array.<goog.vec.Matrix4.Type>}
+ * @private
+ */
+goog.vec.Matrix4.tmpMatrix4_ = [
+  goog.vec.Matrix4.create()
+];

+ 513 - 0
public/lib/closure/vec/quaternion.js

@@ -0,0 +1,513 @@
+// Copyright 2011 The Closure Library Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+/**
+ * @fileoverview Implements quaternions and their conversion functions. In this
+ * implementation, quaternions are represented as 4 element vectors with the
+ * first 3 elements holding the imaginary components and the 4th element holding
+ * the real component.
+ *
+ */
+goog.provide('goog.vec.Quaternion');
+
+goog.require('goog.vec');
+goog.require('goog.vec.Vec3');
+goog.require('goog.vec.Vec4');
+
+
+/** @typedef {goog.vec.Float32} */ goog.vec.Quaternion.Float32;
+/** @typedef {goog.vec.Float64} */ goog.vec.Quaternion.Float64;
+/** @typedef {goog.vec.Number} */ goog.vec.Quaternion.Number;
+/** @typedef {goog.vec.AnyType} */ goog.vec.Quaternion.AnyType;
+
+//The following type are deprecated - use the above types instead.
+/** @typedef {goog.vec.Vec4.Type} */ goog.vec.Quaternion.Type;
+/** @typedef {goog.vec.ArrayType} */ goog.vec.Quaternion.QuaternionLike;
+
+
+/**
+ * @typedef {goog.vec.Vec4.Type}
+ */
+goog.vec.Quaternion.Type;
+
+
+/**
+ * Creates a Float32 quaternion, initialized to zero.
+ *
+ * @return {!goog.vec.Quaternion.Float32} The new quaternion.
+ */
+goog.vec.Quaternion.createFloat32 = goog.vec.Vec4.createFloat32;
+
+
+/**
+ * Creates a Float64 quaternion, initialized to zero.
+ *
+ * @return {goog.vec.Quaternion.Float64} The new quaternion.
+ */
+goog.vec.Quaternion.createFloat64 = goog.vec.Vec4.createFloat64;
+
+
+/**
+ * Creates a Number quaternion, initialized to zero.
+ *
+ * @return {goog.vec.Quaternion.Number} The new quaternion.
+ */
+goog.vec.Quaternion.createNumber = goog.vec.Vec4.createNumber;
+
+
+/**
+ * Creates a quaternion, initialized to zero.
+ *
+ * @deprecated Use createFloat32.
+ * @return {!goog.vec.Quaternion.Type} The new quaternion.
+ */
+goog.vec.Quaternion.create = goog.vec.Vec4.create;
+
+
+/**
+ * Creates a new Float32 quaternion initialized with the values from the
+ * supplied array.
+ *
+ * @param {goog.vec.AnyType} vec The source 4 element array.
+ * @return {!goog.vec.Quaternion.Float32} The new quaternion.
+ */
+goog.vec.Quaternion.createFloat32FromArray =
+    goog.vec.Vec4.createFloat32FromArray;
+
+
+/**
+ * Creates a new Float64 quaternion initialized with the values from the
+ * supplied array.
+ *
+ * @param {goog.vec.AnyType} vec The source 4 element array.
+ * @return {!goog.vec.Quaternion.Float64} The new quaternion.
+ */
+goog.vec.Quaternion.createFloat64FromArray =
+    goog.vec.Vec4.createFloat64FromArray;
+
+
+/**
+ * Creates a new quaternion initialized with the values from the supplied
+ * array.
+ *
+ * @deprecated Use createFloat32FromArray.
+ * @param {!goog.vec.Quaternion.QuaternionLike} vec The source 4 element array.
+ * @return {!goog.vec.Quaternion.Type} The new quaternion.
+ */
+goog.vec.Quaternion.createFromArray =
+    goog.vec.Vec4.createFromArray;
+
+
+/**
+ * Creates a new Float32 quaternion initialized with the supplied values.
+ *
+ * @param {number} v0 The value for element at index 0.
+ * @param {number} v1 The value for element at index 1.
+ * @param {number} v2 The value for element at index 2.
+ * @param {number} v3 The value for element at index 3.
+ * @return {!goog.vec.Quaternion.Float32} The new quaternion.
+ */
+goog.vec.Quaternion.createFloat32FromValues =
+    goog.vec.Vec4.createFloat32FromValues;
+
+
+/**
+ * Creates a new Float64 quaternion initialized with the supplied values.
+ *
+ * @param {number} v0 The value for element at index 0.
+ * @param {number} v1 The value for element at index 1.
+ * @param {number} v2 The value for element at index 2.
+ * @param {number} v3 The value for element at index 3.
+ * @return {!goog.vec.Quaternion.Float64} The new quaternion.
+ */
+goog.vec.Quaternion.createFloat64FromValues =
+    goog.vec.Vec4.createFloat64FromValues;
+
+
+/**
+ * Creates a new quaternion initialized with the supplied values.
+ *
+ * @deprecated Use createFloat32FromValues.
+ * @param {number} v0 The value for element at index 0.
+ * @param {number} v1 The value for element at index 1.
+ * @param {number} v2 The value for element at index 2.
+ * @param {number} v3 The value for element at index 3.
+ * @return {!goog.vec.Quaternion.Type} The new quaternion.
+ */
+goog.vec.Quaternion.createFromValues =
+    goog.vec.Vec4.createFromValues;
+
+
+/**
+ * Creates a clone of the given Float32 quaternion.
+ *
+ * @param {goog.vec.Quaternion.Float32} q The source quaternion.
+ * @return {goog.vec.Quaternion.Float32} The new quaternion.
+ */
+goog.vec.Quaternion.cloneFloat32 = goog.vec.Vec4.cloneFloat32;
+
+
+/**
+ * Creates a clone of the given Float64 quaternion.
+ *
+ * @param {goog.vec.Quaternion.Float64} q The source quaternion.
+ * @return {goog.vec.Quaternion.Float64} The new quaternion.
+ */
+goog.vec.Quaternion.cloneFloat64 = goog.vec.Vec4.cloneFloat64;
+
+
+/**
+ * Creates a clone of the given quaternion.
+ *
+ * @deprecated Use cloneFloat32.
+ * @param {goog.vec.Quaternion.QuaternionLike} q The source quaternion.
+ * @return {!goog.vec.Quaternion.Type} The new quaternion.
+ */
+goog.vec.Quaternion.clone = goog.vec.Vec4.clone;
+
+
+/**
+ * Initializes the quaternion with the given values.
+ *
+ * @param {goog.vec.Quaternion.AnyType} q The quaternion to receive
+ *     the values.
+ * @param {number} v0 The value for element at index 0.
+ * @param {number} v1 The value for element at index 1.
+ * @param {number} v2 The value for element at index 2.
+ * @param {number} v3 The value for element at index 3.
+ * @return {!goog.vec.Vec4.AnyType} return q so that operations can be
+ *     chained together.
+ */
+goog.vec.Quaternion.setFromValues = goog.vec.Vec4.setFromValues;
+
+
+/**
+ * Initializes the quaternion with the given array of values.
+ *
+ * @param {goog.vec.Quaternion.AnyType} q The quaternion to receive
+ *     the values.
+ * @param {goog.vec.AnyType} values The array of values.
+ * @return {!goog.vec.Quaternion.AnyType} return q so that operations can be
+ *     chained together.
+ */
+goog.vec.Quaternion.setFromArray = goog.vec.Vec4.setFromArray;
+
+
+/**
+ * Adds the two quaternions.
+ *
+ * @param {goog.vec.Quaternion.AnyType} quat0 The first addend.
+ * @param {goog.vec.Quaternion.AnyType} quat1 The second addend.
+ * @param {goog.vec.Quaternion.AnyType} resultQuat The quaternion to
+ *     receive the result. May be quat0 or quat1.
+ */
+goog.vec.Quaternion.add = goog.vec.Vec4.add;
+
+
+/**
+ * Negates a quaternion, storing the result into resultQuat.
+ *
+ * @param {goog.vec.Quaternion.AnyType} quat0 The quaternion to negate.
+ * @param {goog.vec.Quaternion.AnyType} resultQuat The quaternion to
+ *     receive the result. May be quat0.
+ */
+goog.vec.Quaternion.negate = goog.vec.Vec4.negate;
+
+
+/**
+ * Multiplies each component of quat0 with scalar storing the product into
+ * resultVec.
+ *
+ * @param {goog.vec.Quaternion.AnyType} quat0 The source quaternion.
+ * @param {number} scalar The value to multiply with each component of quat0.
+ * @param {goog.vec.Quaternion.AnyType} resultQuat The quaternion to
+ *     receive the result. May be quat0.
+ */
+goog.vec.Quaternion.scale = goog.vec.Vec4.scale;
+
+
+/**
+ * Returns the square magnitude of the given quaternion.
+ *
+ * @param {goog.vec.Quaternion.AnyType} quat0 The quaternion.
+ * @return {number} The magnitude of the quaternion.
+ */
+goog.vec.Quaternion.magnitudeSquared =
+    goog.vec.Vec4.magnitudeSquared;
+
+
+/**
+ * Returns the magnitude of the given quaternion.
+ *
+ * @param {goog.vec.Quaternion.AnyType} quat0 The quaternion.
+ * @return {number} The magnitude of the quaternion.
+ */
+goog.vec.Quaternion.magnitude =
+    goog.vec.Vec4.magnitude;
+
+
+/**
+ * Normalizes the given quaternion storing the result into resultVec.
+ *
+ * @param {goog.vec.Quaternion.AnyType} quat0 The quaternion to
+ *     normalize.
+ * @param {goog.vec.Quaternion.AnyType} resultQuat The quaternion to
+ *     receive the result. May be quat0.
+ */
+goog.vec.Quaternion.normalize = goog.vec.Vec4.normalize;
+
+
+/**
+ * Computes the dot (scalar) product of two quaternions.
+ *
+ * @param {goog.vec.Quaternion.AnyType} q0 The first quaternion.
+ * @param {goog.vec.Quaternion.AnyType} q1 The second quaternion.
+ * @return {number} The scalar product.
+ */
+goog.vec.Quaternion.dot = goog.vec.Vec4.dot;
+
+
+/**
+ * Computes the conjugate of the quaternion in quat storing the result into
+ * resultQuat.
+ *
+ * @param {goog.vec.Quaternion.AnyType} quat The source quaternion.
+ * @param {goog.vec.Quaternion.AnyType} resultQuat The quaternion to
+ *     receive the result.
+ * @return {!goog.vec.Quaternion.AnyType} Return q so that
+ *     operations can be chained together.
+ */
+goog.vec.Quaternion.conjugate = function(quat, resultQuat) {
+  resultQuat[0] = -quat[0];
+  resultQuat[1] = -quat[1];
+  resultQuat[2] = -quat[2];
+  resultQuat[3] = quat[3];
+  return resultQuat;
+};
+
+
+/**
+ * Concatenates the two quaternions storing the result into resultQuat.
+ *
+ * @param {goog.vec.Quaternion.AnyType} quat0 The first quaternion.
+ * @param {goog.vec.Quaternion.AnyType} quat1 The second quaternion.
+ * @param {goog.vec.Quaternion.AnyType} resultQuat The quaternion to
+ *     receive the result.
+ * @return {!goog.vec.Quaternion.AnyType} Return q so that
+ *     operations can be chained together.
+ */
+goog.vec.Quaternion.concat = function(quat0, quat1, resultQuat) {
+  var x0 = quat0[0], y0 = quat0[1], z0 = quat0[2], w0 = quat0[3];
+  var x1 = quat1[0], y1 = quat1[1], z1 = quat1[2], w1 = quat1[3];
+  resultQuat[0] = w0 * x1 + x0 * w1 + y0 * z1 - z0 * y1;
+  resultQuat[1] = w0 * y1 - x0 * z1 + y0 * w1 + z0 * x1;
+  resultQuat[2] = w0 * z1 + x0 * y1 - y0 * x1 + z0 * w1;
+  resultQuat[3] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1;
+  return resultQuat;
+};
+
+
+/**
+ * Generates a unit quaternion from the given angle-axis rotation pair.
+ * The rotation axis is not required to be a unit vector, but should
+ * have non-zero length.  The angle should be specified in radians.
+ *
+ * @param {number} angle The angle (in radians) to rotate about the axis.
+ * @param {goog.vec.Quaternion.AnyType} axis Unit vector specifying the
+ *     axis of rotation.
+ * @param {goog.vec.Quaternion.AnyType} quat Unit quaternion to store the
+ *     result.
+ * @return {goog.vec.Quaternion.AnyType} Return q so that
+ *     operations can be chained together.
+ */
+goog.vec.Quaternion.fromAngleAxis = function(angle, axis, quat) {
+  // Normalize the axis of rotation.
+  goog.vec.Vec3.normalize(axis, axis);
+
+  var halfAngle = 0.5 * angle;
+  var sin = Math.sin(halfAngle);
+  goog.vec.Quaternion.setFromValues(
+      quat, sin * axis[0], sin * axis[1], sin * axis[2], Math.cos(halfAngle));
+
+  // Normalize the resulting quaternion.
+  goog.vec.Quaternion.normalize(quat, quat);
+  return quat;
+};
+
+
+/**
+ * Generates an angle-axis rotation pair from a unit quaternion.
+ * The quaternion is assumed to be of unit length.  The calculated
+ * values are returned via the passed 'axis' object and the 'angle'
+ * number returned by the function itself. The returned rotation axis
+ * is a non-zero length unit vector, and the returned angle is in
+ * radians in the range of [-PI, +PI].
+ *
+ * @param {goog.vec.Quaternion.AnyType} quat Unit quaternion to convert.
+ * @param {goog.vec.Quaternion.AnyType} axis Vector to store the returned
+ *     rotation axis.
+ * @return {number} angle Angle (in radians) to rotate about 'axis'.
+ *     The range of the returned angle is [-PI, +PI].
+ */
+goog.vec.Quaternion.toAngleAxis = function(quat, axis) {
+  var angle = 2 * Math.acos(quat[3]);
+  var magnitude = Math.min(Math.max(1 - quat[3] * quat[3], 0), 1);
+  if (magnitude < goog.vec.EPSILON) {
+    // This is nearly an identity rotation, so just use a fixed +X axis.
+    goog.vec.Vec3.setFromValues(axis, 1, 0, 0);
+  } else {
+    // Compute the proper rotation axis.
+    goog.vec.Vec3.setFromValues(axis, quat[0], quat[1], quat[2]);
+    // Make sure the rotation axis is of unit length.
+    goog.vec.Vec3.normalize(axis, axis);
+  }
+  // Adjust the range of the returned angle to [-PI, +PI].
+  if (angle > Math.PI) {
+    angle -= 2 * Math.PI;
+  }
+  return angle;
+};
+
+
+/**
+ * Generates the quaternion from the given rotation matrix.
+ *
+ * @param {goog.vec.Quaternion.AnyType} matrix The source matrix.
+ * @param {goog.vec.Quaternion.AnyType} quat The resulting quaternion.
+ * @return {!goog.vec.Quaternion.AnyType} Return q so that
+ *     operations can be chained together.
+ */
+goog.vec.Quaternion.fromRotationMatrix4 = function(matrix, quat) {
+  var sx = matrix[0], sy = matrix[5], sz = matrix[10];
+  quat[3] = Math.sqrt(Math.max(0, 1 + sx + sy + sz)) / 2;
+  quat[0] = Math.sqrt(Math.max(0, 1 + sx - sy - sz)) / 2;
+  quat[1] = Math.sqrt(Math.max(0, 1 - sx + sy - sz)) / 2;
+  quat[2] = Math.sqrt(Math.max(0, 1 - sx - sy + sz)) / 2;
+
+  quat[0] = (matrix[6] - matrix[9] < 0) != (quat[0] < 0) ? -quat[0] : quat[0];
+  quat[1] = (matrix[8] - matrix[2] < 0) != (quat[1] < 0) ? -quat[1] : quat[1];
+  quat[2] = (matrix[1] - matrix[4] < 0) != (quat[2] < 0) ? -quat[2] : quat[2];
+  return quat;
+};
+
+
+/**
+ * Generates the rotation matrix from the given quaternion.
+ *
+ * @param {goog.vec.Quaternion.AnyType} quat The source quaternion.
+ * @param {goog.vec.AnyType} matrix The resulting matrix.
+ * @return {!goog.vec.AnyType} Return resulting matrix so that
+ *     operations can be chained together.
+ */
+goog.vec.Quaternion.toRotationMatrix4 = function(quat, matrix) {
+  var x = quat[0], y = quat[1], z = quat[2], w = quat[3];
+  var x2 = 2 * x, y2 = 2 * y, z2 = 2 * z;
+  var wx = x2 * w;
+  var wy = y2 * w;
+  var wz = z2 * w;
+  var xx = x2 * x;
+  var xy = y2 * x;
+  var xz = z2 * x;
+  var yy = y2 * y;
+  var yz = z2 * y;
+  var zz = z2 * z;
+
+  matrix[0] = 1 - (yy + zz);
+  matrix[1] = xy + wz;
+  matrix[2] = xz - wy;
+  matrix[3] = 0;
+  matrix[4] = xy - wz;
+  matrix[5] = 1 - (xx + zz);
+  matrix[6] = yz + wx;
+  matrix[7] = 0;
+  matrix[8] = xz + wy;
+  matrix[9] = yz - wx;
+  matrix[10] = 1 - (xx + yy);
+  matrix[11] = 0;
+  matrix[12] = 0;
+  matrix[13] = 0;
+  matrix[14] = 0;
+  matrix[15] = 1;
+  return matrix;
+};
+
+
+/**
+ * Computes the spherical linear interpolated value from the given quaternions
+ * q0 and q1 according to the coefficient t. The resulting quaternion is stored
+ * in resultQuat.
+ *
+ * @param {goog.vec.Quaternion.AnyType} q0 The first quaternion.
+ * @param {goog.vec.Quaternion.AnyType} q1 The second quaternion.
+ * @param {number} t The interpolating coefficient.
+ * @param {goog.vec.Quaternion.AnyType} resultQuat The quaternion to
+ *     receive the result.
+ * @return {goog.vec.Quaternion.AnyType} Return q so that
+ *     operations can be chained together.
+ */
+goog.vec.Quaternion.slerp = function(q0, q1, t, resultQuat) {
+  // Compute the dot product between q0 and q1 (cos of the angle between q0 and
+  // q1). If it's outside the interval [-1,1], then the arccos is not defined.
+  // The usual reason for this is that q0 and q1 are colinear. In this case
+  // the angle between the two is zero, so just return q1.
+  var cosVal = goog.vec.Quaternion.dot(q0, q1);
+  if (cosVal > 1 || cosVal < -1) {
+    goog.vec.Vec4.setFromArray(resultQuat, q1);
+    return resultQuat;
+  }
+
+  // Quaternions are a double cover on the space of rotations. That is, q and -q
+  // represent the same rotation. Thus we have two possibilities when
+  // interpolating between q0 and q1: going the short way or the long way. We
+  // prefer the short way since that is the likely expectation from users.
+  var factor = 1;
+  if (cosVal < 0) {
+    factor = -1;
+    cosVal = -cosVal;
+  }
+
+  // Compute the angle between q0 and q1. If it's very small, then just return
+  // q1 to avoid a very large denominator below.
+  var angle = Math.acos(cosVal);
+  if (angle <= goog.vec.EPSILON) {
+    goog.vec.Vec4.setFromArray(resultQuat, q1);
+    return resultQuat;
+  }
+
+  // Compute the coefficients and interpolate.
+  var invSinVal = 1 / Math.sin(angle);
+  var c0 = Math.sin((1 - t) * angle) * invSinVal;
+  var c1 = factor * Math.sin(t * angle) * invSinVal;
+
+  resultQuat[0] = q0[0] * c0 + q1[0] * c1;
+  resultQuat[1] = q0[1] * c0 + q1[1] * c1;
+  resultQuat[2] = q0[2] * c0 + q1[2] * c1;
+  resultQuat[3] = q0[3] * c0 + q1[3] * c1;
+  return resultQuat;
+};
+
+
+/**
+ * Compute the simple linear interpolation of the two quaternions q0 and q1
+ * according to the coefficient t. The resulting quaternion is stored in
+ * resultVec.
+ *
+ * @param {goog.vec.Quaternion.AnyType} q0 The first quaternion.
+ * @param {goog.vec.Quaternion.AnyType} q1 The second quaternion.
+ * @param {number} t The interpolation factor.
+ * @param {goog.vec.Quaternion.AnyType} resultQuat The quaternion to
+ *     receive the results (may be q0 or q1).
+ */
+goog.vec.Quaternion.nlerp = goog.vec.Vec4.lerp;

+ 94 - 0
public/lib/closure/vec/ray.js

@@ -0,0 +1,94 @@
+// Copyright 2011 The Closure Library Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * @fileoverview Implements a 3D ray that are compatible with WebGL.
+ * Each element is a float64 in case high precision is required.
+ * The API is structured to avoid unnecessary memory allocations.
+ * The last parameter will typically be the output vector and an
+ * object can be both an input and output parameter to all methods
+ * except where noted.
+ *
+ */
+goog.provide('goog.vec.Ray');
+
+goog.require('goog.vec.Vec3');
+
+
+
+/**
+ * Constructs a new ray with an optional origin and direction. If not specified,
+ * the default is [0, 0, 0].
+ * @param {goog.vec.Vec3.AnyType=} opt_origin The optional origin.
+ * @param {goog.vec.Vec3.AnyType=} opt_dir The optional direction.
+ * @constructor
+ */
+goog.vec.Ray = function(opt_origin, opt_dir) {
+  /**
+   * @type {goog.vec.Vec3.Number}
+   */
+  this.origin = goog.vec.Vec3.createNumber();
+  if (opt_origin) {
+    goog.vec.Vec3.setFromArray(this.origin, opt_origin);
+  }
+
+  /**
+   * @type {goog.vec.Vec3.Number}
+   */
+  this.dir = goog.vec.Vec3.createNumber();
+  if (opt_dir) {
+    goog.vec.Vec3.setFromArray(this.dir, opt_dir);
+  }
+};
+
+
+/**
+ * Sets the origin and direction of the ray.
+ * @param {goog.vec.AnyType} origin The new origin.
+ * @param {goog.vec.AnyType} dir The new direction.
+ */
+goog.vec.Ray.prototype.set = function(origin, dir) {
+  goog.vec.Vec3.setFromArray(this.origin, origin);
+  goog.vec.Vec3.setFromArray(this.dir, dir);
+};
+
+
+/**
+ * Sets the origin of the ray.
+ * @param {goog.vec.AnyType} origin the new origin.
+ */
+goog.vec.Ray.prototype.setOrigin = function(origin) {
+  goog.vec.Vec3.setFromArray(this.origin, origin);
+};
+
+
+/**
+ * Sets the direction of the ray.
+ * @param {goog.vec.AnyType} dir The new direction.
+ */
+goog.vec.Ray.prototype.setDir = function(dir) {
+  goog.vec.Vec3.setFromArray(this.dir, dir);
+};
+
+
+/**
+ * Returns true if this ray is equal to the other ray.
+ * @param {goog.vec.Ray} other The other ray.
+ * @return {boolean} True if this ray is equal to the other ray.
+ */
+goog.vec.Ray.prototype.equals = function(other) {
+  return other != null &&
+      goog.vec.Vec3.equals(this.origin, other.origin) &&
+      goog.vec.Vec3.equals(this.dir, other.dir);
+};

+ 65 - 0
public/lib/closure/vec/vec.js

@@ -0,0 +1,65 @@
+// Copyright 2011 The Closure Library Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+/**
+ * @fileoverview Supplies global data types and constants for the vector math
+ *     library.
+ */
+goog.provide('goog.vec');
+
+/**
+ * On platforms that don't have native Float32Array or Float64Array support we
+ * use a javascript implementation so that this math library can be used on all
+ * platforms.
+ */
+goog.require('goog.vec.Float32Array');
+goog.require('goog.vec.Float64Array');
+
+// All vector and matrix operations are based upon arrays of numbers using
+// either Float32Array, Float64Array, or a standard Javascript Array of
+// Numbers.
+
+
+/** @typedef {Float32Array} */
+goog.vec.Float32;
+
+
+/** @typedef {Float64Array} */
+goog.vec.Float64;
+
+
+/** @typedef {Array.<number>} */
+goog.vec.Number;
+
+
+/** @typedef {goog.vec.Float32|goog.vec.Float64|goog.vec.Number} */
+goog.vec.AnyType;
+
+
+/**
+ * @deprecated Use AnyType.
+ * @typedef {Float32Array|Array.<number>}
+ */
+goog.vec.ArrayType;
+
+
+/**
+ * For graphics work, 6 decimal places of accuracy are typically all that is
+ * required.
+ *
+ * @type {number}
+ * @const
+ */
+goog.vec.EPSILON = 1e-6;

+ 375 - 0
public/lib/closure/vec/vec2.js

@@ -0,0 +1,375 @@
+// Copyright 2012 The Closure Library Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * @fileoverview Definition of 2 element vectors.  This follows the same design
+ * patterns as Vec3 and Vec4.
+ *
+ */
+
+goog.provide('goog.vec.Vec2');
+
+goog.require('goog.vec');
+
+
+/** @typedef {goog.vec.Float32} */ goog.vec.Vec2.Float32;
+/** @typedef {goog.vec.Float64} */ goog.vec.Vec2.Float64;
+/** @typedef {goog.vec.Number} */ goog.vec.Vec2.Number;
+/** @typedef {goog.vec.AnyType} */ goog.vec.Vec2.AnyType;
+
+
+/**
+ * Creates a 2 element vector of Float32. The array is initialized to zero.
+ *
+ * @return {!goog.vec.Vec2.Float32} The new 2 element array.
+ */
+goog.vec.Vec2.createFloat32 = function() {
+  return new Float32Array(2);
+};
+
+
+/**
+ * Creates a 2 element vector of Float64. The array is initialized to zero.
+ *
+ * @return {!goog.vec.Vec2.Float64} The new 2 element array.
+ */
+goog.vec.Vec2.createFloat64 = function() {
+  return new Float64Array(2);
+};
+
+
+/**
+ * Creates a 2 element vector of Number. The array is initialized to zero.
+ *
+ * @return {!goog.vec.Vec2.Number} The new 2 element array.
+ */
+goog.vec.Vec2.createNumber = function() {
+  var a = new Array(2);
+  goog.vec.Vec2.setFromValues(a, 0, 0);
+  return a;
+};
+
+
+/**
+ * Creates a new 2 element FLoat32 vector initialized with the value from the
+ * given array.
+ *
+ * @param {goog.vec.Vec2.AnyType} vec The source 2 element array.
+ * @return {!goog.vec.Vec2.Float32} The new 2 element array.
+ */
+goog.vec.Vec2.createFloat32FromArray = function(vec) {
+  var newVec = goog.vec.Vec2.createFloat32();
+  goog.vec.Vec2.setFromArray(newVec, vec);
+  return newVec;
+};
+
+
+/**
+ * Creates a new 2 element Float32 vector initialized with the supplied values.
+ *
+ * @param {number} vec0 The value for element at index 0.
+ * @param {number} vec1 The value for element at index 1.
+ * @return {!goog.vec.Vec2.Float32} The new vector.
+ */
+goog.vec.Vec2.createFloat32FromValues = function(vec0, vec1) {
+  var a = goog.vec.Vec2.createFloat32();
+  goog.vec.Vec2.setFromValues(a, vec0, vec1);
+  return a;
+};
+
+
+/**
+ * Creates a clone of the given 2 element Float32 vector.
+ *
+ * @param {goog.vec.Vec2.Float32} vec The source 2 element vector.
+ * @return {!goog.vec.Vec2.Float32} The new cloned vector.
+ */
+goog.vec.Vec2.cloneFloat32 = goog.vec.Vec2.createFloat32FromArray;
+
+
+/**
+ * Creates a new 2 element Float64 vector initialized with the value from the
+ * given array.
+ *
+ * @param {goog.vec.Vec2.AnyType} vec The source 2 element array.
+ * @return {!goog.vec.Vec2.Float64} The new 2 element array.
+ */
+goog.vec.Vec2.createFloat64FromArray = function(vec) {
+  var newVec = goog.vec.Vec2.createFloat64();
+  goog.vec.Vec2.setFromArray(newVec, vec);
+  return newVec;
+};
+
+
+/**
+* Creates a new 2 element Float64 vector initialized with the supplied values.
+*
+* @param {number} vec0 The value for element at index 0.
+* @param {number} vec1 The value for element at index 1.
+* @return {!goog.vec.Vec2.Float64} The new vector.
+*/
+goog.vec.Vec2.createFloat64FromValues = function(vec0, vec1) {
+  var vec = goog.vec.Vec2.createFloat64();
+  goog.vec.Vec2.setFromValues(vec, vec0, vec1);
+  return vec;
+};
+
+
+/**
+ * Creates a clone of the given 2 element vector.
+ *
+ * @param {goog.vec.Vec2.Float64} vec The source 2 element vector.
+ * @return {!goog.vec.Vec2.Float64} The new cloned vector.
+ */
+goog.vec.Vec2.cloneFloat64 = goog.vec.Vec2.createFloat64FromArray;
+
+
+/**
+ * Initializes the vector with the given values.
+ *
+ * @param {goog.vec.Vec2.AnyType} vec The vector to receive the values.
+ * @param {number} vec0 The value for element at index 0.
+ * @param {number} vec1 The value for element at index 1.
+ * @return {!goog.vec.Vec2.AnyType} Return vec so that operations can be
+ *     chained together.
+ */
+goog.vec.Vec2.setFromValues = function(vec, vec0, vec1) {
+  vec[0] = vec0;
+  vec[1] = vec1;
+  return vec;
+};
+
+
+/**
+ * Initializes the vector with the given array of values.
+ *
+ * @param {goog.vec.Vec2.AnyType} vec The vector to receive the
+ *     values.
+ * @param {goog.vec.Vec2.AnyType} values The array of values.
+ * @return {!goog.vec.Vec2.AnyType} Return vec so that operations can be
+ *     chained together.
+ */
+goog.vec.Vec2.setFromArray = function(vec, values) {
+  vec[0] = values[0];
+  vec[1] = values[1];
+  return vec;
+};
+
+
+/**
+ * Performs a component-wise addition of vec0 and vec1 together storing the
+ * result into resultVec.
+ *
+ * @param {goog.vec.Vec2.AnyType} vec0 The first addend.
+ * @param {goog.vec.Vec2.AnyType} vec1 The second addend.
+ * @param {goog.vec.Vec2.AnyType} resultVec The vector to
+ *     receive the result. May be vec0 or vec1.
+ * @return {!goog.vec.Vec2.AnyType} Return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Vec2.add = function(vec0, vec1, resultVec) {
+  resultVec[0] = vec0[0] + vec1[0];
+  resultVec[1] = vec0[1] + vec1[1];
+  return resultVec;
+};
+
+
+/**
+ * Performs a component-wise subtraction of vec1 from vec0 storing the
+ * result into resultVec.
+ *
+ * @param {goog.vec.Vec2.AnyType} vec0 The minuend.
+ * @param {goog.vec.Vec2.AnyType} vec1 The subtrahend.
+ * @param {goog.vec.Vec2.AnyType} resultVec The vector to
+ *     receive the result. May be vec0 or vec1.
+ * @return {!goog.vec.Vec2.AnyType} Return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Vec2.subtract = function(vec0, vec1, resultVec) {
+  resultVec[0] = vec0[0] - vec1[0];
+  resultVec[1] = vec0[1] - vec1[1];
+  return resultVec;
+};
+
+
+/**
+ * Negates vec0, storing the result into resultVec.
+ *
+ * @param {goog.vec.Vec2.AnyType} vec0 The vector to negate.
+ * @param {goog.vec.Vec2.AnyType} resultVec The vector to
+ *     receive the result. May be vec0.
+ * @return {!goog.vec.Vec2.AnyType} Return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Vec2.negate = function(vec0, resultVec) {
+  resultVec[0] = -vec0[0];
+  resultVec[1] = -vec0[1];
+  return resultVec;
+};
+
+
+/**
+ * Multiplies each component of vec0 with scalar storing the product into
+ * resultVec.
+ *
+ * @param {goog.vec.Vec2.AnyType} vec0 The source vector.
+ * @param {number} scalar The value to multiply with each component of vec0.
+ * @param {goog.vec.Vec2.AnyType} resultVec The vector to
+ *     receive the result. May be vec0.
+ * @return {!goog.vec.Vec2.AnyType} Return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Vec2.scale = function(vec0, scalar, resultVec) {
+  resultVec[0] = vec0[0] * scalar;
+  resultVec[1] = vec0[1] * scalar;
+  return resultVec;
+};
+
+
+/**
+ * Returns the magnitudeSquared of the given vector.
+ *
+ * @param {goog.vec.Vec2.AnyType} vec0 The vector.
+ * @return {number} The magnitude of the vector.
+ */
+goog.vec.Vec2.magnitudeSquared = function(vec0) {
+  var x = vec0[0], y = vec0[1];
+  return x * x + y * y;
+};
+
+
+/**
+ * Returns the magnitude of the given vector.
+ *
+ * @param {goog.vec.Vec2.AnyType} vec0 The vector.
+ * @return {number} The magnitude of the vector.
+ */
+goog.vec.Vec2.magnitude = function(vec0) {
+  var x = vec0[0], y = vec0[1];
+  return Math.sqrt(x * x + y * y);
+};
+
+
+/**
+ * Normalizes the given vector storing the result into resultVec.
+ *
+ * @param {goog.vec.Vec2.AnyType} vec0 The vector to normalize.
+ * @param {goog.vec.Vec2.AnyType} resultVec The vector to
+ *     receive the result. May be vec0.
+ * @return {!goog.vec.Vec2.AnyType} Return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Vec2.normalize = function(vec0, resultVec) {
+  var ilen = 1 / goog.vec.Vec2.magnitude(vec0);
+  resultVec[0] = vec0[0] * ilen;
+  resultVec[1] = vec0[1] * ilen;
+  return resultVec;
+};
+
+
+/**
+ * Returns the scalar product of vectors vec0 and vec1.
+ *
+ * @param {goog.vec.Vec2.AnyType} vec0 The first vector.
+ * @param {goog.vec.Vec2.AnyType} vec1 The second vector.
+ * @return {number} The scalar product.
+ */
+goog.vec.Vec2.dot = function(vec0, vec1) {
+  return vec0[0] * vec1[0] + vec0[1] * vec1[1];
+};
+
+
+/**
+ * Returns the squared distance between two points.
+ *
+ * @param {goog.vec.Vec2.AnyType} vec0 First point.
+ * @param {goog.vec.Vec2.AnyType} vec1 Second point.
+ * @return {number} The squared distance between the points.
+ */
+goog.vec.Vec2.distanceSquared = function(vec0, vec1) {
+  var x = vec0[0] - vec1[0];
+  var y = vec0[1] - vec1[1];
+  return x * x + y * y;
+};
+
+
+/**
+ * Returns the distance between two points.
+ *
+ * @param {goog.vec.Vec2.AnyType} vec0 First point.
+ * @param {goog.vec.Vec2.AnyType} vec1 Second point.
+ * @return {number} The distance between the points.
+ */
+goog.vec.Vec2.distance = function(vec0, vec1) {
+  return Math.sqrt(goog.vec.Vec2.distanceSquared(vec0, vec1));
+};
+
+
+/**
+ * Returns a unit vector pointing from one point to another.
+ * If the input points are equal then the result will be all zeros.
+ *
+ * @param {goog.vec.Vec2.AnyType} vec0 Origin point.
+ * @param {goog.vec.Vec2.AnyType} vec1 Target point.
+ * @param {goog.vec.Vec2.AnyType} resultVec The vector to receive the
+ *     results (may be vec0 or vec1).
+ * @return {!goog.vec.Vec2.AnyType} Return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Vec2.direction = function(vec0, vec1, resultVec) {
+  var x = vec1[0] - vec0[0];
+  var y = vec1[1] - vec0[1];
+  var d = Math.sqrt(x * x + y * y);
+  if (d) {
+    d = 1 / d;
+    resultVec[0] = x * d;
+    resultVec[1] = y * d;
+  } else {
+    resultVec[0] = resultVec[1] = 0;
+  }
+  return resultVec;
+};
+
+
+/**
+ * Linearly interpolate from vec0 to vec1 according to f. The value of f should
+ * be in the range [0..1] otherwise the results are undefined.
+ *
+ * @param {goog.vec.Vec2.AnyType} vec0 The first vector.
+ * @param {goog.vec.Vec2.AnyType} vec1 The second vector.
+ * @param {number} f The interpolation factor.
+ * @param {goog.vec.Vec2.AnyType} resultVec The vector to receive the
+ *     results (may be vec0 or vec1).
+ * @return {!goog.vec.Vec2.AnyType} Return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Vec2.lerp = function(vec0, vec1, f, resultVec) {
+  var x = vec0[0], y = vec0[1];
+  resultVec[0] = (vec1[0] - x) * f + x;
+  resultVec[1] = (vec1[1] - y) * f + y;
+  return resultVec;
+};
+
+
+/**
+ * Returns true if the components of vec0 are equal to the components of vec1.
+ *
+ * @param {goog.vec.Vec2.AnyType} vec0 The first vector.
+ * @param {goog.vec.Vec2.AnyType} vec1 The second vector.
+ * @return {boolean} True if the vectors are equal, false otherwise.
+ */
+goog.vec.Vec2.equals = function(vec0, vec1) {
+  return vec0.length == vec1.length &&
+      vec0[0] == vec1[0] && vec0[1] == vec1[1];
+};

+ 473 - 0
public/lib/closure/vec/vec3.js

@@ -0,0 +1,473 @@
+// Copyright 2011 The Closure Library Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+/**
+ * @fileoverview Supplies 3 element vectors that are compatible with WebGL.
+ * Each element is a float32 since that is typically the desired size of a
+ * 3-vector in the GPU.  The API is structured to avoid unnecessary memory
+ * allocations.  The last parameter will typically be the output vector and
+ * an object can be both an input and output parameter to all methods except
+ * where noted.
+ *
+ */
+goog.provide('goog.vec.Vec3');
+
+goog.require('goog.vec');
+
+/** @typedef {goog.vec.Float32} */ goog.vec.Vec3.Float32;
+/** @typedef {goog.vec.Float64} */ goog.vec.Vec3.Float64;
+/** @typedef {goog.vec.Number} */ goog.vec.Vec3.Number;
+/** @typedef {goog.vec.AnyType} */ goog.vec.Vec3.AnyType;
+
+// The following two types are deprecated - use the above types instead.
+/** @typedef {Float32Array} */ goog.vec.Vec3.Type;
+/** @typedef {goog.vec.ArrayType} */ goog.vec.Vec3.Vec3Like;
+
+
+/**
+ * Creates a 3 element vector of Float32. The array is initialized to zero.
+ *
+ * @return {!goog.vec.Vec3.Float32} The new 3 element array.
+ */
+goog.vec.Vec3.createFloat32 = function() {
+  return new Float32Array(3);
+};
+
+
+/**
+ * Creates a 3 element vector of Float64. The array is initialized to zero.
+ *
+ * @return {!goog.vec.Vec3.Float64} The new 3 element array.
+ */
+goog.vec.Vec3.createFloat64 = function() {
+  return new Float64Array(3);
+};
+
+
+/**
+ * Creates a 3 element vector of Number. The array is initialized to zero.
+ *
+ * @return {!goog.vec.Vec3.Number} The new 3 element array.
+ */
+goog.vec.Vec3.createNumber = function() {
+  var a = new Array(3);
+  goog.vec.Vec3.setFromValues(a, 0, 0, 0);
+  return a;
+};
+
+
+/**
+ * Creates a 3 element vector of Float32Array. The array is initialized to zero.
+ *
+ * @deprecated Use createFloat32.
+ * @return {!goog.vec.Vec3.Type} The new 3 element array.
+ */
+goog.vec.Vec3.create = function() {
+  return new Float32Array(3);
+};
+
+
+/**
+ * Creates a new 3 element FLoat32 vector initialized with the value from the
+ * given array.
+ *
+ * @param {goog.vec.Vec3.AnyType} vec The source 3 element array.
+ * @return {!goog.vec.Vec3.Float32} The new 3 element array.
+ */
+goog.vec.Vec3.createFloat32FromArray = function(vec) {
+  var newVec = goog.vec.Vec3.createFloat32();
+  goog.vec.Vec3.setFromArray(newVec, vec);
+  return newVec;
+};
+
+
+/**
+ * Creates a new 3 element Float32 vector initialized with the supplied values.
+ *
+ * @param {number} v0 The value for element at index 0.
+ * @param {number} v1 The value for element at index 1.
+ * @param {number} v2 The value for element at index 2.
+ * @return {!goog.vec.Vec3.Float32} The new vector.
+ */
+goog.vec.Vec3.createFloat32FromValues = function(v0, v1, v2) {
+  var a = goog.vec.Vec3.createFloat32();
+  goog.vec.Vec3.setFromValues(a, v0, v1, v2);
+  return a;
+};
+
+
+/**
+ * Creates a clone of the given 3 element Float32 vector.
+ *
+ * @param {goog.vec.Vec3.Float32} vec The source 3 element vector.
+ * @return {!goog.vec.Vec3.Float32} The new cloned vector.
+ */
+goog.vec.Vec3.cloneFloat32 = goog.vec.Vec3.createFloat32FromArray;
+
+
+/**
+ * Creates a new 3 element Float64 vector initialized with the value from the
+ * given array.
+ *
+ * @param {goog.vec.Vec3.AnyType} vec The source 3 element array.
+ * @return {!goog.vec.Vec3.Float64} The new 3 element array.
+ */
+goog.vec.Vec3.createFloat64FromArray = function(vec) {
+  var newVec = goog.vec.Vec3.createFloat64();
+  goog.vec.Vec3.setFromArray(newVec, vec);
+  return newVec;
+};
+
+
+/**
+* Creates a new 3 element Float64 vector initialized with the supplied values.
+*
+* @param {number} v0 The value for element at index 0.
+* @param {number} v1 The value for element at index 1.
+* @param {number} v2 The value for element at index 2.
+* @return {!goog.vec.Vec3.Float64} The new vector.
+*/
+goog.vec.Vec3.createFloat64FromValues = function(v0, v1, v2) {
+  var vec = goog.vec.Vec3.createFloat64();
+  goog.vec.Vec3.setFromValues(vec, v0, v1, v2);
+  return vec;
+};
+
+
+/**
+ * Creates a clone of the given 3 element vector.
+ *
+ * @param {goog.vec.Vec3.Float64} vec The source 3 element vector.
+ * @return {!goog.vec.Vec3.Float64} The new cloned vector.
+ */
+goog.vec.Vec3.cloneFloat64 = goog.vec.Vec3.createFloat64FromArray;
+
+
+/**
+ * Creates a new 3 element vector initialized with the value from the given
+ * array.
+ *
+ * @deprecated Use createFloat32FromArray.
+ * @param {goog.vec.Vec3.Vec3Like} vec The source 3 element array.
+ * @return {!goog.vec.Vec3.Type} The new 3 element array.
+ */
+goog.vec.Vec3.createFromArray = function(vec) {
+  var newVec = goog.vec.Vec3.create();
+  goog.vec.Vec3.setFromArray(newVec, vec);
+  return newVec;
+};
+
+
+/**
+ * Creates a new 3 element vector initialized with the supplied values.
+ *
+ * @deprecated Use createFloat32FromValues.
+ * @param {number} v0 The value for element at index 0.
+ * @param {number} v1 The value for element at index 1.
+ * @param {number} v2 The value for element at index 2.
+ * @return {!goog.vec.Vec3.Type} The new vector.
+ */
+goog.vec.Vec3.createFromValues = function(v0, v1, v2) {
+  var vec = goog.vec.Vec3.create();
+  goog.vec.Vec3.setFromValues(vec, v0, v1, v2);
+  return vec;
+};
+
+
+/**
+ * Creates a clone of the given 3 element vector.
+ *
+ * @deprecated Use cloneFloat32.
+ * @param {goog.vec.Vec3.Vec3Like} vec The source 3 element vector.
+ * @return {!goog.vec.Vec3.Type} The new cloned vector.
+ */
+goog.vec.Vec3.clone = function(vec) {
+  var newVec = goog.vec.Vec3.create();
+  goog.vec.Vec3.setFromArray(newVec, vec);
+  return newVec;
+};
+
+
+/**
+ * Initializes the vector with the given values.
+ *
+ * @param {goog.vec.Vec3.AnyType} vec The vector to receive the values.
+ * @param {number} v0 The value for element at index 0.
+ * @param {number} v1 The value for element at index 1.
+ * @param {number} v2 The value for element at index 2.
+ * @return {!goog.vec.Vec3.AnyType} return vec so that operations can be
+ *     chained together.
+ */
+goog.vec.Vec3.setFromValues = function(vec, v0, v1, v2) {
+  vec[0] = v0;
+  vec[1] = v1;
+  vec[2] = v2;
+  return vec;
+};
+
+
+/**
+ * Initializes the vector with the given array of values.
+ *
+ * @param {goog.vec.Vec3.AnyType} vec The vector to receive the
+ *     values.
+ * @param {goog.vec.Vec3.AnyType} values The array of values.
+ * @return {!goog.vec.Vec3.AnyType} return vec so that operations can be
+ *     chained together.
+ */
+goog.vec.Vec3.setFromArray = function(vec, values) {
+  vec[0] = values[0];
+  vec[1] = values[1];
+  vec[2] = values[2];
+  return vec;
+};
+
+
+/**
+ * Performs a component-wise addition of vec0 and vec1 together storing the
+ * result into resultVec.
+ *
+ * @param {goog.vec.Vec3.AnyType} vec0 The first addend.
+ * @param {goog.vec.Vec3.AnyType} vec1 The second addend.
+ * @param {goog.vec.Vec3.AnyType} resultVec The vector to
+ *     receive the result. May be vec0 or vec1.
+ * @return {!goog.vec.Vec3.AnyType} return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Vec3.add = function(vec0, vec1, resultVec) {
+  resultVec[0] = vec0[0] + vec1[0];
+  resultVec[1] = vec0[1] + vec1[1];
+  resultVec[2] = vec0[2] + vec1[2];
+  return resultVec;
+};
+
+
+/**
+ * Performs a component-wise subtraction of vec1 from vec0 storing the
+ * result into resultVec.
+ *
+ * @param {goog.vec.Vec3.AnyType} vec0 The minuend.
+ * @param {goog.vec.Vec3.AnyType} vec1 The subtrahend.
+ * @param {goog.vec.Vec3.AnyType} resultVec The vector to
+ *     receive the result. May be vec0 or vec1.
+ * @return {!goog.vec.Vec3.AnyType} return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Vec3.subtract = function(vec0, vec1, resultVec) {
+  resultVec[0] = vec0[0] - vec1[0];
+  resultVec[1] = vec0[1] - vec1[1];
+  resultVec[2] = vec0[2] - vec1[2];
+  return resultVec;
+};
+
+
+/**
+ * Negates vec0, storing the result into resultVec.
+ *
+ * @param {goog.vec.Vec3.AnyType} vec0 The vector to negate.
+ * @param {goog.vec.Vec3.AnyType} resultVec The vector to
+ *     receive the result. May be vec0.
+ * @return {!goog.vec.Vec3.AnyType} return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Vec3.negate = function(vec0, resultVec) {
+  resultVec[0] = -vec0[0];
+  resultVec[1] = -vec0[1];
+  resultVec[2] = -vec0[2];
+  return resultVec;
+};
+
+
+/**
+ * Multiplies each component of vec0 with scalar storing the product into
+ * resultVec.
+ *
+ * @param {goog.vec.Vec3.AnyType} vec0 The source vector.
+ * @param {number} scalar The value to multiply with each component of vec0.
+ * @param {goog.vec.Vec3.AnyType} resultVec The vector to
+ *     receive the result. May be vec0.
+ * @return {!goog.vec.Vec3.AnyType} return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Vec3.scale = function(vec0, scalar, resultVec) {
+  resultVec[0] = vec0[0] * scalar;
+  resultVec[1] = vec0[1] * scalar;
+  resultVec[2] = vec0[2] * scalar;
+  return resultVec;
+};
+
+
+/**
+ * Returns the magnitudeSquared of the given vector.
+ *
+ * @param {goog.vec.Vec3.AnyType} vec0 The vector.
+ * @return {number} The magnitude of the vector.
+ */
+goog.vec.Vec3.magnitudeSquared = function(vec0) {
+  var x = vec0[0], y = vec0[1], z = vec0[2];
+  return x * x + y * y + z * z;
+};
+
+
+/**
+ * Returns the magnitude of the given vector.
+ *
+ * @param {goog.vec.Vec3.AnyType} vec0 The vector.
+ * @return {number} The magnitude of the vector.
+ */
+goog.vec.Vec3.magnitude = function(vec0) {
+  var x = vec0[0], y = vec0[1], z = vec0[2];
+  return Math.sqrt(x * x + y * y + z * z);
+};
+
+
+/**
+ * Normalizes the given vector storing the result into resultVec.
+ *
+ * @param {goog.vec.Vec3.AnyType} vec0 The vector to normalize.
+ * @param {goog.vec.Vec3.AnyType} resultVec The vector to
+ *     receive the result. May be vec0.
+ * @return {!goog.vec.Vec3.AnyType} return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Vec3.normalize = function(vec0, resultVec) {
+  var ilen = 1 / goog.vec.Vec3.magnitude(vec0);
+  resultVec[0] = vec0[0] * ilen;
+  resultVec[1] = vec0[1] * ilen;
+  resultVec[2] = vec0[2] * ilen;
+  return resultVec;
+};
+
+
+/**
+ * Returns the scalar product of vectors v0 and v1.
+ *
+ * @param {goog.vec.Vec3.AnyType} v0 The first vector.
+ * @param {goog.vec.Vec3.AnyType} v1 The second vector.
+ * @return {number} The scalar product.
+ */
+goog.vec.Vec3.dot = function(v0, v1) {
+  return v0[0] * v1[0] + v0[1] * v1[1] + v0[2] * v1[2];
+};
+
+
+/**
+ * Computes the vector (cross) product of v0 and v1 storing the result into
+ * resultVec.
+ *
+ * @param {goog.vec.Vec3.AnyType} v0 The first vector.
+ * @param {goog.vec.Vec3.AnyType} v1 The second vector.
+ * @param {goog.vec.Vec3.AnyType} resultVec The vector to receive the
+ *     results. May be either v0 or v1.
+ * @return {!goog.vec.Vec3.AnyType} return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Vec3.cross = function(v0, v1, resultVec) {
+  var x0 = v0[0], y0 = v0[1], z0 = v0[2];
+  var x1 = v1[0], y1 = v1[1], z1 = v1[2];
+  resultVec[0] = y0 * z1 - z0 * y1;
+  resultVec[1] = z0 * x1 - x0 * z1;
+  resultVec[2] = x0 * y1 - y0 * x1;
+  return resultVec;
+};
+
+
+/**
+ * Returns the squared distance between two points.
+ *
+ * @param {goog.vec.Vec3.AnyType} vec0 First point.
+ * @param {goog.vec.Vec3.AnyType} vec1 Second point.
+ * @return {number} The squared distance between the points.
+ */
+goog.vec.Vec3.distanceSquared = function(vec0, vec1) {
+  var x = vec0[0] - vec1[0];
+  var y = vec0[1] - vec1[1];
+  var z = vec0[2] - vec1[2];
+  return x * x + y * y + z * z;
+};
+
+
+/**
+ * Returns the distance between two points.
+ *
+ * @param {goog.vec.Vec3.AnyType} vec0 First point.
+ * @param {goog.vec.Vec3.AnyType} vec1 Second point.
+ * @return {number} The distance between the points.
+ */
+goog.vec.Vec3.distance = function(vec0, vec1) {
+  return Math.sqrt(goog.vec.Vec3.distanceSquared(vec0, vec1));
+};
+
+
+/**
+ * Returns a unit vector pointing from one point to another.
+ * If the input points are equal then the result will be all zeros.
+ *
+ * @param {goog.vec.Vec3.AnyType} vec0 Origin point.
+ * @param {goog.vec.Vec3.AnyType} vec1 Target point.
+ * @param {goog.vec.Vec3.AnyType} resultVec The vector to receive the
+ *     results (may be vec0 or vec1).
+ * @return {!goog.vec.Vec3.AnyType} return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Vec3.direction = function(vec0, vec1, resultVec) {
+  var x = vec1[0] - vec0[0];
+  var y = vec1[1] - vec0[1];
+  var z = vec1[2] - vec0[2];
+  var d = Math.sqrt(x * x + y * y + z * z);
+  if (d) {
+    d = 1 / d;
+    resultVec[0] = x * d;
+    resultVec[1] = y * d;
+    resultVec[2] = z * d;
+  } else {
+    resultVec[0] = resultVec[1] = resultVec[2] = 0;
+  }
+  return resultVec;
+};
+
+
+/**
+ * Linearly interpolate from vec0 to v1 according to f. The value of f should be
+ * in the range [0..1] otherwise the results are undefined.
+ *
+ * @param {goog.vec.Vec3.AnyType} v0 The first vector.
+ * @param {goog.vec.Vec3.AnyType} v1 The second vector.
+ * @param {number} f The interpolation factor.
+ * @param {goog.vec.Vec3.AnyType} resultVec The vector to receive the
+ *     results (may be v0 or v1).
+ * @return {!goog.vec.Vec3.AnyType} return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Vec3.lerp = function(v0, v1, f, resultVec) {
+  var x = v0[0], y = v0[1], z = v0[2];
+  resultVec[0] = (v1[0] - x) * f + x;
+  resultVec[1] = (v1[1] - y) * f + y;
+  resultVec[2] = (v1[2] - z) * f + z;
+  return resultVec;
+};
+
+
+/**
+ * Returns true if the components of v0 are equal to the components of v1.
+ *
+ * @param {goog.vec.Vec3.AnyType} v0 The first vector.
+ * @param {goog.vec.Vec3.AnyType} v1 The second vector.
+ * @return {boolean} True if the vectors are equal, false otherwise.
+ */
+goog.vec.Vec3.equals = function(v0, v1) {
+  return v0.length == v1.length &&
+      v0[0] == v1[0] && v0[1] == v1[1] && v0[2] == v1[2];
+};

+ 405 - 0
public/lib/closure/vec/vec4.js

@@ -0,0 +1,405 @@
+// Copyright 2011 The Closure Library Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+/**
+ * @fileoverview Supplies 4 element vectors that are compatible with WebGL.
+ * Each element is a float32 since that is typically the desired size of a
+ * 4-vector in the GPU.  The API is structured to avoid unnecessary memory
+ * allocations.  The last parameter will typically be the output vector and
+ * an object can be both an input and output parameter to all methods except
+ * where noted.
+ *
+ */
+goog.provide('goog.vec.Vec4');
+
+goog.require('goog.vec');
+
+/** @typedef {goog.vec.Float32} */ goog.vec.Vec4.Float32;
+/** @typedef {goog.vec.Float64} */ goog.vec.Vec4.Float64;
+/** @typedef {goog.vec.Number} */ goog.vec.Vec4.Number;
+/** @typedef {goog.vec.AnyType} */ goog.vec.Vec4.AnyType;
+
+// The following two types are deprecated - use the above types instead.
+/** @typedef {Float32Array} */ goog.vec.Vec4.Type;
+/** @typedef {goog.vec.ArrayType} */ goog.vec.Vec4.Vec4Like;
+
+
+/**
+ * Creates a 4 element vector of Float32. The array is initialized to zero.
+ *
+ * @return {!goog.vec.Vec4.Float32} The new 3 element array.
+ */
+goog.vec.Vec4.createFloat32 = function() {
+  return new Float32Array(4);
+};
+
+
+/**
+ * Creates a 4 element vector of Float64. The array is initialized to zero.
+ *
+ * @return {!goog.vec.Vec4.Float64} The new 4 element array.
+ */
+goog.vec.Vec4.createFloat64 = function() {
+  return new Float64Array(4);
+};
+
+
+/**
+ * Creates a 4 element vector of Number. The array is initialized to zero.
+ *
+ * @return {!goog.vec.Vec4.Number} The new 4 element array.
+ */
+goog.vec.Vec4.createNumber = function() {
+  var v = new Array(4);
+  goog.vec.Vec4.setFromValues(v, 0, 0, 0, 0);
+  return v;
+};
+
+
+/**
+ * Creates a 4 element vector of Float32Array. The array is initialized to zero.
+ *
+ * @deprecated Use createFloat32.
+ * @return {!goog.vec.Vec4.Type} The new 4 element array.
+ */
+goog.vec.Vec4.create = function() {
+  return new Float32Array(4);
+};
+
+
+/**
+ * Creates a new 4 element vector initialized with the value from the given
+ * array.
+ *
+ * @deprecated Use createFloat32FromArray.
+ * @param {goog.vec.Vec4.Vec4Like} vec The source 4 element array.
+ * @return {!goog.vec.Vec4.Type} The new 4 element array.
+ */
+goog.vec.Vec4.createFromArray = function(vec) {
+  var newVec = goog.vec.Vec4.create();
+  goog.vec.Vec4.setFromArray(newVec, vec);
+  return newVec;
+};
+
+
+/**
+ * Creates a new 4 element FLoat32 vector initialized with the value from the
+ * given array.
+ *
+ * @param {goog.vec.Vec4.AnyType} vec The source 3 element array.
+ * @return {!goog.vec.Vec4.Float32} The new 3 element array.
+ */
+goog.vec.Vec4.createFloat32FromArray = function(vec) {
+  var newVec = goog.vec.Vec4.createFloat32();
+  goog.vec.Vec4.setFromArray(newVec, vec);
+  return newVec;
+};
+
+
+/**
+ * Creates a new 4 element Float32 vector initialized with the supplied values.
+ *
+ * @param {number} v0 The value for element at index 0.
+ * @param {number} v1 The value for element at index 1.
+ * @param {number} v2 The value for element at index 2.
+ * @param {number} v3 The value for element at index 3.
+ * @return {!goog.vec.Vec4.Float32} The new vector.
+ */
+goog.vec.Vec4.createFloat32FromValues = function(v0, v1, v2, v3) {
+  var vec = goog.vec.Vec4.createFloat32();
+  goog.vec.Vec4.setFromValues(vec, v0, v1, v2, v3);
+  return vec;
+};
+
+
+/**
+ * Creates a clone of the given 4 element Float32 vector.
+ *
+ * @param {goog.vec.Vec4.Float32} vec The source 3 element vector.
+ * @return {!goog.vec.Vec4.Float32} The new cloned vector.
+ */
+goog.vec.Vec4.cloneFloat32 = goog.vec.Vec4.createFloat32FromArray;
+
+
+/**
+ * Creates a new 4 element Float64 vector initialized with the value from the
+ * given array.
+ *
+ * @param {goog.vec.Vec4.AnyType} vec The source 4 element array.
+ * @return {!goog.vec.Vec4.Float64} The new 4 element array.
+ */
+goog.vec.Vec4.createFloat64FromArray = function(vec) {
+  var newVec = goog.vec.Vec4.createFloat64();
+  goog.vec.Vec4.setFromArray(newVec, vec);
+  return newVec;
+};
+
+
+/**
+* Creates a new 4 element Float64 vector initialized with the supplied values.
+*
+* @param {number} v0 The value for element at index 0.
+* @param {number} v1 The value for element at index 1.
+* @param {number} v2 The value for element at index 2.
+* @param {number} v3 The value for element at index 3.
+* @return {!goog.vec.Vec4.Float64} The new vector.
+*/
+goog.vec.Vec4.createFloat64FromValues = function(v0, v1, v2, v3) {
+  var vec = goog.vec.Vec4.createFloat64();
+  goog.vec.Vec4.setFromValues(vec, v0, v1, v2, v3);
+  return vec;
+};
+
+
+/**
+ * Creates a clone of the given 4 element vector.
+ *
+ * @param {goog.vec.Vec4.Float64} vec The source 4 element vector.
+ * @return {!goog.vec.Vec4.Float64} The new cloned vector.
+ */
+goog.vec.Vec4.cloneFloat64 = goog.vec.Vec4.createFloat64FromArray;
+
+
+/**
+ * Creates a new 4 element vector initialized with the supplied values.
+ *
+ * @deprecated Use createFloat32FromValues.
+ * @param {number} v0 The value for element at index 0.
+ * @param {number} v1 The value for element at index 1.
+ * @param {number} v2 The value for element at index 2.
+ * @param {number} v3 The value for element at index 3.
+ * @return {!goog.vec.Vec4.Type} The new vector.
+ */
+goog.vec.Vec4.createFromValues = function(v0, v1, v2, v3) {
+  var vec = goog.vec.Vec4.create();
+  goog.vec.Vec4.setFromValues(vec, v0, v1, v2, v3);
+  return vec;
+};
+
+
+/**
+ * Creates a clone of the given 4 element vector.
+ *
+ * @deprecated Use cloneFloat32.
+ * @param {goog.vec.Vec4.Vec4Like} vec The source 4 element vector.
+ * @return {!goog.vec.Vec4.Type} The new cloned vector.
+ */
+goog.vec.Vec4.clone = goog.vec.Vec4.createFromArray;
+
+
+/**
+ * Initializes the vector with the given values.
+ *
+ * @param {goog.vec.Vec4.AnyType} vec The vector to receive the values.
+ * @param {number} v0 The value for element at index 0.
+ * @param {number} v1 The value for element at index 1.
+ * @param {number} v2 The value for element at index 2.
+ * @param {number} v3 The value for element at index 3.
+ * @return {!goog.vec.Vec4.AnyType} return vec so that operations can be
+ *     chained together.
+ */
+goog.vec.Vec4.setFromValues = function(vec, v0, v1, v2, v3) {
+  vec[0] = v0;
+  vec[1] = v1;
+  vec[2] = v2;
+  vec[3] = v3;
+  return vec;
+};
+
+
+/**
+ * Initializes the vector with the given array of values.
+ *
+ * @param {goog.vec.Vec4.AnyType} vec The vector to receive the
+ *     values.
+ * @param {goog.vec.Vec4.AnyType} values The array of values.
+ * @return {!goog.vec.Vec4.AnyType} return vec so that operations can be
+ *     chained together.
+ */
+goog.vec.Vec4.setFromArray = function(vec, values) {
+  vec[0] = values[0];
+  vec[1] = values[1];
+  vec[2] = values[2];
+  vec[3] = values[3];
+  return vec;
+};
+
+
+/**
+ * Performs a component-wise addition of vec0 and vec1 together storing the
+ * result into resultVec.
+ *
+ * @param {goog.vec.Vec4.AnyType} vec0 The first addend.
+ * @param {goog.vec.Vec4.AnyType} vec1 The second addend.
+ * @param {goog.vec.Vec4.AnyType} resultVec The vector to
+ *     receive the result. May be vec0 or vec1.
+ * @return {!goog.vec.Vec4.AnyType} return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Vec4.add = function(vec0, vec1, resultVec) {
+  resultVec[0] = vec0[0] + vec1[0];
+  resultVec[1] = vec0[1] + vec1[1];
+  resultVec[2] = vec0[2] + vec1[2];
+  resultVec[3] = vec0[3] + vec1[3];
+  return resultVec;
+};
+
+
+/**
+ * Performs a component-wise subtraction of vec1 from vec0 storing the
+ * result into resultVec.
+ *
+ * @param {goog.vec.Vec4.AnyType} vec0 The minuend.
+ * @param {goog.vec.Vec4.AnyType} vec1 The subtrahend.
+ * @param {goog.vec.Vec4.AnyType} resultVec The vector to
+ *     receive the result. May be vec0 or vec1.
+ * @return {!goog.vec.Vec4.AnyType} return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Vec4.subtract = function(vec0, vec1, resultVec) {
+  resultVec[0] = vec0[0] - vec1[0];
+  resultVec[1] = vec0[1] - vec1[1];
+  resultVec[2] = vec0[2] - vec1[2];
+  resultVec[3] = vec0[3] - vec1[3];
+  return resultVec;
+};
+
+
+/**
+ * Negates vec0, storing the result into resultVec.
+ *
+ * @param {goog.vec.Vec4.AnyType} vec0 The vector to negate.
+ * @param {goog.vec.Vec4.AnyType} resultVec The vector to
+ *     receive the result. May be vec0.
+ * @return {!goog.vec.Vec4.AnyType} return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Vec4.negate = function(vec0, resultVec) {
+  resultVec[0] = -vec0[0];
+  resultVec[1] = -vec0[1];
+  resultVec[2] = -vec0[2];
+  resultVec[3] = -vec0[3];
+  return resultVec;
+};
+
+
+/**
+ * Multiplies each component of vec0 with scalar storing the product into
+ * resultVec.
+ *
+ * @param {goog.vec.Vec4.AnyType} vec0 The source vector.
+ * @param {number} scalar The value to multiply with each component of vec0.
+ * @param {goog.vec.Vec4.AnyType} resultVec The vector to
+ *     receive the result. May be vec0.
+ * @return {!goog.vec.Vec4.AnyType} return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Vec4.scale = function(vec0, scalar, resultVec) {
+  resultVec[0] = vec0[0] * scalar;
+  resultVec[1] = vec0[1] * scalar;
+  resultVec[2] = vec0[2] * scalar;
+  resultVec[3] = vec0[3] * scalar;
+  return resultVec;
+};
+
+
+/**
+ * Returns the magnitudeSquared of the given vector.
+ *
+ * @param {goog.vec.Vec4.AnyType} vec0 The vector.
+ * @return {number} The magnitude of the vector.
+ */
+goog.vec.Vec4.magnitudeSquared = function(vec0) {
+  var x = vec0[0], y = vec0[1], z = vec0[2], w = vec0[3];
+  return x * x + y * y + z * z + w * w;
+};
+
+
+/**
+ * Returns the magnitude of the given vector.
+ *
+ * @param {goog.vec.Vec4.AnyType} vec0 The vector.
+ * @return {number} The magnitude of the vector.
+ */
+goog.vec.Vec4.magnitude = function(vec0) {
+  var x = vec0[0], y = vec0[1], z = vec0[2], w = vec0[3];
+  return Math.sqrt(x * x + y * y + z * z + w * w);
+};
+
+
+/**
+ * Normalizes the given vector storing the result into resultVec.
+ *
+ * @param {goog.vec.Vec4.AnyType} vec0 The vector to normalize.
+ * @param {goog.vec.Vec4.AnyType} resultVec The vector to
+ *     receive the result. May be vec0.
+ * @return {!goog.vec.Vec4.AnyType} return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Vec4.normalize = function(vec0, resultVec) {
+  var ilen = 1 / goog.vec.Vec4.magnitude(vec0);
+  resultVec[0] = vec0[0] * ilen;
+  resultVec[1] = vec0[1] * ilen;
+  resultVec[2] = vec0[2] * ilen;
+  resultVec[3] = vec0[3] * ilen;
+  return resultVec;
+};
+
+
+/**
+ * Returns the scalar product of vectors v0 and v1.
+ *
+ * @param {goog.vec.Vec4.AnyType} v0 The first vector.
+ * @param {goog.vec.Vec4.AnyType} v1 The second vector.
+ * @return {number} The scalar product.
+ */
+goog.vec.Vec4.dot = function(v0, v1) {
+  return v0[0] * v1[0] + v0[1] * v1[1] + v0[2] * v1[2] + v0[3] * v1[3];
+};
+
+
+/**
+ * Linearly interpolate from v0 to v1 according to f. The value of f should be
+ * in the range [0..1] otherwise the results are undefined.
+ *
+ * @param {goog.vec.Vec4.AnyType} v0 The first vector.
+ * @param {goog.vec.Vec4.AnyType} v1 The second vector.
+ * @param {number} f The interpolation factor.
+ * @param {goog.vec.Vec4.AnyType} resultVec The vector to receive the
+ *     results (may be v0 or v1).
+ * @return {!goog.vec.Vec4.AnyType} return resultVec so that operations can be
+ *     chained together.
+ */
+goog.vec.Vec4.lerp = function(v0, v1, f, resultVec) {
+  var x = v0[0], y = v0[1], z = v0[2], w = v0[3];
+  resultVec[0] = (v1[0] - x) * f + x;
+  resultVec[1] = (v1[1] - y) * f + y;
+  resultVec[2] = (v1[2] - z) * f + z;
+  resultVec[3] = (v1[3] - w) * f + w;
+  return resultVec;
+};
+
+
+/**
+ * Returns true if the components of v0 are equal to the components of v1.
+ *
+ * @param {goog.vec.Vec4.AnyType} v0 The first vector.
+ * @param {goog.vec.Vec4.AnyType} v1 The second vector.
+ * @return {boolean} True if the vectors are equal, false otherwise.
+ */
+goog.vec.Vec4.equals = function(v0, v1) {
+  return v0.length == v1.length &&
+      v0[0] == v1[0] && v0[1] == v1[1] && v0[2] == v1[2] && v0[3] == v1[3];
+};

+ 43 - 4
public/vwf/model/aframe.js

@@ -195,6 +195,9 @@ define(["module", "vwf/model", "vwf/utility"], function (module, model, utility)
                 node.aframeObj = createAFrameObject(node);
                 addNodeToHierarchy(node);
 
+                if (isAEntityDefinition(node.prototypes))
+                    updateStoredTransform( node );
+
                 //notifyDriverOfPrototypeAndBehaviorProps();
                 //  }
 
@@ -353,7 +356,13 @@ define(["module", "vwf/model", "vwf/utility"], function (module, model, utility)
                     switch (propertyName) {
 
                         case "position":
-                            this.state.setAFrameProperty('position', propertyValue, aframeObject);
+
+                        var position = goog.vec.Vec3.createFromArray( propertyValue || [] );
+                            node.transform.position = position;
+                            value = propertyValue;
+                            node.storedTransformDirty = true; 
+                            //setTransformsDirty( threeObject );
+                            //this.state.setAFrameProperty('position', propertyValue, aframeObject);
                             break;
 
                         case "rotation":
@@ -820,11 +829,19 @@ define(["module", "vwf/model", "vwf/utility"], function (module, model, utility)
                     switch (propertyName) {
 
                         case "position":
-                            var pos = aframeObject.getAttribute('position');
-                            if (pos !== undefined) {
-                                value = pos//[pos.x, pos.y, pos.z]//AFRAME.utils.coordinates.stringify(pos);
+                            // var pos = aframeObject.getAttribute('position');
+                            // if (pos !== undefined) {
+                            //     value = pos//[pos.x, pos.y, pos.z]//AFRAME.utils.coordinates.stringify(pos);
+                            // }
+
+                            if ( node.storedTransformDirty ) {
+                                updateStoredTransform( node );
                             }
+
+                            //value = goog.vec.Vec3.clone(node.transform.position);
+                            value =  node.transform.position;
                             break;
+
                         case "scale":
                             var scale = aframeObject.getAttribute('scale');
                             if (scale !== undefined) {
@@ -1315,6 +1332,28 @@ define(["module", "vwf/model", "vwf/utility"], function (module, model, utility)
     }
 
 
+
+    function updateStoredTransform( node ) {
+        
+        if ( node && node.aframeObj) {
+            // Add a local model-side transform that can stay pure even if the view changes the
+            // transform on the threeObject - this already happened in creatingNode for those nodes that
+            // didn't need to load a model
+            node.transform = {};
+
+            let pos = node.aframeObj.object3D.position;
+            node.transform.position = goog.vec.Vec3.createFromValues(pos.x, pos.y, pos.z);
+
+            //node.transform.position = AFRAME.utils.coordinates.stringify(node.aframeObj.object3D.position);
+            node.transform.rotation = AFRAME.utils.coordinates.stringify(node.aframeObj.object3D.rotation);
+            node.storedTransformDirty = false;             
+        }
+      
+    }    
+
+
+
+
     function getPrototypes(kernel, extendsID) {
         var prototypes = [];
         var id = extendsID;

+ 16 - 7
public/vwf/model/aframe/addon/aframe-interpolation.js

@@ -55,7 +55,7 @@ AFRAME.registerComponent('interpolation', {
     if (this.enabled && this.node && this.node.interpolate) {
 
       this.setInterpolatedTransforms(dt);
-      //this.restoreTransforms();
+      this.restoreTransforms();
 
     }
 
@@ -73,25 +73,34 @@ AFRAME.registerComponent('interpolation', {
 
   restoreTransforms: function () {
 
-    let r = new THREE.Euler();
-    let rot = r.copy(this.node.interpolate.rotation.selfTick);
+    let r = new THREE.Vector3();
+    let rot = r.copy(this.node.interpolate.position.selfTick);
 
     if (rot && this.node.needTransformRestore) {
-      this.el.object3D.rotation.set(rot.x, rot.y, rot.z)
+      this.el.object3D.position.set(rot.x, rot.y, rot.z)
       this.node.needTransformRestore = false;
     }
 
+
+    // let r = new THREE.Euler();
+    // let rot = r.copy(this.node.interpolate.rotation.selfTick);
+
+    // if (rot && this.node.needTransformRestore) {
+    //   this.el.object3D.rotation.set(rot.x, rot.y, rot.z)
+    //   this.node.needTransformRestore = false;
+    // }
+
   },
 
   setInterpolatedTransforms: function (deltaTime) {
 
-    var step = (this.node.tickTime) / (this.driver.realTickDif);
+    var step = (this.driver.tickTime) / (this.driver.realTickDif);
     step = Math.min(step, 1);
     deltaTime = Math.min(deltaTime, this.driver.realTickDif)
-    this.node.tickTime += deltaTime || 0;
+    this.driver.tickTime += deltaTime || 0;
 
     this.interpolatePosition(step);
-    this.interpolateRotation(step);
+    //this.interpolateRotation(step);
 
   },
   radians: function (degrees) {

+ 246 - 0
public/vwf/model/aframe/addon/aframe-interpolation_from_vle.js

@@ -0,0 +1,246 @@
+
+/*
+The MIT License (MIT)
+Copyright (c) 2014-2018 Nikolai Suslov and the Krestianstvo.org project contributors. (https://github.com/NikolaySuslov/livecodingspace/blob/master/LICENSE.md)
+*/
+
+if (typeof AFRAME === 'undefined') {
+  throw new Error('Component attempted to register before AFRAME was available.');
+}
+
+AFRAME.registerComponent('interpolation', {
+  schema: {
+    enabled: { default: true },
+    deltaPos: { default: 0.001 },
+    deltaRot: { default: 0.1 }
+  },
+
+  init: function () {
+
+    this.driver = vwf.views["vwf/view/aframeComponent"];
+    this.tickTime = 0;
+
+  },
+
+  update: function (oldData) {
+
+    if (!this.interpolation) {
+      this.deltaPos = parseFloat(this.data.deltaPos);
+      this.deltaRot = THREE.Math.degToRad(parseFloat(this.data.deltaRot));
+
+      this.enabled = JSON.parse(this.data.enabled);
+      if (this.enabled) {
+      }
+    }
+  },
+
+  /**
+   * Called when a component is removed (e.g., via removeAttribute).
+   * Generally undoes all modifications to the entity.
+   */
+  remove: function () { },
+
+  /**
+   * Called on each scene tick.
+   */
+  tick: function (t, dt) {
+
+    if (!this.node) {
+      let interNode = Object.entries(this.driver.state.nodes).find(el => 
+        el[1].parentID == this.el.id && el[1].extendsID == "http://vwf.example.com/aframe/interpolation-component.vwf"
+      );
+      this.node = this.driver.nodes[interNode[0]];
+      this.nodeState = interNode[1];
+    }
+
+    if (this.enabled && this.node && this.node.interpolate) {
+
+
+      this.setInterpolatedTransformsNew(dt);
+      //this.applyViewTransformOverridesNew(timepassed);
+
+
+      //this.setInterpolatedTransforms(dt);
+      //this.restoreTransforms();
+
+    }
+
+  },
+
+  setInterpolatedTransformsNew: function(deltaTime) {
+
+
+    //deltaTime = Math.min(deltaTime,this.realTickDif)
+    this.tickTime += deltaTime || 0;
+
+
+    var hit = 0;
+    while (this.tickTime > 50) {
+        hit++;
+        this.tickTime -= 50;
+    }
+    var step = (this.tickTime) / (50);
+    if (hit === 1) {
+
+        this.resetInterpolationNew();
+
+    }
+
+    var lerpStep = Math.min(1, .2 * (deltaTime / 16.6)); //the slower the frames ,the more we have to move per frame. Should feel the same at 60 0r 20
+        this.interpolateOneNode(lerpStep,step);
+
+},
+
+  resetInterpolationNew: function() {
+
+     //don't do interpolation for static objects
+     //if (this.nodes[i].isStatic) continue;
+     if (this.node.lastTransformStep + 1 < vwf.time()) {
+          this.node.lastTickTransform = null;
+          this.node.lastFrameInterp = null;
+          this.node.thisTickTransform = null;
+     } else if (this.nodeState) {
+       if(!this.node.thisTickTransform)
+        this.node.thisTickTransform = new THREE.Vector3();
+
+         this.node.lastTickTransform = (new THREE.Vector3()).copy(this.node.thisTickTransform); //matset(this.nodes[i].lastTickTransform, this.nodes[i].thisTickTransform);
+         this.node.thisTickTransform = (new THREE.Vector3()).copy(this.nodeState.aframeObj.el.object3D.position)//this.nodeState.gettingProperty('position')); //matset(this.nodes[i].thisTickTransform, this.state.nodes[i].gettingProperty('transform'));
+     }
+
+  },
+
+  interpolateOneNode:function(lerpStep,step)
+  {
+
+       //don't do interpolation for static objects
+          //if (this.nodes[i].isStatic) return;
+
+        if(this.node.lastTickTransform && this.node.thisTickTransform) {
+
+
+          var interp = null;
+          var last = (new THREE.Vector3()).copy(this.node.lastTickTransform);
+          var now = (new THREE.Vector3()).copy(this.node.thisTickTransform);
+          if (last && now) {
+
+              interp = (new THREE.Vector3()).copy(last); //matset(interp, last);
+              interp = last.lerp(now, step);
+
+              //interp = this.matrixLerp(last, now, step, interp);
+
+              this.node.currentTickTransform = (new THREE.Vector3()).copy(this.nodeState.aframeObj.el.object3D.position)//this.nodeState.gettingProperty('position'))//matset(this.nodes[i].currentTickTransform, this.state.nodes[i].gettingProperty('transform'));
+
+                  if (this.node.lastFrameInterp)
+                     // interp = this.matrixLerp(this.node.lastFrameInterp, now, lerpStep, interp);
+                     interp = this.node.lastFrameInterp.lerp(now, lerpStep);
+
+                  //this.state.node.setTransformInternal(interp, false);
+                  this.el.object3D.position.set(interp.x, interp.y, interp.z);
+                  this.node.lastFrameInterp = (new THREE.Vector3()).copy(interp) //matset(this.nodes[i].lastFrameInterp || [], interp);
+          }
+
+        }
+
+  },
+
+
+  vecCmp: function (a, b, delta) {
+
+    let distance = a.distanceTo(b);
+    if (distance > delta) {
+      return false;
+    }
+
+    return true;
+  },
+
+  restoreTransforms: function () {
+
+    let r = new THREE.Euler();
+    let rot = r.copy(this.node.interpolate.rotation.selfTick);
+
+    if (rot && this.node.needTransformRestore) {
+      this.el.object3D.rotation.set(rot.x, rot.y, rot.z)
+      this.node.needTransformRestore = false;
+    }
+
+  },
+
+  setInterpolatedTransforms: function (deltaTime) {
+
+    var step = (this.node.tickTime) / (this.driver.realTickDif);
+    step = Math.min(step, 1);
+    deltaTime = Math.min(deltaTime, this.driver.realTickDif)
+    this.node.tickTime += deltaTime || 0;
+
+    this.interpolatePosition(step);
+    this.interpolateRotation(step);
+
+  },
+  radians: function (degrees) {
+    // return degrees * Math.PI / 180.0;
+    return THREE.Math.degToRad(degrees)
+  },
+
+  interpolateRotation: function (step) {
+
+    let last = this.node.interpolate.rotation.lastTick;
+    let now = this.node.interpolate.rotation.selfTick;
+
+    if (last && now) {
+
+      let comp = this.vecCmp(last.toVector3(), now.toVector3(), this.deltaRot);
+
+      if (!comp) {
+
+        // console.log('Last:', last, ' Now: ', now);
+
+        let lastV = (new THREE.Quaternion()).setFromEuler(new THREE.Euler(
+          (last.x),
+          (last.y),
+          (last.z), 'YXZ'
+        ));
+
+        let nowV = (new THREE.Quaternion()).setFromEuler(new THREE.Euler(
+          (now.x),
+          (now.y),
+          (now.z), 'YXZ'
+        ));
+
+        let q = new THREE.Quaternion();
+        let e = new THREE.Euler();
+
+        THREE.Quaternion.slerp(lastV, nowV, q, step || 0);
+        let interp = e.setFromQuaternion(q, 'YXZ');
+
+        this.el.object3D.rotation.set(interp.x, interp.y, interp.z);
+        this.node.needTransformRestore = true;
+      }
+    }
+  },
+
+  interpolatePosition: function (step) {
+
+    var last = this.node.interpolate.position.lastTick; //this.node.lastTickTransform;
+    var now = this.node.interpolate.position.selfTick; //Transform;
+
+    if (last && now) {
+
+      let comp = this.vecCmp(last, now, this.deltaPos);
+
+      if (!comp) {
+
+        var lastV = (new THREE.Vector3()).copy(last);
+        var nowV = (new THREE.Vector3()).copy(now);
+
+        var interp = lastV.lerp(nowV, step || 0);
+        //this.el.setAttribute('position',interp);
+
+        this.el.object3D.position.set(interp.x, interp.y, interp.z);
+        this.node.needTransformRestore = true;
+      }
+    }
+  },
+  pause: function () { },
+  play: function () { }
+});

+ 98 - 0
public/vwf/view/aframe.js

@@ -155,6 +155,11 @@ define(["module", "vwf/view"], function (module, view) {
                 return;
             }
 
+            if (propertyName == 'position')
+               {
+                receiveModelTransformChanges( nodeId, propertyValue );
+               }
+
             // if (node.aframeObj.nodeName == "AUDIO" && propertyName == 'itemSrc') {
 
             //     //console.log("sat new item");
@@ -170,6 +175,11 @@ define(["module", "vwf/view"], function (module, view) {
 
             // }
 
+            // if ( propertyName == "position" ) {
+            //     receiveModelTransformChanges( nodeId, propertyValue );
+            // }
+
+
             if (node.aframeObj.nodeName == "AUDIO" && propertyName == 'itemSrc') {
 
                 //console.log("sat new item");
@@ -263,6 +273,18 @@ define(["module", "vwf/view"], function (module, view) {
         },
 
         firedEvent: function (nodeID, eventName, eventParameters) {
+
+            if ( eventName == "changingTransformFromView" ) {
+                var clientThatSatProperty = self.kernel.client();
+                var me = self.kernel.moniker();
+
+                // If the transform property was initially updated by this view....
+                if ( clientThatSatProperty == me ) {
+                    var node = this.state.nodes[ nodeID ];
+                    node.ignoreNextTransformUpdate = true;
+                }
+            }
+
             //var avatarID = vwf_view.kernel.find("", avatarName)
 
             var avatarName = 'avatar-' + self.kernel.moniker();
@@ -377,6 +399,7 @@ define(["module", "vwf/view"], function (module, view) {
             }
 
             // console.log(vwfTime);
+
             //lerpTick ();
         },
 
@@ -389,6 +412,34 @@ define(["module", "vwf/view"], function (module, view) {
             }
 
 
+            switch(methodName) {
+                case "translateBy":
+                case "translateTo":
+                // No need for rotateBy or rotateTo because they call the quaternion methods
+                case "quaterniateBy":
+                case "quaterniateTo":
+                case "scaleBy":
+                case "scaleTo":
+                // No need for transformBy or worldTransformBy because they call transformTo and worldTransformTo
+                case "transformTo":
+                case "worldTransformTo":
+                    // If the duration of the transform is 0, set the transforms to their final value so it doesn't interpolate
+                    if(methodParameters.length < 2 || methodParameters[1] == 0) {
+
+                        // let compDriver = vwf.views["vwf/view/aframeComponent"];
+                        // if (compDriver) {
+
+
+                        //     compDriver.nodes[nodeID].interpolate.position.lastTick = compDriver.getPosition(nodeID);
+                        //     compDriver.nodes[nodeID].interpolate.position.selfTick = goog.vec.Vec3.clone(compDriver.nodes[nodeID].lastTickTransform);
+
+                     
+                    //}
+                    break;
+            }
+
+        }
+
             if (this.nodes[nodeID].extends == "http://vwf.example.com/aframe/acamera.vwf") {
                 if (methodName == "setCameraToActive") {
                     if (methodParameters[0] == vwf.moniker_) {
@@ -415,6 +466,53 @@ define(["module", "vwf/view"], function (module, view) {
     });
 
 
+      // Receive Model Transform Changes algorithm 
+    // 1.0 If (own view changes) then IGNORE (only if no external changes have occurred since the user’s view 
+    //       requested this change – otherwise, will need to treat like 1.1 or 1.2)
+    // 1.1 Elseif (other external changes and no outstanding own view changes) then ADOPT
+    // 1.2 Else Interpolate to the model’s transform (conflict b/w own view and external sourced model changes)
+
+    function receiveModelTransformChanges(nodeID, propertyValue ) {
+
+        var node = self.state.nodes[ nodeID ];
+
+        // If the node does not exist in the state's list of nodes, then this update is from a prototype and we
+        // should ignore it
+        if ( !node ) {
+            return;
+        }
+
+         // If the transform property was initially updated by this view....
+         if ( node.ignoreNextTransformUpdate ) {
+            node.outstandingTransformRequests.shift();
+            node.ignoreNextTransformUpdate = false;
+        } else {
+           // adoptTransform( node, transformMatrix );
+           setAFrameProperty ('position', propertyValue, node.aframeObj)
+        }
+
+
+
+
+    }
+
+
+    function setAFrameProperty (propertyName, propertyValue, aframeObject) {
+        //console.log(propertyValue);
+                if (propertyValue.hasOwnProperty('x')) {
+                    aframeObject.setAttribute(propertyName, propertyValue)
+                } else
+                    if (Array.isArray(propertyValue) || propertyValue instanceof Float32Array ) {
+                        aframeObject.setAttribute(propertyName, { x: propertyValue[0], y: propertyValue[1], z: propertyValue[2] })
+                    } 
+                    
+                    else if (typeof propertyValue === 'string') {
+                        aframeObject.setAttribute(propertyName, AFRAME.utils.coordinates.parse(propertyValue))
+                    }
+        
+            }
+
+
     function compareCoordinates(a, b, delta) {
         return Math.abs(a.x - b.x) > delta || Math.abs(a.y - b.y) > delta || Math.abs(a.z - b.z) > delta
 

+ 3 - 3
public/vwf/view/aframeComponent.js

@@ -173,7 +173,7 @@ define(["module", "vwf/view"], function (module, view) {
                 return;
             }
 
-       
+    
             if (this.nodes[nodeID].extends == "http://vwf.example.com/aframe/a-sound-component.vwf"){
                 if (methodName == "stopSound"){
 
@@ -212,7 +212,7 @@ define(["module", "vwf/view"], function (module, view) {
         self.lastRealTick = now;
  
         //reset - loading can cause us to get behind and always but up against the max prediction value
-       // self.tickTime = 0;
+        self.tickTime = 0;
 
        let interNodes = Object.entries(self.nodes).filter(node => 
         node[1].extends == 'http://vwf.example.com/aframe/interpolation-component.vwf');
@@ -220,7 +220,7 @@ define(["module", "vwf/view"], function (module, view) {
        interNodes.forEach(node => {
            let nodeID = node[0];
         if ( self.state.nodes[nodeID] ) {      
-            self.nodes[nodeID].tickTime = 0;
+           // self.nodes[nodeID].tickTime = 0;
             if(!self.nodes[nodeID].interpolate)
             {
                 self.nodes[nodeID].interpolate = {

Some files were not shown because too many files changed in this diff