BsGUICurves.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859
  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 lengthPerPixel = range / getDrawableWidth();
  318. const Vector<TKeyframe<float>>& keyframes = curve.getKeyFrames();
  319. if (keyframes.empty())
  320. return;
  321. // Draw start line
  322. {
  323. const float curveStart = keyframes[0].time;
  324. const float curveValue = curve.evaluate(curveStart, false);
  325. const Vector2I end = curveToPixelSpace(Vector2(curveStart, curveValue));
  326. const Vector2I start = Vector2I(-(INT32)mPadding, end.y);
  327. if (start.x < end.x)
  328. mCanvas->drawLine(start, end, COLOR_MID_GRAY);
  329. }
  330. Vector<Vector2I> linePoints;
  331. // Draw in between keyframes
  332. const float startVisibleTime = mOffset;
  333. const float endVisibleTime = startVisibleTime + range;
  334. for (INT32 i = 0; i < (INT32)keyframes.size() - 1; i++)
  335. {
  336. const float start = keyframes[i].time;
  337. const float end = keyframes[i + 1].time;
  338. if (end < startVisibleTime || start > endVisibleTime)
  339. continue;
  340. const bool isStep = keyframes[i].outTangent == std::numeric_limits<float>::infinity() ||
  341. keyframes[i + 1].inTangent == std::numeric_limits<float>::infinity();
  342. // If step tangent, draw the required lines without sampling, as the sampling will miss the step
  343. if (isStep)
  344. {
  345. const float startValue = curve.evaluate(start, false);
  346. const float endValue = curve.evaluate(end, false);
  347. linePoints.push_back(curveToPixelSpace(Vector2(start, startValue)));
  348. linePoints.push_back(curveToPixelSpace(Vector2(end, startValue)));
  349. linePoints.push_back(curveToPixelSpace(Vector2(end, endValue)));
  350. }
  351. else // Draw normally
  352. {
  353. float splitIncrement = LINE_SPLIT_WIDTH * lengthPerPixel;
  354. const float startValue = keyframes[i].value;
  355. const float endValue = keyframes[i + 1].value;
  356. Vector2I startPixel;
  357. startPixel.x = (int)(start / lengthPerPixel);
  358. startPixel.y = (int)(startValue / lengthPerPixel);
  359. Vector2I endPixel;
  360. endPixel.x = (int)(end / lengthPerPixel);
  361. endPixel.y = (int)(endValue / lengthPerPixel);
  362. const UINT32 distance = startPixel.manhattanDist(endPixel);
  363. INT32 numSplits;
  364. if (distance > 0)
  365. {
  366. const float fNumSplits = distance / splitIncrement;
  367. numSplits = Math::ceilToInt(fNumSplits);
  368. splitIncrement = distance / (float)numSplits;
  369. }
  370. else
  371. {
  372. numSplits = 1;
  373. splitIncrement = 0.0f;
  374. }
  375. for (int j = 0; j < numSplits; j++)
  376. {
  377. const float t = std::min(start + j * splitIncrement, end);
  378. const float value = curve.evaluate(t, false);
  379. linePoints.push_back(curveToPixelSpace(Vector2(t, value)));
  380. }
  381. }
  382. }
  383. mCanvas->drawPolyLine(linePoints, color);
  384. // Draw end line
  385. {
  386. const float curveEnd = keyframes[keyframes.size() - 1].time;
  387. const float curveValue = curve.evaluate(curveEnd, false);
  388. const Vector2I start = curveToPixelSpace(Vector2(curveEnd, curveValue));
  389. const Vector2I end = Vector2I(mLayoutData.area.width, start.y);
  390. if (start.x < end.x)
  391. mCanvas->drawLine(start, end, COLOR_MID_GRAY);
  392. }
  393. }
  394. void GUICurves::drawCurveRange(const Vector<TAnimationCurve<float>>& curves, Color color)
  395. {
  396. const float range = getRangeWithPadding();
  397. if (curves.size() != 2)
  398. return;
  399. const Vector<TKeyframe<float>>* keyframes[2] = { &curves[0].getKeyFrames(), &curves[1].getKeyFrames() };
  400. if (keyframes[0]->empty() || keyframes[1]->empty())
  401. return;
  402. const UINT32 numSamples = (getDrawableWidth() + LINE_SPLIT_WIDTH - 1) / LINE_SPLIT_WIDTH;
  403. const float timePerSample = range / numSamples;
  404. float time = mOffset;
  405. const float lengthPerPixel = mRange / getDrawableWidth();
  406. time -= lengthPerPixel * mPadding;
  407. INT32 keyframeIndices[] = { 0, 0 };
  408. // Find first valid keyframe indices
  409. for (UINT32 curveIdx = 0; curveIdx < 2; curveIdx++)
  410. {
  411. keyframeIndices[curveIdx] = (INT32)keyframes[curveIdx]->size();
  412. for (UINT32 i = 0; i < (UINT32)keyframes[curveIdx]->size(); i++)
  413. {
  414. if ((*keyframes[curveIdx])[i].time > time)
  415. keyframeIndices[curveIdx] = i;
  416. }
  417. }
  418. Vector<float> times;
  419. Vector<float> points[2];
  420. // Determine start points
  421. for (int curveIdx = 0; curveIdx < 2; curveIdx++)
  422. {
  423. float value = curves[curveIdx].evaluate(time, false);
  424. points[curveIdx].push_back(value);
  425. }
  426. times.push_back(time);
  427. const float rangeEnd = mOffset + range;
  428. while (time < rangeEnd)
  429. {
  430. float nextTime = time + timePerSample;
  431. bool hasStep = false;
  432. // Determine time to sample at. Use fixed increments unless there's a step keyframe within our increment in
  433. // which case we use its time so we can evaluate it directly
  434. for (UINT32 curveIdx = 0; curveIdx < 2; curveIdx++)
  435. {
  436. const INT32 keyframeIdx = keyframeIndices[curveIdx];
  437. if (keyframeIdx < (INT32)keyframes[curveIdx]->size())
  438. {
  439. const TKeyframe<float>& keyframe = (*keyframes[curveIdx])[keyframeIdx];
  440. const bool isStep = keyframe.inTangent == std::numeric_limits<float>::infinity() ||
  441. keyframe.outTangent == std::numeric_limits<float>::infinity();
  442. if (isStep && keyframe.time <= nextTime)
  443. {
  444. nextTime = std::min(nextTime, keyframe.time);
  445. hasStep = true;
  446. }
  447. }
  448. }
  449. // Evaluate
  450. if (hasStep)
  451. {
  452. for (UINT32 curveIdx = 0; curveIdx < 2; curveIdx++)
  453. {
  454. const INT32 keyframeIdx = keyframeIndices[curveIdx];
  455. if (keyframeIdx < (INT32)keyframes[curveIdx]->size())
  456. {
  457. const TKeyframe<float>& keyframe = (*keyframes[curveIdx])[keyframeIdx];
  458. if (Math::approxEquals(keyframe.time, nextTime))
  459. {
  460. if (keyframeIdx > 0)
  461. {
  462. const TKeyframe<float>& prevKeyframe = (*keyframes[curveIdx])[keyframeIdx - 1];
  463. points[curveIdx].push_back(prevKeyframe.value);
  464. }
  465. else
  466. points[curveIdx].push_back(keyframe.value);
  467. points[curveIdx].push_back(keyframe.value);
  468. }
  469. else
  470. {
  471. // The other curve has step but this one doesn't, we just insert the same value twice
  472. float value = curves[curveIdx].evaluate(nextTime, false);
  473. points[curveIdx].push_back(value);
  474. points[curveIdx].push_back(value);
  475. }
  476. times.push_back(nextTime);
  477. times.push_back(nextTime);
  478. }
  479. }
  480. }
  481. else
  482. {
  483. for (UINT32 curveIdx = 0; curveIdx < 2; curveIdx++)
  484. points[curveIdx].push_back(curves[curveIdx].evaluate(nextTime, false));
  485. times.push_back(nextTime);
  486. }
  487. // Advance keyframe indices
  488. for (UINT32 curveIdx = 0; curveIdx < 2; curveIdx++)
  489. {
  490. INT32 keyframeIdx = keyframeIndices[curveIdx];
  491. while (keyframeIdx < (INT32)keyframes[curveIdx]->size())
  492. {
  493. const TKeyframe<float>& keyframe = (*keyframes[curveIdx])[keyframeIdx];
  494. if (keyframe.time > nextTime)
  495. break;
  496. keyframeIdx = ++keyframeIndices[curveIdx];
  497. }
  498. }
  499. time = nextTime;
  500. }
  501. // End points
  502. for (UINT32 curveIdx = 0; curveIdx < 2; curveIdx++)
  503. {
  504. float value = curves[curveIdx].evaluate(rangeEnd, false);
  505. points[curveIdx].push_back(value);
  506. }
  507. times.push_back(rangeEnd);
  508. const INT32 numQuads = (INT32)times.size() - 1;
  509. Vector<Vector2I> vertices;
  510. for (int i = 0; i < numQuads; i++)
  511. {
  512. const INT32 idxLeft = points[0][i] < points[1][i] ? 0 : 1;
  513. const INT32 idxRight = points[0][i + 1] < points[1][i + 1] ? 0 : 1;
  514. Vector2 left[] =
  515. {
  516. Vector2(times[i], points[0][i]),
  517. Vector2(times[i], points[1][i])
  518. };
  519. Vector2 right[] =
  520. {
  521. Vector2(times[i + 1], points[0][i + 1]),
  522. Vector2(times[i + 1], points[1][i + 1])
  523. };
  524. if (idxLeft == idxRight)
  525. {
  526. const INT32 idxA = idxLeft;
  527. const INT32 idxB = (idxLeft + 1) % 2;
  528. vertices.push_back(curveToPixelSpace(left[idxB]));
  529. vertices.push_back(curveToPixelSpace(right[idxB]));
  530. vertices.push_back(curveToPixelSpace(left[idxA]));
  531. vertices.push_back(curveToPixelSpace(right[idxB]));
  532. vertices.push_back(curveToPixelSpace(right[idxA]));
  533. vertices.push_back(curveToPixelSpace(left[idxA]));
  534. }
  535. // Lines intersects, can't represent them with a single quad
  536. else if (idxLeft != idxRight)
  537. {
  538. const INT32 idxA = idxLeft;
  539. const INT32 idxB = (idxLeft + 1) % 2;
  540. Line2 lineA(left[idxB], right[idxA] - left[idxB]);
  541. Line2 lineB(left[idxA], right[idxB] - left[idxA]);
  542. const auto result = lineA.intersects(lineB);
  543. if (result.first)
  544. {
  545. const float t = result.second;
  546. const Vector2 intersection = left[idxB] + t * (right[idxA] - left[idxB]);
  547. vertices.push_back(curveToPixelSpace(left[idxB]));
  548. vertices.push_back(curveToPixelSpace(intersection));
  549. vertices.push_back(curveToPixelSpace(left[idxA]));
  550. vertices.push_back(curveToPixelSpace(intersection));
  551. vertices.push_back(curveToPixelSpace(right[idxB]));
  552. vertices.push_back(curveToPixelSpace(right[idxA]));
  553. }
  554. }
  555. }
  556. mCanvas->drawTriangleList(vertices, color, 129);
  557. }
  558. void GUICurves::selectKeyframe(const KeyframeRef& keyframeRef, const TangentMode& tangentMode, bool selected)
  559. {
  560. auto foundIdx = (UINT32)-1;
  561. for (UINT32 i = 0; i < (UINT32)mSelectedKeyframes.size(); i++)
  562. {
  563. const SelectedKeyframe entry = mSelectedKeyframes[i];
  564. if (entry.keyframeRef.keyIdx == keyframeRef.keyIdx && entry.keyframeRef.curveIdx == keyframeRef.curveIdx &&
  565. entry.tangentMode == tangentMode)
  566. {
  567. foundIdx = i;
  568. break;
  569. }
  570. }
  571. if (selected)
  572. {
  573. if (foundIdx == (UINT32)-1)
  574. mSelectedKeyframes.push_back(SelectedKeyframe(keyframeRef, tangentMode));
  575. }
  576. else
  577. mSelectedKeyframes.erase(mSelectedKeyframes.begin() + foundIdx);
  578. _markContentAsDirty();
  579. }
  580. void GUICurves::clearSelectedKeyframes()
  581. {
  582. mSelectedKeyframes.clear();
  583. _markContentAsDirty();
  584. }
  585. bool GUICurves::isSelected(int curveIdx, int keyIdx) const
  586. {
  587. for(auto& entry : mSelectedKeyframes)
  588. {
  589. if (entry.keyframeRef.curveIdx == curveIdx && entry.keyframeRef.keyIdx == keyIdx)
  590. return true;
  591. }
  592. return false;
  593. }
  594. void GUICurves::updateRenderElementsInternal()
  595. {
  596. mCanvas->clear();
  597. bool drawMarkers = mDrawOptions.isSet(CurveDrawOption::DrawMarkers);
  598. if (drawMarkers)
  599. {
  600. mTickHandler.setRange(mOffset, mOffset + getRangeWithPadding(), getDrawableWidth() + mPadding);
  601. // Draw vertical frame markers
  602. struct TickLevel
  603. {
  604. float t;
  605. float strength;
  606. };
  607. struct TickComparator
  608. {
  609. bool operator()(const TickLevel& a, const TickLevel& b)
  610. {
  611. if (fabs(a.t - b.t) > 0.001f)
  612. return a.t < b.t;
  613. return false;
  614. }
  615. };
  616. Set<TickLevel, TickComparator> uniqueTicks;
  617. const INT32 numTickLevels = (INT32)mTickHandler.getNumLevels();
  618. for (INT32 i = 0; i < numTickLevels; i++)
  619. {
  620. Vector<float> ticks = mTickHandler.getTicks(i);
  621. const float strength = mTickHandler.getLevelStrength(i);
  622. for (UINT32 j = 0; j < (UINT32)ticks.size(); j++)
  623. uniqueTicks.insert({ticks[j], strength });
  624. }
  625. for(auto& entry : uniqueTicks)
  626. {
  627. Color color = PixelUtil::linearToSRGB(COLOR_DARK_GRAY);
  628. color *= entry.strength;
  629. drawFrameMarker(entry.t, color, false);
  630. }
  631. // Draw center line
  632. drawCenterLine();
  633. }
  634. // Draw range
  635. const bool drawRange = mDrawOptions.isSet(CurveDrawOption::DrawRange);
  636. auto curvesToDraw = (UINT32)mCurves.size();
  637. if (drawRange && mCurves.size() >= 2)
  638. {
  639. Vector<TAnimationCurve<float>> curves = { mCurves[0].curve, mCurves[1].curve };
  640. drawCurveRange(curves, Color(1.0f, 0.0f, 0.0f, 0.3f));
  641. curvesToDraw = 2;
  642. }
  643. // Draw curves
  644. const bool drawKeyframes = mDrawOptions.isSet(CurveDrawOption::DrawKeyframes);
  645. for (UINT32 i = 0; i < curvesToDraw; i++)
  646. {
  647. drawCurve(mCurves[i].curve, mCurves[i].color);
  648. // Draw keyframes
  649. if (drawKeyframes)
  650. {
  651. const Vector<TKeyframe<float>> keyframes = mCurves[i].curve.getKeyFrames();
  652. for (UINT32 j = 0; j < (UINT32)keyframes.size(); j++)
  653. {
  654. const bool selected = isSelected(i, j);
  655. drawKeyframe(keyframes[j].time, keyframes[j].value, selected);
  656. }
  657. }
  658. }
  659. // Draw tangents
  660. for (auto& entry : mSelectedKeyframes)
  661. {
  662. KeyframeRef keyframeRef = entry.keyframeRef;
  663. if (keyframeRef.curveIdx < 0 || keyframeRef.curveIdx >= (INT32)mCurves.size())
  664. continue;
  665. TAnimationCurve<float> curve = mCurves[keyframeRef.curveIdx].curve;
  666. if (keyframeRef.keyIdx < 0 || keyframeRef.keyIdx >= (INT32)curve.getNumKeyFrames())
  667. continue;
  668. const Vector<TKeyframe<float>> keyframes = mCurves[keyframeRef.curveIdx].curve.getKeyFrames();
  669. const TangentMode tangentMode = entry.tangentMode;
  670. drawTangents(keyframes[keyframeRef.keyIdx], tangentMode);
  671. }
  672. // Draw selected frame marker
  673. if (drawMarkers && mMarkedFrame != (UINT32)-1)
  674. drawFrameMarker(getTimeForFrame(mMarkedFrame), Color::BansheeOrange, true);
  675. GUIElement::updateRenderElementsInternal();
  676. }
  677. bool GUICurves::_mouseEvent(const GUIMouseEvent& ev)
  678. {
  679. if(ev.getType() == GUIMouseEventType::MouseUp)
  680. {
  681. if (!_isDisabled())
  682. onClicked();
  683. return true;
  684. }
  685. return false;
  686. }
  687. }