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 * Creates an action with a Cardinal Spline array of points and tension 131 * 132 * @constructor 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 var p, lt; 193 var ps = this._points; 194 // eg. 195 // p..p..p..p..p..p..p 196 // 1..2..3..4..5..6..7 197 // want p to be 1, 2, 3, 4, 5, 6 198 if (time == 1) { 199 p = ps.length - 1; 200 lt = 1; 201 } else { 202 var locDT = this._deltaT; 203 p = 0 | (time / locDT); 204 lt = (time - locDT * p) / locDT; 205 } 206 207 var newPos = cc.cardinalSplineAt( 208 cc.getControlPointAt(ps, p - 1), 209 cc.getControlPointAt(ps, p - 0), 210 cc.getControlPointAt(ps, p + 1), 211 cc.getControlPointAt(ps, p + 2), 212 this._tension, lt); 213 214 if (cc.ENABLE_STACKABLE_ACTIONS) { 215 var tempX, tempY; 216 tempX = this.target.getPositionX() - this._previousPosition.x; 217 tempY = this.target.getPositionY() - this._previousPosition.y; 218 if (tempX != 0 || tempY != 0) { 219 var locAccDiff = this._accumulatedDiff; 220 tempX = locAccDiff.x + tempX; 221 tempY = locAccDiff.y + tempY; 222 locAccDiff.x = tempX; 223 locAccDiff.y = tempY; 224 newPos.x += tempX; 225 newPos.y += tempY; 226 } 227 } 228 this.updatePosition(newPos); 229 }, 230 231 /** 232 * reverse a new cc.CardinalSplineTo 233 * @return {cc.CardinalSplineTo} 234 */ 235 reverse:function () { 236 var reversePoints = cc.reverseControlPoints(this._points); 237 return cc.CardinalSplineTo.create(this._duration, reversePoints, this._tension); 238 }, 239 240 /** 241 * update position of target 242 * @param {cc.Point} newPos 243 */ 244 updatePosition:function (newPos) { 245 this.target.setPosition(newPos); 246 this._previousPosition = newPos; 247 }, 248 249 /** 250 * Points getter 251 * @return {Array} 252 */ 253 getPoints:function () { 254 return this._points; 255 }, 256 257 /** 258 * Points setter 259 * @param {Array} points 260 */ 261 setPoints:function (points) { 262 this._points = points; 263 } 264 }); 265 266 /** 267 * creates an action with a Cardinal Spline array of points and tension 268 * @function 269 * @param {Number} duration 270 * @param {Array} points array of control points 271 * @param {Number} tension 272 * @return {cc.CardinalSplineTo} 273 * 274 * @example 275 * //create a cc.CardinalSplineTo 276 * var action1 = cc.CardinalSplineTo.create(3, array, 0); 277 */ 278 cc.CardinalSplineTo.create = function (duration, points, tension) { 279 return new cc.CardinalSplineTo(duration, points, tension); 280 }; 281 282 /** 283 * Cardinal Spline path. http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Cardinal_spline 284 * @class 285 * @extends cc.CardinalSplineTo 286 * 287 * @example 288 * //create a cc.CardinalSplineBy 289 * var action1 = cc.CardinalSplineBy.create(3, array, 0); 290 */ 291 cc.CardinalSplineBy = cc.CardinalSplineTo.extend(/** @lends cc.CardinalSplineBy# */{ 292 _startPosition:null, 293 294 /** 295 * creates an action with a Cardinal Spline array of points and tension 296 * 297 * @constructor 298 * @param {Number} duration 299 * @param {Array} points 300 * @param {Number} tension 301 */ 302 ctor:function (duration, points, tension) { 303 cc.CardinalSplineTo.prototype.ctor.call(this); 304 this._startPosition = cc.p(0, 0); 305 306 tension !== undefined && this.initWithDuration(duration, points, tension); 307 }, 308 309 /** 310 * @param {cc.Node} target 311 */ 312 startWithTarget:function (target) { 313 cc.CardinalSplineTo.prototype.startWithTarget.call(this, target); 314 this._startPosition.x = target.getPositionX(); 315 this._startPosition.y = target.getPositionY(); 316 }, 317 318 /** 319 * reverse a new cc.CardinalSplineBy 320 * @return {cc.CardinalSplineBy} 321 */ 322 reverse:function () { 323 var copyConfig = this._points.slice(); 324 var current; 325 // 326 // convert "absolutes" to "diffs" 327 // 328 var p = copyConfig[0]; 329 for (var i = 1; i < copyConfig.length; ++i) { 330 current = copyConfig[i]; 331 copyConfig[i] = cc.pSub(current, p); 332 p = current; 333 } 334 335 // convert to "diffs" to "reverse absolute" 336 var reverseArray = cc.reverseControlPoints(copyConfig); 337 338 // 1st element (which should be 0,0) should be here too 339 p = reverseArray[ reverseArray.length - 1 ]; 340 reverseArray.pop(); 341 342 p.x = -p.x; 343 p.y = -p.y; 344 345 reverseArray.unshift(p); 346 for (var i = 1; i < reverseArray.length; ++i) { 347 current = reverseArray[i]; 348 current.x = -current.x; 349 current.y = -current.y; 350 current.x += p.x; 351 current.y += p.y; 352 reverseArray[i] = current; 353 p = current; 354 } 355 return cc.CardinalSplineBy.create(this._duration, reverseArray, this._tension); 356 }, 357 358 /** 359 * update position of target 360 * @param {cc.Point} newPos 361 */ 362 updatePosition:function (newPos) { 363 var pos = this._startPosition; 364 var posX = newPos.x + pos.x; 365 var posY = newPos.y + pos.y; 366 this._previousPosition.x = posX; 367 this._previousPosition.y = posY; 368 this.target.setPosition(posX, posY); 369 }, 370 371 /** 372 * returns a new clone of the action 373 * @returns {cc.CardinalSplineBy} 374 */ 375 clone:function () { 376 var a = new cc.CardinalSplineBy(); 377 a.initWithDuration(this._duration, cc.copyControlPoints(this._points), this._tension); 378 return a; 379 } 380 }); 381 382 /** 383 * creates an action with a Cardinal Spline array of points and tension 384 * @function 385 * @param {Number} duration 386 * @param {Array} points 387 * @param {Number} tension 388 * @return {cc.CardinalSplineBy} 389 */ 390 cc.CardinalSplineBy.create = function (duration, points, tension) { 391 return new cc.CardinalSplineBy(duration, points, tension); 392 }; 393 394 /** 395 * <p> 396 * An action that moves the target with a CatmullRom curve to a destination point.<br/> 397 * A Catmull Rom is a Cardinal Spline with a tension of 0.5. <br/> 398 * http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline 399 * </p> 400 * @class 401 * @extends cc.CardinalSplineTo 402 * 403 * @example 404 * var action1 = cc.CatmullRomTo.create(3, array); 405 */ 406 cc.CatmullRomTo = cc.CardinalSplineTo.extend(/** @lends cc.CatmullRomTo# */{ 407 408 /** 409 * creates an action with a Cardinal Spline array of points and tension 410 * 411 * @constructor 412 * @param {Number} dt 413 * @param {Array} points 414 * 415 * @example 416 * var action1 = new cc.CatmullRomTo(3, array); 417 */ 418 ctor: function(dt, points) { 419 points && this.initWithDuration(dt, points); 420 }, 421 422 /** 423 * Initializes the action with a duration and an array of points 424 * 425 * @function 426 * @param {Number} dt 427 * @param {Array} points 428 */ 429 initWithDuration:function (dt, points) { 430 return cc.CardinalSplineTo.prototype.initWithDuration.call(this, dt, points, 0.5); 431 }, 432 433 /** 434 * returns a new clone of the action 435 * @returns {cc.CatmullRomTo} 436 */ 437 clone:function () { 438 var action = new cc.CatmullRomTo(); 439 action.initWithDuration(this._duration, cc.copyControlPoints(this._points)); 440 return action; 441 } 442 }); 443 444 /** 445 * creates an action with a Cardinal Spline array of points and tension 446 * @param {Number} dt 447 * @param {Array} points 448 * @return {cc.CatmullRomTo} 449 * 450 * @example 451 * var action1 = cc.CatmullRomTo.create(3, array); 452 */ 453 cc.CatmullRomTo.create = function (dt, points) { 454 return new cc.CatmullRomTo(dt, points); 455 }; 456 457 /** 458 * <p> 459 * An action that moves the target with a CatmullRom curve by a certain distance. <br/> 460 * A Catmull Rom is a Cardinal Spline with a tension of 0.5.<br/> 461 * http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline 462 * </p> 463 * @class 464 * @extends cc.CardinalSplineBy 465 * 466 * @example 467 * var action1 = cc.CatmullRomBy.create(3, array); 468 */ 469 cc.CatmullRomBy = cc.CardinalSplineBy.extend({ 470 471 /** 472 * Creates an action with a Cardinal Spline array of points and tension 473 * 474 * @constructor 475 * @param {Number} dt 476 * @param {Array} points 477 * 478 * @example 479 * var action1 = new cc.CatmullRomBy(3, array); 480 */ 481 ctor: function(dt, points) { 482 cc.CardinalSplineBy.prototype.ctor.call(this); 483 points && this.initWithDuration(dt, points); 484 }, 485 486 /** 487 * initializes the action with a duration and an array of points 488 * 489 * @function 490 * @param {Number} dt 491 * @param {Array} points 492 */ 493 initWithDuration:function (dt, points) { 494 return cc.CardinalSplineTo.prototype.initWithDuration.call(this, dt, points, 0.5); 495 }, 496 497 /** 498 * returns a new clone of the action 499 * @returns {cc.CatmullRomBy} 500 */ 501 clone:function () { 502 var action = new cc.CatmullRomBy(); 503 action.initWithDuration(this._duration, cc.copyControlPoints(this._points)); 504 return action; 505 } 506 }); 507 508 /** 509 * Creates an action with a Cardinal Spline array of points and tension 510 * 511 * @example 512 * var action1 = cc.CatmullRomBy.create(3, array); 513 */ 514 cc.CatmullRomBy.create = function (dt, points) { 515 return new cc.CatmullRomBy(dt, points); 516 }; 517