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.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 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.create(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.create(3, array, 0);
278  */
279 cc.CardinalSplineTo.create = function (duration, points, tension) {
280     return new cc.CardinalSplineTo(duration, points, tension);
281 };
282 
283 /**
284  * Cardinal Spline path.  http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Cardinal_spline
285  * @class
286  * @extends cc.CardinalSplineTo
287  *
288  * @example
289  * //create a cc.CardinalSplineBy
290  * var action1 = cc.CardinalSplineBy.create(3, array, 0);
291  */
292 cc.CardinalSplineBy = cc.CardinalSplineTo.extend(/** @lends cc.CardinalSplineBy# */{
293     _startPosition:null,
294 
295 	/**
296 	 * creates an action with a Cardinal Spline array of points and tension
297 	 *
298 	 * Constructor of cc.CardinalSplineBy
299 	 * @param {Number} duration
300 	 * @param {Array} points
301 	 * @param {Number} tension
302 	 */
303     ctor:function (duration, points, tension) {
304         cc.CardinalSplineTo.prototype.ctor.call(this);
305         this._startPosition = cc.p(0, 0);
306 
307 		tension !== undefined && this.initWithDuration(duration, points, tension);
308     },
309 
310     /**
311      * @param {cc.Node} target
312      */
313     startWithTarget:function (target) {
314         cc.CardinalSplineTo.prototype.startWithTarget.call(this, target);
315         this._startPosition.x = target.getPositionX();
316         this._startPosition.y = target.getPositionY();
317     },
318 
319     /**
320      * reverse a new cc.CardinalSplineBy
321      * @return {cc.CardinalSplineBy}
322      */
323     reverse:function () {
324         var copyConfig = this._points.slice();
325         var current;
326         //
327         // convert "absolutes" to "diffs"
328         //
329         var p = copyConfig[0];
330         for (var i = 1; i < copyConfig.length; ++i) {
331             current = copyConfig[i];
332             copyConfig[i] = cc.pSub(current, p);
333             p = current;
334         }
335 
336         // convert to "diffs" to "reverse absolute"
337         var reverseArray = cc.reverseControlPoints(copyConfig);
338 
339         // 1st element (which should be 0,0) should be here too
340         p = reverseArray[ reverseArray.length - 1 ];
341         reverseArray.pop();
342 
343         p.x = -p.x;
344         p.y = -p.y;
345 
346         reverseArray.unshift(p);
347         for (var i = 1; i < reverseArray.length; ++i) {
348             current = reverseArray[i];
349             current.x = -current.x;
350             current.y = -current.y;
351             current.x += p.x;
352             current.y += p.y;
353             reverseArray[i] = current;
354             p = current;
355         }
356         return cc.CardinalSplineBy.create(this._duration, reverseArray, this._tension);
357     },
358 
359     /**
360      * update position of target
361      * @param {cc.Point} newPos
362      */
363     updatePosition:function (newPos) {
364         var pos = this._startPosition;
365         var posX = newPos.x + pos.x;
366         var posY = newPos.y + pos.y;
367 	    this._previousPosition.x = posX;
368 	    this._previousPosition.y = posY;
369 	    this.target.setPosition(posX, posY);
370     },
371 
372     /**
373      * returns a new clone of the action
374      * @returns {cc.CardinalSplineBy}
375      */
376     clone:function () {
377         var a = new cc.CardinalSplineBy();
378         a.initWithDuration(this._duration, cc.copyControlPoints(this._points), this._tension);
379         return a;
380     }
381 });
382 
383 /**
384  * creates an action with a Cardinal Spline array of points and tension
385  * @function
386  * @param {Number} duration
387  * @param {Array} points
388  * @param {Number} tension
389  * @return {cc.CardinalSplineBy}
390  */
391 cc.CardinalSplineBy.create = function (duration, points, tension) {
392     return new cc.CardinalSplineBy(duration, points, tension);
393 };
394 
395 /**
396  * <p>
397  *   An action that moves the target with a CatmullRom curve to a destination point.<br/>
398  *   A Catmull Rom is a Cardinal Spline with a tension of 0.5.  <br/>
399  *   http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline
400  * </p>
401  * @class
402  * @extends cc.CardinalSplineTo
403  *
404  * @example
405  * var action1 = cc.CatmullRomTo.create(3, array);
406  */
407 cc.CatmullRomTo = cc.CardinalSplineTo.extend(/** @lends cc.CatmullRomTo# */{
408 
409 	/**
410 	 * creates an action with a Cardinal Spline array of points and tension
411 	 *
412 	 * Constructor of cc.CatmullRomTo
413 	 * @param {Number} dt
414 	 * @param {Array} points
415 	 *
416 	 * @example
417 	 * var action1 = new cc.CatmullRomTo(3, array);
418 	 */
419 	ctor: function(dt, points) {
420 		points && this.initWithDuration(dt, points);
421 	},
422 
423     /**
424      * Initializes the action with a duration and an array of points
425      *
426      * @function
427      * @param {Number} dt
428      * @param {Array} points
429      */
430     initWithDuration:function (dt, points) {
431         return cc.CardinalSplineTo.prototype.initWithDuration.call(this, dt, points, 0.5);
432     },
433 
434     /**
435      * returns a new clone of the action
436      * @returns {cc.CatmullRomTo}
437      */
438     clone:function () {
439         var action = new cc.CatmullRomTo();
440         action.initWithDuration(this._duration, cc.copyControlPoints(this._points));
441         return action;
442     }
443 });
444 
445 /**
446  * creates an action with a Cardinal Spline array of points and tension
447  * @param {Number} dt
448  * @param {Array} points
449  * @return {cc.CatmullRomTo}
450  *
451  * @example
452  * var action1 = cc.CatmullRomTo.create(3, array);
453  */
454 cc.CatmullRomTo.create = function (dt, points) {
455     return new cc.CatmullRomTo(dt, points);
456 };
457 
458 /**
459  * <p>
460  *   An action that moves the target with a CatmullRom curve by a certain distance.  <br/>
461  *   A Catmull Rom is a Cardinal Spline with a tension of 0.5.<br/>
462  *   http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline
463  * </p>
464  * @class
465  * @extends cc.CardinalSplineBy
466  *
467  * @example
468  * var action1 = cc.CatmullRomBy.create(3, array);
469  */
470 cc.CatmullRomBy = cc.CardinalSplineBy.extend({
471 
472 	/**
473 	 * Creates an action with a Cardinal Spline array of points and tension
474 	 *
475 	 * Constructor of cc.CatmullRomBy
476 	 * @param {Number} dt
477 	 * @param {Array} points
478 	 *
479 	 * @example
480 	 * var action1 = new cc.CatmullRomBy(3, array);
481 	 */
482 	ctor: function(dt, points) {
483 		cc.CardinalSplineBy.prototype.ctor.call(this);
484 		points && this.initWithDuration(dt, points);
485 	},
486 
487     /**
488      * initializes the action with a duration and an array of points
489      *
490      * @function
491      * @param {Number} dt
492      * @param {Array} points
493      */
494     initWithDuration:function (dt, points) {
495         return cc.CardinalSplineTo.prototype.initWithDuration.call(this, dt, points, 0.5);
496     },
497 
498     /**
499      * returns a new clone of the action
500      * @returns {cc.CatmullRomBy}
501      */
502     clone:function () {
503         var action = new cc.CatmullRomBy();
504         action.initWithDuration(this._duration, cc.copyControlPoints(this._points));
505         return action;
506     }
507 });
508 
509 /**
510  * Creates an action with a Cardinal Spline array of points and tension
511  *
512  * @example
513  * var action1 = cc.CatmullRomBy.create(3, array);
514  */
515 cc.CatmullRomBy.create = function (dt, points) {
516     return new cc.CatmullRomBy(dt, points);
517 };
518