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 * Constructor 131 */ 132 ctor:function () { 133 cc.ActionInterval.prototype.ctor.call(this); 134 135 this._points = []; 136 this._deltaT = 0; 137 this._tension = 0; 138 this._previousPosition = null; 139 this._accumulatedDiff = null; 140 }, 141 142 /** 143 * initializes the action with a duration and an array of points 144 * @param {Number} duration 145 * @param {Array} points array of control points 146 * @param {Number} tension 147 * @return {Boolean} 148 */ 149 initWithDuration:function (duration, points, tension) { 150 cc.Assert(points.length > 0, "Invalid configuration. It must at least have one control point"); 151 if (cc.ActionInterval.prototype.initWithDuration.call(this, duration)) { 152 this.setPoints(points); 153 this._tension = tension; 154 return true; 155 } 156 return false; 157 }, 158 159 /** 160 * returns a new clone of the action 161 * @returns {cc.CardinalSplineTo} 162 */ 163 clone:function () { 164 var action = new cc.CardinalSplineTo(); 165 action.initWithDuration(this._duration, cc.copyControlPoints(this._points), this._tension); 166 return action; 167 }, 168 169 /** 170 * @param {cc.Node} target 171 */ 172 startWithTarget:function (target) { 173 cc.ActionInterval.prototype.startWithTarget.call(this, target); 174 // Issue #1441 from cocos2d-iphone 175 this._deltaT = 1 / (this._points.length - 1); 176 177 this._previousPosition = this._target.getPosition(); 178 this._accumulatedDiff = cc.p(0, 0); 179 }, 180 181 /** 182 * @param {Number} time 183 */ 184 update:function (time) { 185 var p, lt; 186 var ps = this._points; 187 // eg. 188 // p..p..p..p..p..p..p 189 // 1..2..3..4..5..6..7 190 // want p to be 1, 2, 3, 4, 5, 6 191 if (time == 1) { 192 p = ps.length - 1; 193 lt = 1; 194 } else { 195 var locDT = this._deltaT; 196 p = 0 | (time / locDT); 197 lt = (time - locDT * p) / locDT; 198 } 199 200 var newPos = cc.CardinalSplineAt( 201 cc.getControlPointAt(ps, p - 1), 202 cc.getControlPointAt(ps, p - 0), 203 cc.getControlPointAt(ps, p + 1), 204 cc.getControlPointAt(ps, p + 2), 205 this._tension, lt); 206 207 if (cc.ENABLE_STACKABLE_ACTIONS) { 208 var tempX, tempY; 209 tempX = this._target.getPositionX() - this._previousPosition.x; 210 tempY = this._target.getPositionY() - this._previousPosition.y; 211 if (tempX != 0 || tempY != 0) { 212 var locAccDiff = this._accumulatedDiff; 213 tempX = locAccDiff.x + tempX; 214 tempY = locAccDiff.y + tempY; 215 locAccDiff.x = tempX; 216 locAccDiff.y = tempY; 217 newPos.x += tempX; 218 newPos.y += tempY; 219 } 220 } 221 this.updatePosition(newPos); 222 }, 223 224 /** 225 * reverse a new cc.CardinalSplineTo 226 * @return {cc.CardinalSplineTo} 227 */ 228 reverse:function () { 229 var reversePoints = cc.reverseControlPoints(this._points); 230 return cc.CardinalSplineTo.create(this._duration, reversePoints, this._tension); 231 }, 232 233 /** 234 * update position of target 235 * @param {cc.Point} newPos 236 */ 237 updatePosition:function (newPos) { 238 this._target.setPosition(newPos); 239 this._previousPosition = newPos; 240 }, 241 242 /** 243 * Points getter 244 * @return {Array} 245 */ 246 getPoints:function () { 247 return this._points; 248 }, 249 250 /** 251 * Points setter 252 * @param {Array} points 253 */ 254 setPoints:function (points) { 255 this._points = points; 256 } 257 }); 258 259 /** 260 * creates an action with a Cardinal Spline array of points and tension 261 * @function 262 * @param {Number} duration 263 * @param {Array} points array of control points 264 * @param {Number} tension 265 * @return {cc.CardinalSplineTo} 266 * 267 * @example 268 * //create a cc.CardinalSplineTo 269 * var action1 = cc.CardinalSplineTo.create(3, array, 0); 270 */ 271 cc.CardinalSplineTo.create = function (duration, points, tension) { 272 var ret = new cc.CardinalSplineTo(); 273 if (ret.initWithDuration(duration, points, tension)) { 274 return ret; 275 } 276 return null; 277 }; 278 279 /** 280 * Cardinal Spline path. http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Cardinal_spline 281 * @class 282 * @extends cc.CardinalSplineTo 283 * 284 * @example 285 * //create a cc.CardinalSplineBy 286 * var action1 = cc.CardinalSplineBy.create(3, array, 0); 287 */ 288 cc.CardinalSplineBy = cc.CardinalSplineTo.extend(/** @lends cc.CardinalSplineBy# */{ 289 _startPosition:null, 290 291 /** 292 * Constructor 293 */ 294 ctor:function () { 295 cc.CardinalSplineTo.prototype.ctor.call(this); 296 this._startPosition = cc.p(0, 0); 297 }, 298 299 /** 300 * @param {cc.Node} target 301 */ 302 startWithTarget:function (target) { 303 cc.CardinalSplineTo.prototype.startWithTarget.call(this, target); 304 this._startPosition = target.getPosition(); 305 }, 306 307 /** 308 * reverse a new cc.CardinalSplineBy 309 * @return {cc.CardinalSplineBy} 310 */ 311 reverse:function () { 312 var copyConfig = this._points.slice(); 313 var current; 314 // 315 // convert "absolutes" to "diffs" 316 // 317 var p = copyConfig[0]; 318 for (var i = 1; i < copyConfig.length; ++i) { 319 current = copyConfig[i]; 320 copyConfig[i] = cc.pSub(current, p); 321 p = current; 322 } 323 324 // convert to "diffs" to "reverse absolute" 325 var reverseArray = cc.reverseControlPoints(copyConfig); 326 327 // 1st element (which should be 0,0) should be here too 328 p = reverseArray[ reverseArray.length - 1 ]; 329 reverseArray.pop(); 330 331 p.x = -p.x; 332 p.y = -p.y; 333 334 reverseArray.unshift(p); 335 for (var i = 1; i < reverseArray.length; ++i) { 336 current = reverseArray[i]; 337 current.x = -current.x; 338 current.y = -current.y; 339 current.x += p.x; 340 current.y += p.y; 341 reverseArray[i] = current; 342 p = current; 343 } 344 return cc.CardinalSplineBy.create(this._duration, reverseArray, this._tension); 345 }, 346 347 /** 348 * update position of target 349 * @param {cc.Point} newPos 350 */ 351 updatePosition:function (newPos) { 352 var pos = this._startPosition; 353 var posX = newPos.x + pos.x; 354 var posY = newPos.y + pos.y; 355 this._target.setPosition(posX, posY); 356 this._previousPosition.x = posX; 357 this._previousPosition.y = posY; 358 }, 359 360 /** 361 * returns a new clone of the action 362 * @returns {cc.CardinalSplineBy} 363 */ 364 clone:function () { 365 var a = new cc.CardinalSplineBy(); 366 a.initWithDuration(this._duration, cc.copyControlPoints(this._points), this._tension); 367 return a; 368 } 369 }); 370 371 /** 372 * creates an action with a Cardinal Spline array of points and tension 373 * @function 374 * @param {Number} duration 375 * @param {Array} points 376 * @param {Number} tension 377 * @return {cc.CardinalSplineBy} 378 */ 379 cc.CardinalSplineBy.create = function (duration, points, tension) { 380 var ret = new cc.CardinalSplineBy(); 381 if (ret.initWithDuration(duration, points, tension)) 382 return ret; 383 return null; 384 }; 385 386 /** 387 * <p> 388 * An action that moves the target with a CatmullRom curve to a destination point.<br/> 389 * A Catmull Rom is a Cardinal Spline with a tension of 0.5. <br/> 390 * http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline 391 * </p> 392 * @class 393 * @extends cc.CardinalSplineTo 394 * 395 * @example 396 * var action1 = cc.CatmullRomTo.create(3, array); 397 */ 398 cc.CatmullRomTo = cc.CardinalSplineTo.extend(/** @lends cc.CatmullRomTo# */{ 399 /** 400 * initializes the action with a duration and an array of points 401 */ 402 initWithDuration:function (dt, points) { 403 return cc.CardinalSplineTo.prototype.initWithDuration.call(this, dt, points, 0.5); 404 }, 405 406 /** 407 * returns a new clone of the action 408 * @returns {cc.CatmullRomTo} 409 */ 410 clone:function () { 411 var action = new cc.CatmullRomTo(); 412 action.initWithDuration(this._duration, cc.copyControlPoints(this._points)); 413 return action; 414 } 415 }); 416 417 /** 418 * creates an action with a Cardinal Spline array of points and tension 419 * @param {Number} dt 420 * @param {Array} points 421 * @return {cc.CatmullRomTo} 422 * 423 * @example 424 * var action1 = cc.CatmullRomTo.create(3, array); 425 */ 426 cc.CatmullRomTo.create = function (dt, points) { 427 var ret = new cc.CatmullRomTo(); 428 if (ret.initWithDuration(dt, points)) 429 return ret; 430 return null; 431 }; 432 433 /** 434 * <p> 435 * An action that moves the target with a CatmullRom curve by a certain distance. <br/> 436 * A Catmull Rom is a Cardinal Spline with a tension of 0.5.<br/> 437 * http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline 438 * </p> 439 * @class 440 * @extends cc.CardinalSplineBy 441 * 442 * @example 443 * var action1 = cc.CatmullRomBy.create(3, array); 444 */ 445 cc.CatmullRomBy = cc.CardinalSplineBy.extend({ 446 /** initializes the action with a duration and an array of points */ 447 initWithDuration:function (dt, points) { 448 return cc.CardinalSplineTo.prototype.initWithDuration.call(this, dt, points, 0.5); 449 }, 450 451 /** 452 * returns a new clone of the action 453 * @returns {cc.CatmullRomBy} 454 */ 455 clone:function () { 456 var action = new cc.CatmullRomBy(); 457 action.initWithDuration(this._duration, cc.copyControlPoints(this._points)); 458 return action; 459 } 460 }); 461 462 /** 463 * creates an action with a Cardinal Spline array of points and tension 464 * 465 * @example 466 * var action1 = cc.CatmullRomBy.create(3, array); 467 */ 468 cc.CatmullRomBy.create = function (dt, points) { 469 var ret = new cc.CatmullRomBy(); 470 if (ret.initWithDuration(dt, points)) 471 return ret; 472 return null; 473 }; 474