BsGUICurves.cpp 25 KB

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