Transform2D.cs 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. using System;
  2. using System.Runtime.InteropServices;
  3. #if REAL_T_IS_DOUBLE
  4. using real_t = System.Double;
  5. #else
  6. using real_t = System.Single;
  7. #endif
  8. namespace Godot
  9. {
  10. [StructLayout(LayoutKind.Sequential)]
  11. public struct Transform2D : IEquatable<Transform2D>
  12. {
  13. public Vector2 x;
  14. public Vector2 y;
  15. public Vector2 origin;
  16. public real_t Rotation
  17. {
  18. get
  19. {
  20. real_t det = BasisDeterminant();
  21. Transform2D t = Orthonormalized();
  22. if (det < 0)
  23. {
  24. t.ScaleBasis(new Vector2(1, -1));
  25. }
  26. return Mathf.Atan2(t.x.y, t.x.x);
  27. }
  28. set
  29. {
  30. Vector2 scale = Scale;
  31. x.x = y.y = Mathf.Cos(value);
  32. x.y = y.x = Mathf.Sin(value);
  33. y.x *= -1;
  34. Scale = scale;
  35. }
  36. }
  37. public Vector2 Scale
  38. {
  39. get
  40. {
  41. real_t detSign = Mathf.Sign(BasisDeterminant());
  42. return new Vector2(x.Length(), detSign * y.Length());
  43. }
  44. set
  45. {
  46. x = x.Normalized();
  47. y = y.Normalized();
  48. x *= value.x;
  49. y *= value.y;
  50. }
  51. }
  52. public Vector2 this[int rowIndex]
  53. {
  54. get
  55. {
  56. switch (rowIndex)
  57. {
  58. case 0:
  59. return x;
  60. case 1:
  61. return y;
  62. case 2:
  63. return origin;
  64. default:
  65. throw new IndexOutOfRangeException();
  66. }
  67. }
  68. set
  69. {
  70. switch (rowIndex)
  71. {
  72. case 0:
  73. x = value;
  74. return;
  75. case 1:
  76. y = value;
  77. return;
  78. case 2:
  79. origin = value;
  80. return;
  81. default:
  82. throw new IndexOutOfRangeException();
  83. }
  84. }
  85. }
  86. public real_t this[int rowIndex, int columnIndex]
  87. {
  88. get
  89. {
  90. switch (rowIndex)
  91. {
  92. case 0:
  93. return x[columnIndex];
  94. case 1:
  95. return y[columnIndex];
  96. default:
  97. throw new IndexOutOfRangeException();
  98. }
  99. }
  100. set
  101. {
  102. switch (rowIndex)
  103. {
  104. case 0:
  105. x[columnIndex] = value;
  106. return;
  107. case 1:
  108. y[columnIndex] = value;
  109. return;
  110. default:
  111. throw new IndexOutOfRangeException();
  112. }
  113. }
  114. }
  115. public Transform2D AffineInverse()
  116. {
  117. real_t det = BasisDeterminant();
  118. if (det == 0)
  119. throw new InvalidOperationException("Matrix determinant is zero and cannot be inverted.");
  120. var inv = this;
  121. real_t temp = inv[0, 0];
  122. inv[0, 0] = inv[1, 1];
  123. inv[1, 1] = temp;
  124. real_t detInv = 1.0f / det;
  125. inv[0] *= new Vector2(detInv, -detInv);
  126. inv[1] *= new Vector2(-detInv, detInv);
  127. inv[2] = BasisXform(-inv[2]);
  128. return inv;
  129. }
  130. private real_t BasisDeterminant()
  131. {
  132. return x.x * y.y - x.y * y.x;
  133. }
  134. public Vector2 BasisXform(Vector2 v)
  135. {
  136. return new Vector2(Tdotx(v), Tdoty(v));
  137. }
  138. public Vector2 BasisXformInv(Vector2 v)
  139. {
  140. return new Vector2(x.Dot(v), y.Dot(v));
  141. }
  142. public Transform2D InterpolateWith(Transform2D m, real_t c)
  143. {
  144. real_t r1 = Rotation;
  145. real_t r2 = m.Rotation;
  146. Vector2 s1 = Scale;
  147. Vector2 s2 = m.Scale;
  148. // Slerp rotation
  149. var v1 = new Vector2(Mathf.Cos(r1), Mathf.Sin(r1));
  150. var v2 = new Vector2(Mathf.Cos(r2), Mathf.Sin(r2));
  151. real_t dot = v1.Dot(v2);
  152. // Clamp dot to [-1, 1]
  153. dot = dot < -1.0f ? -1.0f : (dot > 1.0f ? 1.0f : dot);
  154. Vector2 v;
  155. if (dot > 0.9995f)
  156. {
  157. // Linearly interpolate to avoid numerical precision issues
  158. v = v1.LinearInterpolate(v2, c).Normalized();
  159. }
  160. else
  161. {
  162. real_t angle = c * Mathf.Acos(dot);
  163. Vector2 v3 = (v2 - v1 * dot).Normalized();
  164. v = v1 * Mathf.Cos(angle) + v3 * Mathf.Sin(angle);
  165. }
  166. // Extract parameters
  167. Vector2 p1 = origin;
  168. Vector2 p2 = m.origin;
  169. // Construct matrix
  170. var res = new Transform2D(Mathf.Atan2(v.y, v.x), p1.LinearInterpolate(p2, c));
  171. Vector2 scale = s1.LinearInterpolate(s2, c);
  172. res.x *= scale;
  173. res.y *= scale;
  174. return res;
  175. }
  176. public Transform2D Inverse()
  177. {
  178. var inv = this;
  179. // Swap
  180. real_t temp = inv.x.y;
  181. inv.x.y = inv.y.x;
  182. inv.y.x = temp;
  183. inv.origin = inv.BasisXform(-inv.origin);
  184. return inv;
  185. }
  186. public Transform2D Orthonormalized()
  187. {
  188. var on = this;
  189. Vector2 onX = on.x;
  190. Vector2 onY = on.y;
  191. onX.Normalize();
  192. onY = onY - onX * onX.Dot(onY);
  193. onY.Normalize();
  194. on.x = onX;
  195. on.y = onY;
  196. return on;
  197. }
  198. public Transform2D Rotated(real_t phi)
  199. {
  200. return this * new Transform2D(phi, new Vector2());
  201. }
  202. public Transform2D Scaled(Vector2 scale)
  203. {
  204. var copy = this;
  205. copy.x *= scale;
  206. copy.y *= scale;
  207. copy.origin *= scale;
  208. return copy;
  209. }
  210. private void ScaleBasis(Vector2 scale)
  211. {
  212. x.x *= scale.x;
  213. x.y *= scale.y;
  214. y.x *= scale.x;
  215. y.y *= scale.y;
  216. }
  217. private real_t Tdotx(Vector2 with)
  218. {
  219. return this[0, 0] * with[0] + this[1, 0] * with[1];
  220. }
  221. private real_t Tdoty(Vector2 with)
  222. {
  223. return this[0, 1] * with[0] + this[1, 1] * with[1];
  224. }
  225. public Transform2D Translated(Vector2 offset)
  226. {
  227. var copy = this;
  228. copy.origin += copy.BasisXform(offset);
  229. return copy;
  230. }
  231. public Vector2 Xform(Vector2 v)
  232. {
  233. return new Vector2(Tdotx(v), Tdoty(v)) + origin;
  234. }
  235. public Vector2 XformInv(Vector2 v)
  236. {
  237. Vector2 vInv = v - origin;
  238. return new Vector2(x.Dot(vInv), y.Dot(vInv));
  239. }
  240. // Constants
  241. private static readonly Transform2D _identity = new Transform2D(1, 0, 0, 1, 0, 0);
  242. private static readonly Transform2D _flipX = new Transform2D(-1, 0, 0, 1, 0, 0);
  243. private static readonly Transform2D _flipY = new Transform2D(1, 0, 0, -1, 0, 0);
  244. public static Transform2D Identity => _identity;
  245. public static Transform2D FlipX => _flipX;
  246. public static Transform2D FlipY => _flipY;
  247. // Constructors
  248. public Transform2D(Vector2 xAxis, Vector2 yAxis, Vector2 originPos)
  249. {
  250. x = xAxis;
  251. y = yAxis;
  252. origin = originPos;
  253. }
  254. public Transform2D(real_t xx, real_t xy, real_t yx, real_t yy, real_t ox, real_t oy)
  255. {
  256. x = new Vector2(xx, xy);
  257. y = new Vector2(yx, yy);
  258. origin = new Vector2(ox, oy);
  259. }
  260. public Transform2D(real_t rot, Vector2 pos)
  261. {
  262. x.x = y.y = Mathf.Cos(rot);
  263. x.y = y.x = Mathf.Sin(rot);
  264. y.x *= -1;
  265. origin = pos;
  266. }
  267. public static Transform2D operator *(Transform2D left, Transform2D right)
  268. {
  269. left.origin = left.Xform(right.origin);
  270. real_t x0 = left.Tdotx(right.x);
  271. real_t x1 = left.Tdoty(right.x);
  272. real_t y0 = left.Tdotx(right.y);
  273. real_t y1 = left.Tdoty(right.y);
  274. left.x.x = x0;
  275. left.x.y = x1;
  276. left.y.x = y0;
  277. left.y.y = y1;
  278. return left;
  279. }
  280. public static bool operator ==(Transform2D left, Transform2D right)
  281. {
  282. return left.Equals(right);
  283. }
  284. public static bool operator !=(Transform2D left, Transform2D right)
  285. {
  286. return !left.Equals(right);
  287. }
  288. public override bool Equals(object obj)
  289. {
  290. return obj is Transform2D transform2D && Equals(transform2D);
  291. }
  292. public bool Equals(Transform2D other)
  293. {
  294. return x.Equals(other.x) && y.Equals(other.y) && origin.Equals(other.origin);
  295. }
  296. public override int GetHashCode()
  297. {
  298. return x.GetHashCode() ^ y.GetHashCode() ^ origin.GetHashCode();
  299. }
  300. public override string ToString()
  301. {
  302. return String.Format("({0}, {1}, {2})", new object[]
  303. {
  304. x.ToString(),
  305. y.ToString(),
  306. origin.ToString()
  307. });
  308. }
  309. public string ToString(string format)
  310. {
  311. return String.Format("({0}, {1}, {2})", new object[]
  312. {
  313. x.ToString(format),
  314. y.ToString(format),
  315. origin.ToString(format)
  316. });
  317. }
  318. }
  319. }