CCActionCatmullRom.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  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. #include <iterator>
  36. using namespace std;
  37. NS_CC_BEGIN;
  38. /*
  39. * Implementation of PointArray
  40. */
  41. PointArray* PointArray::create(ssize_t capacity)
  42. {
  43. PointArray* pointArray = new (std::nothrow) PointArray();
  44. if (pointArray && pointArray->initWithCapacity(capacity))
  45. {
  46. pointArray->autorelease();
  47. return pointArray;
  48. }
  49. delete pointArray;
  50. return nullptr;
  51. }
  52. bool PointArray::initWithCapacity(ssize_t capacity)
  53. {
  54. _controlPoints.reserve(capacity);
  55. return true;
  56. }
  57. PointArray* PointArray::clone() const
  58. {
  59. vector<Vec2> newArray = _controlPoints;
  60. PointArray *points = new (std::nothrow) PointArray();
  61. points->initWithCapacity(10);
  62. points->setControlPoints(std::move(newArray));
  63. points->autorelease();
  64. return points;
  65. }
  66. PointArray::~PointArray()
  67. {
  68. CCLOGINFO("deallocing PointArray: %p", this);
  69. }
  70. PointArray::PointArray() {}
  71. const std::vector<Vec2>& PointArray::getControlPoints() const
  72. {
  73. return _controlPoints;
  74. }
  75. void PointArray::setControlPoints(vector<Vec2> controlPoints)
  76. {
  77. _controlPoints = std::move(controlPoints);
  78. }
  79. void PointArray::addControlPoint(const Vec2& controlPoint)
  80. {
  81. _controlPoints.push_back(controlPoint);
  82. }
  83. void PointArray::insertControlPoint(const Vec2& controlPoint, ssize_t index)
  84. {
  85. _controlPoints.insert(std::next(_controlPoints.begin(), index), controlPoint);
  86. }
  87. const Vec2& PointArray::getControlPointAtIndex(ssize_t index) const
  88. {
  89. index = MIN(static_cast<ssize_t>(_controlPoints.size())-1, MAX(index, 0));
  90. return _controlPoints.at(index);
  91. }
  92. void PointArray::replaceControlPoint(const Vec2& controlPoint, ssize_t index)
  93. {
  94. _controlPoints.at(index) = controlPoint;
  95. }
  96. void PointArray::removeControlPointAtIndex(ssize_t index)
  97. {
  98. vector<Vec2>::iterator iter = std::next(_controlPoints.begin(), index);
  99. _controlPoints.erase(iter);
  100. }
  101. ssize_t PointArray::count() const
  102. {
  103. return _controlPoints.size();
  104. }
  105. PointArray* PointArray::reverse() const
  106. {
  107. vector<Vec2> newArray;
  108. newArray.reserve(_controlPoints.size());
  109. for (auto iter = _controlPoints.rbegin(), iterRend = _controlPoints.rend(); iter != iterRend; ++iter)
  110. {
  111. newArray.push_back(*iter);
  112. }
  113. PointArray *config = PointArray::create(0);
  114. config->setControlPoints(std::move(newArray));
  115. return config;
  116. }
  117. void PointArray::reverseInline()
  118. {
  119. const size_t l = _controlPoints.size();
  120. Vec2 *p1 = nullptr;
  121. Vec2 *p2 = nullptr;
  122. float x, y;
  123. for (size_t i = 0; i < l/2; ++i)
  124. {
  125. p1 = &_controlPoints.at(i);
  126. p2 = &_controlPoints.at(l-i-1);
  127. x = p1->x;
  128. y = p1->y;
  129. p1->x = p2->x;
  130. p1->y = p2->y;
  131. p2->x = x;
  132. p2->y = y;
  133. }
  134. }
  135. // CatmullRom Spline formula:
  136. Vec2 ccCardinalSplineAt(const Vec2 &p0, const Vec2 &p1, const Vec2 &p2, const Vec2 &p3, float tension, float t)
  137. {
  138. float t2 = t * t;
  139. float t3 = t2 * t;
  140. /*
  141. * 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
  142. */
  143. float s = (1 - tension) / 2;
  144. float b1 = s * ((-t3 + (2 * t2)) - t); // s(-t3 + 2 t2 - t)P1
  145. float b2 = s * (-t3 + t2) + (2 * t3 - 3 * t2 + 1); // s(-t3 + t2)P2 + (2 t3 - 3 t2 + 1)P2
  146. float b3 = s * (t3 - 2 * t2 + t) + (-2 * t3 + 3 * t2); // s(t3 - 2 t2 + t)P3 + (-2 t3 + 3 t2)P3
  147. float b4 = s * (t3 - t2); // s(t3 - t2)P4
  148. float x = (p0.x*b1 + p1.x*b2 + p2.x*b3 + p3.x*b4);
  149. float y = (p0.y*b1 + p1.y*b2 + p2.y*b3 + p3.y*b4);
  150. return Vec2(x,y);
  151. }
  152. /* Implementation of CardinalSplineTo
  153. */
  154. CardinalSplineTo* CardinalSplineTo::create(float duration, PointArray *points, float tension)
  155. {
  156. CardinalSplineTo *ret = new (std::nothrow) CardinalSplineTo();
  157. if (ret)
  158. {
  159. if (ret->initWithDuration(duration, points, tension))
  160. {
  161. ret->autorelease();
  162. }
  163. else
  164. {
  165. CC_SAFE_RELEASE_NULL(ret);
  166. }
  167. }
  168. return ret;
  169. }
  170. bool CardinalSplineTo::initWithDuration(float duration, PointArray *points, float tension)
  171. {
  172. CCASSERT(points->count() > 0, "Invalid configuration. It must at least have one control point");
  173. if (ActionInterval::initWithDuration(duration))
  174. {
  175. this->setPoints(points);
  176. this->_tension = tension;
  177. return true;
  178. }
  179. return false;
  180. }
  181. CardinalSplineTo::~CardinalSplineTo()
  182. {
  183. CC_SAFE_RELEASE_NULL(_points);
  184. }
  185. CardinalSplineTo::CardinalSplineTo()
  186. : _points(nullptr)
  187. , _deltaT(0.f)
  188. , _tension(0.f)
  189. {
  190. }
  191. void CardinalSplineTo::startWithTarget(Node *target)
  192. {
  193. ActionInterval::startWithTarget(target);
  194. // _deltaT = (float) 1 / _points->count();
  195. // Issue #1441
  196. _deltaT = (float) 1 / (_points->count() - 1);
  197. _previousPosition = target->getPosition();
  198. _accumulatedDiff.setZero();
  199. }
  200. CardinalSplineTo* CardinalSplineTo::clone() const
  201. {
  202. // no copy constructor
  203. auto a = new (std::nothrow) CardinalSplineTo();
  204. a->initWithDuration(this->_duration, this->_points->clone(), this->_tension);
  205. a->autorelease();
  206. return a;
  207. }
  208. void CardinalSplineTo::update(float time)
  209. {
  210. ssize_t p;
  211. float lt;
  212. // eg.
  213. // p..p..p..p..p..p..p
  214. // 1..2..3..4..5..6..7
  215. // want p to be 1, 2, 3, 4, 5, 6
  216. if (time == 1)
  217. {
  218. p = _points->count() - 1;
  219. lt = 1;
  220. }
  221. else
  222. {
  223. p = time / _deltaT;
  224. lt = (time - _deltaT * (float)p) / _deltaT;
  225. }
  226. // Interpolate
  227. Vec2 pp0 = _points->getControlPointAtIndex(p-1);
  228. Vec2 pp1 = _points->getControlPointAtIndex(p+0);
  229. Vec2 pp2 = _points->getControlPointAtIndex(p+1);
  230. Vec2 pp3 = _points->getControlPointAtIndex(p+2);
  231. Vec2 newPos = ccCardinalSplineAt(pp0, pp1, pp2, pp3, _tension, lt);
  232. #if CC_ENABLE_STACKABLE_ACTIONS
  233. // Support for stacked actions
  234. Node *node = _target;
  235. Vec2 diff = node->getPosition() - _previousPosition;
  236. if( diff.x !=0 || diff.y != 0 )
  237. {
  238. _accumulatedDiff = _accumulatedDiff + diff;
  239. newPos = newPos + _accumulatedDiff;
  240. }
  241. #endif
  242. this->updatePosition(newPos);
  243. }
  244. void CardinalSplineTo::updatePosition(const Vec2 &newPos)
  245. {
  246. _target->setPosition(newPos);
  247. _previousPosition = newPos;
  248. }
  249. CardinalSplineTo* CardinalSplineTo::reverse() const
  250. {
  251. PointArray *pReverse = _points->reverse();
  252. return CardinalSplineTo::create(_duration, pReverse, _tension);
  253. }
  254. /* CardinalSplineBy
  255. */
  256. CardinalSplineBy* CardinalSplineBy::create(float duration, PointArray *points, float tension)
  257. {
  258. CardinalSplineBy *ret = new (std::nothrow) CardinalSplineBy();
  259. if (ret)
  260. {
  261. if (ret->initWithDuration(duration, points, tension))
  262. {
  263. ret->autorelease();
  264. }
  265. else
  266. {
  267. CC_SAFE_RELEASE_NULL(ret);
  268. }
  269. }
  270. return ret;
  271. }
  272. CardinalSplineBy::CardinalSplineBy() : _startPosition(0,0)
  273. {
  274. }
  275. void CardinalSplineBy::updatePosition(const Vec2 &newPos)
  276. {
  277. Vec2 p = newPos + _startPosition;
  278. _target->setPosition(p);
  279. _previousPosition = p;
  280. }
  281. CardinalSplineBy* CardinalSplineBy::reverse() const
  282. {
  283. PointArray *copyConfig = _points->clone();
  284. //
  285. // convert "absolutes" to "diffs"
  286. //
  287. Vec2 p = copyConfig->getControlPointAtIndex(0);
  288. for (ssize_t i = 1; i < copyConfig->count(); ++i)
  289. {
  290. Vec2 current = copyConfig->getControlPointAtIndex(i);
  291. Vec2 diff = current - p;
  292. copyConfig->replaceControlPoint(diff, i);
  293. p = current;
  294. }
  295. // convert to "diffs" to "reverse absolute"
  296. PointArray *pReverse = copyConfig->reverse();
  297. // 1st element (which should be 0,0) should be here too
  298. p = pReverse->getControlPointAtIndex(pReverse->count()-1);
  299. pReverse->removeControlPointAtIndex(pReverse->count()-1);
  300. p = -p;
  301. pReverse->insertControlPoint(p, 0);
  302. for (ssize_t i = 1; i < pReverse->count(); ++i)
  303. {
  304. Vec2 current = pReverse->getControlPointAtIndex(i);
  305. current = -current;
  306. Vec2 abs = current + p;
  307. pReverse->replaceControlPoint(abs, i);
  308. p = abs;
  309. }
  310. return CardinalSplineBy::create(_duration, pReverse, _tension);
  311. }
  312. void CardinalSplineBy::startWithTarget(Node *target)
  313. {
  314. CardinalSplineTo::startWithTarget(target);
  315. _startPosition = target->getPosition();
  316. }
  317. CardinalSplineBy* CardinalSplineBy::clone() const
  318. {
  319. // no copy constructor
  320. auto a = new (std::nothrow) CardinalSplineBy();
  321. a->initWithDuration(this->_duration, this->_points->clone(), this->_tension);
  322. a->autorelease();
  323. return a;
  324. }
  325. /* CatmullRomTo
  326. */
  327. CatmullRomTo* CatmullRomTo::create(float dt, PointArray *points)
  328. {
  329. CatmullRomTo *ret = new (std::nothrow) CatmullRomTo();
  330. if (ret)
  331. {
  332. if (ret->initWithDuration(dt, points))
  333. {
  334. ret->autorelease();
  335. }
  336. else
  337. {
  338. CC_SAFE_RELEASE_NULL(ret);
  339. }
  340. }
  341. return ret;
  342. }
  343. bool CatmullRomTo::initWithDuration(float dt, PointArray *points)
  344. {
  345. if (CardinalSplineTo::initWithDuration(dt, points, 0.5f))
  346. {
  347. return true;
  348. }
  349. return false;
  350. }
  351. CatmullRomTo* CatmullRomTo::clone() const
  352. {
  353. // no copy constructor
  354. auto a = new (std::nothrow) CatmullRomTo();
  355. a->initWithDuration(this->_duration, this->_points->clone());
  356. a->autorelease();
  357. return a;
  358. }
  359. CatmullRomTo* CatmullRomTo::reverse() const
  360. {
  361. PointArray *reverse = _points->reverse();
  362. return CatmullRomTo::create(_duration, reverse);
  363. }
  364. /* CatmullRomBy
  365. */
  366. CatmullRomBy* CatmullRomBy::create(float dt, PointArray *points)
  367. {
  368. CatmullRomBy *ret = new (std::nothrow) CatmullRomBy();
  369. if (ret)
  370. {
  371. if (ret->initWithDuration(dt, points))
  372. {
  373. ret->autorelease();
  374. }
  375. else
  376. {
  377. CC_SAFE_RELEASE_NULL(ret);
  378. }
  379. }
  380. return ret;
  381. }
  382. bool CatmullRomBy::initWithDuration(float dt, PointArray *points)
  383. {
  384. if (CardinalSplineTo::initWithDuration(dt, points, 0.5f))
  385. {
  386. return true;
  387. }
  388. return false;
  389. }
  390. CatmullRomBy* CatmullRomBy::clone() const
  391. {
  392. // no copy constructor
  393. auto a = new (std::nothrow) CatmullRomBy();
  394. a->initWithDuration(this->_duration, this->_points->clone());
  395. a->autorelease();
  396. return a;
  397. }
  398. CatmullRomBy* CatmullRomBy::reverse() const
  399. {
  400. PointArray *copyConfig = _points->clone();
  401. //
  402. // convert "absolutes" to "diffs"
  403. //
  404. Vec2 p = copyConfig->getControlPointAtIndex(0);
  405. for (ssize_t i = 1; i < copyConfig->count(); ++i)
  406. {
  407. Vec2 current = copyConfig->getControlPointAtIndex(i);
  408. Vec2 diff = current - p;
  409. copyConfig->replaceControlPoint(diff, i);
  410. p = current;
  411. }
  412. // convert to "diffs" to "reverse absolute"
  413. PointArray *reverse = copyConfig->reverse();
  414. // 1st element (which should be 0,0) should be here too
  415. p = reverse->getControlPointAtIndex(reverse->count()-1);
  416. reverse->removeControlPointAtIndex(reverse->count()-1);
  417. p = -p;
  418. reverse->insertControlPoint(p, 0);
  419. for (ssize_t i = 1; i < reverse->count(); ++i)
  420. {
  421. Vec2 current = reverse->getControlPointAtIndex(i);
  422. current = -current;
  423. Vec2 abs = current + p;
  424. reverse->replaceControlPoint(abs, i);
  425. p = abs;
  426. }
  427. return CatmullRomBy::create(_duration, reverse);
  428. }
  429. NS_CC_END;