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