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 var locPosition = this._target.getPosition(); 179 this._previousPosition = cc.p(locPosition.x, locPosition.y); 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 var locPosition = target.getPosition(); 307 this._startPosition.x = locPosition.x; 308 this._startPosition.y = locPosition.y; 309 }, 310 311 /** 312 * reverse a new cc.CardinalSplineBy 313 * @return {cc.CardinalSplineBy} 314 */ 315 reverse:function () { 316 var copyConfig = this._points.slice(); 317 var current; 318 // 319 // convert "absolutes" to "diffs" 320 // 321 var p = copyConfig[0]; 322 for (var i = 1; i < copyConfig.length; ++i) { 323 current = copyConfig[i]; 324 copyConfig[i] = cc.pSub(current, p); 325 p = current; 326 } 327 328 // convert to "diffs" to "reverse absolute" 329 var reverseArray = cc.reverseControlPoints(copyConfig); 330 331 // 1st element (which should be 0,0) should be here too 332 p = reverseArray[ reverseArray.length - 1 ]; 333 reverseArray.pop(); 334 335 p.x = -p.x; 336 p.y = -p.y; 337 338 reverseArray.unshift(p); 339 for (var i = 1; i < reverseArray.length; ++i) { 340 current = reverseArray[i]; 341 current.x = -current.x; 342 current.y = -current.y; 343 current.x += p.x; 344 current.y += p.y; 345 reverseArray[i] = current; 346 p = current; 347 } 348 return cc.CardinalSplineBy.create(this._duration, reverseArray, this._tension); 349 }, 350 351 /** 352 * update position of target 353 * @param {cc.Point} newPos 354 */ 355 updatePosition:function (newPos) { 356 var pos = this._startPosition; 357 var posX = newPos.x + pos.x; 358 var posY = newPos.y + pos.y; 359 this._target.setPosition(posX, posY); 360 this._previousPosition.x = posX; 361 this._previousPosition.y = posY; 362 }, 363 364 /** 365 * returns a new clone of the action 366 * @returns {cc.CardinalSplineBy} 367 */ 368 clone:function () { 369 var a = new cc.CardinalSplineBy(); 370 a.initWithDuration(this._duration, cc.copyControlPoints(this._points), this._tension); 371 return a; 372 } 373 }); 374 375 /** 376 * creates an action with a Cardinal Spline array of points and tension 377 * @function 378 * @param {Number} duration 379 * @param {Array} points 380 * @param {Number} tension 381 * @return {cc.CardinalSplineBy} 382 */ 383 cc.CardinalSplineBy.create = function (duration, points, tension) { 384 var ret = new cc.CardinalSplineBy(); 385 if (ret.initWithDuration(duration, points, tension)) 386 return ret; 387 return null; 388 }; 389 390 /** 391 * <p> 392 * An action that moves the target with a CatmullRom curve to a destination point.<br/> 393 * A Catmull Rom is a Cardinal Spline with a tension of 0.5. <br/> 394 * http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline 395 * </p> 396 * @class 397 * @extends cc.CardinalSplineTo 398 * 399 * @example 400 * var action1 = cc.CatmullRomTo.create(3, array); 401 */ 402 cc.CatmullRomTo = cc.CardinalSplineTo.extend(/** @lends cc.CatmullRomTo# */{ 403 /** 404 * initializes the action with a duration and an array of points 405 */ 406 initWithDuration:function (dt, points) { 407 return cc.CardinalSplineTo.prototype.initWithDuration.call(this, dt, points, 0.5); 408 }, 409 410 /** 411 * returns a new clone of the action 412 * @returns {cc.CatmullRomTo} 413 */ 414 clone:function () { 415 var action = new cc.CatmullRomTo(); 416 action.initWithDuration(this._duration, cc.copyControlPoints(this._points)); 417 return action; 418 } 419 }); 420 421 /** 422 * creates an action with a Cardinal Spline array of points and tension 423 * @param {Number} dt 424 * @param {Array} points 425 * @return {cc.CatmullRomTo} 426 * 427 * @example 428 * var action1 = cc.CatmullRomTo.create(3, array); 429 */ 430 cc.CatmullRomTo.create = function (dt, points) { 431 var ret = new cc.CatmullRomTo(); 432 if (ret.initWithDuration(dt, points)) 433 return ret; 434 return null; 435 }; 436 437 /** 438 * <p> 439 * An action that moves the target with a CatmullRom curve by a certain distance. <br/> 440 * A Catmull Rom is a Cardinal Spline with a tension of 0.5.<br/> 441 * http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline 442 * </p> 443 * @class 444 * @extends cc.CardinalSplineBy 445 * 446 * @example 447 * var action1 = cc.CatmullRomBy.create(3, array); 448 */ 449 cc.CatmullRomBy = cc.CardinalSplineBy.extend({ 450 /** initializes the action with a duration and an array of points */ 451 initWithDuration:function (dt, points) { 452 return cc.CardinalSplineTo.prototype.initWithDuration.call(this, dt, points, 0.5); 453 }, 454 455 /** 456 * returns a new clone of the action 457 * @returns {cc.CatmullRomBy} 458 */ 459 clone:function () { 460 var action = new cc.CatmullRomBy(); 461 action.initWithDuration(this._duration, cc.copyControlPoints(this._points)); 462 return action; 463 } 464 }); 465 466 /** 467 * creates an action with a Cardinal Spline array of points and tension 468 * 469 * @example 470 * var action1 = cc.CatmullRomBy.create(3, array); 471 */ 472 cc.CatmullRomBy.create = function (dt, points) { 473 var ret = new cc.CatmullRomBy(); 474 if (ret.initWithDuration(dt, points)) 475 return ret; 476 return null; 477 }; 478