TransformUtilities.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773
  1. #include "TransformUtilities.h"
  2. #include "../../Include/RmlUi/Core/Element.h"
  3. #include "../../Include/RmlUi/Core/TransformPrimitive.h"
  4. namespace Rml {
  5. using namespace Transforms;
  6. static Vector3f Combine(const Vector3f a, const Vector3f b, float a_scale, float b_scale)
  7. {
  8. Vector3f result;
  9. result.x = a_scale * a.x + b_scale * b.x;
  10. result.y = a_scale * a.y + b_scale * b.y;
  11. result.z = a_scale * a.z + b_scale * b.z;
  12. return result;
  13. }
  14. // Interpolate two quaternions a, b with weight alpha [0, 1]
  15. static Vector4f QuaternionSlerp(const Vector4f a, const Vector4f b, float alpha)
  16. {
  17. using namespace Math;
  18. const float eps = 0.9995f;
  19. float dot = a.DotProduct(b);
  20. dot = Clamp(dot, -1.f, 1.f);
  21. if (dot > eps)
  22. return a;
  23. float theta = ACos(dot);
  24. float w = Sin(alpha * theta) / SquareRoot(1.f - dot * dot);
  25. float a_scale = Cos(alpha * theta) - dot * w;
  26. Vector4f result;
  27. for (int i = 0; i < 4; i++)
  28. {
  29. result[i] = a[i] * a_scale + b[i] * w;
  30. }
  31. return result;
  32. }
  33. // Resolve a numeric property value with the element's width as relative base value.
  34. static inline float ResolveWidth(NumericValue value, Element& e) noexcept
  35. {
  36. if (value.unit == Unit::PX || value.unit == Unit::NUMBER)
  37. return value.number;
  38. return e.ResolveNumericValue(value, e.GetBox().GetSize(BoxArea::Border).x);
  39. }
  40. // Resolve a numeric property value with the element's height as relative base value.
  41. static inline float ResolveHeight(NumericValue value, Element& e) noexcept
  42. {
  43. if (value.unit == Unit::PX || value.unit == Unit::NUMBER)
  44. return value.number;
  45. return e.ResolveNumericValue(value, e.GetBox().GetSize(BoxArea::Border).y);
  46. }
  47. // Resolve a length numeric property value for the given element.
  48. static inline float ResolveLength(NumericValue value, Element& e) noexcept
  49. {
  50. if (value.unit == Unit::PX || value.unit == Unit::NUMBER)
  51. return value.number;
  52. return e.ResolveLength(value);
  53. }
  54. struct SetIdentityVisitor {
  55. template <size_t N>
  56. void operator()(Transforms::ResolvedPrimitive<N>& p)
  57. {
  58. for (auto& value : p.values)
  59. value = 0.0f;
  60. }
  61. template <size_t N>
  62. void operator()(Transforms::UnresolvedPrimitive<N>& p)
  63. {
  64. for (auto& value : p.values)
  65. value.number = 0.0f;
  66. }
  67. void operator()(Transforms::Matrix2D& p)
  68. {
  69. for (int i = 0; i < 6; i++)
  70. p.values[i] = ((i == 0 || i == 3) ? 1.0f : 0.0f);
  71. }
  72. void operator()(Transforms::Matrix3D& p)
  73. {
  74. for (int i = 0; i < 16; i++)
  75. p.values[i] = ((i % 5) == 0 ? 1.0f : 0.0f);
  76. }
  77. void operator()(Transforms::ScaleX& p) { p.values[0] = 1; }
  78. void operator()(Transforms::ScaleY& p) { p.values[0] = 1; }
  79. void operator()(Transforms::ScaleZ& p) { p.values[0] = 1; }
  80. void operator()(Transforms::Scale2D& p) { p.values[0] = p.values[1] = 1; }
  81. void operator()(Transforms::Scale3D& p) { p.values[0] = p.values[1] = p.values[2] = 1; }
  82. void operator()(Transforms::DecomposedMatrix4& p)
  83. {
  84. p.perspective = Vector4f(0, 0, 0, 1);
  85. p.quaternion = Vector4f(0, 0, 0, 1);
  86. p.translation = Vector3f(0, 0, 0);
  87. p.scale = Vector3f(1, 1, 1);
  88. p.skew = Vector3f(0, 0, 0);
  89. }
  90. void run(TransformPrimitive& primitive)
  91. {
  92. switch (primitive.type)
  93. {
  94. case TransformPrimitive::MATRIX2D: this->operator()(primitive.matrix_2d); break;
  95. case TransformPrimitive::MATRIX3D: this->operator()(primitive.matrix_3d); break;
  96. case TransformPrimitive::TRANSLATEX: this->operator()(primitive.translate_x); break;
  97. case TransformPrimitive::TRANSLATEY: this->operator()(primitive.translate_y); break;
  98. case TransformPrimitive::TRANSLATEZ: this->operator()(primitive.translate_z); break;
  99. case TransformPrimitive::TRANSLATE2D: this->operator()(primitive.translate_2d); break;
  100. case TransformPrimitive::TRANSLATE3D: this->operator()(primitive.translate_3d); break;
  101. case TransformPrimitive::SCALEX: this->operator()(primitive.scale_x); break;
  102. case TransformPrimitive::SCALEY: this->operator()(primitive.scale_y); break;
  103. case TransformPrimitive::SCALEZ: this->operator()(primitive.scale_z); break;
  104. case TransformPrimitive::SCALE2D: this->operator()(primitive.scale_2d); break;
  105. case TransformPrimitive::SCALE3D: this->operator()(primitive.scale_3d); break;
  106. case TransformPrimitive::ROTATEX: this->operator()(primitive.rotate_x); break;
  107. case TransformPrimitive::ROTATEY: this->operator()(primitive.rotate_y); break;
  108. case TransformPrimitive::ROTATEZ: this->operator()(primitive.rotate_z); break;
  109. case TransformPrimitive::ROTATE2D: this->operator()(primitive.rotate_2d); break;
  110. case TransformPrimitive::ROTATE3D: this->operator()(primitive.rotate_3d); break;
  111. case TransformPrimitive::SKEWX: this->operator()(primitive.skew_x); break;
  112. case TransformPrimitive::SKEWY: this->operator()(primitive.skew_y); break;
  113. case TransformPrimitive::SKEW2D: this->operator()(primitive.skew_2d); break;
  114. case TransformPrimitive::PERSPECTIVE: this->operator()(primitive.perspective); break;
  115. case TransformPrimitive::DECOMPOSEDMATRIX4: this->operator()(primitive.decomposed_matrix_4); break;
  116. }
  117. }
  118. };
  119. void TransformUtilities::SetIdentity(TransformPrimitive& p) noexcept
  120. {
  121. SetIdentityVisitor{}.run(p);
  122. }
  123. struct ResolveTransformVisitor {
  124. Matrix4f& m;
  125. Element& e;
  126. void operator()(const Transforms::Matrix2D& p)
  127. {
  128. m = Matrix4f::FromRows(Vector4f(p.values[0], p.values[2], 0, p.values[4]), Vector4f(p.values[1], p.values[3], 0, p.values[5]),
  129. Vector4f(0, 0, 1, 0), Vector4f(0, 0, 0, 1));
  130. }
  131. void operator()(const Transforms::Matrix3D& p)
  132. {
  133. m = Matrix4f::FromColumns(Vector4f(p.values[0], p.values[1], p.values[2], p.values[3]),
  134. Vector4f(p.values[4], p.values[5], p.values[6], p.values[7]), Vector4f(p.values[8], p.values[9], p.values[10], p.values[11]),
  135. Vector4f(p.values[12], p.values[13], p.values[14], p.values[15]));
  136. }
  137. void operator()(const Transforms::TranslateX& p) { m = Matrix4f::TranslateX(ResolveWidth(p.values[0], e)); }
  138. void operator()(const Transforms::TranslateY& p) { m = Matrix4f::TranslateY(ResolveHeight(p.values[0], e)); }
  139. void operator()(const Transforms::TranslateZ& p) { m = Matrix4f::TranslateZ(ResolveLength(p.values[0], e)); }
  140. void operator()(const Transforms::Translate2D& p) { m = Matrix4f::Translate(ResolveWidth(p.values[0], e), ResolveHeight(p.values[1], e), 0); }
  141. void operator()(const Transforms::Translate3D& p)
  142. {
  143. m = Matrix4f::Translate(ResolveWidth(p.values[0], e), ResolveHeight(p.values[1], e), ResolveLength(p.values[2], e));
  144. }
  145. void operator()(const Transforms::ScaleX& p) { m = Matrix4f::ScaleX(p.values[0]); }
  146. void operator()(const Transforms::ScaleY& p) { m = Matrix4f::ScaleY(p.values[0]); }
  147. void operator()(const Transforms::ScaleZ& p) { m = Matrix4f::ScaleZ(p.values[0]); }
  148. void operator()(const Transforms::Scale2D& p) { m = Matrix4f::Scale(p.values[0], p.values[1], 1); }
  149. void operator()(const Transforms::Scale3D& p) { m = Matrix4f::Scale(p.values[0], p.values[1], p.values[2]); }
  150. void operator()(const Transforms::RotateX& p) { m = Matrix4f::RotateX(p.values[0]); }
  151. void operator()(const Transforms::RotateY& p) { m = Matrix4f::RotateY(p.values[0]); }
  152. void operator()(const Transforms::RotateZ& p) { m = Matrix4f::RotateZ(p.values[0]); }
  153. void operator()(const Transforms::Rotate2D& p) { m = Matrix4f::RotateZ(p.values[0]); }
  154. void operator()(const Transforms::Rotate3D& p) { m = Matrix4f::Rotate(Vector3f(p.values[0], p.values[1], p.values[2]), p.values[3]); }
  155. void operator()(const Transforms::SkewX& p) { m = Matrix4f::SkewX(p.values[0]); }
  156. void operator()(const Transforms::SkewY& p) { m = Matrix4f::SkewY(p.values[0]); }
  157. void operator()(const Transforms::Skew2D& p) { m = Matrix4f::Skew(p.values[0], p.values[1]); }
  158. void operator()(const Transforms::DecomposedMatrix4& p) { m = Matrix4f::Compose(p.translation, p.scale, p.skew, p.perspective, p.quaternion); }
  159. void operator()(const Transforms::Perspective& p) { m = Matrix4f::Perspective(ResolveLength(p.values[0], e)); }
  160. void run(const TransformPrimitive& primitive)
  161. {
  162. switch (primitive.type)
  163. {
  164. case TransformPrimitive::MATRIX2D: this->operator()(primitive.matrix_2d); break;
  165. case TransformPrimitive::MATRIX3D: this->operator()(primitive.matrix_3d); break;
  166. case TransformPrimitive::TRANSLATEX: this->operator()(primitive.translate_x); break;
  167. case TransformPrimitive::TRANSLATEY: this->operator()(primitive.translate_y); break;
  168. case TransformPrimitive::TRANSLATEZ: this->operator()(primitive.translate_z); break;
  169. case TransformPrimitive::TRANSLATE2D: this->operator()(primitive.translate_2d); break;
  170. case TransformPrimitive::TRANSLATE3D: this->operator()(primitive.translate_3d); break;
  171. case TransformPrimitive::SCALEX: this->operator()(primitive.scale_x); break;
  172. case TransformPrimitive::SCALEY: this->operator()(primitive.scale_y); break;
  173. case TransformPrimitive::SCALEZ: this->operator()(primitive.scale_z); break;
  174. case TransformPrimitive::SCALE2D: this->operator()(primitive.scale_2d); break;
  175. case TransformPrimitive::SCALE3D: this->operator()(primitive.scale_3d); break;
  176. case TransformPrimitive::ROTATEX: this->operator()(primitive.rotate_x); break;
  177. case TransformPrimitive::ROTATEY: this->operator()(primitive.rotate_y); break;
  178. case TransformPrimitive::ROTATEZ: this->operator()(primitive.rotate_z); break;
  179. case TransformPrimitive::ROTATE2D: this->operator()(primitive.rotate_2d); break;
  180. case TransformPrimitive::ROTATE3D: this->operator()(primitive.rotate_3d); break;
  181. case TransformPrimitive::SKEWX: this->operator()(primitive.skew_x); break;
  182. case TransformPrimitive::SKEWY: this->operator()(primitive.skew_y); break;
  183. case TransformPrimitive::SKEW2D: this->operator()(primitive.skew_2d); break;
  184. case TransformPrimitive::PERSPECTIVE: this->operator()(primitive.perspective); break;
  185. case TransformPrimitive::DECOMPOSEDMATRIX4: this->operator()(primitive.decomposed_matrix_4); break;
  186. }
  187. }
  188. };
  189. Matrix4f TransformUtilities::ResolveTransform(const TransformPrimitive& p, Element& e) noexcept
  190. {
  191. Matrix4f m;
  192. ResolveTransformVisitor visitor{m, e};
  193. visitor.run(p);
  194. return m;
  195. }
  196. struct PrepareVisitor {
  197. Element& e;
  198. bool operator()(TranslateX& p)
  199. {
  200. p.values[0] = NumericValue{ResolveWidth(p.values[0], e), Unit::PX};
  201. return true;
  202. }
  203. bool operator()(TranslateY& p)
  204. {
  205. p.values[0] = NumericValue{ResolveHeight(p.values[0], e), Unit::PX};
  206. return true;
  207. }
  208. bool operator()(TranslateZ& p)
  209. {
  210. p.values[0] = NumericValue{ResolveLength(p.values[0], e), Unit::PX};
  211. return true;
  212. }
  213. bool operator()(Translate2D& p)
  214. {
  215. p.values[0] = NumericValue{ResolveWidth(p.values[0], e), Unit::PX};
  216. p.values[1] = NumericValue{ResolveHeight(p.values[1], e), Unit::PX};
  217. return true;
  218. }
  219. bool operator()(Translate3D& p)
  220. {
  221. p.values[0] = NumericValue{ResolveWidth(p.values[0], e), Unit::PX};
  222. p.values[1] = NumericValue{ResolveHeight(p.values[1], e), Unit::PX};
  223. p.values[2] = NumericValue{ResolveLength(p.values[2], e), Unit::PX};
  224. return true;
  225. }
  226. template <size_t N>
  227. bool operator()(ResolvedPrimitive<N>& /*p*/)
  228. {
  229. // No conversion needed for resolved transforms (with some exceptions below)
  230. return true;
  231. }
  232. bool operator()(DecomposedMatrix4& /*p*/) { return true; }
  233. bool operator()(Rotate3D& p)
  234. {
  235. // Rotate3D can be interpolated if and only if their rotation axes point in the same direction.
  236. // We normalize the rotation vector here for easy comparison, and return true here. Later on we make the
  237. // pair-wise check in 'TryConvertToMatchingGenericType' to see if we need to decompose.
  238. Vector3f vec = Vector3f(p.values[0], p.values[1], p.values[2]).Normalise();
  239. p.values[0] = vec.x;
  240. p.values[1] = vec.y;
  241. p.values[2] = vec.z;
  242. return true;
  243. }
  244. bool operator()(Matrix3D& /*p*/)
  245. {
  246. // Matrices must be decomposed for interpolation
  247. return false;
  248. }
  249. bool operator()(Matrix2D& /*p*/)
  250. {
  251. // Matrix2D can also be optimized for interpolation, but for now we decompose it to a full DecomposedMatrix4
  252. return false;
  253. }
  254. bool operator()(Perspective& /*p*/)
  255. {
  256. // Perspective must be decomposed
  257. return false;
  258. }
  259. bool run(TransformPrimitive& primitive)
  260. {
  261. switch (primitive.type)
  262. {
  263. case TransformPrimitive::MATRIX2D: return this->operator()(primitive.matrix_2d);
  264. case TransformPrimitive::MATRIX3D: return this->operator()(primitive.matrix_3d);
  265. case TransformPrimitive::TRANSLATEX: return this->operator()(primitive.translate_x);
  266. case TransformPrimitive::TRANSLATEY: return this->operator()(primitive.translate_y);
  267. case TransformPrimitive::TRANSLATEZ: return this->operator()(primitive.translate_z);
  268. case TransformPrimitive::TRANSLATE2D: return this->operator()(primitive.translate_2d);
  269. case TransformPrimitive::TRANSLATE3D: return this->operator()(primitive.translate_3d);
  270. case TransformPrimitive::SCALEX: return this->operator()(primitive.scale_x);
  271. case TransformPrimitive::SCALEY: return this->operator()(primitive.scale_y);
  272. case TransformPrimitive::SCALEZ: return this->operator()(primitive.scale_z);
  273. case TransformPrimitive::SCALE2D: return this->operator()(primitive.scale_2d);
  274. case TransformPrimitive::SCALE3D: return this->operator()(primitive.scale_3d);
  275. case TransformPrimitive::ROTATEX: return this->operator()(primitive.rotate_x);
  276. case TransformPrimitive::ROTATEY: return this->operator()(primitive.rotate_y);
  277. case TransformPrimitive::ROTATEZ: return this->operator()(primitive.rotate_z);
  278. case TransformPrimitive::ROTATE2D: return this->operator()(primitive.rotate_2d);
  279. case TransformPrimitive::ROTATE3D: return this->operator()(primitive.rotate_3d);
  280. case TransformPrimitive::SKEWX: return this->operator()(primitive.skew_x);
  281. case TransformPrimitive::SKEWY: return this->operator()(primitive.skew_y);
  282. case TransformPrimitive::SKEW2D: return this->operator()(primitive.skew_2d);
  283. case TransformPrimitive::PERSPECTIVE: return this->operator()(primitive.perspective);
  284. case TransformPrimitive::DECOMPOSEDMATRIX4: return this->operator()(primitive.decomposed_matrix_4);
  285. }
  286. RMLUI_ASSERT(false);
  287. return false;
  288. }
  289. };
  290. bool TransformUtilities::PrepareForInterpolation(TransformPrimitive& p, Element& e) noexcept
  291. {
  292. return PrepareVisitor{e}.run(p);
  293. }
  294. enum class GenericType { None, Scale3D, Translate3D, Rotate3D };
  295. struct GetGenericTypeVisitor {
  296. GenericType run(const TransformPrimitive& primitive)
  297. {
  298. switch (primitive.type)
  299. {
  300. case TransformPrimitive::TRANSLATEX:
  301. case TransformPrimitive::TRANSLATEY:
  302. case TransformPrimitive::TRANSLATEZ:
  303. case TransformPrimitive::TRANSLATE2D:
  304. case TransformPrimitive::TRANSLATE3D: return GenericType::Translate3D;
  305. case TransformPrimitive::SCALEX:
  306. case TransformPrimitive::SCALEY:
  307. case TransformPrimitive::SCALEZ:
  308. case TransformPrimitive::SCALE2D:
  309. case TransformPrimitive::SCALE3D: return GenericType::Scale3D;
  310. case TransformPrimitive::ROTATEX:
  311. case TransformPrimitive::ROTATEY:
  312. case TransformPrimitive::ROTATEZ:
  313. case TransformPrimitive::ROTATE2D:
  314. case TransformPrimitive::ROTATE3D: return GenericType::Rotate3D;
  315. case TransformPrimitive::MATRIX2D:
  316. case TransformPrimitive::MATRIX3D:
  317. case TransformPrimitive::SKEWX:
  318. case TransformPrimitive::SKEWY:
  319. case TransformPrimitive::SKEW2D:
  320. case TransformPrimitive::PERSPECTIVE:
  321. case TransformPrimitive::DECOMPOSEDMATRIX4: return GenericType::None;
  322. }
  323. RMLUI_ASSERT(false);
  324. return GenericType::None;
  325. }
  326. };
  327. struct ConvertToGenericTypeVisitor {
  328. Translate3D operator()(const TranslateX& p) { return Translate3D{p.values[0], {0.0f, Unit::PX}, {0.0f, Unit::PX}}; }
  329. Translate3D operator()(const TranslateY& p) { return Translate3D{{0.0f, Unit::PX}, p.values[0], {0.0f, Unit::PX}}; }
  330. Translate3D operator()(const TranslateZ& p) { return Translate3D{{0.0f, Unit::PX}, {0.0f, Unit::PX}, p.values[0]}; }
  331. Translate3D operator()(const Translate2D& p) { return Translate3D{p.values[0], p.values[1], {0.0f, Unit::PX}}; }
  332. Scale3D operator()(const ScaleX& p) { return Scale3D{p.values[0], 1.0f, 1.0f}; }
  333. Scale3D operator()(const ScaleY& p) { return Scale3D{1.0f, p.values[0], 1.0f}; }
  334. Scale3D operator()(const ScaleZ& p) { return Scale3D{1.0f, 1.0f, p.values[0]}; }
  335. Scale3D operator()(const Scale2D& p) { return Scale3D{p.values[0], p.values[1], 1.0f}; }
  336. Rotate3D operator()(const RotateX& p) { return Rotate3D{1, 0, 0, p.values[0], Unit::RAD}; }
  337. Rotate3D operator()(const RotateY& p) { return Rotate3D{0, 1, 0, p.values[0], Unit::RAD}; }
  338. Rotate3D operator()(const RotateZ& p) { return Rotate3D{0, 0, 1, p.values[0], Unit::RAD}; }
  339. Rotate3D operator()(const Rotate2D& p) { return Rotate3D{0, 0, 1, p.values[0], Unit::RAD}; }
  340. template <typename T>
  341. TransformPrimitive operator()(const T& p)
  342. {
  343. RMLUI_ERROR;
  344. return p;
  345. }
  346. TransformPrimitive run(const TransformPrimitive& primitive)
  347. {
  348. TransformPrimitive result = primitive;
  349. // clang-format off
  350. switch (primitive.type)
  351. {
  352. case TransformPrimitive::TRANSLATEX: result.type = TransformPrimitive::TRANSLATE3D; result.translate_3d = this->operator()(primitive.translate_x); break;
  353. case TransformPrimitive::TRANSLATEY: result.type = TransformPrimitive::TRANSLATE3D; result.translate_3d = this->operator()(primitive.translate_y); break;
  354. case TransformPrimitive::TRANSLATEZ: result.type = TransformPrimitive::TRANSLATE3D; result.translate_3d = this->operator()(primitive.translate_z); break;
  355. case TransformPrimitive::TRANSLATE2D: result.type = TransformPrimitive::TRANSLATE3D; result.translate_3d = this->operator()(primitive.translate_2d); break;
  356. case TransformPrimitive::TRANSLATE3D: break;
  357. case TransformPrimitive::SCALEX: result.type = TransformPrimitive::SCALE3D; result.scale_3d = this->operator()(primitive.scale_x); break;
  358. case TransformPrimitive::SCALEY: result.type = TransformPrimitive::SCALE3D; result.scale_3d = this->operator()(primitive.scale_y); break;
  359. case TransformPrimitive::SCALEZ: result.type = TransformPrimitive::SCALE3D; result.scale_3d = this->operator()(primitive.scale_z); break;
  360. case TransformPrimitive::SCALE2D: result.type = TransformPrimitive::SCALE3D; result.scale_3d = this->operator()(primitive.scale_2d); break;
  361. case TransformPrimitive::SCALE3D: break;
  362. case TransformPrimitive::ROTATEX: result.type = TransformPrimitive::ROTATE3D; result.rotate_3d = this->operator()(primitive.rotate_x); break;
  363. case TransformPrimitive::ROTATEY: result.type = TransformPrimitive::ROTATE3D; result.rotate_3d = this->operator()(primitive.rotate_y); break;
  364. case TransformPrimitive::ROTATEZ: result.type = TransformPrimitive::ROTATE3D; result.rotate_3d = this->operator()(primitive.rotate_z); break;
  365. case TransformPrimitive::ROTATE2D: result.type = TransformPrimitive::ROTATE3D; result.rotate_3d = this->operator()(primitive.rotate_2d); break;
  366. case TransformPrimitive::ROTATE3D: break;
  367. default: RMLUI_ASSERT(false); break;
  368. }
  369. // clang-format on
  370. return result;
  371. }
  372. };
  373. static bool CanInterpolateRotate3D(const Rotate3D& p0, const Rotate3D& p1)
  374. {
  375. // Rotate3D can only be interpolated if and only if their rotation axes point in the same direction.
  376. // Assumes each rotation axis has already been normalized.
  377. auto& v0 = p0.values;
  378. auto& v1 = p1.values;
  379. return v0[0] == v1[0] && v0[1] == v1[1] && v0[2] == v1[2];
  380. }
  381. bool TransformUtilities::TryConvertToMatchingGenericType(TransformPrimitive& p0, TransformPrimitive& p1) noexcept
  382. {
  383. if (p0.type == p1.type)
  384. {
  385. if (p0.type == TransformPrimitive::ROTATE3D && !CanInterpolateRotate3D(p0.rotate_3d, p1.rotate_3d))
  386. return false;
  387. return true;
  388. }
  389. GenericType c0 = GetGenericTypeVisitor{}.run(p0);
  390. GenericType c1 = GetGenericTypeVisitor{}.run(p1);
  391. if (c0 == c1 && c0 != GenericType::None)
  392. {
  393. TransformPrimitive new_p0 = ConvertToGenericTypeVisitor{}.run(p0);
  394. TransformPrimitive new_p1 = ConvertToGenericTypeVisitor{}.run(p1);
  395. RMLUI_ASSERT(new_p0.type == new_p1.type);
  396. if (new_p0.type == TransformPrimitive::ROTATE3D && !CanInterpolateRotate3D(new_p0.rotate_3d, new_p1.rotate_3d))
  397. return false;
  398. p0 = new_p0;
  399. p1 = new_p1;
  400. return true;
  401. }
  402. return false;
  403. }
  404. struct InterpolateVisitor {
  405. const TransformPrimitive& other_variant;
  406. float alpha;
  407. template <size_t N>
  408. bool Interpolate(ResolvedPrimitive<N>& p0, const ResolvedPrimitive<N>& p1)
  409. {
  410. for (size_t i = 0; i < N; i++)
  411. p0.values[i] = p0.values[i] * (1.0f - alpha) + p1.values[i] * alpha;
  412. return true;
  413. }
  414. template <size_t N>
  415. bool Interpolate(UnresolvedPrimitive<N>& p0, const UnresolvedPrimitive<N>& p1)
  416. {
  417. // Assumes that the underlying units have been resolved (e.g. to pixels)
  418. for (size_t i = 0; i < N; i++)
  419. p0.values[i].number = p0.values[i].number * (1.0f - alpha) + p1.values[i].number * alpha;
  420. return true;
  421. }
  422. bool Interpolate(Rotate3D& p0, const Rotate3D& p1)
  423. {
  424. RMLUI_ASSERT(CanInterpolateRotate3D(p0, p1));
  425. // We can only interpolate rotate3d if their rotation axes align. That should be the case if we get here,
  426. // otherwise the generic type matching should decompose them. Thus, we only need to interpolate
  427. // the angle value here.
  428. p0.values[3] = p0.values[3] * (1.0f - alpha) + p1.values[3] * alpha;
  429. return true;
  430. }
  431. bool Interpolate(Matrix2D& /*p0*/, const Matrix2D& /*p1*/)
  432. {
  433. RMLUI_ERROR;
  434. return false; /* Error if we get here, see PrepareForInterpolation() */
  435. }
  436. bool Interpolate(Matrix3D& /*p0*/, const Matrix3D& /*p1*/)
  437. {
  438. RMLUI_ERROR;
  439. return false; /* Error if we get here, see PrepareForInterpolation() */
  440. }
  441. bool Interpolate(Perspective& /*p0*/, const Perspective& /*p1*/)
  442. {
  443. RMLUI_ERROR;
  444. return false; /* Error if we get here, see PrepareForInterpolation() */
  445. }
  446. bool Interpolate(DecomposedMatrix4& p0, const DecomposedMatrix4& p1)
  447. {
  448. p0.perspective = p0.perspective * (1.0f - alpha) + p1.perspective * alpha;
  449. p0.quaternion = QuaternionSlerp(p0.quaternion, p1.quaternion, alpha);
  450. p0.translation = p0.translation * (1.0f - alpha) + p1.translation * alpha;
  451. p0.scale = p0.scale * (1.0f - alpha) + p1.scale * alpha;
  452. p0.skew = p0.skew * (1.0f - alpha) + p1.skew * alpha;
  453. return true;
  454. }
  455. bool run(TransformPrimitive& variant)
  456. {
  457. RMLUI_ASSERT(variant.type == other_variant.type);
  458. switch (variant.type)
  459. {
  460. case TransformPrimitive::MATRIX2D: return Interpolate(variant.matrix_2d, other_variant.matrix_2d);
  461. case TransformPrimitive::MATRIX3D: return Interpolate(variant.matrix_3d, other_variant.matrix_3d);
  462. case TransformPrimitive::TRANSLATEX: return Interpolate(variant.translate_x, other_variant.translate_x);
  463. case TransformPrimitive::TRANSLATEY: return Interpolate(variant.translate_y, other_variant.translate_y);
  464. case TransformPrimitive::TRANSLATEZ: return Interpolate(variant.translate_z, other_variant.translate_z);
  465. case TransformPrimitive::TRANSLATE2D: return Interpolate(variant.translate_2d, other_variant.translate_2d);
  466. case TransformPrimitive::TRANSLATE3D: return Interpolate(variant.translate_3d, other_variant.translate_3d);
  467. case TransformPrimitive::SCALEX: return Interpolate(variant.scale_x, other_variant.scale_x);
  468. case TransformPrimitive::SCALEY: return Interpolate(variant.scale_y, other_variant.scale_y);
  469. case TransformPrimitive::SCALEZ: return Interpolate(variant.scale_z, other_variant.scale_z);
  470. case TransformPrimitive::SCALE2D: return Interpolate(variant.scale_2d, other_variant.scale_2d);
  471. case TransformPrimitive::SCALE3D: return Interpolate(variant.scale_3d, other_variant.scale_3d);
  472. case TransformPrimitive::ROTATEX: return Interpolate(variant.rotate_x, other_variant.rotate_x);
  473. case TransformPrimitive::ROTATEY: return Interpolate(variant.rotate_y, other_variant.rotate_y);
  474. case TransformPrimitive::ROTATEZ: return Interpolate(variant.rotate_z, other_variant.rotate_z);
  475. case TransformPrimitive::ROTATE2D: return Interpolate(variant.rotate_2d, other_variant.rotate_2d);
  476. case TransformPrimitive::ROTATE3D: return Interpolate(variant.rotate_3d, other_variant.rotate_3d);
  477. case TransformPrimitive::SKEWX: return Interpolate(variant.skew_x, other_variant.skew_x);
  478. case TransformPrimitive::SKEWY: return Interpolate(variant.skew_y, other_variant.skew_y);
  479. case TransformPrimitive::SKEW2D: return Interpolate(variant.skew_2d, other_variant.skew_2d);
  480. case TransformPrimitive::PERSPECTIVE: return Interpolate(variant.perspective, other_variant.perspective);
  481. case TransformPrimitive::DECOMPOSEDMATRIX4: return Interpolate(variant.decomposed_matrix_4, other_variant.decomposed_matrix_4);
  482. }
  483. RMLUI_ASSERT(false);
  484. return false;
  485. }
  486. };
  487. bool TransformUtilities::InterpolateWith(TransformPrimitive& target, const TransformPrimitive& other, float alpha) noexcept
  488. {
  489. if (target.type != other.type)
  490. return false;
  491. bool result = InterpolateVisitor{other, alpha}.run(target);
  492. return result;
  493. }
  494. template <size_t N>
  495. static String ToString(const Transforms::ResolvedPrimitive<N>& p, const String& unit, bool rad_to_deg = false,
  496. bool only_unit_on_last_value = false) noexcept
  497. {
  498. float multiplier = 1.0f;
  499. String tmp;
  500. String result = "(";
  501. for (size_t i = 0; i < N; i++)
  502. {
  503. if (only_unit_on_last_value && i < N - 1)
  504. multiplier = 1.0f;
  505. else if (rad_to_deg)
  506. multiplier = 180.f / Math::RMLUI_PI;
  507. if (TypeConverter<float, String>::Convert(p.values[i] * multiplier, tmp))
  508. result += tmp;
  509. if (!unit.empty() && (!only_unit_on_last_value || (i == N - 1)))
  510. result += unit;
  511. if (i < N - 1)
  512. result += ", ";
  513. }
  514. result += ")";
  515. return result;
  516. }
  517. static inline String ToString(NumericValue value) noexcept
  518. {
  519. return ToString(value.number) + ToString(value.unit);
  520. }
  521. template <size_t N>
  522. static inline String ToString(const Transforms::UnresolvedPrimitive<N>& p) noexcept
  523. {
  524. String result = "(";
  525. for (size_t i = 0; i < N; i++)
  526. {
  527. result += ToString(p.values[i]);
  528. if (i != N - 1)
  529. result += ", ";
  530. }
  531. result += ")";
  532. return result;
  533. }
  534. static inline String ToString(const Transforms::DecomposedMatrix4& p) noexcept
  535. {
  536. static const Transforms::DecomposedMatrix4 d{Vector4f(0, 0, 0, 1), Vector4f(0, 0, 0, 1), Vector3f(0, 0, 0), Vector3f(1, 1, 1), Vector3f(0, 0, 0)};
  537. String tmp;
  538. String result;
  539. if (p.perspective != d.perspective && TypeConverter<Vector4f, String>::Convert(p.perspective, tmp))
  540. result += "perspective(" + tmp + "), ";
  541. if (p.quaternion != d.quaternion && TypeConverter<Vector4f, String>::Convert(p.quaternion, tmp))
  542. result += "quaternion(" + tmp + "), ";
  543. if (p.translation != d.translation && TypeConverter<Vector3f, String>::Convert(p.translation, tmp))
  544. result += "translation(" + tmp + "), ";
  545. if (p.scale != d.scale && TypeConverter<Vector3f, String>::Convert(p.scale, tmp))
  546. result += "scale(" + tmp + "), ";
  547. if (p.skew != d.skew && TypeConverter<Vector3f, String>::Convert(p.skew, tmp))
  548. result += "skew(" + tmp + "), ";
  549. if (result.size() > 2)
  550. result.resize(result.size() - 2);
  551. result = "decomposedMatrix3d{ " + result + " }";
  552. return result;
  553. }
  554. // clang-format off
  555. static inline String ToString(const Transforms::Matrix2D& p) noexcept { return "matrix" + ToString(static_cast<const Transforms::ResolvedPrimitive< 6 >&>(p), ""); }
  556. static inline String ToString(const Transforms::Matrix3D& p) noexcept { return "matrix3d" + ToString(static_cast<const Transforms::ResolvedPrimitive< 16 >&>(p), ""); }
  557. static inline String ToString(const Transforms::TranslateX& p) noexcept { return "translateX" + ToString(static_cast<const Transforms::UnresolvedPrimitive< 1 >&>(p)); }
  558. static inline String ToString(const Transforms::TranslateY& p) noexcept { return "translateY" + ToString(static_cast<const Transforms::UnresolvedPrimitive< 1 >&>(p)); }
  559. static inline String ToString(const Transforms::TranslateZ& p) noexcept { return "translateZ" + ToString(static_cast<const Transforms::UnresolvedPrimitive< 1 >&>(p)); }
  560. static inline String ToString(const Transforms::Translate2D& p) noexcept { return "translate" + ToString(static_cast<const Transforms::UnresolvedPrimitive< 2 >&>(p)); }
  561. static inline String ToString(const Transforms::Translate3D& p) noexcept { return "translate3d" + ToString(static_cast<const Transforms::UnresolvedPrimitive< 3 >&>(p)); }
  562. static inline String ToString(const Transforms::ScaleX& p) noexcept { return "scaleX" + ToString(static_cast<const Transforms::ResolvedPrimitive< 1 >&>(p), ""); }
  563. static inline String ToString(const Transforms::ScaleY& p) noexcept { return "scaleY" + ToString(static_cast<const Transforms::ResolvedPrimitive< 1 >&>(p), ""); }
  564. static inline String ToString(const Transforms::ScaleZ& p) noexcept { return "scaleZ" + ToString(static_cast<const Transforms::ResolvedPrimitive< 1 >&>(p), ""); }
  565. static inline String ToString(const Transforms::Scale2D& p) noexcept { return "scale" + ToString(static_cast<const Transforms::ResolvedPrimitive< 2 >&>(p), ""); }
  566. static inline String ToString(const Transforms::Scale3D& p) noexcept { return "scale3d" + ToString(static_cast<const Transforms::ResolvedPrimitive< 3 >&>(p), ""); }
  567. static inline String ToString(const Transforms::RotateX& p) noexcept { return "rotateX" + ToString(static_cast<const Transforms::ResolvedPrimitive< 1 >&>(p), "deg", true); }
  568. static inline String ToString(const Transforms::RotateY& p) noexcept { return "rotateY" + ToString(static_cast<const Transforms::ResolvedPrimitive< 1 >&>(p), "deg", true); }
  569. static inline String ToString(const Transforms::RotateZ& p) noexcept { return "rotateZ" + ToString(static_cast<const Transforms::ResolvedPrimitive< 1 >&>(p), "deg", true); }
  570. static inline String ToString(const Transforms::Rotate2D& p) noexcept { return "rotate" + ToString(static_cast<const Transforms::ResolvedPrimitive< 1 >&>(p), "deg", true); }
  571. static inline String ToString(const Transforms::Rotate3D& p) noexcept { return "rotate3d" + ToString(static_cast<const Transforms::ResolvedPrimitive< 4 >&>(p), "deg", true, true); }
  572. static inline String ToString(const Transforms::SkewX& p) noexcept { return "skewX" + ToString(static_cast<const Transforms::ResolvedPrimitive< 1 >&>(p), "deg", true); }
  573. static inline String ToString(const Transforms::SkewY& p) noexcept { return "skewY" + ToString(static_cast<const Transforms::ResolvedPrimitive< 1 >&>(p), "deg", true); }
  574. static inline String ToString(const Transforms::Skew2D& p) noexcept { return "skew" + ToString(static_cast<const Transforms::ResolvedPrimitive< 2 >&>(p), "deg", true); }
  575. static inline String ToString(const Transforms::Perspective& p) noexcept { return "perspective" + ToString(static_cast<const Transforms::UnresolvedPrimitive< 1 >&>(p)); }
  576. // clang-format on
  577. struct ToStringVisitor {
  578. String run(const TransformPrimitive& variant)
  579. {
  580. switch (variant.type)
  581. {
  582. case TransformPrimitive::MATRIX2D: return ToString(variant.matrix_2d);
  583. case TransformPrimitive::MATRIX3D: return ToString(variant.matrix_3d);
  584. case TransformPrimitive::TRANSLATEX: return ToString(variant.translate_x);
  585. case TransformPrimitive::TRANSLATEY: return ToString(variant.translate_y);
  586. case TransformPrimitive::TRANSLATEZ: return ToString(variant.translate_z);
  587. case TransformPrimitive::TRANSLATE2D: return ToString(variant.translate_2d);
  588. case TransformPrimitive::TRANSLATE3D: return ToString(variant.translate_3d);
  589. case TransformPrimitive::SCALEX: return ToString(variant.scale_x);
  590. case TransformPrimitive::SCALEY: return ToString(variant.scale_y);
  591. case TransformPrimitive::SCALEZ: return ToString(variant.scale_z);
  592. case TransformPrimitive::SCALE2D: return ToString(variant.scale_2d);
  593. case TransformPrimitive::SCALE3D: return ToString(variant.scale_3d);
  594. case TransformPrimitive::ROTATEX: return ToString(variant.rotate_x);
  595. case TransformPrimitive::ROTATEY: return ToString(variant.rotate_y);
  596. case TransformPrimitive::ROTATEZ: return ToString(variant.rotate_z);
  597. case TransformPrimitive::ROTATE2D: return ToString(variant.rotate_2d);
  598. case TransformPrimitive::ROTATE3D: return ToString(variant.rotate_3d);
  599. case TransformPrimitive::SKEWX: return ToString(variant.skew_x);
  600. case TransformPrimitive::SKEWY: return ToString(variant.skew_y);
  601. case TransformPrimitive::SKEW2D: return ToString(variant.skew_2d);
  602. case TransformPrimitive::PERSPECTIVE: return ToString(variant.perspective);
  603. case TransformPrimitive::DECOMPOSEDMATRIX4: return ToString(variant.decomposed_matrix_4);
  604. }
  605. RMLUI_ASSERT(false);
  606. return String();
  607. }
  608. };
  609. String TransformUtilities::ToString(const TransformPrimitive& p) noexcept
  610. {
  611. String result = ToStringVisitor{}.run(p);
  612. return result;
  613. }
  614. bool TransformUtilities::Decompose(Transforms::DecomposedMatrix4& d, const Matrix4f& m) noexcept
  615. {
  616. // Follows the procedure given in https://drafts.csswg.org/css-transforms-2/#interpolation-of-3d-matrices
  617. const float eps = 0.0005f;
  618. if (Math::Absolute(m[3][3]) < eps)
  619. return false;
  620. // Perspective matrix
  621. Matrix4f p = m;
  622. for (int i = 0; i < 3; i++)
  623. p[i][3] = 0;
  624. p[3][3] = 1;
  625. if (Math::Absolute(p.Determinant()) < eps)
  626. return false;
  627. if (m[0][3] != 0 || m[1][3] != 0 || m[2][3] != 0)
  628. {
  629. auto rhs = m.GetColumn(3);
  630. Matrix4f p_inv = p;
  631. if (!p_inv.Invert())
  632. return false;
  633. auto& p_inv_trans = p.Transpose();
  634. d.perspective = p_inv_trans * rhs;
  635. }
  636. else
  637. {
  638. d.perspective[0] = d.perspective[1] = d.perspective[2] = 0;
  639. d.perspective[3] = 1;
  640. }
  641. for (int i = 0; i < 3; i++)
  642. d.translation[i] = m[3][i];
  643. Vector3f row[3];
  644. for (int i = 0; i < 3; i++)
  645. {
  646. row[i][0] = m[i][0];
  647. row[i][1] = m[i][1];
  648. row[i][2] = m[i][2];
  649. }
  650. d.scale[0] = row[0].Magnitude();
  651. row[0] = row[0].Normalise();
  652. d.skew[0] = row[0].DotProduct(row[1]);
  653. row[1] = Combine(row[1], row[0], 1, -d.skew[0]);
  654. d.scale[1] = row[1].Magnitude();
  655. row[1] = row[1].Normalise();
  656. d.skew[0] /= d.scale[1];
  657. d.skew[1] = row[0].DotProduct(row[2]);
  658. row[2] = Combine(row[2], row[0], 1, -d.skew[1]);
  659. d.skew[2] = row[1].DotProduct(row[2]);
  660. row[2] = Combine(row[2], row[1], 1, -d.skew[2]);
  661. d.scale[2] = row[2].Magnitude();
  662. row[2] = row[2].Normalise();
  663. d.skew[2] /= d.scale[2];
  664. d.skew[1] /= d.scale[2];
  665. // Check if we need to flip coordinate system
  666. auto pdum3 = row[1].CrossProduct(row[2]);
  667. if (row[0].DotProduct(pdum3) < 0.0f)
  668. {
  669. for (int i = 0; i < 3; i++)
  670. {
  671. d.scale[i] *= -1.f;
  672. row[i] *= -1.f;
  673. }
  674. }
  675. d.quaternion[0] = 0.5f * Math::SquareRoot(Math::Max(1.f + row[0][0] - row[1][1] - row[2][2], 0.0f));
  676. d.quaternion[1] = 0.5f * Math::SquareRoot(Math::Max(1.f - row[0][0] + row[1][1] - row[2][2], 0.0f));
  677. d.quaternion[2] = 0.5f * Math::SquareRoot(Math::Max(1.f - row[0][0] - row[1][1] + row[2][2], 0.0f));
  678. d.quaternion[3] = 0.5f * Math::SquareRoot(Math::Max(1.f + row[0][0] + row[1][1] + row[2][2], 0.0f));
  679. if (row[2][1] > row[1][2])
  680. d.quaternion[0] *= -1.f;
  681. if (row[0][2] > row[2][0])
  682. d.quaternion[1] *= -1.f;
  683. if (row[1][0] > row[0][1])
  684. d.quaternion[2] *= -1.f;
  685. return true;
  686. }
  687. } // namespace Rml