CCActionCatmullRom.cpp 13 KB

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