1 /****************************************************************************
  2  Copyright (c) 2010-2012 cocos2d-x.org
  3  Copyright (c) 2008-2010 Ricardo Quesada
  4  Copyright (c) 2011      Zynga Inc.
  5  Copyright (c) 2008 Radu Gruian
  6  Copyright (c) 2011 Vit Valentin
  7 
  8  http://www.cocos2d-x.org
  9 
 10  Permission is hereby granted, free of charge, to any person obtaining a copy
 11  of this software and associated documentation files (the "Software"), to deal
 12  in the Software without restriction, including without limitation the rights
 13  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 14  copies of the Software, and to permit persons to whom the Software is
 15  furnished to do so, subject to the following conditions:
 16 
 17  The above copyright notice and this permission notice shall be included in
 18  all copies or substantial portions of the Software.
 19 
 20  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 21  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 22  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 23  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 24  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 25  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 26  THE SOFTWARE.
 27 
 28  Orignal code by Radu Gruian: http://www.codeproject.com/Articles/30838/Overhauser-Catmull-Rom-Splines-for-Camera-Animatio.So
 29 
 30  Adapted to cocos2d-x by Vit Valentin
 31 
 32  Adapted from cocos2d-x to cocos2d-iphone by Ricardo Quesada
 33  ****************************************************************************/
 34 
 35 /**
 36  * <p>Returns the Cardinal Spline position for a given set of control points, tension and time CatmullRom Spline formula: <br/>
 37  *   s(-ttt + 2tt - t)P1 + s(-ttt + tt)P2 + (2ttt - 3tt + 1)P2 + s(ttt - 2tt + t)P3 + (-2ttt + 3tt)P3 + s(ttt - tt)P4
 38  * </p>
 39  * @function
 40  * @param {cc.Point} p0
 41  * @param {cc.Point} p1
 42  * @param {cc.Point} p2
 43  * @param {cc.Point} p3
 44  * @param {Number} tension
 45  * @param {Number} t
 46  * @return {cc.Point}
 47  */
 48 cc.cardinalSplineAt = function (p0, p1, p2, p3, tension, t) {
 49     var t2 = t * t;
 50     var t3 = t2 * t;
 51 
 52     /*
 53      * Formula: s(-ttt + 2tt - t)P1 + s(-ttt + tt)P2 + (2ttt - 3tt + 1)P2 + s(ttt - 2tt + t)P3 + (-2ttt + 3tt)P3 + s(ttt - tt)P4
 54      */
 55     var s = (1 - tension) / 2;
 56 
 57     var b1 = s * ((-t3 + (2 * t2)) - t);                      // s(-t3 + 2 t2 - t)P1
 58     var b2 = s * (-t3 + t2) + (2 * t3 - 3 * t2 + 1);          // s(-t3 + t2)P2 + (2 t3 - 3 t2 + 1)P2
 59     var b3 = s * (t3 - 2 * t2 + t) + (-2 * t3 + 3 * t2);      // s(t3 - 2 t2 + t)P3 + (-2 t3 + 3 t2)P3
 60     var b4 = s * (t3 - t2);                                   // s(t3 - t2)P4
 61 
 62     var x = (p0.x * b1 + p1.x * b2 + p2.x * b3 + p3.x * b4);
 63     var y = (p0.y * b1 + p1.y * b2 + p2.y * b3 + p3.y * b4);
 64     return cc.p(x, y);
 65 };
 66 
 67 
 68 /**
 69  * returns a new copy of the array reversed.
 70  * @return {Array}
 71  */
 72 cc.reverseControlPoints = function (controlPoints) {
 73     var newArray = [];
 74     for (var i = controlPoints.length - 1; i >= 0; i--) {
 75         newArray.push(cc.p(controlPoints[i].x, controlPoints[i].y));
 76     }
 77     return newArray;
 78 };
 79 
 80 cc.copyControlPoints = function (controlPoints) {
 81     var newArray = [];
 82     for (var i = 0; i < controlPoints.length; i++)
 83         newArray.push(cc.p(controlPoints[i].x, controlPoints[i].y));
 84     return newArray;
 85 };
 86 
 87 /**
 88  * returns a point from the array
 89  * @param {Array} controlPoints
 90  * @param {Number} pos
 91  * @return {Array}
 92  */
 93 cc.getControlPointAt = function (controlPoints, pos) {
 94     var p = Math.min(controlPoints.length - 1, Math.max(pos, 0));
 95     return controlPoints[p];
 96 };
 97 
 98 /**
 99  * reverse the current control point array inline, without generating a new one
100  */
101 cc.reverseControlPointsInline = function (controlPoints) {
102     var len = controlPoints.length;
103     var mid = 0 | (len / 2);
104     for (var i = 0; i < mid; ++i) {
105         var temp = controlPoints[i];
106         controlPoints[i] = controlPoints[len - i - 1];
107         controlPoints[len - i - 1] = temp;
108     }
109 };
110 
111 
112 /**
113  * Cardinal Spline path. http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Cardinal_spline
114  * @class
115  * @extends cc.ActionInterval
116  *
117  * @example
118  * //create a cc.CardinalSplineTo
119  * var action1 = cc.CardinalSplineTo.create(3, array, 0);
120  */
121 cc.CardinalSplineTo = cc.ActionInterval.extend(/** @lends cc.CardinalSplineTo# */{
122     /** Array of control points */
123     _points:null,
124     _deltaT:0,
125     _tension:0,
126     _previousPosition:null,
127     _accumulatedDiff:null,
128 
129 	/**
130 	 * Creates an action with a Cardinal Spline array of points and tension
131 	 *
132 	 * @constructor
133 	 * @param {Number} duration
134 	 * @param {Array} points array of control points
135 	 * @param {Number} tension
136 	 *
137 	 * @example
138 	 * //create a cc.CardinalSplineTo
139 	 * var action1 = new cc.CardinalSplineTo(3, array, 0);
140 	 */
141     ctor: function (duration, points, tension) {
142         cc.ActionInterval.prototype.ctor.call(this);
143 
144         this._points = [];
145 		tension !== undefined && this.initWithDuration(duration, points, tension);
146     },
147 
148     /**
149      * initializes the action with a duration and an array of points
150      * @param {Number} duration
151      * @param {Array} points array of control points
152      * @param {Number} tension
153      * @return {Boolean}
154      */
155     initWithDuration:function (duration, points, tension) {
156         if(!points || points.length == 0)
157             throw "Invalid configuration. It must at least have one control point";
158 
159         if (cc.ActionInterval.prototype.initWithDuration.call(this, duration)) {
160             this.setPoints(points);
161             this._tension = tension;
162             return true;
163         }
164         return false;
165     },
166 
167     /**
168      * returns a new clone of the action
169      * @returns {cc.CardinalSplineTo}
170      */
171     clone:function () {
172         var action = new cc.CardinalSplineTo();
173         action.initWithDuration(this._duration, cc.copyControlPoints(this._points), this._tension);
174         return action;
175     },
176 
177     /**
178      * @param {cc.Node} target
179      */
180     startWithTarget:function (target) {
181         cc.ActionInterval.prototype.startWithTarget.call(this, target);
182         // Issue #1441 from cocos2d-iphone
183         this._deltaT = 1 / (this._points.length - 1);
184         this._previousPosition = cc.p(this.target.getPositionX(), this.target.getPositionY());
185         this._accumulatedDiff = cc.p(0, 0);
186     },
187 
188     /**
189      * @param {Number} time
190      */
191     update:function (time) {
192         var p, lt;
193         var ps = this._points;
194         // eg.
195         // p..p..p..p..p..p..p
196         // 1..2..3..4..5..6..7
197         // want p to be 1, 2, 3, 4, 5, 6
198         if (time == 1) {
199             p = ps.length - 1;
200             lt = 1;
201         } else {
202             var locDT = this._deltaT;
203             p = 0 | (time / locDT);
204             lt = (time - locDT * p) / locDT;
205         }
206 
207         var newPos = cc.cardinalSplineAt(
208             cc.getControlPointAt(ps, p - 1),
209             cc.getControlPointAt(ps, p - 0),
210             cc.getControlPointAt(ps, p + 1),
211             cc.getControlPointAt(ps, p + 2),
212             this._tension, lt);
213 
214         if (cc.ENABLE_STACKABLE_ACTIONS) {
215             var tempX, tempY;
216             tempX = this.target.getPositionX() - this._previousPosition.x;
217             tempY = this.target.getPositionY() - this._previousPosition.y;
218             if (tempX != 0 || tempY != 0) {
219                 var locAccDiff = this._accumulatedDiff;
220                 tempX = locAccDiff.x + tempX;
221                 tempY = locAccDiff.y + tempY;
222                 locAccDiff.x = tempX;
223                 locAccDiff.y = tempY;
224                 newPos.x += tempX;
225                 newPos.y += tempY;
226             }
227         }
228         this.updatePosition(newPos);
229     },
230 
231     /**
232      * reverse a new cc.CardinalSplineTo
233      * @return {cc.CardinalSplineTo}
234      */
235     reverse:function () {
236         var reversePoints = cc.reverseControlPoints(this._points);
237         return cc.CardinalSplineTo.create(this._duration, reversePoints, this._tension);
238     },
239 
240     /**
241      * update position of target
242      * @param {cc.Point} newPos
243      */
244     updatePosition:function (newPos) {
245         this.target.setPosition(newPos);
246         this._previousPosition = newPos;
247     },
248 
249     /**
250      * Points getter
251      * @return {Array}
252      */
253     getPoints:function () {
254         return this._points;
255     },
256 
257     /**
258      * Points setter
259      * @param {Array} points
260      */
261     setPoints:function (points) {
262         this._points = points;
263     }
264 });
265 
266 /**
267  * creates an action with a Cardinal Spline array of points and tension
268  * @function
269  * @param {Number} duration
270  * @param {Array} points array of control points
271  * @param {Number} tension
272  * @return {cc.CardinalSplineTo}
273  *
274  * @example
275  * //create a cc.CardinalSplineTo
276  * var action1 = cc.CardinalSplineTo.create(3, array, 0);
277  */
278 cc.CardinalSplineTo.create = function (duration, points, tension) {
279     return new cc.CardinalSplineTo(duration, points, tension);
280 };
281 
282 /**
283  * Cardinal Spline path.  http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Cardinal_spline
284  * @class
285  * @extends cc.CardinalSplineTo
286  *
287  * @example
288  * //create a cc.CardinalSplineBy
289  * var action1 = cc.CardinalSplineBy.create(3, array, 0);
290  */
291 cc.CardinalSplineBy = cc.CardinalSplineTo.extend(/** @lends cc.CardinalSplineBy# */{
292     _startPosition:null,
293 
294 	/**
295 	 * creates an action with a Cardinal Spline array of points and tension
296 	 *
297 	 * @constructor
298 	 * @param {Number} duration
299 	 * @param {Array} points
300 	 * @param {Number} tension
301 	 */
302     ctor:function (duration, points, tension) {
303         cc.CardinalSplineTo.prototype.ctor.call(this);
304         this._startPosition = cc.p(0, 0);
305 
306 		tension !== undefined && this.initWithDuration(duration, points, tension);
307     },
308 
309     /**
310      * @param {cc.Node} target
311      */
312     startWithTarget:function (target) {
313         cc.CardinalSplineTo.prototype.startWithTarget.call(this, target);
314         this._startPosition.x = target.getPositionX();
315         this._startPosition.y = target.getPositionY();
316     },
317 
318     /**
319      * reverse a new cc.CardinalSplineBy
320      * @return {cc.CardinalSplineBy}
321      */
322     reverse:function () {
323         var copyConfig = this._points.slice();
324         var current;
325         //
326         // convert "absolutes" to "diffs"
327         //
328         var p = copyConfig[0];
329         for (var i = 1; i < copyConfig.length; ++i) {
330             current = copyConfig[i];
331             copyConfig[i] = cc.pSub(current, p);
332             p = current;
333         }
334 
335         // convert to "diffs" to "reverse absolute"
336         var reverseArray = cc.reverseControlPoints(copyConfig);
337 
338         // 1st element (which should be 0,0) should be here too
339         p = reverseArray[ reverseArray.length - 1 ];
340         reverseArray.pop();
341 
342         p.x = -p.x;
343         p.y = -p.y;
344 
345         reverseArray.unshift(p);
346         for (var i = 1; i < reverseArray.length; ++i) {
347             current = reverseArray[i];
348             current.x = -current.x;
349             current.y = -current.y;
350             current.x += p.x;
351             current.y += p.y;
352             reverseArray[i] = current;
353             p = current;
354         }
355         return cc.CardinalSplineBy.create(this._duration, reverseArray, this._tension);
356     },
357 
358     /**
359      * update position of target
360      * @param {cc.Point} newPos
361      */
362     updatePosition:function (newPos) {
363         var pos = this._startPosition;
364         var posX = newPos.x + pos.x;
365         var posY = newPos.y + pos.y;
366 	    this._previousPosition.x = posX;
367 	    this._previousPosition.y = posY;
368 	    this.target.setPosition(posX, posY);
369     },
370 
371     /**
372      * returns a new clone of the action
373      * @returns {cc.CardinalSplineBy}
374      */
375     clone:function () {
376         var a = new cc.CardinalSplineBy();
377         a.initWithDuration(this._duration, cc.copyControlPoints(this._points), this._tension);
378         return a;
379     }
380 });
381 
382 /**
383  * creates an action with a Cardinal Spline array of points and tension
384  * @function
385  * @param {Number} duration
386  * @param {Array} points
387  * @param {Number} tension
388  * @return {cc.CardinalSplineBy}
389  */
390 cc.CardinalSplineBy.create = function (duration, points, tension) {
391     return new cc.CardinalSplineBy(duration, points, tension);
392 };
393 
394 /**
395  * <p>
396  *   An action that moves the target with a CatmullRom curve to a destination point.<br/>
397  *   A Catmull Rom is a Cardinal Spline with a tension of 0.5.  <br/>
398  *   http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline
399  * </p>
400  * @class
401  * @extends cc.CardinalSplineTo
402  *
403  * @example
404  * var action1 = cc.CatmullRomTo.create(3, array);
405  */
406 cc.CatmullRomTo = cc.CardinalSplineTo.extend(/** @lends cc.CatmullRomTo# */{
407 
408 	/**
409 	 * creates an action with a Cardinal Spline array of points and tension
410 	 *
411 	 * @constructor
412 	 * @param {Number} dt
413 	 * @param {Array} points
414 	 *
415 	 * @example
416 	 * var action1 = new cc.CatmullRomTo(3, array);
417 	 */
418 	ctor: function(dt, points) {
419 		points && this.initWithDuration(dt, points);
420 	},
421 
422     /**
423      * Initializes the action with a duration and an array of points
424      *
425      * @function
426      * @param {Number} dt
427      * @param {Array} points
428      */
429     initWithDuration:function (dt, points) {
430         return cc.CardinalSplineTo.prototype.initWithDuration.call(this, dt, points, 0.5);
431     },
432 
433     /**
434      * returns a new clone of the action
435      * @returns {cc.CatmullRomTo}
436      */
437     clone:function () {
438         var action = new cc.CatmullRomTo();
439         action.initWithDuration(this._duration, cc.copyControlPoints(this._points));
440         return action;
441     }
442 });
443 
444 /**
445  * creates an action with a Cardinal Spline array of points and tension
446  * @param {Number} dt
447  * @param {Array} points
448  * @return {cc.CatmullRomTo}
449  *
450  * @example
451  * var action1 = cc.CatmullRomTo.create(3, array);
452  */
453 cc.CatmullRomTo.create = function (dt, points) {
454     return new cc.CatmullRomTo(dt, points);
455 };
456 
457 /**
458  * <p>
459  *   An action that moves the target with a CatmullRom curve by a certain distance.  <br/>
460  *   A Catmull Rom is a Cardinal Spline with a tension of 0.5.<br/>
461  *   http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline
462  * </p>
463  * @class
464  * @extends cc.CardinalSplineBy
465  *
466  * @example
467  * var action1 = cc.CatmullRomBy.create(3, array);
468  */
469 cc.CatmullRomBy = cc.CardinalSplineBy.extend({
470 
471 	/**
472 	 * Creates an action with a Cardinal Spline array of points and tension
473 	 *
474 	 * @constructor
475 	 * @param {Number} dt
476 	 * @param {Array} points
477 	 *
478 	 * @example
479 	 * var action1 = new cc.CatmullRomBy(3, array);
480 	 */
481 	ctor: function(dt, points) {
482 		cc.CardinalSplineBy.prototype.ctor.call(this);
483 		points && this.initWithDuration(dt, points);
484 	},
485 
486     /**
487      * initializes the action with a duration and an array of points
488      *
489      * @function
490      * @param {Number} dt
491      * @param {Array} points
492      */
493     initWithDuration:function (dt, points) {
494         return cc.CardinalSplineTo.prototype.initWithDuration.call(this, dt, points, 0.5);
495     },
496 
497     /**
498      * returns a new clone of the action
499      * @returns {cc.CatmullRomBy}
500      */
501     clone:function () {
502         var action = new cc.CatmullRomBy();
503         action.initWithDuration(this._duration, cc.copyControlPoints(this._points));
504         return action;
505     }
506 });
507 
508 /**
509  * Creates an action with a Cardinal Spline array of points and tension
510  *
511  * @example
512  * var action1 = cc.CatmullRomBy.create(3, array);
513  */
514 cc.CatmullRomBy.create = function (dt, points) {
515     return new cc.CatmullRomBy(dt, points);
516 };
517