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