BsGUICurves.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854
  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. #include "Image/BsPixelUtil.h"
  12. namespace bs
  13. {
  14. static constexpr int LINE_SPLIT_WIDTH = 2;
  15. static constexpr int TANGENT_LINE_DISTANCE = 30;
  16. static constexpr Color COLOR_MID_GRAY = Color(90.0f / 255.0f, 90.0f / 255.0f, 90.0f / 255.0f, 1.0f);
  17. static constexpr Color COLOR_DARK_GRAY = Color(40.0f / 255.0f, 40.0f / 255.0f, 40.0f / 255.0f, 1.0f);
  18. GUICurves::GUICurves(CurveDrawOptions drawOptions, const String& styleName, const GUIDimensions& dimensions)
  19. :GUITimeline(styleName, dimensions), mDrawOptions(drawOptions), mTickHandler(GUITickStepType::Time)
  20. { }
  21. const String& GUICurves::getGUITypeName()
  22. {
  23. static String name = "GUICurves";
  24. return name;
  25. }
  26. GUICurves* GUICurves::create(const String& styleName)
  27. {
  28. return new (bs_alloc<GUICurves>()) GUICurves(CurveDrawOption::DrawMarkers | CurveDrawOption::DrawKeyframes,
  29. getStyleName<GUICurves>(styleName), GUIDimensions::create());
  30. }
  31. GUICurves* GUICurves::create(CurveDrawOptions drawOptions, const String& styleName)
  32. {
  33. return new (bs_alloc<GUICurves>()) GUICurves(drawOptions, getStyleName<GUICurves>(styleName), GUIDimensions::create());
  34. }
  35. GUICurves* GUICurves::create(const GUIOptions& options, const String& styleName)
  36. {
  37. return new (bs_alloc<GUICurves>()) GUICurves(CurveDrawOption::DrawMarkers | CurveDrawOption::DrawKeyframes,
  38. getStyleName<GUICurves>(styleName), GUIDimensions::create(options));
  39. }
  40. GUICurves* GUICurves::create(CurveDrawOptions drawOptions, const GUIOptions& options, const String& styleName)
  41. {
  42. return new (bs_alloc<GUICurves>()) GUICurves(drawOptions, getStyleName<GUICurves>(styleName),
  43. GUIDimensions::create(options));
  44. }
  45. void GUICurves::setCurves(const Vector<CurveDrawInfo>& curves)
  46. {
  47. if(mCurves == curves)
  48. return;
  49. mCurves = curves;
  50. _markContentAsDirty();
  51. }
  52. void GUICurves::setRange(float xRange, float yRange)
  53. {
  54. GUITimeline::setRange(xRange);
  55. mYRange = yRange;
  56. }
  57. void GUICurves::setOffset(const Vector2& offset)
  58. {
  59. GUITimeline::setOffset(offset.x);
  60. mYOffset = offset.y;
  61. }
  62. void GUICurves::centerAndZoom()
  63. {
  64. Vector<TAnimationCurve<float>> curves(mCurves.size());
  65. UINT32 idx = 0;
  66. for(auto& entry : mCurves)
  67. curves[idx++] = entry.curve;
  68. float xMin, xMax;
  69. float yMin, yMax;
  70. AnimationUtility::calculateRange(curves, xMin, xMax, yMin, yMax);
  71. float xRange = xMax - xMin;
  72. float yRange = (yMax - yMin);
  73. float yOffset = yMin + yRange * 0.5f;
  74. // Add padding to y range
  75. yRange *= 1.05f;
  76. // Don't allow zero range
  77. if (xRange == 0.0f)
  78. xRange = 60.0f;
  79. if (yRange == 0.0f)
  80. yRange = 10.0f;
  81. setOffset(Vector2(xMin, yOffset));
  82. setRange(xRange, yRange);
  83. }
  84. UINT32 GUICurves::findCurve(const Vector2I& pixelCoords)
  85. {
  86. Vector2 curveCoords;
  87. if (!pixelToCurveSpace(pixelCoords, curveCoords, true))
  88. return (UINT32)-1;
  89. const float time = curveCoords.x;
  90. float nearestDistance = std::numeric_limits<float>::max();
  91. auto curveIdx = (UINT32)-1;
  92. for (UINT32 i = 0; i < (UINT32)mCurves.size(); i++)
  93. {
  94. const TAnimationCurve<float>& curve = mCurves[i].curve;
  95. const float value = curve.evaluate(time, false);
  96. const Vector2I curPixelPos = curveToPixelSpace(Vector2(time, value));
  97. const auto distanceToKey = (float)pixelCoords.manhattanDist(curPixelPos);
  98. if (distanceToKey < nearestDistance)
  99. {
  100. nearestDistance = distanceToKey;
  101. curveIdx = i;
  102. }
  103. }
  104. // We're not near any curve
  105. if (nearestDistance > 5.0f)
  106. return (UINT32)-1;
  107. return curveIdx;
  108. }
  109. /**
  110. * Attempts to find a keyframe under the provided coordinates.
  111. *
  112. * @param[in] pixelCoords Coordinates relative to this GUI element in pixels.
  113. * @param[out] keyframe Output object containing keyframe index and index of the curve it belongs to. Only
  114. * valid if method returns true.
  115. * @return True if there is a keyframe under the coordinates, false otherwise.
  116. */
  117. bool GUICurves::findKeyFrame(const Vector2I& pixelCoords, KeyframeRef& keyframe)
  118. {
  119. keyframe = KeyframeRef();
  120. float nearestDistance = std::numeric_limits<float>::max();
  121. for (UINT32 i = 0; i < (UINT32)mCurves.size(); i++)
  122. {
  123. const TAnimationCurve<float>& curve = mCurves[i].curve;
  124. const Vector<TKeyframe<float>>& keyframes = curve.getKeyFrames();
  125. for (UINT32 j = 0; j < (UINT32)keyframes.size(); j++)
  126. {
  127. const Vector2 keyframeCurveCoords = Vector2(keyframes[j].time, keyframes[j].value);
  128. const Vector2I keyframeCoords = curveToPixelSpace(keyframeCurveCoords);
  129. const auto distanceToKey = (float)pixelCoords.manhattanDist(keyframeCoords);
  130. if (distanceToKey < nearestDistance)
  131. {
  132. nearestDistance = distanceToKey;
  133. keyframe.keyIdx = j;
  134. keyframe.curveIdx = i;
  135. }
  136. }
  137. }
  138. return nearestDistance <= 5.0f;
  139. }
  140. /**
  141. * Attempts to find a a tangent handle under the provided coordinates.
  142. *
  143. * @param[in] pixelCoords Coordinates relative to this GUI element in pixels.
  144. * @param[in] tangent Output object containing keyframe information and tangent type. Only valid if method
  145. * returns true.
  146. * @return True if there is a tangent handle under the coordinates, false otherwise.
  147. */
  148. bool GUICurves::findTangent(const Vector2I& pixelCoords, TangentRef& tangent)
  149. {
  150. tangent = TangentRef();
  151. float nearestDistance = std::numeric_limits<float>::max();
  152. for (auto& entry : mSelectedKeyframes)
  153. {
  154. KeyframeRef keyframeRef = entry.keyframeRef;
  155. if (keyframeRef.curveIdx < 0 || keyframeRef.curveIdx >= (INT32)mCurves.size())
  156. continue;
  157. const TAnimationCurve<float>& curve = mCurves[keyframeRef.curveIdx].curve;
  158. if (keyframeRef.keyIdx < 0 || keyframeRef.keyIdx >= (INT32)curve.getNumKeyFrames())
  159. continue;
  160. const Vector<TKeyframe<float>>& keyframes = curve.getKeyFrames();
  161. TangentMode tangentMode = entry.tangentMode;
  162. if (isTangentDisplayed(tangentMode, TangentType::In))
  163. {
  164. const Vector2I tangentCoords = getTangentPosition(keyframes[keyframeRef.keyIdx], TangentType::In);
  165. const auto distanceToHandle = (float)pixelCoords.manhattanDist(tangentCoords);
  166. if (distanceToHandle < nearestDistance)
  167. {
  168. nearestDistance = distanceToHandle;
  169. tangent.keyframeRef.keyIdx = keyframeRef.keyIdx;
  170. tangent.keyframeRef.curveIdx = keyframeRef.curveIdx;
  171. tangent.type = TangentType::In;
  172. }
  173. }
  174. if (isTangentDisplayed(tangentMode, TangentType::Out))
  175. {
  176. const Vector2I tangentCoords = getTangentPosition(keyframes[keyframeRef.keyIdx], TangentType::Out);
  177. const auto distanceToHandle = (float)pixelCoords.manhattanDist(tangentCoords);
  178. if (distanceToHandle < nearestDistance)
  179. {
  180. nearestDistance = distanceToHandle;
  181. tangent.keyframeRef.keyIdx = keyframeRef.keyIdx;
  182. tangent.keyframeRef.curveIdx = keyframeRef.curveIdx;
  183. tangent.type = TangentType::Out;
  184. }
  185. }
  186. }
  187. return nearestDistance <= 5.0f;
  188. }
  189. bool GUICurves::pixelToCurveSpace(const Vector2I& pixelCoords, Vector2& curveCoords, bool padding) const
  190. {
  191. const Rect2I& bounds = mLayoutData.area;
  192. bool outsideHorizontal;
  193. if (padding)
  194. outsideHorizontal = pixelCoords.x < 0 || pixelCoords.x >= (INT32)bounds.width;
  195. else
  196. outsideHorizontal = pixelCoords.x < (INT32)mPadding || pixelCoords.x >= ((INT32)bounds.width - (INT32)mPadding);
  197. // Check if outside of curve drawing bounds
  198. if (outsideHorizontal || pixelCoords.y < 0 || pixelCoords.y >= (INT32)bounds.height)
  199. {
  200. curveCoords = Vector2::ZERO;
  201. return false;
  202. }
  203. // Find time and value of the place under the coordinates
  204. const Vector2I relativeCoords = pixelCoords - Vector2I(mPadding, 0);
  205. const float lengthPerPixel = getRange() / getDrawableWidth();
  206. const float heightPerPixel = mYRange / mLayoutData.area.height;
  207. const float centerOffset = mYRange / 2.0f;
  208. const float t = mOffset + relativeCoords.x * lengthPerPixel;
  209. const float value = mYOffset + centerOffset - relativeCoords.y * heightPerPixel;
  210. curveCoords = Vector2();
  211. curveCoords.x = t;
  212. curveCoords.y = value;
  213. return true;
  214. }
  215. Vector2I GUICurves::curveToPixelSpace(const Vector2& curveCoords) const
  216. {
  217. const UINT32 heightOffset = mLayoutData.area.height / 2; // So that y = 0 is at center of canvas
  218. const Vector2 relativeCurveCoords = curveCoords - Vector2(mOffset, mYOffset);
  219. Vector2I pixelCoords = Vector2I();
  220. pixelCoords.x = (int)((relativeCurveCoords.x / getRange()) * getDrawableWidth()) + mPadding;
  221. pixelCoords.y = heightOffset - (int)((relativeCurveCoords.y / mYRange) * mLayoutData.area.height);
  222. return pixelCoords;
  223. }
  224. Vector2 GUICurves::tangentToNormal(float tangent) const
  225. {
  226. if (tangent == std::numeric_limits<float>::infinity())
  227. return Vector2(0, 1);
  228. Vector2 normal = Vector2(1, tangent);
  229. return Vector2::normalize(normal);
  230. }
  231. Vector2I GUICurves::getTangentPosition(const TKeyframe<float>& keyFrame, TangentType type) const
  232. {
  233. const Vector2I position = curveToPixelSpace(Vector2(keyFrame.time, keyFrame.value));
  234. Vector2 normal;
  235. if (type == TangentType::In)
  236. normal = -tangentToNormal(keyFrame.inTangent);
  237. else
  238. normal = tangentToNormal(keyFrame.outTangent);
  239. // X/Y ranges aren't scaled 1:1, adjust normal accordingly
  240. normal.x /= getRange();
  241. normal.y /= mYRange;
  242. normal = Vector2::normalize(normal);
  243. // Convert normal (in percentage) to pixel values
  244. Vector2I offset = Vector2I((int)(normal.x * TANGENT_LINE_DISTANCE),
  245. (int)(-normal.y * TANGENT_LINE_DISTANCE));
  246. return position + offset;
  247. }
  248. bool GUICurves::isTangentDisplayed(TangentMode mode, TangentType type) const
  249. {
  250. if (mode == TangentModeBits::Auto)
  251. return false;
  252. else if (mode == TangentModeBits::Free)
  253. return true;
  254. if (type == TangentType::In)
  255. return mode.isSet(TangentModeBits::InFree);
  256. else
  257. return mode.isSet(TangentModeBits::OutFree);
  258. }
  259. void GUICurves::drawFrameMarker(float t, Color color, bool onTop)
  260. {
  261. const INT32 xPos = (INT32)(((t - mOffset) / getRange()) * getDrawableWidth()) + mPadding;
  262. const Vector2I start = Vector2I(xPos, 0);
  263. const Vector2I end = Vector2I(xPos, mLayoutData.area.height);
  264. UINT8 depth;
  265. if (onTop)
  266. depth = 110;
  267. else
  268. depth = 130;
  269. mCanvas->drawLine(start, end, color, depth);
  270. }
  271. void GUICurves::drawCenterLine()
  272. {
  273. const Vector2I center = curveToPixelSpace(Vector2(0.0f, 0.0f));
  274. const Vector2I start = Vector2I(0, center.y);
  275. const Vector2I end = Vector2I(mLayoutData.area.width, center.y);
  276. mCanvas->drawLine(start, end, COLOR_DARK_GRAY, 130);
  277. }
  278. void GUICurves::drawDiamond(Vector2I center, int size, Color innerColor, Color outerColor)
  279. {
  280. const Vector2I a = Vector2I(center.x - size, center.y);
  281. const Vector2I b = Vector2I(center.x, center.y - size);
  282. const Vector2I c = Vector2I(center.x + size, center.y);
  283. const Vector2I d = Vector2I(center.x, center.y + size);
  284. // Draw diamond shape
  285. const Vector<Vector2I> linePoints = { a, b, c, d, a };
  286. const Vector<Vector2I> trianglePoints = { b, c, a, d };
  287. mCanvas->drawTriangleStrip(trianglePoints, innerColor, 101);
  288. mCanvas->drawPolyLine(linePoints, outerColor, 100);
  289. }
  290. void GUICurves::drawKeyframe(float t, float y, bool selected)
  291. {
  292. const Vector2I pixelCoords = curveToPixelSpace(Vector2(t, y));
  293. if (selected)
  294. drawDiamond(pixelCoords, 3, Color::White, Color::BansheeOrange);
  295. else
  296. drawDiamond(pixelCoords, 3, Color::White, Color::Black);
  297. }
  298. void GUICurves::drawTangents(const TKeyframe<float>& keyFrame, TangentMode tangentMode)
  299. {
  300. const Vector2I keyframeCoords = curveToPixelSpace(Vector2(keyFrame.time, keyFrame.value));
  301. if (isTangentDisplayed(tangentMode, TangentType::In))
  302. {
  303. Vector2I tangentCoords = getTangentPosition(keyFrame, TangentType::In);
  304. mCanvas->drawLine(keyframeCoords, tangentCoords, Color::LightGray);
  305. drawDiamond(tangentCoords, 2, Color::Green, Color::Black);
  306. }
  307. if (isTangentDisplayed(tangentMode, TangentType::Out))
  308. {
  309. Vector2I tangentCoords = getTangentPosition(keyFrame, TangentType::Out);
  310. mCanvas->drawLine(keyframeCoords, tangentCoords, Color::LightGray);
  311. drawDiamond(tangentCoords, 2, Color::Green, Color::Black);
  312. }
  313. }
  314. void GUICurves::drawCurve(const TAnimationCurve<float>& curve, const Color& color)
  315. {
  316. const float range = getRangeWithPadding();
  317. const float widthPerPixel = range / getDrawableWidth();
  318. const float heightPerPixel = mYRange / mLayoutData.area.height;
  319. const Vector<TKeyframe<float>>& keyframes = curve.getKeyFrames();
  320. if (keyframes.empty())
  321. return;
  322. // Draw start line
  323. {
  324. const float curveStart = keyframes[0].time;
  325. const float curveValue = curve.evaluate(curveStart, false);
  326. const Vector2I end = curveToPixelSpace(Vector2(curveStart, curveValue));
  327. const Vector2I start = Vector2I(-(INT32)mPadding, end.y);
  328. if (start.x < end.x)
  329. mCanvas->drawLine(start, end, COLOR_MID_GRAY);
  330. }
  331. Vector<Vector2I> linePoints;
  332. // Draw in between keyframes
  333. const float startVisibleTime = mOffset;
  334. const float endVisibleTime = startVisibleTime + range;
  335. for (INT32 i = 0; i < (INT32)keyframes.size() - 1; i++)
  336. {
  337. const float start = keyframes[i].time;
  338. const float end = keyframes[i + 1].time;
  339. if (end < startVisibleTime || start > endVisibleTime)
  340. continue;
  341. const bool isStep = keyframes[i].outTangent == std::numeric_limits<float>::infinity() ||
  342. keyframes[i + 1].inTangent == std::numeric_limits<float>::infinity();
  343. // If step tangent, draw the required lines without sampling, as the sampling will miss the step
  344. if (isStep)
  345. {
  346. const float startValue = curve.evaluate(start, false);
  347. const float endValue = curve.evaluate(end, false);
  348. linePoints.push_back(curveToPixelSpace(Vector2(start, startValue)));
  349. linePoints.push_back(curveToPixelSpace(Vector2(end, startValue)));
  350. linePoints.push_back(curveToPixelSpace(Vector2(end, endValue)));
  351. }
  352. else // Draw normally
  353. {
  354. const float startValue = keyframes[i].value;
  355. const float endValue = keyframes[i + 1].value;
  356. Vector2I startPixel = curveToPixelSpace(Vector2(start, startValue));
  357. Vector2I endPixel = curveToPixelSpace(Vector2(end, endValue));
  358. const UINT32 distance = startPixel.manhattanDist(endPixel);
  359. INT32 numSplits;
  360. float splitIncrement;
  361. if (distance > 0)
  362. {
  363. const float fNumSplits = distance / (float)LINE_SPLIT_WIDTH;
  364. numSplits = Math::ceilToInt(fNumSplits) + 1;
  365. splitIncrement = (end - start) / (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. struct TickLevel
  600. {
  601. float t;
  602. float strength;
  603. };
  604. struct TickComparator
  605. {
  606. bool operator()(const TickLevel& a, const TickLevel& b) const
  607. {
  608. if (fabs(a.t - b.t) > 0.001f)
  609. return a.t < b.t;
  610. return false;
  611. }
  612. };
  613. Set<TickLevel, TickComparator> uniqueTicks;
  614. const INT32 numTickLevels = (INT32)mTickHandler.getNumLevels();
  615. for (INT32 i = 0; i < numTickLevels; i++)
  616. {
  617. Vector<float> ticks = mTickHandler.getTicks(i);
  618. const float strength = mTickHandler.getLevelStrength(i);
  619. for (UINT32 j = 0; j < (UINT32)ticks.size(); j++)
  620. uniqueTicks.insert({ticks[j], strength });
  621. }
  622. for(auto& entry : uniqueTicks)
  623. {
  624. Color color = COLOR_DARK_GRAY.getGamma();
  625. color *= entry.strength;
  626. drawFrameMarker(entry.t, color, false);
  627. }
  628. // Draw center line
  629. drawCenterLine();
  630. }
  631. // Draw range
  632. const bool drawRange = mDrawOptions.isSet(CurveDrawOption::DrawRange);
  633. auto curvesToDraw = (UINT32)mCurves.size();
  634. if (drawRange && mCurves.size() >= 2)
  635. {
  636. Vector<TAnimationCurve<float>> curves = { mCurves[0].curve, mCurves[1].curve };
  637. drawCurveRange(curves, Color(1.0f, 0.0f, 0.0f, 0.3f));
  638. curvesToDraw = 2;
  639. }
  640. // Draw curves
  641. const bool drawKeyframes = mDrawOptions.isSet(CurveDrawOption::DrawKeyframes);
  642. for (UINT32 i = 0; i < curvesToDraw; i++)
  643. {
  644. drawCurve(mCurves[i].curve, mCurves[i].color);
  645. // Draw keyframes
  646. if (drawKeyframes)
  647. {
  648. const Vector<TKeyframe<float>> keyframes = mCurves[i].curve.getKeyFrames();
  649. for (UINT32 j = 0; j < (UINT32)keyframes.size(); j++)
  650. {
  651. const bool selected = isSelected(i, j);
  652. drawKeyframe(keyframes[j].time, keyframes[j].value, selected);
  653. }
  654. }
  655. }
  656. // Draw tangents
  657. for (auto& entry : mSelectedKeyframes)
  658. {
  659. KeyframeRef keyframeRef = entry.keyframeRef;
  660. if (keyframeRef.curveIdx < 0 || keyframeRef.curveIdx >= (INT32)mCurves.size())
  661. continue;
  662. TAnimationCurve<float> curve = mCurves[keyframeRef.curveIdx].curve;
  663. if (keyframeRef.keyIdx < 0 || keyframeRef.keyIdx >= (INT32)curve.getNumKeyFrames())
  664. continue;
  665. const Vector<TKeyframe<float>> keyframes = mCurves[keyframeRef.curveIdx].curve.getKeyFrames();
  666. const TangentMode tangentMode = entry.tangentMode;
  667. drawTangents(keyframes[keyframeRef.keyIdx], tangentMode);
  668. }
  669. // Draw selected frame marker
  670. if (drawMarkers && mMarkedFrame != (UINT32)-1)
  671. drawFrameMarker(getTimeForFrame(mMarkedFrame), Color::BansheeOrange, true);
  672. GUIElement::updateRenderElementsInternal();
  673. }
  674. bool GUICurves::_mouseEvent(const GUIMouseEvent& ev)
  675. {
  676. if(ev.getType() == GUIMouseEventType::MouseUp)
  677. {
  678. if (!_isDisabled())
  679. onClicked();
  680. return true;
  681. }
  682. return false;
  683. }
  684. }