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