diff options
Diffstat (limited to 'o3d/samples/o3d-webgl/curve.js')
-rw-r--r-- | o3d/samples/o3d-webgl/curve.js | 912 |
1 files changed, 0 insertions, 912 deletions
diff --git a/o3d/samples/o3d-webgl/curve.js b/o3d/samples/o3d-webgl/curve.js deleted file mode 100644 index 6425fbb..0000000 --- a/o3d/samples/o3d-webgl/curve.js +++ /dev/null @@ -1,912 +0,0 @@ -/* - * Copyright 2010, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * A CurveKey prepresents a key on an Curve. - * - * @constructor - * @extends {o3d.ObjectBase} - */ -o3d.CurveKey = function(owner) { - o3d.ObjectBase.call(this); - /** - * The only owner of this CurveKey (cannot be shared). - * @private - * @type {!o3d.Curve} - */ - this.owner_ = owner; - - /** - * See input setter. - * @private - * @type {number} - */ - this.input_ = 0; - - /** - * See input setter. - * @private - * @type {number} - */ - this.output_ = 0; -}; -o3d.inherit('CurveKey', 'ObjectBase'); - -/** - * Destroys this key, removing it from its owner. - * - */ -o3d.CurveKey.prototype.destroy = function() { - o3d.removeFromArray(this.owner_.keys, this); - this.owner_.invalidateCache_(); -}; - -/** - * Computes the value within the range from this.input to next_key.input. - * Defaults to the LinearCurveKey implementation which is continuous. - * @param {number} offset Offset within the key (function input - this.input) - * @param {o3d.CurveKey} next_key The next key in the set, can not be null. - * @return {number} return - */ -o3d.CurveKey.prototype.getOutputAtOffset = function(offset, next_key) { - var input_span = next_key.input - this.input; - var output_span = next_key.output - this.output; - return this.output + offset / input_span * output_span; -}; - -/** - * Input for this key. This curve key will apply until the next key's input. - * @type {number} - */ -o3d.CurveKey.prototype.__defineGetter__("input", function() { - return this.input_; -}); -o3d.CurveKey.prototype.__defineSetter__("input", function(new_input) { - if (new_input != this.input_) { - this.input_ = new_input; - this.owner_.invalidateCache_(); - } -}); - -/** - * Output corresponding to input. - * @type {number} - */ -o3d.CurveKey.prototype.__defineGetter__("output", function() { - return this.output_; -}); -o3d.CurveKey.prototype.__defineSetter__("output", function(new_output) { - if (new_output != this.output_) { - this.output_ = new_output; - this.owner_.invalidateCache_(); - } -}); - -/** - * An CurveKey that holds its output (is not interpolated between this key - * and the next.) - * - * Other discontinuous CurveKey classes must be derived from StepCurveKey. - * - * @constructor - * @extends {o3d.CurveKey} - */ -o3d.StepCurveKey = function(owner) { - o3d.CurveKey.call(this, owner); - owner.num_step_keys_++; -}; -o3d.inherit('StepCurveKey', 'CurveKey'); - -/** - * Simple discontinuous implementation. - * @param {number} offset Ignored: constant output within a StepCurveKey - * @param {o3d.CurveKey} next_key Ignored: discontinuous - * @return {number} output - */ -o3d.StepCurveKey.prototype.getOutputAtOffset = function(offset, next_key) { - return this.output; -}; - -/** - * Specialized destroy method to update the num_step_keys_ of the owner_. - */ -o3d.StepCurveKey.prototype.destroy = function() { - o3d.CurveKey.prototype.destroy.call(this); - this.owner_.num_step_keys_--; -}; - -/** - * An CurveKey that linearly interpolates between this key and the next key. - * - * @constructor - * @extends {o3d.CurveKey} - */ -o3d.LinearCurveKey = function(owner) { - o3d.CurveKey.call(this, owner); -}; -o3d.inherit('LinearCurveKey', 'CurveKey'); - -/** - * An CurveKey that uses a bezier curve for interpolation between this key - * and the next. - * - * @constructor - * @extends {o3d.CurveKey} - */ -o3d.BezierCurveKey = function(owner) { - o3d.CurveKey.call(this, owner); - - /** - * See input setter. - * @private - * @type {Array.<number>} - */ - this.in_tangent_ = [0, 0]; - - /** - * See input setter. - * @private - * @type {Array.<number>} - */ - this.out_tangent_ = [0, 0]; -}; -o3d.inherit('BezierCurveKey', 'CurveKey'); - -/** - * Tangent for values approaching this key. Do not set elements in the array - * directly as that will prevent calling invalidateCache_. - * @type {Array.<number>} - */ -o3d.BezierCurveKey.prototype.__defineGetter__("inTangent", function() { - return this.in_tangent_; -}); -o3d.BezierCurveKey.prototype.__defineSetter__("inTangent", function(new_t) { - if (new_t != this.in_tangent_) { - this.in_tangent_ = new_t; - this.owner_.invalidateCache_(); - } -}); - -/** - * Tangent for values approaching the next key. Do not set elements in the array - * directly as that will prevent calling invalidateCache_. - * @type {Array.<number>} - */ -o3d.BezierCurveKey.prototype.__defineGetter__("outTangent", function() { - return this.out_tangent_; -}); -o3d.BezierCurveKey.prototype.__defineSetter__("outTangent", function(new_t) { - if (new_t != this.out_tangent_) { - this.out_tangent_ = new_t; - this.owner_.invalidateCache_(); - } -}); - -/** - * Uses iterative method to accurately pin-point the 't' of the Bezier - * equation that corresponds to the current time. - * @param {number} control_point_0_x this.input - * @param {number} control_point_1_x this.outTangent - * @param {number} control_point_2_x next.inTangent - * @param {number} control_point_3_x next.input - * @param {number} input Absolute input value relative to span - * @param {number} initial_guess Starting point assuming mostly linear. - * @private - */ -o3d.BezierCurveKey.findT_ = function(control_point_0_x, - control_point_1_x, - control_point_2_x, - control_point_3_x, - input, - initial_guess) { - var local_tolerance = 0.001; - var high_t = 1.0; - var low_t = 0.0; - - // TODO: Optimize here, start with a more intuitive value than 0.5 - // (comment left over from original code) - var mid_t = 0.5; - if (initial_guess <= 0.1) { - mid_t = 0.1; // clamp to 10% or 90%, because if miss, the cost is - // too high. - } else if (initial_guess >= 0.9) { - mid_t = 0.9; - } else { - mid_t = initial_guess; - } - var once = true; - while ((high_t-low_t) > local_tolerance) { - if (once) { - once = false; - } else { - mid_t = (high_t - low_t) / 2.0 + low_t; - } - var ti = 1.0 - mid_t; // (1 - t) - var calculated_time = control_point_0_x * ti * ti * ti + - 3 * control_point_1_x * mid_t * ti * ti + - 3 * control_point_2_x * mid_t * mid_t * ti + - control_point_3_x * mid_t * mid_t * mid_t; - if (Math.abs(calculated_time - input) <= local_tolerance) { - break; // If we 'fall' very close, we like it and break. - } - if (calculated_time > input) { - high_t = mid_t; - } else { - low_t = mid_t; - } - } - return mid_t; -}; - -/** - * Computes the value of the Bezier curve within the range from this.input - * to next_key.input. - * @param {number} offset Offset within the key (function input - this.input) - * @param {o3d.CurveKey} next_key The next key in the set, can not be null. - * @return {number} return - */ -o3d.BezierCurveKey.prototype.getOutputAtOffset = function(offset, next_key) { - var input_span = next_key.input - this.input; - var output_span = next_key.output - this.output; - var in_tangent; - - // We check bezier first because it's the most likely match for another - // bezier key. - if (next_key.inTangent) { - in_tangent = next_key.inTangent; - } else { - in_tangent = [next_key.input - input_span / 3.0, - next_key.output - output_span / 3.0]; - } - - // Do a bezier calculation. - var t = offset / input_span; - t = o3d.BezierCurveKey.findT_(this.input, - this.outTangent[0], - in_tangent[0], - next_key.input, - this.input + offset, - t); - var b = this.outTangent[1]; - var c = in_tangent[1]; - var ti = 1.0 - t; - var br = 3.0; - var cr = 3.0; - return this.output * ti * ti * ti + br * b * ti * ti * t + - cr * c * ti * t * t + next_key.output * t * t * t; -}; - -/** - * A Curve stores a bunch of CurveKeys and given a value - * representing an input point on a curve returns the output of the curve for - * that input. Curve is data only. It is used by 1 or more - * FunctionEval objects or by direct use from javascript. - * - * @constructor - * @extends {o3d.Function} - */ -o3d.Curve = function() { - o3d.Function.call(this, this.evaluate); - - /** - * The behavior of the curve before the first key. - * @see o3d.Curve.CONSTANT - * @see o3d.Curve.LINEAR - * @see o3d.Curve.CYCLE - * @see o3d.Curve.CYCLE_RELATIVE - * @see o3d.Curve.OSCILLATE - * @type {o3d.Curve.Infinity} - * @default CONSTANT - */ - this.preInfinity = o3d.Curve.CONSTANT; - - /** - * The behavior of the curve before the first key. - * @see o3d.Curve.CONSTANT - * @see o3d.Curve.LINEAR - * @see o3d.Curve.CYCLE - * @see o3d.Curve.CYCLE_RELATIVE - * @see o3d.Curve.OSCILLATE - * @type {o3d.Curve.Infinity} - * @default CONSTANT - */ - this.postInfinity = o3d.Curve.CONSTANT; - - /** - * Whether or not a cache is used to speed up evaluation of this Curve. - */ - this.useCache = true; - - /** - * Data for sampleRate setter. - * @type {number} - * @private - */ - this.sample_rate_ = o3d.Curve.kDefaultSampleRate; - - /** - * The keys for this curve. - * - * This property is read-only. - * @type {!Array.<!o3d.CurveKey>} - */ - this.keys = []; - - /** - * Keep track if a new key has been added which hasn't been sorted. - * @type {boolean} - * @private - */ - this.sorted_ = true; - - /** - * True if the curve needs to checks for discontinuity errors before the next - * evauluation. - * @see updateCurveInfo_ - * @type {boolean} - * @private - */ - this.check_discontinuity_ = false; - - /** - * Keep track if any discontinuous (steps or gaps) keys are in the mix. - * Call isDiscontinuous to access this value, which updates it if necessary. - * @type {boolean} - * @private - */ - this.discontinuous_= false; - - /** - * @type {number} Number of step keys--used to speed up updateCurveInfo_ - * discontinuity check if it's non-zero. - * @private - */ - this.num_step_keys_ = 0; - -}; - -o3d.inherit('Curve', 'Function'); - -/** - * Constant representing the fastest possible sample period. More samples take - * more computation initially and more memroy. - */ -o3d.Curve.kMinimumSampleRate = 1.0 / 240.0; - -/** - * By default, sample 30 times per curve key. - */ -o3d.Curve.kDefaultSampleRate = 1.0 / 30.0; - -/** - * Gets the sample rate for the cache. By default Animation data is - * cached so that using the animation is fast. To do this the keys that - * represent the animation are sampled. The higher the frequency of the - * samples the closer the cache will match the actual keys. - * The default is 1/30 (30hz). You can set it anywhere from 1/240th (240hz) to - * any larger value. Note: Setting the sample rate has the side effect of - * invalidating the cache thereby causing it to get rebuilt. - * Must be 1/240 or greater. Default = 1/30. - * - * @type {number} - */ -o3d.Curve.prototype.__defineGetter__("sampleRate", function() { - return this.sample_rate_; -}); - -o3d.Curve.prototype.__defineSetter__("sampleRate", function(rate) { - if (rate < o3d.Curve.kMinimumSampleRate) { - rate = o3d.Curve.kMinimumSampleRate; - this.gl.client.error_callback( - "attempt to set sample rate to " + rate + - " which is lower than the minimum of " + o3d.Curve.kMinimumSampleRate); - } else if (rate != this.sample_rate_) { - this.sample_rate_ = rate; - this.invalidateCache_(); - } -}); - -/** - * @type {number} - */ -o3d.Curve.Infinity = goog.typedef; - -/** - * Uses the output value of the first or last animation key. - * @type {o3d.Curve.Infinity} - */ -o3d.Curve.CONSTANT = 0; - -/** - * Takes the distance between the closest animation key input value and the - * evaluation time. Multiplies this distance against the instant slope at the - * closest animation key and offsets the result with the closest animation key - * output value. - * @type {o3d.Curve.Infinity} - */ -o3d.Curve.LINEAR = 1; - -/** - * Cycles over the first and last keys using: - * input = (input - first) % (last - first) + first; - * Note that in CYCLE mode you can never get the end output because a cycle - * goes from start to end exclusive of end. - * @type {o3d.Curve.Infinity} - */ -o3d.Curve.CYCLE = 2; - -/** - * Same as cycle except the offset of the entire cycle is added to each - * consecutive cycle. - * @type {o3d.Curve.Infinity} - */ -o3d.Curve.CYCLE_RELATIVE = 3; - -/** - * Ping Pongs between the first and last keys. - * @type {o3d.Curve.Infinity} - */ -o3d.Curve.OSCILLATE = 4; - -/** - * Deserializes from the curve data given a RawData object. - * - * @param {!o3d.RawData} rawData contains curve data - * @param {number} opt_offset is a byte offset from the start of raw_data - * @param {number} opt_length is the byte length of the data to set - * @return {boolean} True if operation was successful. - * - */ -o3d.Curve.prototype.set = function(rawData, opt_offset, opt_length) { - o3d.notImplemented(); -}; - -/** - * Creates a new key for this curve. - * @param {string} keyType name of key class to create. Valid type names are: - * <li> 'StepCurveKey', - * <li> 'LinearCurveKey', - * <li> 'BezierCurveKey', - * @return {!o3d.CurveKey} The created key. - * - */ -o3d.Curve.prototype.createKey = function(keyType) { - var newKey = new (o3d[keyType]) (this); - this.keys.push(newKey); - return newKey; -}; - -/** - * Adds 1 or more LinearKeys to this Curve. - * - * Example: - * <pre> - * // Creates 2 keys. - * // 1 key at 0 with an output of 10 - * // 1 key at 20 with an output of 30 - * curve.addLinearKeys([0,10,20,30]); - * </pre>. - * - * @param {!Array.<number>} values Array of input, output pairs. - * Length must be a multiple of 2 - */ -o3d.Curve.prototype.addLinearKeys = function(values) { - var kNumLinearKeyValues = 2; - if (values.length % kNumLinearKeyValues != 0) { - this.gl.client.error_callback( - "addLinearKeys: expected multiple of 2 values got "+values.size()); - return; - } - for (var i = 0; i < values.length; i += kNumLinearKeyValues) { - var newKey = this.createKey("LinearCurveKey"); - newKey.input = values[i]; - newKey.output = values[i+1]; - } - this.sorted_ = false; -}; - -/** - * Adds 1 or more StepKeys to this Curve. - * - * Example: - * <pre> - * // Creates 2 keys. - * // 1 key at 0 with an output of 10 - * // 1 key at 20 with an output of 30 - * curve.addStepKeys([0,10,20,30]); - * </pre>. - * - * @param {!Array.<number>} values Array of input, output pairs. - * Length must be a multiple of 2 - */ -o3d.Curve.prototype.addStepKeys = function(values) { - var kNumStepKeyValues = 2; - if (values.length % kNumStepKeyValues != 0) { - this.gl.client.error_callback( - "addStepKeys: expected multiple of 2 values got "+values.size()); - return; - } - for (var i = 0; i < values.length; i += kNumStepKeyValues) { - var newKey = this.createKey("StepCurveKey"); - newKey.input = values[i]; - newKey.output = values[i+1]; - } - this.sorted_ = false; -}; - -/** - * Adds 1 or more BezierKeys to this Curve. - * - * Example: - * <pre> - * // Creates 2 keys. - * // 1 key at 0 with an output of 10, in tangent of 1,9, out tangent 9,0.5 - * // 1 key at 20 with an output of 30, in tangent of 30, 3, out tangent 4, 28 - * curve.addBezierKeys([0,10,1,9,9,0.5,2,30,3,4,28]); - * </pre>. - * - * @param {!Array.<number>} values Array of tuples of the form (input, output, - * inTangent[0], inTangent[1], outTangent[0], outTangent[1]). - * Length must be a multiple of 6. - */ -o3d.Curve.prototype.addBezierKeys = function(values) { - var kNumBezierKeyValues = 6; - if (values.length % kNumBezierKeyValues != 0) { - this.gl.client.error_callback( - "addBezierKeys: expected multiple of 6 values got "+values.size()); - return; - } - for (var i = 0; i < values.length; i += kNumBezierKeyValues) { - var newKey = this.createKey("BezierCurveKey"); - newKey.input = values[i]; - newKey.output = values[i+1]; - newKey.inTangent[0] = values[i+2]; - newKey.inTangent[1] = values[i+3]; - newKey.outTangent[0] = values[i+4]; - newKey.outTangent[1] = values[i+5]; - } - this.sorted_ = false; -}; - -/** - * Force updating the cache or checking discontinuity. - * @private - */ -o3d.Curve.prototype.invalidateCache_ = function() { - this.check_valid_ = false; - this.check_discontinuity_ = true; -}; - -/** - * Returns whether or not the curve is discontinuous. A discontinuous curve - * takes more time to evaluate. - * @return {boolean} True if the curve is discontinuous. - */ -o3d.Curve.prototype.isDiscontinuous = function() { - this.updateCurveInfo_(); - return this.discontinuous_; -}; - -/** - * Comparator to allow sorting by keys by their input value. - * @param {o3d.CurveKey} a First input to compare. - * @param {o3d.CurveKey} b Second input to compare. - * @return {number} positive, zero, or negative (see Array.sort) - */ -o3d.Curve.compareInputs_ = function(a, b) { - return a.input - b.input; -}; - -/** - * Sorts keys (if sorted_ is false) and updates discontinuous_ - * (if check_discontinuity_ is true). - * Called automatically when necessary in evaluate and isDiscontinuous. - * @private - */ -o3d.Curve.prototype.updateCurveInfo_ = function() { - if (!this.sorted_) { - // resort keys - this.keys.sort(o3d.Curve.compareInputs_); - this.sorted_ = true; - this.invalidateCache_(); - } - if (this.check_discontinuity_) { - // Mark the curve as discontinuous if any 2 keys share the same input and - // if their outputs are different. - this.check_discontinuity_ = false; - var keys_size = this.keys.length; - this.discontinuous_ = (this.num_step_keys_ > 0 && - this.num_step_keys_ != keys_size); - if (!this.discontinuous_ && keys_size > 1) { - for (var ii = 0; ii < keys_size - 1; ++ii) { - if (this.keys[ii].input == this.keys[ii + 1].input && - this.keys[ii].output != this.keys[ii + 1].output) { - this.discontinuous_ = true; - break; - } - } - } - } -}; - -/** - * @param {number} input Guaranteed to be between the first and last key. - * @param {object} context Generic cache to speed up adjacent computations. - * @return {number} Final output value - * @private - */ -o3d.Curve.prototype.getOutputInSpan_ = function(input, context) { - var keys = this.keys; - var keys_size = keys.length; - if (input < keys[0].input) { - this.gl.client.error_callback( - "Curve.getOutputInSpan_: input is lower than any key"); - return 0; - } - - if (input >= keys[keys_size-1].input) { - return keys[keys_size-1].output; - } - - // use the keys directly. - var start = 0; - var end = keys_size; - var key_index; - var found = false; - - var kKeysToSearch = 3; - - // See if the context already has a index to the correct key. - if (context) { - key_index = context.curve_last_key_index_; - // is that index in range. - if (key_index < end - 1) { - // Are we between these keys. - if (keys[key_index].input <= input && - keys[key_index + 1].input > input) { - // Yes! - found = true; - } else { - // No, so check which way we need to go. - if (input > keys[key_index].input) { - // search forward a few keys. If it's not within a few keys give up. - var check_end = key_index + kKeysToSearch; - if (check_end > end) { - check_end = end; - } - for (++key_index; key_index < check_end; ++key_index) { - if (keys[key_index].input <= input && - keys[key_index + 1].input > input) { - // Yes! - found = true; - break; - } - } - } else if (key_index > 0) { - // search backward a few keys. If it's not within a few keys give up. - var check_end = key_index - kKeysToSearch; - if (check_end < 0) { - check_end = 0; - } - for (--key_index; key_index >= check_end; --key_index) { - if (keys[key_index].input <= input && - keys[key_index + 1].input > input) { - // Yes! - found = true; - break; - } - } - } - } - } - } - - if (!found) { - // TODO: If we assume the most common case is sampled keys and - // constant intervals we can make a quick guess where that key is. - - // Find the current the keys that cover our input. - while (start <= end) { - var mid = Math.floor((start + end)/2); - if (input > keys[mid].input) { - start = mid + 1; - } else { - if (mid == 0) { - break; - } - end = mid - 1; - } - } - - end = keys_size; - while (start < end) { - if (keys[start].input > input) { - break; - } - ++start; - } - if (start <= 0 || start >= end) { - this.gl.client.error_callback( - "Curve.getOutputInSpan_: start is outside range."); - } - - key_index = start - 1; - } - - var key = keys[key_index]; - if (context) { - context.curve_last_key_index_ = key_index; - } - if (key_index+1 >= keys_size || !keys[key_index+1]) { - this.gl.client.error_callback( - "Curve.getOutputInSpan_: next key is null: index is "+key_index+ - "; size is "+keys_size); - return key.output; - } else { - return key.getOutputAtOffset(input - key.input, keys[key_index+1]); - } -}; - -/** - * Evaluates a point on this bezier curve corresponding to input. - * - * @param {number} input Input value to evaluate. - * @param {number} context Context of the last evaluation. - * @return {number} output value - */ -o3d.Curve.prototype.evaluate = function(input, context) { - var keys = this.keys; - var keys_size = keys.length; - - if (keys_size == 0) { - return 0.0; - } - - if (keys_size == 1) { - return keys[0].output; - } - - this.updateCurveInfo_(); - - var start_input = keys[0].input; - var end_input = keys[keys_size-1].input; - var input_span = end_input - start_input; - var start_output = keys[0].output; - var end_output = keys[keys_size-1].output; - var output_delta = end_output - start_output; - - var kEpsilon = 0.00001; - - var output_offset = 0.0; - // check for pre-infinity - if (input < start_input) { - if (input_span <= 0.0) { - return start_output; - } - var pre_infinity_offset = start_input - input; - switch (this.preInfinity) { - case o3d.Curve.CONSTANT: - return start_output; - case o3d.Curve.LINEAR: { - var second_key = keys[1]; - var input_delta = second_key.input - start_input; - if (input_delta > kEpsilon) { - return start_output - pre_infinity_offset * - (second_key.output - start_output) / input_delta; - } else { - return start_output; - } - } - case o3d.Curve.CYCLE: { - var cycle_count = Math.ceil(pre_infinity_offset / input_span); - input += cycle_count * input_span; - input = start_input + (input - start_input) % input_span; - break; - } - case o3d.Curve.CYCLE_RELATIVE: { - var cycle_count = Math.ceil(pre_infinity_offset / input_span); - input += cycle_count * input_span; - input = start_input + (input - start_input) % input_span; - output_offset -= cycle_count * output_delta; - break; - } - case o3d.Curve.OSCILLATE: { - var cycle_count = Math.ceil(pre_infinity_offset / (2.0 * input_span)); - input += cycle_count * 2.0 * input_span; - input = end_input - Math.abs(input - end_input); - break; - } - default: - this.gl.client.error_callback( - "Curve: invalid value "+this.preInfinity+"for pre-infinity"); - return start_output; - } - } else if (input >= end_input) { - // check for post-infinity - if (input_span <= 0.0) { - return end_output; - } - var post_infinity_offset = input - end_input; - switch (this.postInfinity) { - case o3d.Curve.CONSTANT: - return end_output; - case o3d.Curve.LINEAR: { - var next_to_last_key = keys[keys_size - 2]; - var input_delta = end_input - next_to_last_key.input; - if (input_delta > kEpsilon) { - return end_output + post_infinity_offset * - (end_output - next_to_last_key.output) / - input_delta; - } else { - return end_output; - } - } - case o3d.Curve.CYCLE: { - var cycle_count = Math.ceil(post_infinity_offset / input_span); - input -= cycle_count * input_span; - input = start_input + (input - start_input) % input_span; - break; - } - case o3d.Curve.CYCLE_RELATIVE: { - var cycle_count = Math.floor((input - start_input) / input_span); - input -= cycle_count * input_span; - input = start_input + (input - start_input) % input_span; - output_offset += cycle_count * output_delta; - break; - } - case o3d.Curve.OSCILLATE: { - var cycle_count = Math.ceil(post_infinity_offset / (2.0 * - input_span)); - input -= cycle_count * 2.0 * input_span; - input = start_input + Math.abs(input - start_input); - break; - } - default: - this.gl.client.error_callback( - "Curve.invalid value "+this.postInfinity+"for post-infinity"); - return end_output; - } - } - - // At this point input should be between start_input and end_input - // inclusive. - - // If we are at end_input then just return end_output since we can't - // interpolate end_input to anything past it. - if (input >= end_input) { - return end_output + output_offset; - } - - // TODO(pathorn): Implement curve cache in javascript. - // See 'void Curve::CreateCache' in o3d/core/cross/curve.cc - - return this.getOutputInSpan_(input, context) + output_offset; -}; |