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 /**
 81  * returns a point from the array
 82  * @return {Array}
 83  */
 84 cc.getControlPointAt = function( controlPoints, pos ) {
 85     var p = Math.min( controlPoints.length-1, Math.max(pos,0));
 86     return controlPoints[p];
 87 };
 88 
 89 
 90 /**
 91  * reverse the current control point array inline, without generating a new one
 92  */
 93 cc.reverseControlPointsInline = function (controlPoints) {
 94     var len = controlPoints.length;
 95     var mid = 0 | (len / 2);
 96     for (var i = 0; i < mid; ++i) {
 97         var temp = controlPoints[i];
 98         controlPoints[i] = controlPoints[len - i - 1];
 99         controlPoints[len - i - 1] = temp;
100     }
101 },
102 
103 
104 /**
105  * Cardinal Spline path. http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Cardinal_spline
106  * @class
107  * @extends cc.ActionInterval
108  *
109  * @example
110  * //create a cc.CardinalSplineTo
111  * var action1 = cc.CardinalSplineTo.create(3, array, 0);
112  */
113 cc.CardinalSplineTo = cc.ActionInterval.extend(/** @lends cc.CardinalSplineTo# */{
114     /** Array of control points */
115     _points:null,
116     _deltaT:0,
117     _tension:0,
118 
119     /**
120      * Constructor
121      */
122     ctor:function () {
123         this._points = [];
124     },
125 
126     /**
127      * initializes the action with a duration and an array of points
128      * @param {Number} duration
129      * @param {Array} points array of control points
130      * @param {Number} tension
131      * @return {Boolean}
132      */
133     initWithDuration:function (duration, points, tension) {
134         cc.Assert(points.length > 0, "Invalid configuration. It must at least have one control point");
135         if (this._super(duration)) {
136             this.setPoints(points);
137             this._tension = tension;
138             return true;
139         }
140         return false;
141     },
142 
143     /**
144      * @param {cc.Node} target
145      */
146     startWithTarget:function (target) {
147         this._super(target);
148         this._deltaT = 1 / this._points.length;
149     },
150 
151     /**
152      * @param {Number} time
153      */
154     update:function (time) {
155         var p, lt;
156 
157         // border
158         if (time == 1) {
159             p = this._points.length - 1;
160             lt = 1;
161         } else {
162             p = 0 | (time / this._deltaT);
163             lt = (time - this._deltaT * p) / this._deltaT;
164         }
165 
166         var newPos = cc.CardinalSplineAt(
167             cc.getControlPointAt( this._points, p - 1),
168             cc.getControlPointAt( this._points, p - 0),
169             cc.getControlPointAt( this._points, p + 1),
170             cc.getControlPointAt( this._points, p + 2),
171             this._tension, lt);
172         this.updatePosition(newPos);
173     },
174 
175     /**
176      * reverse a new cc.CardinalSplineTo
177      * @return {cc.CardinalSplineTo}
178      */
179     reverse:function () {
180         var reversePoints = cc.reverseControlPoints(this._points);
181         return cc.CardinalSplineTo.create(this._duration, reversePoints, this._tension);
182     },
183 
184     /**
185      * update position of target
186      * @param {cc.Point} newPos
187      */
188     updatePosition:function (newPos) {
189         this._target.setPosition(newPos);
190     },
191 
192     /**
193      * Points getter
194      * @return {Array}
195      */
196     getPoints:function () {
197         return this._points;
198     },
199 
200     /**
201      * Points setter
202      * @param {Array} points
203      */
204     setPoints:function (points) {
205         this._points = points;
206     }
207 });
208 
209 /**
210  * creates an action with a Cardinal Spline array of points and tension
211  * @function
212  * @param {Number} duration
213  * @param {Array} points array of control points
214  * @param {Number} tension
215  * @return {cc.CardinalSplineTo}
216  *
217  * @example
218  * //create a cc.CardinalSplineTo
219  * var action1 = cc.CardinalSplineTo.create(3, array, 0);
220  */
221 cc.CardinalSplineTo.create = function (duration, points, tension) {
222     var ret = new cc.CardinalSplineTo();
223     if (ret.initWithDuration(duration, points, tension)) {
224         return ret;
225     }
226     return null;
227 };
228 
229 /**
230  * Cardinal Spline path.  http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Cardinal_spline
231  * @class
232  * @extends cc.CardinalSplineTo
233  *
234  * @example
235  * //create a cc.CardinalSplineBy
236  * var action1 = cc.CardinalSplineBy.create(3, array, 0);
237  */
238 cc.CardinalSplineBy = cc.CardinalSplineTo.extend(/** @lends cc.CardinalSplineBy# */{
239     _startPosition:null,
240 
241     /**
242      * Constructor
243      */
244     ctor:function () {
245         this._startPosition = cc.p(0, 0);
246     },
247 
248     /**
249      * @param {cc.Node} target
250      */
251     startWithTarget:function (target) {
252         this._super(target);
253         this._startPosition = target.getPosition();
254     },
255 
256     /**
257      * reverse a new cc.CardinalSplineBy
258      * @return {cc.CardinalSplineBy}
259      */
260     reverse:function () {
261         var copyConfig = this._points.slice();
262         var current;
263         //
264         // convert "absolutes" to "diffs"
265         //
266         var p = copyConfig[0];
267         for (var i = 1; i < copyConfig.length; ++i) {
268             current = copyConfig[i];
269             var diff = cc.pSub(current, p);
270             copyConfig[i] = diff;
271             p = current;
272         }
273 
274         // convert to "diffs" to "reverse absolute"
275         var reverseArray =  cc.reverseControlPoints( copyConfig );
276 
277         // 1st element (which should be 0,0) should be here too
278         p = reverseArray[ reverseArray.length - 1 ];
279         reverseArray.pop();
280 
281         p = cc.pNeg(p);
282         reverseArray.unshift(p);
283         for (i = 1; i < reverseArray.length; ++i) {
284             current = reverseArray[i];
285             current = cc.pNeg(current);
286             var abs = cc.pAdd(current, p);
287             reverseArray[i] = abs;
288             p = abs;
289         }
290         return cc.CardinalSplineBy.create(this._duration, reverseArray, this._tension);
291     },
292 
293     /**
294      * update position of target
295      * @param {cc.Point} newPos
296      */
297     updatePosition:function (newPos) {
298         this._target.setPosition(cc.pAdd(newPos, this._startPosition));
299     }
300 });
301 
302 /**
303  * creates an action with a Cardinal Spline array of points and tension
304  * @function
305  * @param {Number} duration
306  * @param {cc.PointArray} points
307  * @param {Number} tension
308  * @return {cc.CardinalSplineBy}
309  */
310 cc.CardinalSplineBy.create = function (duration, points, tension) {
311     var ret = new cc.CardinalSplineBy();
312     if (ret.initWithDuration(duration, points, tension))
313         return ret;
314     return null;
315 };
316 
317 /**
318  * <p>
319  *   An action that moves the target with a CatmullRom curve to a destination point.<br/>
320  *   A Catmull Rom is a Cardinal Spline with a tension of 0.5.  <br/>
321  *   http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline
322  * </p>
323  * @class
324  * @extends cc.CardinalSplineTo
325  *
326  * @example
327  * var action1 = cc.CatmullRomTo.create(3, array);
328  */
329 cc.CatmullRomTo = cc.CardinalSplineTo.extend(/** @lends cc.CatmullRomTo# */{
330     /**
331      *  initializes the action with a duration and an array of points
332      */
333     initWithDuration:function (dt, points) {
334         return this._super(dt, points, 0.5);
335     }
336 });
337 
338 /**
339  * creates an action with a Cardinal Spline array of points and tension
340  * @param {Number} dt
341  * @param {cc.PointArray} points
342  * @return {cc.CatmullRomTo}
343  *
344  * @example
345  * var action1 = cc.CatmullRomTo.create(3, array);
346  */
347 cc.CatmullRomTo.create = function (dt, points) {
348     var ret = new cc.CatmullRomTo();
349     if (ret.initWithDuration(dt, points))
350         return ret;
351     return null;
352 };
353 
354 /**
355  * <p>
356  *   An action that moves the target with a CatmullRom curve by a certain distance.  <br/>
357  *   A Catmull Rom is a Cardinal Spline with a tension of 0.5.<br/>
358  *   http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline
359  * </p>
360  * @class
361  * @extends cc.CardinalSplineBy
362  *
363  * @example
364  * var action1 = cc.CatmullRomBy.create(3, array);
365  */
366 cc.CatmullRomBy = cc.CardinalSplineBy.extend({
367     /** initializes the action with a duration and an array of points */
368     initWithDuration:function (dt, points) {
369         return this._super(dt, points, 0.5);
370     }
371 });
372 
373 /**
374  * creates an action with a Cardinal Spline array of points and tension
375  *
376  * @example
377  * var action1 = cc.CatmullRomBy.create(3, array);
378  */
379 cc.CatmullRomBy.create = function (dt, points) {
380     var ret = new cc.CatmullRomBy();
381     if (ret.initWithDuration(dt, points))
382         return ret;
383     return null;
384 };
385