BsGUICurves.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include <utility>
  4. #include "GUI/BsGUICurves.h"
  5. #include "GUI/BsGUIDimensions.h"
  6. #include "GUI/BsGUIMouseEvent.h"
  7. #include "GUI/BsGUIHelper.h"
  8. #include "GUI/BsGUICanvas.h"
  9. #include "Math/BsLine2.h"
  10. #include "GUI/BsGUIWidget.h"
  11. namespace bs
  12. {
  13. static constexpr int LINE_SPLIT_WIDTH = 2;
  14. static constexpr int TANGENT_LINE_DISTANCE = 30;
  15. static constexpr Color COLOR_MID_GRAY = Color(90.0f / 255.0f, 90.0f / 255.0f, 90.0f / 255.0f, 1.0f);
  16. static constexpr Color COLOR_DARK_GRAY = Color(40.0f / 255.0f, 40.0f / 255.0f, 40.0f / 255.0f, 1.0f);
  17. GUICurves::GUICurves(CurveDrawOptions drawOptions, const String& styleName, const GUIDimensions& dimensions)
  18. :GUITimeline(styleName, dimensions), mDrawOptions(drawOptions), mTickHandler(GUITickStepType::Time)
  19. { }
  20. const String& GUICurves::getGUITypeName()
  21. {
  22. static String name = "Curves";
  23. return name;
  24. }
  25. GUICurves* GUICurves::create(const String& styleName)
  26. {
  27. return new (bs_alloc<GUICurves>()) GUICurves(CurveDrawOption::DrawMarkers | CurveDrawOption::DrawKeyframes,
  28. getStyleName<GUICurves>(styleName), GUIDimensions::create());
  29. }
  30. GUICurves* GUICurves::create(CurveDrawOptions drawOptions, const String& styleName)
  31. {
  32. return new (bs_alloc<GUICurves>()) GUICurves(drawOptions, getStyleName<GUICurves>(styleName), GUIDimensions::create());
  33. }
  34. GUICurves* GUICurves::create(const GUIOptions& options, const String& styleName)
  35. {
  36. return new (bs_alloc<GUICurves>()) GUICurves(CurveDrawOption::DrawMarkers | CurveDrawOption::DrawKeyframes,
  37. getStyleName<GUICurves>(styleName), GUIDimensions::create(options));
  38. }
  39. GUICurves* GUICurves::create(CurveDrawOptions drawOptions, const GUIOptions& options, const String& styleName)
  40. {
  41. return new (bs_alloc<GUICurves>()) GUICurves(drawOptions, getStyleName<GUICurves>(styleName),
  42. GUIDimensions::create(options));
  43. }
  44. void GUICurves::setCurves(const Vector<CurveDrawInfo>& curves)
  45. {
  46. mCurves = curves;
  47. _markContentAsDirty();
  48. }
  49. void GUICurves::setRange(float xRange, float yRange)
  50. {
  51. GUITimeline::setRange(xRange);
  52. mYRange = yRange;
  53. }
  54. void GUICurves::setOffset(const Vector2& offset)
  55. {
  56. GUITimeline::setOffset(offset.x);
  57. mYOffset = offset.y;
  58. }
  59. void GUICurves::centerAndZoom()
  60. {
  61. Vector<TAnimationCurve<float>> curves(mCurves.size());
  62. UINT32 idx = 0;
  63. for(auto& entry : mCurves)
  64. curves[idx++] = entry.curve;
  65. float xMin, xMax;
  66. float yMin, yMax;
  67. AnimationUtility::calculateRange(curves, xMin, xMax, yMin, yMax);
  68. float xRange = xMax - xMin;
  69. float yRange = (yMax - yMin);
  70. float yOffset = yMin + yRange * 0.5f;
  71. // Add padding to y range
  72. yRange *= 1.05f;
  73. // Don't allow zero range
  74. if (xRange == 0.0f)
  75. xRange = 60.0f;
  76. if (yRange == 0.0f)
  77. yRange = 10.0f;
  78. setOffset(Vector2(xMin, yOffset));
  79. setRange(xRange, yRange);
  80. }
  81. UINT32 GUICurves::findCurve(const Vector2I& pixelCoords)
  82. {
  83. Vector2 curveCoords;
  84. if (!pixelToCurveSpace(pixelCoords, curveCoords, true))
  85. return (UINT32)-1;
  86. const float time = curveCoords.x;
  87. float nearestDistance = std::numeric_limits<float>::max();
  88. auto curveIdx = (UINT32)-1;
  89. for (UINT32 i = 0; i < (UINT32)mCurves.size(); i++)
  90. {
  91. const TAnimationCurve<float>& curve = mCurves[i].curve;
  92. const float value = curve.evaluate(time, false);
  93. const Vector2I curPixelPos = curveToPixelSpace(Vector2(time, value));
  94. const auto distanceToKey = (float)pixelCoords.manhattanDist(curPixelPos);
  95. if (distanceToKey < nearestDistance)
  96. {
  97. nearestDistance = distanceToKey;
  98. curveIdx = i;
  99. }
  100. }
  101. // We're not near any curve
  102. if (nearestDistance > 5.0f)
  103. return (UINT32)-1;
  104. return curveIdx;
  105. }
  106. /**
  107. * Attempts to find a keyframe under the provided coordinates.
  108. *
  109. * @param[in] pixelCoords Coordinates relative to this GUI element in pixels.
  110. * @param[out] keyframe Output object containing keyframe index and index of the curve it belongs to. Only
  111. * valid if method returns true.
  112. * @return True if there is a keyframe under the coordinates, false otherwise.
  113. */
  114. bool GUICurves::findKeyFrame(const Vector2I& pixelCoords, KeyframeRef& keyframe)
  115. {
  116. keyframe = KeyframeRef();
  117. float nearestDistance = std::numeric_limits<float>::max();
  118. for (UINT32 i = 0; i < (UINT32)mCurves.size(); i++)
  119. {
  120. const TAnimationCurve<float>& curve = mCurves[i].curve;
  121. const Vector<TKeyframe<float>>& keyframes = curve.getKeyFrames();
  122. for (UINT32 j = 0; j < (UINT32)keyframes.size(); j++)
  123. {
  124. const Vector2 keyframeCurveCoords = Vector2(keyframes[j].time, keyframes[j].value);
  125. const Vector2I keyframeCoords = curveToPixelSpace(keyframeCurveCoords);
  126. const auto distanceToKey = (float)pixelCoords.manhattanDist(keyframeCoords);
  127. if (distanceToKey < nearestDistance)
  128. {
  129. nearestDistance = distanceToKey;
  130. keyframe.keyIdx = j;
  131. keyframe.curveIdx = i;
  132. }
  133. }
  134. }
  135. return nearestDistance <= 5.0f;
  136. }
  137. /**
  138. * Attempts to find a a tangent handle under the provided coordinates.
  139. *
  140. * @param[in] pixelCoords Coordinates relative to this GUI element in pixels.
  141. * @param[in] tangent Output object containing keyframe information and tangent type. Only valid if method
  142. * returns true.
  143. * @return True if there is a tangent handle under the coordinates, false otherwise.
  144. */
  145. bool GUICurves::findTangent(const Vector2I& pixelCoords, TangentRef& tangent)
  146. {
  147. tangent = TangentRef();
  148. float nearestDistance = std::numeric_limits<float>::max();
  149. for (auto& entry : mSelectedKeyframes)
  150. {
  151. KeyframeRef keyframeRef = entry.keyframeRef;
  152. if (keyframeRef.curveIdx < 0 || keyframeRef.curveIdx >= (INT32)mCurves.size())
  153. continue;
  154. const TAnimationCurve<float>& curve = mCurves[keyframeRef.curveIdx].curve;
  155. if (keyframeRef.keyIdx < 0 || keyframeRef.keyIdx >= (INT32)curve.getNumKeyFrames())
  156. continue;
  157. const Vector<TKeyframe<float>>& keyframes = curve.getKeyFrames();
  158. TangentMode tangentMode = entry.tangentMode;
  159. if (isTangentDisplayed(tangentMode, TangentType::In))
  160. {
  161. const Vector2I tangentCoords = getTangentPosition(keyframes[keyframeRef.keyIdx], TangentType::In);
  162. const auto distanceToHandle = (float)pixelCoords.manhattanDist(tangentCoords);
  163. if (distanceToHandle < nearestDistance)
  164. {
  165. nearestDistance = distanceToHandle;
  166. tangent.keyframeRef.keyIdx = keyframeRef.keyIdx;
  167. tangent.keyframeRef.curveIdx = keyframeRef.curveIdx;
  168. tangent.type = TangentType::In;
  169. }
  170. }
  171. if (isTangentDisplayed(tangentMode, TangentType::Out))
  172. {
  173. const Vector2I tangentCoords = getTangentPosition(keyframes[keyframeRef.keyIdx], TangentType::Out);
  174. const auto distanceToHandle = (float)pixelCoords.manhattanDist(tangentCoords);
  175. if (distanceToHandle < nearestDistance)
  176. {
  177. nearestDistance = distanceToHandle;
  178. tangent.keyframeRef.keyIdx = keyframeRef.keyIdx;
  179. tangent.keyframeRef.curveIdx = keyframeRef.curveIdx;
  180. tangent.type = TangentType::Out;
  181. }
  182. }
  183. }
  184. return nearestDistance <= 5.0f;
  185. }
  186. bool GUICurves::pixelToCurveSpace(const Vector2I& pixelCoords, Vector2& curveCoords, bool padding) const
  187. {
  188. const Rect2I& bounds = mLayoutData.area;
  189. bool outsideHorizontal;
  190. if (padding)
  191. outsideHorizontal = pixelCoords.x < 0 || pixelCoords.x >= (INT32)bounds.width;
  192. else
  193. outsideHorizontal = pixelCoords.x < (INT32)mPadding || pixelCoords.x >= ((INT32)bounds.width - (INT32)mPadding);
  194. // Check if outside of curve drawing bounds
  195. if (outsideHorizontal || pixelCoords.y < 0 || pixelCoords.y >= (INT32)bounds.height)
  196. {
  197. curveCoords = Vector2::ZERO;
  198. return false;
  199. }
  200. // Find time and value of the place under the coordinates
  201. const Vector2I relativeCoords = pixelCoords - Vector2I(mPadding, 0);
  202. const float lengthPerPixel = getRange() / getDrawableWidth();
  203. const float heightPerPixel = mYRange / mLayoutData.area.height;
  204. const float centerOffset = mYRange / 2.0f;
  205. const float t = mOffset + relativeCoords.x * lengthPerPixel;
  206. const float value = mYOffset + centerOffset - relativeCoords.y * heightPerPixel;
  207. curveCoords = Vector2();
  208. curveCoords.x = t;
  209. curveCoords.y = value;
  210. return true;
  211. }
  212. Vector2I GUICurves::curveToPixelSpace(const Vector2& curveCoords) const
  213. {
  214. const UINT32 heightOffset = mLayoutData.area.height / 2; // So that y = 0 is at center of canvas
  215. const Vector2 relativeCurveCoords = curveCoords - Vector2(mOffset, mYOffset);
  216. Vector2I pixelCoords = Vector2I();
  217. pixelCoords.x = (int)((relativeCurveCoords.x / getRange()) * getDrawableWidth()) + mPadding;
  218. pixelCoords.y = heightOffset - (int)((relativeCurveCoords.y / mYRange) * mLayoutData.area.height);
  219. return pixelCoords;
  220. }
  221. Vector2 GUICurves::tangentToNormal(float tangent) const
  222. {
  223. if (tangent == std::numeric_limits<float>::infinity())
  224. return Vector2(0, 1);
  225. Vector2 normal = Vector2(1, tangent);
  226. return Vector2::normalize(normal);
  227. }
  228. Vector2I GUICurves::getTangentPosition(const TKeyframe<float>& keyFrame, TangentType type) const
  229. {
  230. const Vector2I position = curveToPixelSpace(Vector2(keyFrame.time, keyFrame.value));
  231. Vector2 normal;
  232. if (type == TangentType::In)
  233. normal = -tangentToNormal(keyFrame.inTangent);
  234. else
  235. normal = tangentToNormal(keyFrame.outTangent);
  236. // X/Y ranges aren't scaled 1:1, adjust normal accordingly
  237. normal.x /= getRange();
  238. normal.y /= mYRange;
  239. normal = Vector2::normalize(normal);
  240. // Convert normal (in percentage) to pixel values
  241. Vector2I offset = Vector2I((int)(normal.x * TANGENT_LINE_DISTANCE),
  242. (int)(-normal.y * TANGENT_LINE_DISTANCE));
  243. return position + offset;
  244. }
  245. bool GUICurves::isTangentDisplayed(TangentMode mode, TangentType type) const
  246. {
  247. if (mode == TangentModeBits::Auto)
  248. return false;
  249. else if (mode == TangentModeBits::Free)
  250. return true;
  251. if (type == TangentType::In)
  252. return mode.isSet(TangentModeBits::InFree);
  253. else
  254. return mode.isSet(TangentModeBits::OutFree);
  255. }
  256. void GUICurves::drawFrameMarker(float t, Color color, bool onTop)
  257. {
  258. const INT32 xPos = (INT32)(((t - mOffset) / getRange()) * getDrawableWidth()) + mPadding;
  259. const Vector2I start = Vector2I(xPos, 0);
  260. const Vector2I end = Vector2I(xPos, mLayoutData.area.height);
  261. UINT8 depth;
  262. if (onTop)
  263. depth = 110;
  264. else
  265. depth = 130;
  266. mCanvas->drawLine(start, end, color, depth);
  267. }
  268. void GUICurves::drawCenterLine()
  269. {
  270. const Vector2I center = curveToPixelSpace(Vector2(0.0f, 0.0f));
  271. const Vector2I start = Vector2I(0, center.y);
  272. const Vector2I end = Vector2I(mLayoutData.area.width, center.y);
  273. mCanvas->drawLine(start, end, COLOR_DARK_GRAY, 130);
  274. }
  275. void GUICurves::drawDiamond(Vector2I center, int size, Color innerColor, Color outerColor)
  276. {
  277. const Vector2I a = Vector2I(center.x - size, center.y);
  278. const Vector2I b = Vector2I(center.x, center.y - size);
  279. const Vector2I c = Vector2I(center.x + size, center.y);
  280. const Vector2I d = Vector2I(center.x, center.y + size);
  281. // Draw diamond shape
  282. const Vector<Vector2I> linePoints = { a, b, c, d, a };
  283. const Vector<Vector2I> trianglePoints = { b, c, a, d };
  284. mCanvas->drawTriangleStrip(trianglePoints, innerColor, 101);
  285. mCanvas->drawPolyLine(linePoints, outerColor, 100);
  286. }
  287. void GUICurves::drawKeyframe(float t, float y, bool selected)
  288. {
  289. const Vector2I pixelCoords = curveToPixelSpace(Vector2(t, y));
  290. if (selected)
  291. drawDiamond(pixelCoords, 3, Color::White, Color::BansheeOrange);
  292. else
  293. drawDiamond(pixelCoords, 3, Color::White, Color::Black);
  294. }
  295. void GUICurves::drawTangents(const TKeyframe<float>& keyFrame, TangentMode tangentMode)
  296. {
  297. const Vector2I keyframeCoords = curveToPixelSpace(Vector2(keyFrame.time, keyFrame.value));
  298. if (isTangentDisplayed(tangentMode, TangentType::In))
  299. {
  300. Vector2I tangentCoords = getTangentPosition(keyFrame, TangentType::In);
  301. mCanvas->drawLine(keyframeCoords, tangentCoords, Color::LightGray);
  302. drawDiamond(tangentCoords, 2, Color::Green, Color::Black);
  303. }
  304. if (isTangentDisplayed(tangentMode, TangentType::Out))
  305. {
  306. Vector2I tangentCoords = getTangentPosition(keyFrame, TangentType::Out);
  307. mCanvas->drawLine(keyframeCoords, tangentCoords, Color::LightGray);
  308. drawDiamond(tangentCoords, 2, Color::Green, Color::Black);
  309. }
  310. }
  311. void GUICurves::drawCurve(const TAnimationCurve<float>& curve, const Color& color)
  312. {
  313. const float range = getRangeWithPadding();
  314. const float lengthPerPixel = range / getDrawableWidth();
  315. const Vector<TKeyframe<float>>& keyframes = curve.getKeyFrames();
  316. if (keyframes.empty())
  317. return;
  318. // Draw start line
  319. {
  320. const float curveStart = keyframes[0].time;
  321. const float curveValue = curve.evaluate(curveStart, false);
  322. const Vector2I end = curveToPixelSpace(Vector2(curveStart, curveValue));
  323. const Vector2I start = Vector2I(-(INT32)mPadding, end.y);
  324. if (start.x < end.x)
  325. mCanvas->drawLine(start, end, COLOR_MID_GRAY);
  326. }
  327. Vector<Vector2I> linePoints;
  328. // Draw in between keyframes
  329. const float startVisibleTime = mOffset;
  330. const float endVisibleTime = startVisibleTime + range;
  331. for (INT32 i = 0; i < (INT32)keyframes.size() - 1; i++)
  332. {
  333. const float start = keyframes[i].time;
  334. const float end = keyframes[i + 1].time;
  335. if (end < startVisibleTime || start > endVisibleTime)
  336. continue;
  337. const bool isStep = keyframes[i].outTangent == std::numeric_limits<float>::infinity() ||
  338. keyframes[i + 1].inTangent == std::numeric_limits<float>::infinity();
  339. // If step tangent, draw the required lines without sampling, as the sampling will miss the step
  340. if (isStep)
  341. {
  342. const float startValue = curve.evaluate(start, false);
  343. const float endValue = curve.evaluate(end, false);
  344. linePoints.push_back(curveToPixelSpace(Vector2(start, startValue)));
  345. linePoints.push_back(curveToPixelSpace(Vector2(end, startValue)));
  346. linePoints.push_back(curveToPixelSpace(Vector2(end, endValue)));
  347. }
  348. else // Draw normally
  349. {
  350. float splitIncrement = LINE_SPLIT_WIDTH * lengthPerPixel;
  351. const float startValue = keyframes[i].value;
  352. const float endValue = keyframes[i + 1].value;
  353. Vector2I startPixel;
  354. startPixel.x = (int)(start / lengthPerPixel);
  355. startPixel.y = (int)(startValue / lengthPerPixel);
  356. Vector2I endPixel;
  357. endPixel.x = (int)(end / lengthPerPixel);
  358. endPixel.y = (int)(endValue / lengthPerPixel);
  359. const UINT32 distance = startPixel.manhattanDist(endPixel);
  360. INT32 numSplits;
  361. if (distance > 0)
  362. {
  363. const float fNumSplits = distance / splitIncrement;
  364. numSplits = Math::ceilToInt(fNumSplits);
  365. splitIncrement = distance / (float)numSplits;
  366. }
  367. else
  368. {
  369. numSplits = 1;
  370. splitIncrement = 0.0f;
  371. }
  372. for (int j = 0; j < numSplits; j++)
  373. {
  374. const float t = std::min(start + j * splitIncrement, end);
  375. const float value = curve.evaluate(t, false);
  376. linePoints.push_back(curveToPixelSpace(Vector2(t, value)));
  377. }
  378. }
  379. }
  380. mCanvas->drawPolyLine(linePoints, color);
  381. // Draw end line
  382. {
  383. const float curveEnd = keyframes[keyframes.size() - 1].time;
  384. const float curveValue = curve.evaluate(curveEnd, false);
  385. const Vector2I start = curveToPixelSpace(Vector2(curveEnd, curveValue));
  386. const Vector2I end = Vector2I(mLayoutData.area.width, start.y);
  387. if (start.x < end.x)
  388. mCanvas->drawLine(start, end, COLOR_MID_GRAY);
  389. }
  390. }
  391. void GUICurves::drawCurveRange(const Vector<TAnimationCurve<float>>& curves, Color color)
  392. {
  393. const float range = getRangeWithPadding();
  394. if (curves.size() != 2)
  395. return;
  396. const Vector<TKeyframe<float>>* keyframes[2] = { &curves[0].getKeyFrames(), &curves[1].getKeyFrames() };
  397. if (keyframes[0]->empty() || keyframes[1]->empty())
  398. return;
  399. const UINT32 numSamples = (getDrawableWidth() + LINE_SPLIT_WIDTH - 1) / LINE_SPLIT_WIDTH;
  400. const float timePerSample = range / numSamples;
  401. float time = mOffset;
  402. const float lengthPerPixel = mRange / getDrawableWidth();
  403. time -= lengthPerPixel * mPadding;
  404. INT32 keyframeIndices[] = { 0, 0 };
  405. // Find first valid keyframe indices
  406. for (UINT32 curveIdx = 0; curveIdx < 2; curveIdx++)
  407. {
  408. keyframeIndices[curveIdx] = (INT32)keyframes[curveIdx]->size();
  409. for (UINT32 i = 0; i < (UINT32)keyframes[curveIdx]->size(); i++)
  410. {
  411. if ((*keyframes[curveIdx])[i].time > time)
  412. keyframeIndices[curveIdx] = i;
  413. }
  414. }
  415. Vector<float> times;
  416. Vector<float> points[2];
  417. // Determine start points
  418. for (int curveIdx = 0; curveIdx < 2; curveIdx++)
  419. {
  420. float value = curves[curveIdx].evaluate(time, false);
  421. points[curveIdx].push_back(value);
  422. }
  423. times.push_back(time);
  424. const float rangeEnd = mOffset + range;
  425. while (time < rangeEnd)
  426. {
  427. float nextTime = time + timePerSample;
  428. bool hasStep = false;
  429. // Determine time to sample at. Use fixed increments unless there's a step keyframe within our increment in
  430. // which case we use its time so we can evaluate it directly
  431. for (UINT32 curveIdx = 0; curveIdx < 2; curveIdx++)
  432. {
  433. const INT32 keyframeIdx = keyframeIndices[curveIdx];
  434. if (keyframeIdx < (INT32)keyframes[curveIdx]->size())
  435. {
  436. const TKeyframe<float>& keyframe = (*keyframes[curveIdx])[keyframeIdx];
  437. const bool isStep = keyframe.inTangent == std::numeric_limits<float>::infinity() ||
  438. keyframe.outTangent == std::numeric_limits<float>::infinity();
  439. if (isStep && keyframe.time <= nextTime)
  440. {
  441. nextTime = std::min(nextTime, keyframe.time);
  442. hasStep = true;
  443. }
  444. }
  445. }
  446. // Evaluate
  447. if (hasStep)
  448. {
  449. for (UINT32 curveIdx = 0; curveIdx < 2; curveIdx++)
  450. {
  451. const INT32 keyframeIdx = keyframeIndices[curveIdx];
  452. if (keyframeIdx < (INT32)keyframes[curveIdx]->size())
  453. {
  454. const TKeyframe<float>& keyframe = (*keyframes[curveIdx])[keyframeIdx];
  455. if (Math::approxEquals(keyframe.time, nextTime))
  456. {
  457. if (keyframeIdx > 0)
  458. {
  459. const TKeyframe<float>& prevKeyframe = (*keyframes[curveIdx])[keyframeIdx - 1];
  460. points[curveIdx].push_back(prevKeyframe.value);
  461. }
  462. else
  463. points[curveIdx].push_back(keyframe.value);
  464. points[curveIdx].push_back(keyframe.value);
  465. }
  466. else
  467. {
  468. // The other curve has step but this one doesn't, we just insert the same value twice
  469. float value = curves[curveIdx].evaluate(nextTime, false);
  470. points[curveIdx].push_back(value);
  471. points[curveIdx].push_back(value);
  472. }
  473. times.push_back(nextTime);
  474. times.push_back(nextTime);
  475. }
  476. }
  477. }
  478. else
  479. {
  480. for (UINT32 curveIdx = 0; curveIdx < 2; curveIdx++)
  481. points[curveIdx].push_back(curves[curveIdx].evaluate(nextTime, false));
  482. times.push_back(nextTime);
  483. }
  484. // Advance keyframe indices
  485. for (UINT32 curveIdx = 0; curveIdx < 2; curveIdx++)
  486. {
  487. INT32 keyframeIdx = keyframeIndices[curveIdx];
  488. while (keyframeIdx < (INT32)keyframes[curveIdx]->size())
  489. {
  490. const TKeyframe<float>& keyframe = (*keyframes[curveIdx])[keyframeIdx];
  491. if (keyframe.time > nextTime)
  492. break;
  493. keyframeIdx = ++keyframeIndices[curveIdx];
  494. }
  495. }
  496. time = nextTime;
  497. }
  498. // End points
  499. for (UINT32 curveIdx = 0; curveIdx < 2; curveIdx++)
  500. {
  501. float value = curves[curveIdx].evaluate(rangeEnd, false);
  502. points[curveIdx].push_back(value);
  503. }
  504. times.push_back(rangeEnd);
  505. const INT32 numQuads = (INT32)times.size() - 1;
  506. Vector<Vector2I> vertices;
  507. for (int i = 0; i < numQuads; i++)
  508. {
  509. const INT32 idxLeft = points[0][i] < points[1][i] ? 0 : 1;
  510. const INT32 idxRight = points[0][i + 1] < points[1][i + 1] ? 0 : 1;
  511. Vector2 left[] =
  512. {
  513. Vector2(times[i], points[0][i]),
  514. Vector2(times[i], points[1][i])
  515. };
  516. Vector2 right[] =
  517. {
  518. Vector2(times[i + 1], points[0][i + 1]),
  519. Vector2(times[i + 1], points[1][i + 1])
  520. };
  521. if (idxLeft == idxRight)
  522. {
  523. const INT32 idxA = idxLeft;
  524. const INT32 idxB = (idxLeft + 1) % 2;
  525. vertices.push_back(curveToPixelSpace(left[idxB]));
  526. vertices.push_back(curveToPixelSpace(right[idxB]));
  527. vertices.push_back(curveToPixelSpace(left[idxA]));
  528. vertices.push_back(curveToPixelSpace(right[idxB]));
  529. vertices.push_back(curveToPixelSpace(right[idxA]));
  530. vertices.push_back(curveToPixelSpace(left[idxA]));
  531. }
  532. // Lines intersects, can't represent them with a single quad
  533. else if (idxLeft != idxRight)
  534. {
  535. const INT32 idxA = idxLeft;
  536. const INT32 idxB = (idxLeft + 1) % 2;
  537. Line2 lineA(left[idxB], right[idxA] - left[idxB]);
  538. Line2 lineB(left[idxA], right[idxB] - left[idxA]);
  539. const auto result = lineA.intersects(lineB);
  540. if (result.first)
  541. {
  542. const float t = result.second;
  543. const Vector2 intersection = left[idxB] + t * (right[idxA] - left[idxB]);
  544. vertices.push_back(curveToPixelSpace(left[idxB]));
  545. vertices.push_back(curveToPixelSpace(intersection));
  546. vertices.push_back(curveToPixelSpace(left[idxA]));
  547. vertices.push_back(curveToPixelSpace(intersection));
  548. vertices.push_back(curveToPixelSpace(right[idxB]));
  549. vertices.push_back(curveToPixelSpace(right[idxA]));
  550. }
  551. }
  552. }
  553. mCanvas->drawTriangleList(vertices, color, 129);
  554. }
  555. void GUICurves::selectKeyframe(const KeyframeRef& keyframeRef, const TangentMode& tangentMode, bool selected)
  556. {
  557. auto foundIdx = (UINT32)-1;
  558. for (UINT32 i = 0; i < (UINT32)mSelectedKeyframes.size(); i++)
  559. {
  560. const SelectedKeyframe entry = mSelectedKeyframes[i];
  561. if (entry.keyframeRef.keyIdx == keyframeRef.keyIdx && entry.keyframeRef.curveIdx == keyframeRef.curveIdx &&
  562. entry.tangentMode == tangentMode)
  563. {
  564. foundIdx = i;
  565. break;
  566. }
  567. }
  568. if (selected)
  569. {
  570. if (foundIdx == (UINT32)-1)
  571. mSelectedKeyframes.push_back(SelectedKeyframe(keyframeRef, tangentMode));
  572. }
  573. else
  574. mSelectedKeyframes.erase(mSelectedKeyframes.begin() + foundIdx);
  575. _markContentAsDirty();
  576. }
  577. void GUICurves::clearSelectedKeyframes()
  578. {
  579. mSelectedKeyframes.clear();
  580. _markContentAsDirty();
  581. }
  582. bool GUICurves::isSelected(int curveIdx, int keyIdx) const
  583. {
  584. for(auto& entry : mSelectedKeyframes)
  585. {
  586. if (entry.keyframeRef.curveIdx == curveIdx && entry.keyframeRef.keyIdx == keyIdx)
  587. return true;
  588. }
  589. return false;
  590. }
  591. void GUICurves::updateRenderElementsInternal()
  592. {
  593. mCanvas->clear();
  594. bool drawMarkers = mDrawOptions.isSet(CurveDrawOption::DrawMarkers);
  595. if (drawMarkers)
  596. {
  597. mTickHandler.setRange(mOffset, mOffset + getRangeWithPadding(), getDrawableWidth() + mPadding);
  598. // Draw vertical frame markers
  599. const INT32 numTickLevels = (INT32)mTickHandler.getNumLevels();
  600. for (INT32 i = numTickLevels - 1; i >= 0; i--)
  601. {
  602. Vector<float> ticks = mTickHandler.getTicks(i);
  603. const float strength = mTickHandler.getLevelStrength(i);
  604. for (UINT32 j = 0; j < (UINT32)ticks.size(); j++)
  605. {
  606. Color color = COLOR_DARK_GRAY;
  607. color.a *= strength;
  608. drawFrameMarker(ticks[j], color, false);
  609. }
  610. }
  611. // Draw center line
  612. drawCenterLine();
  613. }
  614. // Draw range
  615. const bool drawRange = mDrawOptions.isSet(CurveDrawOption::DrawRange);
  616. auto curvesToDraw = (UINT32)mCurves.size();
  617. if (drawRange && mCurves.size() >= 2)
  618. {
  619. Vector<TAnimationCurve<float>> curves = { mCurves[0].curve, mCurves[1].curve };
  620. drawCurveRange(curves, Color(1.0f, 0.0f, 0.0f, 0.3f));
  621. curvesToDraw = 2;
  622. }
  623. // Draw curves
  624. const bool drawKeyframes = mDrawOptions.isSet(CurveDrawOption::DrawKeyframes);
  625. for (UINT32 i = 0; i < curvesToDraw; i++)
  626. {
  627. drawCurve(mCurves[i].curve, mCurves[i].color);
  628. // Draw keyframes
  629. if (drawKeyframes)
  630. {
  631. const Vector<TKeyframe<float>> keyframes = mCurves[i].curve.getKeyFrames();
  632. for (UINT32 j = 0; j < (UINT32)keyframes.size(); j++)
  633. {
  634. const bool selected = isSelected(i, j);
  635. drawKeyframe(keyframes[j].time, keyframes[j].value, selected);
  636. }
  637. }
  638. }
  639. // Draw tangents
  640. for (auto& entry : mSelectedKeyframes)
  641. {
  642. KeyframeRef keyframeRef = entry.keyframeRef;
  643. if (keyframeRef.curveIdx < 0 || keyframeRef.curveIdx >= (INT32)mCurves.size())
  644. continue;
  645. TAnimationCurve<float> curve = mCurves[keyframeRef.curveIdx].curve;
  646. if (keyframeRef.keyIdx < 0 || keyframeRef.keyIdx >= (INT32)curve.getNumKeyFrames())
  647. continue;
  648. const Vector<TKeyframe<float>> keyframes = mCurves[keyframeRef.curveIdx].curve.getKeyFrames();
  649. const TangentMode tangentMode = entry.tangentMode;
  650. drawTangents(keyframes[keyframeRef.keyIdx], tangentMode);
  651. }
  652. // Draw selected frame marker
  653. if (drawMarkers && mMarkedFrame != (UINT32)-1)
  654. drawFrameMarker(getTimeForFrame(mMarkedFrame), Color::BansheeOrange, true);
  655. GUIElement::updateRenderElementsInternal();
  656. }
  657. bool GUICurves::_mouseEvent(const GUIMouseEvent& ev)
  658. {
  659. if(ev.getType() == GUIMouseEventType::MouseUp)
  660. {
  661. if (!_isDisabled())
  662. onClicked();
  663. return true;
  664. }
  665. return false;
  666. }
  667. }