blob: 9cbbaaf41b961befe885cec6cfd93e44c9628463 [file] [log] [blame]
//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
//>>description: Tizen motion path component for gallery3d
//>>label: Motion path
//>>group: Tizen:Widgets:Components
define( [
], function ( ) {
//>>excludeEnd("jqmBuildExclude");
/* ***************************************************************************
* Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
* ***************************************************************************
*
* Authors: Hyunsook Park <hyunsook.park@samsung.com>
* Wonseop Kim <wonseop.kim@samsung.com>
*/
( function ( $, window, undefined ) {
var HALF_PI = Math.PI / 2,
DEFAULT_STEP = 0.001,
MotionPath = {},
vec3 = window.vec3,
arcLength3d = function ( p0, p1 ) {
var d = [ p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2] ],
value = Math.sqrt( d[0] * d[0] + d[1] * d[1] + d[2] * d[2] );
return value;
};
MotionPath.base = function () {};
MotionPath.base.prototype = {
points: [],
step: DEFAULT_STEP,
length: 0,
levels: [],
init: function ( data ) {},
calculateLevel: function ( maxLevel ) {},
calculateTotalLength: function () {},
getPosition: function ( percent ) {},
getPercent: function ( start, interval ) {},
getAngle: function ( percent ) {}
};
MotionPath.bezier2d = function () {};
MotionPath.bezier2d.prototype = $.extend( true, {}, MotionPath.base.prototype, {
init: function ( data ) {
this.points = data.points;
this.step = data.step || DEFAULT_STEP;
this.length = this.calculateTotalLength();
this.levels = this.calculateLevel( data.maxLevel ) || [];
},
calculateLevel: function ( maxLevel ) {
var totalLength = this.length,
interval = totalLength / maxLevel,
levels = [],
i;
if ( !maxLevel ) {
return null;
}
for ( i = 0; i < maxLevel; i += 1 ) {
levels[maxLevel - i] = this.getPercent( 0, interval * i );
}
return levels;
},
calculateTotalLength: function () {
var step = this.step,
current = this.getPosition( 0 ),
last = current,
length = 0,
percent;
for ( percent = step; percent <= 1; percent += step ) {
current = this.getPosition( percent );
length += arcLength3d( last, current );
last = current;
}
return length;
},
getPosition: function ( percent ) {
var points = this.points,
getValue = function ( p1, c1, c2, p2, t ) {
return Math.pow(1 - t, 3) * p1 +
3 * t * Math.pow( 1 - t, 2 ) * c1 +
3 * Math.pow( t, 2 ) * ( 1 - t ) * c2 +
Math.pow( t, 3 ) * p2;
},
result = [
getValue( points[0][0], points[1][0], points[2][0], points[3][0], percent ),
getValue( points[0][2], points[1][2], points[2][2], points[3][2], percent )
];
return [ result[0], 0, result[1] ];
},
getPercent: function ( start, interval ) {
var step = this.step,
current = this.getPosition( start = start || 0 ),
last = current,
targetLength = start + interval,
length = 0,
percent;
for ( percent = start + step; percent <= 1; percent += step ) {
current = this.getPosition( percent );
length += arcLength3d( last, current );
if ( length >= targetLength ) {
return percent;
}
last = current;
}
return 1;
},
getAngle: function ( percent ) {
var points = this.points,
getTangent = function ( p1, c1, c2, p2, t ) {
return 3 * t * t * ( -p1 + 3 * c1 - 3 * c2 + p2 ) + 6 * t * ( p1 - 2 * c1 + c2 ) + 3 * ( -p1 + c1 );
},
tx = getTangent( points[0][0], points[1][0], points[2][0], points[3][0], percent ),
ty = getTangent( points[0][2], points[1][2], points[2][2], points[3][2], percent );
return Math.atan2( tx, ty ) - HALF_PI;
}
} );
// clamped cubic B-spline curve
// http://web.mit.edu/hyperbook/Patrikalakis-Maekawa-Cho/node17.html
// http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/B-spline/bspline-curve-coef.html
MotionPath.bspline = function () {};
MotionPath.bspline.prototype = $.extend( true, {}, MotionPath.base.prototype, {
_degree: 3,
_numberOfControls : 0,
_knotVectors: [],
_numberOfKnots: 0,
init: function ( data ) {
this.points = data.points;
this.step = data.step || DEFAULT_STEP;
this._numberOfPoints = this.points.length - 1;
this._numberOfKnots = this._numberOfPoints + this._degree + 1;
var deltaKnot = 1 / ( this._numberOfKnots - ( 2 * this._degree ) ),
v = deltaKnot,
i = 0;
while ( i <= this._numberOfKnots ) {
if ( i <= this._degree ) {
this._knotVectors.push( 0 );
} else if ( i < this._numberOfKnots - this._degree + 1 ) {
this._knotVectors.push( v );
v += deltaKnot;
} else {
this._knotVectors.push( 1 );
}
i += 1;
}
this.length = this.calculateTotalLength();
this.levels = this.calculateLevel( data.maxLevel ) || [];
},
_Np: function ( percent, i, degree ) {
var knots = this._knotVectors,
A = 0,
B = 0,
denominator = 0,
N0 = function ( percent, i ) {
return ( ( knots[i] <= percent && percent < knots[i + 1] ) ? 1 : 0 );
};
if ( degree === 1 ) {
A = N0( percent, i );
B = N0( percent, i + 1 );
} else {
A = this._Np( percent, i, degree - 1 );
B = this._Np( percent, i + 1, degree - 1 );
}
denominator = knots[i + degree] - knots[i];
A *= ( denominator !== 0 ) ? ( ( percent - knots[i] ) / denominator ) : 0;
denominator = knots[i + degree + 1] - knots[i + 1];
B *= ( denominator !== 0 ) ? ( ( knots[i + degree + 1] - percent ) / denominator ) : 0;
return A + B;
},
calculateLevel: function ( maxLevel ) {
var totalLength = this.length,
interval = totalLength / maxLevel,
levels = [],
i;
if ( !maxLevel ) {
return null;
}
for ( i = 0; i < maxLevel; i += 1 ) {
levels[maxLevel - i] = this.getPercent( 0, interval * i );
}
return levels;
},
calculateTotalLength: function () {
var step = this.step,
current = this.getPosition( 0 ),
last = current,
length = 0,
percent;
for ( percent = step; percent <= 1; percent += step ) {
current = this.getPosition( percent );
length += arcLength3d( last, current );
last = current;
}
return length;
},
getPosition: function ( percent ) {
var result = [], i, j, sum;
percent = percent.toFixed( 4 );
for ( j = 0; j < 3; j += 1 ) {
sum = 0;
for ( i = 0; i <= this._numberOfPoints; i += 1 ) {
sum += this.points[i][j] * this._Np( percent, i, this._degree );
}
result[j] = sum;
}
return result;
},
getPercent: function ( start, interval ) {
var step = this.step,
current = this.getPosition( start = start || 0 ),
last = current,
targetLength = start + interval,
length = 0,
percent;
for ( percent = start + step; percent <= 1; percent += step ) {
current = this.getPosition( percent );
length += arcLength3d( last, current );
if ( length >= targetLength ) {
return percent;
}
last = current;
}
return 1;
},
getAngle: function ( percent ) {
var prev = this.getPosition( percent ),
next = this.getPosition( percent + 0.001 ),
dir = vec3.normalize( vec3.direction( prev, next ) ),
cosValue = vec3.dot( dir, [1, 0, 0] );
return Math.acos( cosValue ) + Math.PI;
}
} );
$.motionpath = function ( type, data ) {
var object = new MotionPath[type]();
object.init( data );
return object;
};
} ( jQuery, window ) );
//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
} );
//>>excludeEnd("jqmBuildExclude");