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 * Returns the Cardinal Spline position for a given set of control points, tension and time. <br /> 37 * CatmullRom Spline formula. <br /> 38 * 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 39 * 40 * @function 41 * @param {cc.Point} p0 42 * @param {cc.Point} p1 43 * @param {cc.Point} p2 44 * @param {cc.Point} p3 45 * @param {Number} tension 46 * @param {Number} t 47 * @return {cc.Point} 48 */ 49 cc.cardinalSplineAt = function (p0, p1, p2, p3, tension, t) { 50 var t2 = t * t; 51 var t3 = t2 * t; 52 53 /* 54 * 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 55 */ 56 var s = (1 - tension) / 2; 57 58 var b1 = s * ((-t3 + (2 * t2)) - t); // s(-t3 + 2 t2 - t)P1 59 var b2 = s * (-t3 + t2) + (2 * t3 - 3 * t2 + 1); // s(-t3 + t2)P2 + (2 t3 - 3 t2 + 1)P2 60 var b3 = s * (t3 - 2 * t2 + t) + (-2 * t3 + 3 * t2); // s(t3 - 2 t2 + t)P3 + (-2 t3 + 3 t2)P3 61 var b4 = s * (t3 - t2); // s(t3 - t2)P4 62 63 var x = (p0.x * b1 + p1.x * b2 + p2.x * b3 + p3.x * b4); 64 var y = (p0.y * b1 + p1.y * b2 + p2.y * b3 + p3.y * b4); 65 return cc.p(x, y); 66 }; 67 68 /** 69 * returns a new copy of the array reversed. 70 * 71 * @return {Array} 72 */ 73 cc.reverseControlPoints = function (controlPoints) { 74 var newArray = []; 75 for (var i = controlPoints.length - 1; i >= 0; i--) { 76 newArray.push(cc.p(controlPoints[i].x, controlPoints[i].y)); 77 } 78 return newArray; 79 }; 80 81 82 /** 83 * returns a new clone of the controlPoints 84 * 85 * @param controlPoints 86 * @returns {Array} 87 */ 88 cc.cloneControlPoints = function (controlPoints) { 89 var newArray = []; 90 for (var i = 0; i < controlPoints.length; i++) 91 newArray.push(cc.p(controlPoints[i].x, controlPoints[i].y)); 92 return newArray; 93 }; 94 95 /** 96 * returns a new clone of the controlPoints 97 * @deprecated since v3.0 please use cc.cloneControlPoints() instead. 98 * @param controlPoints 99 * @returns {Array} 100 */ 101 cc.copyControlPoints = cc.cloneControlPoints; 102 103 /** 104 * returns a point from the array 105 * 106 * @param {Array} controlPoints 107 * @param {Number} pos 108 * @return {Array} 109 */ 110 cc.getControlPointAt = function (controlPoints, pos) { 111 var p = Math.min(controlPoints.length - 1, Math.max(pos, 0)); 112 return controlPoints[p]; 113 }; 114 115 /** 116 * reverse the current control point array inline, without generating a new one <br /> 117 * 118 * @param controlPoints 119 */ 120 cc.reverseControlPointsInline = function (controlPoints) { 121 var len = controlPoints.length; 122 var mid = 0 | (len / 2); 123 for (var i = 0; i < mid; ++i) { 124 var temp = controlPoints[i]; 125 controlPoints[i] = controlPoints[len - i - 1]; 126 controlPoints[len - i - 1] = temp; 127 } 128 }; 129 130 131 /** 132 * Cardinal Spline path. {@link http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Cardinal_spline} 133 * Absolute coordinates. 134 * 135 * @class 136 * @extends cc.ActionInterval 137 * @param {Number} duration 138 * @param {Array} points array of control points 139 * @param {Number} tension 140 * 141 * @example 142 * //create a cc.CardinalSplineTo 143 * var action1 = cc.cardinalSplineTo(3, array, 0); 144 */ 145 cc.CardinalSplineTo = cc.ActionInterval.extend(/** @lends cc.CardinalSplineTo# */{ 146 /** Array of control points */ 147 _points:null, 148 _deltaT:0, 149 _tension:0, 150 _previousPosition:null, 151 _accumulatedDiff:null, 152 153 /** 154 * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. <br /> 155 * Creates an action with a Cardinal Spline array of points and tension. 156 * @param {Number} duration 157 * @param {Array} points array of control points 158 * @param {Number} tension 159 */ 160 ctor: function (duration, points, tension) { 161 cc.ActionInterval.prototype.ctor.call(this); 162 163 this._points = []; 164 tension !== undefined && this.initWithDuration(duration, points, tension); 165 }, 166 167 /** 168 * initializes the action with a duration and an array of points 169 * 170 * @param {Number} duration 171 * @param {Array} points array of control points 172 * @param {Number} tension 173 * 174 * @return {Boolean} 175 */ 176 initWithDuration:function (duration, points, tension) { 177 if(!points || points.length === 0) 178 throw "Invalid configuration. It must at least have one control point"; 179 180 if (cc.ActionInterval.prototype.initWithDuration.call(this, duration)) { 181 this.setPoints(points); 182 this._tension = tension; 183 return true; 184 } 185 return false; 186 }, 187 188 /** 189 * returns a new clone of the action 190 * 191 * @returns {cc.CardinalSplineTo} 192 */ 193 clone:function () { 194 var action = new cc.CardinalSplineTo(); 195 action.initWithDuration(this._duration, cc.copyControlPoints(this._points), this._tension); 196 return action; 197 }, 198 199 /** 200 * called before the action start. It will also set the target. 201 * 202 * @param {cc.Node} target 203 */ 204 startWithTarget:function (target) { 205 cc.ActionInterval.prototype.startWithTarget.call(this, target); 206 // Issue #1441 from cocos2d-iphone 207 this._deltaT = 1 / (this._points.length - 1); 208 this._previousPosition = cc.p(this.target.getPositionX(), this.target.getPositionY()); 209 this._accumulatedDiff = cc.p(0, 0); 210 }, 211 212 /** 213 * Called once per frame. Time is the number of seconds of a frame interval. 214 * 215 * @param {Number} dt 216 */ 217 update:function (dt) { 218 dt = this._computeEaseTime(dt); 219 var p, lt; 220 var ps = this._points; 221 // eg. 222 // p..p..p..p..p..p..p 223 // 1..2..3..4..5..6..7 224 // want p to be 1, 2, 3, 4, 5, 6 225 if (dt === 1) { 226 p = ps.length - 1; 227 lt = 1; 228 } else { 229 var locDT = this._deltaT; 230 p = 0 | (dt / locDT); 231 lt = (dt - locDT * p) / locDT; 232 } 233 234 var newPos = cc.cardinalSplineAt( 235 cc.getControlPointAt(ps, p - 1), 236 cc.getControlPointAt(ps, p - 0), 237 cc.getControlPointAt(ps, p + 1), 238 cc.getControlPointAt(ps, p + 2), 239 this._tension, lt); 240 241 if (cc.ENABLE_STACKABLE_ACTIONS) { 242 var tempX, tempY; 243 tempX = this.target.getPositionX() - this._previousPosition.x; 244 tempY = this.target.getPositionY() - this._previousPosition.y; 245 if (tempX !== 0 || tempY !== 0) { 246 var locAccDiff = this._accumulatedDiff; 247 tempX = locAccDiff.x + tempX; 248 tempY = locAccDiff.y + tempY; 249 locAccDiff.x = tempX; 250 locAccDiff.y = tempY; 251 newPos.x += tempX; 252 newPos.y += tempY; 253 } 254 } 255 this.updatePosition(newPos); 256 }, 257 258 /** 259 * reverse a new cc.CardinalSplineTo. <br /> 260 * Along the track of movement in the opposite. 261 * 262 * @return {cc.CardinalSplineTo} 263 */ 264 reverse:function () { 265 var reversePoints = cc.reverseControlPoints(this._points); 266 return cc.cardinalSplineTo(this._duration, reversePoints, this._tension); 267 }, 268 269 /** 270 * update position of target 271 * 272 * @param {cc.Point} newPos 273 */ 274 updatePosition:function (newPos) { 275 this.target.setPosition(newPos); 276 this._previousPosition = newPos; 277 }, 278 279 /** 280 * Points getter 281 * 282 * @return {Array} 283 */ 284 getPoints:function () { 285 return this._points; 286 }, 287 288 /** 289 * Points setter 290 * 291 * @param {Array} points 292 */ 293 setPoints:function (points) { 294 this._points = points; 295 } 296 }); 297 298 /** 299 * creates an action with a Cardinal Spline array of points and tension. 300 * 301 * @function 302 * @param {Number} duration 303 * @param {Array} points array of control points 304 * @param {Number} tension 305 * @return {cc.CardinalSplineTo} 306 * 307 * @example 308 * //create a cc.CardinalSplineTo 309 * var action1 = cc.cardinalSplineTo(3, array, 0); 310 */ 311 cc.cardinalSplineTo = function (duration, points, tension) { 312 return new cc.CardinalSplineTo(duration, points, tension); 313 }; 314 315 /** 316 * Please use cc.cardinalSplineTo instead. <br /> 317 * creates an action with a Cardinal Spline array of points and tension 318 * 319 * @function 320 * @param {Number} duration 321 * @param {Array} points array of control points 322 * @param {Number} tension 323 * @return {cc.CardinalSplineTo} 324 * @static 325 * @deprecated since v3.0 please use cc.cardinalSplineTo(duration, points, tension) instead. 326 */ 327 cc.CardinalSplineTo.create = cc.cardinalSplineTo; 328 329 /** 330 * Cardinal Spline path. {@link http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Cardinal_spline} 331 * Relative coordinates. 332 * 333 * @class 334 * @extends cc.CardinalSplineTo 335 * @param {Number} duration 336 * @param {Array} points 337 * @param {Number} tension 338 * 339 * @example 340 * //create a cc.CardinalSplineBy 341 * var action1 = cc.cardinalSplineBy(3, array, 0); 342 */ 343 cc.CardinalSplineBy = cc.CardinalSplineTo.extend(/** @lends cc.CardinalSplineBy# */{ 344 _startPosition:null, 345 346 /** 347 * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. <br /> 348 * creates an action with a Cardinal Spline array of points and tension. 349 * @param {Number} duration 350 * @param {Array} points 351 * @param {Number} tension 352 */ 353 ctor:function (duration, points, tension) { 354 cc.CardinalSplineTo.prototype.ctor.call(this); 355 this._startPosition = cc.p(0, 0); 356 357 tension !== undefined && this.initWithDuration(duration, points, tension); 358 }, 359 360 /** 361 * called before the action start. It will also set the target. 362 * 363 * @param {cc.Node} target 364 */ 365 startWithTarget:function (target) { 366 cc.CardinalSplineTo.prototype.startWithTarget.call(this, target); 367 this._startPosition.x = target.getPositionX(); 368 this._startPosition.y = target.getPositionY(); 369 }, 370 371 /** 372 * reverse a new cc.CardinalSplineBy 373 * 374 * @return {cc.CardinalSplineBy} 375 */ 376 reverse:function () { 377 var copyConfig = this._points.slice(); 378 var current; 379 // 380 // convert "absolutes" to "diffs" 381 // 382 var p = copyConfig[0]; 383 for (var i = 1; i < copyConfig.length; ++i) { 384 current = copyConfig[i]; 385 copyConfig[i] = cc.pSub(current, p); 386 p = current; 387 } 388 389 // convert to "diffs" to "reverse absolute" 390 var reverseArray = cc.reverseControlPoints(copyConfig); 391 392 // 1st element (which should be 0,0) should be here too 393 p = reverseArray[ reverseArray.length - 1 ]; 394 reverseArray.pop(); 395 396 p.x = -p.x; 397 p.y = -p.y; 398 399 reverseArray.unshift(p); 400 for (var i = 1; i < reverseArray.length; ++i) { 401 current = reverseArray[i]; 402 current.x = -current.x; 403 current.y = -current.y; 404 current.x += p.x; 405 current.y += p.y; 406 reverseArray[i] = current; 407 p = current; 408 } 409 return cc.cardinalSplineBy(this._duration, reverseArray, this._tension); 410 }, 411 412 /** 413 * update position of target 414 * 415 * @param {cc.Point} newPos 416 */ 417 updatePosition:function (newPos) { 418 var pos = this._startPosition; 419 var posX = newPos.x + pos.x; 420 var posY = newPos.y + pos.y; 421 this._previousPosition.x = posX; 422 this._previousPosition.y = posY; 423 this.target.setPosition(posX, posY); 424 }, 425 426 /** 427 * returns a new clone of the action 428 * 429 * @returns {cc.CardinalSplineBy} 430 */ 431 clone:function () { 432 var a = new cc.CardinalSplineBy(); 433 a.initWithDuration(this._duration, cc.copyControlPoints(this._points), this._tension); 434 return a; 435 } 436 }); 437 438 /** 439 * creates an action with a Cardinal Spline array of points and tension. 440 * 441 * @function 442 * @param {Number} duration 443 * @param {Array} points 444 * @param {Number} tension 445 * 446 * @return {cc.CardinalSplineBy} 447 */ 448 cc.cardinalSplineBy = function (duration, points, tension) { 449 return new cc.CardinalSplineBy(duration, points, tension); 450 }; 451 452 /** 453 * Please use cc.cardinalSplineBy instead. 454 * creates an action with a Cardinal Spline array of points and tension. 455 * @function 456 * @param {Number} duration 457 * @param {Array} points 458 * @param {Number} tension 459 * @return {cc.CardinalSplineBy} 460 * @static 461 * @deprecated since v3.0 please use cc.cardinalSplineBy(duration, points, tension); 462 */ 463 cc.CardinalSplineBy.create = cc.cardinalSplineBy; 464 465 /** 466 * An action that moves the target with a CatmullRom curve to a destination point.<br/> 467 * A Catmull Rom is a Cardinal Spline with a tension of 0.5. <br/> 468 * {@link http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline} 469 * Absolute coordinates. 470 * 471 * @class 472 * @extends cc.CardinalSplineTo 473 * @param {Number} dt 474 * @param {Array} points 475 * 476 * @example 477 * var action1 = cc.catmullRomTo(3, array); 478 */ 479 cc.CatmullRomTo = cc.CardinalSplineTo.extend(/** @lends cc.CatmullRomTo# */{ 480 481 /** 482 * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. <br /> 483 * creates an action with a Cardinal Spline array of points and tension. 484 * @param {Number} dt 485 * @param {Array} points 486 */ 487 ctor: function(dt, points) { 488 points && this.initWithDuration(dt, points); 489 }, 490 491 /** 492 * Initializes the action with a duration and an array of points 493 * 494 * @param {Number} dt 495 * @param {Array} points 496 */ 497 initWithDuration:function (dt, points) { 498 return cc.CardinalSplineTo.prototype.initWithDuration.call(this, dt, points, 0.5); 499 }, 500 501 /** 502 * returns a new clone of the action 503 * @returns {cc.CatmullRomTo} 504 */ 505 clone:function () { 506 var action = new cc.CatmullRomTo(); 507 action.initWithDuration(this._duration, cc.copyControlPoints(this._points)); 508 return action; 509 } 510 }); 511 512 /** 513 * creates an action with a Cardinal Spline array of points and tension. 514 * 515 * @function 516 * @param {Number} dt 517 * @param {Array} points 518 * @return {cc.CatmullRomTo} 519 * 520 * @example 521 * var action1 = cc.catmullRomTo(3, array); 522 */ 523 cc.catmullRomTo = function (dt, points) { 524 return new cc.CatmullRomTo(dt, points); 525 }; 526 /** 527 * Please use cc.catmullRomTo instead. 528 * creates an action with a Cardinal Spline array of points and tension. 529 * 530 * @param {Number} dt 531 * @param {Array} points 532 * @return {cc.CatmullRomTo} 533 * @static 534 * @deprecated since v3.0 please use cc.catmullRomTo(dt, points) instead. 535 */ 536 cc.CatmullRomTo.create = cc.catmullRomTo; 537 538 /** 539 * An action that moves the target with a CatmullRom curve by a certain distance. <br/> 540 * A Catmull Rom is a Cardinal Spline with a tension of 0.5.<br/> 541 * http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline 542 * Relative coordinates. 543 * 544 * @class 545 * @extends cc.CardinalSplineBy 546 * @param {Number} dt 547 * @param {Array} points 548 * 549 * @example 550 * var action1 = cc.catmullRomBy(3, array); 551 */ 552 cc.CatmullRomBy = cc.CardinalSplineBy.extend({ 553 554 /** 555 * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. <br /> 556 * Creates an action with a Cardinal Spline array of points and tension. 557 * @param {Number} dt 558 * @param {Array} points 559 */ 560 ctor: function(dt, points) { 561 cc.CardinalSplineBy.prototype.ctor.call(this); 562 points && this.initWithDuration(dt, points); 563 }, 564 565 /** 566 * initializes the action with a duration and an array of points 567 * 568 * @function 569 * @param {Number} dt 570 * @param {Array} points 571 */ 572 initWithDuration:function (dt, points) { 573 return cc.CardinalSplineTo.prototype.initWithDuration.call(this, dt, points, 0.5); 574 }, 575 576 /** 577 * returns a new clone of the action 578 * @returns {cc.CatmullRomBy} 579 */ 580 clone:function () { 581 var action = new cc.CatmullRomBy(); 582 action.initWithDuration(this._duration, cc.copyControlPoints(this._points)); 583 return action; 584 } 585 }); 586 587 /** 588 * Creates an action with a Cardinal Spline array of points and tension 589 * @function 590 * @param {Number} dt 591 * @param {Array} points 592 * @return {cc.CatmullRomBy} 593 * @example 594 * var action1 = cc.catmullRomBy(3, array); 595 */ 596 cc.catmullRomBy = function (dt, points) { 597 return new cc.CatmullRomBy(dt, points); 598 }; 599 /** 600 * Please use cc.catmullRomBy instead 601 * Creates an action with a Cardinal Spline array of points and tension 602 * @static 603 * @deprecated since v3.0 please cc.catmullRomBy(dt, points) instead. 604 */ 605 cc.CatmullRomBy.create = cc.catmullRomBy; 606