CCActionCatmullRom.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. /*
  2. * Copyright (c) 2008 Radu Gruian
  3. * Copyright (c) 2011 Vit Valentin
  4. * Copyright (c) 2012 cocos2d-x.org
  5. * Copyright (c) 2013-2017 Chukong Technologies Inc.
  6. *
  7. * Permission is hereby granted, free of charge, to any person obtaining a copy
  8. * of this software and associated documentation files (the "Software"), to deal
  9. * in the Software without restriction, including without limitation the rights
  10. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. * copies of the Software, and to permit persons to whom the Software is
  12. * furnished to do so, subject to the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be included in
  15. * all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23. * THE SOFTWARE.
  24. *
  25. *
  26. * Original code by Radu Gruian: http://www.codeproject.com/Articles/30838/Overhauser-Catmull-Rom-Splines-for-Camera-Animatio.So
  27. *
  28. * Adapted to cocos2d-x by Vit Valentin
  29. *
  30. * Adapted from cocos2d-x to cocos2d-iphone by Ricardo Quesada
  31. */
  32. #include "base/ccMacros.h"
  33. #include "2d/CCActionCatmullRom.h"
  34. #include "2d/CCNode.h"
  35. using namespace std;
  36. NS_CC_BEGIN;
  37. /*
  38. * Implementation of PointArray
  39. */
  40. PointArray* PointArray::create(ssize_t capacity)
  41. {
  42. PointArray* pointArray = new (std::nothrow) PointArray();
  43. if (pointArray && pointArray->initWithCapacity(capacity))
  44. {
  45. pointArray->autorelease();
  46. return pointArray;
  47. }
  48. delete pointArray;
  49. return nullptr;
  50. }
  51. bool PointArray::initWithCapacity(ssize_t capacity)
  52. {
  53. _controlPoints = new (std::nothrow) vector<Vec2*>();
  54. if (capacity > 0) {
  55. _controlPoints->reserve(capacity);
  56. }
  57. return true;
  58. }
  59. PointArray* PointArray::clone() const
  60. {
  61. vector<Vec2*> *newArray = new (std::nothrow) vector<Vec2*>();
  62. for (auto& controlPoint : *_controlPoints)
  63. {
  64. newArray->push_back(new Vec2(controlPoint->x, controlPoint->y));
  65. }
  66. PointArray *points = new (std::nothrow) PointArray();
  67. points->initWithCapacity(10);
  68. points->setControlPoints(newArray);
  69. points->autorelease();
  70. return points;
  71. }
  72. PointArray::~PointArray()
  73. {
  74. CCLOGINFO("deallocing PointArray: %p", this);
  75. for (auto& controlPoint : *_controlPoints)
  76. {
  77. delete controlPoint;
  78. }
  79. delete _controlPoints;
  80. }
  81. PointArray::PointArray() :_controlPoints(nullptr){}
  82. const std::vector<Vec2*>* PointArray::getControlPoints() const
  83. {
  84. return _controlPoints;
  85. }
  86. void PointArray::setControlPoints(vector<Vec2*> *controlPoints)
  87. {
  88. CCASSERT(controlPoints != nullptr, "control points should not be nullptr");
  89. // delete old points
  90. vector<Vec2*>::iterator iter;
  91. for (auto& controlPoint : *_controlPoints)
  92. {
  93. delete controlPoint;
  94. }
  95. delete _controlPoints;
  96. _controlPoints = controlPoints;
  97. }
  98. void PointArray::addControlPoint(const Vec2& controlPoint)
  99. {
  100. _controlPoints->push_back(new Vec2(controlPoint.x, controlPoint.y));
  101. }
  102. void PointArray::insertControlPoint(Vec2 &controlPoint, ssize_t index)
  103. {
  104. Vec2 *temp = new (std::nothrow) Vec2(controlPoint.x, controlPoint.y);
  105. _controlPoints->insert(_controlPoints->begin() + index, temp);
  106. }
  107. Vec2 PointArray::getControlPointAtIndex(ssize_t index)
  108. {
  109. index = MIN(static_cast<ssize_t>(_controlPoints->size())-1, MAX(index, 0));
  110. return *(_controlPoints->at(index));
  111. }
  112. void PointArray::replaceControlPoint(cocos2d::Vec2 &controlPoint, ssize_t index)
  113. {
  114. Vec2 *temp = _controlPoints->at(index);
  115. temp->x = controlPoint.x;
  116. temp->y = controlPoint.y;
  117. }
  118. void PointArray::removeControlPointAtIndex(ssize_t index)
  119. {
  120. vector<Vec2*>::iterator iter = _controlPoints->begin() + index;
  121. Vec2* removedPoint = *iter;
  122. _controlPoints->erase(iter);
  123. delete removedPoint;
  124. }
  125. ssize_t PointArray::count() const
  126. {
  127. return _controlPoints->size();
  128. }
  129. PointArray* PointArray::reverse() const
  130. {
  131. vector<Vec2*> *newArray = new (std::nothrow) vector<Vec2*>();
  132. Vec2 *point = nullptr;
  133. for (auto iter = _controlPoints->rbegin(), iterRend = _controlPoints->rend(); iter != iterRend; ++iter)
  134. {
  135. point = *iter;
  136. newArray->push_back(new Vec2(point->x, point->y));
  137. }
  138. PointArray *config = PointArray::create(0);
  139. config->setControlPoints(newArray);
  140. return config;
  141. }
  142. void PointArray::reverseInline()
  143. {
  144. size_t l = _controlPoints->size();
  145. Vec2 *p1 = nullptr;
  146. Vec2 *p2 = nullptr;
  147. float x, y;
  148. for (size_t i = 0; i < l/2; ++i)
  149. {
  150. p1 = _controlPoints->at(i);
  151. p2 = _controlPoints->at(l-i-1);
  152. x = p1->x;
  153. y = p1->y;
  154. p1->x = p2->x;
  155. p1->y = p2->y;
  156. p2->x = x;
  157. p2->y = y;
  158. }
  159. }
  160. // CatmullRom Spline formula:
  161. Vec2 ccCardinalSplineAt(Vec2 &p0, Vec2 &p1, Vec2 &p2, Vec2 &p3, float tension, float t)
  162. {
  163. float t2 = t * t;
  164. float t3 = t2 * t;
  165. /*
  166. * 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
  167. */
  168. float s = (1 - tension) / 2;
  169. float b1 = s * ((-t3 + (2 * t2)) - t); // s(-t3 + 2 t2 - t)P1
  170. float b2 = s * (-t3 + t2) + (2 * t3 - 3 * t2 + 1); // s(-t3 + t2)P2 + (2 t3 - 3 t2 + 1)P2
  171. float b3 = s * (t3 - 2 * t2 + t) + (-2 * t3 + 3 * t2); // s(t3 - 2 t2 + t)P3 + (-2 t3 + 3 t2)P3
  172. float b4 = s * (t3 - t2); // s(t3 - t2)P4
  173. float x = (p0.x*b1 + p1.x*b2 + p2.x*b3 + p3.x*b4);
  174. float y = (p0.y*b1 + p1.y*b2 + p2.y*b3 + p3.y*b4);
  175. return Vec2(x,y);
  176. }
  177. /* Implementation of CardinalSplineTo
  178. */
  179. CardinalSplineTo* CardinalSplineTo::create(float duration, cocos2d::PointArray *points, float tension)
  180. {
  181. CardinalSplineTo *ret = new (std::nothrow) CardinalSplineTo();
  182. if (ret)
  183. {
  184. if (ret->initWithDuration(duration, points, tension))
  185. {
  186. ret->autorelease();
  187. }
  188. else
  189. {
  190. CC_SAFE_RELEASE_NULL(ret);
  191. }
  192. }
  193. return ret;
  194. }
  195. bool CardinalSplineTo::initWithDuration(float duration, cocos2d::PointArray *points, float tension)
  196. {
  197. CCASSERT(points->count() > 0, "Invalid configuration. It must at least have one control point");
  198. if (ActionInterval::initWithDuration(duration))
  199. {
  200. this->setPoints(points);
  201. this->_tension = tension;
  202. return true;
  203. }
  204. return false;
  205. }
  206. CardinalSplineTo::~CardinalSplineTo()
  207. {
  208. CC_SAFE_RELEASE_NULL(_points);
  209. }
  210. CardinalSplineTo::CardinalSplineTo()
  211. : _points(nullptr)
  212. , _deltaT(0.f)
  213. , _tension(0.f)
  214. {
  215. }
  216. void CardinalSplineTo::startWithTarget(cocos2d::Node *target)
  217. {
  218. ActionInterval::startWithTarget(target);
  219. // _deltaT = (float) 1 / _points->count();
  220. // Issue #1441
  221. _deltaT = (float) 1 / (_points->count() - 1);
  222. _previousPosition = target->getPosition();
  223. _accumulatedDiff.setZero();
  224. }
  225. CardinalSplineTo* CardinalSplineTo::clone() const
  226. {
  227. // no copy constructor
  228. auto a = new (std::nothrow) CardinalSplineTo();
  229. a->initWithDuration(this->_duration, this->_points->clone(), this->_tension);
  230. a->autorelease();
  231. return a;
  232. }
  233. void CardinalSplineTo::update(float time)
  234. {
  235. ssize_t p;
  236. float lt;
  237. // eg.
  238. // p..p..p..p..p..p..p
  239. // 1..2..3..4..5..6..7
  240. // want p to be 1, 2, 3, 4, 5, 6
  241. if (time == 1)
  242. {
  243. p = _points->count() - 1;
  244. lt = 1;
  245. }
  246. else
  247. {
  248. p = time / _deltaT;
  249. lt = (time - _deltaT * (float)p) / _deltaT;
  250. }
  251. // Interpolate
  252. Vec2 pp0 = _points->getControlPointAtIndex(p-1);
  253. Vec2 pp1 = _points->getControlPointAtIndex(p+0);
  254. Vec2 pp2 = _points->getControlPointAtIndex(p+1);
  255. Vec2 pp3 = _points->getControlPointAtIndex(p+2);
  256. Vec2 newPos = ccCardinalSplineAt(pp0, pp1, pp2, pp3, _tension, lt);
  257. #if CC_ENABLE_STACKABLE_ACTIONS
  258. // Support for stacked actions
  259. Node *node = _target;
  260. Vec2 diff = node->getPosition() - _previousPosition;
  261. if( diff.x !=0 || diff.y != 0 )
  262. {
  263. _accumulatedDiff = _accumulatedDiff + diff;
  264. newPos = newPos + _accumulatedDiff;
  265. }
  266. #endif
  267. this->updatePosition(newPos);
  268. }
  269. void CardinalSplineTo::updatePosition(cocos2d::Vec2 &newPos)
  270. {
  271. _target->setPosition(newPos);
  272. _previousPosition = newPos;
  273. }
  274. CardinalSplineTo* CardinalSplineTo::reverse() const
  275. {
  276. PointArray *pReverse = _points->reverse();
  277. return CardinalSplineTo::create(_duration, pReverse, _tension);
  278. }
  279. /* CardinalSplineBy
  280. */
  281. CardinalSplineBy* CardinalSplineBy::create(float duration, cocos2d::PointArray *points, float tension)
  282. {
  283. CardinalSplineBy *ret = new (std::nothrow) CardinalSplineBy();
  284. if (ret)
  285. {
  286. if (ret->initWithDuration(duration, points, tension))
  287. {
  288. ret->autorelease();
  289. }
  290. else
  291. {
  292. CC_SAFE_RELEASE_NULL(ret);
  293. }
  294. }
  295. return ret;
  296. }
  297. CardinalSplineBy::CardinalSplineBy() : _startPosition(0,0)
  298. {
  299. }
  300. void CardinalSplineBy::updatePosition(cocos2d::Vec2 &newPos)
  301. {
  302. Vec2 p = newPos + _startPosition;
  303. _target->setPosition(p);
  304. _previousPosition = p;
  305. }
  306. CardinalSplineBy* CardinalSplineBy::reverse() const
  307. {
  308. PointArray *copyConfig = _points->clone();
  309. //
  310. // convert "absolutes" to "diffs"
  311. //
  312. Vec2 p = copyConfig->getControlPointAtIndex(0);
  313. for (ssize_t i = 1; i < copyConfig->count(); ++i)
  314. {
  315. Vec2 current = copyConfig->getControlPointAtIndex(i);
  316. Vec2 diff = current - p;
  317. copyConfig->replaceControlPoint(diff, i);
  318. p = current;
  319. }
  320. // convert to "diffs" to "reverse absolute"
  321. PointArray *pReverse = copyConfig->reverse();
  322. // 1st element (which should be 0,0) should be here too
  323. p = pReverse->getControlPointAtIndex(pReverse->count()-1);
  324. pReverse->removeControlPointAtIndex(pReverse->count()-1);
  325. p = -p;
  326. pReverse->insertControlPoint(p, 0);
  327. for (ssize_t i = 1; i < pReverse->count(); ++i)
  328. {
  329. Vec2 current = pReverse->getControlPointAtIndex(i);
  330. current = -current;
  331. Vec2 abs = current + p;
  332. pReverse->replaceControlPoint(abs, i);
  333. p = abs;
  334. }
  335. return CardinalSplineBy::create(_duration, pReverse, _tension);
  336. }
  337. void CardinalSplineBy::startWithTarget(cocos2d::Node *target)
  338. {
  339. CardinalSplineTo::startWithTarget(target);
  340. _startPosition = target->getPosition();
  341. }
  342. CardinalSplineBy* CardinalSplineBy::clone() const
  343. {
  344. // no copy constructor
  345. auto a = new (std::nothrow) CardinalSplineBy();
  346. a->initWithDuration(this->_duration, this->_points->clone(), this->_tension);
  347. a->autorelease();
  348. return a;
  349. }
  350. /* CatmullRomTo
  351. */
  352. CatmullRomTo* CatmullRomTo::create(float dt, cocos2d::PointArray *points)
  353. {
  354. CatmullRomTo *ret = new (std::nothrow) CatmullRomTo();
  355. if (ret)
  356. {
  357. if (ret->initWithDuration(dt, points))
  358. {
  359. ret->autorelease();
  360. }
  361. else
  362. {
  363. CC_SAFE_RELEASE_NULL(ret);
  364. }
  365. }
  366. return ret;
  367. }
  368. bool CatmullRomTo::initWithDuration(float dt, cocos2d::PointArray *points)
  369. {
  370. if (CardinalSplineTo::initWithDuration(dt, points, 0.5f))
  371. {
  372. return true;
  373. }
  374. return false;
  375. }
  376. CatmullRomTo* CatmullRomTo::clone() const
  377. {
  378. // no copy constructor
  379. auto a = new (std::nothrow) CatmullRomTo();
  380. a->initWithDuration(this->_duration, this->_points->clone());
  381. a->autorelease();
  382. return a;
  383. }
  384. CatmullRomTo* CatmullRomTo::reverse() const
  385. {
  386. PointArray *reverse = _points->reverse();
  387. return CatmullRomTo::create(_duration, reverse);
  388. }
  389. /* CatmullRomBy
  390. */
  391. CatmullRomBy* CatmullRomBy::create(float dt, cocos2d::PointArray *points)
  392. {
  393. CatmullRomBy *ret = new (std::nothrow) CatmullRomBy();
  394. if (ret)
  395. {
  396. if (ret->initWithDuration(dt, points))
  397. {
  398. ret->autorelease();
  399. }
  400. else
  401. {
  402. CC_SAFE_RELEASE_NULL(ret);
  403. }
  404. }
  405. return ret;
  406. }
  407. bool CatmullRomBy::initWithDuration(float dt, cocos2d::PointArray *points)
  408. {
  409. if (CardinalSplineTo::initWithDuration(dt, points, 0.5f))
  410. {
  411. return true;
  412. }
  413. return false;
  414. }
  415. CatmullRomBy* CatmullRomBy::clone() const
  416. {
  417. // no copy constructor
  418. auto a = new (std::nothrow) CatmullRomBy();
  419. a->initWithDuration(this->_duration, this->_points->clone());
  420. a->autorelease();
  421. return a;
  422. }
  423. CatmullRomBy* CatmullRomBy::reverse() const
  424. {
  425. PointArray *copyConfig = _points->clone();
  426. //
  427. // convert "absolutes" to "diffs"
  428. //
  429. Vec2 p = copyConfig->getControlPointAtIndex(0);
  430. for (ssize_t i = 1; i < copyConfig->count(); ++i)
  431. {
  432. Vec2 current = copyConfig->getControlPointAtIndex(i);
  433. Vec2 diff = current - p;
  434. copyConfig->replaceControlPoint(diff, i);
  435. p = current;
  436. }
  437. // convert to "diffs" to "reverse absolute"
  438. PointArray *reverse = copyConfig->reverse();
  439. // 1st element (which should be 0,0) should be here too
  440. p = reverse->getControlPointAtIndex(reverse->count()-1);
  441. reverse->removeControlPointAtIndex(reverse->count()-1);
  442. p = -p;
  443. reverse->insertControlPoint(p, 0);
  444. for (ssize_t i = 1; i < reverse->count(); ++i)
  445. {
  446. Vec2 current = reverse->getControlPointAtIndex(i);
  447. current = -current;
  448. Vec2 abs = current + p;
  449. reverse->replaceControlPoint(abs, i);
  450. p = abs;
  451. }
  452. return CatmullRomBy::create(_duration, reverse);
  453. }
  454. NS_CC_END;