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  * <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(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 of cc.CardinalSplineTo
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         time = this._computeEaseTime(time);
193         var p, lt;
194         var ps = this._points;
195         // eg.
196         // p..p..p..p..p..p..p
197         // 1..2..3..4..5..6..7
198         // want p to be 1, 2, 3, 4, 5, 6
199         if (time == 1) {
200             p = ps.length - 1;
201             lt = 1;
202         } else {
203             var locDT = this._deltaT;
204             p = 0 | (time / locDT);
205             lt = (time - locDT * p) / locDT;
206         }
207 
208         var newPos = cc.cardinalSplineAt(
209             cc.getControlPointAt(ps, p - 1),
210             cc.getControlPointAt(ps, p - 0),
211             cc.getControlPointAt(ps, p + 1),
212             cc.getControlPointAt(ps, p + 2),
213             this._tension, lt);
214 
215         if (cc.ENABLE_STACKABLE_ACTIONS) {
216             var tempX, tempY;
217             tempX = this.target.getPositionX() - this._previousPosition.x;
218             tempY = this.target.getPositionY() - this._previousPosition.y;
219             if (tempX != 0 || tempY != 0) {
220                 var locAccDiff = this._accumulatedDiff;
221                 tempX = locAccDiff.x + tempX;
222                 tempY = locAccDiff.y + tempY;
223                 locAccDiff.x = tempX;
224                 locAccDiff.y = tempY;
225                 newPos.x += tempX;
226                 newPos.y += tempY;
227             }
228         }
229         this.updatePosition(newPos);
230     },
231 
232     /**
233      * reverse a new cc.CardinalSplineTo
234      * @return {cc.CardinalSplineTo}
235      */
236     reverse:function () {
237         var reversePoints = cc.reverseControlPoints(this._points);
238         return cc.cardinalSplineTo(this._duration, reversePoints, this._tension);
239     },
240 
241     /**
242      * update position of target
243      * @param {cc.Point} newPos
244      */
245     updatePosition:function (newPos) {
246         this.target.setPosition(newPos);
247         this._previousPosition = newPos;
248     },
249 
250     /**
251      * Points getter
252      * @return {Array}
253      */
254     getPoints:function () {
255         return this._points;
256     },
257 
258     /**
259      * Points setter
260      * @param {Array} points
261      */
262     setPoints:function (points) {
263         this._points = points;
264     }
265 });
266 
267 /**
268  * creates an action with a Cardinal Spline array of points and tension
269  * @function
270  * @param {Number} duration
271  * @param {Array} points array of control points
272  * @param {Number} tension
273  * @return {cc.CardinalSplineTo}
274  *
275  * @example
276  * //create a cc.CardinalSplineTo
277  * var action1 = cc.cardinalSplineTo(3, array, 0);
278  */
279 cc.cardinalSplineTo = function (duration, points, tension) {
280     return new cc.CardinalSplineTo(duration, points, tension);
281 };
282 /**
283  * Please use cc.cardinalSplineTo instead
284  * creates an action with a Cardinal Spline array of points and tension
285  * @function
286  * @param {Number} duration
287  * @param {Array} points array of control points
288  * @param {Number} tension
289  * @return {cc.CardinalSplineTo}
290  * @static
291  * @deprecated
292  */
293 cc.CardinalSplineTo.create = cc.cardinalSplineTo;
294 
295 /**
296  * Cardinal Spline path.  http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Cardinal_spline
297  * @class
298  * @extends cc.CardinalSplineTo
299  *
300  * @example
301  * //create a cc.CardinalSplineBy
302  * var action1 = cc.cardinalSplineBy(3, array, 0);
303  */
304 cc.CardinalSplineBy = cc.CardinalSplineTo.extend(/** @lends cc.CardinalSplineBy# */{
305     _startPosition:null,
306 
307 	/**
308 	 * creates an action with a Cardinal Spline array of points and tension
309 	 *
310 	 * Constructor of cc.CardinalSplineBy
311 	 * @param {Number} duration
312 	 * @param {Array} points
313 	 * @param {Number} tension
314 	 */
315     ctor:function (duration, points, tension) {
316         cc.CardinalSplineTo.prototype.ctor.call(this);
317         this._startPosition = cc.p(0, 0);
318 
319 		tension !== undefined && this.initWithDuration(duration, points, tension);
320     },
321 
322     /**
323      * @param {cc.Node} target
324      */
325     startWithTarget:function (target) {
326         cc.CardinalSplineTo.prototype.startWithTarget.call(this, target);
327         this._startPosition.x = target.getPositionX();
328         this._startPosition.y = target.getPositionY();
329     },
330 
331     /**
332      * reverse a new cc.CardinalSplineBy
333      * @return {cc.CardinalSplineBy}
334      */
335     reverse:function () {
336         var copyConfig = this._points.slice();
337         var current;
338         //
339         // convert "absolutes" to "diffs"
340         //
341         var p = copyConfig[0];
342         for (var i = 1; i < copyConfig.length; ++i) {
343             current = copyConfig[i];
344             copyConfig[i] = cc.pSub(current, p);
345             p = current;
346         }
347 
348         // convert to "diffs" to "reverse absolute"
349         var reverseArray = cc.reverseControlPoints(copyConfig);
350 
351         // 1st element (which should be 0,0) should be here too
352         p = reverseArray[ reverseArray.length - 1 ];
353         reverseArray.pop();
354 
355         p.x = -p.x;
356         p.y = -p.y;
357 
358         reverseArray.unshift(p);
359         for (var i = 1; i < reverseArray.length; ++i) {
360             current = reverseArray[i];
361             current.x = -current.x;
362             current.y = -current.y;
363             current.x += p.x;
364             current.y += p.y;
365             reverseArray[i] = current;
366             p = current;
367         }
368         return cc.cardinalSplineBy(this._duration, reverseArray, this._tension);
369     },
370 
371     /**
372      * update position of target
373      * @param {cc.Point} newPos
374      */
375     updatePosition:function (newPos) {
376         var pos = this._startPosition;
377         var posX = newPos.x + pos.x;
378         var posY = newPos.y + pos.y;
379 	    this._previousPosition.x = posX;
380 	    this._previousPosition.y = posY;
381 	    this.target.setPosition(posX, posY);
382     },
383 
384     /**
385      * returns a new clone of the action
386      * @returns {cc.CardinalSplineBy}
387      */
388     clone:function () {
389         var a = new cc.CardinalSplineBy();
390         a.initWithDuration(this._duration, cc.copyControlPoints(this._points), this._tension);
391         return a;
392     }
393 });
394 
395 /**
396  * creates an action with a Cardinal Spline array of points and tension
397  * @function
398  * @param {Number} duration
399  * @param {Array} points
400  * @param {Number} tension
401  * @return {cc.CardinalSplineBy}
402  */
403 cc.cardinalSplineBy = function (duration, points, tension) {
404     return new cc.CardinalSplineBy(duration, points, tension);
405 };
406 /**
407  * Please use cc.cardinalSplineBy instead
408  * creates an action with a Cardinal Spline array of points and tension
409  * @function
410  * @param {Number} duration
411  * @param {Array} points
412  * @param {Number} tension
413  * @return {cc.CardinalSplineBy}
414  * @static
415  * @deprecated
416  */
417 cc.CardinalSplineBy.create = cc.cardinalSplineBy;
418 
419 /**
420  * <p>
421  *   An action that moves the target with a CatmullRom curve to a destination point.<br/>
422  *   A Catmull Rom is a Cardinal Spline with a tension of 0.5.  <br/>
423  *   http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline
424  * </p>
425  * @class
426  * @extends cc.CardinalSplineTo
427  *
428  * @example
429  * var action1 = cc.catmullRomTo(3, array);
430  */
431 cc.CatmullRomTo = cc.CardinalSplineTo.extend(/** @lends cc.CatmullRomTo# */{
432 
433 	/**
434 	 * creates an action with a Cardinal Spline array of points and tension
435 	 *
436 	 * Constructor of cc.CatmullRomTo
437 	 * @param {Number} dt
438 	 * @param {Array} points
439 	 *
440 	 * @example
441 	 * var action1 = new cc.CatmullRomTo(3, array);
442 	 */
443 	ctor: function(dt, points) {
444 		points && this.initWithDuration(dt, points);
445 	},
446 
447     /**
448      * Initializes the action with a duration and an array of points
449      *
450      * @function
451      * @param {Number} dt
452      * @param {Array} points
453      */
454     initWithDuration:function (dt, points) {
455         return cc.CardinalSplineTo.prototype.initWithDuration.call(this, dt, points, 0.5);
456     },
457 
458     /**
459      * returns a new clone of the action
460      * @returns {cc.CatmullRomTo}
461      */
462     clone:function () {
463         var action = new cc.CatmullRomTo();
464         action.initWithDuration(this._duration, cc.copyControlPoints(this._points));
465         return action;
466     }
467 });
468 
469 /**
470  * creates an action with a Cardinal Spline array of points and tension
471  * @function
472  * @param {Number} dt
473  * @param {Array} points
474  * @return {cc.CatmullRomTo}
475  *
476  * @example
477  * var action1 = cc.catmullRomTo(3, array);
478  */
479 cc.catmullRomTo = function (dt, points) {
480     return new cc.CatmullRomTo(dt, points);
481 };
482 /**
483  * Please use cc.catmullRomTo instead
484  * creates an action with a Cardinal Spline array of points and tension
485  * @param {Number} dt
486  * @param {Array} points
487  * @return {cc.CatmullRomTo}
488  * @static
489  * @deprecated
490  */
491 cc.CatmullRomTo.create = cc.catmullRomTo;
492 
493 /**
494  * <p>
495  *   An action that moves the target with a CatmullRom curve by a certain distance.  <br/>
496  *   A Catmull Rom is a Cardinal Spline with a tension of 0.5.<br/>
497  *   http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline
498  * </p>
499  * @class
500  * @extends cc.CardinalSplineBy
501  *
502  * @example
503  * var action1 = cc.catmullRomBy(3, array);
504  */
505 cc.CatmullRomBy = cc.CardinalSplineBy.extend({
506 
507 	/**
508 	 * Creates an action with a Cardinal Spline array of points and tension
509 	 *
510 	 * Constructor of cc.CatmullRomBy
511 	 * @param {Number} dt
512 	 * @param {Array} points
513 	 *
514 	 * @example
515 	 * var action1 = new cc.CatmullRomBy(3, array);
516 	 */
517 	ctor: function(dt, points) {
518 		cc.CardinalSplineBy.prototype.ctor.call(this);
519 		points && this.initWithDuration(dt, points);
520 	},
521 
522     /**
523      * initializes the action with a duration and an array of points
524      *
525      * @function
526      * @param {Number} dt
527      * @param {Array} points
528      */
529     initWithDuration:function (dt, points) {
530         return cc.CardinalSplineTo.prototype.initWithDuration.call(this, dt, points, 0.5);
531     },
532 
533     /**
534      * returns a new clone of the action
535      * @returns {cc.CatmullRomBy}
536      */
537     clone:function () {
538         var action = new cc.CatmullRomBy();
539         action.initWithDuration(this._duration, cc.copyControlPoints(this._points));
540         return action;
541     }
542 });
543 
544 /**
545  * Creates an action with a Cardinal Spline array of points and tension
546  * @function
547  * @param {Number} dt
548  * @param {Array} points
549  * @return {cc.CatmullRomBy}
550  * @example
551  * var action1 = cc.catmullRomBy(3, array);
552  */
553 cc.catmullRomBy = function (dt, points) {
554     return new cc.CatmullRomBy(dt, points);
555 };
556 /**
557  * Please use cc.catmullRomBy instead
558  * Creates an action with a Cardinal Spline array of points and tension
559  * @static
560  * @deprecated
561  */
562 cc.CatmullRomBy.create = cc.catmullRomBy;
563