TransformUtilities.cpp 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924
  1. /*
  2. * This source file is part of RmlUi, the HTML/CSS Interface Middleware
  3. *
  4. * For the latest information, see http://github.com/mikke89/RmlUi
  5. *
  6. * Copyright (c) 2014 Markus Schöngart
  7. * Copyright (c) 2019 The RmlUi Team, and contributors
  8. *
  9. * Permission is hereby granted, free of charge, to any person obtaining a copy
  10. * of this software and associated documentation files (the "Software"), to deal
  11. * in the Software without restriction, including without limitation the rights
  12. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. * copies of the Software, and to permit persons to whom the Software is
  14. * furnished to do so, subject to the following conditions:
  15. *
  16. * The above copyright notice and this permission notice shall be included in
  17. * all copies or substantial portions of the Software.
  18. *
  19. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. * THE SOFTWARE.
  26. *
  27. */
  28. #include "TransformUtilities.h"
  29. #include "../../Include/RmlUi/Core/Element.h"
  30. #include "../../Include/RmlUi/Core/TransformPrimitive.h"
  31. namespace Rml {
  32. using namespace Transforms;
  33. static Vector3f Combine(const Vector3f a, const Vector3f b, float a_scale, float b_scale)
  34. {
  35. Vector3f result;
  36. result.x = a_scale * a.x + b_scale * b.x;
  37. result.y = a_scale * a.y + b_scale * b.y;
  38. result.z = a_scale * a.z + b_scale * b.z;
  39. return result;
  40. }
  41. // Interpolate two quaternions a, b with weight alpha [0, 1]
  42. static Vector4f QuaternionSlerp(const Vector4f a, const Vector4f b, float alpha)
  43. {
  44. using namespace Math;
  45. const float eps = 0.9995f;
  46. float dot = a.DotProduct(b);
  47. dot = Clamp(dot, -1.f, 1.f);
  48. if (dot > eps)
  49. return a;
  50. float theta = ACos(dot);
  51. float w = Sin(alpha * theta) / SquareRoot(1.f - dot * dot);
  52. float a_scale = Cos(alpha * theta) - dot * w;
  53. Vector4f result;
  54. for (int i = 0; i < 4; i++)
  55. {
  56. result[i] = a[i] * a_scale + b[i] * w;
  57. }
  58. return result;
  59. }
  60. /// Resolve a numeric property value for an element.
  61. static inline float ResolveLengthPercentage(NumericValue value, Element& e, float base) noexcept
  62. {
  63. Property prop;
  64. prop.value = Variant(value.number);
  65. prop.unit = value.unit;
  66. return e.ResolveNumericProperty(&prop, base);
  67. }
  68. /// Resolve a numeric property value with the element's width as relative base value.
  69. static inline float ResolveWidth(NumericValue value, Element& e) noexcept
  70. {
  71. if (value.unit & (Property::PX | Property::NUMBER)) return value.number;
  72. return ResolveLengthPercentage(value, e, e.GetBox().GetSize(Box::BORDER).x);
  73. }
  74. /// Resolve a numeric property value with the element's height as relative base value.
  75. static inline float ResolveHeight(NumericValue value, Element& e) noexcept
  76. {
  77. if (value.unit & (Property::PX | Property::NUMBER)) return value.number;
  78. return ResolveLengthPercentage(value, e, e.GetBox().GetSize(Box::BORDER).y);
  79. }
  80. /// Resolve a numeric property value with the element's depth as relative base value.
  81. static inline float ResolveDepth(NumericValue value, Element& e) noexcept
  82. {
  83. if (value.unit & (Property::PX | Property::NUMBER)) return value.number;
  84. Vector2f size = e.GetBox().GetSize(Box::BORDER);
  85. return ResolveLengthPercentage(value, e, Math::Max(size.x, size.y));
  86. }
  87. static inline String ToString(NumericValue value) noexcept
  88. {
  89. Property prop;
  90. prop.value = Variant(value.number);
  91. prop.unit = value.unit;
  92. return prop.ToString();
  93. }
  94. struct SetIdentityVisitor
  95. {
  96. template <size_t N>
  97. void operator()(Transforms::ResolvedPrimitive<N>& p)
  98. {
  99. for (auto& value : p.values)
  100. value = 0.0f;
  101. }
  102. template <size_t N>
  103. void operator()(Transforms::UnresolvedPrimitive<N>& p)
  104. {
  105. for (auto& value : p.values)
  106. value.number = 0.0f;
  107. }
  108. void operator()(Transforms::Matrix2D& p)
  109. {
  110. for (int i = 0; i < 6; i++)
  111. p.values[i] = ((i == 0 || i == 3) ? 1.0f : 0.0f);
  112. }
  113. void operator()(Transforms::Matrix3D& p)
  114. {
  115. for (int i = 0; i < 16; i++)
  116. p.values[i] = ((i % 5) == 0 ? 1.0f : 0.0f);
  117. }
  118. void operator()(Transforms::ScaleX& p)
  119. {
  120. p.values[0] = 1;
  121. }
  122. void operator()(Transforms::ScaleY& p)
  123. {
  124. p.values[0] = 1;
  125. }
  126. void operator()(Transforms::ScaleZ& p)
  127. {
  128. p.values[0] = 1;
  129. }
  130. void operator()(Transforms::Scale2D& p)
  131. {
  132. p.values[0] = p.values[1] = 1;
  133. }
  134. void operator()(Transforms::Scale3D& p)
  135. {
  136. p.values[0] = p.values[1] = p.values[2] = 1;
  137. }
  138. void operator()(Transforms::DecomposedMatrix4& p)
  139. {
  140. p.perspective = Vector4f(0, 0, 0, 1);
  141. p.quaternion = Vector4f(0, 0, 0, 1);
  142. p.translation = Vector3f(0, 0, 0);
  143. p.scale = Vector3f(1, 1, 1);
  144. p.skew = Vector3f(0, 0, 0);
  145. }
  146. void run(TransformPrimitive& primitive)
  147. {
  148. switch (primitive.type)
  149. {
  150. case TransformPrimitive::MATRIX2D: this->operator()(primitive.matrix_2d); break;
  151. case TransformPrimitive::MATRIX3D: this->operator()(primitive.matrix_3d); break;
  152. case TransformPrimitive::TRANSLATEX: this->operator()(primitive.translate_x); break;
  153. case TransformPrimitive::TRANSLATEY: this->operator()(primitive.translate_y); break;
  154. case TransformPrimitive::TRANSLATEZ: this->operator()(primitive.translate_z); break;
  155. case TransformPrimitive::TRANSLATE2D: this->operator()(primitive.translate_2d); break;
  156. case TransformPrimitive::TRANSLATE3D: this->operator()(primitive.translate_3d); break;
  157. case TransformPrimitive::SCALEX: this->operator()(primitive.scale_x); break;
  158. case TransformPrimitive::SCALEY: this->operator()(primitive.scale_y); break;
  159. case TransformPrimitive::SCALEZ: this->operator()(primitive.scale_z); break;
  160. case TransformPrimitive::SCALE2D: this->operator()(primitive.scale_2d); break;
  161. case TransformPrimitive::SCALE3D: this->operator()(primitive.scale_3d); break;
  162. case TransformPrimitive::ROTATEX: this->operator()(primitive.rotate_x); break;
  163. case TransformPrimitive::ROTATEY: this->operator()(primitive.rotate_y); break;
  164. case TransformPrimitive::ROTATEZ: this->operator()(primitive.rotate_z); break;
  165. case TransformPrimitive::ROTATE2D: this->operator()(primitive.rotate_2d); break;
  166. case TransformPrimitive::ROTATE3D: this->operator()(primitive.rotate_3d); break;
  167. case TransformPrimitive::SKEWX: this->operator()(primitive.skew_x); break;
  168. case TransformPrimitive::SKEWY: this->operator()(primitive.skew_y); break;
  169. case TransformPrimitive::SKEW2D: this->operator()(primitive.skew_2d); break;
  170. case TransformPrimitive::PERSPECTIVE: this->operator()(primitive.perspective); break;
  171. case TransformPrimitive::DECOMPOSEDMATRIX4: this->operator()(primitive.decomposed_matrix_4); break;
  172. default:
  173. RMLUI_ASSERT(false);
  174. break;
  175. }
  176. }
  177. };
  178. void TransformUtilities::SetIdentity(TransformPrimitive& p) noexcept
  179. {
  180. SetIdentityVisitor{}.run(p);
  181. }
  182. struct ResolveTransformVisitor
  183. {
  184. Matrix4f& m;
  185. Element& e;
  186. void operator()(const Transforms::Matrix2D& p)
  187. {
  188. m = Matrix4f::FromRows(
  189. Vector4f(p.values[0], p.values[2], 0, p.values[4]),
  190. Vector4f(p.values[1], p.values[3], 0, p.values[5]),
  191. Vector4f(0, 0, 1, 0),
  192. Vector4f(0, 0, 0, 1)
  193. );
  194. }
  195. void operator()(const Transforms::Matrix3D& p)
  196. {
  197. m = Matrix4f::FromColumns(
  198. Vector4f(p.values[0], p.values[1], p.values[2], p.values[3]),
  199. Vector4f(p.values[4], p.values[5], p.values[6], p.values[7]),
  200. Vector4f(p.values[8], p.values[9], p.values[10], p.values[11]),
  201. Vector4f(p.values[12], p.values[13], p.values[14], p.values[15])
  202. );
  203. }
  204. void operator()(const Transforms::TranslateX& p)
  205. {
  206. m = Matrix4f::TranslateX(ResolveWidth(p.values[0], e));
  207. }
  208. void operator()(const Transforms::TranslateY& p)
  209. {
  210. m = Matrix4f::TranslateY(ResolveHeight(p.values[0], e));
  211. }
  212. void operator()(const Transforms::TranslateZ& p)
  213. {
  214. m = Matrix4f::TranslateZ(ResolveDepth(p.values[0], e));
  215. }
  216. void operator()(const Transforms::Translate2D& p)
  217. {
  218. m = Matrix4f::Translate(
  219. ResolveWidth(p.values[0], e),
  220. ResolveHeight(p.values[1], e),
  221. 0
  222. );
  223. }
  224. void operator()(const Transforms::Translate3D& p)
  225. {
  226. m = Matrix4f::Translate(
  227. ResolveWidth(p.values[0], e),
  228. ResolveHeight(p.values[1], e),
  229. ResolveDepth(p.values[2], e)
  230. );
  231. }
  232. void operator()(const Transforms::ScaleX& p)
  233. {
  234. m = Matrix4f::ScaleX(p.values[0]);
  235. }
  236. void operator()(const Transforms::ScaleY& p)
  237. {
  238. m = Matrix4f::ScaleY(p.values[0]);
  239. }
  240. void operator()(const Transforms::ScaleZ& p)
  241. {
  242. m = Matrix4f::ScaleZ(p.values[0]);
  243. }
  244. void operator()(const Transforms::Scale2D& p)
  245. {
  246. m = Matrix4f::Scale(p.values[0], p.values[1], 1);
  247. }
  248. void operator()(const Transforms::Scale3D& p)
  249. {
  250. m = Matrix4f::Scale(p.values[0], p.values[1], p.values[2]);
  251. }
  252. void operator()(const Transforms::RotateX& p)
  253. {
  254. m = Matrix4f::RotateX(p.values[0]);
  255. }
  256. void operator()(const Transforms::RotateY& p)
  257. {
  258. m = Matrix4f::RotateY(p.values[0]);
  259. }
  260. void operator()(const Transforms::RotateZ& p)
  261. {
  262. m = Matrix4f::RotateZ(p.values[0]);
  263. }
  264. void operator()(const Transforms::Rotate2D& p)
  265. {
  266. m = Matrix4f::RotateZ(p.values[0]);
  267. }
  268. void operator()(const Transforms::Rotate3D& p)
  269. {
  270. m = Matrix4f::Rotate(Vector3f(p.values[0], p.values[1], p.values[2]), p.values[3]);
  271. }
  272. void operator()(const Transforms::SkewX& p)
  273. {
  274. m = Matrix4f::SkewX(p.values[0]);
  275. }
  276. void operator()(const Transforms::SkewY& p)
  277. {
  278. m = Matrix4f::SkewY(p.values[0]);
  279. }
  280. void operator()(const Transforms::Skew2D& p)
  281. {
  282. m = Matrix4f::Skew(p.values[0], p.values[1]);
  283. }
  284. void operator()(const Transforms::DecomposedMatrix4& p)
  285. {
  286. m = Matrix4f::Compose(p.translation, p.scale, p.skew, p.perspective, p.quaternion);
  287. }
  288. void operator()(const Transforms::Perspective& p)
  289. {
  290. m = Matrix4f::Perspective(ResolveDepth(p.values[0], e));
  291. }
  292. void run(const TransformPrimitive& primitive)
  293. {
  294. switch (primitive.type)
  295. {
  296. case TransformPrimitive::MATRIX2D: this->operator()(primitive.matrix_2d); break;
  297. case TransformPrimitive::MATRIX3D: this->operator()(primitive.matrix_3d); break;
  298. case TransformPrimitive::TRANSLATEX: this->operator()(primitive.translate_x); break;
  299. case TransformPrimitive::TRANSLATEY: this->operator()(primitive.translate_y); break;
  300. case TransformPrimitive::TRANSLATEZ: this->operator()(primitive.translate_z); break;
  301. case TransformPrimitive::TRANSLATE2D: this->operator()(primitive.translate_2d); break;
  302. case TransformPrimitive::TRANSLATE3D: this->operator()(primitive.translate_3d); break;
  303. case TransformPrimitive::SCALEX: this->operator()(primitive.scale_x); break;
  304. case TransformPrimitive::SCALEY: this->operator()(primitive.scale_y); break;
  305. case TransformPrimitive::SCALEZ: this->operator()(primitive.scale_z); break;
  306. case TransformPrimitive::SCALE2D: this->operator()(primitive.scale_2d); break;
  307. case TransformPrimitive::SCALE3D: this->operator()(primitive.scale_3d); break;
  308. case TransformPrimitive::ROTATEX: this->operator()(primitive.rotate_x); break;
  309. case TransformPrimitive::ROTATEY: this->operator()(primitive.rotate_y); break;
  310. case TransformPrimitive::ROTATEZ: this->operator()(primitive.rotate_z); break;
  311. case TransformPrimitive::ROTATE2D: this->operator()(primitive.rotate_2d); break;
  312. case TransformPrimitive::ROTATE3D: this->operator()(primitive.rotate_3d); break;
  313. case TransformPrimitive::SKEWX: this->operator()(primitive.skew_x); break;
  314. case TransformPrimitive::SKEWY: this->operator()(primitive.skew_y); break;
  315. case TransformPrimitive::SKEW2D: this->operator()(primitive.skew_2d); break;
  316. case TransformPrimitive::PERSPECTIVE: this->operator()(primitive.perspective); break;
  317. case TransformPrimitive::DECOMPOSEDMATRIX4: this->operator()(primitive.decomposed_matrix_4); break;
  318. }
  319. }
  320. };
  321. Matrix4f TransformUtilities::ResolveTransform(const TransformPrimitive& p, Element& e) noexcept
  322. {
  323. Matrix4f m;
  324. ResolveTransformVisitor visitor{ m, e };
  325. visitor.run(p);
  326. return m;
  327. }
  328. struct PrepareVisitor
  329. {
  330. Element& e;
  331. bool operator()(TranslateX& p)
  332. {
  333. p.values[0] = NumericValue{ ResolveWidth(p.values[0], e), Property::PX };
  334. return true;
  335. }
  336. bool operator()(TranslateY& p)
  337. {
  338. p.values[0] = NumericValue{ ResolveHeight(p.values[0], e), Property::PX };
  339. return true;
  340. }
  341. bool operator()(TranslateZ& p)
  342. {
  343. p.values[0] = NumericValue{ ResolveDepth(p.values[0], e), Property::PX };
  344. return true;
  345. }
  346. bool operator()(Translate2D& p)
  347. {
  348. p.values[0] = NumericValue{ ResolveWidth(p.values[0], e), Property::PX };
  349. p.values[1] = NumericValue{ ResolveHeight(p.values[1], e), Property::PX };
  350. return true;
  351. }
  352. bool operator()(Translate3D& p)
  353. {
  354. p.values[0] = NumericValue{ ResolveWidth(p.values[0], e), Property::PX };
  355. p.values[1] = NumericValue{ ResolveHeight(p.values[1], e), Property::PX };
  356. p.values[2] = NumericValue{ ResolveDepth(p.values[2], e), Property::PX };
  357. return true;
  358. }
  359. template <size_t N>
  360. bool operator()(ResolvedPrimitive<N>& /*p*/)
  361. {
  362. // No conversion needed for resolved transforms (with some exceptions below)
  363. return true;
  364. }
  365. bool operator()(DecomposedMatrix4& /*p*/)
  366. {
  367. return true;
  368. }
  369. bool operator()(Rotate3D& p)
  370. {
  371. // Rotate3D can be interpolated if and only if their rotation axes point in the same direction.
  372. // We normalize the rotation vector here for easy comparison, and return true here. Later on we make the
  373. // pair-wise check in 'TryConvertToMatchingGenericType' to see if we need to decompose.
  374. Vector3f vec = Vector3f(p.values[0], p.values[1], p.values[2]).Normalise();
  375. p.values[0] = vec.x;
  376. p.values[1] = vec.y;
  377. p.values[2] = vec.z;
  378. return true;
  379. }
  380. bool operator()(Matrix3D& /*p*/)
  381. {
  382. // Matrices must be decomposed for interpolation
  383. return false;
  384. }
  385. bool operator()(Matrix2D& /*p*/)
  386. {
  387. // Matrix2D can also be optimized for interpolation, but for now we decompose it to a full DecomposedMatrix4
  388. return false;
  389. }
  390. bool operator()(Perspective& /*p*/)
  391. {
  392. // Perspective must be decomposed
  393. return false;
  394. }
  395. bool run(TransformPrimitive& primitive)
  396. {
  397. switch (primitive.type)
  398. {
  399. case TransformPrimitive::MATRIX2D: return this->operator()(primitive.matrix_2d);
  400. case TransformPrimitive::MATRIX3D: return this->operator()(primitive.matrix_3d);
  401. case TransformPrimitive::TRANSLATEX: return this->operator()(primitive.translate_x);
  402. case TransformPrimitive::TRANSLATEY: return this->operator()(primitive.translate_y);
  403. case TransformPrimitive::TRANSLATEZ: return this->operator()(primitive.translate_z);
  404. case TransformPrimitive::TRANSLATE2D: return this->operator()(primitive.translate_2d);
  405. case TransformPrimitive::TRANSLATE3D: return this->operator()(primitive.translate_3d);
  406. case TransformPrimitive::SCALEX: return this->operator()(primitive.scale_x);
  407. case TransformPrimitive::SCALEY: return this->operator()(primitive.scale_y);
  408. case TransformPrimitive::SCALEZ: return this->operator()(primitive.scale_z);
  409. case TransformPrimitive::SCALE2D: return this->operator()(primitive.scale_2d);
  410. case TransformPrimitive::SCALE3D: return this->operator()(primitive.scale_3d);
  411. case TransformPrimitive::ROTATEX: return this->operator()(primitive.rotate_x);
  412. case TransformPrimitive::ROTATEY: return this->operator()(primitive.rotate_y);
  413. case TransformPrimitive::ROTATEZ: return this->operator()(primitive.rotate_z);
  414. case TransformPrimitive::ROTATE2D: return this->operator()(primitive.rotate_2d);
  415. case TransformPrimitive::ROTATE3D: return this->operator()(primitive.rotate_3d);
  416. case TransformPrimitive::SKEWX: return this->operator()(primitive.skew_x);
  417. case TransformPrimitive::SKEWY: return this->operator()(primitive.skew_y);
  418. case TransformPrimitive::SKEW2D: return this->operator()(primitive.skew_2d);
  419. case TransformPrimitive::PERSPECTIVE: return this->operator()(primitive.perspective);
  420. case TransformPrimitive::DECOMPOSEDMATRIX4: return this->operator()(primitive.decomposed_matrix_4);
  421. default:
  422. break;
  423. }
  424. RMLUI_ASSERT(false);
  425. return false;
  426. }
  427. };
  428. bool TransformUtilities::PrepareForInterpolation(TransformPrimitive& p, Element& e) noexcept
  429. {
  430. return PrepareVisitor{ e }.run(p);
  431. }
  432. enum class GenericType { None, Scale3D, Translate3D, Rotate3D };
  433. struct GetGenericTypeVisitor
  434. {
  435. GenericType run(const TransformPrimitive& primitive)
  436. {
  437. switch (primitive.type)
  438. {
  439. case TransformPrimitive::TRANSLATEX: return GenericType::Translate3D;
  440. case TransformPrimitive::TRANSLATEY: return GenericType::Translate3D;
  441. case TransformPrimitive::TRANSLATEZ: return GenericType::Translate3D;
  442. case TransformPrimitive::TRANSLATE2D: return GenericType::Translate3D;
  443. case TransformPrimitive::TRANSLATE3D: return GenericType::Translate3D;
  444. case TransformPrimitive::SCALEX: return GenericType::Scale3D;
  445. case TransformPrimitive::SCALEY: return GenericType::Scale3D;
  446. case TransformPrimitive::SCALEZ: return GenericType::Scale3D;
  447. case TransformPrimitive::SCALE2D: return GenericType::Scale3D;
  448. case TransformPrimitive::SCALE3D: return GenericType::Scale3D;
  449. case TransformPrimitive::ROTATEX: return GenericType::Rotate3D;
  450. case TransformPrimitive::ROTATEY: return GenericType::Rotate3D;
  451. case TransformPrimitive::ROTATEZ: return GenericType::Rotate3D;
  452. case TransformPrimitive::ROTATE2D: return GenericType::Rotate3D;
  453. case TransformPrimitive::ROTATE3D: return GenericType::Rotate3D;
  454. default:
  455. break;
  456. }
  457. return GenericType::None;
  458. }
  459. };
  460. struct ConvertToGenericTypeVisitor
  461. {
  462. Translate3D operator()(const TranslateX& p) { return Translate3D{ p.values[0], {0.0f, Property::PX}, {0.0f, Property::PX} }; }
  463. Translate3D operator()(const TranslateY& p) { return Translate3D{ {0.0f, Property::PX}, p.values[0], {0.0f, Property::PX} }; }
  464. Translate3D operator()(const TranslateZ& p) { return Translate3D{ {0.0f, Property::PX}, {0.0f, Property::PX}, p.values[0] }; }
  465. Translate3D operator()(const Translate2D& p) { return Translate3D{ p.values[0], p.values[1], {0.0f, Property::PX} }; }
  466. Scale3D operator()(const ScaleX& p) { return Scale3D{ p.values[0], 1.0f, 1.0f }; }
  467. Scale3D operator()(const ScaleY& p) { return Scale3D{ 1.0f, p.values[0], 1.0f }; }
  468. Scale3D operator()(const ScaleZ& p) { return Scale3D{ 1.0f, 1.0f, p.values[0] }; }
  469. Scale3D operator()(const Scale2D& p) { return Scale3D{ p.values[0], p.values[1], 1.0f }; }
  470. Rotate3D operator()(const RotateX& p) { return Rotate3D{ 1, 0, 0, p.values[0], Property::RAD }; }
  471. Rotate3D operator()(const RotateY& p) { return Rotate3D{ 0, 1, 0, p.values[0], Property::RAD }; }
  472. Rotate3D operator()(const RotateZ& p) { return Rotate3D{ 0, 0, 1, p.values[0], Property::RAD }; }
  473. Rotate3D operator()(const Rotate2D& p) { return Rotate3D{ 0, 0, 1, p.values[0], Property::RAD }; }
  474. template <typename T>
  475. TransformPrimitive operator()(const T& p) { RMLUI_ERROR; return p; }
  476. TransformPrimitive run(const TransformPrimitive& primitive)
  477. {
  478. TransformPrimitive result = primitive;
  479. switch (primitive.type)
  480. {
  481. case TransformPrimitive::TRANSLATEX: result.type = TransformPrimitive::TRANSLATE3D; result.translate_3d = this->operator()(primitive.translate_x); break;
  482. case TransformPrimitive::TRANSLATEY: result.type = TransformPrimitive::TRANSLATE3D; result.translate_3d = this->operator()(primitive.translate_y); break;
  483. case TransformPrimitive::TRANSLATEZ: result.type = TransformPrimitive::TRANSLATE3D; result.translate_3d = this->operator()(primitive.translate_z); break;
  484. case TransformPrimitive::TRANSLATE2D: result.type = TransformPrimitive::TRANSLATE3D; result.translate_3d = this->operator()(primitive.translate_2d); break;
  485. case TransformPrimitive::TRANSLATE3D: break;
  486. case TransformPrimitive::SCALEX: result.type = TransformPrimitive::SCALE3D; result.scale_3d = this->operator()(primitive.scale_x); break;
  487. case TransformPrimitive::SCALEY: result.type = TransformPrimitive::SCALE3D; result.scale_3d = this->operator()(primitive.scale_y); break;
  488. case TransformPrimitive::SCALEZ: result.type = TransformPrimitive::SCALE3D; result.scale_3d = this->operator()(primitive.scale_z); break;
  489. case TransformPrimitive::SCALE2D: result.type = TransformPrimitive::SCALE3D; result.scale_3d = this->operator()(primitive.scale_2d); break;
  490. case TransformPrimitive::SCALE3D: break;
  491. case TransformPrimitive::ROTATEX: result.type = TransformPrimitive::ROTATE3D; result.rotate_3d = this->operator()(primitive.rotate_x); break;
  492. case TransformPrimitive::ROTATEY: result.type = TransformPrimitive::ROTATE3D; result.rotate_3d = this->operator()(primitive.rotate_y); break;
  493. case TransformPrimitive::ROTATEZ: result.type = TransformPrimitive::ROTATE3D; result.rotate_3d = this->operator()(primitive.rotate_z); break;
  494. case TransformPrimitive::ROTATE2D: result.type = TransformPrimitive::ROTATE3D; result.rotate_3d = this->operator()(primitive.rotate_2d); break;
  495. case TransformPrimitive::ROTATE3D: break;
  496. default:
  497. RMLUI_ASSERT(false);
  498. break;
  499. }
  500. return result;
  501. }
  502. };
  503. static bool CanInterpolateRotate3D(const Rotate3D& p0, const Rotate3D& p1)
  504. {
  505. // Rotate3D can only be interpolated if and only if their rotation axes point in the same direction.
  506. // Assumes each rotation axis has already been normalized.
  507. auto& v0 = p0.values;
  508. auto& v1 = p1.values;
  509. return v0[0] == v1[0] && v0[1] == v1[1] && v0[2] == v1[2];
  510. }
  511. bool TransformUtilities::TryConvertToMatchingGenericType(TransformPrimitive& p0, TransformPrimitive& p1) noexcept
  512. {
  513. if (p0.type == p1.type)
  514. {
  515. if (p0.type == TransformPrimitive::ROTATE3D && !CanInterpolateRotate3D(p0.rotate_3d, p1.rotate_3d))
  516. return false;
  517. return true;
  518. }
  519. GenericType c0 = GetGenericTypeVisitor{}.run(p0);
  520. GenericType c1 = GetGenericTypeVisitor{}.run(p1);
  521. if (c0 == c1 && c0 != GenericType::None)
  522. {
  523. TransformPrimitive new_p0 = ConvertToGenericTypeVisitor{}.run(p0);
  524. TransformPrimitive new_p1 = ConvertToGenericTypeVisitor{}.run(p1);
  525. RMLUI_ASSERT(new_p0.type == new_p1.type);
  526. if (new_p0.type == TransformPrimitive::ROTATE3D && !CanInterpolateRotate3D(new_p0.rotate_3d, new_p1.rotate_3d))
  527. return false;
  528. p0 = new_p0;
  529. p1 = new_p1;
  530. return true;
  531. }
  532. return false;
  533. }
  534. struct InterpolateVisitor
  535. {
  536. const TransformPrimitive& other_variant;
  537. float alpha;
  538. template <size_t N>
  539. bool Interpolate(ResolvedPrimitive<N>& p0, const ResolvedPrimitive<N>& p1)
  540. {
  541. for (size_t i = 0; i < N; i++)
  542. p0.values[i] = p0.values[i] * (1.0f - alpha) + p1.values[i] * alpha;
  543. return true;
  544. }
  545. template <size_t N>
  546. bool Interpolate(UnresolvedPrimitive<N>& p0, const UnresolvedPrimitive<N>& p1)
  547. {
  548. // Assumes that the underlying units have been resolved (e.g. to pixels)
  549. for (size_t i = 0; i < N; i++)
  550. p0.values[i].number = p0.values[i].number * (1.0f - alpha) + p1.values[i].number * alpha;
  551. return true;
  552. }
  553. bool Interpolate(Rotate3D& p0, const Rotate3D& p1)
  554. {
  555. RMLUI_ASSERT(CanInterpolateRotate3D(p0, p1));
  556. // We can only interpolate rotate3d if their rotation axes align. That should be the case if we get here,
  557. // otherwise the generic type matching should decompose them. Thus, we only need to interpolate
  558. // the angle value here.
  559. p0.values[3] = p0.values[3] * (1.0f - alpha) + p1.values[3] * alpha;
  560. return true;
  561. }
  562. bool Interpolate(Matrix2D& /*p0*/, const Matrix2D& /*p1*/) { RMLUI_ERROR; return false; /* Error if we get here, see PrepareForInterpolation() */ }
  563. bool Interpolate(Matrix3D& /*p0*/, const Matrix3D& /*p1*/) { RMLUI_ERROR; return false; /* Error if we get here, see PrepareForInterpolation() */ }
  564. bool Interpolate(Perspective& /*p0*/, const Perspective& /*p1*/) { RMLUI_ERROR; return false; /* Error if we get here, see PrepareForInterpolation() */ }
  565. bool Interpolate(DecomposedMatrix4& p0, const DecomposedMatrix4& p1)
  566. {
  567. p0.perspective = p0.perspective * (1.0f - alpha) + p1.perspective * alpha;
  568. p0.quaternion = QuaternionSlerp(p0.quaternion, p1.quaternion, alpha);
  569. p0.translation = p0.translation * (1.0f - alpha) + p1.translation * alpha;
  570. p0.scale = p0.scale * (1.0f - alpha) + p1.scale * alpha;
  571. p0.skew = p0.skew * (1.0f - alpha) + p1.skew * alpha;
  572. return true;
  573. }
  574. bool run(TransformPrimitive& variant)
  575. {
  576. RMLUI_ASSERT(variant.type == other_variant.type);
  577. switch (variant.type)
  578. {
  579. case TransformPrimitive::MATRIX2D: return Interpolate(variant.matrix_2d, other_variant.matrix_2d);
  580. case TransformPrimitive::MATRIX3D: return Interpolate(variant.matrix_3d, other_variant.matrix_3d);
  581. case TransformPrimitive::TRANSLATEX: return Interpolate(variant.translate_x, other_variant.translate_x);
  582. case TransformPrimitive::TRANSLATEY: return Interpolate(variant.translate_y, other_variant.translate_y);
  583. case TransformPrimitive::TRANSLATEZ: return Interpolate(variant.translate_z, other_variant.translate_z);
  584. case TransformPrimitive::TRANSLATE2D: return Interpolate(variant.translate_2d, other_variant.translate_2d);
  585. case TransformPrimitive::TRANSLATE3D: return Interpolate(variant.translate_3d, other_variant.translate_3d);
  586. case TransformPrimitive::SCALEX: return Interpolate(variant.scale_x, other_variant.scale_x);
  587. case TransformPrimitive::SCALEY: return Interpolate(variant.scale_y, other_variant.scale_y);
  588. case TransformPrimitive::SCALEZ: return Interpolate(variant.scale_z, other_variant.scale_z);
  589. case TransformPrimitive::SCALE2D: return Interpolate(variant.scale_2d, other_variant.scale_2d);
  590. case TransformPrimitive::SCALE3D: return Interpolate(variant.scale_3d, other_variant.scale_3d);
  591. case TransformPrimitive::ROTATEX: return Interpolate(variant.rotate_x, other_variant.rotate_x);
  592. case TransformPrimitive::ROTATEY: return Interpolate(variant.rotate_y, other_variant.rotate_y);
  593. case TransformPrimitive::ROTATEZ: return Interpolate(variant.rotate_z, other_variant.rotate_z);
  594. case TransformPrimitive::ROTATE2D: return Interpolate(variant.rotate_2d, other_variant.rotate_2d);
  595. case TransformPrimitive::ROTATE3D: return Interpolate(variant.rotate_3d, other_variant.rotate_3d);
  596. case TransformPrimitive::SKEWX: return Interpolate(variant.skew_x, other_variant.skew_x);
  597. case TransformPrimitive::SKEWY: return Interpolate(variant.skew_y, other_variant.skew_y);
  598. case TransformPrimitive::SKEW2D: return Interpolate(variant.skew_2d, other_variant.skew_2d);
  599. case TransformPrimitive::PERSPECTIVE: return Interpolate(variant.perspective, other_variant.perspective);
  600. case TransformPrimitive::DECOMPOSEDMATRIX4: return Interpolate(variant.decomposed_matrix_4, other_variant.decomposed_matrix_4);
  601. }
  602. RMLUI_ASSERT(false);
  603. return false;
  604. }
  605. };
  606. bool TransformUtilities::InterpolateWith(TransformPrimitive& target, const TransformPrimitive& other, float alpha) noexcept
  607. {
  608. if (target.type != other.type)
  609. return false;
  610. bool result = InterpolateVisitor{ other, alpha }.run(target);
  611. return result;
  612. }
  613. template<size_t N>
  614. static inline String ToString(const Transforms::ResolvedPrimitive<N>& p, String unit, bool rad_to_deg = false, bool only_unit_on_last_value = false) noexcept {
  615. float multiplier = 1.0f;
  616. String tmp;
  617. String result = "(";
  618. for (size_t i = 0; i < N; i++)
  619. {
  620. if (only_unit_on_last_value && i < N - 1)
  621. multiplier = 1.0f;
  622. else if (rad_to_deg)
  623. multiplier = 180.f / Math::RMLUI_PI;
  624. if (TypeConverter<float, String>::Convert(p.values[i] * multiplier, tmp))
  625. result += tmp;
  626. if (!unit.empty() && (!only_unit_on_last_value || (i == N - 1)))
  627. result += unit;
  628. if (i < N - 1)
  629. result += ", ";
  630. }
  631. result += ")";
  632. return result;
  633. }
  634. template<size_t N>
  635. static inline String ToString(const Transforms::UnresolvedPrimitive<N>& p) noexcept {
  636. String result = "(";
  637. for (size_t i = 0; i < N; i++)
  638. {
  639. result += ToString(p.values[i]);
  640. if (i != N - 1)
  641. result += ", ";
  642. }
  643. result += ")";
  644. return result;
  645. }
  646. static inline String ToString(const Transforms::DecomposedMatrix4& p) noexcept {
  647. static const Transforms::DecomposedMatrix4 d{
  648. Vector4f(0, 0, 0, 1),
  649. Vector4f(0, 0, 0, 1),
  650. Vector3f(0, 0, 0),
  651. Vector3f(1, 1, 1),
  652. Vector3f(0, 0, 0)
  653. };
  654. String tmp;
  655. String result;
  656. if (p.perspective != d.perspective && TypeConverter< Vector4f, String >::Convert(p.perspective, tmp))
  657. result += "perspective(" + tmp + "), ";
  658. if (p.quaternion != d.quaternion && TypeConverter< Vector4f, String >::Convert(p.quaternion, tmp))
  659. result += "quaternion(" + tmp + "), ";
  660. if (p.translation != d.translation && TypeConverter< Vector3f, String >::Convert(p.translation, tmp))
  661. result += "translation(" + tmp + "), ";
  662. if (p.scale != d.scale && TypeConverter< Vector3f, String >::Convert(p.scale, tmp))
  663. result += "scale(" + tmp + "), ";
  664. if (p.skew != d.skew && TypeConverter< Vector3f, String >::Convert(p.skew, tmp))
  665. result += "skew(" + tmp + "), ";
  666. if (result.size() > 2)
  667. result.resize(result.size() - 2);
  668. result = "decomposedMatrix3d{ " + result + " }";
  669. return result;
  670. }
  671. static inline String ToString(const Transforms::Matrix2D& p) noexcept { return "matrix" + ToString(static_cast<const Transforms::ResolvedPrimitive< 6 >&>(p), ""); }
  672. static inline String ToString(const Transforms::Matrix3D& p) noexcept { return "matrix3d" + ToString(static_cast<const Transforms::ResolvedPrimitive< 16 >&>(p), ""); }
  673. static inline String ToString(const Transforms::TranslateX& p) noexcept { return "translateX" + ToString(static_cast<const Transforms::UnresolvedPrimitive< 1 >&>(p)); }
  674. static inline String ToString(const Transforms::TranslateY& p) noexcept { return "translateY" + ToString(static_cast<const Transforms::UnresolvedPrimitive< 1 >&>(p)); }
  675. static inline String ToString(const Transforms::TranslateZ& p) noexcept { return "translateZ" + ToString(static_cast<const Transforms::UnresolvedPrimitive< 1 >&>(p)); }
  676. static inline String ToString(const Transforms::Translate2D& p) noexcept { return "translate" + ToString(static_cast<const Transforms::UnresolvedPrimitive< 2 >&>(p)); }
  677. static inline String ToString(const Transforms::Translate3D& p) noexcept { return "translate3d" + ToString(static_cast<const Transforms::UnresolvedPrimitive< 3 >&>(p)); }
  678. static inline String ToString(const Transforms::ScaleX& p) noexcept { return "scaleX" + ToString(static_cast<const Transforms::ResolvedPrimitive< 1 >&>(p), ""); }
  679. static inline String ToString(const Transforms::ScaleY& p) noexcept { return "scaleY" + ToString(static_cast<const Transforms::ResolvedPrimitive< 1 >&>(p), ""); }
  680. static inline String ToString(const Transforms::ScaleZ& p) noexcept { return "scaleZ" + ToString(static_cast<const Transforms::ResolvedPrimitive< 1 >&>(p), ""); }
  681. static inline String ToString(const Transforms::Scale2D& p) noexcept { return "scale" + ToString(static_cast<const Transforms::ResolvedPrimitive< 2 >&>(p), ""); }
  682. static inline String ToString(const Transforms::Scale3D& p) noexcept { return "scale3d" + ToString(static_cast<const Transforms::ResolvedPrimitive< 3 >&>(p), ""); }
  683. static inline String ToString(const Transforms::RotateX& p) noexcept { return "rotateX" + ToString(static_cast<const Transforms::ResolvedPrimitive< 1 >&>(p), "deg", true); }
  684. static inline String ToString(const Transforms::RotateY& p) noexcept { return "rotateY" + ToString(static_cast<const Transforms::ResolvedPrimitive< 1 >&>(p), "deg", true); }
  685. static inline String ToString(const Transforms::RotateZ& p) noexcept { return "rotateZ" + ToString(static_cast<const Transforms::ResolvedPrimitive< 1 >&>(p), "deg", true); }
  686. static inline String ToString(const Transforms::Rotate2D& p) noexcept { return "rotate" + ToString(static_cast<const Transforms::ResolvedPrimitive< 1 >&>(p), "deg", true); }
  687. static inline String ToString(const Transforms::Rotate3D& p) noexcept { return "rotate3d" + ToString(static_cast<const Transforms::ResolvedPrimitive< 4 >&>(p), "deg", true, true); }
  688. static inline String ToString(const Transforms::SkewX& p) noexcept { return "skewX" + ToString(static_cast<const Transforms::ResolvedPrimitive< 1 >&>(p), "deg", true); }
  689. static inline String ToString(const Transforms::SkewY& p) noexcept { return "skewY" + ToString(static_cast<const Transforms::ResolvedPrimitive< 1 >&>(p), "deg", true); }
  690. static inline String ToString(const Transforms::Skew2D& p) noexcept { return "skew" + ToString(static_cast<const Transforms::ResolvedPrimitive< 2 >&>(p), "deg", true); }
  691. static inline String ToString(const Transforms::Perspective& p) noexcept { return "perspective" + ToString(static_cast<const Transforms::UnresolvedPrimitive< 1 >&>(p)); }
  692. struct ToStringVisitor
  693. {
  694. String run(const TransformPrimitive& variant)
  695. {
  696. switch (variant.type)
  697. {
  698. case TransformPrimitive::MATRIX2D: return ToString(variant.matrix_2d);
  699. case TransformPrimitive::MATRIX3D: return ToString(variant.matrix_3d);
  700. case TransformPrimitive::TRANSLATEX: return ToString(variant.translate_x);
  701. case TransformPrimitive::TRANSLATEY: return ToString(variant.translate_y);
  702. case TransformPrimitive::TRANSLATEZ: return ToString(variant.translate_z);
  703. case TransformPrimitive::TRANSLATE2D: return ToString(variant.translate_2d);
  704. case TransformPrimitive::TRANSLATE3D: return ToString(variant.translate_3d);
  705. case TransformPrimitive::SCALEX: return ToString(variant.scale_x);
  706. case TransformPrimitive::SCALEY: return ToString(variant.scale_y);
  707. case TransformPrimitive::SCALEZ: return ToString(variant.scale_z);
  708. case TransformPrimitive::SCALE2D: return ToString(variant.scale_2d);
  709. case TransformPrimitive::SCALE3D: return ToString(variant.scale_3d);
  710. case TransformPrimitive::ROTATEX: return ToString(variant.rotate_x);
  711. case TransformPrimitive::ROTATEY: return ToString(variant.rotate_y);
  712. case TransformPrimitive::ROTATEZ: return ToString(variant.rotate_z);
  713. case TransformPrimitive::ROTATE2D: return ToString(variant.rotate_2d);
  714. case TransformPrimitive::ROTATE3D: return ToString(variant.rotate_3d);
  715. case TransformPrimitive::SKEWX: return ToString(variant.skew_x);
  716. case TransformPrimitive::SKEWY: return ToString(variant.skew_y);
  717. case TransformPrimitive::SKEW2D: return ToString(variant.skew_2d);
  718. case TransformPrimitive::PERSPECTIVE: return ToString(variant.perspective);
  719. case TransformPrimitive::DECOMPOSEDMATRIX4: return ToString(variant.decomposed_matrix_4);
  720. default:
  721. break;
  722. }
  723. RMLUI_ASSERT(false);
  724. return String();
  725. }
  726. };
  727. String TransformUtilities::ToString(const TransformPrimitive& p) noexcept
  728. {
  729. String result = ToStringVisitor{}.run(p);
  730. return result;
  731. }
  732. bool TransformUtilities::Decompose(Transforms::DecomposedMatrix4& d, const Matrix4f& m) noexcept
  733. {
  734. // Follows the procedure given in https://drafts.csswg.org/css-transforms-2/#interpolation-of-3d-matrices
  735. const float eps = 0.0005f;
  736. if (Math::AbsoluteValue(m[3][3]) < eps)
  737. return false;
  738. // Perspective matrix
  739. Matrix4f p = m;
  740. for (int i = 0; i < 3; i++)
  741. p[i][3] = 0;
  742. p[3][3] = 1;
  743. if (Math::AbsoluteValue(p.Determinant()) < eps)
  744. return false;
  745. if (m[0][3] != 0 || m[1][3] != 0 || m[2][3] != 0)
  746. {
  747. auto rhs = m.GetColumn(3);
  748. Matrix4f p_inv = p;
  749. if (!p_inv.Invert())
  750. return false;
  751. auto& p_inv_trans = p.Transpose();
  752. d.perspective = p_inv_trans * rhs;
  753. }
  754. else
  755. {
  756. d.perspective[0] = d.perspective[1] = d.perspective[2] = 0;
  757. d.perspective[3] = 1;
  758. }
  759. for (int i = 0; i < 3; i++)
  760. d.translation[i] = m[3][i];
  761. Vector3f row[3];
  762. for (int i = 0; i < 3; i++)
  763. {
  764. row[i][0] = m[i][0];
  765. row[i][1] = m[i][1];
  766. row[i][2] = m[i][2];
  767. }
  768. d.scale[0] = row[0].Magnitude();
  769. row[0] = row[0].Normalise();
  770. d.skew[0] = row[0].DotProduct(row[1]);
  771. row[1] = Combine(row[1], row[0], 1, -d.skew[0]);
  772. d.scale[1] = row[1].Magnitude();
  773. row[1] = row[1].Normalise();
  774. d.skew[0] /= d.scale[1];
  775. d.skew[1] = row[0].DotProduct(row[2]);
  776. row[2] = Combine(row[2], row[0], 1, -d.skew[1]);
  777. d.skew[2] = row[1].DotProduct(row[2]);
  778. row[2] = Combine(row[2], row[1], 1, -d.skew[2]);
  779. d.scale[2] = row[2].Magnitude();
  780. row[2] = row[2].Normalise();
  781. d.skew[2] /= d.scale[2];
  782. d.skew[1] /= d.scale[2];
  783. // Check if we need to flip coordinate system
  784. auto pdum3 = row[1].CrossProduct(row[2]);
  785. if (row[0].DotProduct(pdum3) < 0.0f)
  786. {
  787. for (int i = 0; i < 3; i++)
  788. {
  789. d.scale[i] *= -1.f;
  790. row[i] *= -1.f;
  791. }
  792. }
  793. d.quaternion[0] = 0.5f * Math::SquareRoot(Math::Max(1.f + row[0][0] - row[1][1] - row[2][2], 0.0f));
  794. d.quaternion[1] = 0.5f * Math::SquareRoot(Math::Max(1.f - row[0][0] + row[1][1] - row[2][2], 0.0f));
  795. d.quaternion[2] = 0.5f * Math::SquareRoot(Math::Max(1.f - row[0][0] - row[1][1] + row[2][2], 0.0f));
  796. d.quaternion[3] = 0.5f * Math::SquareRoot(Math::Max(1.f + row[0][0] + row[1][1] + row[2][2], 0.0f));
  797. if (row[2][1] > row[1][2])
  798. d.quaternion[0] *= -1.f;
  799. if (row[0][2] > row[2][0])
  800. d.quaternion[1] *= -1.f;
  801. if (row[1][0] > row[0][1])
  802. d.quaternion[2] *= -1.f;
  803. return true;
  804. }
  805. } // namespace Rml