diff options
Diffstat (limited to 'modules/mono/glue/GodotSharp')
36 files changed, 1933 insertions, 1075 deletions
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs index a2916d4ae8..0e46b63b59 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs @@ -437,48 +437,25 @@ namespace Godot /// <summary> /// Returns <see langword="true"/> if the <see cref="AABB"/> overlaps with <paramref name="with"/> /// (i.e. they have at least one point in common). - /// - /// If <paramref name="includeBorders"/> is <see langword="true"/>, - /// they will also be considered overlapping if their borders touch, - /// even without intersection. /// </summary> /// <param name="with">The other <see cref="AABB"/> to check for intersections with.</param> - /// <param name="includeBorders">Whether or not to consider borders.</param> /// <returns> /// A <see langword="bool"/> for whether or not they are intersecting. /// </returns> - public readonly bool Intersects(AABB with, bool includeBorders = false) + public readonly bool Intersects(AABB with) { - if (includeBorders) - { - if (_position.x > with._position.x + with._size.x) - return false; - if (_position.x + _size.x < with._position.x) - return false; - if (_position.y > with._position.y + with._size.y) - return false; - if (_position.y + _size.y < with._position.y) - return false; - if (_position.z > with._position.z + with._size.z) - return false; - if (_position.z + _size.z < with._position.z) - return false; - } - else - { - if (_position.x >= with._position.x + with._size.x) - return false; - if (_position.x + _size.x <= with._position.x) - return false; - if (_position.y >= with._position.y + with._size.y) - return false; - if (_position.y + _size.y <= with._position.y) - return false; - if (_position.z >= with._position.z + with._size.z) - return false; - if (_position.z + _size.z <= with._position.z) - return false; - } + if (_position.x >= with._position.x + with._size.x) + return false; + if (_position.x + _size.x <= with._position.x) + return false; + if (_position.y >= with._position.y + with._size.y) + return false; + if (_position.y + _size.y <= with._position.y) + return false; + if (_position.z >= with._position.z + with._size.z) + return false; + if (_position.z + _size.z <= with._position.z) + return false; return true; } @@ -586,6 +563,16 @@ namespace Godot } /// <summary> + /// Returns <see langword="true"/> if this <see cref="AABB"/> is finite, by calling + /// <see cref="Mathf.IsFinite"/> on each component. + /// </summary> + /// <returns>Whether this vector is finite or not.</returns> + public readonly bool IsFinite() + { + return _position.IsFinite() && _size.IsFinite(); + } + + /// <summary> /// Returns a larger <see cref="AABB"/> that contains this <see cref="AABB"/> and <paramref name="with"/>. /// </summary> /// <param name="with">The other <see cref="AABB"/>.</param> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs index acdae83d2e..81659f74de 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs @@ -10,7 +10,7 @@ namespace Godot /// collection of types that implement scripts; otherwise, retrieving the types requires lookup. /// </summary> [AttributeUsage(AttributeTargets.Assembly)] - public class AssemblyHasScriptsAttribute : Attribute + public sealed class AssemblyHasScriptsAttribute : Attribute { /// <summary> /// If the Godot scripts contained in the assembly require lookup diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/MustBeVariantAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/MustBeVariantAttribute.cs index 23088378d1..0070223c95 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/MustBeVariantAttribute.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/MustBeVariantAttribute.cs @@ -7,5 +7,5 @@ namespace Godot /// that can be marshaled from/to a <see cref="Variant"/>. /// </summary> [AttributeUsage(AttributeTargets.GenericParameter)] - public class MustBeVariantAttribute : Attribute { } + public sealed class MustBeVariantAttribute : Attribute { } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttribute.cs index afee926464..02a38d310e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttribute.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttribute.cs @@ -9,7 +9,7 @@ namespace Godot /// By default, methods are not exposed to networking (and RPCs). /// </summary> [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] - public class RPCAttribute : Attribute + public sealed class RPCAttribute : Attribute { /// <summary> /// RPC mode for the annotated method. diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs index f05bcdac38..d363e14c5d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs @@ -6,7 +6,7 @@ namespace Godot /// An attribute that contains the path to the object's script. /// </summary> [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - public class ScriptPathAttribute : Attribute + public sealed class ScriptPathAttribute : Attribute { /// <summary> /// File path to the script. diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs index 38e68a89d5..0a08bb5df8 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs @@ -3,5 +3,5 @@ using System; namespace Godot { [AttributeUsage(AttributeTargets.Delegate)] - public class SignalAttribute : Attribute { } + public sealed class SignalAttribute : Attribute { } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ToolAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ToolAttribute.cs index d2344389f4..4c56201727 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ToolAttribute.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ToolAttribute.cs @@ -3,5 +3,5 @@ using System; namespace Godot { [AttributeUsage(AttributeTargets.Class)] - public class ToolAttribute : Attribute { } + public sealed class ToolAttribute : Attribute { } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs index 5d390a298d..2b139a78bd 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs @@ -120,12 +120,12 @@ namespace Godot } /// <summary> - /// The scale of this basis. + /// Assuming that the matrix is the combination of a rotation and scaling, + /// return the absolute value of scaling factors along each axis. /// </summary> - /// <value>Equivalent to the lengths of each column vector, but negative if the determinant is negative.</value> - public Vector3 Scale + public readonly Vector3 Scale { - readonly get + get { real_t detSign = Mathf.Sign(Determinant()); return detSign * new Vector3 @@ -135,13 +135,6 @@ namespace Godot Column2.Length() ); } - set - { - value /= Scale; // Value becomes what's called "delta_scale" in core. - Column0 *= value.x; - Column1 *= value.y; - Column2 *= value.z; - } } /// <summary> @@ -493,12 +486,6 @@ namespace Godot } } - /// <summary> - /// Returns the basis's rotation in the form of a quaternion. - /// See <see cref="GetEuler"/> if you need Euler angles, but keep in - /// mind that quaternions should generally be preferred to Euler angles. - /// </summary> - /// <returns>A <see cref="Quaternion"/> representing the basis's rotation.</returns> internal readonly Quaternion GetQuaternion() { real_t trace = Row0[0] + Row1[1] + Row2[2]; @@ -573,109 +560,6 @@ namespace Godot } /// <summary> - /// Get rows by index. Rows are not very useful for user code, - /// but are more efficient for some internal calculations. - /// </summary> - /// <param name="index">Which row.</param> - /// <exception cref="ArgumentOutOfRangeException"> - /// <paramref name="index"/> is not 0, 1 or 2. - /// </exception> - /// <returns>One of <c>Row0</c>, <c>Row1</c>, or <c>Row2</c>.</returns> - public readonly Vector3 GetRow(int index) - { - switch (index) - { - case 0: - return Row0; - case 1: - return Row1; - case 2: - return Row2; - default: - throw new ArgumentOutOfRangeException(nameof(index)); - } - } - - /// <summary> - /// Sets rows by index. Rows are not very useful for user code, - /// but are more efficient for some internal calculations. - /// </summary> - /// <param name="index">Which row.</param> - /// <param name="value">The vector to set the row to.</param> - /// <exception cref="ArgumentOutOfRangeException"> - /// <paramref name="index"/> is not 0, 1 or 2. - /// </exception> - public void SetRow(int index, Vector3 value) - { - switch (index) - { - case 0: - Row0 = value; - return; - case 1: - Row1 = value; - return; - case 2: - Row2 = value; - return; - default: - throw new ArgumentOutOfRangeException(nameof(index)); - } - } - - /// <summary> - /// This function considers a discretization of rotations into - /// 24 points on unit sphere, lying along the vectors (x, y, z) with - /// each component being either -1, 0, or 1, and returns the index - /// of the point best representing the orientation of the object. - /// It is mainly used by the <see cref="GridMap"/> editor. - /// - /// For further details, refer to the Godot source code. - /// </summary> - /// <returns>The orthogonal index.</returns> - public readonly int GetOrthogonalIndex() - { - var orth = this; - - for (int i = 0; i < 3; i++) - { - for (int j = 0; j < 3; j++) - { - var row = orth.GetRow(i); - - real_t v = row[j]; - - if (v > 0.5f) - { - v = 1.0f; - } - else if (v < -0.5f) - { - v = -1.0f; - } - else - { - v = 0f; - } - - row[j] = v; - - orth.SetRow(i, row); - } - } - - for (int i = 0; i < 24; i++) - { - if (orth == _orthoBases[i]) - { - return i; - } - } - - return 0; - } - - /// <summary> /// Returns the inverse of the matrix. /// </summary> /// <returns>The inverse matrix.</returns> @@ -709,6 +593,16 @@ namespace Godot ); } + /// <summary> + /// Returns <see langword="true"/> if this basis is finite, by calling + /// <see cref="Mathf.IsFinite"/> on each component. + /// </summary> + /// <returns>Whether this vector is finite or not.</returns> + public readonly bool IsFinite() + { + return Row0.IsFinite() && Row1.IsFinite() && Row2.IsFinite(); + } + internal readonly Basis Lerp(Basis to, real_t weight) { Basis b = this; @@ -896,7 +790,7 @@ namespace Godot /// <param name="quaternion">The quaternion to create the basis from.</param> public Basis(Quaternion quaternion) { - real_t s = 2.0f / quaternion.LengthSquared; + real_t s = 2.0f / quaternion.LengthSquared(); real_t xs = quaternion.x * s; real_t ys = quaternion.y * s; @@ -925,26 +819,26 @@ namespace Godot public Basis(Vector3 axis, real_t angle) { Vector3 axisSq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z); - real_t cosine = Mathf.Cos(angle); - Row0.x = axisSq.x + cosine * (1.0f - axisSq.x); - Row1.y = axisSq.y + cosine * (1.0f - axisSq.y); - Row2.z = axisSq.z + cosine * (1.0f - axisSq.z); + (real_t sin, real_t cos) = Mathf.SinCos(angle); - real_t sine = Mathf.Sin(angle); - real_t t = 1.0f - cosine; + Row0.x = axisSq.x + cos * (1.0f - axisSq.x); + Row1.y = axisSq.y + cos * (1.0f - axisSq.y); + Row2.z = axisSq.z + cos * (1.0f - axisSq.z); + + real_t t = 1.0f - cos; real_t xyzt = axis.x * axis.y * t; - real_t zyxs = axis.z * sine; + real_t zyxs = axis.z * sin; Row0.y = xyzt - zyxs; Row1.x = xyzt + zyxs; xyzt = axis.x * axis.z * t; - zyxs = axis.y * sine; + zyxs = axis.y * sin; Row0.z = xyzt + zyxs; Row2.x = xyzt - zyxs; xyzt = axis.y * axis.z * t; - zyxs = axis.x * sine; + zyxs = axis.x * sin; Row1.z = xyzt - zyxs; Row2.y = xyzt + zyxs; } @@ -967,8 +861,20 @@ namespace Godot // We need to assign the struct fields here first so we can't do it that way... } - // Arguments are named such that xy is equal to calling x.y - internal Basis(real_t xx, real_t yx, real_t zx, real_t xy, real_t yy, real_t zy, real_t xz, real_t yz, real_t zz) + /// <summary> + /// Constructs a transformation matrix from the given components. + /// Arguments are named such that xy is equal to calling <c>x.y</c>. + /// </summary> + /// <param name="xx">The X component of the X column vector, accessed via <c>b.x.x</c> or <c>[0][0]</c>.</param> + /// <param name="yx">The X component of the Y column vector, accessed via <c>b.y.x</c> or <c>[1][0]</c>.</param> + /// <param name="zx">The X component of the Z column vector, accessed via <c>b.z.x</c> or <c>[2][0]</c>.</param> + /// <param name="xy">The Y component of the X column vector, accessed via <c>b.x.y</c> or <c>[0][1]</c>.</param> + /// <param name="yy">The Y component of the Y column vector, accessed via <c>b.y.y</c> or <c>[1][1]</c>.</param> + /// <param name="zy">The Y component of the Z column vector, accessed via <c>b.y.y</c> or <c>[2][1]</c>.</param> + /// <param name="xz">The Z component of the X column vector, accessed via <c>b.x.y</c> or <c>[0][2]</c>.</param> + /// <param name="yz">The Z component of the Y column vector, accessed via <c>b.y.y</c> or <c>[1][2]</c>.</param> + /// <param name="zz">The Z component of the Z column vector, accessed via <c>b.y.y</c> or <c>[2][2]</c>.</param> + public Basis(real_t xx, real_t yx, real_t zx, real_t xy, real_t yy, real_t zy, real_t xz, real_t yz, real_t zz) { Row0 = new Vector3(xx, yx, zx); Row1 = new Vector3(xy, yy, zy); @@ -982,19 +888,29 @@ namespace Godot /// <param name="order">The order to compose the Euler angles.</param> public static Basis FromEuler(Vector3 euler, EulerOrder order = EulerOrder.Yxz) { - real_t c, s; - - c = Mathf.Cos(euler.x); - s = Mathf.Sin(euler.x); - Basis xmat = new Basis(new Vector3(1, 0, 0), new Vector3(0, c, s), new Vector3(0, -s, c)); + (real_t sin, real_t cos) = Mathf.SinCos(euler.x); + Basis xmat = new Basis + ( + new Vector3(1, 0, 0), + new Vector3(0, cos, sin), + new Vector3(0, -sin, cos) + ); - c = Mathf.Cos(euler.y); - s = Mathf.Sin(euler.y); - Basis ymat = new Basis(new Vector3(c, 0, -s), new Vector3(0, 1, 0), new Vector3(s, 0, c)); + (sin, cos) = Mathf.SinCos(euler.y); + Basis ymat = new Basis + ( + new Vector3(cos, 0, -sin), + new Vector3(0, 1, 0), + new Vector3(sin, 0, cos) + ); - c = Mathf.Cos(euler.z); - s = Mathf.Sin(euler.z); - Basis zmat = new Basis(new Vector3(c, s, 0), new Vector3(-s, c, 0), new Vector3(0, 0, 1)); + (sin, cos) = Mathf.SinCos(euler.z); + Basis zmat = new Basis + ( + new Vector3(cos, sin, 0), + new Vector3(-sin, cos, 0), + new Vector3(0, 0, 1) + ); switch (order) { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.generics.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.generics.cs index ff385da1c9..3005582bea 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.generics.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.generics.cs @@ -45,7 +45,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From(Action)"/> - public static unsafe Callable From<T0>( + public static unsafe Callable From<[MustBeVariant] T0>( Action<T0> action ) { @@ -64,7 +64,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From(Action)"/> - public static unsafe Callable From<T0, T1>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1>( Action<T0, T1> action ) { @@ -84,7 +84,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From(Action)"/> - public static unsafe Callable From<T0, T1, T2>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2>( Action<T0, T1, T2> action ) { @@ -105,7 +105,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From(Action)"/> - public static unsafe Callable From<T0, T1, T2, T3>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3>( Action<T0, T1, T2, T3> action ) { @@ -127,7 +127,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From(Action)"/> - public static unsafe Callable From<T0, T1, T2, T3, T4>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4>( Action<T0, T1, T2, T3, T4> action ) { @@ -150,7 +150,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From(Action)"/> - public static unsafe Callable From<T0, T1, T2, T3, T4, T5>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5>( Action<T0, T1, T2, T3, T4, T5> action ) { @@ -174,7 +174,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From(Action)"/> - public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6>( Action<T0, T1, T2, T3, T4, T5, T6> action ) { @@ -199,7 +199,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From(Action)"/> - public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6, T7>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6, [MustBeVariant] T7>( Action<T0, T1, T2, T3, T4, T5, T6, T7> action ) { @@ -225,7 +225,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From(Action)"/> - public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6, T7, T8>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6, [MustBeVariant] T7, [MustBeVariant] T8>( Action<T0, T1, T2, T3, T4, T5, T6, T7, T8> action ) { @@ -255,7 +255,7 @@ public readonly partial struct Callable /// Constructs a new <see cref="Callable"/> for the given <paramref name="func"/>. /// </summary> /// <param name="func">Action method that will be called.</param> - public static unsafe Callable From<TResult>( + public static unsafe Callable From<[MustBeVariant] TResult>( Func<TResult> func ) { @@ -272,7 +272,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From{TResult}(Func{TResult})"/> - public static unsafe Callable From<T0, TResult>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] TResult>( Func<T0, TResult> func ) { @@ -291,7 +291,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From{TResult}(Func{TResult})"/> - public static unsafe Callable From<T0, T1, TResult>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] TResult>( Func<T0, T1, TResult> func ) { @@ -311,7 +311,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From{TResult}(Func{TResult})"/> - public static unsafe Callable From<T0, T1, T2, TResult>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] TResult>( Func<T0, T1, T2, TResult> func ) { @@ -332,7 +332,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From{TResult}(Func{TResult})"/> - public static unsafe Callable From<T0, T1, T2, T3, TResult>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] TResult>( Func<T0, T1, T2, T3, TResult> func ) { @@ -354,7 +354,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From{TResult}(Func{TResult})"/> - public static unsafe Callable From<T0, T1, T2, T3, T4, TResult>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] TResult>( Func<T0, T1, T2, T3, T4, TResult> func ) { @@ -377,7 +377,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From{TResult}(Func{TResult})"/> - public static unsafe Callable From<T0, T1, T2, T3, T4, T5, TResult>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] TResult>( Func<T0, T1, T2, T3, T4, T5, TResult> func ) { @@ -401,7 +401,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From{TResult}(Func{TResult})"/> - public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6, TResult>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6, [MustBeVariant] TResult>( Func<T0, T1, T2, T3, T4, T5, T6, TResult> func ) { @@ -426,7 +426,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From{TResult}(Func{TResult})"/> - public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6, T7, TResult>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6, [MustBeVariant] T7, [MustBeVariant] TResult>( Func<T0, T1, T2, T3, T4, T5, T6, T7, TResult> func ) { @@ -452,7 +452,7 @@ public readonly partial struct Callable } /// <inheritdoc cref="From{TResult}(Func{TResult})"/> - public static unsafe Callable From<T0, T1, T2, T3, T4, T5, T6, T7, T8, TResult>( + public static unsafe Callable From<[MustBeVariant] T0, [MustBeVariant] T1, [MustBeVariant] T2, [MustBeVariant] T3, [MustBeVariant] T4, [MustBeVariant] T5, [MustBeVariant] T6, [MustBeVariant] T7, [MustBeVariant] T8, [MustBeVariant] TResult>( Func<T0, T1, T2, T3, T4, T5, T6, T7, T8, TResult> func ) { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs index 4075a878d2..6a7863112a 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs @@ -359,24 +359,6 @@ namespace Godot } /// <summary> - /// Returns the result of the linear interpolation between - /// this color and <paramref name="to"/> by color amount <paramref name="weight"/>. - /// </summary> - /// <param name="to">The destination color for interpolation.</param> - /// <param name="weight">A color with components on the range of 0.0 to 1.0, representing the amount of interpolation.</param> - /// <returns>The resulting color of the interpolation.</returns> - public readonly Color Lerp(Color to, Color weight) - { - return new Color - ( - (float)Mathf.Lerp(r, to.r, weight.r), - (float)Mathf.Lerp(g, to.g, weight.g), - (float)Mathf.Lerp(b, to.b, weight.b), - (float)Mathf.Lerp(a, to.a, weight.a) - ); - } - - /// <summary> /// Returns the color converted to the sRGB color space. /// This method assumes the original color is in the linear color space. /// See also <see cref="SrgbToLinear"/> which performs the opposite operation. @@ -815,7 +797,7 @@ namespace Godot { if (saturation == 0) { - // Achromatic (grey) + // Achromatic (gray) return new Color(value, value, value, alpha); } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs index d94fbff331..0f97149130 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs @@ -399,7 +399,7 @@ namespace Godot { ulong objectId = reader.ReadUInt64(); // ReSharper disable once RedundantNameQualifier - Godot.Object godotObject = GD.InstanceFromId(objectId); + Godot.Object godotObject = Godot.Object.InstanceFromId(objectId); if (godotObject == null) return false; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index b5a8742d3d..505763e6a2 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -68,7 +68,15 @@ namespace Godot.Collections } /// <summary> - /// Duplicates this <see cref="Dictionary"/>. + /// Returns a copy of the <see cref="Dictionary"/>. + /// If <paramref name="deep"/> is <see langword="true"/>, a deep copy is performed: + /// all nested arrays and dictionaries are duplicated and will not be shared with + /// the original dictionary. If <see langword="false"/>, a shallow copy is made and + /// references to the original nested arrays and dictionaries are kept, so that + /// modifying a sub-array or dictionary in the copy will also impact those + /// referenced in the source dictionary. Note that any <see cref="Object"/> derived + /// elements will be shallow copied regardless of the <paramref name="deep"/> + /// setting. /// </summary> /// <param name="deep">If <see langword="true"/>, performs a deep copy.</param> /// <returns>A new Godot Dictionary.</returns> @@ -80,6 +88,39 @@ namespace Godot.Collections return CreateTakingOwnershipOfDisposableValue(newDictionary); } + /// <summary> + /// Adds entries from <paramref name="dictionary"/> to this dictionary. + /// By default, duplicate keys are not copied over, unless <paramref name="overwrite"/> + /// is <see langword="true"/>. + /// </summary> + /// <param name="dictionary">Dictionary to copy entries from.</param> + /// <param name="overwrite">If duplicate keys should be copied over as well.</param> + public void Merge(Dictionary dictionary, bool overwrite = false) + { + var self = (godot_dictionary)NativeValue; + var other = (godot_dictionary)dictionary.NativeValue; + NativeFuncs.godotsharp_dictionary_merge(ref self, in other, overwrite.ToGodotBool()); + } + + /// <summary> + /// Compares this <see cref="Dictionary"/> against the <paramref name="other"/> + /// <see cref="Dictionary"/> recursively. Returns <see langword="true"/> if the + /// two dictionaries contain the same keys and values. The order of the entries + /// does not matter. + /// otherwise. + /// </summary> + /// <param name="other">The other dictionary to compare against.</param> + /// <returns> + /// <see langword="true"/> if the dictionaries contain the same keys and values, + /// <see langword="false"/> otherwise. + /// </returns> + public bool RecursiveEqual(Dictionary other) + { + var self = (godot_dictionary)NativeValue; + var otherVariant = (godot_dictionary)other.NativeValue; + return NativeFuncs.godotsharp_dictionary_recursive_equal(ref self, otherVariant).ToBool(); + } + // IDictionary /// <summary> @@ -134,6 +175,9 @@ namespace Godot.Collections /// <summary> /// Returns the value at the given <paramref name="key"/>. /// </summary> + /// <exception cref="KeyNotFoundException"> + /// An entry for <paramref name="key"/> does not exist in the dictionary. + /// </exception> /// <value>The value at the given <paramref name="key"/>.</value> public Variant this[Variant key] { @@ -163,6 +207,9 @@ namespace Godot.Collections /// Adds an value <paramref name="value"/> at key <paramref name="key"/> /// to this <see cref="Dictionary"/>. /// </summary> + /// <exception cref="ArgumentException"> + /// An entry for <paramref name="key"/> already exists in the dictionary. + /// </exception> /// <param name="key">The key at which to add the value.</param> /// <param name="value">The value to add.</param> public void Add(Variant key, Variant value) @@ -181,7 +228,7 @@ namespace Godot.Collections => Add(item.Key, item.Value); /// <summary> - /// Erases all items from this <see cref="Dictionary"/>. + /// Clears the dictionary, removing all entries from it. /// </summary> public void Clear() { @@ -200,7 +247,7 @@ namespace Godot.Collections return NativeFuncs.godotsharp_dictionary_contains_key(ref self, (godot_variant)key.NativeVar).ToBool(); } - public bool Contains(KeyValuePair<Variant, Variant> item) + bool ICollection<KeyValuePair<Variant, Variant>>.Contains(KeyValuePair<Variant, Variant> item) { godot_variant variantKey = (godot_variant)item.Key.NativeVar; var self = (godot_dictionary)NativeValue; @@ -227,7 +274,7 @@ namespace Godot.Collections return NativeFuncs.godotsharp_dictionary_remove_key(ref self, (godot_variant)key.NativeVar).ToBool(); } - public bool Remove(KeyValuePair<Variant, Variant> item) + bool ICollection<KeyValuePair<Variant, Variant>>.Remove(KeyValuePair<Variant, Variant> item) { godot_variant variantKey = (godot_variant)item.Key.NativeVar; var self = (godot_dictionary)NativeValue; @@ -266,6 +313,14 @@ namespace Godot.Collections bool ICollection<KeyValuePair<Variant, Variant>>.IsReadOnly => false; + /// <summary> + /// Gets the value for the given <paramref name="key"/> in the dictionary. + /// Returns <see langword="true"/> if an entry for the given key exists in + /// the dictionary; otherwise, returns <see langword="false"/>. + /// </summary> + /// <param name="key">The key of the element to get.</param> + /// <param name="value">The value at the given <paramref name="key"/>.</param> + /// <returns>If an entry was found for the given <paramref name="key"/>.</returns> public bool TryGetValue(Variant key, out Variant value) { var self = (godot_dictionary)NativeValue; @@ -283,7 +338,7 @@ namespace Godot.Collections /// </summary> /// <param name="array">The array to copy to.</param> /// <param name="arrayIndex">The index to start at.</param> - public void CopyTo(KeyValuePair<Variant, Variant>[] array, int arrayIndex) + void ICollection<KeyValuePair<Variant, Variant>>.CopyTo(KeyValuePair<Variant, Variant>[] array, int arrayIndex) { if (array == null) throw new ArgumentNullException(nameof(array), "Value cannot be null."); @@ -433,15 +488,49 @@ namespace Godot.Collections } /// <summary> - /// Duplicates this <see cref="Dictionary{TKey, TValue}"/>. + /// Returns a copy of the <see cref="Dictionary{TKey, TValue}"/>. + /// If <paramref name="deep"/> is <see langword="true"/>, a deep copy is performed: + /// all nested arrays and dictionaries are duplicated and will not be shared with + /// the original dictionary. If <see langword="false"/>, a shallow copy is made and + /// references to the original nested arrays and dictionaries are kept, so that + /// modifying a sub-array or dictionary in the copy will also impact those + /// referenced in the source dictionary. Note that any <see cref="Object"/> derived + /// elements will be shallow copied regardless of the <paramref name="deep"/> + /// setting. /// </summary> - /// <param name="deep">If <see langword="true"/>, performs a deep copy.</param> - /// <returns>A new Godot Dictionary.</returns> public Dictionary<TKey, TValue> Duplicate(bool deep = false) { return new Dictionary<TKey, TValue>(_underlyingDict.Duplicate(deep)); } + /// <summary> + /// Adds entries from <paramref name="dictionary"/> to this dictionary. + /// By default, duplicate keys are not copied over, unless <paramref name="overwrite"/> + /// is <see langword="true"/>. + /// </summary> + /// <param name="dictionary">Dictionary to copy entries from.</param> + /// <param name="overwrite">If duplicate keys should be copied over as well.</param> + public void Merge(Dictionary<TKey, TValue> dictionary, bool overwrite = false) + { + _underlyingDict.Merge(dictionary._underlyingDict, overwrite); + } + + /// <summary> + /// Compares this <see cref="Dictionary{TKey, TValue}"/> against the <paramref name="other"/> + /// <see cref="Dictionary{TKey, TValue}"/> recursively. Returns <see langword="true"/> if the + /// two dictionaries contain the same keys and values. The order of the entries does not matter. + /// otherwise. + /// </summary> + /// <param name="other">The other dictionary to compare against.</param> + /// <returns> + /// <see langword="true"/> if the dictionaries contain the same keys and values, + /// <see langword="false"/> otherwise. + /// </returns> + public bool RecursiveEqual(Dictionary<TKey, TValue> other) + { + return _underlyingDict.RecursiveEqual(other._underlyingDict); + } + // IDictionary<TKey, TValue> /// <summary> @@ -565,11 +654,13 @@ namespace Godot.Collections } /// <summary> - /// Gets the object at the given <paramref name="key"/>. + /// Gets the value for the given <paramref name="key"/> in the dictionary. + /// Returns <see langword="true"/> if an entry for the given key exists in + /// the dictionary; otherwise, returns <see langword="false"/>. /// </summary> /// <param name="key">The key of the element to get.</param> /// <param name="value">The value at the given <paramref name="key"/>.</param> - /// <returns>If an object was found for the given <paramref name="key"/>.</returns> + /// <returns>If an entry was found for the given <paramref name="key"/>.</returns> public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) { using var variantKey = VariantUtils.CreateFrom(key); @@ -598,7 +689,7 @@ namespace Godot.Collections => Add(item.Key, item.Value); /// <summary> - /// Erases all the items from this <see cref="Dictionary{TKey, TValue}"/>. + /// Clears the dictionary, removing all entries from it. /// </summary> public void Clear() => _underlyingDict.Clear(); @@ -625,7 +716,7 @@ namespace Godot.Collections /// </summary> /// <param name="array">The array to copy to.</param> /// <param name="arrayIndex">The index to start at.</param> - public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) + void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) { if (array == null) throw new ArgumentNullException(nameof(array), "Value cannot be null."); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs index e6cb4171a7..e7370b2b05 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs @@ -9,7 +9,10 @@ namespace Godot internal static GodotTaskScheduler DefaultGodotTaskScheduler; internal static void InitializeDefaultGodotTaskScheduler() - => DefaultGodotTaskScheduler = new GodotTaskScheduler(); + { + DefaultGodotTaskScheduler?.Dispose(); + DefaultGodotTaskScheduler = new GodotTaskScheduler(); + } public static GodotSynchronizationContext SynchronizationContext => DefaultGodotTaskScheduler.Context; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs index 4094ceeb22..a9f3f02856 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs @@ -6,8 +6,46 @@ namespace Godot public partial class Object { /// <summary> - /// Returns whether <paramref name="instance"/> is a valid object - /// (e.g. has not been deleted from memory). + /// Returns the <see cref="Object"/> that corresponds to <paramref name="instanceId"/>. + /// All Objects have a unique instance ID. See also <see cref="GetInstanceId"/>. + /// </summary> + /// <example> + /// <code> + /// public partial class MyNode : Node + /// { + /// public string Foo { get; set; } = "bar"; + /// + /// public override void _Ready() + /// { + /// ulong id = GetInstanceId(); + /// var inst = (MyNode)InstanceFromId(Id); + /// GD.Print(inst.Foo); // Prints bar + /// } + /// } + /// </code> + /// </example> + /// <param name="instanceId">Instance ID of the Object to retrieve.</param> + /// <returns>The <see cref="Object"/> instance.</returns> + public static Object InstanceFromId(ulong instanceId) + { + return InteropUtils.UnmanagedGetManaged(NativeFuncs.godotsharp_instance_from_id(instanceId)); + } + + /// <summary> + /// Returns <see langword="true"/> if the <see cref="Object"/> that corresponds + /// to <paramref name="id"/> is a valid object (e.g. has not been deleted from + /// memory). All Objects have a unique instance ID. + /// </summary> + /// <param name="id">The Object ID to check.</param> + /// <returns>If the instance with the given ID is a valid object.</returns> + public static bool IsInstanceIdValid(ulong id) + { + return IsInstanceValid(InstanceFromId(id)); + } + + /// <summary> + /// Returns <see langword="true"/> if <paramref name="instance"/> is a + /// valid <see cref="Object"/> (e.g. has not been deleted from memory). /// </summary> /// <param name="instance">The instance to check.</param> /// <returns>If the instance is a valid object.</returns> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs index e4b79e7ec4..9425b7424c 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Text; using Godot.NativeInterop; namespace Godot @@ -10,36 +11,45 @@ namespace Godot public static partial class GD { /// <summary> - /// Decodes a byte array back to a <c>Variant</c> value. - /// If <paramref name="allowObjects"/> is <see langword="true"/> decoding objects is allowed. - /// - /// WARNING: Deserialized object can contain code which gets executed. - /// Do not set <paramref name="allowObjects"/> to <see langword="true"/> - /// if the serialized object comes from untrusted sources to avoid - /// potential security threats (remote code execution). + /// Decodes a byte array back to a <see cref="Variant"/> value, without decoding objects. + /// Note: If you need object deserialization, see <see cref="BytesToVarWithObjects"/>. /// </summary> - /// <param name="bytes">Byte array that will be decoded to a <c>Variant</c>.</param> - /// <param name="allowObjects">If objects should be decoded.</param> - /// <returns>The decoded <c>Variant</c>.</returns> - public static Variant BytesToVar(Span<byte> bytes, bool allowObjects = false) + /// <param name="bytes">Byte array that will be decoded to a <see cref="Variant"/>.</param> + /// <returns>The decoded <see cref="Variant"/>.</returns> + public static Variant BytesToVar(Span<byte> bytes) { using var varBytes = Marshaling.ConvertSystemArrayToNativePackedByteArray(bytes); - NativeFuncs.godotsharp_bytes_to_var(varBytes, allowObjects.ToGodotBool(), out godot_variant ret); + NativeFuncs.godotsharp_bytes_to_var(varBytes, godot_bool.False, out godot_variant ret); return Variant.CreateTakingOwnershipOfDisposableValue(ret); } /// <summary> - /// Converts from a <c>Variant</c> type to another in the best way possible. + /// Decodes a byte array back to a <see cref="Variant"/> value. Decoding objects is allowed. + /// Warning: Deserialized object can contain code which gets executed. Do not use this + /// option if the serialized object comes from untrusted sources to avoid potential security + /// threats (remote code execution). + /// </summary> + /// <param name="bytes">Byte array that will be decoded to a <see cref="Variant"/>.</param> + /// <returns>The decoded <see cref="Variant"/>.</returns> + public static Variant BytesToVarWithObjects(Span<byte> bytes) + { + using var varBytes = Marshaling.ConvertSystemArrayToNativePackedByteArray(bytes); + NativeFuncs.godotsharp_bytes_to_var(varBytes, godot_bool.True, out godot_variant ret); + return Variant.CreateTakingOwnershipOfDisposableValue(ret); + } + + /// <summary> + /// Converts <paramref name="what"/> to <paramref name="type"/> in the best way possible. /// The <paramref name="type"/> parameter uses the <see cref="Variant.Type"/> values. /// </summary> /// <example> /// <code> - /// var a = new Vector2(1, 0); - /// // Prints 1 - /// GD.Print(a.Length()); - /// var b = GD.Convert(a, Variant.Type.String) - /// // Prints 6 as "(1, 0)" is 6 characters - /// GD.Print(b.Length); + /// Variant a = new Godot.Collections.Array { 4, 2.5, 1.2 }; + /// GD.Print(a.VariantType == Variant.Type.Array); // Prints true + /// + /// var b = GD.Convert(a, Variant.Type.PackedByteArray); + /// GD.Print(b); // Prints [4, 2, 1] + /// GD.Print(b.VariantType == Variant.Type.Array); // Prints false /// </code> /// </example> /// <returns>The <c>Variant</c> converted to the given <paramref name="type"/>.</returns> @@ -50,28 +60,7 @@ namespace Godot } /// <summary> - /// Converts from decibels to linear energy (audio). - /// </summary> - /// <seealso cref="LinearToDb(real_t)"/> - /// <param name="db">Decibels to convert.</param> - /// <returns>Audio volume as linear energy.</returns> - public static real_t DbToLinear(real_t db) - { - return (real_t)Math.Exp(db * 0.11512925464970228420089957273422); - } - - private static string[] GetPrintParams(object[] parameters) - { - if (parameters == null) - { - return new[] { "null" }; - } - - return Array.ConvertAll(parameters, x => x?.ToString() ?? "null"); - } - - /// <summary> - /// Returns the integer hash of the variable passed. + /// Returns the integer hash of the passed <paramref name="var"/>. /// </summary> /// <example> /// <code> @@ -86,52 +75,6 @@ namespace Godot } /// <summary> - /// Returns the <see cref="Object"/> that corresponds to <paramref name="instanceId"/>. - /// All Objects have a unique instance ID. - /// </summary> - /// <example> - /// <code> - /// public class MyNode : Node - /// { - /// public string foo = "bar"; - /// - /// public override void _Ready() - /// { - /// ulong id = GetInstanceId(); - /// var inst = (MyNode)GD.InstanceFromId(Id); - /// GD.Print(inst.foo); // Prints bar - /// } - /// } - /// </code> - /// </example> - /// <param name="instanceId">Instance ID of the Object to retrieve.</param> - /// <returns>The <see cref="Object"/> instance.</returns> - public static Object InstanceFromId(ulong instanceId) - { - return InteropUtils.UnmanagedGetManaged(NativeFuncs.godotsharp_instance_from_id(instanceId)); - } - - /// <summary> - /// Converts from linear energy to decibels (audio). - /// This can be used to implement volume sliders that behave as expected (since volume isn't linear). - /// </summary> - /// <seealso cref="DbToLinear(real_t)"/> - /// <example> - /// <code> - /// // "slider" refers to a node that inherits Range such as HSlider or VSlider. - /// // Its range must be configured to go from 0 to 1. - /// // Change the bus name if you'd like to change the volume of a specific bus only. - /// AudioServer.SetBusVolumeDb(AudioServer.GetBusIndex("Master"), GD.LinearToDb(slider.value)); - /// </code> - /// </example> - /// <param name="linear">The linear energy to convert.</param> - /// <returns>Audio as decibels.</returns> - public static real_t LinearToDb(real_t linear) - { - return (real_t)(Math.Log(linear) * 8.6858896380650365530225783783321); - } - - /// <summary> /// Loads a resource from the filesystem located at <paramref name="path"/>. /// The resource is loaded on the method call (unless it's referenced already /// elsewhere, e.g. in another script or in the scene), which might cause slight delay, @@ -185,57 +128,96 @@ namespace Godot return ResourceLoader.Load<T>(path); } - /// <summary> - /// Pushes an error message to Godot's built-in debugger and to the OS terminal. - /// - /// Note: Errors printed this way will not pause project execution. - /// </summary> - /// <example> - /// <code> - /// GD.PushError("test_error"); // Prints "test error" to debugger and terminal as error call - /// </code> - /// </example> - /// <param name="message">Error message.</param> - public static void PushError(string message) + private static string AppendPrintParams(object[] parameters) { - using var godotStr = Marshaling.ConvertStringToNative(message); - NativeFuncs.godotsharp_pusherror(godotStr); + if (parameters == null) + { + return "null"; + } + + var sb = new StringBuilder(); + for (int i = 0; i < parameters.Length; i++) + { + sb.Append(parameters[i]?.ToString() ?? "null"); + } + return sb.ToString(); + } + + private static string AppendPrintParams(char separator, object[] parameters) + { + if (parameters == null) + { + return "null"; + } + + var sb = new StringBuilder(); + for (int i = 0; i < parameters.Length; i++) + { + if (i != 0) + sb.Append(separator); + sb.Append(parameters[i]?.ToString() ?? "null"); + } + return sb.ToString(); } /// <summary> - /// Pushes a warning message to Godot's built-in debugger and to the OS terminal. + /// Prints a message to the console. + /// + /// Note: Consider using <see cref="PushError(string)"/> and <see cref="PushWarning(string)"/> + /// to print error and warning messages instead of <see cref="Print(string)"/>. + /// This distinguishes them from print messages used for debugging purposes, + /// while also displaying a stack trace when an error or warning is printed. /// </summary> - /// <example> - /// GD.PushWarning("test warning"); // Prints "test warning" to debugger and terminal as warning call - /// </example> - /// <param name="message">Warning message.</param> - public static void PushWarning(string message) + /// <param name="what">Message that will be printed.</param> + public static void Print(string what) { - using var godotStr = Marshaling.ConvertStringToNative(message); - NativeFuncs.godotsharp_pushwarning(godotStr); + using var godotStr = Marshaling.ConvertStringToNative(what); + NativeFuncs.godotsharp_print(godotStr); } /// <summary> /// Converts one or more arguments of any type to string in the best way possible /// and prints them to the console. /// - /// Note: Consider using <see cref="PushError(string)"/> and <see cref="PushWarning(string)"/> + /// Note: Consider using <see cref="PushError(object[])"/> and <see cref="PushWarning(object[])"/> /// to print error and warning messages instead of <see cref="Print(object[])"/>. /// This distinguishes them from print messages used for debugging purposes, /// while also displaying a stack trace when an error or warning is printed. /// </summary> /// <example> /// <code> - /// var a = new int[] { 1, 2, 3 }; + /// var a = new Godot.Collections.Array { 1, 2, 3 }; /// GD.Print("a", "b", a); // Prints ab[1, 2, 3] /// </code> /// </example> /// <param name="what">Arguments that will be printed.</param> public static void Print(params object[] what) { - string str = string.Concat(GetPrintParams(what)); - using var godotStr = Marshaling.ConvertStringToNative(str); - NativeFuncs.godotsharp_print(godotStr); + Print(AppendPrintParams(what)); + } + + /// <summary> + /// Prints a message to the console. + /// The following BBCode tags are supported: b, i, u, s, indent, code, url, center, + /// right, color, bgcolor, fgcolor. + /// Color tags only support named colors such as <c>red</c>, not hexadecimal color codes. + /// Unsupported tags will be left as-is in standard output. + /// When printing to standard output, the supported subset of BBCode is converted to + /// ANSI escape codes for the terminal emulator to display. Displaying ANSI escape codes + /// is currently only supported on Linux and macOS. Support for ANSI escape codes may vary + /// across terminal emulators, especially for italic and strikethrough. + /// + /// Note: Consider using <see cref="PushError(string)"/> and <see cref="PushWarning(string)"/> + /// to print error and warning messages instead of <see cref="Print(string)"/> or + /// <see cref="PrintRich(string)"/>. + /// This distinguishes them from print messages used for debugging purposes, + /// while also displaying a stack trace when an error or warning is printed. + /// </summary> + /// <param name="what">Message that will be printed.</param> + public static void PrintRich(string what) + { + using var godotStr = Marshaling.ConvertStringToNative(what); + NativeFuncs.godotsharp_print_rich(godotStr); } /// <summary> @@ -250,7 +232,7 @@ namespace Godot /// is currently only supported on Linux and macOS. Support for ANSI escape codes may vary /// across terminal emulators, especially for italic and strikethrough. /// - /// Note: Consider using <see cref="PushError(string)"/> and <see cref="PushWarning(string)"/> + /// Note: Consider using <see cref="PushError(object[])"/> and <see cref="PushWarning(object[])"/> /// to print error and warning messages instead of <see cref="Print(object[])"/> or /// <see cref="PrintRich(object[])"/>. /// This distinguishes them from print messages used for debugging purposes, @@ -258,23 +240,23 @@ namespace Godot /// </summary> /// <example> /// <code> - /// GD.PrintRich("[b]Hello world![/b]"); // Prints out "Hello world!" in bold. + /// GD.PrintRich("[code][b]Hello world![/b][/code]"); // Prints out: [b]Hello world![/b] /// </code> /// </example> /// <param name="what">Arguments that will be printed.</param> public static void PrintRich(params object[] what) { - string str = string.Concat(GetPrintParams(what)); - using var godotStr = Marshaling.ConvertStringToNative(str); - NativeFuncs.godotsharp_print_rich(godotStr); + PrintRich(AppendPrintParams(what)); } /// <summary> - /// Prints the current stack trace information to the console. + /// Prints a message to standard error line. /// </summary> - public static void PrintStack() + /// <param name="what">Message that will be printed.</param> + public static void PrintErr(string what) { - Print(System.Environment.StackTrace); + using var godotStr = Marshaling.ConvertStringToNative(what); + NativeFuncs.godotsharp_printerr(godotStr); } /// <summary> @@ -288,31 +270,36 @@ namespace Godot /// <param name="what">Arguments that will be printed.</param> public static void PrintErr(params object[] what) { - string str = string.Concat(GetPrintParams(what)); - using var godotStr = Marshaling.ConvertStringToNative(str); - NativeFuncs.godotsharp_printerr(godotStr); + PrintErr(AppendPrintParams(what)); } /// <summary> - /// Prints one or more arguments to strings in the best way possible to console. - /// No newline is added at the end. - /// - /// Note: Due to limitations with Godot's built-in console, this only prints to the terminal. - /// If you need to print in the editor, use another method, such as <see cref="Print(object[])"/>. + /// Prints a message to the OS terminal. + /// Unlike <see cref="Print(string)"/>, no newline is added at the end. + /// </summary> + /// <param name="what">Message that will be printed.</param> + public static void PrintRaw(string what) + { + using var godotStr = Marshaling.ConvertStringToNative(what); + NativeFuncs.godotsharp_printraw(godotStr); + } + + /// <summary> + /// Prints one or more arguments to strings in the best way possible to the OS terminal. + /// Unlike <see cref="Print(object[])"/>, no newline is added at the end. /// </summary> /// <example> /// <code> /// GD.PrintRaw("A"); /// GD.PrintRaw("B"); - /// // Prints AB + /// GD.PrintRaw("C"); + /// // Prints ABC to terminal /// </code> /// </example> /// <param name="what">Arguments that will be printed.</param> public static void PrintRaw(params object[] what) { - string str = string.Concat(GetPrintParams(what)); - using var godotStr = Marshaling.ConvertStringToNative(str); - NativeFuncs.godotsharp_printraw(godotStr); + PrintRaw(AppendPrintParams(what)); } /// <summary> @@ -326,8 +313,8 @@ namespace Godot /// <param name="what">Arguments that will be printed.</param> public static void PrintS(params object[] what) { - string str = string.Join(' ', GetPrintParams(what)); - using var godotStr = Marshaling.ConvertStringToNative(str); + string message = AppendPrintParams(' ', what); + using var godotStr = Marshaling.ConvertStringToNative(message); NativeFuncs.godotsharp_prints(godotStr); } @@ -342,12 +329,74 @@ namespace Godot /// <param name="what">Arguments that will be printed.</param> public static void PrintT(params object[] what) { - string str = string.Join('\t', GetPrintParams(what)); - using var godotStr = Marshaling.ConvertStringToNative(str); + string message = AppendPrintParams('\t', what); + using var godotStr = Marshaling.ConvertStringToNative(message); NativeFuncs.godotsharp_printt(godotStr); } /// <summary> + /// Pushes an error message to Godot's built-in debugger and to the OS terminal. + /// + /// Note: Errors printed this way will not pause project execution. + /// </summary> + /// <example> + /// <code> + /// GD.PushError("test error"); // Prints "test error" to debugger and terminal as error call + /// </code> + /// </example> + /// <param name="message">Error message.</param> + public static void PushError(string message) + { + using var godotStr = Marshaling.ConvertStringToNative(message); + NativeFuncs.godotsharp_pusherror(godotStr); + } + + /// <summary> + /// Pushes an error message to Godot's built-in debugger and to the OS terminal. + /// + /// Note: Errors printed this way will not pause project execution. + /// </summary> + /// <example> + /// <code> + /// GD.PushError("test_error"); // Prints "test error" to debugger and terminal as error call + /// </code> + /// </example> + /// <param name="what">Arguments that form the error message.</param> + public static void PushError(params object[] what) + { + PushError(AppendPrintParams(what)); + } + + /// <summary> + /// Pushes a warning message to Godot's built-in debugger and to the OS terminal. + /// </summary> + /// <example> + /// <code> + /// GD.PushWarning("test warning"); // Prints "test warning" to debugger and terminal as warning call + /// </code> + /// </example> + /// <param name="message">Warning message.</param> + public static void PushWarning(string message) + { + using var godotStr = Marshaling.ConvertStringToNative(message); + NativeFuncs.godotsharp_pushwarning(godotStr); + } + + /// <summary> + /// Pushes a warning message to Godot's built-in debugger and to the OS terminal. + /// </summary> + /// <example> + /// <code> + /// GD.PushWarning("test warning"); // Prints "test warning" to debugger and terminal as warning call + /// </code> + /// </example> + /// <param name="what">Arguments that form the warning message.</param> + public static void PushWarning(params object[] what) + { + PushWarning(AppendPrintParams(what)); + } + + /// <summary> /// Returns a random floating point value between <c>0.0</c> and <c>1.0</c> (inclusive). /// </summary> /// <example> @@ -362,7 +411,9 @@ namespace Godot } /// <summary> - /// Returns a normally-distributed pseudo-random number, using Box-Muller transform with the specified <c>mean</c> and a standard <c>deviation</c>. + /// Returns a normally-distributed pseudo-random floating point value + /// using Box-Muller transform with the specified <pararmref name="mean"/> + /// and a standard <paramref name="deviation"/>. /// This is also called Gaussian distribution. /// </summary> /// <returns>A random normally-distributed <see langword="float"/> number.</returns> @@ -373,7 +424,8 @@ namespace Godot /// <summary> /// Returns a random unsigned 32-bit integer. - /// Use remainder to obtain a random value in the interval <c>[0, N - 1]</c> (where N is smaller than 2^32). + /// Use remainder to obtain a random value in the interval <c>[0, N - 1]</c> + /// (where N is smaller than 2^32). /// </summary> /// <example> /// <code> @@ -391,11 +443,11 @@ namespace Godot /// <summary> /// Randomizes the seed (or the internal state) of the random number generator. - /// Current implementation reseeds using a number based on time. + /// The current implementation uses a number based on the device's time. /// /// Note: This method is called automatically when the project is run. - /// If you need to fix the seed to have reproducible results, use <see cref="Seed(ulong)"/> - /// to initialize the random number generator. + /// If you need to fix the seed to have consistent, reproducible results, + /// use <see cref="Seed(ulong)"/> to initialize the random number generator. /// </summary> public static void Randomize() { @@ -403,12 +455,13 @@ namespace Godot } /// <summary> - /// Returns a random floating point value on the interval between <paramref name="from"/> + /// Returns a random floating point value between <paramref name="from"/> /// and <paramref name="to"/> (inclusive). /// </summary> /// <example> /// <code> - /// GD.PrintS(GD.RandRange(-10.0, 10.0), GD.RandRange(-10.0, 10.0)); // Prints e.g. -3.844535 7.45315 + /// GD.RandRange(0.0, 20.5); // Returns e.g. 7.45315 + /// GD.RandRange(-10.0, 10.0); // Returns e.g. -3.844535 /// </code> /// </example> /// <returns>A random <see langword="double"/> number inside the given range.</returns> @@ -424,8 +477,8 @@ namespace Godot /// </summary> /// <example> /// <code> - /// GD.Print(GD.RandRange(0, 1)); // Prints 0 or 1 - /// GD.Print(GD.RandRange(-10, 1000)); // Prints any number from -10 to 1000 + /// GD.RandRange(0, 1); // Returns either 0 or 1 + /// GD.RandRange(-10, 1000); // Returns random integer between -10 and 1000 /// </code> /// </example> /// <returns>A random <see langword="int"/> number inside the given range.</returns> @@ -435,8 +488,18 @@ namespace Godot } /// <summary> - /// Returns a random unsigned 32-bit integer, using the given <paramref name="seed"/>. + /// Given a <paramref name="seed"/>, returns a randomized <see langword="uint"/> + /// value. The <paramref name="seed"/> may be modified. + /// Passing the same <paramref name="seed"/> consistently returns the same value. + /// + /// Note: "Seed" here refers to the internal state of the pseudo random number + /// generator, currently implemented as a 64 bit integer. /// </summary> + /// <example> + /// <code> + /// var a = GD.RandFromSeed(4); + /// </code> + /// </example> /// <param name="seed"> /// Seed to use to generate the random number. /// If a different seed is used, its value will be modified. @@ -449,7 +512,8 @@ namespace Godot /// <summary> /// Returns a <see cref="IEnumerable{T}"/> that iterates from - /// <c>0</c> to <paramref name="end"/> in steps of <c>1</c>. + /// <c>0</c> (inclusive) to <paramref name="end"/> (exclusive) + /// in steps of <c>1</c>. /// </summary> /// <param name="end">The last index.</param> public static IEnumerable<int> Range(int end) @@ -459,7 +523,8 @@ namespace Godot /// <summary> /// Returns a <see cref="IEnumerable{T}"/> that iterates from - /// <paramref name="start"/> to <paramref name="end"/> in steps of <c>1</c>. + /// <paramref name="start"/> (inclusive) to <paramref name="end"/> (exclusive) + /// in steps of <c>1</c>. /// </summary> /// <param name="start">The first index.</param> /// <param name="end">The last index.</param> @@ -470,13 +535,21 @@ namespace Godot /// <summary> /// Returns a <see cref="IEnumerable{T}"/> that iterates from - /// <paramref name="start"/> to <paramref name="end"/> in steps of <paramref name="step"/>. + /// <paramref name="start"/> (inclusive) to <paramref name="end"/> (exclusive) + /// in steps of <paramref name="step"/>. + /// The argument <paramref name="step"/> can be negative, but not <c>0</c>. /// </summary> + /// <exception cref="ArgumentException"> + /// <paramref name="step"/> is 0. + /// </exception> /// <param name="start">The first index.</param> /// <param name="end">The last index.</param> /// <param name="step">The amount by which to increment the index on each iteration.</param> public static IEnumerable<int> Range(int start, int end, int step) { + if (step == 0) + throw new ArgumentException("step cannot be 0.", nameof(step)); + if (end < start && step > 0) yield break; @@ -496,8 +569,20 @@ namespace Godot } /// <summary> - /// Sets seed for the random number generator. + /// Sets seed for the random number generator to <paramref name="seed"/>. + /// Setting the seed manually can ensure consistent, repeatable results for + /// most random functions. /// </summary> + /// <example> + /// <code> + /// ulong mySeed = (ulong)GD.Hash("Godot Rocks"); + /// GD.Seed(mySeed); + /// var a = GD.Randf() + GD.Randi(); + /// GD.Seed(mySeed); + /// var b = GD.Randf() + GD.Randi(); + /// // a and b are now identical + /// </code> + /// </example> /// <param name="seed">Seed that will be used.</param> public static void Seed(ulong seed) { @@ -505,26 +590,14 @@ namespace Godot } /// <summary> - /// Converts one or more arguments of any type to string in the best way possible. - /// </summary> - /// <param name="what">Arguments that will converted to string.</param> - /// <returns>The string formed by the given arguments.</returns> - public static string Str(params Variant[] what) - { - using var whatGodot = new Godot.Collections.Array(what); - NativeFuncs.godotsharp_str((godot_array)whatGodot.NativeValue, out godot_string ret); - using (ret) - return Marshaling.ConvertStringToManaged(ret); - } - - /// <summary> - /// Converts a formatted string that was returned by <see cref="VarToStr(Variant)"/> to the original value. + /// Converts a formatted string that was returned by <see cref="VarToStr(Variant)"/> + /// to the original value. /// </summary> /// <example> /// <code> - /// string a = "{\"a\": 1, \"b\": 2 }"; - /// var b = (Godot.Collections.Dictionary)GD.StrToVar(a); - /// GD.Print(b["a"]); // Prints 1 + /// string a = "{ \"a\": 1, \"b\": 2 }"; // a is a string + /// var b = GD.StrToVar(a).AsGodotDictionary(); // b is a Dictionary + /// GD.Print(b["a"]); // Prints 1 /// </code> /// </example> /// <param name="str">String that will be converted to Variant.</param> @@ -537,38 +610,49 @@ namespace Godot } /// <summary> - /// Encodes a <c>Variant</c> value to a byte array. - /// If <paramref name="fullObjects"/> is <see langword="true"/> encoding objects is allowed - /// (and can potentially include code). - /// Deserialization can be done with <see cref="BytesToVar(Span{byte}, bool)"/>. + /// Encodes a <see cref="Variant"/> value to a byte array, without encoding objects. + /// Deserialization can be done with <see cref="BytesToVar"/>. + /// Note: If you need object serialization, see <see cref="VarToBytesWithObjects"/>. + /// </summary> + /// <param name="var"><see cref="Variant"/> that will be encoded.</param> + /// <returns>The <see cref="Variant"/> encoded as an array of bytes.</returns> + public static byte[] VarToBytes(Variant var) + { + NativeFuncs.godotsharp_var_to_bytes((godot_variant)var.NativeVar, godot_bool.False, out var varBytes); + using (varBytes) + return Marshaling.ConvertNativePackedByteArrayToSystemArray(varBytes); + } + + /// <summary> + /// Encodes a <see cref="Variant"/>. Encoding objects is allowed (and can potentially + /// include executable code). Deserialization can be done with <see cref="BytesToVarWithObjects"/>. /// </summary> - /// <param name="var">Variant that will be encoded.</param> - /// <param name="fullObjects">If objects should be serialized.</param> - /// <returns>The <c>Variant</c> encoded as an array of bytes.</returns> - public static byte[] VarToBytes(Variant var, bool fullObjects = false) + /// <param name="var"><see cref="Variant"/> that will be encoded.</param> + /// <returns>The <see cref="Variant"/> encoded as an array of bytes.</returns> + public static byte[] VarToBytesWithObjects(Variant var) { - NativeFuncs.godotsharp_var_to_bytes((godot_variant)var.NativeVar, fullObjects.ToGodotBool(), out var varBytes); + NativeFuncs.godotsharp_var_to_bytes((godot_variant)var.NativeVar, godot_bool.True, out var varBytes); using (varBytes) return Marshaling.ConvertNativePackedByteArrayToSystemArray(varBytes); } /// <summary> - /// Converts a <c>Variant</c> <paramref name="var"/> to a formatted string that + /// Converts a <see cref="Variant"/> <paramref name="var"/> to a formatted string that /// can later be parsed using <see cref="StrToVar(string)"/>. /// </summary> /// <example> /// <code> /// var a = new Godot.Collections.Dictionary { ["a"] = 1, ["b"] = 2 }; /// GD.Print(GD.VarToStr(a)); - /// // Prints + /// // Prints: /// // { - /// // "a": 1, - /// // "b": 2 + /// // "a": 1, + /// // "b": 2 /// // } /// </code> /// </example> /// <param name="var">Variant that will be converted to string.</param> - /// <returns>The <c>Variant</c> encoded as a string.</returns> + /// <returns>The <see cref="Variant"/> encoded as a string.</returns> public static string VarToStr(Variant var) { NativeFuncs.godotsharp_var_to_str((godot_variant)var.NativeVar, out godot_string ret); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs index 1b599beab5..027eab30fc 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs @@ -1,13 +1,13 @@ +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; namespace Godot { - public class GodotSynchronizationContext : SynchronizationContext + public sealed class GodotSynchronizationContext : SynchronizationContext, IDisposable { - private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> _queue = - new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>(); + private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> _queue = new(); public override void Post(SendOrPostCallback d, object state) { @@ -24,5 +24,10 @@ namespace Godot workItem.Key(workItem.Value); } } + + public void Dispose() + { + _queue.Dispose(); + } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs index 408bed71b2..f6c36455b2 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs @@ -10,7 +10,7 @@ namespace Godot /// GodotTaskScheduler contains a linked list of tasks to perform as a queue. Methods /// within the class are used to control the queue and perform the contained tasks. /// </summary> - public class GodotTaskScheduler : TaskScheduler + public sealed class GodotTaskScheduler : TaskScheduler, IDisposable { /// <summary> /// The current synchronization context. @@ -108,5 +108,10 @@ namespace Godot } } } + + public void Dispose() + { + Context.Dispose(); + } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs index 3f9e986f62..ca0032df73 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; namespace Godot { @@ -35,15 +36,18 @@ namespace Godot public const real_t NaN = real_t.NaN; // 0.0174532924f and 0.0174532925199433 - private const real_t _degToRadConst = (real_t)0.0174532925199432957692369077M; + private const float _degToRadConstF = (float)0.0174532925199432957692369077M; + private const double _degToRadConstD = (double)0.0174532925199432957692369077M; // 57.29578f and 57.2957795130823 - private const real_t _radToDegConst = (real_t)57.295779513082320876798154814M; + private const float _radToDegConstF = (float)57.295779513082320876798154814M; + private const double _radToDegConstD = (double)57.295779513082320876798154814M; /// <summary> /// Returns the absolute value of <paramref name="s"/> (i.e. positive value). /// </summary> /// <param name="s">The input number.</param> /// <returns>The absolute value of <paramref name="s"/>.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Abs(int s) { return Math.Abs(s); @@ -54,7 +58,19 @@ namespace Godot /// </summary> /// <param name="s">The input number.</param> /// <returns>The absolute value of <paramref name="s"/>.</returns> - public static real_t Abs(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Abs(float s) + { + return Math.Abs(s); + } + + /// <summary> + /// Returns the absolute value of <paramref name="s"/> (i.e. positive value). + /// </summary> + /// <param name="s">The input number.</param> + /// <returns>The absolute value of <paramref name="s"/>.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Abs(double s) { return Math.Abs(s); } @@ -67,9 +83,38 @@ namespace Godot /// <returns> /// An angle that would result in the given cosine value. On the range <c>0</c> to <c>Tau/2</c>. /// </returns> - public static real_t Acos(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Acos(float s) { - return (real_t)Math.Acos(s); + return MathF.Acos(s); + } + + /// <summary> + /// Returns the arc cosine of <paramref name="s"/> in radians. + /// Use to get the angle of cosine <paramref name="s"/>. + /// </summary> + /// <param name="s">The input cosine value. Must be on the range of -1.0 to 1.0.</param> + /// <returns> + /// An angle that would result in the given cosine value. On the range <c>0</c> to <c>Tau/2</c>. + /// </returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Acos(double s) + { + return Math.Acos(s); + } + + /// <summary> + /// Returns the arc sine of <paramref name="s"/> in radians. + /// Use to get the angle of sine <paramref name="s"/>. + /// </summary> + /// <param name="s">The input sine value. Must be on the range of -1.0 to 1.0.</param> + /// <returns> + /// An angle that would result in the given sine value. On the range <c>-Tau/4</c> to <c>Tau/4</c>. + /// </returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Asin(float s) + { + return MathF.Asin(s); } /// <summary> @@ -80,9 +125,10 @@ namespace Godot /// <returns> /// An angle that would result in the given sine value. On the range <c>-Tau/4</c> to <c>Tau/4</c>. /// </returns> - public static real_t Asin(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Asin(double s) { - return (real_t)Math.Asin(s); + return Math.Asin(s); } /// <summary> @@ -90,15 +136,51 @@ namespace Godot /// Use to get the angle of tangent <paramref name="s"/>. /// /// The method cannot know in which quadrant the angle should fall. - /// See <see cref="Atan2(real_t, real_t)"/> if you have both <c>y</c> and <c>x</c>. + /// See <see cref="Atan2(float, float)"/> if you have both <c>y</c> and <c>x</c>. /// </summary> /// <param name="s">The input tangent value.</param> /// <returns> /// An angle that would result in the given tangent value. On the range <c>-Tau/4</c> to <c>Tau/4</c>. /// </returns> - public static real_t Atan(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Atan(float s) { - return (real_t)Math.Atan(s); + return MathF.Atan(s); + } + + /// <summary> + /// Returns the arc tangent of <paramref name="s"/> in radians. + /// Use to get the angle of tangent <paramref name="s"/>. + /// + /// The method cannot know in which quadrant the angle should fall. + /// See <see cref="Atan2(double, double)"/> if you have both <c>y</c> and <c>x</c>. + /// </summary> + /// <param name="s">The input tangent value.</param> + /// <returns> + /// An angle that would result in the given tangent value. On the range <c>-Tau/4</c> to <c>Tau/4</c>. + /// </returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Atan(double s) + { + return Math.Atan(s); + } + + /// <summary> + /// Returns the arc tangent of <paramref name="y"/> and <paramref name="x"/> in radians. + /// Use to get the angle of the tangent of <c>y/x</c>. To compute the value, the method takes into + /// account the sign of both arguments in order to determine the quadrant. + /// + /// Important note: The Y coordinate comes first, by convention. + /// </summary> + /// <param name="y">The Y coordinate of the point to find the angle to.</param> + /// <param name="x">The X coordinate of the point to find the angle to.</param> + /// <returns> + /// An angle that would result in the given tangent value. On the range <c>-Tau/2</c> to <c>Tau/2</c>. + /// </returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Atan2(float y, float x) + { + return MathF.Atan2(y, x); } /// <summary> @@ -113,9 +195,21 @@ namespace Godot /// <returns> /// An angle that would result in the given tangent value. On the range <c>-Tau/2</c> to <c>Tau/2</c>. /// </returns> - public static real_t Atan2(real_t y, real_t x) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Atan2(double y, double x) + { + return Math.Atan2(y, x); + } + + /// <summary> + /// Rounds <paramref name="s"/> upward (towards positive infinity). + /// </summary> + /// <param name="s">The number to ceil.</param> + /// <returns>The smallest whole number that is not less than <paramref name="s"/>.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Ceil(float s) { - return (real_t)Math.Atan2(y, x); + return MathF.Ceiling(s); } /// <summary> @@ -123,9 +217,10 @@ namespace Godot /// </summary> /// <param name="s">The number to ceil.</param> /// <returns>The smallest whole number that is not less than <paramref name="s"/>.</returns> - public static real_t Ceil(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Ceil(double s) { - return (real_t)Math.Ceiling(s); + return Math.Ceiling(s); } /// <summary> @@ -136,9 +231,10 @@ namespace Godot /// <param name="min">The minimum allowed value.</param> /// <param name="max">The maximum allowed value.</param> /// <returns>The clamped value.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Clamp(int value, int min, int max) { - return value < min ? min : value > max ? max : value; + return Math.Clamp(value, min, max); } /// <summary> @@ -149,9 +245,35 @@ namespace Godot /// <param name="min">The minimum allowed value.</param> /// <param name="max">The maximum allowed value.</param> /// <returns>The clamped value.</returns> - public static real_t Clamp(real_t value, real_t min, real_t max) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Clamp(float value, float min, float max) { - return value < min ? min : value > max ? max : value; + return Math.Clamp(value, min, max); + } + + /// <summary> + /// Clamps a <paramref name="value"/> so that it is not less than <paramref name="min"/> + /// and not more than <paramref name="max"/>. + /// </summary> + /// <param name="value">The value to clamp.</param> + /// <param name="min">The minimum allowed value.</param> + /// <param name="max">The maximum allowed value.</param> + /// <returns>The clamped value.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Clamp(double value, double min, double max) + { + return Math.Clamp(value, min, max); + } + + /// <summary> + /// Returns the cosine of angle <paramref name="s"/> in radians. + /// </summary> + /// <param name="s">The angle in radians.</param> + /// <returns>The cosine of that angle.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Cos(float s) + { + return MathF.Cos(s); } /// <summary> @@ -159,9 +281,21 @@ namespace Godot /// </summary> /// <param name="s">The angle in radians.</param> /// <returns>The cosine of that angle.</returns> - public static real_t Cos(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Cos(double s) + { + return Math.Cos(s); + } + + /// <summary> + /// Returns the hyperbolic cosine of angle <paramref name="s"/> in radians. + /// </summary> + /// <param name="s">The angle in radians.</param> + /// <returns>The hyperbolic cosine of that angle.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Cosh(float s) { - return (real_t)Math.Cos(s); + return MathF.Cosh(s); } /// <summary> @@ -169,9 +303,10 @@ namespace Godot /// </summary> /// <param name="s">The angle in radians.</param> /// <returns>The hyperbolic cosine of that angle.</returns> - public static real_t Cosh(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Cosh(double s) { - return (real_t)Math.Cosh(s); + return Math.Cosh(s); } /// <summary> @@ -184,7 +319,7 @@ namespace Godot /// <param name="post">The value which after "to" value for interpolation.</param> /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> /// <returns>The resulting value of the interpolation.</returns> - public static real_t CubicInterpolate(real_t from, real_t to, real_t pre, real_t post, real_t weight) + public static float CubicInterpolate(float from, float to, float pre, float post, float weight) { return 0.5f * ((from * 2.0f) + @@ -194,9 +329,55 @@ namespace Godot } /// <summary> + /// Cubic interpolates between two values by the factor defined in <paramref name="weight"/> + /// with pre and post values. + /// </summary> + /// <param name="from">The start value for interpolation.</param> + /// <param name="to">The destination value for interpolation.</param> + /// <param name="pre">The value which before "from" value for interpolation.</param> + /// <param name="post">The value which after "to" value for interpolation.</param> + /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> + /// <returns>The resulting value of the interpolation.</returns> + public static double CubicInterpolate(double from, double to, double pre, double post, double weight) + { + return 0.5 * + ((from * 2.0) + + (-pre + to) * weight + + (2.0 * pre - 5.0 * from + 4.0 * to - post) * (weight * weight) + + (-pre + 3.0 * from - 3.0 * to + post) * (weight * weight * weight)); + } + + /// <summary> + /// Cubic interpolates between two rotation values with shortest path + /// by the factor defined in <paramref name="weight"/> with pre and post values. + /// See also <see cref="LerpAngle(float, float, float)"/>. + /// </summary> + /// <param name="from">The start value for interpolation.</param> + /// <param name="to">The destination value for interpolation.</param> + /// <param name="pre">The value which before "from" value for interpolation.</param> + /// <param name="post">The value which after "to" value for interpolation.</param> + /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> + /// <returns>The resulting value of the interpolation.</returns> + public static float CubicInterpolateAngle(float from, float to, float pre, float post, float weight) + { + float fromRot = from % MathF.Tau; + + float preDiff = (pre - fromRot) % MathF.Tau; + float preRot = fromRot + (2.0f * preDiff) % MathF.Tau - preDiff; + + float toDiff = (to - fromRot) % MathF.Tau; + float toRot = fromRot + (2.0f * toDiff) % MathF.Tau - toDiff; + + float postDiff = (post - toRot) % MathF.Tau; + float postRot = toRot + (2.0f * postDiff) % MathF.Tau - postDiff; + + return CubicInterpolate(fromRot, toRot, preRot, postRot, weight); + } + + /// <summary> /// Cubic interpolates between two rotation values with shortest path /// by the factor defined in <paramref name="weight"/> with pre and post values. - /// See also <see cref="LerpAngle"/>. + /// See also <see cref="LerpAngle(double, double, double)"/>. /// </summary> /// <param name="from">The start value for interpolation.</param> /// <param name="to">The destination value for interpolation.</param> @@ -204,18 +385,18 @@ namespace Godot /// <param name="post">The value which after "to" value for interpolation.</param> /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> /// <returns>The resulting value of the interpolation.</returns> - public static real_t CubicInterpolateAngle(real_t from, real_t to, real_t pre, real_t post, real_t weight) + public static double CubicInterpolateAngle(double from, double to, double pre, double post, double weight) { - real_t fromRot = from % Mathf.Tau; + double fromRot = from % Math.Tau; - real_t preDiff = (pre - fromRot) % Mathf.Tau; - real_t preRot = fromRot + (2.0f * preDiff) % Mathf.Tau - preDiff; + double preDiff = (pre - fromRot) % Math.Tau; + double preRot = fromRot + (2.0 * preDiff) % Math.Tau - preDiff; - real_t toDiff = (to - fromRot) % Mathf.Tau; - real_t toRot = fromRot + (2.0f * toDiff) % Mathf.Tau - toDiff; + double toDiff = (to - fromRot) % Math.Tau; + double toRot = fromRot + (2.0 * toDiff) % Math.Tau - toDiff; - real_t postDiff = (post - toRot) % Mathf.Tau; - real_t postRot = toRot + (2.0f * postDiff) % Mathf.Tau - postDiff; + double postDiff = (post - toRot) % Math.Tau; + double postRot = toRot + (2.0 * postDiff) % Math.Tau - postDiff; return CubicInterpolate(fromRot, toRot, preRot, postRot, weight); } @@ -223,7 +404,8 @@ namespace Godot /// <summary> /// Cubic interpolates between two values by the factor defined in <paramref name="weight"/> /// with pre and post values. - /// It can perform smoother interpolation than <see cref="CubicInterpolate"/> + /// It can perform smoother interpolation than + /// <see cref="CubicInterpolate(float, float, float, float, float)"/> /// by the time values. /// </summary> /// <param name="from">The start value for interpolation.</param> @@ -235,23 +417,52 @@ namespace Godot /// <param name="preT"></param> /// <param name="postT"></param> /// <returns>The resulting value of the interpolation.</returns> - public static real_t CubicInterpolateInTime(real_t from, real_t to, real_t pre, real_t post, real_t weight, real_t toT, real_t preT, real_t postT) + public static float CubicInterpolateInTime(float from, float to, float pre, float post, float weight, float toT, float preT, float postT) { /* Barry-Goldman method */ - real_t t = Lerp(0.0f, toT, weight); - real_t a1 = Lerp(pre, from, preT == 0 ? 0.0f : (t - preT) / -preT); - real_t a2 = Lerp(from, to, toT == 0 ? 0.5f : t / toT); - real_t a3 = Lerp(to, post, postT - toT == 0 ? 1.0f : (t - toT) / (postT - toT)); - real_t b1 = Lerp(a1, a2, toT - preT == 0 ? 0.0f : (t - preT) / (toT - preT)); - real_t b2 = Lerp(a2, a3, postT == 0 ? 1.0f : t / postT); + float t = Lerp(0.0f, toT, weight); + float a1 = Lerp(pre, from, preT == 0 ? 0.0f : (t - preT) / -preT); + float a2 = Lerp(from, to, toT == 0 ? 0.5f : t / toT); + float a3 = Lerp(to, post, postT - toT == 0 ? 1.0f : (t - toT) / (postT - toT)); + float b1 = Lerp(a1, a2, toT - preT == 0 ? 0.0f : (t - preT) / (toT - preT)); + float b2 = Lerp(a2, a3, postT == 0 ? 1.0f : t / postT); return Lerp(b1, b2, toT == 0 ? 0.5f : t / toT); } /// <summary> + /// Cubic interpolates between two values by the factor defined in <paramref name="weight"/> + /// with pre and post values. + /// It can perform smoother interpolation than + /// <see cref="CubicInterpolate(double, double, double, double, double)"/> + /// by the time values. + /// </summary> + /// <param name="from">The start value for interpolation.</param> + /// <param name="to">The destination value for interpolation.</param> + /// <param name="pre">The value which before "from" value for interpolation.</param> + /// <param name="post">The value which after "to" value for interpolation.</param> + /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> + /// <param name="toT"></param> + /// <param name="preT"></param> + /// <param name="postT"></param> + /// <returns>The resulting value of the interpolation.</returns> + public static double CubicInterpolateInTime(double from, double to, double pre, double post, double weight, double toT, double preT, double postT) + { + /* Barry-Goldman method */ + double t = Lerp(0.0, toT, weight); + double a1 = Lerp(pre, from, preT == 0 ? 0.0 : (t - preT) / -preT); + double a2 = Lerp(from, to, toT == 0 ? 0.5 : t / toT); + double a3 = Lerp(to, post, postT - toT == 0 ? 1.0 : (t - toT) / (postT - toT)); + double b1 = Lerp(a1, a2, toT - preT == 0 ? 0.0 : (t - preT) / (toT - preT)); + double b2 = Lerp(a2, a3, postT == 0 ? 1.0 : t / postT); + return Lerp(b1, b2, toT == 0 ? 0.5 : t / toT); + } + + /// <summary> /// Cubic interpolates between two rotation values with shortest path /// by the factor defined in <paramref name="weight"/> with pre and post values. - /// See also <see cref="LerpAngle"/>. - /// It can perform smoother interpolation than <see cref="CubicInterpolateAngle"/> + /// See also <see cref="LerpAngle(float, float, float)"/>. + /// It can perform smoother interpolation than + /// <see cref="CubicInterpolateAngle(float, float, float, float, float)"/> /// by the time values. /// </summary> /// <param name="from">The start value for interpolation.</param> @@ -263,24 +474,78 @@ namespace Godot /// <param name="preT"></param> /// <param name="postT"></param> /// <returns>The resulting value of the interpolation.</returns> - public static real_t CubicInterpolateAngleInTime(real_t from, real_t to, real_t pre, real_t post, real_t weight, - real_t toT, real_t preT, real_t postT) + public static float CubicInterpolateAngleInTime(float from, float to, float pre, float post, float weight, float toT, float preT, float postT) { - real_t fromRot = from % Mathf.Tau; + float fromRot = from % MathF.Tau; - real_t preDiff = (pre - fromRot) % Mathf.Tau; - real_t preRot = fromRot + (2.0f * preDiff) % Mathf.Tau - preDiff; + float preDiff = (pre - fromRot) % MathF.Tau; + float preRot = fromRot + (2.0f * preDiff) % MathF.Tau - preDiff; - real_t toDiff = (to - fromRot) % Mathf.Tau; - real_t toRot = fromRot + (2.0f * toDiff) % Mathf.Tau - toDiff; + float toDiff = (to - fromRot) % MathF.Tau; + float toRot = fromRot + (2.0f * toDiff) % MathF.Tau - toDiff; - real_t postDiff = (post - toRot) % Mathf.Tau; - real_t postRot = toRot + (2.0f * postDiff) % Mathf.Tau - postDiff; + float postDiff = (post - toRot) % MathF.Tau; + float postRot = toRot + (2.0f * postDiff) % MathF.Tau - postDiff; return CubicInterpolateInTime(fromRot, toRot, preRot, postRot, weight, toT, preT, postT); } /// <summary> + /// Cubic interpolates between two rotation values with shortest path + /// by the factor defined in <paramref name="weight"/> with pre and post values. + /// See also <see cref="LerpAngle(double, double, double)"/>. + /// It can perform smoother interpolation than + /// <see cref="CubicInterpolateAngle(double, double, double, double, double)"/> + /// by the time values. + /// </summary> + /// <param name="from">The start value for interpolation.</param> + /// <param name="to">The destination value for interpolation.</param> + /// <param name="pre">The value which before "from" value for interpolation.</param> + /// <param name="post">The value which after "to" value for interpolation.</param> + /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> + /// <param name="toT"></param> + /// <param name="preT"></param> + /// <param name="postT"></param> + /// <returns>The resulting value of the interpolation.</returns> + public static double CubicInterpolateAngleInTime(double from, double to, double pre, double post, double weight, double toT, double preT, double postT) + { + double fromRot = from % Math.Tau; + + double preDiff = (pre - fromRot) % Math.Tau; + double preRot = fromRot + (2.0 * preDiff) % Math.Tau - preDiff; + + double toDiff = (to - fromRot) % Math.Tau; + double toRot = fromRot + (2.0 * toDiff) % Math.Tau - toDiff; + + double postDiff = (post - toRot) % Math.Tau; + double postRot = toRot + (2.0 * postDiff) % Math.Tau - postDiff; + + return CubicInterpolateInTime(fromRot, toRot, preRot, postRot, weight, toT, preT, postT); + } + + /// <summary> + /// Returns the point at the given <paramref name="t"/> on a one-dimensional Bezier curve defined by + /// the given <paramref name="control1"/>, <paramref name="control2"/>, and <paramref name="end"/> points. + /// </summary> + /// <param name="start">The start value for the interpolation.</param> + /// <param name="control1">Control point that defines the bezier curve.</param> + /// <param name="control2">Control point that defines the bezier curve.</param> + /// <param name="end">The destination value for the interpolation.</param> + /// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> + /// <returns>The resulting value of the interpolation.</returns> + public static float BezierInterpolate(float start, float control1, float control2, float end, float t) + { + // Formula from Wikipedia article on Bezier curves + float omt = 1.0f - t; + float omt2 = omt * omt; + float omt3 = omt2 * omt; + float t2 = t * t; + float t3 = t2 * t; + + return start * omt3 + control1 * omt2 * t * 3.0f + control2 * omt * t2 * 3.0f + end * t3; + } + + /// <summary> /// Returns the point at the given <paramref name="t"/> on a one-dimensional Bezier curve defined by /// the given <paramref name="control1"/>, <paramref name="control2"/>, and <paramref name="end"/> points. /// </summary> @@ -290,16 +555,16 @@ namespace Godot /// <param name="end">The destination value for the interpolation.</param> /// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> /// <returns>The resulting value of the interpolation.</returns> - public static real_t BezierInterpolate(real_t start, real_t control1, real_t control2, real_t end, real_t t) + public static double BezierInterpolate(double start, double control1, double control2, double end, double t) { // Formula from Wikipedia article on Bezier curves - real_t omt = 1 - t; - real_t omt2 = omt * omt; - real_t omt3 = omt2 * omt; - real_t t2 = t * t; - real_t t3 = t2 * t; + double omt = 1.0 - t; + double omt2 = omt * omt; + double omt3 = omt2 * omt; + double t2 = t * t; + double t3 = t2 * t; - return start * omt3 + control1 * omt2 * t * 3 + control2 * omt * t2 * 3 + end * t3; + return start * omt3 + control1 * omt2 * t * 3.0 + control2 * omt * t2 * 3.0 + end * t3; } /// <summary> @@ -312,25 +577,78 @@ namespace Godot /// <param name="end">The destination value for the interpolation.</param> /// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> /// <returns>The resulting value of the interpolation.</returns> - public static real_t BezierDerivative(real_t start, real_t control1, real_t control2, real_t end, real_t t) + public static float BezierDerivative(float start, float control1, float control2, float end, float t) { // Formula from Wikipedia article on Bezier curves - real_t omt = 1 - t; - real_t omt2 = omt * omt; - real_t t2 = t * t; + float omt = 1.0f - t; + float omt2 = omt * omt; + float t2 = t * t; - real_t d = (control1 - start) * 3 * omt2 + (control2 - control1) * 6 * omt * t + (end - control2) * 3 * t2; + float d = (control1 - start) * 3.0f * omt2 + (control2 - control1) * 6.0f * omt * t + (end - control2) * 3.0f * t2; return d; } /// <summary> + /// Returns the derivative at the given <paramref name="t"/> on a one dimensional Bezier curve defined by + /// the given <paramref name="control1"/>, <paramref name="control2"/>, and <paramref name="end"/> points. + /// </summary> + /// <param name="start">The start value for the interpolation.</param> + /// <param name="control1">Control point that defines the bezier curve.</param> + /// <param name="control2">Control point that defines the bezier curve.</param> + /// <param name="end">The destination value for the interpolation.</param> + /// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> + /// <returns>The resulting value of the interpolation.</returns> + public static double BezierDerivative(double start, double control1, double control2, double end, double t) + { + // Formula from Wikipedia article on Bezier curves + double omt = 1.0 - t; + double omt2 = omt * omt; + double t2 = t * t; + + double d = (control1 - start) * 3.0 * omt2 + (control2 - control1) * 6.0 * omt * t + (end - control2) * 3.0 * t2; + return d; + } + + /// <summary> + /// Converts from decibels to linear energy (audio). + /// </summary> + /// <seealso cref="LinearToDb(float)"/> + /// <param name="db">Decibels to convert.</param> + /// <returns>Audio volume as linear energy.</returns> + public static float DbToLinear(float db) + { + return MathF.Exp(db * 0.11512925464970228420089957273422f); + } + + /// <summary> + /// Converts from decibels to linear energy (audio). + /// </summary> + /// <seealso cref="LinearToDb(double)"/> + /// <param name="db">Decibels to convert.</param> + /// <returns>Audio volume as linear energy.</returns> + public static double DbToLinear(double db) + { + return Math.Exp(db * 0.11512925464970228420089957273422); + } + + /// <summary> + /// Converts an angle expressed in degrees to radians. + /// </summary> + /// <param name="deg">An angle expressed in degrees.</param> + /// <returns>The same angle expressed in radians.</returns> + public static float DegToRad(float deg) + { + return deg * _degToRadConstF; + } + + /// <summary> /// Converts an angle expressed in degrees to radians. /// </summary> /// <param name="deg">An angle expressed in degrees.</param> /// <returns>The same angle expressed in radians.</returns> - public static real_t DegToRad(real_t deg) + public static double DegToRad(double deg) { - return deg * _degToRadConst; + return deg * _degToRadConstD; } /// <summary> @@ -343,38 +661,94 @@ namespace Godot /// <c>0</c> is constant, <c>1</c> is linear, <c>0</c> to <c>1</c> is ease-in, <c>1</c> or more is ease-out. /// </param> /// <returns>The eased value.</returns> - public static real_t Ease(real_t s, real_t curve) + public static float Ease(float s, float curve) { - if (s < 0f) + if (s < 0.0f) { - s = 0f; + s = 0.0f; } else if (s > 1.0f) { s = 1.0f; } - if (curve > 0f) + if (curve > 0.0f) { if (curve < 1.0f) { - return 1.0f - Pow(1.0f - s, 1.0f / curve); + return 1.0f - MathF.Pow(1.0f - s, 1.0f / curve); } - return Pow(s, curve); + return MathF.Pow(s, curve); } - if (curve < 0f) + if (curve < 0.0f) { if (s < 0.5f) { - return Pow(s * 2.0f, -curve) * 0.5f; + return MathF.Pow(s * 2.0f, -curve) * 0.5f; + } + + return ((1.0f - MathF.Pow(1.0f - ((s - 0.5f) * 2.0f), -curve)) * 0.5f) + 0.5f; + } + + return 0.0f; + } + + /// <summary> + /// Easing function, based on exponent. The <paramref name="curve"/> values are: + /// <c>0</c> is constant, <c>1</c> is linear, <c>0</c> to <c>1</c> is ease-in, <c>1</c> or more is ease-out. + /// Negative values are in-out/out-in. + /// </summary> + /// <param name="s">The value to ease.</param> + /// <param name="curve"> + /// <c>0</c> is constant, <c>1</c> is linear, <c>0</c> to <c>1</c> is ease-in, <c>1</c> or more is ease-out. + /// </param> + /// <returns>The eased value.</returns> + public static double Ease(double s, double curve) + { + if (s < 0.0) + { + s = 0.0; + } + else if (s > 1.0) + { + s = 1.0; + } + + if (curve > 0) + { + if (curve < 1.0) + { + return 1.0 - Math.Pow(1.0 - s, 1.0 / curve); + } + + return Math.Pow(s, curve); + } + + if (curve < 0.0) + { + if (s < 0.5) + { + return Math.Pow(s * 2.0, -curve) * 0.5; } - return ((1.0f - Pow(1.0f - ((s - 0.5f) * 2.0f), -curve)) * 0.5f) + 0.5f; + return ((1.0 - Math.Pow(1.0 - ((s - 0.5) * 2.0), -curve)) * 0.5) + 0.5; } - return 0f; + return 0.0; + } + + /// <summary> + /// The natural exponential function. It raises the mathematical + /// constant <c>e</c> to the power of <paramref name="s"/> and returns it. + /// </summary> + /// <param name="s">The exponent to raise <c>e</c> to.</param> + /// <returns><c>e</c> raised to the power of <paramref name="s"/>.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Exp(float s) + { + return MathF.Exp(s); } /// <summary> @@ -383,9 +757,21 @@ namespace Godot /// </summary> /// <param name="s">The exponent to raise <c>e</c> to.</param> /// <returns><c>e</c> raised to the power of <paramref name="s"/>.</returns> - public static real_t Exp(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Exp(double s) + { + return Math.Exp(s); + } + + /// <summary> + /// Rounds <paramref name="s"/> downward (towards negative infinity). + /// </summary> + /// <param name="s">The number to floor.</param> + /// <returns>The largest whole number that is not more than <paramref name="s"/>.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Floor(float s) { - return (real_t)Math.Exp(s); + return MathF.Floor(s); } /// <summary> @@ -393,14 +779,32 @@ namespace Godot /// </summary> /// <param name="s">The number to floor.</param> /// <returns>The largest whole number that is not more than <paramref name="s"/>.</returns> - public static real_t Floor(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Floor(double s) + { + return Math.Floor(s); + } + + /// <summary> + /// Returns a normalized value considering the given range. + /// This is the opposite of <see cref="Lerp(float, float, float)"/>. + /// </summary> + /// <param name="from">The start value for interpolation.</param> + /// <param name="to">The destination value for interpolation.</param> + /// <param name="weight">The interpolated value.</param> + /// <returns> + /// The resulting value of the inverse interpolation. + /// The returned value will be between 0.0 and 1.0 if <paramref name="weight"/> is + /// between <paramref name="from"/> and <paramref name="to"/> (inclusive). + /// </returns> + public static float InverseLerp(float from, float to, float weight) { - return (real_t)Math.Floor(s); + return (weight - from) / (to - from); } /// <summary> /// Returns a normalized value considering the given range. - /// This is the opposite of <see cref="Lerp(real_t, real_t, real_t)"/>. + /// This is the opposite of <see cref="Lerp(double, double, double)"/>. /// </summary> /// <param name="from">The start value for interpolation.</param> /// <param name="to">The destination value for interpolation.</param> @@ -410,7 +814,7 @@ namespace Godot /// The returned value will be between 0.0 and 1.0 if <paramref name="weight"/> is /// between <paramref name="from"/> and <paramref name="to"/> (inclusive). /// </returns> - public static real_t InverseLerp(real_t from, real_t to, real_t weight) + public static double InverseLerp(double from, double to, double weight) { return (weight - from) / (to - from); } @@ -423,7 +827,7 @@ namespace Godot /// <param name="a">One of the values.</param> /// <param name="b">The other value.</param> /// <returns>A <see langword="bool"/> for whether or not the two values are approximately equal.</returns> - public static bool IsEqualApprox(real_t a, real_t b) + public static bool IsEqualApprox(float a, float b) { // Check for exact equality first, required to handle "infinity" values. if (a == b) @@ -431,12 +835,71 @@ namespace Godot return true; } // Then check for approximate equality. - real_t tolerance = Epsilon * Abs(a); - if (tolerance < Epsilon) + float tolerance = _epsilonF * Math.Abs(a); + if (tolerance < _epsilonF) { - tolerance = Epsilon; + tolerance = _epsilonF; } - return Abs(a - b) < tolerance; + return Math.Abs(a - b) < tolerance; + } + + /// <summary> + /// Returns <see langword="true"/> if <paramref name="a"/> and <paramref name="b"/> are approximately equal + /// to each other. + /// The comparison is done using a tolerance calculation with <see cref="Epsilon"/>. + /// </summary> + /// <param name="a">One of the values.</param> + /// <param name="b">The other value.</param> + /// <returns>A <see langword="bool"/> for whether or not the two values are approximately equal.</returns> + public static bool IsEqualApprox(double a, double b) + { + // Check for exact equality first, required to handle "infinity" values. + if (a == b) + { + return true; + } + // Then check for approximate equality. + double tolerance = _epsilonD * Math.Abs(a); + if (tolerance < _epsilonD) + { + tolerance = _epsilonD; + } + return Math.Abs(a - b) < tolerance; + } + + /// <summary> + /// Returns whether <paramref name="s"/> is a finite value, i.e. it is not + /// <see cref="NaN"/>, positive infinite, or negative infinity. + /// </summary> + /// <param name="s">The value to check.</param> + /// <returns>A <see langword="bool"/> for whether or not the value is a finite value.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsFinite(float s) + { + return float.IsFinite(s); + } + + /// <summary> + /// Returns whether <paramref name="s"/> is a finite value, i.e. it is not + /// <see cref="NaN"/>, positive infinite, or negative infinity. + /// </summary> + /// <param name="s">The value to check.</param> + /// <returns>A <see langword="bool"/> for whether or not the value is a finite value.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsFinite(double s) + { + return double.IsFinite(s); + } + + /// <summary> + /// Returns whether <paramref name="s"/> is an infinity value (either positive infinity or negative infinity). + /// </summary> + /// <param name="s">The value to check.</param> + /// <returns>A <see langword="bool"/> for whether or not the value is an infinity value.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsInf(float s) + { + return float.IsInfinity(s); } /// <summary> @@ -444,9 +907,10 @@ namespace Godot /// </summary> /// <param name="s">The value to check.</param> /// <returns>A <see langword="bool"/> for whether or not the value is an infinity value.</returns> - public static bool IsInf(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsInf(double s) { - return real_t.IsInfinity(s); + return double.IsInfinity(s); } /// <summary> @@ -454,33 +918,75 @@ namespace Godot /// </summary> /// <param name="s">The value to check.</param> /// <returns>A <see langword="bool"/> for whether or not the value is a <c>NaN</c> value.</returns> - public static bool IsNaN(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNaN(float s) + { + return float.IsNaN(s); + } + + /// <summary> + /// Returns whether <paramref name="s"/> is a <c>NaN</c> ("Not a Number" or invalid) value. + /// </summary> + /// <param name="s">The value to check.</param> + /// <returns>A <see langword="bool"/> for whether or not the value is a <c>NaN</c> value.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNaN(double s) + { + return double.IsNaN(s); + } + + /// <summary> + /// Returns <see langword="true"/> if <paramref name="s"/> is zero or almost zero. + /// The comparison is done using a tolerance calculation with <see cref="Epsilon"/>. + /// + /// This method is faster than using <see cref="IsEqualApprox(float, float)"/> with + /// one value as zero. + /// </summary> + /// <param name="s">The value to check.</param> + /// <returns>A <see langword="bool"/> for whether or not the value is nearly zero.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsZeroApprox(float s) { - return real_t.IsNaN(s); + return Math.Abs(s) < _epsilonF; } /// <summary> - /// Returns <see langword="true"/> if <paramref name="s"/> is approximately zero. + /// Returns <see langword="true"/> if <paramref name="s"/> is zero or almost zero. /// The comparison is done using a tolerance calculation with <see cref="Epsilon"/>. /// - /// This method is faster than using <see cref="IsEqualApprox(real_t, real_t)"/> with one value as zero. + /// This method is faster than using <see cref="IsEqualApprox(double, double)"/> with + /// one value as zero. /// </summary> /// <param name="s">The value to check.</param> /// <returns>A <see langword="bool"/> for whether or not the value is nearly zero.</returns> - public static bool IsZeroApprox(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsZeroApprox(double s) + { + return Math.Abs(s) < _epsilonD; + } + + /// <summary> + /// Linearly interpolates between two values by a normalized value. + /// This is the opposite <see cref="InverseLerp(float, float, float)"/>. + /// </summary> + /// <param name="from">The start value for interpolation.</param> + /// <param name="to">The destination value for interpolation.</param> + /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> + /// <returns>The resulting value of the interpolation.</returns> + public static float Lerp(float from, float to, float weight) { - return Abs(s) < Epsilon; + return from + ((to - from) * weight); } /// <summary> /// Linearly interpolates between two values by a normalized value. - /// This is the opposite <see cref="InverseLerp(real_t, real_t, real_t)"/>. + /// This is the opposite <see cref="InverseLerp(double, double, double)"/>. /// </summary> /// <param name="from">The start value for interpolation.</param> /// <param name="to">The destination value for interpolation.</param> /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> /// <returns>The resulting value of the interpolation.</returns> - public static real_t Lerp(real_t from, real_t to, real_t weight) + public static double Lerp(double from, double to, double weight) { return from + ((to - from) * weight); } @@ -488,30 +994,101 @@ namespace Godot /// <summary> /// Linearly interpolates between two angles (in radians) by a normalized value. /// - /// Similar to <see cref="Lerp(real_t, real_t, real_t)"/>, + /// Similar to <see cref="Lerp(float, float, float)"/>, + /// but interpolates correctly when the angles wrap around <see cref="Tau"/>. + /// </summary> + /// <param name="from">The start angle for interpolation.</param> + /// <param name="to">The destination angle for interpolation.</param> + /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> + /// <returns>The resulting angle of the interpolation.</returns> + public static float LerpAngle(float from, float to, float weight) + { + float difference = (to - from) % MathF.Tau; + float distance = ((2 * difference) % MathF.Tau) - difference; + return from + (distance * weight); + } + + /// <summary> + /// Linearly interpolates between two angles (in radians) by a normalized value. + /// + /// Similar to <see cref="Lerp(double, double, double)"/>, /// but interpolates correctly when the angles wrap around <see cref="Tau"/>. /// </summary> /// <param name="from">The start angle for interpolation.</param> /// <param name="to">The destination angle for interpolation.</param> /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> /// <returns>The resulting angle of the interpolation.</returns> - public static real_t LerpAngle(real_t from, real_t to, real_t weight) + public static double LerpAngle(double from, double to, double weight) { - real_t difference = (to - from) % Mathf.Tau; - real_t distance = ((2 * difference) % Mathf.Tau) - difference; + double difference = (to - from) % Math.Tau; + double distance = ((2 * difference) % Math.Tau) - difference; return from + (distance * weight); } /// <summary> + /// Converts from linear energy to decibels (audio). + /// This can be used to implement volume sliders that behave as expected (since volume isn't linear). + /// </summary> + /// <seealso cref="DbToLinear(float)"/> + /// <example> + /// <code> + /// // "slider" refers to a node that inherits Range such as HSlider or VSlider. + /// // Its range must be configured to go from 0 to 1. + /// // Change the bus name if you'd like to change the volume of a specific bus only. + /// AudioServer.SetBusVolumeDb(AudioServer.GetBusIndex("Master"), GD.LinearToDb(slider.value)); + /// </code> + /// </example> + /// <param name="linear">The linear energy to convert.</param> + /// <returns>Audio as decibels.</returns> + public static float LinearToDb(float linear) + { + return MathF.Log(linear) * 8.6858896380650365530225783783321f; + } + + /// <summary> + /// Converts from linear energy to decibels (audio). + /// This can be used to implement volume sliders that behave as expected (since volume isn't linear). + /// </summary> + /// <seealso cref="DbToLinear(double)"/> + /// <example> + /// <code> + /// // "slider" refers to a node that inherits Range such as HSlider or VSlider. + /// // Its range must be configured to go from 0 to 1. + /// // Change the bus name if you'd like to change the volume of a specific bus only. + /// AudioServer.SetBusVolumeDb(AudioServer.GetBusIndex("Master"), GD.LinearToDb(slider.value)); + /// </code> + /// </example> + /// <param name="linear">The linear energy to convert.</param> + /// <returns>Audio as decibels.</returns> + public static double LinearToDb(double linear) + { + return Math.Log(linear) * 8.6858896380650365530225783783321; + } + + /// <summary> + /// Natural logarithm. The amount of time needed to reach a certain level of continuous growth. + /// + /// Note: This is not the same as the "log" function on most calculators, which uses a base 10 logarithm. + /// </summary> + /// <param name="s">The input value.</param> + /// <returns>The natural log of <paramref name="s"/>.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Log(float s) + { + return MathF.Log(s); + } + + /// <summary> /// Natural logarithm. The amount of time needed to reach a certain level of continuous growth. /// /// Note: This is not the same as the "log" function on most calculators, which uses a base 10 logarithm. /// </summary> /// <param name="s">The input value.</param> /// <returns>The natural log of <paramref name="s"/>.</returns> - public static real_t Log(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Log(double s) { - return (real_t)Math.Log(s); + return Math.Log(s); } /// <summary> @@ -520,9 +1097,22 @@ namespace Godot /// <param name="a">One of the values.</param> /// <param name="b">The other value.</param> /// <returns>Whichever of the two values is higher.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Max(int a, int b) { - return a > b ? a : b; + return Math.Max(a, b); + } + + /// <summary> + /// Returns the maximum of two values. + /// </summary> + /// <param name="a">One of the values.</param> + /// <param name="b">The other value.</param> + /// <returns>Whichever of the two values is higher.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Max(float a, float b) + { + return Math.Max(a, b); } /// <summary> @@ -531,9 +1121,10 @@ namespace Godot /// <param name="a">One of the values.</param> /// <param name="b">The other value.</param> /// <returns>Whichever of the two values is higher.</returns> - public static real_t Max(real_t a, real_t b) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Max(double a, double b) { - return a > b ? a : b; + return Math.Max(a, b); } /// <summary> @@ -542,9 +1133,22 @@ namespace Godot /// <param name="a">One of the values.</param> /// <param name="b">The other value.</param> /// <returns>Whichever of the two values is lower.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Min(int a, int b) { - return a < b ? a : b; + return Math.Min(a, b); + } + + /// <summary> + /// Returns the minimum of two values. + /// </summary> + /// <param name="a">One of the values.</param> + /// <param name="b">The other value.</param> + /// <returns>Whichever of the two values is lower.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Min(float a, float b) + { + return Math.Min(a, b); } /// <summary> @@ -553,9 +1157,27 @@ namespace Godot /// <param name="a">One of the values.</param> /// <param name="b">The other value.</param> /// <returns>Whichever of the two values is lower.</returns> - public static real_t Min(real_t a, real_t b) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Min(double a, double b) + { + return Math.Min(a, b); + } + + /// <summary> + /// Moves <paramref name="from"/> toward <paramref name="to"/> by the <paramref name="delta"/> value. + /// + /// Use a negative <paramref name="delta"/> value to move away. + /// </summary> + /// <param name="from">The start value.</param> + /// <param name="to">The value to move towards.</param> + /// <param name="delta">The amount to move by.</param> + /// <returns>The value after moving.</returns> + public static float MoveToward(float from, float to, float delta) { - return a < b ? a : b; + if (Math.Abs(to - from) <= delta) + return to; + + return from + (Math.Sign(to - from) * delta); } /// <summary> @@ -567,12 +1189,12 @@ namespace Godot /// <param name="to">The value to move towards.</param> /// <param name="delta">The amount to move by.</param> /// <returns>The value after moving.</returns> - public static real_t MoveToward(real_t from, real_t to, real_t delta) + public static double MoveToward(double from, double to, double delta) { - if (Abs(to - from) <= delta) + if (Math.Abs(to - from) <= delta) return to; - return from + (Sign(to - from) * delta); + return from + (Math.Sign(to - from) * delta); } /// <summary> @@ -614,9 +1236,25 @@ namespace Godot /// <param name="a">The dividend, the primary input.</param> /// <param name="b">The divisor. The output is on the range [0, <paramref name="b"/>).</param> /// <returns>The resulting output.</returns> - public static real_t PosMod(real_t a, real_t b) + public static float PosMod(float a, float b) + { + float c = a % b; + if ((c < 0 && b > 0) || (c > 0 && b < 0)) + { + c += b; + } + return c; + } + + /// <summary> + /// Performs a canonical Modulus operation, where the output is on the range [0, <paramref name="b"/>). + /// </summary> + /// <param name="a">The dividend, the primary input.</param> + /// <param name="b">The divisor. The output is on the range [0, <paramref name="b"/>).</param> + /// <returns>The resulting output.</returns> + public static double PosMod(double a, double b) { - real_t c = a % b; + double c = a % b; if ((c < 0 && b > 0) || (c > 0 && b < 0)) { c += b; @@ -630,9 +1268,33 @@ namespace Godot /// <param name="x">The base.</param> /// <param name="y">The exponent.</param> /// <returns><paramref name="x"/> raised to the power of <paramref name="y"/>.</returns> - public static real_t Pow(real_t x, real_t y) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Pow(float x, float y) + { + return MathF.Pow(x, y); + } + + /// <summary> + /// Returns the result of <paramref name="x"/> raised to the power of <paramref name="y"/>. + /// </summary> + /// <param name="x">The base.</param> + /// <param name="y">The exponent.</param> + /// <returns><paramref name="x"/> raised to the power of <paramref name="y"/>.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Pow(double x, double y) + { + return Math.Pow(x, y); + } + + /// <summary> + /// Converts an angle expressed in radians to degrees. + /// </summary> + /// <param name="rad">An angle expressed in radians.</param> + /// <returns>The same angle expressed in degrees.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float RadToDeg(float rad) { - return (real_t)Math.Pow(x, y); + return rad * _radToDegConstF; } /// <summary> @@ -640,9 +1302,25 @@ namespace Godot /// </summary> /// <param name="rad">An angle expressed in radians.</param> /// <returns>The same angle expressed in degrees.</returns> - public static real_t RadToDeg(real_t rad) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double RadToDeg(double rad) + { + return rad * _radToDegConstD; + } + + /// <summary> + /// Maps a <paramref name="value"/> from [<paramref name="inFrom"/>, <paramref name="inTo"/>] + /// to [<paramref name="outFrom"/>, <paramref name="outTo"/>]. + /// </summary> + /// <param name="value">The value to map.</param> + /// <param name="inFrom">The start value for the input interpolation.</param> + /// <param name="inTo">The destination value for the input interpolation.</param> + /// <param name="outFrom">The start value for the output interpolation.</param> + /// <param name="outTo">The destination value for the output interpolation.</param> + /// <returns>The resulting mapped value mapped.</returns> + public static float Remap(float value, float inFrom, float inTo, float outFrom, float outTo) { - return rad * _radToDegConst; + return Lerp(outFrom, outTo, InverseLerp(inFrom, inTo, value)); } /// <summary> @@ -655,7 +1333,7 @@ namespace Godot /// <param name="outFrom">The start value for the output interpolation.</param> /// <param name="outTo">The destination value for the output interpolation.</param> /// <returns>The resulting mapped value mapped.</returns> - public static real_t Remap(real_t value, real_t inFrom, real_t inTo, real_t outFrom, real_t outTo) + public static double Remap(double value, double inFrom, double inTo, double outFrom, double outTo) { return Lerp(outFrom, outTo, InverseLerp(inFrom, inTo, value)); } @@ -666,9 +1344,22 @@ namespace Godot /// </summary> /// <param name="s">The number to round.</param> /// <returns>The rounded number.</returns> - public static real_t Round(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Round(float s) + { + return MathF.Round(s); + } + + /// <summary> + /// Rounds <paramref name="s"/> to the nearest whole number, + /// with halfway cases rounded towards the nearest multiple of two. + /// </summary> + /// <param name="s">The number to round.</param> + /// <returns>The rounded number.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Round(double s) { - return (real_t)Math.Round(s); + return Math.Round(s); } /// <summary> @@ -677,11 +1368,22 @@ namespace Godot /// </summary> /// <param name="s">The input number.</param> /// <returns>One of three possible values: <c>1</c>, <c>-1</c>, or <c>0</c>.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Sign(int s) { - if (s == 0) - return 0; - return s < 0 ? -1 : 1; + return Math.Sign(s); + } + + /// <summary> + /// Returns the sign of <paramref name="s"/>: <c>-1</c> or <c>1</c>. + /// Returns <c>0</c> if <paramref name="s"/> is <c>0</c>. + /// </summary> + /// <param name="s">The input number.</param> + /// <returns>One of three possible values: <c>1</c>, <c>-1</c>, or <c>0</c>.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Sign(float s) + { + return Math.Sign(s); } /// <summary> @@ -690,11 +1392,21 @@ namespace Godot /// </summary> /// <param name="s">The input number.</param> /// <returns>One of three possible values: <c>1</c>, <c>-1</c>, or <c>0</c>.</returns> - public static int Sign(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Sign(double s) + { + return Math.Sign(s); + } + + /// <summary> + /// Returns the sine of angle <paramref name="s"/> in radians. + /// </summary> + /// <param name="s">The angle in radians.</param> + /// <returns>The sine of that angle.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Sin(float s) { - if (s == 0) - return 0; - return s < 0 ? -1 : 1; + return MathF.Sin(s); } /// <summary> @@ -702,9 +1414,21 @@ namespace Godot /// </summary> /// <param name="s">The angle in radians.</param> /// <returns>The sine of that angle.</returns> - public static real_t Sin(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Sin(double s) + { + return Math.Sin(s); + } + + /// <summary> + /// Returns the hyperbolic sine of angle <paramref name="s"/> in radians. + /// </summary> + /// <param name="s">The angle in radians.</param> + /// <returns>The hyperbolic sine of that angle.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Sinh(float s) { - return (real_t)Math.Sin(s); + return MathF.Sinh(s); } /// <summary> @@ -712,27 +1436,47 @@ namespace Godot /// </summary> /// <param name="s">The angle in radians.</param> /// <returns>The hyperbolic sine of that angle.</returns> - public static real_t Sinh(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Sinh(double s) + { + return Math.Sinh(s); + } + + /// <summary> + /// Returns a number smoothly interpolated between <paramref name="from"/> and <paramref name="to"/>, + /// based on the <paramref name="weight"/>. Similar to <see cref="Lerp(float, float, float)"/>, + /// but interpolates faster at the beginning and slower at the end. + /// </summary> + /// <param name="from">The start value for interpolation.</param> + /// <param name="to">The destination value for interpolation.</param> + /// <param name="weight">A value representing the amount of interpolation.</param> + /// <returns>The resulting value of the interpolation.</returns> + public static float SmoothStep(float from, float to, float weight) { - return (real_t)Math.Sinh(s); + if (IsEqualApprox(from, to)) + { + return from; + } + float x = Math.Clamp((weight - from) / (to - from), 0.0f, 1.0f); + return x * x * (3 - (2 * x)); } /// <summary> /// Returns a number smoothly interpolated between <paramref name="from"/> and <paramref name="to"/>, - /// based on the <paramref name="weight"/>. Similar to <see cref="Lerp(real_t, real_t, real_t)"/>, + /// based on the <paramref name="weight"/>. Similar to <see cref="Lerp(double, double, double)"/>, /// but interpolates faster at the beginning and slower at the end. /// </summary> /// <param name="from">The start value for interpolation.</param> /// <param name="to">The destination value for interpolation.</param> /// <param name="weight">A value representing the amount of interpolation.</param> /// <returns>The resulting value of the interpolation.</returns> - public static real_t SmoothStep(real_t from, real_t to, real_t weight) + public static double SmoothStep(double from, double to, double weight) { if (IsEqualApprox(from, to)) { return from; } - real_t x = Clamp((weight - from) / (to - from), (real_t)0.0, (real_t)1.0); + double x = Math.Clamp((weight - from) / (to - from), 0.0, 1.0); return x * x * (3 - (2 * x)); } @@ -743,9 +1487,23 @@ namespace Godot /// </summary> /// <param name="s">The input number. Must not be negative.</param> /// <returns>The square root of <paramref name="s"/>.</returns> - public static real_t Sqrt(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Sqrt(float s) + { + return MathF.Sqrt(s); + } + + /// <summary> + /// Returns the square root of <paramref name="s"/>, where <paramref name="s"/> is a non-negative number. + /// + /// If you need negative inputs, use <see cref="System.Numerics.Complex"/>. + /// </summary> + /// <param name="s">The input number. Must not be negative.</param> + /// <returns>The square root of <paramref name="s"/>.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Sqrt(double s) { - return (real_t)Math.Sqrt(s); + return Math.Sqrt(s); } /// <summary> @@ -755,7 +1513,7 @@ namespace Godot /// </summary> /// <param name="step">The input value.</param> /// <returns>The position of the first non-zero digit.</returns> - public static int StepDecimals(real_t step) + public static int StepDecimals(double step) { double[] sd = new double[] { @@ -769,7 +1527,7 @@ namespace Godot 0.00000009999, 0.000000009999, }; - double abs = Abs(step); + double abs = Math.Abs(step); double decs = abs - (int)abs; // Strip away integer part for (int i = 0; i < sd.Length; i++) { @@ -788,11 +1546,28 @@ namespace Godot /// <param name="s">The value to snap.</param> /// <param name="step">The step size to snap to.</param> /// <returns>The snapped value.</returns> - public static real_t Snapped(real_t s, real_t step) + public static float Snapped(float s, float step) + { + if (step != 0f) + { + return MathF.Floor((s / step) + 0.5f) * step; + } + + return s; + } + + /// <summary> + /// Snaps float value <paramref name="s"/> to a given <paramref name="step"/>. + /// This can also be used to round a floating point number to an arbitrary number of decimals. + /// </summary> + /// <param name="s">The value to snap.</param> + /// <param name="step">The step size to snap to.</param> + /// <returns>The snapped value.</returns> + public static double Snapped(double s, double step) { if (step != 0f) { - return Floor((s / step) + 0.5f) * step; + return Math.Floor((s / step) + 0.5f) * step; } return s; @@ -803,9 +1578,32 @@ namespace Godot /// </summary> /// <param name="s">The angle in radians.</param> /// <returns>The tangent of that angle.</returns> - public static real_t Tan(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Tan(float s) + { + return MathF.Tan(s); + } + + /// <summary> + /// Returns the tangent of angle <paramref name="s"/> in radians. + /// </summary> + /// <param name="s">The angle in radians.</param> + /// <returns>The tangent of that angle.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Tan(double s) + { + return Math.Tan(s); + } + + /// <summary> + /// Returns the hyperbolic tangent of angle <paramref name="s"/> in radians. + /// </summary> + /// <param name="s">The angle in radians.</param> + /// <returns>The hyperbolic tangent of that angle.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Tanh(float s) { - return (real_t)Math.Tan(s); + return MathF.Tanh(s); } /// <summary> @@ -813,9 +1611,10 @@ namespace Godot /// </summary> /// <param name="s">The angle in radians.</param> /// <returns>The hyperbolic tangent of that angle.</returns> - public static real_t Tanh(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Tanh(double s) { - return (real_t)Math.Tanh(s); + return Math.Tanh(s); } /// <summary> @@ -841,15 +1640,35 @@ namespace Godot /// Wraps <paramref name="value"/> between <paramref name="min"/> and <paramref name="max"/>. /// Usable for creating loop-alike behavior or infinite surfaces. /// If <paramref name="min"/> is <c>0</c>, this is equivalent - /// to <see cref="PosMod(real_t, real_t)"/>, so prefer using that instead. + /// to <see cref="PosMod(float, float)"/>, so prefer using that instead. + /// </summary> + /// <param name="value">The value to wrap.</param> + /// <param name="min">The minimum allowed value and lower bound of the range.</param> + /// <param name="max">The maximum allowed value and upper bound of the range.</param> + /// <returns>The wrapped value.</returns> + public static float Wrap(float value, float min, float max) + { + float range = max - min; + if (IsZeroApprox(range)) + { + return min; + } + return min + ((((value - min) % range) + range) % range); + } + + /// <summary> + /// Wraps <paramref name="value"/> between <paramref name="min"/> and <paramref name="max"/>. + /// Usable for creating loop-alike behavior or infinite surfaces. + /// If <paramref name="min"/> is <c>0</c>, this is equivalent + /// to <see cref="PosMod(double, double)"/>, so prefer using that instead. /// </summary> /// <param name="value">The value to wrap.</param> /// <param name="min">The minimum allowed value and lower bound of the range.</param> /// <param name="max">The maximum allowed value and upper bound of the range.</param> /// <returns>The wrapped value.</returns> - public static real_t Wrap(real_t value, real_t min, real_t max) + public static double Wrap(double value, double min, double max) { - real_t range = max - min; + double range = max - min; if (IsZeroApprox(range)) { return min; @@ -857,9 +1676,23 @@ namespace Godot return min + ((((value - min) % range) + range) % range); } - private static real_t Fract(real_t value) + /// <summary> + /// Returns the <paramref name="value"/> wrapped between <c>0</c> and the <paramref name="length"/>. + /// If the limit is reached, the next value the function returned is decreased to the <c>0</c> side + /// or increased to the <paramref name="length"/> side (like a triangle wave). + /// If <paramref name="length"/> is less than zero, it becomes positive. + /// </summary> + /// <param name="value">The value to pingpong.</param> + /// <param name="length">The maximum value of the function.</param> + /// <returns>The ping-ponged value.</returns> + public static float PingPong(float value, float length) { - return value - (real_t)Math.Floor(value); + return (length != 0.0f) ? Math.Abs(Fract((value - length) / (length * 2.0f)) * length * 2.0f - length) : 0.0f; + + static float Fract(float value) + { + return value - MathF.Floor(value); + } } /// <summary> @@ -871,9 +1704,14 @@ namespace Godot /// <param name="value">The value to pingpong.</param> /// <param name="length">The maximum value of the function.</param> /// <returns>The ping-ponged value.</returns> - public static real_t PingPong(real_t value, real_t length) + public static double PingPong(double value, double length) { - return (length != (real_t)0.0) ? Abs(Fract((value - length) / (length * (real_t)2.0)) * length * (real_t)2.0 - length) : (real_t)0.0; + return (length != 0.0) ? Math.Abs(Fract((value - length) / (length * 2.0)) * length * 2.0 - length) : 0.0; + + static double Fract(double value) + { + return value - Math.Floor(value); + } } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs index ea05c1547c..cc2d61f58d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs @@ -1,4 +1,8 @@ using System; +using System.Runtime.CompilerServices; + +// This file contains extra members for the Mathf class that aren't part of Godot's Core API. +// Math API that is also part of Core should go into Mathf.cs. namespace Godot { @@ -16,14 +20,18 @@ namespace Godot /// </summary> public const real_t Sqrt2 = (real_t)1.4142135623730950488016887242M; // 1.4142136f and 1.414213562373095 + // Epsilon size should depend on the precision used. + private const float _epsilonF = 1e-06f; + private const double _epsilonD = 1e-14; + /// <summary> /// A very small number used for float comparison with error tolerance. /// 1e-06 with single-precision floats, but 1e-14 if <c>REAL_T_IS_DOUBLE</c>. /// </summary> #if REAL_T_IS_DOUBLE - public const real_t Epsilon = 1e-14; // Epsilon size should depend on the precision used. + public const real_t Epsilon = _epsilonD; #else - public const real_t Epsilon = 1e-06f; + public const real_t Epsilon = _epsilonF; #endif /// <summary> @@ -31,7 +39,8 @@ namespace Godot /// </summary> /// <param name="s">The input value.</param> /// <returns>The amount of digits.</returns> - public static int DecimalCount(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int DecimalCount(double s) { return DecimalCount((decimal)s); } @@ -41,6 +50,7 @@ namespace Godot /// </summary> /// <param name="s">The input <see langword="decimal"/> value.</param> /// <returns>The amount of digits.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int DecimalCount(decimal s) { return BitConverter.GetBytes(decimal.GetBits(s)[3])[2]; @@ -49,11 +59,25 @@ namespace Godot /// <summary> /// Rounds <paramref name="s"/> upward (towards positive infinity). /// - /// This is the same as <see cref="Ceil(real_t)"/>, but returns an <see langword="int"/>. + /// This is the same as <see cref="Ceil(float)"/>, but returns an <see langword="int"/>. /// </summary> /// <param name="s">The number to ceil.</param> /// <returns>The smallest whole number that is not less than <paramref name="s"/>.</returns> - public static int CeilToInt(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CeilToInt(float s) + { + return (int)MathF.Ceiling(s); + } + + /// <summary> + /// Rounds <paramref name="s"/> upward (towards positive infinity). + /// + /// This is the same as <see cref="Ceil(double)"/>, but returns an <see langword="int"/>. + /// </summary> + /// <param name="s">The number to ceil.</param> + /// <returns>The smallest whole number that is not less than <paramref name="s"/>.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CeilToInt(double s) { return (int)Math.Ceiling(s); } @@ -61,11 +85,25 @@ namespace Godot /// <summary> /// Rounds <paramref name="s"/> downward (towards negative infinity). /// - /// This is the same as <see cref="Floor(real_t)"/>, but returns an <see langword="int"/>. + /// This is the same as <see cref="Floor(float)"/>, but returns an <see langword="int"/>. /// </summary> /// <param name="s">The number to floor.</param> /// <returns>The largest whole number that is not more than <paramref name="s"/>.</returns> - public static int FloorToInt(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int FloorToInt(float s) + { + return (int)MathF.Floor(s); + } + + /// <summary> + /// Rounds <paramref name="s"/> downward (towards negative infinity). + /// + /// This is the same as <see cref="Floor(double)"/>, but returns an <see langword="int"/>. + /// </summary> + /// <param name="s">The number to floor.</param> + /// <returns>The largest whole number that is not more than <paramref name="s"/>.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int FloorToInt(double s) { return (int)Math.Floor(s); } @@ -73,26 +111,81 @@ namespace Godot /// <summary> /// Rounds <paramref name="s"/> to the nearest whole number. /// - /// This is the same as <see cref="Round(real_t)"/>, but returns an <see langword="int"/>. + /// This is the same as <see cref="Round(float)"/>, but returns an <see langword="int"/>. + /// </summary> + /// <param name="s">The number to round.</param> + /// <returns>The rounded number.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int RoundToInt(float s) + { + return (int)MathF.Round(s); + } + + /// <summary> + /// Rounds <paramref name="s"/> to the nearest whole number. + /// + /// This is the same as <see cref="Round(double)"/>, but returns an <see langword="int"/>. /// </summary> /// <param name="s">The number to round.</param> /// <returns>The rounded number.</returns> - public static int RoundToInt(real_t s) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int RoundToInt(double s) { return (int)Math.Round(s); } /// <summary> + /// Returns the sine and cosine of angle <paramref name="s"/> in radians. + /// </summary> + /// <param name="s">The angle in radians.</param> + /// <returns>The sine and cosine of that angle.</returns> + public static (float Sin, float Cos) SinCos(float s) + { + return MathF.SinCos(s); + } + + /// <summary> + /// Returns the sine and cosine of angle <paramref name="s"/> in radians. + /// </summary> + /// <param name="s">The angle in radians.</param> + /// <returns>The sine and cosine of that angle.</returns> + public static (double Sin, double Cos) SinCos(double s) + { + return Math.SinCos(s); + } + + /// <summary> + /// Returns <see langword="true"/> if <paramref name="a"/> and <paramref name="b"/> are approximately + /// equal to each other. + /// The comparison is done using the provided tolerance value. + /// If you want the tolerance to be calculated for you, use <see cref="IsEqualApprox(float, float)"/>. + /// </summary> + /// <param name="a">One of the values.</param> + /// <param name="b">The other value.</param> + /// <param name="tolerance">The pre-calculated tolerance value.</param> + /// <returns>A <see langword="bool"/> for whether or not the two values are equal.</returns> + public static bool IsEqualApprox(float a, float b, float tolerance) + { + // Check for exact equality first, required to handle "infinity" values. + if (a == b) + { + return true; + } + // Then check for approximate equality. + return Math.Abs(a - b) < tolerance; + } + + /// <summary> /// Returns <see langword="true"/> if <paramref name="a"/> and <paramref name="b"/> are approximately /// equal to each other. /// The comparison is done using the provided tolerance value. - /// If you want the tolerance to be calculated for you, use <see cref="IsEqualApprox(real_t, real_t)"/>. + /// If you want the tolerance to be calculated for you, use <see cref="IsEqualApprox(double, double)"/>. /// </summary> /// <param name="a">One of the values.</param> /// <param name="b">The other value.</param> /// <param name="tolerance">The pre-calculated tolerance value.</param> /// <returns>A <see langword="bool"/> for whether or not the two values are equal.</returns> - public static bool IsEqualApprox(real_t a, real_t b, real_t tolerance) + public static bool IsEqualApprox(double a, double b, double tolerance) { // Check for exact equality first, required to handle "infinity" values. if (a == b) @@ -100,7 +193,7 @@ namespace Godot return true; } // Then check for approximate equality. - return Abs(a - b) < tolerance; + return Math.Abs(a - b) < tolerance; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs index 0d9a698af0..11e600ca0b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -310,7 +310,7 @@ namespace Godot.NativeInterop public static Signal ConvertSignalToManaged(in godot_signal p_signal) { - var owner = GD.InstanceFromId(p_signal.ObjectId); + var owner = Godot.Object.InstanceFromId(p_signal.ObjectId); var name = StringName.CreateTakingOwnershipOfDisposableValue( NativeFuncs.godotsharp_string_name_new_copy(p_signal.Name)); return new Signal(owner, name); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index 57488bd586..66c94166ae 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -409,6 +409,10 @@ namespace Godot.NativeInterop public static partial void godotsharp_dictionary_duplicate(ref godot_dictionary p_self, godot_bool p_deep, out godot_dictionary r_dest); + public static partial void godotsharp_dictionary_merge(ref godot_dictionary p_self, in godot_dictionary p_dictionary, godot_bool p_overwrite); + + public static partial godot_bool godotsharp_dictionary_recursive_equal(ref godot_dictionary p_self, in godot_dictionary p_other); + public static partial godot_bool godotsharp_dictionary_remove_key(ref godot_dictionary p_self, in godot_variant p_key); @@ -494,8 +498,6 @@ namespace Godot.NativeInterop internal static partial void godotsharp_weakref(IntPtr p_obj, out godot_ref r_weak_ref); - internal static partial void godotsharp_str(in godot_array p_what, out godot_string r_ret); - internal static partial void godotsharp_str_to_var(in godot_string p_str, out godot_variant r_ret); internal static partial void godotsharp_var_to_bytes(in godot_variant p_what, godot_bool p_full_objects, diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs index 9c9258dd9e..bb306ddaea 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs @@ -260,7 +260,7 @@ namespace Godot.NativeInterop => from != null ? CreateFromArray((godot_array)from.NativeValue) : default; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static godot_variant CreateFromArray<T>(Array<T>? from) + public static godot_variant CreateFromArray<[MustBeVariant] T>(Array<T>? from) => from != null ? CreateFromArray((godot_array)((Collections.Array)from).NativeValue) : default; public static godot_variant CreateFromDictionary(godot_dictionary from) @@ -274,7 +274,7 @@ namespace Godot.NativeInterop => from != null ? CreateFromDictionary((godot_dictionary)from.NativeValue) : default; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static godot_variant CreateFromDictionary<TKey, TValue>(Dictionary<TKey, TValue>? from) + public static godot_variant CreateFromDictionary<[MustBeVariant] TKey, [MustBeVariant] TValue>(Dictionary<TKey, TValue>? from) => from != null ? CreateFromDictionary((godot_dictionary)((Dictionary)from).NativeValue) : default; public static godot_variant CreateFromStringName(godot_string_name from) @@ -526,7 +526,7 @@ namespace Godot.NativeInterop => Collections.Array.CreateTakingOwnershipOfDisposableValue(ConvertToNativeArray(p_var)); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Array<T> ConvertToArray<T>(in godot_variant p_var) + public static Array<T> ConvertToArray<[MustBeVariant] T>(in godot_variant p_var) => Array<T>.CreateTakingOwnershipOfDisposableValue(ConvertToNativeArray(p_var)); public static godot_dictionary ConvertToNativeDictionary(in godot_variant p_var) @@ -539,7 +539,7 @@ namespace Godot.NativeInterop => Dictionary.CreateTakingOwnershipOfDisposableValue(ConvertToNativeDictionary(p_var)); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Dictionary<TKey, TValue> ConvertToDictionary<TKey, TValue>(in godot_variant p_var) + public static Dictionary<TKey, TValue> ConvertToDictionary<[MustBeVariant] TKey, [MustBeVariant] TValue>(in godot_variant p_var) => Dictionary<TKey, TValue>.CreateTakingOwnershipOfDisposableValue(ConvertToNativeDictionary(p_var)); public static byte[] ConvertAsPackedByteArrayToSystemArray(in godot_variant p_var) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs index 42c6b0a37e..8a125e3c73 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs @@ -15,7 +15,7 @@ namespace Godot private Vector3 _normal; /// <summary> - /// The normal of the plane, which must be normalized. + /// The normal of the plane, which must be a unit vector. /// In the scalar equation of the plane <c>ax + by + cz = d</c>, this is /// the vector <c>(a, b, c)</c>, where <c>d</c> is the <see cref="D"/> property. /// </summary> @@ -85,23 +85,6 @@ namespace Godot public real_t D { get; set; } /// <summary> - /// The center of the plane, the point where the normal line intersects the plane. - /// </summary> - /// <value>Equivalent to <see cref="Normal"/> multiplied by <see cref="D"/>.</value> - public Vector3 Center - { - readonly get - { - return _normal * D; - } - set - { - _normal = value.Normalized(); - D = value.Length(); - } - } - - /// <summary> /// Returns the shortest distance from this plane to the position <paramref name="point"/>. /// </summary> /// <param name="point">The position to use for the calculation.</param> @@ -112,6 +95,16 @@ namespace Godot } /// <summary> + /// Returns the center of the plane, the point on the plane closest to the origin. + /// The point where the normal line going through the origin intersects the plane. + /// </summary> + /// <value>Equivalent to <see cref="Normal"/> multiplied by <see cref="D"/>.</value> + public readonly Vector3 GetCenter() + { + return _normal * D; + } + + /// <summary> /// Returns <see langword="true"/> if point is inside the plane. /// Comparison uses a custom minimum tolerance threshold. /// </summary> @@ -155,7 +148,7 @@ namespace Godot /// <param name="from">The start of the ray.</param> /// <param name="dir">The direction of the ray, normalized.</param> /// <returns>The intersection, or <see langword="null"/> if none is found.</returns> - public readonly Vector3? IntersectRay(Vector3 from, Vector3 dir) + public readonly Vector3? IntersectsRay(Vector3 from, Vector3 dir) { real_t den = _normal.Dot(dir); @@ -183,7 +176,7 @@ namespace Godot /// <param name="begin">The start of the line segment.</param> /// <param name="end">The end of the line segment.</param> /// <returns>The intersection, or <see langword="null"/> if none is found.</returns> - public readonly Vector3? IntersectSegment(Vector3 begin, Vector3 end) + public readonly Vector3? IntersectsSegment(Vector3 begin, Vector3 end) { Vector3 segment = begin - end; real_t den = _normal.Dot(segment); @@ -205,6 +198,16 @@ namespace Godot } /// <summary> + /// Returns <see langword="true"/> if this plane is finite, by calling + /// <see cref="Mathf.IsFinite"/> on each component. + /// </summary> + /// <returns>Whether this vector is finite or not.</returns> + public readonly bool IsFinite() + { + return _normal.IsFinite() && Mathf.IsFinite(D); + } + + /// <summary> /// Returns <see langword="true"/> if <paramref name="point"/> is located above the plane. /// </summary> /// <param name="point">The point to check.</param> @@ -280,10 +283,21 @@ namespace Godot } /// <summary> + /// Constructs a <see cref="Plane"/> from a <paramref name="normal"/> vector. + /// The plane will intersect the origin. + /// </summary> + /// <param name="normal">The normal of the plane, must be a unit vector.</param> + public Plane(Vector3 normal) + { + _normal = normal; + D = 0; + } + + /// <summary> /// Constructs a <see cref="Plane"/> from a <paramref name="normal"/> vector and /// the plane's distance to the origin <paramref name="d"/>. /// </summary> - /// <param name="normal">The normal of the plane, must be normalized.</param> + /// <param name="normal">The normal of the plane, must be a unit vector.</param> /// <param name="d">The plane's distance from the origin. This value is typically non-negative.</param> public Plane(Vector3 normal, real_t d) { @@ -295,7 +309,7 @@ namespace Godot /// Constructs a <see cref="Plane"/> from a <paramref name="normal"/> vector and /// a <paramref name="point"/> on the plane. /// </summary> - /// <param name="normal">The normal of the plane, must be normalized.</param> + /// <param name="normal">The normal of the plane, must be a unit vector.</param> /// <param name="point">The point on the plane.</param> public Plane(Vector3 normal, Vector3 point) { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs index fd37f8d9e8..f11b3c553a 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs @@ -381,14 +381,14 @@ namespace Godot } real_t radians = Mathf.DegToRad(fovyDegrees / (real_t)2.0); real_t deltaZ = zFar - zNear; - real_t sine = Mathf.Sin(radians); + (real_t sin, real_t cos) = Mathf.SinCos(radians); - if ((deltaZ == 0) || (sine == 0) || (aspect == 0)) + if ((deltaZ == 0) || (sin == 0) || (aspect == 0)) { return Zero; } - real_t cotangent = Mathf.Cos(radians) / sine; + real_t cotangent = cos / sin; Projection proj = Projection.Identity; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs index bd0dea0c1c..8e4f9178f7 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs @@ -97,27 +97,6 @@ namespace Godot } /// <summary> - /// Returns the length (magnitude) of the quaternion. - /// </summary> - /// <seealso cref="LengthSquared"/> - /// <value>Equivalent to <c>Mathf.Sqrt(LengthSquared)</c>.</value> - public readonly real_t Length - { - get { return Mathf.Sqrt(LengthSquared); } - } - - /// <summary> - /// Returns the squared length (squared magnitude) of the quaternion. - /// This method runs faster than <see cref="Length"/>, so prefer it if - /// you need to compare quaternions or need the squared length for some formula. - /// </summary> - /// <value>Equivalent to <c>Dot(this)</c>.</value> - public readonly real_t LengthSquared - { - get { return Dot(this); } - } - - /// <summary> /// Returns the angle between this quaternion and <paramref name="to"/>. /// This is the magnitude of the angle you would need to rotate /// by to get from one to the other. @@ -340,12 +319,22 @@ namespace Godot } /// <summary> + /// Returns <see langword="true"/> if this quaternion is finite, by calling + /// <see cref="Mathf.IsFinite"/> on each component. + /// </summary> + /// <returns>Whether this vector is finite or not.</returns> + public readonly bool IsFinite() + { + return Mathf.IsFinite(x) && Mathf.IsFinite(y) && Mathf.IsFinite(z) && Mathf.IsFinite(w); + } + + /// <summary> /// Returns whether the quaternion is normalized or not. /// </summary> /// <returns>A <see langword="bool"/> for whether the quaternion is normalized or not.</returns> public readonly bool IsNormalized() { - return Mathf.Abs(LengthSquared - 1) <= Mathf.Epsilon; + return Mathf.Abs(LengthSquared() - 1) <= Mathf.Epsilon; } public readonly Quaternion Log() @@ -355,12 +344,33 @@ namespace Godot } /// <summary> + /// Returns the length (magnitude) of the quaternion. + /// </summary> + /// <seealso cref="LengthSquared"/> + /// <value>Equivalent to <c>Mathf.Sqrt(LengthSquared)</c>.</value> + public readonly real_t Length() + { + return Mathf.Sqrt(LengthSquared()); + } + + /// <summary> + /// Returns the squared length (squared magnitude) of the quaternion. + /// This method runs faster than <see cref="Length"/>, so prefer it if + /// you need to compare quaternions or need the squared length for some formula. + /// </summary> + /// <value>Equivalent to <c>Dot(this)</c>.</value> + public readonly real_t LengthSquared() + { + return Dot(this); + } + + /// <summary> /// Returns a copy of the quaternion, normalized to unit length. /// </summary> /// <returns>The normalized quaternion.</returns> public readonly Quaternion Normalized() { - return this / Length; + return this / Length(); } /// <summary> @@ -532,14 +542,13 @@ namespace Godot } else { - real_t sinAngle = Mathf.Sin(angle * 0.5f); - real_t cosAngle = Mathf.Cos(angle * 0.5f); - real_t s = sinAngle / d; + (real_t sin, real_t cos) = Mathf.SinCos(angle * 0.5f); + real_t s = sin / d; x = axis.x * s; y = axis.y * s; z = axis.z * s; - w = cosAngle; + w = cos; } } @@ -583,12 +592,9 @@ namespace Godot // Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-6) // a3 is the angle of the first rotation, following the notation in this reference. - real_t cosA1 = Mathf.Cos(halfA1); - real_t sinA1 = Mathf.Sin(halfA1); - real_t cosA2 = Mathf.Cos(halfA2); - real_t sinA2 = Mathf.Sin(halfA2); - real_t cosA3 = Mathf.Cos(halfA3); - real_t sinA3 = Mathf.Sin(halfA3); + (real_t sinA1, real_t cosA1) = Mathf.SinCos(halfA1); + (real_t sinA2, real_t cosA2) = Mathf.SinCos(halfA2); + (real_t sinA3, real_t cosA3) = Mathf.SinCos(halfA3); return new Quaternion( (sinA1 * cosA2 * sinA3) + (cosA1 * sinA2 * cosA3), diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs index b0e0e75a34..1a8696d3bc 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs @@ -101,6 +101,16 @@ namespace Godot } /// <summary> + /// Returns <see langword="true"/> if this <see cref="Rect2"/> is finite, by calling + /// <see cref="Mathf.IsFinite"/> on each component. + /// </summary> + /// <returns>Whether this vector is finite or not.</returns> + public bool IsFinite() + { + return _position.IsFinite() && _size.IsFinite(); + } + + /// <summary> /// Returns <see langword="true"/> if this <see cref="Rect2"/> completely encloses another one. /// </summary> /// <param name="b">The other <see cref="Rect2"/> that may be enclosed.</param> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs index faee81a98a..cf8939a859 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs @@ -275,38 +275,19 @@ namespace Godot /// <summary> /// Returns <see langword="true"/> if the <see cref="Rect2i"/> overlaps with <paramref name="b"/> /// (i.e. they have at least one point in common). - /// - /// If <paramref name="includeBorders"/> is <see langword="true"/>, - /// they will also be considered overlapping if their borders touch, - /// even without intersection. /// </summary> /// <param name="b">The other <see cref="Rect2i"/> to check for intersections with.</param> - /// <param name="includeBorders">Whether or not to consider borders.</param> /// <returns>A <see langword="bool"/> for whether or not they are intersecting.</returns> - public readonly bool Intersects(Rect2i b, bool includeBorders = false) + public readonly bool Intersects(Rect2i b) { - if (includeBorders) - { - if (_position.x > b._position.x + b._size.x) - return false; - if (_position.x + _size.x < b._position.x) - return false; - if (_position.y > b._position.y + b._size.y) - return false; - if (_position.y + _size.y < b._position.y) - return false; - } - else - { - if (_position.x >= b._position.x + b._size.x) - return false; - if (_position.x + _size.x <= b._position.x) - return false; - if (_position.y >= b._position.y + b._size.y) - return false; - if (_position.y + _size.y <= b._position.y) - return false; - } + if (_position.x >= b._position.x + b._size.x) + return false; + if (_position.x + _size.x <= b._position.x) + return false; + if (_position.y >= b._position.y + b._size.y) + return false; + if (_position.y + _size.y <= b._position.y) + return false; return true; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs index 756f71e5b2..1dfa83ffc6 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Godot @@ -32,42 +33,20 @@ namespace Godot public Vector2 origin; /// <summary> - /// The rotation of this transformation matrix. + /// Returns the transform's rotation (in radians). /// </summary> - /// <value>Getting is equivalent to calling <see cref="Mathf.Atan2(real_t, real_t)"/> with the values of <see cref="x"/>.</value> - public real_t Rotation - { - readonly get - { - return Mathf.Atan2(x.y, x.x); - } - set - { - Vector2 scale = Scale; - x.x = y.y = Mathf.Cos(value); - x.y = y.x = Mathf.Sin(value); - y.x *= -1; - Scale = scale; - } - } + public readonly real_t Rotation => Mathf.Atan2(x.y, x.x); /// <summary> - /// The scale of this transformation matrix. + /// Returns the scale. /// </summary> - /// <value>Equivalent to the lengths of each column vector, but Y is negative if the determinant is negative.</value> - public Vector2 Scale + public readonly Vector2 Scale { - readonly get + get { real_t detSign = Mathf.Sign(BasisDeterminant()); return new Vector2(x.Length(), detSign * y.Length()); } - set - { - value /= Scale; // Value becomes what's called "delta_scale" in core. - x *= value.x; - y *= value.y; - } } /// <summary> @@ -170,6 +149,7 @@ namespace Godot /// and is usually considered invalid. /// </summary> /// <returns>The determinant of the basis matrix.</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] private readonly real_t BasisDeterminant() { return (x.x * y.y) - (x.y * y.x); @@ -217,8 +197,10 @@ namespace Godot Vector2 s2 = transform.Scale; // Slerp rotation - var v1 = new Vector2(Mathf.Cos(r1), Mathf.Sin(r1)); - var v2 = new Vector2(Mathf.Cos(r2), Mathf.Sin(r2)); + (real_t sin1, real_t cos1) = Mathf.SinCos(r1); + (real_t sin2, real_t cos2) = Mathf.SinCos(r2); + var v1 = new Vector2(cos1, sin1); + var v2 = new Vector2(cos2, sin2); real_t dot = v1.Dot(v2); @@ -235,7 +217,8 @@ namespace Godot { real_t angle = weight * Mathf.Acos(dot); Vector2 v3 = (v2 - (v1 * dot)).Normalized(); - v = (v1 * Mathf.Cos(angle)) + (v3 * Mathf.Sin(angle)); + (real_t sine, real_t cos) = Mathf.SinCos(angle); + v = (v1 * sine) + (v3 * cos); } // Extract parameters @@ -271,6 +254,16 @@ namespace Godot } /// <summary> + /// Returns <see langword="true"/> if this transform is finite, by calling + /// <see cref="Mathf.IsFinite"/> on each component. + /// </summary> + /// <returns>Whether this vector is finite or not.</returns> + public readonly bool IsFinite() + { + return x.IsFinite() && y.IsFinite() && origin.IsFinite(); + } + + /// <summary> /// Returns the transform with the basis orthogonal (90 degrees), /// and normalized axis vectors (scale of 1 or -1). /// </summary> @@ -423,7 +416,7 @@ namespace Godot /// <summary> /// Constructs a transformation matrix from the given components. - /// Arguments are named such that xy is equal to calling x.y + /// Arguments are named such that xy is equal to calling <c>x.y</c>. /// </summary> /// <param name="xx">The X component of the X column vector, accessed via <c>t.x.x</c> or <c>[0][0]</c>.</param> /// <param name="xy">The Y component of the X column vector, accessed via <c>t.x.y</c> or <c>[0][1]</c>.</param> @@ -446,13 +439,34 @@ namespace Godot /// <param name="origin">The origin vector, or column index 2.</param> public Transform2D(real_t rotation, Vector2 origin) { - x.x = y.y = Mathf.Cos(rotation); - x.y = y.x = Mathf.Sin(rotation); + (real_t sin, real_t cos) = Mathf.SinCos(rotation); + x.x = y.y = cos; + x.y = y.x = sin; y.x *= -1; this.origin = origin; } /// <summary> + /// Constructs a transformation matrix from a <paramref name="rotation"/> value, + /// <paramref name="scale"/> vector, <paramref name="skew"/> value, and + /// <paramref name="origin"/> vector. + /// </summary> + /// <param name="rotation">The rotation of the new transform, in radians.</param> + /// <param name="scale">The scale of the new transform.</param> + /// <param name="skew">The skew of the new transform, in radians.</param> + /// <param name="origin">The origin vector, or column index 2.</param> + public Transform2D(real_t rotation, Vector2 scale, real_t skew, Vector2 origin) + { + (real_t rotationSin, real_t rotationCos) = Mathf.SinCos(rotation); + (real_t rotationSkewSin, real_t rotationSkewCos) = Mathf.SinCos(rotation + skew); + x.x = rotationCos * scale.x; + y.y = rotationSkewCos * scale.y; + y.x = -rotationSkewSin * scale.y; + x.y = rotationSin * scale.x; + this.origin = origin; + } + + /// <summary> /// Composes these two transformation matrices by multiplying them /// together. This has the effect of transforming the second transform /// (the child) by the first transform (the parent). diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs index 39167bd116..ce5f98a6fc 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs @@ -115,16 +115,30 @@ namespace Godot } /// <summary> - /// Interpolates this transform to the other <paramref name="transform"/> by <paramref name="weight"/>. + /// Returns a transform interpolated between this transform and another + /// <paramref name="transform"/> by a given <paramref name="weight"/> + /// (on the range of 0.0 to 1.0). /// </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 readonly Transform3D InterpolateWith(Transform3D transform, real_t weight) { - Basis retBasis = basis.Lerp(transform.basis, weight); - Vector3 retOrigin = origin.Lerp(transform.origin, weight); - return new Transform3D(retBasis, retOrigin); + Vector3 sourceScale = basis.Scale; + Quaternion sourceRotation = basis.GetRotationQuaternion(); + Vector3 sourceLocation = origin; + + Vector3 destinationScale = transform.basis.Scale; + Quaternion destinationRotation = transform.basis.GetRotationQuaternion(); + Vector3 destinationLocation = transform.origin; + + var interpolated = new Transform3D(); + Quaternion quaternion = sourceRotation.Slerp(destinationRotation, weight).Normalized(); + Vector3 scale = sourceScale.Lerp(destinationScale, weight); + interpolated.basis.SetQuaternionScale(quaternion, scale); + interpolated.origin = sourceLocation.Lerp(destinationLocation, weight); + + return interpolated; } /// <summary> @@ -140,6 +154,16 @@ namespace Godot } /// <summary> + /// Returns <see langword="true"/> if this transform is finite, by calling + /// <see cref="Mathf.IsFinite"/> on each component. + /// </summary> + /// <returns>Whether this vector is finite or not.</returns> + public readonly bool IsFinite() + { + return basis.IsFinite() && origin.IsFinite(); + } + + /// <summary> /// Returns a copy of the transform rotated such that its /// -Z axis (forward) points towards the <paramref name="target"/> position. /// @@ -223,34 +247,6 @@ namespace Godot return new Transform3D(basis * tmpBasis, origin); } - /// <summary> - /// Returns a transform spherically interpolated between this transform and - /// another <paramref name="transform"/> by <paramref name="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 readonly Transform3D SphericalInterpolateWith(Transform3D transform, real_t weight) - { - /* not sure if very "efficient" but good enough? */ - - Vector3 sourceScale = basis.Scale; - Quaternion sourceRotation = basis.GetRotationQuaternion(); - Vector3 sourceLocation = origin; - - Vector3 destinationScale = transform.basis.Scale; - Quaternion destinationRotation = transform.basis.GetRotationQuaternion(); - Vector3 destinationLocation = transform.origin; - - var interpolated = new Transform3D(); - Quaternion quaternion = sourceRotation.Slerp(destinationRotation, weight).Normalized(); - Vector3 scale = sourceScale.Lerp(destinationScale, weight); - interpolated.basis.SetQuaternionScale(quaternion, scale); - interpolated.origin = sourceLocation.Lerp(destinationLocation, weight); - - return interpolated; - } - private void SetLookAt(Vector3 eye, Vector3 target, Vector3 up) { // Make rotation matrix @@ -346,15 +342,25 @@ namespace Godot } /// <summary> - /// Constructs a transformation matrix from the given <paramref name="quaternion"/> - /// and <paramref name="origin"/> vector. + /// Constructs a transformation matrix from the given components. + /// Arguments are named such that xy is equal to calling <c>basis.x.y</c>. /// </summary> - /// <param name="quaternion">The <see cref="Quaternion"/> to create the basis from.</param> - /// <param name="origin">The origin vector, or column index 3.</param> - public Transform3D(Quaternion quaternion, Vector3 origin) + /// <param name="xx">The X component of the X column vector, accessed via <c>t.basis.x.x</c> or <c>[0][0]</c>.</param> + /// <param name="yx">The X component of the Y column vector, accessed via <c>t.basis.y.x</c> or <c>[1][0]</c>.</param> + /// <param name="zx">The X component of the Z column vector, accessed via <c>t.basis.z.x</c> or <c>[2][0]</c>.</param> + /// <param name="xy">The Y component of the X column vector, accessed via <c>t.basis.x.y</c> or <c>[0][1]</c>.</param> + /// <param name="yy">The Y component of the Y column vector, accessed via <c>t.basis.y.y</c> or <c>[1][1]</c>.</param> + /// <param name="zy">The Y component of the Z column vector, accessed via <c>t.basis.y.y</c> or <c>[2][1]</c>.</param> + /// <param name="xz">The Z component of the X column vector, accessed via <c>t.basis.x.y</c> or <c>[0][2]</c>.</param> + /// <param name="yz">The Z component of the Y column vector, accessed via <c>t.basis.y.y</c> or <c>[1][2]</c>.</param> + /// <param name="zz">The Z component of the Z column vector, accessed via <c>t.basis.y.y</c> or <c>[2][2]</c>.</param> + /// <param name="ox">The X component of the origin vector, accessed via <c>t.origin.x</c> or <c>[2][0]</c>.</param> + /// <param name="oy">The Y component of the origin vector, accessed via <c>t.origin.y</c> or <c>[2][1]</c>.</param> + /// <param name="oz">The Z component of the origin vector, accessed via <c>t.origin.z</c> or <c>[2][2]</c>.</param> + public Transform3D(real_t xx, real_t yx, real_t zx, real_t xy, real_t yy, real_t zy, real_t xz, real_t yz, real_t zz, real_t ox, real_t oy, real_t oz) { - basis = new Basis(quaternion); - this.origin = origin; + basis = new Basis(xx, yx, zx, xy, yy, zy, xz, yz, zz); + origin = new Vector3(ox, oy, oz); } /// <summary> @@ -370,6 +376,29 @@ namespace Godot } /// <summary> + /// Constructs a transformation matrix from the given <paramref name="projection"/> + /// by trimming the last row of the projection matrix (<c>projection.x.w</c>, + /// <c>projection.y.w</c>, <c>projection.z.w</c>, and <c>projection.w.w</c> + /// are not copied over). + /// </summary> + /// <param name="projection">The <see cref="Projection"/> to create the transform from.</param> + public Transform3D(Projection projection) + { + basis = new Basis + ( + projection.x.x, projection.y.x, projection.z.x, + projection.x.y, projection.y.y, projection.z.y, + projection.x.z, projection.y.z, projection.z.z + ); + origin = new Vector3 + ( + projection.w.x, + projection.w.y, + projection.w.z + ); + } + + /// <summary> /// Composes these two transformation matrices by multiplying them /// together. This has the effect of transforming the second transform /// (the child) by the first transform (the parent). diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs index da12309217..0ac2e2969c 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs @@ -328,11 +328,11 @@ public partial struct Variant : IDisposable VariantUtils.ConvertToSystemArrayOfGodotObject<T>((godot_variant)NativeVar); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Collections.Dictionary<TKey, TValue> AsGodotDictionary<TKey, TValue>() => + public Collections.Dictionary<TKey, TValue> AsGodotDictionary<[MustBeVariant] TKey, [MustBeVariant] TValue>() => VariantUtils.ConvertToDictionary<TKey, TValue>((godot_variant)NativeVar); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Collections.Array<T> AsGodotArray<T>() => + public Collections.Array<T> AsGodotArray<[MustBeVariant] T>() => VariantUtils.ConvertToArray<T>((godot_variant)NativeVar); [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -647,11 +647,11 @@ public partial struct Variant : IDisposable public static Variant CreateFrom(Godot.Object[] from) => from; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Variant CreateFrom<TKey, TValue>(Collections.Dictionary<TKey, TValue> from) => + public static Variant CreateFrom<[MustBeVariant] TKey, [MustBeVariant] TValue>(Collections.Dictionary<TKey, TValue> from) => CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromDictionary(from)); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Variant CreateFrom<T>(Collections.Array<T> from) => + public static Variant CreateFrom<[MustBeVariant] T>(Collections.Array<T> from) => CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromArray(from)); [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs index 4c60080ee9..07cb34cadd 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs @@ -334,6 +334,16 @@ namespace Godot } /// <summary> + /// Returns <see langword="true"/> if this vector is finite, by calling + /// <see cref="Mathf.IsFinite"/> on each component. + /// </summary> + /// <returns>Whether this vector is finite or not.</returns> + public readonly bool IsFinite() + { + return Mathf.IsFinite(x) && Mathf.IsFinite(y); + } + + /// <summary> /// Returns <see langword="true"/> if the vector is normalized, and <see langword="false"/> otherwise. /// </summary> /// <returns>A <see langword="bool"/> indicating whether or not the vector is normalized.</returns> @@ -380,24 +390,6 @@ namespace Godot } /// <summary> - /// Returns the result of the linear interpolation between - /// this vector and <paramref name="to"/> by the vector amount <paramref name="weight"/>. - /// </summary> - /// <param name="to">The destination vector for interpolation.</param> - /// <param name="weight"> - /// A vector with components on the range of 0.0 to 1.0, representing the amount of interpolation. - /// </param> - /// <returns>The resulting vector of the interpolation.</returns> - public readonly Vector2 Lerp(Vector2 to, Vector2 weight) - { - return new Vector2 - ( - Mathf.Lerp(x, to.x, weight.x), - Mathf.Lerp(y, to.y, weight.y) - ); - } - - /// <summary> /// Returns the vector with a maximum length by limiting its length to <paramref name="length"/>. /// </summary> /// <param name="length">The length to limit to.</param> @@ -529,11 +521,12 @@ namespace Godot /// <returns>The rotated vector.</returns> public readonly Vector2 Rotated(real_t angle) { - real_t sine = Mathf.Sin(angle); - real_t cosi = Mathf.Cos(angle); - return new Vector2( - x * cosi - y * sine, - x * sine + y * cosi); + (real_t sin, real_t cos) = Mathf.SinCos(angle); + return new Vector2 + ( + x * cos - y * sin, + x * sin + y * cos + ); } /// <summary> @@ -683,7 +676,8 @@ namespace Godot /// <returns>The resulting vector.</returns> public static Vector2 FromAngle(real_t angle) { - return new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)); + (real_t sin, real_t cos) = Mathf.SinCos(angle); + return new Vector2(cos, sin); } /// <summary> @@ -989,6 +983,18 @@ namespace Godot } /// <summary> + /// Returns <see langword="true"/> if this vector's values are approximately zero, + /// by running <see cref="Mathf.IsZeroApprox(real_t)"/> on each component. + /// This method is faster than using <see cref="IsEqualApprox"/> with one value + /// as a zero vector. + /// </summary> + /// <returns>Whether or not the vector is approximately zero.</returns> + public readonly bool IsZeroApprox() + { + return Mathf.IsZeroApprox(x) && Mathf.IsZeroApprox(y); + } + + /// <summary> /// Serves as the hash function for <see cref="Vector2"/>. /// </summary> /// <returns>A hash code for this vector.</returns> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs index 91be548a21..740fedec66 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs @@ -95,38 +95,6 @@ namespace Godot } /// <summary> - /// Returns this vector's angle with respect to the X axis, or (1, 0) vector, in radians. - /// - /// Equivalent to the result of <see cref="Mathf.Atan2(real_t, real_t)"/> when - /// called with the vector's <see cref="y"/> and <see cref="x"/> as parameters: <c>Mathf.Atan2(v.y, v.x)</c>. - /// </summary> - /// <returns>The angle of this vector, in radians.</returns> - public readonly real_t Angle() - { - return Mathf.Atan2(y, x); - } - - /// <summary> - /// Returns the angle to the given vector, in radians. - /// </summary> - /// <param name="to">The other vector to compare this vector to.</param> - /// <returns>The angle between the two vectors, in radians.</returns> - public readonly real_t AngleTo(Vector2i to) - { - return Mathf.Atan2(Cross(to), Dot(to)); - } - - /// <summary> - /// Returns the angle between the line connecting the two points and the X axis, in radians. - /// </summary> - /// <param name="to">The other vector to compare this vector to.</param> - /// <returns>The angle between the two vectors, in radians.</returns> - public readonly real_t AngleToPoint(Vector2i to) - { - return Mathf.Atan2(to.y - y, to.x - x); - } - - /// <summary> /// Returns the aspect ratio of this vector, the ratio of <see cref="x"/> to <see cref="y"/>. /// </summary> /// <returns>The <see cref="x"/> component divided by the <see cref="y"/> component.</returns> @@ -153,48 +121,6 @@ namespace Godot } /// <summary> - /// Returns the cross product of this vector and <paramref name="with"/>. - /// </summary> - /// <param name="with">The other vector.</param> - /// <returns>The cross product vector.</returns> - public readonly int Cross(Vector2i with) - { - return x * with.y - y * with.x; - } - - /// <summary> - /// Returns the squared distance between this vector and <paramref name="to"/>. - /// This method runs faster than <see cref="DistanceTo"/>, so prefer it if - /// you need to compare vectors or need the squared distance for some formula. - /// </summary> - /// <param name="to">The other vector to use.</param> - /// <returns>The squared distance between the two vectors.</returns> - public readonly int DistanceSquaredTo(Vector2i to) - { - return (to - this).LengthSquared(); - } - - /// <summary> - /// Returns the distance between this vector and <paramref name="to"/>. - /// </summary> - /// <param name="to">The other vector to use.</param> - /// <returns>The distance between the two vectors.</returns> - public readonly real_t DistanceTo(Vector2i to) - { - return (to - this).Length(); - } - - /// <summary> - /// Returns the dot product of this vector and <paramref name="with"/>. - /// </summary> - /// <param name="with">The other vector to use.</param> - /// <returns>The dot product of the two vectors.</returns> - public readonly int Dot(Vector2i with) - { - return x * with.x + y * with.y; - } - - /// <summary> /// Returns the length (magnitude) of this vector. /// </summary> /// <seealso cref="LengthSquared"/> @@ -242,38 +168,6 @@ namespace Godot } /// <summary> - /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components - /// and <paramref name="mod"/>. - /// </summary> - /// <param name="mod">A value representing the divisor of the operation.</param> - /// <returns> - /// A vector with each component <see cref="Mathf.PosMod(int, int)"/> by <paramref name="mod"/>. - /// </returns> - public readonly Vector2i PosMod(int mod) - { - Vector2i v = this; - v.x = Mathf.PosMod(v.x, mod); - v.y = Mathf.PosMod(v.y, mod); - return v; - } - - /// <summary> - /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components - /// and <paramref name="modv"/>'s components. - /// </summary> - /// <param name="modv">A vector representing the divisors of the operation.</param> - /// <returns> - /// A vector with each component <see cref="Mathf.PosMod(int, int)"/> by <paramref name="modv"/>'s components. - /// </returns> - public readonly Vector2i PosMod(Vector2i modv) - { - Vector2i v = this; - v.x = Mathf.PosMod(v.x, modv.x); - v.y = Mathf.PosMod(v.y, modv.y); - return v; - } - - /// <summary> /// Returns a vector with each component set to one or negative one, depending /// on the signs of this vector's components, or zero if the component is zero, /// by calling <see cref="Mathf.Sign(int)"/> on each component. @@ -287,16 +181,6 @@ namespace Godot return v; } - /// <summary> - /// Returns a perpendicular vector rotated 90 degrees counter-clockwise - /// compared to the original, with the same length. - /// </summary> - /// <returns>The perpendicular vector.</returns> - public readonly Vector2i Orthogonal() - { - return new Vector2i(y, -x); - } - // Constants private static readonly Vector2i _zero = new Vector2i(0, 0); private static readonly Vector2i _one = new Vector2i(1, 1); @@ -467,7 +351,7 @@ namespace Godot /// with the components of the given <see langword="int"/>. /// This operation uses truncated division, which is often not desired /// as it does not work well with negative numbers. - /// Consider using <see cref="PosMod(int)"/> instead + /// Consider using <see cref="Mathf.PosMod(int, int)"/> instead /// if you want to handle negative numbers. /// </summary> /// <example> @@ -490,7 +374,7 @@ namespace Godot /// with the components of the given <see cref="Vector2i"/>. /// This operation uses truncated division, which is often not desired /// as it does not work well with negative numbers. - /// Consider using <see cref="PosMod(Vector2i)"/> instead + /// Consider using <see cref="Mathf.PosMod(int, int)"/> instead /// if you want to handle negative numbers. /// </summary> /// <example> @@ -509,34 +393,6 @@ namespace Godot } /// <summary> - /// Performs a bitwise AND operation with this <see cref="Vector2i"/> - /// and the given <see langword="int"/>. - /// </summary> - /// <param name="vec">The vector to AND with.</param> - /// <param name="and">The integer to AND with.</param> - /// <returns>The result of the bitwise AND.</returns> - public static Vector2i operator &(Vector2i vec, int and) - { - vec.x &= and; - vec.y &= and; - return vec; - } - - /// <summary> - /// Performs a bitwise AND operation with this <see cref="Vector2i"/> - /// and the given <see cref="Vector2i"/>. - /// </summary> - /// <param name="vec">The left vector to AND with.</param> - /// <param name="andv">The right vector to AND with.</param> - /// <returns>The result of the bitwise AND.</returns> - public static Vector2i operator &(Vector2i vec, Vector2i andv) - { - vec.x &= andv.x; - vec.y &= andv.y; - return vec; - } - - /// <summary> /// Returns <see langword="true"/> if the vectors are equal. /// </summary> /// <param name="left">The left vector.</param> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs index fefdee33a5..b017ba5853 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs @@ -331,6 +331,16 @@ namespace Godot } /// <summary> + /// Returns <see langword="true"/> if this vector is finite, by calling + /// <see cref="Mathf.IsFinite"/> on each component. + /// </summary> + /// <returns>Whether this vector is finite or not.</returns> + public readonly bool IsFinite() + { + return Mathf.IsFinite(x) && Mathf.IsFinite(y) && Mathf.IsFinite(z); + } + + /// <summary> /// Returns <see langword="true"/> if the vector is normalized, and <see langword="false"/> otherwise. /// </summary> /// <returns>A <see langword="bool"/> indicating whether or not the vector is normalized.</returns> @@ -386,23 +396,6 @@ namespace Godot } /// <summary> - /// Returns the result of the linear interpolation between - /// this vector and <paramref name="to"/> by the vector amount <paramref name="weight"/>. - /// </summary> - /// <param name="to">The destination vector for interpolation.</param> - /// <param name="weight">A vector with components on the range of 0.0 to 1.0, representing the amount of interpolation.</param> - /// <returns>The resulting vector of the interpolation.</returns> - public readonly Vector3 Lerp(Vector3 to, Vector3 weight) - { - return new Vector3 - ( - Mathf.Lerp(x, to.x, weight.x), - Mathf.Lerp(y, to.y, weight.y), - Mathf.Lerp(z, to.z, weight.z) - ); - } - - /// <summary> /// Returns the vector with a maximum length by limiting its length to <paramref name="length"/>. /// </summary> /// <param name="length">The length to limit to.</param> @@ -1060,6 +1053,18 @@ namespace Godot } /// <summary> + /// Returns <see langword="true"/> if this vector's values are approximately zero, + /// by running <see cref="Mathf.IsZeroApprox(real_t)"/> on each component. + /// This method is faster than using <see cref="IsEqualApprox"/> with one value + /// as a zero vector. + /// </summary> + /// <returns>Whether or not the vector is approximately zero.</returns> + public readonly bool IsZeroApprox() + { + return Mathf.IsZeroApprox(x) && Mathf.IsZeroApprox(y) && Mathf.IsZeroApprox(z); + } + + /// <summary> /// Serves as the hash function for <see cref="Vector3"/>. /// </summary> /// <returns>A hash code for this vector.</returns> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs index e631a9f443..de0c6d27e7 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs @@ -129,39 +129,6 @@ namespace Godot } /// <summary> - /// Returns the squared distance between this vector and <paramref name="to"/>. - /// This method runs faster than <see cref="DistanceTo"/>, so prefer it if - /// you need to compare vectors or need the squared distance for some formula. - /// </summary> - /// <param name="to">The other vector to use.</param> - /// <returns>The squared distance between the two vectors.</returns> - public readonly int DistanceSquaredTo(Vector3i to) - { - return (to - this).LengthSquared(); - } - - /// <summary> - /// Returns the distance between this vector and <paramref name="to"/>. - /// </summary> - /// <seealso cref="DistanceSquaredTo(Vector3i)"/> - /// <param name="to">The other vector to use.</param> - /// <returns>The distance between the two vectors.</returns> - public readonly real_t DistanceTo(Vector3i to) - { - return (to - this).Length(); - } - - /// <summary> - /// Returns the dot product of this vector and <paramref name="with"/>. - /// </summary> - /// <param name="with">The other vector to use.</param> - /// <returns>The dot product of the two vectors.</returns> - public readonly int Dot(Vector3i with) - { - return x * with.x + y * with.y + z * with.z; - } - - /// <summary> /// Returns the length (magnitude) of this vector. /// </summary> /// <seealso cref="LengthSquared"/> @@ -211,40 +178,6 @@ namespace Godot } /// <summary> - /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components - /// and <paramref name="mod"/>. - /// </summary> - /// <param name="mod">A value representing the divisor of the operation.</param> - /// <returns> - /// A vector with each component <see cref="Mathf.PosMod(int, int)"/> by <paramref name="mod"/>. - /// </returns> - public readonly Vector3i PosMod(int mod) - { - Vector3i v = this; - v.x = Mathf.PosMod(v.x, mod); - v.y = Mathf.PosMod(v.y, mod); - v.z = Mathf.PosMod(v.z, mod); - return v; - } - - /// <summary> - /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components - /// and <paramref name="modv"/>'s components. - /// </summary> - /// <param name="modv">A vector representing the divisors of the operation.</param> - /// <returns> - /// A vector with each component <see cref="Mathf.PosMod(int, int)"/> by <paramref name="modv"/>'s components. - /// </returns> - public readonly Vector3i PosMod(Vector3i modv) - { - Vector3i v = this; - v.x = Mathf.PosMod(v.x, modv.x); - v.y = Mathf.PosMod(v.y, modv.y); - v.z = Mathf.PosMod(v.z, modv.z); - return v; - } - - /// <summary> /// Returns a vector with each component set to one or negative one, depending /// on the signs of this vector's components, or zero if the component is zero, /// by calling <see cref="Mathf.Sign(int)"/> on each component. @@ -455,7 +388,7 @@ namespace Godot /// with the components of the given <see langword="int"/>. /// This operation uses truncated division, which is often not desired /// as it does not work well with negative numbers. - /// Consider using <see cref="PosMod(int)"/> instead + /// Consider using <see cref="Mathf.PosMod(int, int)"/> instead /// if you want to handle negative numbers. /// </summary> /// <example> @@ -479,7 +412,7 @@ namespace Godot /// with the components of the given <see cref="Vector3i"/>. /// This operation uses truncated division, which is often not desired /// as it does not work well with negative numbers. - /// Consider using <see cref="PosMod(Vector3i)"/> instead + /// Consider using <see cref="Mathf.PosMod(int, int)"/> instead /// if you want to handle negative numbers. /// </summary> /// <example> @@ -499,36 +432,6 @@ namespace Godot } /// <summary> - /// Performs a bitwise AND operation with this <see cref="Vector3i"/> - /// and the given <see langword="int"/>. - /// </summary> - /// <param name="vec">The vector to AND with.</param> - /// <param name="and">The integer to AND with.</param> - /// <returns>The result of the bitwise AND.</returns> - public static Vector3i operator &(Vector3i vec, int and) - { - vec.x &= and; - vec.y &= and; - vec.z &= and; - return vec; - } - - /// <summary> - /// Performs a bitwise AND operation with this <see cref="Vector3i"/> - /// and the given <see cref="Vector3i"/>. - /// </summary> - /// <param name="vec">The left vector to AND with.</param> - /// <param name="andv">The right vector to AND with.</param> - /// <returns>The result of the bitwise AND.</returns> - public static Vector3i operator &(Vector3i vec, Vector3i andv) - { - vec.x &= andv.x; - vec.y &= andv.y; - vec.z &= andv.z; - return vec; - } - - /// <summary> /// Returns <see langword="true"/> if the vectors are equal. /// </summary> /// <param name="left">The left vector.</param> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs index 3191e8adc0..0f4528bb40 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4.cs @@ -280,6 +280,16 @@ namespace Godot } /// <summary> + /// Returns <see langword="true"/> if this vector is finite, by calling + /// <see cref="Mathf.IsFinite"/> on each component. + /// </summary> + /// <returns>Whether this vector is finite or not.</returns> + public readonly bool IsFinite() + { + return Mathf.IsFinite(x) && Mathf.IsFinite(y) && Mathf.IsFinite(z) && Mathf.IsFinite(w); + } + + /// <summary> /// Returns <see langword="true"/> if the vector is normalized, and <see langword="false"/> otherwise. /// </summary> /// <returns>A <see langword="bool"/> indicating whether or not the vector is normalized.</returns> @@ -857,6 +867,18 @@ namespace Godot } /// <summary> + /// Returns <see langword="true"/> if this vector's values are approximately zero, + /// by running <see cref="Mathf.IsZeroApprox(real_t)"/> on each component. + /// This method is faster than using <see cref="IsEqualApprox"/> with one value + /// as a zero vector. + /// </summary> + /// <returns>Whether or not the vector is approximately zero.</returns> + public readonly bool IsZeroApprox() + { + return Mathf.IsZeroApprox(x) && Mathf.IsZeroApprox(y) && Mathf.IsZeroApprox(z) && Mathf.IsZeroApprox(w); + } + + /// <summary> /// Serves as the hash function for <see cref="Vector4"/>. /// </summary> /// <returns>A hash code for this vector.</returns> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs index 8146991fd7..00ecc64856 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector4i.cs @@ -433,38 +433,6 @@ namespace Godot } /// <summary> - /// Performs a bitwise AND operation with this <see cref="Vector4i"/> - /// and the given <see langword="int"/>. - /// </summary> - /// <param name="vec">The vector to AND with.</param> - /// <param name="and">The integer to AND with.</param> - /// <returns>The result of the bitwise AND.</returns> - public static Vector4i operator &(Vector4i vec, int and) - { - vec.x &= and; - vec.y &= and; - vec.z &= and; - vec.w &= and; - return vec; - } - - /// <summary> - /// Performs a bitwise AND operation with this <see cref="Vector4i"/> - /// and the given <see cref="Vector4i"/>. - /// </summary> - /// <param name="vec">The left vector to AND with.</param> - /// <param name="andv">The right vector to AND with.</param> - /// <returns>The result of the bitwise AND.</returns> - public static Vector4i operator &(Vector4i vec, Vector4i andv) - { - vec.x &= andv.x; - vec.y &= andv.y; - vec.z &= andv.z; - vec.w &= andv.w; - return vec; - } - - /// <summary> /// Returns <see langword="true"/> if the vectors are equal. /// </summary> /// <param name="left">The left vector.</param> |