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 179 this._previousPosition = this._target.getPosition(); 180 this._accumulatedDiff = cc.p(0, 0); 181 }, 182 183 /** 184 * @param {Number} time 185 */ 186 update:function (time) { 187 var p, lt; 188 var ps = this._points; 189 // eg. 190 // p..p..p..p..p..p..p 191 // 1..2..3..4..5..6..7 192 // want p to be 1, 2, 3, 4, 5, 6 193 if (time == 1) { 194 p = ps.length - 1; 195 lt = 1; 196 } else { 197 var locDT = this._deltaT; 198 p = 0 | (time / locDT); 199 lt = (time - locDT * p) / locDT; 200 } 201 202 var newPos = cc.CardinalSplineAt( 203 cc.getControlPointAt(ps, p - 1), 204 cc.getControlPointAt(ps, p - 0), 205 cc.getControlPointAt(ps, p + 1), 206 cc.getControlPointAt(ps, p + 2), 207 this._tension, lt); 208 209 if (cc.ENABLE_STACKABLE_ACTIONS) { 210 var tempX, tempY; 211 tempX = this._target.getPositionX() - this._previousPosition.x; 212 tempY = this._target.getPositionY() - this._previousPosition.y; 213 if (tempX != 0 || tempY != 0) { 214 var locAccDiff = this._accumulatedDiff; 215 tempX = locAccDiff.x + tempX; 216 tempY = locAccDiff.y + tempY; 217 locAccDiff.x = tempX; 218 locAccDiff.y = tempY; 219 newPos.x += tempX; 220 newPos.y += tempY; 221 } 222 } 223 this.updatePosition(newPos); 224 }, 225 226 /** 227 * reverse a new cc.CardinalSplineTo 228 * @return {cc.CardinalSplineTo} 229 */ 230 reverse:function () { 231 var reversePoints = cc.reverseControlPoints(this._points); 232 return cc.CardinalSplineTo.create(this._duration, reversePoints, this._tension); 233 }, 234 235 /** 236 * update position of target 237 * @param {cc.Point} newPos 238 */ 239 updatePosition:function (newPos) { 240 this._target.setPosition(newPos); 241 this._previousPosition = newPos; 242 }, 243 244 /** 245 * Points getter 246 * @return {Array} 247 */ 248 getPoints:function () { 249 return this._points; 250 }, 251 252 /** 253 * Points setter 254 * @param {Array} points 255 */ 256 setPoints:function (points) { 257 this._points = points; 258 } 259 }); 260 261 /** 262 * creates an action with a Cardinal Spline array of points and tension 263 * @function 264 * @param {Number} duration 265 * @param {Array} points array of control points 266 * @param {Number} tension 267 * @return {cc.CardinalSplineTo} 268 * 269 * @example 270 * //create a cc.CardinalSplineTo 271 * var action1 = cc.CardinalSplineTo.create(3, array, 0); 272 */ 273 cc.CardinalSplineTo.create = function (duration, points, tension) { 274 var ret = new cc.CardinalSplineTo(); 275 if (ret.initWithDuration(duration, points, tension)) { 276 return ret; 277 } 278 return null; 279 }; 280 281 /** 282 * Cardinal Spline path. http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Cardinal_spline 283 * @class 284 * @extends cc.CardinalSplineTo 285 * 286 * @example 287 * //create a cc.CardinalSplineBy 288 * var action1 = cc.CardinalSplineBy.create(3, array, 0); 289 */ 290 cc.CardinalSplineBy = cc.CardinalSplineTo.extend(/** @lends cc.CardinalSplineBy# */{ 291 _startPosition:null, 292 293 /** 294 * Constructor 295 */ 296 ctor:function () { 297 cc.CardinalSplineTo.prototype.ctor.call(this); 298 this._startPosition = cc.p(0, 0); 299 }, 300 301 /** 302 * @param {cc.Node} target 303 */ 304 startWithTarget:function (target) { 305 cc.CardinalSplineTo.prototype.startWithTarget.call(this, target); 306 this._startPosition = target.getPosition(); 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._target.setPosition(posX, posY); 358 this._previousPosition.x = posX; 359 this._previousPosition.y = 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