modify wrong changelog date
[platform/framework/web/web-ui-fw.git] / src / js / widgets / components / motionpath.js
1 //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
2 //>>description: Tizen motion path component for gallery3d
3 //>>label: Motion path
4 //>>group: Tizen:Widgets:Components
5
6 define( [ ], function ( ) {
7 //>>excludeEnd("jqmBuildExclude");
8
9 /* ***************************************************************************
10  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a
13  * copy of this software and associated documentation files (the "Software"),
14  * to deal in the Software without restriction, including without limitation
15  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16  * and/or sell copies of the Software, and to permit persons to whom the
17  * Software is furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included in
20  * all copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28  * DEALINGS IN THE SOFTWARE.
29  * ***************************************************************************
30  *
31  * Authors: Hyunsook Park <hyunsook.park@samsung.com>
32  *                      Wonseop Kim <wonseop.kim@samsung.com>
33 */
34
35 ( function ( $, window, undefined ) {
36         var HALF_PI = Math.PI / 2,
37                 DEFAULT_STEP = 0.001,
38                 MotionPath = {},
39                 vec3 = window.vec3,
40                 arcLength3d = function ( p0, p1 ) {
41                         var d = [ p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2] ],
42                                 value = Math.sqrt( d[0] * d[0] + d[1] * d[1] + d[2] * d[2] );
43                         return value;
44                 };
45
46         MotionPath.base = function () {};
47         MotionPath.base.prototype = {
48                 points: [],
49                 step: DEFAULT_STEP,
50                 length: 0,
51                 levels: [],
52                 init: function ( data ) {},
53                 calculateLevel: function ( maxLevel ) {},
54                 calculateTotalLength: function () {},
55                 getPosition: function ( percent ) {},
56                 getPercent: function ( start, interval ) {},
57                 getAngle: function ( percent ) {}
58         };
59
60         MotionPath.bezier2d = function () {};
61         MotionPath.bezier2d.prototype = $.extend( true, {}, MotionPath.base.prototype, {
62                 init: function ( data ) {
63                         this.points = data.points;
64                         this.step = data.step || DEFAULT_STEP;
65                         this.length = this.calculateTotalLength();
66                         this.levels = this.calculateLevel( data.maxLevel ) || [];
67                 },
68
69                 calculateLevel: function ( maxLevel ) {
70                         var totalLength = this.length,
71                                 interval = totalLength / maxLevel,
72                                 levels = [],
73                                 i;
74
75                         if ( !maxLevel ) {
76                                 return null;
77                         }
78
79                         for ( i = 0; i < maxLevel; i += 1 ) {
80                                 levels[maxLevel - i] = this.getPercent( 0, interval * i );
81                         }
82
83                         return levels;
84                 },
85
86                 calculateTotalLength: function () {
87                         var step = this.step,
88                                 current = this.getPosition( 0 ),
89                                 last = current,
90                                 length = 0,
91                                 percent;
92                         for ( percent = step; percent <= 1; percent += step ) {
93                                 current = this.getPosition( percent );
94                                 length += arcLength3d( last, current );
95                                 last = current;
96                         }
97                         return length;
98                 },
99
100                 getPosition: function ( percent ) {
101                         var points = this.points,
102                                 getValue = function ( p1, c1, c2, p2, t ) {
103                                         return Math.pow(1 - t, 3) * p1 +
104                                                 3 * t * Math.pow( 1 - t, 2 ) * c1 +
105                                                 3 * Math.pow( t, 2 ) * ( 1 - t ) * c2 +
106                                                 Math.pow( t, 3 ) * p2;
107                                 },
108                                 result = [
109                                         getValue( points[0][0], points[1][0], points[2][0], points[3][0], percent ),
110                                         getValue( points[0][2], points[1][2], points[2][2], points[3][2], percent )
111                                 ];
112                         return [ result[0], 0, result[1] ];
113                 },
114
115                 getPercent: function ( start, interval ) {
116                         var step = this.step,
117                                 current = this.getPosition( start = start || 0 ),
118                                 last = current,
119                                 targetLength = start + interval,
120                                 length = 0,
121                                 percent;
122
123                         for ( percent = start + step; percent <= 1; percent += step ) {
124                                 current = this.getPosition( percent );
125                                 length += arcLength3d( last, current );
126                                 if ( length >= targetLength ) {
127                                         return percent;
128                                 }
129                                 last = current;
130                         }
131                         return 1;
132                 },
133
134                 getAngle: function ( percent ) {
135                         var points = this.points,
136                                 getTangent = function ( p1, c1, c2, p2, t ) {
137                                         return 3 * t * t * ( -p1 + 3 * c1 - 3 * c2 + p2 ) + 6 * t * ( p1 - 2 * c1 + c2 ) + 3 * ( -p1 + c1 );
138                                 },
139                                 tx = getTangent( points[0][0], points[1][0], points[2][0], points[3][0], percent ),
140                                 ty = getTangent( points[0][2], points[1][2], points[2][2], points[3][2], percent );
141                         return Math.atan2( tx, ty ) - HALF_PI;
142                 }
143
144         } );
145
146         // clamped cubic B-spline curve
147         // http://web.mit.edu/hyperbook/Patrikalakis-Maekawa-Cho/node17.html
148         // http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/B-spline/bspline-curve-coef.html
149         MotionPath.bspline = function () {};
150         MotionPath.bspline.prototype = $.extend( true, {}, MotionPath.base.prototype, {
151                 _degree: 3,
152                 _numberOfControls : 0,
153                 _knotVectors: [],
154                 _numberOfKnots: 0,
155
156                 init: function ( data ) {
157                         this.points = data.points;
158                         this.step = data.step || DEFAULT_STEP;
159                         this._numberOfPoints = this.points.length - 1;
160                         this._numberOfKnots = this._numberOfPoints + this._degree + 1;
161
162                         var deltaKnot = 1 / ( this._numberOfKnots - ( 2 * this._degree ) ),
163                                 v = deltaKnot,
164                                 i = 0;
165
166                         while ( i <= this._numberOfKnots ) {
167                                 if ( i <= this._degree ) {
168                                         this._knotVectors.push( 0 );
169                                 } else if ( i < this._numberOfKnots - this._degree + 1 ) {
170                                         this._knotVectors.push( v );
171                                         v += deltaKnot;
172                                 } else {
173                                         this._knotVectors.push( 1 );
174                                 }
175                                 i += 1;
176                         }
177
178                         this.length = this.calculateTotalLength();
179                         this.levels = this.calculateLevel( data.maxLevel ) || [];
180                 },
181
182                 _Np: function ( percent, i, degree ) {
183                         var knots = this._knotVectors,
184                                 A = 0,
185                                 B = 0,
186                                 denominator = 0,
187                                 N0 = function ( percent, i ) {
188                                         return ( ( knots[i] <= percent && percent < knots[i + 1] ) ? 1 : 0 );
189                                 };
190
191                         if ( degree === 1 ) {
192                                 A = N0( percent, i );
193                                 B = N0( percent, i + 1 );
194                         } else {
195                                 A = this._Np( percent, i, degree - 1 );
196                                 B = this._Np( percent, i + 1, degree - 1 );
197                         }
198
199                         denominator = knots[i + degree] - knots[i];
200                         A *= ( denominator !== 0 ) ? ( ( percent - knots[i] ) / denominator ) : 0;
201                         denominator = knots[i + degree + 1] - knots[i + 1];
202                         B *= ( denominator !== 0 ) ? ( ( knots[i + degree + 1] - percent ) / denominator ) : 0;
203
204                         return A + B;
205                 },
206
207                 calculateLevel: function ( maxLevel ) {
208                         var totalLength = this.length,
209                                 interval = totalLength / maxLevel,
210                                 levels = [],
211                                 i;
212
213                         if ( !maxLevel ) {
214                                 return null;
215                         }
216
217                         for ( i = 0; i < maxLevel; i += 1 ) {
218                                 levels[maxLevel - i] = this.getPercent( 0, interval * i );
219                         }
220                         return levels;
221                 },
222
223                 calculateTotalLength: function () {
224                         var step = this.step,
225                                 current = this.getPosition( 0 ),
226                                 last = current,
227                                 length = 0,
228                                 percent;
229                         for ( percent = step; percent <= 1; percent += step ) {
230                                 current = this.getPosition( percent );
231                                 length += arcLength3d( last, current );
232                                 last = current;
233                         }
234                         return length;
235                 },
236
237                 getPosition: function ( percent ) {
238                         var result = [], i, j, sum;
239                         percent = percent.toFixed( 4 );
240                         for ( j = 0; j < 3; j += 1 ) {
241                                 sum = 0;
242                                 for ( i = 0; i <= this._numberOfPoints; i += 1 ) {
243                                         sum += this.points[i][j] * this._Np( percent, i, this._degree );
244                                 }
245                                 result[j] = sum;
246                         }
247
248                         return result;
249                 },
250
251                 getPercent: function ( start, interval ) {
252                         var step = this.step,
253                                 current = this.getPosition( start = start || 0 ),
254                                 last = current,
255                                 targetLength = start + interval,
256                                 length = 0,
257                                 percent;
258
259                         for ( percent = start + step; percent <= 1; percent += step ) {
260                                 current = this.getPosition( percent );
261                                 length += arcLength3d( last, current );
262                                 if ( length >= targetLength ) {
263                                         return percent;
264                                 }
265                                 last = current;
266                         }
267                         return 1;
268                 },
269
270                 getAngle: function ( percent ) {
271                         var prev = this.getPosition( percent ),
272                                 next = this.getPosition( percent + 0.001 ),
273                                 dir = vec3.normalize( vec3.direction( prev, next ) ),
274                                 cosValue = vec3.dot( dir, [1, 0, 0] );
275
276                         return Math.acos( cosValue ) + Math.PI;
277                 }
278         } );
279
280         $.motionpath = function ( type, data ) {
281                 var object = new MotionPath[type]();
282                 object.init( data );
283                 return object;
284         };
285 } ( jQuery, window ) );
286
287 //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
288 } );
289 //>>excludeEnd("jqmBuildExclude");