2018-03-01 08:51:35 +01:00
|
|
|
|
#if REAL_T_IS_DOUBLE
|
|
|
|
|
using real_t = System.Double;
|
|
|
|
|
#else
|
|
|
|
|
using real_t = System.Single;
|
|
|
|
|
#endif
|
2021-07-23 01:56:37 +02:00
|
|
|
|
using System;
|
|
|
|
|
using System.Runtime.InteropServices;
|
2018-03-01 08:51:35 +01:00
|
|
|
|
|
2017-10-02 23:24:00 +02:00
|
|
|
|
namespace Godot
|
|
|
|
|
{
|
2020-07-07 23:29:35 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 2×3 matrix (2 rows, 3 columns) used for 2D linear transformations.
|
|
|
|
|
/// It can represent transformations such as translation, rotation, or scaling.
|
|
|
|
|
/// It consists of a three <see cref="Vector2"/> values: x, y, and the origin.
|
|
|
|
|
///
|
|
|
|
|
/// For more information, read this documentation article:
|
|
|
|
|
/// https://docs.godotengine.org/en/latest/tutorials/math/matrices_and_transforms.html
|
|
|
|
|
/// </summary>
|
2019-08-08 03:29:40 +02:00
|
|
|
|
[Serializable]
|
2017-10-02 23:24:00 +02:00
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
|
|
|
public struct Transform2D : IEquatable<Transform2D>
|
|
|
|
|
{
|
2020-07-07 23:29:35 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// The basis matrix's X vector (column 0). Equivalent to array index `[0]`.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <value></value>
|
2017-10-02 23:24:00 +02:00
|
|
|
|
public Vector2 x;
|
2020-07-07 23:29:35 +02:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The basis matrix's Y vector (column 1). Equivalent to array index `[1]`.
|
|
|
|
|
/// </summary>
|
2017-10-02 23:24:00 +02:00
|
|
|
|
public Vector2 y;
|
2020-07-07 23:29:35 +02:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The origin vector (column 2, the third column). Equivalent to array index `[2]`.
|
|
|
|
|
/// The origin vector represents translation.
|
|
|
|
|
/// </summary>
|
2019-02-09 20:56:09 +01:00
|
|
|
|
public Vector2 origin;
|
2017-10-02 23:24:00 +02:00
|
|
|
|
|
2020-07-07 23:29:35 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// The rotation of this transformation matrix.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <value>Getting is equivalent to calling <see cref="Mathf.Atan2(real_t, real_t)"/> with the values of <see cref="x"/>.</value>
|
2018-03-01 08:51:35 +01:00
|
|
|
|
public real_t Rotation
|
2017-10-02 23:24:00 +02:00
|
|
|
|
{
|
2019-02-12 18:39:10 +01:00
|
|
|
|
get
|
|
|
|
|
{
|
2020-07-07 23:29:35 +02:00
|
|
|
|
return Mathf.Atan2(x.y, x.x);
|
2019-02-12 18:39:10 +01:00
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
Vector2 scale = Scale;
|
|
|
|
|
x.x = y.y = Mathf.Cos(value);
|
|
|
|
|
x.y = y.x = Mathf.Sin(value);
|
|
|
|
|
y.x *= -1;
|
|
|
|
|
Scale = scale;
|
|
|
|
|
}
|
2017-10-02 23:24:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-07-07 23:29:35 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// The scale of this transformation matrix.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <value>Equivalent to the lengths of each column vector, but Y is negative if the determinant is negative.</value>
|
2017-10-02 23:24:00 +02:00
|
|
|
|
public Vector2 Scale
|
|
|
|
|
{
|
2019-02-12 18:39:10 +01:00
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
real_t detSign = Mathf.Sign(BasisDeterminant());
|
|
|
|
|
return new Vector2(x.Length(), detSign * y.Length());
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
2020-07-07 23:29:35 +02:00
|
|
|
|
value /= Scale; // Value becomes what's called "delta_scale" in core.
|
2019-02-12 18:39:10 +01:00
|
|
|
|
x *= value.x;
|
|
|
|
|
y *= value.y;
|
|
|
|
|
}
|
2017-10-02 23:24:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-11-29 08:20:31 +01:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Access whole columns in the form of Vector2. The third column is the origin vector.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="column">Which column vector.</param>
|
|
|
|
|
public Vector2 this[int column]
|
2017-10-02 23:24:00 +02:00
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
2019-11-29 08:20:31 +01:00
|
|
|
|
switch (column)
|
2017-10-02 23:24:00 +02:00
|
|
|
|
{
|
|
|
|
|
case 0:
|
|
|
|
|
return x;
|
|
|
|
|
case 1:
|
|
|
|
|
return y;
|
|
|
|
|
case 2:
|
2019-02-09 20:56:09 +01:00
|
|
|
|
return origin;
|
2017-10-02 23:24:00 +02:00
|
|
|
|
default:
|
|
|
|
|
throw new IndexOutOfRangeException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
2019-11-29 08:20:31 +01:00
|
|
|
|
switch (column)
|
2017-10-02 23:24:00 +02:00
|
|
|
|
{
|
|
|
|
|
case 0:
|
|
|
|
|
x = value;
|
|
|
|
|
return;
|
|
|
|
|
case 1:
|
|
|
|
|
y = value;
|
|
|
|
|
return;
|
|
|
|
|
case 2:
|
2019-02-09 20:56:09 +01:00
|
|
|
|
origin = value;
|
2017-10-02 23:24:00 +02:00
|
|
|
|
return;
|
|
|
|
|
default:
|
|
|
|
|
throw new IndexOutOfRangeException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-29 08:20:31 +01:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Access matrix elements in column-major order. The third column is the origin vector.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="column">Which column, the matrix horizontal position.</param>
|
|
|
|
|
/// <param name="row">Which row, the matrix vertical position.</param>
|
|
|
|
|
public real_t this[int column, int row]
|
2017-10-02 23:24:00 +02:00
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
2019-11-29 08:20:31 +01:00
|
|
|
|
return this[column][row];
|
2017-10-02 23:24:00 +02:00
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
2019-11-29 08:20:31 +01:00
|
|
|
|
Vector2 columnVector = this[column];
|
|
|
|
|
columnVector[row] = value;
|
|
|
|
|
this[column] = columnVector;
|
2017-10-02 23:24:00 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-07 23:29:35 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns the inverse of the transform, under the assumption that
|
|
|
|
|
/// the transformation is composed of rotation, scaling, and translation.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>The inverse transformation matrix.</returns>
|
2017-11-21 23:32:19 +01:00
|
|
|
|
public Transform2D AffineInverse()
|
2017-10-02 23:24:00 +02:00
|
|
|
|
{
|
2019-02-12 18:39:10 +01:00
|
|
|
|
real_t det = BasisDeterminant();
|
2017-10-02 23:24:00 +02:00
|
|
|
|
|
|
|
|
|
if (det == 0)
|
2019-02-23 18:22:27 +01:00
|
|
|
|
throw new InvalidOperationException("Matrix determinant is zero and cannot be inverted.");
|
2017-10-02 23:24:00 +02:00
|
|
|
|
|
2019-02-23 18:22:27 +01:00
|
|
|
|
var inv = this;
|
2017-10-02 23:24:00 +02:00
|
|
|
|
|
2019-02-23 18:22:27 +01:00
|
|
|
|
real_t temp = inv[0, 0];
|
|
|
|
|
inv[0, 0] = inv[1, 1];
|
|
|
|
|
inv[1, 1] = temp;
|
2017-10-02 23:24:00 +02:00
|
|
|
|
|
2019-02-23 18:22:27 +01:00
|
|
|
|
real_t detInv = 1.0f / det;
|
|
|
|
|
|
|
|
|
|
inv[0] *= new Vector2(detInv, -detInv);
|
|
|
|
|
inv[1] *= new Vector2(-detInv, detInv);
|
2017-10-02 23:24:00 +02:00
|
|
|
|
|
2019-07-27 02:15:29 +02:00
|
|
|
|
inv[2] = inv.BasisXform(-inv[2]);
|
2017-10-02 23:24:00 +02:00
|
|
|
|
|
|
|
|
|
return inv;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-07 23:29:35 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns the determinant of the basis matrix. If the basis is
|
|
|
|
|
/// uniformly scaled, its determinant is the square of the scale.
|
|
|
|
|
///
|
|
|
|
|
/// A negative determinant means the Y scale is negative.
|
|
|
|
|
/// A zero determinant means the basis isn't invertible,
|
|
|
|
|
/// and is usually considered invalid.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>The determinant of the basis matrix.</returns>
|
2019-02-12 18:39:10 +01:00
|
|
|
|
private real_t BasisDeterminant()
|
|
|
|
|
{
|
|
|
|
|
return x.x * y.y - x.y * y.x;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-07 23:29:35 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns a vector transformed (multiplied) by the basis matrix.
|
|
|
|
|
/// This method does not account for translation (the origin vector).
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="v">A vector to transform.</param>
|
2020-07-22 22:46:04 +02:00
|
|
|
|
/// <returns>The transformed vector.</returns>
|
2017-11-21 23:32:19 +01:00
|
|
|
|
public Vector2 BasisXform(Vector2 v)
|
2017-10-02 23:24:00 +02:00
|
|
|
|
{
|
2017-11-21 23:32:19 +01:00
|
|
|
|
return new Vector2(Tdotx(v), Tdoty(v));
|
2017-10-02 23:24:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-07-07 23:29:35 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns a vector transformed (multiplied) by the inverse basis matrix.
|
|
|
|
|
/// This method does not account for translation (the origin vector).
|
|
|
|
|
///
|
|
|
|
|
/// Note: This results in a multiplication by the inverse of the
|
|
|
|
|
/// basis matrix only if it represents a rotation-reflection.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="v">A vector to inversely transform.</param>
|
2020-07-22 22:46:04 +02:00
|
|
|
|
/// <returns>The inversely transformed vector.</returns>
|
2017-11-21 23:32:19 +01:00
|
|
|
|
public Vector2 BasisXformInv(Vector2 v)
|
2017-10-02 23:24:00 +02:00
|
|
|
|
{
|
2017-11-21 23:32:19 +01:00
|
|
|
|
return new Vector2(x.Dot(v), y.Dot(v));
|
2017-10-02 23:24:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-07-07 23:29:35 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Interpolates this transform to the other `transform` by `weight`.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="transform">The other transform.</param>
|
|
|
|
|
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
|
|
|
|
|
/// <returns>The interpolated transform.</returns>
|
|
|
|
|
public Transform2D InterpolateWith(Transform2D transform, real_t weight)
|
2017-10-02 23:24:00 +02:00
|
|
|
|
{
|
2018-03-01 08:51:35 +01:00
|
|
|
|
real_t r1 = Rotation;
|
2020-07-07 23:29:35 +02:00
|
|
|
|
real_t r2 = transform.Rotation;
|
2017-10-02 23:24:00 +02:00
|
|
|
|
|
|
|
|
|
Vector2 s1 = Scale;
|
2020-07-07 23:29:35 +02:00
|
|
|
|
Vector2 s2 = transform.Scale;
|
2017-10-02 23:24:00 +02:00
|
|
|
|
|
|
|
|
|
// Slerp rotation
|
2018-04-08 05:30:43 +02:00
|
|
|
|
var v1 = new Vector2(Mathf.Cos(r1), Mathf.Sin(r1));
|
|
|
|
|
var v2 = new Vector2(Mathf.Cos(r2), Mathf.Sin(r2));
|
2017-10-02 23:24:00 +02:00
|
|
|
|
|
2018-03-01 08:51:35 +01:00
|
|
|
|
real_t dot = v1.Dot(v2);
|
2017-10-02 23:24:00 +02:00
|
|
|
|
|
2020-11-10 20:30:02 +01:00
|
|
|
|
dot = Mathf.Clamp(dot, -1.0f, 1.0f);
|
2017-10-02 23:24:00 +02:00
|
|
|
|
|
2018-04-08 05:38:02 +02:00
|
|
|
|
Vector2 v;
|
2017-10-02 23:24:00 +02:00
|
|
|
|
|
|
|
|
|
if (dot > 0.9995f)
|
|
|
|
|
{
|
|
|
|
|
// Linearly interpolate to avoid numerical precision issues
|
2020-07-07 23:29:35 +02:00
|
|
|
|
v = v1.Lerp(v2, weight).Normalized();
|
2017-10-02 23:24:00 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-07-07 23:29:35 +02:00
|
|
|
|
real_t angle = weight * Mathf.Acos(dot);
|
2017-11-21 23:32:19 +01:00
|
|
|
|
Vector2 v3 = (v2 - v1 * dot).Normalized();
|
|
|
|
|
v = v1 * Mathf.Cos(angle) + v3 * Mathf.Sin(angle);
|
2017-10-02 23:24:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Extract parameters
|
2019-02-09 20:56:09 +01:00
|
|
|
|
Vector2 p1 = origin;
|
2020-07-07 23:29:35 +02:00
|
|
|
|
Vector2 p2 = transform.origin;
|
2017-10-02 23:24:00 +02:00
|
|
|
|
|
|
|
|
|
// Construct matrix
|
2020-07-07 23:29:35 +02:00
|
|
|
|
var res = new Transform2D(Mathf.Atan2(v.y, v.x), p1.Lerp(p2, weight));
|
|
|
|
|
Vector2 scale = s1.Lerp(s2, weight);
|
2017-10-02 23:24:00 +02:00
|
|
|
|
res.x *= scale;
|
|
|
|
|
res.y *= scale;
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-07 23:29:35 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns the inverse of the transform, under the assumption that
|
|
|
|
|
/// the transformation is composed of rotation and translation
|
|
|
|
|
/// (no scaling, use <see cref="AffineInverse"/> for transforms with scaling).
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>The inverse matrix.</returns>
|
2017-11-21 23:32:19 +01:00
|
|
|
|
public Transform2D Inverse()
|
2017-10-02 23:24:00 +02:00
|
|
|
|
{
|
2018-04-08 05:30:43 +02:00
|
|
|
|
var inv = this;
|
2017-10-02 23:24:00 +02:00
|
|
|
|
|
|
|
|
|
// Swap
|
2018-03-01 08:51:35 +01:00
|
|
|
|
real_t temp = inv.x.y;
|
2017-10-02 23:24:00 +02:00
|
|
|
|
inv.x.y = inv.y.x;
|
|
|
|
|
inv.y.x = temp;
|
|
|
|
|
|
2019-02-09 20:56:09 +01:00
|
|
|
|
inv.origin = inv.BasisXform(-inv.origin);
|
2017-10-02 23:24:00 +02:00
|
|
|
|
|
|
|
|
|
return inv;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-07 23:29:35 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns the transform with the basis orthogonal (90 degrees),
|
|
|
|
|
/// and normalized axis vectors (scale of 1 or -1).
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>The orthonormalized transform.</returns>
|
2017-11-21 23:32:19 +01:00
|
|
|
|
public Transform2D Orthonormalized()
|
2017-10-02 23:24:00 +02:00
|
|
|
|
{
|
2018-04-08 05:30:43 +02:00
|
|
|
|
var on = this;
|
2017-10-02 23:24:00 +02:00
|
|
|
|
|
|
|
|
|
Vector2 onX = on.x;
|
|
|
|
|
Vector2 onY = on.y;
|
|
|
|
|
|
2017-11-21 23:32:19 +01:00
|
|
|
|
onX.Normalize();
|
2018-04-08 05:39:35 +02:00
|
|
|
|
onY = onY - onX * onX.Dot(onY);
|
2017-11-21 23:32:19 +01:00
|
|
|
|
onY.Normalize();
|
2017-10-02 23:24:00 +02:00
|
|
|
|
|
|
|
|
|
on.x = onX;
|
|
|
|
|
on.y = onY;
|
|
|
|
|
|
|
|
|
|
return on;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-07 23:29:35 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Rotates the transform by `phi` (in radians), using matrix multiplication.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="phi">The angle to rotate, in radians.</param>
|
|
|
|
|
/// <returns>The rotated transformation matrix.</returns>
|
2018-03-01 08:51:35 +01:00
|
|
|
|
public Transform2D Rotated(real_t phi)
|
2017-10-02 23:24:00 +02:00
|
|
|
|
{
|
|
|
|
|
return this * new Transform2D(phi, new Vector2());
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-07 23:29:35 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Scales the transform by the given scaling factor, using matrix multiplication.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="scale">The scale to introduce.</param>
|
|
|
|
|
/// <returns>The scaled transformation matrix.</returns>
|
2017-11-21 23:32:19 +01:00
|
|
|
|
public Transform2D Scaled(Vector2 scale)
|
2017-10-02 23:24:00 +02:00
|
|
|
|
{
|
2018-04-08 05:30:43 +02:00
|
|
|
|
var copy = this;
|
2017-10-02 23:24:00 +02:00
|
|
|
|
copy.x *= scale;
|
|
|
|
|
copy.y *= scale;
|
2019-02-09 20:56:09 +01:00
|
|
|
|
copy.origin *= scale;
|
2017-10-02 23:24:00 +02:00
|
|
|
|
return copy;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-12 18:39:10 +01:00
|
|
|
|
private void ScaleBasis(Vector2 scale)
|
|
|
|
|
{
|
|
|
|
|
x.x *= scale.x;
|
|
|
|
|
x.y *= scale.y;
|
|
|
|
|
y.x *= scale.x;
|
|
|
|
|
y.y *= scale.y;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-01 08:51:35 +01:00
|
|
|
|
private real_t Tdotx(Vector2 with)
|
2017-10-02 23:24:00 +02:00
|
|
|
|
{
|
|
|
|
|
return this[0, 0] * with[0] + this[1, 0] * with[1];
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-01 08:51:35 +01:00
|
|
|
|
private real_t Tdoty(Vector2 with)
|
2017-10-02 23:24:00 +02:00
|
|
|
|
{
|
|
|
|
|
return this[0, 1] * with[0] + this[1, 1] * with[1];
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-07 23:29:35 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Translates the transform by the given `offset`,
|
|
|
|
|
/// relative to the transform's basis vectors.
|
|
|
|
|
///
|
|
|
|
|
/// Unlike <see cref="Rotated"/> and <see cref="Scaled"/>,
|
|
|
|
|
/// this does not use matrix multiplication.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="offset">The offset to translate by.</param>
|
|
|
|
|
/// <returns>The translated matrix.</returns>
|
2017-11-21 23:32:19 +01:00
|
|
|
|
public Transform2D Translated(Vector2 offset)
|
2017-10-02 23:24:00 +02:00
|
|
|
|
{
|
2018-04-08 05:30:43 +02:00
|
|
|
|
var copy = this;
|
2019-02-09 20:56:09 +01:00
|
|
|
|
copy.origin += copy.BasisXform(offset);
|
2017-10-02 23:24:00 +02:00
|
|
|
|
return copy;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-07 23:29:35 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns a vector transformed (multiplied) by this transformation matrix.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="v">A vector to transform.</param>
|
2020-07-22 22:46:04 +02:00
|
|
|
|
/// <returns>The transformed vector.</returns>
|
2017-11-21 23:32:19 +01:00
|
|
|
|
public Vector2 Xform(Vector2 v)
|
2017-10-02 23:24:00 +02:00
|
|
|
|
{
|
2019-02-09 20:56:09 +01:00
|
|
|
|
return new Vector2(Tdotx(v), Tdoty(v)) + origin;
|
2017-10-02 23:24:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-07-07 23:29:35 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns a vector transformed (multiplied) by the inverse transformation matrix.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="v">A vector to inversely transform.</param>
|
2020-07-22 22:46:04 +02:00
|
|
|
|
/// <returns>The inversely transformed vector.</returns>
|
2017-11-21 23:32:19 +01:00
|
|
|
|
public Vector2 XformInv(Vector2 v)
|
2017-10-02 23:24:00 +02:00
|
|
|
|
{
|
2019-02-09 20:56:09 +01:00
|
|
|
|
Vector2 vInv = v - origin;
|
2017-11-21 23:32:19 +01:00
|
|
|
|
return new Vector2(x.Dot(vInv), y.Dot(vInv));
|
2017-10-02 23:24:00 +02:00
|
|
|
|
}
|
2018-08-10 20:46:03 +02:00
|
|
|
|
|
|
|
|
|
// Constants
|
2019-02-09 20:56:09 +01:00
|
|
|
|
private static readonly Transform2D _identity = new Transform2D(1, 0, 0, 1, 0, 0);
|
|
|
|
|
private static readonly Transform2D _flipX = new Transform2D(-1, 0, 0, 1, 0, 0);
|
|
|
|
|
private static readonly Transform2D _flipY = new Transform2D(1, 0, 0, -1, 0, 0);
|
2018-08-10 20:46:03 +02:00
|
|
|
|
|
2020-07-07 23:29:35 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// The identity transform, with no translation, rotation, or scaling applied.
|
|
|
|
|
/// This is used as a replacement for `Transform2D()` in GDScript.
|
|
|
|
|
/// Do not use `new Transform2D()` with no arguments in C#, because it sets all values to zero.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <value>Equivalent to `new Transform2D(Vector2.Right, Vector2.Down, Vector2.Zero)`.</value>
|
|
|
|
|
public static Transform2D Identity { get { return _identity; } }
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The transform that will flip something along the X axis.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <value>Equivalent to `new Transform2D(Vector2.Left, Vector2.Down, Vector2.Zero)`.</value>
|
|
|
|
|
public static Transform2D FlipX { get { return _flipX; } }
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The transform that will flip something along the Y axis.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <value>Equivalent to `new Transform2D(Vector2.Right, Vector2.Up, Vector2.Zero)`.</value>
|
|
|
|
|
public static Transform2D FlipY { get { return _flipY; } }
|
2018-11-20 11:14:07 +01:00
|
|
|
|
|
2020-07-07 23:29:35 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Constructs a transformation matrix from 3 vectors (matrix columns).
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="xAxis">The X vector, or column index 0.</param>
|
|
|
|
|
/// <param name="yAxis">The Y vector, or column index 1.</param>
|
|
|
|
|
/// <param name="originPos">The origin vector, or column index 2.</param>
|
2019-02-09 20:56:09 +01:00
|
|
|
|
public Transform2D(Vector2 xAxis, Vector2 yAxis, Vector2 originPos)
|
2017-10-02 23:24:00 +02:00
|
|
|
|
{
|
2018-04-08 05:28:24 +02:00
|
|
|
|
x = xAxis;
|
|
|
|
|
y = yAxis;
|
2019-02-09 20:56:09 +01:00
|
|
|
|
origin = originPos;
|
2017-10-02 23:24:00 +02:00
|
|
|
|
}
|
2018-11-20 11:14:07 +01:00
|
|
|
|
|
2020-07-07 23:29:35 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Constructs a transformation matrix from the given components.
|
|
|
|
|
/// Arguments are named such that xy is equal to calling x.y
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="xx">The X component of the X column vector, accessed via `t.x.x` or `[0][0]`</param>
|
|
|
|
|
/// <param name="xy">The Y component of the X column vector, accessed via `t.x.y` or `[0][1]`</param>
|
|
|
|
|
/// <param name="yx">The X component of the Y column vector, accessed via `t.y.x` or `[1][0]`</param>
|
|
|
|
|
/// <param name="yy">The Y component of the Y column vector, accessed via `t.y.y` or `[1][1]`</param>
|
|
|
|
|
/// <param name="ox">The X component of the origin vector, accessed via `t.origin.x` or `[2][0]`</param>
|
|
|
|
|
/// <param name="oy">The Y component of the origin vector, accessed via `t.origin.y` or `[2][1]`</param>
|
2018-03-01 08:51:35 +01:00
|
|
|
|
public Transform2D(real_t xx, real_t xy, real_t yx, real_t yy, real_t ox, real_t oy)
|
2017-10-02 23:24:00 +02:00
|
|
|
|
{
|
2018-04-08 05:28:24 +02:00
|
|
|
|
x = new Vector2(xx, xy);
|
|
|
|
|
y = new Vector2(yx, yy);
|
2019-02-09 20:56:09 +01:00
|
|
|
|
origin = new Vector2(ox, oy);
|
2017-10-02 23:24:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-07-07 23:29:35 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Constructs a transformation matrix from a rotation value and origin vector.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="rot">The rotation of the new transform, in radians.</param>
|
|
|
|
|
/// <param name="pos">The origin vector, or column index 2.</param>
|
2018-03-01 08:51:35 +01:00
|
|
|
|
public Transform2D(real_t rot, Vector2 pos)
|
2017-10-02 23:24:00 +02:00
|
|
|
|
{
|
2019-02-12 18:39:10 +01:00
|
|
|
|
x.x = y.y = Mathf.Cos(rot);
|
|
|
|
|
x.y = y.x = Mathf.Sin(rot);
|
|
|
|
|
y.x *= -1;
|
2019-02-09 20:56:09 +01:00
|
|
|
|
origin = pos;
|
2017-10-02 23:24:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Transform2D operator *(Transform2D left, Transform2D right)
|
|
|
|
|
{
|
2019-02-09 20:56:09 +01:00
|
|
|
|
left.origin = left.Xform(right.origin);
|
2017-10-02 23:24:00 +02:00
|
|
|
|
|
2019-02-23 18:22:27 +01:00
|
|
|
|
real_t x0 = left.Tdotx(right.x);
|
|
|
|
|
real_t x1 = left.Tdoty(right.x);
|
|
|
|
|
real_t y0 = left.Tdotx(right.y);
|
|
|
|
|
real_t y1 = left.Tdoty(right.y);
|
2017-10-02 23:24:00 +02:00
|
|
|
|
|
|
|
|
|
left.x.x = x0;
|
|
|
|
|
left.x.y = x1;
|
|
|
|
|
left.y.x = y0;
|
|
|
|
|
left.y.y = y1;
|
|
|
|
|
|
|
|
|
|
return left;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool operator ==(Transform2D left, Transform2D right)
|
|
|
|
|
{
|
|
|
|
|
return left.Equals(right);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool operator !=(Transform2D left, Transform2D right)
|
|
|
|
|
{
|
|
|
|
|
return !left.Equals(right);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override bool Equals(object obj)
|
|
|
|
|
{
|
2019-02-23 18:22:27 +01:00
|
|
|
|
return obj is Transform2D transform2D && Equals(transform2D);
|
2017-10-02 23:24:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool Equals(Transform2D other)
|
|
|
|
|
{
|
2019-02-09 20:56:09 +01:00
|
|
|
|
return x.Equals(other.x) && y.Equals(other.y) && origin.Equals(other.origin);
|
2017-10-02 23:24:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-07-07 23:29:35 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns true if this transform and `other` are approximately equal, by running
|
|
|
|
|
/// <see cref="Vector2.IsEqualApprox(Vector2)"/> on each component.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="other">The other transform to compare.</param>
|
|
|
|
|
/// <returns>Whether or not the matrices are approximately equal.</returns>
|
2019-10-14 22:33:45 +02:00
|
|
|
|
public bool IsEqualApprox(Transform2D other)
|
|
|
|
|
{
|
|
|
|
|
return x.IsEqualApprox(other.x) && y.IsEqualApprox(other.y) && origin.IsEqualApprox(other.origin);
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-02 23:24:00 +02:00
|
|
|
|
public override int GetHashCode()
|
|
|
|
|
{
|
2019-02-09 20:56:09 +01:00
|
|
|
|
return x.GetHashCode() ^ y.GetHashCode() ^ origin.GetHashCode();
|
2017-10-02 23:24:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override string ToString()
|
|
|
|
|
{
|
2021-02-25 15:54:50 +01:00
|
|
|
|
return "[X: " + x.ToString() +
|
|
|
|
|
", Y: " + y.ToString() +
|
|
|
|
|
", O: " + origin.ToString() + "]";
|
2017-10-02 23:24:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string ToString(string format)
|
|
|
|
|
{
|
2021-02-25 15:54:50 +01:00
|
|
|
|
return "[X: " + x.ToString(format) +
|
|
|
|
|
", Y: " + y.ToString(format) +
|
|
|
|
|
", O: " + origin.ToString(format) + "]";
|
2017-10-02 23:24:00 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|