1 /****************************************************************************
  2  Copyright (c) 2008-2010 Ricardo Quesada
  3  Copyright (c) 2011-2012 cocos2d-x.org
  4  Copyright (c) 2013-2014 Chukong Technologies 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  * Returns the Cardinal Spline position for a given set of control points, tension and time. <br />
 37  * CatmullRom Spline formula. <br />
 38  * 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
 39  *
 40  * @function
 41  * @param {cc.Point} p0
 42  * @param {cc.Point} p1
 43  * @param {cc.Point} p2
 44  * @param {cc.Point} p3
 45  * @param {Number} tension
 46  * @param {Number} t
 47  * @return {cc.Point}
 48  */
 49 cc.cardinalSplineAt = function (p0, p1, p2, p3, tension, t) {
 50     var t2 = t * t;
 51     var t3 = t2 * t;
 52 
 53     /*
 54      * 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
 55      */
 56     var s = (1 - tension) / 2;
 57 
 58     var b1 = s * ((-t3 + (2 * t2)) - t);                      // s(-t3 + 2 t2 - t)P1
 59     var b2 = s * (-t3 + t2) + (2 * t3 - 3 * t2 + 1);          // s(-t3 + t2)P2 + (2 t3 - 3 t2 + 1)P2
 60     var b3 = s * (t3 - 2 * t2 + t) + (-2 * t3 + 3 * t2);      // s(t3 - 2 t2 + t)P3 + (-2 t3 + 3 t2)P3
 61     var b4 = s * (t3 - t2);                                   // s(t3 - t2)P4
 62 
 63     var x = (p0.x * b1 + p1.x * b2 + p2.x * b3 + p3.x * b4);
 64     var y = (p0.y * b1 + p1.y * b2 + p2.y * b3 + p3.y * b4);
 65     return cc.p(x, y);
 66 };
 67 
 68 /**
 69  * returns a new copy of the array reversed.
 70  *
 71  * @return {Array}
 72  */
 73 cc.reverseControlPoints = function (controlPoints) {
 74     var newArray = [];
 75     for (var i = controlPoints.length - 1; i >= 0; i--) {
 76         newArray.push(cc.p(controlPoints[i].x, controlPoints[i].y));
 77     }
 78     return newArray;
 79 };
 80 
 81 
 82 /**
 83  * returns a new clone of the controlPoints
 84  *
 85  * @param controlPoints
 86  * @returns {Array}
 87  */
 88 cc.cloneControlPoints = function (controlPoints) {
 89     var newArray = [];
 90     for (var i = 0; i < controlPoints.length; i++)
 91         newArray.push(cc.p(controlPoints[i].x, controlPoints[i].y));
 92     return newArray;
 93 };
 94 
 95 /**
 96  * returns a new clone of the controlPoints
 97  * @deprecated since v3.0 please use cc.cloneControlPoints() instead.
 98  * @param controlPoints
 99  * @returns {Array}
100  */
101 cc.copyControlPoints = cc.cloneControlPoints;
102 
103 /**
104  * returns a point from the array
105  *
106  * @param {Array} controlPoints
107  * @param {Number} pos
108  * @return {Array}
109  */
110 cc.getControlPointAt = function (controlPoints, pos) {
111     var p = Math.min(controlPoints.length - 1, Math.max(pos, 0));
112     return controlPoints[p];
113 };
114 
115 /**
116  * reverse the current control point array inline, without generating a new one <br />
117  *
118  * @param controlPoints
119  */
120 cc.reverseControlPointsInline = function (controlPoints) {
121     var len = controlPoints.length;
122     var mid = 0 | (len / 2);
123     for (var i = 0; i < mid; ++i) {
124         var temp = controlPoints[i];
125         controlPoints[i] = controlPoints[len - i - 1];
126         controlPoints[len - i - 1] = temp;
127     }
128 };
129 
130 
131 /**
132  * Cardinal Spline path. {@link http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Cardinal_spline}
133  * Absolute coordinates.
134  *
135  * @class
136  * @extends cc.ActionInterval
137  * @param {Number} duration
138  * @param {Array} points array of control points
139  * @param {Number} tension
140  *
141  * @example
142  * //create a cc.CardinalSplineTo
143  * var action1 = cc.cardinalSplineTo(3, array, 0);
144  */
145 cc.CardinalSplineTo = cc.ActionInterval.extend(/** @lends cc.CardinalSplineTo# */{
146     /** Array of control points */
147     _points:null,
148     _deltaT:0,
149     _tension:0,
150     _previousPosition:null,
151     _accumulatedDiff:null,
152 
153 	/**
154      * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. <br />
155 	 * Creates an action with a Cardinal Spline array of points and tension.
156 	 * @param {Number} duration
157 	 * @param {Array} points array of control points
158 	 * @param {Number} tension
159 	 */
160     ctor: function (duration, points, tension) {
161         cc.ActionInterval.prototype.ctor.call(this);
162 
163         this._points = [];
164 		tension !== undefined && this.initWithDuration(duration, points, tension);
165     },
166 
167     /**
168      * initializes the action with a duration and an array of points
169      *
170      * @param {Number} duration
171      * @param {Array} points array of control points
172      * @param {Number} tension
173      *
174      * @return {Boolean}
175      */
176     initWithDuration:function (duration, points, tension) {
177         if(!points || points.length == 0)
178             throw "Invalid configuration. It must at least have one control point";
179 
180         if (cc.ActionInterval.prototype.initWithDuration.call(this, duration)) {
181             this.setPoints(points);
182             this._tension = tension;
183             return true;
184         }
185         return false;
186     },
187 
188     /**
189      * returns a new clone of the action
190      *
191      * @returns {cc.CardinalSplineTo}
192      */
193     clone:function () {
194         var action = new cc.CardinalSplineTo();
195         action.initWithDuration(this._duration, cc.copyControlPoints(this._points), this._tension);
196         return action;
197     },
198 
199     /**
200      * called before the action start. It will also set the target.
201      *
202      * @param {cc.Node} target
203      */
204     startWithTarget:function (target) {
205         cc.ActionInterval.prototype.startWithTarget.call(this, target);
206         // Issue #1441 from cocos2d-iphone
207         this._deltaT = 1 / (this._points.length - 1);
208         this._previousPosition = cc.p(this.target.getPositionX(), this.target.getPositionY());
209         this._accumulatedDiff = cc.p(0, 0);
210     },
211 
212     /**
213      * Called once per frame. Time is the number of seconds of a frame interval.
214      *
215      * @param {Number}  dt
216      */
217     update:function (dt) {
218         dt = this._computeEaseTime(dt);
219         var p, lt;
220         var ps = this._points;
221         // eg.
222         // p..p..p..p..p..p..p
223         // 1..2..3..4..5..6..7
224         // want p to be 1, 2, 3, 4, 5, 6
225         if (dt == 1) {
226             p = ps.length - 1;
227             lt = 1;
228         } else {
229             var locDT = this._deltaT;
230             p = 0 | (dt / locDT);
231             lt = (dt - locDT * p) / locDT;
232         }
233 
234         var newPos = cc.cardinalSplineAt(
235             cc.getControlPointAt(ps, p - 1),
236             cc.getControlPointAt(ps, p - 0),
237             cc.getControlPointAt(ps, p + 1),
238             cc.getControlPointAt(ps, p + 2),
239             this._tension, lt);
240 
241         if (cc.ENABLE_STACKABLE_ACTIONS) {
242             var tempX, tempY;
243             tempX = this.target.getPositionX() - this._previousPosition.x;
244             tempY = this.target.getPositionY() - this._previousPosition.y;
245             if (tempX != 0 || tempY != 0) {
246                 var locAccDiff = this._accumulatedDiff;
247                 tempX = locAccDiff.x + tempX;
248                 tempY = locAccDiff.y + tempY;
249                 locAccDiff.x = tempX;
250                 locAccDiff.y = tempY;
251                 newPos.x += tempX;
252                 newPos.y += tempY;
253             }
254         }
255         this.updatePosition(newPos);
256     },
257 
258     /**
259      * reverse a new cc.CardinalSplineTo. <br />
260      * Along the track of movement in the opposite.
261      *
262      * @return {cc.CardinalSplineTo}
263      */
264     reverse:function () {
265         var reversePoints = cc.reverseControlPoints(this._points);
266         return cc.cardinalSplineTo(this._duration, reversePoints, this._tension);
267     },
268 
269     /**
270      * update position of target
271      *
272      * @param {cc.Point} newPos
273      */
274     updatePosition:function (newPos) {
275         this.target.setPosition(newPos);
276         this._previousPosition = newPos;
277     },
278 
279     /**
280      * Points getter
281      *
282      * @return {Array}
283      */
284     getPoints:function () {
285         return this._points;
286     },
287 
288     /**
289      * Points setter
290      *
291      * @param {Array} points
292      */
293     setPoints:function (points) {
294         this._points = points;
295     }
296 });
297 
298 /**
299  * creates an action with a Cardinal Spline array of points and tension.
300  *
301  * @function
302  * @param {Number} duration
303  * @param {Array} points array of control points
304  * @param {Number} tension
305  * @return {cc.CardinalSplineTo}
306  *
307  * @example
308  * //create a cc.CardinalSplineTo
309  * var action1 = cc.cardinalSplineTo(3, array, 0);
310  */
311 cc.cardinalSplineTo = function (duration, points, tension) {
312     return new cc.CardinalSplineTo(duration, points, tension);
313 };
314 
315 /**
316  * Please use cc.cardinalSplineTo instead. <br />
317  * creates an action with a Cardinal Spline array of points and tension
318  *
319  * @function
320  * @param {Number} duration
321  * @param {Array} points array of control points
322  * @param {Number} tension
323  * @return {cc.CardinalSplineTo}
324  * @static
325  * @deprecated since v3.0 please use cc.cardinalSplineTo(duration, points, tension) instead.
326  */
327 cc.CardinalSplineTo.create = cc.cardinalSplineTo;
328 
329 /**
330  * Cardinal Spline path. {@link http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Cardinal_spline}
331  * Relative coordinates.
332  *
333  * @class
334  * @extends cc.CardinalSplineTo
335  * @param {Number} duration
336  * @param {Array} points
337  * @param {Number} tension
338  *
339  * @example
340  * //create a cc.CardinalSplineBy
341  * var action1 = cc.cardinalSplineBy(3, array, 0);
342  */
343 cc.CardinalSplineBy = cc.CardinalSplineTo.extend(/** @lends cc.CardinalSplineBy# */{
344     _startPosition:null,
345 
346 	/**
347      * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. <br />
348 	 * creates an action with a Cardinal Spline array of points and tension.
349 	 * @param {Number} duration
350 	 * @param {Array} points
351 	 * @param {Number} tension
352 	 */
353     ctor:function (duration, points, tension) {
354         cc.CardinalSplineTo.prototype.ctor.call(this);
355         this._startPosition = cc.p(0, 0);
356 
357 		tension !== undefined && this.initWithDuration(duration, points, tension);
358     },
359 
360     /**
361      * called before the action start. It will also set the target.
362      *
363      * @param {cc.Node} target
364      */
365     startWithTarget:function (target) {
366         cc.CardinalSplineTo.prototype.startWithTarget.call(this, target);
367         this._startPosition.x = target.getPositionX();
368         this._startPosition.y = target.getPositionY();
369     },
370 
371     /**
372      * reverse a new cc.CardinalSplineBy
373      *
374      * @return {cc.CardinalSplineBy}
375      */
376     reverse:function () {
377         var copyConfig = this._points.slice();
378         var current;
379         //
380         // convert "absolutes" to "diffs"
381         //
382         var p = copyConfig[0];
383         for (var i = 1; i < copyConfig.length; ++i) {
384             current = copyConfig[i];
385             copyConfig[i] = cc.pSub(current, p);
386             p = current;
387         }
388 
389         // convert to "diffs" to "reverse absolute"
390         var reverseArray = cc.reverseControlPoints(copyConfig);
391 
392         // 1st element (which should be 0,0) should be here too
393         p = reverseArray[ reverseArray.length - 1 ];
394         reverseArray.pop();
395 
396         p.x = -p.x;
397         p.y = -p.y;
398 
399         reverseArray.unshift(p);
400         for (var i = 1; i < reverseArray.length; ++i) {
401             current = reverseArray[i];
402             current.x = -current.x;
403             current.y = -current.y;
404             current.x += p.x;
405             current.y += p.y;
406             reverseArray[i] = current;
407             p = current;
408         }
409         return cc.cardinalSplineBy(this._duration, reverseArray, this._tension);
410     },
411 
412     /**
413      * update position of target
414      *
415      * @param {cc.Point} newPos
416      */
417     updatePosition:function (newPos) {
418         var pos = this._startPosition;
419         var posX = newPos.x + pos.x;
420         var posY = newPos.y + pos.y;
421 	    this._previousPosition.x = posX;
422 	    this._previousPosition.y = posY;
423 	    this.target.setPosition(posX, posY);
424     },
425 
426     /**
427      * returns a new clone of the action
428      *
429      * @returns {cc.CardinalSplineBy}
430      */
431     clone:function () {
432         var a = new cc.CardinalSplineBy();
433         a.initWithDuration(this._duration, cc.copyControlPoints(this._points), this._tension);
434         return a;
435     }
436 });
437 
438 /**
439  * creates an action with a Cardinal Spline array of points and tension.
440  *
441  * @function
442  * @param {Number} duration
443  * @param {Array} points
444  * @param {Number} tension
445  *
446  * @return {cc.CardinalSplineBy}
447  */
448 cc.cardinalSplineBy = function (duration, points, tension) {
449     return new cc.CardinalSplineBy(duration, points, tension);
450 };
451 
452 /**
453  * Please use cc.cardinalSplineBy instead.
454  * creates an action with a Cardinal Spline array of points and tension.
455  * @function
456  * @param {Number} duration
457  * @param {Array} points
458  * @param {Number} tension
459  * @return {cc.CardinalSplineBy}
460  * @static
461  * @deprecated since v3.0 please use cc.cardinalSplineBy(duration, points, tension);
462  */
463 cc.CardinalSplineBy.create = cc.cardinalSplineBy;
464 
465 /**
466  * An action that moves the target with a CatmullRom curve to a destination point.<br/>
467  * A Catmull Rom is a Cardinal Spline with a tension of 0.5.  <br/>
468  * {@link http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline}
469  * Absolute coordinates.
470  *
471  * @class
472  * @extends cc.CardinalSplineTo
473  * @param {Number} dt
474  * @param {Array} points
475  *
476  * @example
477  * var action1 = cc.catmullRomTo(3, array);
478  */
479 cc.CatmullRomTo = cc.CardinalSplineTo.extend(/** @lends cc.CatmullRomTo# */{
480 
481 	/**
482      * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. <br />
483 	 * creates an action with a Cardinal Spline array of points and tension.
484 	 * @param {Number} dt
485 	 * @param {Array} points
486 	 */
487 	ctor: function(dt, points) {
488 		points && this.initWithDuration(dt, points);
489 	},
490 
491     /**
492      * Initializes the action with a duration and an array of points
493      *
494      * @param {Number} dt
495      * @param {Array} points
496      */
497     initWithDuration:function (dt, points) {
498         return cc.CardinalSplineTo.prototype.initWithDuration.call(this, dt, points, 0.5);
499     },
500 
501     /**
502      * returns a new clone of the action
503      * @returns {cc.CatmullRomTo}
504      */
505     clone:function () {
506         var action = new cc.CatmullRomTo();
507         action.initWithDuration(this._duration, cc.copyControlPoints(this._points));
508         return action;
509     }
510 });
511 
512 /**
513  * creates an action with a Cardinal Spline array of points and tension.
514  *
515  * @function
516  * @param {Number} dt
517  * @param {Array} points
518  * @return {cc.CatmullRomTo}
519  *
520  * @example
521  * var action1 = cc.catmullRomTo(3, array);
522  */
523 cc.catmullRomTo = function (dt, points) {
524     return new cc.CatmullRomTo(dt, points);
525 };
526 /**
527  * Please use cc.catmullRomTo instead.
528  * creates an action with a Cardinal Spline array of points and tension.
529  *
530  * @param {Number} dt
531  * @param {Array} points
532  * @return {cc.CatmullRomTo}
533  * @static
534  * @deprecated since v3.0 please use cc.catmullRomTo(dt, points) instead.
535  */
536 cc.CatmullRomTo.create = cc.catmullRomTo;
537 
538 /**
539  * An action that moves the target with a CatmullRom curve by a certain distance.  <br/>
540  * A Catmull Rom is a Cardinal Spline with a tension of 0.5.<br/>
541  * http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline
542  * Relative coordinates.
543  *
544  * @class
545  * @extends cc.CardinalSplineBy
546  * @param {Number} dt
547  * @param {Array} points
548  *
549  * @example
550  * var action1 = cc.catmullRomBy(3, array);
551  */
552 cc.CatmullRomBy = cc.CardinalSplineBy.extend({
553 
554 	/**
555      * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. <br />
556 	 * Creates an action with a Cardinal Spline array of points and tension.
557 	 * @param {Number} dt
558 	 * @param {Array} points
559 	 */
560 	ctor: function(dt, points) {
561 		cc.CardinalSplineBy.prototype.ctor.call(this);
562 		points && this.initWithDuration(dt, points);
563 	},
564 
565     /**
566      * initializes the action with a duration and an array of points
567      *
568      * @function
569      * @param {Number} dt
570      * @param {Array} points
571      */
572     initWithDuration:function (dt, points) {
573         return cc.CardinalSplineTo.prototype.initWithDuration.call(this, dt, points, 0.5);
574     },
575 
576     /**
577      * returns a new clone of the action
578      * @returns {cc.CatmullRomBy}
579      */
580     clone:function () {
581         var action = new cc.CatmullRomBy();
582         action.initWithDuration(this._duration, cc.copyControlPoints(this._points));
583         return action;
584     }
585 });
586 
587 /**
588  * Creates an action with a Cardinal Spline array of points and tension
589  * @function
590  * @param {Number} dt
591  * @param {Array} points
592  * @return {cc.CatmullRomBy}
593  * @example
594  * var action1 = cc.catmullRomBy(3, array);
595  */
596 cc.catmullRomBy = function (dt, points) {
597     return new cc.CatmullRomBy(dt, points);
598 };
599 /**
600  * Please use cc.catmullRomBy instead
601  * Creates an action with a Cardinal Spline array of points and tension
602  * @static
603  * @deprecated since v3.0 please cc.catmullRomBy(dt, points) instead.
604  */
605 cc.CatmullRomBy.create = cc.catmullRomBy;
606