From e10a1e078f835934d39f7e575904939e9019bb7d Mon Sep 17 00:00:00 2001 From: Aaron Franke Date: Tue, 21 Jul 2020 20:10:35 -0400 Subject: [PATCH] [3.2] Add C# XML documentation to core C# math types --- .../glue/GodotSharp/GodotSharp/Core/AABB.cs | 234 ++++++++++- .../glue/GodotSharp/GodotSharp/Core/Basis.cs | 301 ++++++++++++-- .../glue/GodotSharp/GodotSharp/Core/Color.cs | 276 ++++++++++++- .../glue/GodotSharp/GodotSharp/Core/Colors.cs | 4 + .../glue/GodotSharp/GodotSharp/Core/Mathf.cs | 329 ++++++++++++++- .../GodotSharp/GodotSharp/Core/MathfEx.cs | 49 +++ .../glue/GodotSharp/GodotSharp/Core/Plane.cs | 149 ++++++- .../glue/GodotSharp/GodotSharp/Core/Quat.cs | 230 +++++++++-- .../glue/GodotSharp/GodotSharp/Core/Rect2.cs | 183 ++++++++- .../GodotSharp/GodotSharp/Core/Transform.cs | 142 ++++++- .../GodotSharp/GodotSharp/Core/Transform2D.cs | 180 +++++++- .../GodotSharp/GodotSharp/Core/Vector2.cs | 371 +++++++++++++++-- .../GodotSharp/GodotSharp/Core/Vector3.cs | 387 +++++++++++++++--- 13 files changed, 2605 insertions(+), 230 deletions(-) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs index 6a4f7855517..3aecce50f54 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs @@ -14,6 +14,10 @@ using real_t = System.Single; namespace Godot { + /// + /// Axis-Aligned Bounding Box. AABB consists of a position, a size, and + /// several utility functions. It is typically used for fast overlap tests. + /// [Serializable] [StructLayout(LayoutKind.Sequential)] public struct AABB : IEquatable @@ -21,24 +25,55 @@ namespace Godot private Vector3 _position; private Vector3 _size; + /// + /// Beginning corner. Typically has values lower than End. + /// + /// Directly uses a private field. public Vector3 Position { get { return _position; } set { _position = value; } } + /// + /// Size from Position to End. Typically all components are positive. + /// If the size is negative, you can use to fix it. + /// + /// Directly uses a private field. public Vector3 Size { get { return _size; } set { _size = value; } } + /// + /// Ending corner. This is calculated as plus + /// . Setting this value will change the size. + /// + /// Getting is equivalent to `value = Position + Size`, setting is equivalent to `Size = value - Position`. public Vector3 End { get { return _position + _size; } set { _size = value - _position; } } + /// + /// Returns an AABB with equivalent position and size, modified so that + /// the most-negative corner is the origin and the size is positive. + /// + /// The modified AABB. + public AABB Abs() + { + Vector3 end = End; + Vector3 topLeft = new Vector3(Mathf.Min(_position.x, end.x), Mathf.Min(_position.y, end.y), Mathf.Min(_position.z, end.z)); + return new AABB(topLeft, _size.Abs()); + } + + /// + /// Returns true if this AABB completely encloses another one. + /// + /// The other AABB that may be enclosed. + /// A bool for whether or not this AABB encloses `b`. public bool Encloses(AABB with) { Vector3 src_min = _position; @@ -54,33 +89,59 @@ namespace Godot src_max.z > dst_max.z; } + /// + /// Returns this AABB expanded to include a given point. + /// + /// The point to include. + /// The expanded AABB. public AABB Expand(Vector3 point) { Vector3 begin = _position; Vector3 end = _position + _size; if (point.x < begin.x) + { begin.x = point.x; + } if (point.y < begin.y) + { begin.y = point.y; + } if (point.z < begin.z) + { begin.z = point.z; + } if (point.x > end.x) + { end.x = point.x; + } if (point.y > end.y) + { end.y = point.y; + } if (point.z > end.z) + { end.z = point.z; + } return new AABB(begin, end - begin); } + /// + /// Returns the area of the AABB. + /// + /// The area. public real_t GetArea() { return _size.x * _size.y * _size.z; } + /// + /// Gets the position of one of the 8 endpoints of the AABB. + /// + /// Which endpoint to get. + /// An endpoint of the AABB. public Vector3 GetEndpoint(int idx) { switch (idx) @@ -106,6 +167,10 @@ namespace Godot } } + /// + /// Returns the normalized longest axis of the AABB. + /// + /// A vector representing the normalized longest axis of the AABB. public Vector3 GetLongestAxis() { var axis = new Vector3(1f, 0f, 0f); @@ -125,6 +190,10 @@ namespace Godot return axis; } + /// + /// Returns the index of the longest axis of the AABB. + /// + /// A index for which axis is longest. public Vector3.Axis GetLongestAxisIndex() { var axis = Vector3.Axis.X; @@ -144,6 +213,10 @@ namespace Godot return axis; } + /// + /// Returns the scalar length of the longest axis of the AABB. + /// + /// The scalar length of the longest axis of the AABB. public real_t GetLongestAxisSize() { real_t max_size = _size.x; @@ -157,6 +230,10 @@ namespace Godot return max_size; } + /// + /// Returns the normalized shortest axis of the AABB. + /// + /// A vector representing the normalized shortest axis of the AABB. public Vector3 GetShortestAxis() { var axis = new Vector3(1f, 0f, 0f); @@ -176,6 +253,10 @@ namespace Godot return axis; } + /// + /// Returns the index of the shortest axis of the AABB. + /// + /// A index for which axis is shortest. public Vector3.Axis GetShortestAxisIndex() { var axis = Vector3.Axis.X; @@ -195,6 +276,10 @@ namespace Godot return axis; } + /// + /// Returns the scalar length of the shortest axis of the AABB. + /// + /// The scalar length of the shortest axis of the AABB. public real_t GetShortestAxisSize() { real_t max_size = _size.x; @@ -208,6 +293,12 @@ namespace Godot return max_size; } + /// + /// Returns the support point in a given direction. + /// This is useful for collision detection algorithms. + /// + /// The direction to find support for. + /// A vector representing the support. public Vector3 GetSupport(Vector3 dir) { Vector3 half_extents = _size * 0.5f; @@ -219,6 +310,11 @@ namespace Godot dir.z > 0f ? -half_extents.z : half_extents.z); } + /// + /// Returns a copy of the AABB grown a given amount of units towards all the sides. + /// + /// The amount to grow by. + /// The grown AABB. public AABB Grow(real_t by) { var res = this; @@ -233,16 +329,29 @@ namespace Godot return res; } + /// + /// Returns true if the AABB is flat or empty, or false otherwise. + /// + /// A bool for whether or not the AABB has area. public bool HasNoArea() { return _size.x <= 0f || _size.y <= 0f || _size.z <= 0f; } + /// + /// Returns true if the AABB has no surface (no size), or false otherwise. + /// + /// A bool for whether or not the AABB has area. public bool HasNoSurface() { return _size.x <= 0f && _size.y <= 0f && _size.z <= 0f; } + /// + /// Returns true if the AABB contains a point, or false otherwise. + /// + /// The point to check. + /// A bool for whether or not the AABB contains `point`. public bool HasPoint(Vector3 point) { if (point.x < _position.x) @@ -261,6 +370,11 @@ namespace Godot return true; } + /// + /// Returns the intersection of this AABB and `b`. + /// + /// The other AABB. + /// The clipped AABB. public AABB Intersection(AABB with) { Vector3 src_min = _position; @@ -297,24 +411,57 @@ namespace Godot return new AABB(min, max - min); } - public bool Intersects(AABB with) + /// + /// Returns true if the AABB overlaps with `b` + /// (i.e. they have at least one point in common). + /// + /// If `includeBorders` is true, they will also be considered overlapping + /// if their borders touch, even without intersection. + /// + /// The other AABB to check for intersections with. + /// Whether or not to consider borders. + /// A bool for whether or not they are intersecting. + public bool Intersects(AABB with, bool includeBorders = 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; + 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; + } return true; } + /// + /// Returns true if the AABB is on both sides of `plane`. + /// + /// The plane to check for intersection. + /// A bool for whether or not the AABB intersects the plane. public bool IntersectsPlane(Plane plane) { Vector3[] points = @@ -335,14 +482,24 @@ namespace Godot for (int i = 0; i < 8; i++) { if (plane.DistanceTo(points[i]) > 0) + { over = true; + } else + { under = true; + } } return under && over; } + /// + /// Returns true if the AABB intersects the line segment between `from` and `to`. + /// + /// The start of the line segment. + /// The end of the line segment. + /// A bool for whether or not the AABB intersects the line segment. public bool IntersectsSegment(Vector3 from, Vector3 to) { real_t min = 0f; @@ -359,7 +516,9 @@ namespace Godot if (segFrom < segTo) { if (segFrom > boxEnd || segTo < boxBegin) + { return false; + } real_t length = segTo - segFrom; cmin = segFrom < boxBegin ? (boxBegin - segFrom) / length : 0f; @@ -368,7 +527,9 @@ namespace Godot else { if (segTo > boxEnd || segFrom < boxBegin) + { return false; + } real_t length = segTo - segFrom; cmin = segFrom > boxEnd ? (boxEnd - segFrom) / length : 0f; @@ -381,14 +542,23 @@ namespace Godot } if (cmax < max) + { max = cmax; + } if (max < min) + { return false; + } } return true; } + /// + /// Returns a larger AABB that contains this AABB and `b`. + /// + /// The other AABB. + /// The merged AABB. public AABB Merge(AABB with) { Vector3 beg1 = _position; @@ -411,22 +581,52 @@ namespace Godot return new AABB(min, max - min); } - // Constructors + /// + /// Constructs an AABB from a position and size. + /// + /// The position. + /// The size, typically positive. public AABB(Vector3 position, Vector3 size) { _position = position; _size = size; } + + /// + /// Constructs an AABB from a position, width, height, and depth. + /// + /// The position. + /// The width, typically positive. + /// The height, typically positive. + /// The depth, typically positive. public AABB(Vector3 position, real_t width, real_t height, real_t depth) { _position = position; _size = new Vector3(width, height, depth); } + + /// + /// Constructs an AABB from x, y, z, and size. + /// + /// The position's X coordinate. + /// The position's Y coordinate. + /// The position's Z coordinate. + /// The size, typically positive. public AABB(real_t x, real_t y, real_t z, Vector3 size) { _position = new Vector3(x, y, z); _size = size; } + + /// + /// Constructs an AABB from x, y, z, width, height, and depth. + /// + /// The position's X coordinate. + /// The position's Y coordinate. + /// The position's Z coordinate. + /// The width, typically positive. + /// The height, typically positive. + /// The depth, typically positive. public AABB(real_t x, real_t y, real_t z, real_t width, real_t height, real_t depth) { _position = new Vector3(x, y, z); @@ -458,6 +658,12 @@ namespace Godot return _position == other._position && _size == other._size; } + /// + /// Returns true if this AABB and `other` are approximately equal, by running + /// on each component. + /// + /// The other AABB to compare. + /// Whether or not the AABBs are approximately equal. public bool IsEqualApprox(AABB other) { return _position.IsEqualApprox(other._position) && _size.IsEqualApprox(other._size); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs index 55408fecb8a..bbc4dfdfeb6 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs @@ -8,6 +8,20 @@ using real_t = System.Single; namespace Godot { + /// + /// 3×3 matrix used for 3D rotation and scale. + /// Almost always used as an orthogonal basis for a Transform. + /// + /// Contains 3 vector fields X, Y and Z as its columns, which are typically + /// interpreted as the local basis vectors of a 3D transformation. For such use, + /// it is composed of a scaling and a rotation matrix, in that order (M = R.S). + /// + /// Can also be accessed as array of 3D vectors. These vectors are normally + /// orthogonal to each other, but are not necessarily normalized (due to scaling). + /// + /// For more information, read this documentation article: + /// https://docs.godotengine.org/en/latest/tutorials/math/matrices_and_transforms.html + /// [Serializable] [StructLayout(LayoutKind.Sequential)] public struct Basis : IEquatable @@ -15,9 +29,9 @@ namespace Godot // NOTE: x, y and z are public-only. Use Column0, Column1 and Column2 internally. /// - /// Returns the basis matrix’s x vector. - /// This is equivalent to . + /// The basis matrix's X vector (column 0). /// + /// Equivalent to and array index `[0]`. public Vector3 x { get => Column0; @@ -25,9 +39,9 @@ namespace Godot } /// - /// Returns the basis matrix’s y vector. - /// This is equivalent to . + /// The basis matrix's Y vector (column 1). /// + /// Equivalent to and array index `[1]`. public Vector3 y { get => Column1; @@ -35,19 +49,40 @@ namespace Godot } /// - /// Returns the basis matrix’s z vector. - /// This is equivalent to . + /// The basis matrix's Z vector (column 2). /// + /// Equivalent to and array index `[2]`. public Vector3 z { get => Column2; set => Column2 = value; } + /// + /// Row 0 of the basis matrix. Shows which vectors contribute + /// to the X direction. Rows are not very useful for user code, + /// but are more efficient for some internal calculations. + /// public Vector3 Row0; + + /// + /// Row 1 of the basis matrix. Shows which vectors contribute + /// to the Y direction. Rows are not very useful for user code, + /// but are more efficient for some internal calculations. + /// public Vector3 Row1; + + /// + /// Row 2 of the basis matrix. Shows which vectors contribute + /// to the Z direction. Rows are not very useful for user code, + /// but are more efficient for some internal calculations. + /// public Vector3 Row2; + /// + /// Column 0 of the basis matrix (the X vector). + /// + /// Equivalent to and array index `[0]`. public Vector3 Column0 { get => new Vector3(Row0.x, Row1.x, Row2.x); @@ -58,6 +93,11 @@ namespace Godot this.Row2.x = value.z; } } + + /// + /// Column 1 of the basis matrix (the Y vector). + /// + /// Equivalent to and array index `[1]`. public Vector3 Column1 { get => new Vector3(Row0.y, Row1.y, Row2.y); @@ -68,6 +108,11 @@ namespace Godot this.Row2.y = value.z; } } + + /// + /// Column 2 of the basis matrix (the Z vector). + /// + /// Equivalent to and array index `[2]`. public Vector3 Column2 { get => new Vector3(Row0.z, Row1.z, Row2.z); @@ -79,6 +124,10 @@ namespace Godot } } + /// + /// The scale of this basis. + /// + /// Equivalent to the lengths of each column vector, but negative if the determinant is negative. public Vector3 Scale { get @@ -86,11 +135,18 @@ namespace Godot real_t detSign = Mathf.Sign(Determinant()); return detSign * new Vector3 ( - new Vector3(this.Row0[0], this.Row1[0], this.Row2[0]).Length(), - new Vector3(this.Row0[1], this.Row1[1], this.Row2[1]).Length(), - new Vector3(this.Row0[2], this.Row1[2], this.Row2[2]).Length() + Column0.Length(), + Column1.Length(), + Column2.Length() ); } + set + { + value /= Scale; // Value becomes what's called "delta_scale" in core. + Column0 *= value.x; + Column1 *= value.y; + Column2 *= value.z; + } } /// @@ -157,8 +213,9 @@ namespace Godot real_t det = orthonormalizedBasis.Determinant(); if (det < 0) { - // Ensure that the determinant is 1, such that result is a proper rotation matrix which can be represented by Euler angles. - orthonormalizedBasis = orthonormalizedBasis.Scaled(Vector3.NegOne); + // Ensure that the determinant is 1, such that result is a proper + // rotation matrix which can be represented by Euler angles. + orthonormalizedBasis = orthonormalizedBasis.Scaled(-Vector3.One); } return orthonormalizedBasis.Quat(); @@ -182,6 +239,15 @@ namespace Godot Row2 = new Vector3(0, 0, diagonal.z); } + /// + /// Returns the determinant of the basis matrix. If the basis is + /// uniformly scaled, its determinant is the square of the scale. + /// + /// A negative determinant means the basis has a negative scale. + /// A zero determinant means the basis isn't invertible, + /// and is usually considered invalid. + /// + /// The determinant of the basis matrix. public real_t Determinant() { real_t cofac00 = Row1[1] * Row2[2] - Row1[2] * Row2[1]; @@ -191,6 +257,16 @@ namespace Godot return Row0[0] * cofac00 + Row0[1] * cofac10 + Row0[2] * cofac20; } + /// + /// Returns the basis's rotation in the form of Euler angles + /// (in the YXZ convention: when *decomposing*, first Z, then X, and Y last). + /// The returned vector contains the rotation angles in + /// the format (X angle, Y angle, Z angle). + /// + /// Consider using the method instead, which + /// returns a quaternion instead of Euler angles. + /// + /// A Vector3 representing the basis rotation in Euler angles. public Vector3 GetEuler() { Basis m = Orthonormalized(); @@ -223,6 +299,12 @@ namespace Godot return euler; } + /// + /// Get rows by index. Rows are not very useful for user code, + /// but are more efficient for some internal calculations. + /// + /// Which row. + /// One of `Row0`, `Row1`, or `Row2`. public Vector3 GetRow(int index) { switch (index) @@ -238,6 +320,12 @@ namespace Godot } } + /// + /// Sets rows by index. Rows are not very useful for user code, + /// but are more efficient for some internal calculations. + /// + /// Which row. + /// The vector to set the row to. public void SetRow(int index, Vector3 value) { switch (index) @@ -256,22 +344,49 @@ namespace Godot } } + /// + /// Deprecated, please use the array operator instead. + /// + /// Which column. + /// One of `Column0`, `Column1`, or `Column2`. + [Obsolete("GetColumn is deprecated. Use the array operator instead.")] public Vector3 GetColumn(int index) { return this[index]; } + /// + /// Deprecated, please use the array operator instead. + /// + /// Which column. + /// The vector to set the column to. + [Obsolete("SetColumn is deprecated. Use the array operator instead.")] public void SetColumn(int index, Vector3 value) { this[index] = value; } - [Obsolete("GetAxis is deprecated. Use GetColumn instead.")] + /// + /// Deprecated, please use the array operator instead. + /// + /// Which column. + /// One of `Column0`, `Column1`, or `Column2`. + [Obsolete("GetAxis is deprecated. Use the array operator instead.")] public Vector3 GetAxis(int axis) { return new Vector3(this.Row0[axis], this.Row1[axis], this.Row2[axis]); } + /// + /// 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 editor. + /// + /// For further details, refer to the Godot source code. + /// + /// The orthogonal index. public int GetOrthogonalIndex() { var orth = this; @@ -285,11 +400,17 @@ namespace Godot 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; @@ -300,12 +421,18 @@ namespace Godot for (int i = 0; i < 24; i++) { if (orth == _orthoBases[i]) + { return i; + } } return 0; } + /// + /// Returns the inverse of the matrix. + /// + /// The inverse matrix. public Basis Inverse() { real_t cofac00 = Row1[1] * Row2[2] - Row1[2] * Row2[1]; @@ -315,7 +442,9 @@ namespace Godot real_t det = Row0[0] * cofac00 + Row0[1] * cofac10 + Row0[2] * cofac20; if (det == 0) + { throw new InvalidOperationException("Matrix determinant is zero and cannot be inverted."); + } real_t detInv = 1.0f / det; @@ -334,11 +463,17 @@ namespace Godot ); } + /// + /// Returns the orthonormalized version of the basis matrix (useful to + /// call occasionally to avoid rounding errors for orthogonal matrices). + /// This performs a Gram-Schmidt orthonormalization on the basis of the matrix. + /// + /// An orthonormalized basis matrix. public Basis Orthonormalized() { - Vector3 column0 = GetColumn(0); - Vector3 column1 = GetColumn(1); - Vector3 column2 = GetColumn(2); + Vector3 column0 = this[0]; + Vector3 column1 = this[1]; + Vector3 column2 = this[2]; column0.Normalize(); column1 = column1 - column0 * column0.Dot(column1); @@ -349,48 +484,86 @@ namespace Godot return new Basis(column0, column1, column2); } + /// + /// Introduce an additional rotation around the given `axis` + /// by `phi` (in radians). The axis must be a normalized vector. + /// + /// The axis to rotate around. Must be normalized. + /// The angle to rotate, in radians. + /// The rotated basis matrix. public Basis Rotated(Vector3 axis, real_t phi) { return new Basis(axis, phi) * this; } + /// + /// Introduce an additional scaling specified by the given 3D scaling factor. + /// + /// The scale to introduce. + /// The scaled basis matrix. public Basis Scaled(Vector3 scale) { - var b = this; + Basis b = this; b.Row0 *= scale.x; b.Row1 *= scale.y; b.Row2 *= scale.z; return b; } - public Basis Slerp(Basis target, real_t t) + /// + /// Assuming that the matrix is a proper rotation matrix, slerp performs + /// a spherical-linear interpolation with another rotation matrix. + /// + /// The destination basis for interpolation. + /// A value on the range of 0.0 to 1.0, representing the amount of interpolation. + /// The resulting basis matrix of the interpolation. + public Basis Slerp(Basis target, real_t weight) { - var from = new Quat(this); - var to = new Quat(target); + Quat from = new Quat(this); + Quat to = new Quat(target); - var b = new Basis(from.Slerp(to, t)); - b.Row0 *= Mathf.Lerp(Row0.Length(), target.Row0.Length(), t); - b.Row1 *= Mathf.Lerp(Row1.Length(), target.Row1.Length(), t); - b.Row2 *= Mathf.Lerp(Row2.Length(), target.Row2.Length(), t); + Basis b = new Basis(from.Slerp(to, weight)); + b.Row0 *= Mathf.Lerp(Row0.Length(), target.Row0.Length(), weight); + b.Row1 *= Mathf.Lerp(Row1.Length(), target.Row1.Length(), weight); + b.Row2 *= Mathf.Lerp(Row2.Length(), target.Row2.Length(), weight); return b; } + /// + /// Transposed dot product with the X axis of the matrix. + /// + /// A vector to calculate the dot product with. + /// The resulting dot product. public real_t Tdotx(Vector3 with) { return this.Row0[0] * with[0] + this.Row1[0] * with[1] + this.Row2[0] * with[2]; } + /// + /// Transposed dot product with the Y axis of the matrix. + /// + /// A vector to calculate the dot product with. + /// The resulting dot product. public real_t Tdoty(Vector3 with) { return this.Row0[1] * with[0] + this.Row1[1] * with[1] + this.Row2[1] * with[2]; } + /// + /// Transposed dot product with the Z axis of the matrix. + /// + /// A vector to calculate the dot product with. + /// The resulting dot product. public real_t Tdotz(Vector3 with) { return this.Row0[2] * with[0] + this.Row1[2] * with[1] + this.Row2[2] * with[2]; } + /// + /// Returns the transposed version of the basis matrix. + /// + /// The transposed basis matrix. public Basis Transposed() { var tr = this; @@ -410,6 +583,11 @@ namespace Godot return tr; } + /// + /// Returns a vector transformed (multiplied) by the basis matrix. + /// + /// A vector to transform. + /// The transfomed vector. public Vector3 Xform(Vector3 v) { return new Vector3 @@ -420,6 +598,14 @@ namespace Godot ); } + /// + /// Returns a vector transformed (multiplied) by the transposed basis matrix. + /// + /// Note: This results in a multiplication by the inverse of the + /// basis matrix only if it represents a rotation-reflection. + /// + /// A vector to inversely transform. + /// The inversely transfomed vector. public Vector3 XformInv(Vector3 v) { return new Vector3 @@ -430,6 +616,12 @@ namespace Godot ); } + /// + /// Returns the basis's rotation in the form of a quaternion. + /// See if you need Euler angles, but keep in + /// mind that quaternions should generally be preferred to Euler angles. + /// + /// A representing the basis's rotation. public Quat Quat() { real_t trace = Row0[0] + Row1[1] + Row2[2]; @@ -514,11 +706,33 @@ namespace Godot private static readonly Basis _flipY = new Basis(1, 0, 0, 0, -1, 0, 0, 0, 1); private static readonly Basis _flipZ = new Basis(1, 0, 0, 0, 1, 0, 0, 0, -1); + /// + /// The identity basis, with no rotation or scaling applied. + /// This is used as a replacement for `Basis()` in GDScript. + /// Do not use `new Basis()` with no arguments in C#, because it sets all values to zero. + /// + /// Equivalent to `new Basis(Vector3.Right, Vector3.Up, Vector3.Back)`. public static Basis Identity { get { return _identity; } } + /// + /// The basis that will flip something along the X axis when used in a transformation. + /// + /// Equivalent to `new Basis(Vector3.Left, Vector3.Up, Vector3.Back)`. public static Basis FlipX { get { return _flipX; } } + /// + /// The basis that will flip something along the Y axis when used in a transformation. + /// + /// Equivalent to `new Basis(Vector3.Right, Vector3.Down, Vector3.Back)`. public static Basis FlipY { get { return _flipY; } } + /// + /// The basis that will flip something along the Z axis when used in a transformation. + /// + /// Equivalent to `new Basis(Vector3.Right, Vector3.Up, Vector3.Forward)`. public static Basis FlipZ { get { return _flipZ; } } + /// + /// Constructs a pure rotation basis matrix from the given quaternion. + /// + /// The quaternion to create the basis from. public Basis(Quat quat) { real_t s = 2.0f / quat.LengthSquared; @@ -541,26 +755,41 @@ namespace Godot Row2 = new Vector3(xz - wy, yz + wx, 1.0f - (xx + yy)); } - public Basis(Vector3 euler) + /// + /// Constructs a pure rotation basis matrix from the given Euler angles + /// (in the YXZ convention: when *composing*, first Y, then X, and Z last), + /// given in the vector format as (X angle, Y angle, Z angle). + /// + /// Consider using the constructor instead, which + /// uses a quaternion instead of Euler angles. + /// + /// The Euler angles to create the basis from. + public Basis(Vector3 eulerYXZ) { real_t c; real_t s; - c = Mathf.Cos(euler.x); - s = Mathf.Sin(euler.x); + c = Mathf.Cos(eulerYXZ.x); + s = Mathf.Sin(eulerYXZ.x); var xmat = new Basis(1, 0, 0, 0, c, -s, 0, s, c); - c = Mathf.Cos(euler.y); - s = Mathf.Sin(euler.y); + c = Mathf.Cos(eulerYXZ.y); + s = Mathf.Sin(eulerYXZ.y); var ymat = new Basis(c, 0, s, 0, 1, 0, -s, 0, c); - c = Mathf.Cos(euler.z); - s = Mathf.Sin(euler.z); + c = Mathf.Cos(eulerYXZ.z); + s = Mathf.Sin(eulerYXZ.z); var zmat = new Basis(c, -s, 0, s, c, 0, 0, 0, 1); this = ymat * xmat * zmat; } + /// + /// Constructs a pure rotation basis matrix, rotated around the given `axis` + /// by `phi` (in radians). The axis must be a normalized vector. + /// + /// The axis to rotate around. Must be normalized. + /// The angle to rotate, in radians. public Basis(Vector3 axis, real_t phi) { Vector3 axisSq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z); @@ -588,6 +817,12 @@ namespace Godot Row2.y = xyzt + zyxs; } + /// + /// Constructs a basis matrix from 3 axis vectors (matrix columns). + /// + /// The X vector, or Column0. + /// The Y vector, or Column1. + /// The Z vector, or Column2. public Basis(Vector3 column0, Vector3 column1, Vector3 column2) { Row0 = new Vector3(column0.x, column1.x, column2.x); @@ -643,6 +878,12 @@ namespace Godot return Row0.Equals(other.Row0) && Row1.Equals(other.Row1) && Row2.Equals(other.Row2); } + /// + /// Returns true if this basis and `other` are approximately equal, by running + /// on each component. + /// + /// The other basis to compare. + /// Whether or not the matrices are approximately equal. public bool IsEqualApprox(Basis other) { return Row0.IsEqualApprox(other.Row0) && Row1.IsEqualApprox(other.Row1) && Row2.IsEqualApprox(other.Row2); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs index 0462ef11259..59b67e3315d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs @@ -3,15 +3,44 @@ using System.Runtime.InteropServices; namespace Godot { + /// + /// A color represented by red, green, blue, and alpha (RGBA) components. + /// The alpha component is often used for transparency. + /// Values are in floating-point and usually range from 0 to 1. + /// Some properties (such as CanvasItem.modulate) may accept values + /// greater than 1 (overbright or HDR colors). + /// + /// If you want to supply values in a range of 0 to 255, you should use + /// and the `r8`/`g8`/`b8`/`a8` properties. + /// [Serializable] [StructLayout(LayoutKind.Sequential)] public struct Color : IEquatable { + /// + /// The color's red component, typically on the range of 0 to 1. + /// public float r; + + /// + /// The color's green component, typically on the range of 0 to 1. + /// public float g; + + /// + /// The color's blue component, typically on the range of 0 to 1. + /// public float b; + + /// + /// The color's alpha (transparency) component, typically on the range of 0 to 1. + /// public float a; + /// + /// Wrapper for that uses the range 0 to 255 instead of 0 to 1. + /// + /// Getting is equivalent to multiplying by 255 and rounding. Setting is equivalent to dividing by 255. public int r8 { get @@ -24,6 +53,10 @@ namespace Godot } } + /// + /// Wrapper for that uses the range 0 to 255 instead of 0 to 1. + /// + /// Getting is equivalent to multiplying by 255 and rounding. Setting is equivalent to dividing by 255. public int g8 { get @@ -36,6 +69,10 @@ namespace Godot } } + /// + /// Wrapper for that uses the range 0 to 255 instead of 0 to 1. + /// + /// Getting is equivalent to multiplying by 255 and rounding. Setting is equivalent to dividing by 255. public int b8 { get @@ -48,6 +85,10 @@ namespace Godot } } + /// + /// Wrapper for that uses the range 0 to 255 instead of 0 to 1. + /// + /// Getting is equivalent to multiplying by 255 and rounding. Setting is equivalent to dividing by 255. public int a8 { get @@ -60,6 +101,10 @@ namespace Godot } } + /// + /// The HSV hue of this color, on the range 0 to 1. + /// + /// Getting is a long process, refer to the source code for details. Setting uses . public float h { get @@ -70,21 +115,31 @@ namespace Godot float delta = max - min; if (delta == 0) + { return 0; + } float h; if (r == max) + { h = (g - b) / delta; // Between yellow & magenta + } else if (g == max) + { h = 2 + (b - r) / delta; // Between cyan & yellow + } else + { h = 4 + (r - g) / delta; // Between magenta & cyan + } h /= 6.0f; if (h < 0) + { h += 1.0f; + } return h; } @@ -94,6 +149,10 @@ namespace Godot } } + /// + /// The HSV saturation of this color, on the range 0 to 1. + /// + /// Getting is equivalent to the ratio between the min and max RGB value. Setting uses . public float s { get @@ -103,7 +162,7 @@ namespace Godot float delta = max - min; - return max != 0 ? delta / max : 0; + return max == 0 ? 0 : delta / max; } set { @@ -111,6 +170,10 @@ namespace Godot } } + /// + /// The HSV value (brightness) of this color, on the range 0 to 1. + /// + /// Getting is equivalent to using `Max()` on the RGB components. Setting uses . public float v { get @@ -123,6 +186,14 @@ namespace Godot } } + /// + /// Returns a color according to the standardized name, with the + /// specified alpha value. Supported color names are the same as + /// the constants defined in . + /// + /// The name of the color. + /// The alpha (transparency) component represented on the range of 0 to 1. Default: 1. + /// The constructed color. public static Color ColorN(string name, float alpha = 1f) { name = name.Replace(" ", String.Empty); @@ -142,6 +213,10 @@ namespace Godot return color; } + /// + /// Access color components using their index. + /// + /// `[0]` is equivalent to `.r`, `[1]` is equivalent to `.g`, `[2]` is equivalent to `.b`, `[3]` is equivalent to `.a`. public float this[int index] { get @@ -182,6 +257,13 @@ namespace Godot } } + /// + /// Converts a color to HSV values. This is equivalent to using each of + /// the `h`/`s`/`v` properties, but much more efficient. + /// + /// Output parameter for the HSV hue. + /// Output parameter for the HSV saturation. + /// Output parameter for the HSV value. public void ToHsv(out float hue, out float saturation, out float value) { float max = (float)Mathf.Max(r, Mathf.Max(g, b)); @@ -212,6 +294,16 @@ namespace Godot value = max; } + /// + /// Constructs a color from an HSV profile, with values on the + /// range of 0 to 1. This is equivalent to using each of + /// the `h`/`s`/`v` properties, but much more efficient. + /// + /// The HSV hue, typically on the range of 0 to 1. + /// The HSV saturation, typically on the range of 0 to 1. + /// The HSV value (brightness), typically on the range of 0 to 1. + /// The alpha (transparency) value, typically on the range of 0 to 1. + /// The constructed color. public static Color FromHsv(float hue, float saturation, float value, float alpha = 1.0f) { if (saturation == 0) @@ -249,6 +341,13 @@ namespace Godot } } + /// + /// Returns a new color resulting from blending this color over another. + /// If the color is opaque, the result is also opaque. + /// The second color may have a range of alpha values. + /// + /// The color to blend over. + /// This color blended over `over`. public Color Blend(Color over) { Color res; @@ -268,6 +367,10 @@ namespace Godot return res; } + /// + /// Returns the most contrasting color. + /// + /// The most contrasting color public Color Contrasted() { return new Color( @@ -278,6 +381,12 @@ namespace Godot ); } + /// + /// Returns a new color resulting from making this color darker + /// by the specified ratio (on the range of 0 to 1). + /// + /// The ratio to darken by. + /// The darkened color. public Color Darkened(float amount) { Color res = this; @@ -287,6 +396,10 @@ namespace Godot return res; } + /// + /// Returns the inverted color: `(1 - r, 1 - g, 1 - b, a)`. + /// + /// The inverted color. public Color Inverted() { return new Color( @@ -297,6 +410,12 @@ namespace Godot ); } + /// + /// Returns a new color resulting from making this color lighter + /// by the specified ratio (on the range of 0 to 1). + /// + /// The ratio to lighten by. + /// The darkened color. public Color Lightened(float amount) { Color res = this; @@ -306,18 +425,48 @@ namespace Godot return res; } - public Color LinearInterpolate(Color c, float t) + /// + /// Returns the result of the linear interpolation between + /// this color and `to` by amount `weight`. + /// + /// The destination color for interpolation. + /// A value on the range of 0.0 to 1.0, representing the amount of interpolation. + /// The resulting color of the interpolation. + public Color LinearInterpolate(Color to, float weight) { - var res = this; - - res.r += t * (c.r - r); - res.g += t * (c.g - g); - res.b += t * (c.b - b); - res.a += t * (c.a - a); - - return res; + return new Color + ( + Mathf.Lerp(r, to.r, weight), + Mathf.Lerp(g, to.g, weight), + Mathf.Lerp(b, to.b, weight), + Mathf.Lerp(a, to.a, weight) + ); } + /// + /// Returns the result of the linear interpolation between + /// this color and `to` by color amount `weight`. + /// + /// The destination color for interpolation. + /// A color with components on the range of 0.0 to 1.0, representing the amount of interpolation. + /// The resulting color of the interpolation. + public Color LinearInterpolate(Color to, Color weight) + { + return new Color + ( + Mathf.Lerp(r, to.r, weight.r), + Mathf.Lerp(g, to.g, weight.g), + Mathf.Lerp(b, to.b, weight.b), + Mathf.Lerp(a, to.a, weight.a) + ); + } + + /// + /// Returns the color's 32-bit integer in ABGR format + /// (each byte represents a component of the ABGR profile). + /// ABGR is the reversed version of the default format. + /// + /// An int representing this color in ABGR32 format. public int ToAbgr32() { int c = (byte)Math.Round(a * 255); @@ -331,6 +480,12 @@ namespace Godot return c; } + /// + /// Returns the color's 64-bit integer in ABGR format + /// (each byte represents a component of the ABGR profile). + /// ABGR is the reversed version of the default format. + /// + /// An int representing this color in ABGR64 format. public long ToAbgr64() { long c = (ushort)Math.Round(a * 65535); @@ -344,6 +499,12 @@ namespace Godot return c; } + /// + /// Returns the color's 32-bit integer in ARGB format + /// (each byte represents a component of the ARGB profile). + /// ARGB is more compatible with DirectX, but not used much in Godot. + /// + /// An int representing this color in ARGB32 format. public int ToArgb32() { int c = (byte)Math.Round(a * 255); @@ -357,6 +518,12 @@ namespace Godot return c; } + /// + /// Returns the color's 64-bit integer in ARGB format + /// (each word represents a component of the ARGB profile). + /// ARGB is more compatible with DirectX, but not used much in Godot. + /// + /// A long representing this color in ARGB64 format. public long ToArgb64() { long c = (ushort)Math.Round(a * 65535); @@ -370,6 +537,12 @@ namespace Godot return c; } + /// + /// Returns the color's 32-bit integer in RGBA format + /// (each byte represents a component of the RGBA profile). + /// RGBA is Godot's default and recommended format. + /// + /// An int representing this color in RGBA32 format. public int ToRgba32() { int c = (byte)Math.Round(r * 255); @@ -383,6 +556,12 @@ namespace Godot return c; } + /// + /// Returns the color's 64-bit integer in RGBA format + /// (each word represents a component of the RGBA profile). + /// RGBA is Godot's default and recommended format. + /// + /// A long representing this color in RGBA64 format. public long ToRgba64() { long c = (ushort)Math.Round(r * 65535); @@ -396,6 +575,11 @@ namespace Godot return c; } + /// + /// Returns the color's HTML hexadecimal color string in RGBA format. + /// + /// Whether or not to include alpha. If false, the color is RGB instead of RGBA. + /// A string for the HTML hexadecimal representation of this color. public string ToHtml(bool includeAlpha = true) { var txt = string.Empty; @@ -410,7 +594,13 @@ namespace Godot return txt; } - // Constructors + /// + /// Constructs a color from RGBA values on the range of 0 to 1. + /// + /// The color's red component, typically on the range of 0 to 1. + /// The color's green component, typically on the range of 0 to 1. + /// The color's blue component, typically on the range of 0 to 1. + /// The color's alpha (transparency) value, typically on the range of 0 to 1. Default: 1. public Color(float r, float g, float b, float a = 1.0f) { this.r = r; @@ -419,6 +609,24 @@ namespace Godot this.a = a; } + /// + /// Constructs a color from an existing color and an alpha value. + /// + /// The color to construct from. Only its RGB values are used. + /// The color's alpha (transparency) value, typically on the range of 0 to 1. Default: 1. + public Color(Color c, float a = 1.0f) + { + r = c.r; + g = c.g; + b = c.b; + this.a = a; + } + + /// + /// Constructs a color from a 32-bit integer + /// (each byte represents a component of the RGBA profile). + /// + /// The int representing the color. public Color(int rgba) { a = (rgba & 0xFF) / 255.0f; @@ -430,6 +638,11 @@ namespace Godot r = (rgba & 0xFF) / 255.0f; } + /// + /// Constructs a color from a 64-bit integer + /// (each word represents a component of the RGBA profile). + /// + /// The long representing the color. public Color(long rgba) { a = (rgba & 0xFFFF) / 65535.0f; @@ -470,9 +683,13 @@ namespace Godot } if (i == 0) + { ig += v * 16; + } else + { ig += v; + } } return ig; @@ -490,9 +707,13 @@ namespace Godot int lv = v & 0xF; if (lv < 10) + { c = (char)('0' + lv); + } else + { c = (char)('a' + lv - 10); + } v >>= 4; ret = c + ret; @@ -504,10 +725,14 @@ namespace Godot internal static bool HtmlIsValid(string color) { if (color.Length == 0) + { return false; + } if (color[0] == '#') + { color = color.Substring(1, color.Length - 1); + } bool alpha; @@ -526,7 +751,9 @@ namespace Godot if (alpha) { if (ParseCol8(color, 0) < 0) + { return false; + } } int from = alpha ? 2 : 0; @@ -541,11 +768,24 @@ namespace Godot return true; } + /// + /// Returns a color constructed from integer red, green, blue, and alpha channels. + /// Each channel should have 8 bits of information ranging from 0 to 255. + /// + /// The red component represented on the range of 0 to 255. + /// The green component represented on the range of 0 to 255. + /// The blue component represented on the range of 0 to 255. + /// The alpha (transparency) component represented on the range of 0 to 255. + /// The constructed color. public static Color Color8(byte r8, byte g8, byte b8, byte a8 = 255) { return new Color(r8 / 255f, g8 / 255f, b8 / 255f, a8 / 255f); } + /// + /// Constructs a color from the HTML hexadecimal color string in RGBA format. + /// + /// A string for the HTML hexadecimal representation of this color. public Color(string rgba) { if (rgba.Length == 0) @@ -690,13 +930,13 @@ namespace Godot if (Mathf.IsEqualApprox(left.g, right.g)) { if (Mathf.IsEqualApprox(left.b, right.b)) + { return left.a < right.a; + } return left.b < right.b; } - return left.g < right.g; } - return left.r < right.r; } @@ -707,13 +947,13 @@ namespace Godot if (Mathf.IsEqualApprox(left.g, right.g)) { if (Mathf.IsEqualApprox(left.b, right.b)) + { return left.a > right.a; + } return left.b > right.b; } - return left.g > right.g; } - return left.r > right.r; } @@ -732,6 +972,12 @@ namespace Godot return r == other.r && g == other.g && b == other.b && a == other.a; } + /// + /// Returns true if this color and `other` are approximately equal, by running + /// on each component. + /// + /// The other color to compare. + /// Whether or not the colors are approximately equal. public bool IsEqualApprox(Color other) { return Mathf.IsEqualApprox(r, other.r) && Mathf.IsEqualApprox(g, other.g) && Mathf.IsEqualApprox(b, other.b) && Mathf.IsEqualApprox(a, other.a); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs index f41f5e9fc84..d05a0414aa5 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs @@ -3,6 +3,10 @@ using System.Collections.Generic; namespace Godot { + /// + /// This class contains color constants created from standardized color names. + /// The standardized color set is based on the X11 and .NET color names. + /// public static class Colors { // Color names and values are derived from core/color_names.inc diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs index 4f7aa99df86..6eecc262d6c 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs @@ -11,79 +11,185 @@ namespace Godot { // Define constants with Decimal precision and cast down to double or float. + /// + /// The circle constant, the circumference of the unit circle in radians. + /// public const real_t Tau = (real_t) 6.2831853071795864769252867666M; // 6.2831855f and 6.28318530717959 + + /// + /// Constant that represents how many times the diameter of a circle + /// fits around its perimeter. This is equivalent to `Mathf.Tau / 2`. + /// public const real_t Pi = (real_t) 3.1415926535897932384626433833M; // 3.1415927f and 3.14159265358979 + + /// + /// Positive infinity. For negative infinity, use `-Mathf.Inf`. + /// public const real_t Inf = real_t.PositiveInfinity; + + /// + /// "Not a Number", an invalid value. `NaN` has special properties, including + /// that it is not equal to itself. It is output by some invalid operations, + /// such as dividing zero by zero. + /// public const real_t NaN = real_t.NaN; private const real_t Deg2RadConst = (real_t) 0.0174532925199432957692369077M; // 0.0174532924f and 0.0174532925199433 private const real_t Rad2DegConst = (real_t) 57.295779513082320876798154814M; // 57.29578f and 57.2957795130823 + /// + /// Returns the absolute value of `s` (i.e. positive value). + /// + /// The input number. + /// The absolute value of `s`. public static int Abs(int s) { return Math.Abs(s); } + /// + /// Returns the absolute value of `s` (i.e. positive value). + /// + /// The input number. + /// The absolute value of `s`. public static real_t Abs(real_t s) { return Math.Abs(s); } + /// + /// Returns the arc cosine of `s` in radians. Use to get the angle of cosine s. + /// + /// The input cosine value. Must be on the range of -1.0 to 1.0. + /// An angle that would result in the given cosine value. On the range `0` to `Tau/2`. public static real_t Acos(real_t s) { return (real_t)Math.Acos(s); } + /// + /// Returns the arc sine of `s` in radians. Use to get the angle of sine s. + /// + /// The input sine value. Must be on the range of -1.0 to 1.0. + /// An angle that would result in the given sine value. On the range `-Tau/4` to `Tau/4`. public static real_t Asin(real_t s) { return (real_t)Math.Asin(s); } + /// + /// Returns the arc tangent of `s` in radians. Use to get the angle of tangent s. + /// + /// The method cannot know in which quadrant the angle should fall. + /// See if you have both `y` and `x`. + /// + /// The input tangent value. + /// An angle that would result in the given tangent value. On the range `-Tau/4` to `Tau/4`. public static real_t Atan(real_t s) { return (real_t)Math.Atan(s); } + /// + /// Returns the arc tangent of `y` and `x` in radians. Use to get the angle + /// of the tangent of `y/x`. 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. + /// + /// The Y coordinate of the point to find the angle to. + /// The X coordinate of the point to find the angle to. + /// An angle that would result in the given tangent value. On the range `-Tau/2` to `Tau/2`. public static real_t Atan2(real_t y, real_t x) { return (real_t)Math.Atan2(y, x); } + /// + /// Converts a 2D point expressed in the cartesian coordinate + /// system (X and Y axis) to the polar coordinate system + /// (a distance from the origin and an angle). + /// + /// The input X coordinate. + /// The input Y coordinate. + /// A with X representing the distance and Y representing the angle. public static Vector2 Cartesian2Polar(real_t x, real_t y) { return new Vector2(Sqrt(x * x + y * y), Atan2(y, x)); } + /// + /// Rounds `s` upward (towards positive infinity). + /// + /// The number to ceil. + /// The smallest whole number that is not less than `s`. public static real_t Ceil(real_t s) { return (real_t)Math.Ceiling(s); } + /// + /// Clamps a `value` so that it is not less than `min` and not more than `max`. + /// + /// The value to clamp. + /// The minimum allowed value. + /// The maximum allowed value. + /// The clamped value. public static int Clamp(int value, int min, int max) { return value < min ? min : value > max ? max : value; } + /// + /// Clamps a `value` so that it is not less than `min` and not more than `max`. + /// + /// The value to clamp. + /// The minimum allowed value. + /// The maximum allowed value. + /// The clamped value. public static real_t Clamp(real_t value, real_t min, real_t max) { return value < min ? min : value > max ? max : value; } + /// + /// Returns the cosine of angle `s` in radians. + /// + /// The angle in radians. + /// The cosine of that angle. public static real_t Cos(real_t s) { return (real_t)Math.Cos(s); } + /// + /// Returns the hyperbolic cosine of angle `s` in radians. + /// + /// The angle in radians. + /// The hyperbolic cosine of that angle. public static real_t Cosh(real_t s) { return (real_t)Math.Cosh(s); } + /// + /// Converts an angle expressed in degrees to radians. + /// + /// An angle expressed in degrees. + /// The same angle expressed in radians. public static real_t Deg2Rad(real_t deg) { return deg * Deg2RadConst; } + /// + /// Easing function, based on exponent. The curve values are: + /// `0` is constant, `1` is linear, `0` to `1` is ease-in, `1` or more is ease-out. + /// Negative values are in-out/out-in. + /// + /// The value to ease. + /// `0` is constant, `1` is linear, `0` to `1` is ease-in, `1` or more is ease-out. + /// The eased value. public static real_t Ease(real_t s, real_t curve) { if (s < 0f) @@ -118,21 +224,47 @@ namespace Godot return 0f; } + /// + /// The natural exponential function. It raises the mathematical + /// constant `e` to the power of `s` and returns it. + /// + /// The exponent to raise `e` to. + /// `e` raised to the power of `s`. public static real_t Exp(real_t s) { return (real_t)Math.Exp(s); } + /// + /// Rounds `s` downward (towards negative infinity). + /// + /// The number to floor. + /// The largest whole number that is not more than `s`. public static real_t Floor(real_t s) { return (real_t)Math.Floor(s); } + /// + /// Returns a normalized value considering the given range. + /// This is the opposite of . + /// + /// The interpolated value. + /// The destination value for interpolation. + /// A value on the range of 0.0 to 1.0, representing the amount of interpolation. + /// The resulting value of the inverse interpolation. public static real_t InverseLerp(real_t from, real_t to, real_t weight) { return (weight - from) / (to - from); } + /// + /// Returns true if `a` and `b` are approximately equal to each other. + /// The comparison is done using a tolerance calculation with . + /// + /// One of the values. + /// The other value. + /// A bool for whether or not the two values are approximately equal. public static bool IsEqualApprox(real_t a, real_t b) { // Check for exact equality first, required to handle "infinity" values. @@ -149,26 +281,62 @@ namespace Godot return Abs(a - b) < tolerance; } + /// + /// Returns whether `s` is an infinity value (either positive infinity or negative infinity). + /// + /// The value to check. + /// A bool for whether or not the value is an infinity value. public static bool IsInf(real_t s) { return real_t.IsInfinity(s); } + /// + /// Returns whether `s` is a `NaN` ("Not a Number" or invalid) value. + /// + /// The value to check. + /// A bool for whether or not the value is a `NaN` value. public static bool IsNaN(real_t s) { return real_t.IsNaN(s); } + /// + /// Returns true if `s` is approximately zero. + /// The comparison is done using a tolerance calculation with . + /// + /// This method is faster than using with one value as zero. + /// + /// The value to check. + /// A bool for whether or not the value is nearly zero. public static bool IsZeroApprox(real_t s) { return Abs(s) < Epsilon; } + /// + /// Linearly interpolates between two values by a normalized value. + /// This is the opposite . + /// + /// The start value for interpolation. + /// The destination value for interpolation. + /// A value on the range of 0.0 to 1.0, representing the amount of interpolation. + /// The resulting value of the interpolation. public static real_t Lerp(real_t from, real_t to, real_t weight) { return from + (to - from) * weight; } + /// + /// Linearly interpolates between two angles (in radians) by a normalized value. + /// + /// Similar to , + /// but interpolates correctly when the angles wrap around . + /// + /// The start angle for interpolation. + /// The destination angle for interpolation. + /// A value on the range of 0.0 to 1.0, representing the amount of interpolation. + /// The resulting angle of the interpolation. public static real_t LerpAngle(real_t from, real_t to, real_t weight) { real_t difference = (to - from) % Mathf.Tau; @@ -176,36 +344,81 @@ namespace Godot return from + distance * weight; } + /// + /// 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. + /// + /// The input value. + /// The natural log of `s`. public static real_t Log(real_t s) { return (real_t)Math.Log(s); } + /// + /// Returns the maximum of two values. + /// + /// One of the values. + /// The other value. + /// Whichever of the two values is higher. public static int Max(int a, int b) { return a > b ? a : b; } + /// + /// Returns the maximum of two values. + /// + /// One of the values. + /// The other value. + /// Whichever of the two values is higher. public static real_t Max(real_t a, real_t b) { return a > b ? a : b; } + /// + /// Returns the minimum of two values. + /// + /// One of the values. + /// The other value. + /// Whichever of the two values is lower. public static int Min(int a, int b) { return a < b ? a : b; } + /// + /// Returns the minimum of two values. + /// + /// One of the values. + /// The other value. + /// Whichever of the two values is lower. public static real_t Min(real_t a, real_t b) { return a < b ? a : b; } + /// + /// Moves `from` toward `to` by the `delta` value. + /// + /// Use a negative delta value to move away. + /// + /// The start value. + /// The value to move towards. + /// The amount to move by. + /// The value after moving. public static real_t MoveToward(real_t from, real_t to, real_t delta) { return Abs(to - from) <= delta ? to : from + Sign(to - from) * delta; } + /// + /// Returns the nearest larger power of 2 for the integer `value`. + /// + /// The input value. + /// The nearest larger power of 2. public static int NearestPo2(int value) { value--; @@ -218,14 +431,25 @@ namespace Godot return value; } + /// + /// Converts a 2D point expressed in the polar coordinate + /// system (a distance from the origin `r` and an angle `th`) + /// to the cartesian coordinate system (X and Y axis). + /// + /// The distance from the origin. + /// The angle of the point. + /// A representing the cartesian coordinate. public static Vector2 Polar2Cartesian(real_t r, real_t th) { return new Vector2(r * Cos(th), r * Sin(th)); } /// - /// Performs a canonical Modulus operation, where the output is on the range [0, b). + /// Performs a canonical Modulus operation, where the output is on the range `[0, b)`. /// + /// The dividend, the primary input. + /// The divisor. The output is on the range `[0, b)`. + /// The resulting output. public static int PosMod(int a, int b) { int c = a % b; @@ -237,8 +461,11 @@ namespace Godot } /// - /// Performs a canonical Modulus operation, where the output is on the range [0, b). + /// Performs a canonical Modulus operation, where the output is on the range `[0, b)`. /// + /// The dividend, the primary input. + /// The divisor. The output is on the range `[0, b)`. + /// The resulting output. public static real_t PosMod(real_t a, real_t b) { real_t c = a % b; @@ -249,43 +476,89 @@ namespace Godot return c; } + /// + /// Returns the result of `x` raised to the power of `y`. + /// + /// The base. + /// The exponent. + /// `x` raised to the power of `y`. public static real_t Pow(real_t x, real_t y) { return (real_t)Math.Pow(x, y); } + /// + /// Converts an angle expressed in radians to degrees. + /// + /// An angle expressed in radians. + /// The same angle expressed in degrees. public static real_t Rad2Deg(real_t rad) { return rad * Rad2DegConst; } + /// + /// Rounds `s` to the nearest whole number, + /// with halfway cases rounded towards the nearest multiple of two. + /// + /// The number to round. + /// The rounded number. public static real_t Round(real_t s) { return (real_t)Math.Round(s); } + /// + /// Returns the sign of `s`: `-1` or `1`. Returns `0` if `s` is `0`. + /// + /// The input number. + /// One of three possible values: `1`, `-1`, or `0`. public static int Sign(int s) { if (s == 0) return 0; return s < 0 ? -1 : 1; } + /// + /// Returns the sign of `s`: `-1` or `1`. Returns `0` if `s` is `0`. + /// + /// The input number. + /// One of three possible values: `1`, `-1`, or `0`. public static int Sign(real_t s) { if (s == 0) return 0; return s < 0 ? -1 : 1; } + /// + /// Returns the sine of angle `s` in radians. + /// + /// The angle in radians. + /// The sine of that angle. public static real_t Sin(real_t s) { return (real_t)Math.Sin(s); } + /// + /// Returns the hyperbolic sine of angle `s` in radians. + /// + /// The angle in radians. + /// The hyperbolic sine of that angle. public static real_t Sinh(real_t s) { return (real_t)Math.Sinh(s); } + /// + /// Returns a number smoothly interpolated between `from` and `to`, + /// based on the `weight`. Similar to , + /// but interpolates faster at the beginning and slower at the end. + /// + /// The start value for interpolation. + /// The destination value for interpolation. + /// A value representing the amount of interpolation. + /// The resulting value of the interpolation. public static real_t SmoothStep(real_t from, real_t to, real_t weight) { if (IsEqualApprox(from, to)) @@ -296,11 +569,25 @@ namespace Godot return x * x * (3 - 2 * x); } + /// + /// Returns the square root of `s`, where `s` is a non-negative number. + /// + /// If you need negative inputs, use `System.Numerics.Complex`. + /// + /// The input number. Must not be negative. + /// The square root of `s`. public static real_t Sqrt(real_t s) { return (real_t)Math.Sqrt(s); } + /// + /// Returns the position of the first non-zero digit, after the + /// decimal point. Note that the maximum return value is 10, + /// which is a design decision in the implementation. + /// + /// The input value. + /// The position of the first non-zero digit. public static int StepDecimals(real_t step) { double[] sd = new double[] { @@ -326,32 +613,68 @@ namespace Godot return 0; } + /// + /// Snaps float value `s` to a given `step`. + /// This can also be used to round a floating point + /// number to an arbitrary number of decimals. + /// + /// The value to stepify. + /// The step size to snap to. + /// public static real_t Stepify(real_t s, real_t step) { if (step != 0f) { - s = Floor(s / step + 0.5f) * step; + return Floor(s / step + 0.5f) * step; } return s; } + /// + /// Returns the tangent of angle `s` in radians. + /// + /// The angle in radians. + /// The tangent of that angle. public static real_t Tan(real_t s) { return (real_t)Math.Tan(s); } + /// + /// Returns the hyperbolic tangent of angle `s` in radians. + /// + /// The angle in radians. + /// The hyperbolic tangent of that angle. public static real_t Tanh(real_t s) { return (real_t)Math.Tanh(s); } + /// + /// Wraps `value` between `min` and `max`. Usable for creating loop-alike + /// behavior or infinite surfaces. If `min` is `0`, this is equivalent + /// to , so prefer using that instead. + /// + /// The value to wrap. + /// The minimum allowed value and lower bound of the range. + /// The maximum allowed value and upper bound of the range. + /// The wrapped value. public static int Wrap(int value, int min, int max) { int range = max - min; return range == 0 ? min : min + ((value - min) % range + range) % range; } + /// + /// Wraps `value` between `min` and `max`. Usable for creating loop-alike + /// behavior or infinite surfaces. If `min` is `0`, this is equivalent + /// to , so prefer using that instead. + /// + /// The value to wrap. + /// The minimum allowed value and lower bound of the range. + /// The maximum allowed value and upper bound of the range. + /// The wrapped value. public static real_t Wrap(real_t value, real_t min, real_t max) { real_t range = max - min; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs index 1b7fd4906f6..c2f4701b5fa 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs @@ -12,40 +12,89 @@ namespace Godot { // Define constants with Decimal precision and cast down to double or float. + /// + /// The natural number `e`. + /// public const real_t E = (real_t) 2.7182818284590452353602874714M; // 2.7182817f and 2.718281828459045 + + /// + /// The square root of 2. + /// public const real_t Sqrt2 = (real_t) 1.4142135623730950488016887242M; // 1.4142136f and 1.414213562373095 + /// + /// A very small number used for float comparison with error tolerance. + /// 1e-06 with single-precision floats, but 1e-14 if `REAL_T_IS_DOUBLE`. + /// #if REAL_T_IS_DOUBLE public const real_t Epsilon = 1e-14; // Epsilon size should depend on the precision used. #else public const real_t Epsilon = 1e-06f; #endif + /// + /// Returns the amount of digits after the decimal place. + /// + /// The input value. + /// The amount of digits. public static int DecimalCount(real_t s) { return DecimalCount((decimal)s); } + /// + /// Returns the amount of digits after the decimal place. + /// + /// The input value. + /// The amount of digits. public static int DecimalCount(decimal s) { return BitConverter.GetBytes(decimal.GetBits(s)[3])[2]; } + /// + /// Rounds `s` upward (towards positive infinity). + /// + /// This is the same as , but returns an `int`. + /// + /// The number to ceil. + /// The smallest whole number that is not less than `s`. public static int CeilToInt(real_t s) { return (int)Math.Ceiling(s); } + /// + /// Rounds `s` downward (towards negative infinity). + /// + /// This is the same as , but returns an `int`. + /// + /// The number to floor. + /// The largest whole number that is not more than `s`. public static int FloorToInt(real_t s) { return (int)Math.Floor(s); } + /// + /// + /// + /// + /// public static int RoundToInt(real_t s) { return (int)Math.Round(s); } + /// + /// Returns true if `a` and `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 . + /// + /// One of the values. + /// The other value. + /// The pre-calculated tolerance value. + /// A bool for whether or not the two values are equal. public static bool IsEqualApprox(real_t a, real_t b, real_t tolerance) { // Check for exact equality first, required to handle "infinity" values. diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs index 885845e3a46..cc273c906b1 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs @@ -8,18 +8,33 @@ using real_t = System.Single; namespace Godot { + /// + /// Plane represents a normalized plane equation. + /// "Over" or "Above" the plane is considered the side of + /// the plane towards where the normal is pointing. + /// [Serializable] [StructLayout(LayoutKind.Sequential)] public struct Plane : IEquatable { private Vector3 _normal; + /// + /// The normal of the plane, which must be normalized. + /// In the scalar equation of the plane `ax + by + cz = d`, this is + /// the vector `(a, b, c)`, where `d` is the property. + /// + /// Equivalent to `x`, `y`, and `z`. public Vector3 Normal { get { return _normal; } set { _normal = value; } } + /// + /// The X component of the plane's normal vector. + /// + /// Equivalent to 's X value. public real_t x { get @@ -32,6 +47,10 @@ namespace Godot } } + /// + /// The Y component of the plane's normal vector. + /// + /// Equivalent to 's Y value. public real_t y { get @@ -44,6 +63,10 @@ namespace Godot } } + /// + /// The Z component of the plane's normal vector. + /// + /// Equivalent to 's Z value. public real_t z { get @@ -56,38 +79,82 @@ namespace Godot } } + /// + /// The distance from the origin to the plane (in the direction of + /// ). This value is typically non-negative. + /// In the scalar equation of the plane `ax + by + cz = d`, + /// this is `d`, while the `(a, b, c)` coordinates are represented + /// by the property. + /// + /// The plane's distance from the origin. public real_t D { get; set; } + /// + /// The center of the plane, the point where the normal line intersects the plane. + /// + /// Equivalent to multiplied by `D`. public Vector3 Center { get { return _normal * D; } + set + { + _normal = value.Normalized(); + D = value.Length(); + } } + /// + /// Returns the shortest distance from this plane to the position `point`. + /// + /// The position to use for the calcualtion. + /// The shortest distance. public real_t DistanceTo(Vector3 point) { return _normal.Dot(point) - D; } + /// + /// The center of the plane, the point where the normal line intersects the plane. + /// Deprecated, use the Center property instead. + /// + /// Equivalent to multiplied by `D`. + [Obsolete("GetAnyPoint is deprecated. Use the Center property instead.")] public Vector3 GetAnyPoint() { return _normal * D; } + /// + /// Returns true if point is inside the plane. + /// Comparison uses a custom minimum epsilon threshold. + /// + /// The point to check. + /// The tolerance threshold. + /// A bool for whether or not the plane has the point. public bool HasPoint(Vector3 point, real_t epsilon = Mathf.Epsilon) { real_t dist = _normal.Dot(point) - D; return Mathf.Abs(dist) <= epsilon; } + /// + /// Returns the intersection point of the three planes: `b`, `c`, + /// and this plane. If no intersection is found, `null` is returned. + /// + /// One of the three planes to use in the calculation. + /// One of the three planes to use in the calculation. + /// The intersection, or `null` if none is found. public Vector3? Intersect3(Plane b, Plane c) { real_t denom = _normal.Cross(b._normal).Dot(c._normal); if (Mathf.IsZeroApprox(denom)) + { return null; + } Vector3 result = b._normal.Cross(c._normal) * D + c._normal.Cross(_normal) * b.D + @@ -96,54 +163,94 @@ namespace Godot return result / denom; } + /// + /// Returns the intersection point of a ray consisting of the + /// position `from` and the direction normal `dir` with this plane. + /// If no intersection is found, `null` is returned. + /// + /// The start of the ray. + /// The direction of the ray, normalized. + /// The intersection, or `null` if none is found. public Vector3? IntersectRay(Vector3 from, Vector3 dir) { real_t den = _normal.Dot(dir); if (Mathf.IsZeroApprox(den)) + { return null; + } real_t dist = (_normal.Dot(from) - D) / den; // This is a ray, before the emitting pos (from) does not exist if (dist > Mathf.Epsilon) + { return null; + } return from + dir * -dist; } + /// + /// Returns the intersection point of a line segment from + /// position `begin` to position `end` with this plane. + /// If no intersection is found, `null` is returned. + /// + /// The start of the line segment. + /// The end of the line segment. + /// The intersection, or `null` if none is found. public Vector3? IntersectSegment(Vector3 begin, Vector3 end) { Vector3 segment = begin - end; real_t den = _normal.Dot(segment); if (Mathf.IsZeroApprox(den)) + { return null; + } real_t dist = (_normal.Dot(begin) - D) / den; // Only allow dist to be in the range of 0 to 1, with tolerance. if (dist < -Mathf.Epsilon || dist > 1.0f + Mathf.Epsilon) + { return null; + } return begin + segment * -dist; } + /// + /// Returns true if `point` is located above the plane. + /// + /// The point to check. + /// A bool for whether or not the point is above the plane. public bool IsPointOver(Vector3 point) { return _normal.Dot(point) > D; } + /// + /// Returns the plane scaled to unit length. + /// + /// A normalized version of the plane. public Plane Normalized() { real_t len = _normal.Length(); if (len == 0) + { return new Plane(0, 0, 0, 0); + } return new Plane(_normal / len, D / len); } + /// + /// Returns the orthogonal projection of `point` into the plane. + /// + /// The point to project. + /// The projected point. public Vector3 Project(Vector3 point) { return point - _normal * DistanceTo(point); @@ -154,22 +261,56 @@ namespace Godot private static readonly Plane _planeXZ = new Plane(0, 1, 0, 0); private static readonly Plane _planeXY = new Plane(0, 0, 1, 0); + /// + /// A plane that extends in the Y and Z axes (normal vector points +X). + /// + /// Equivalent to `new Plane(1, 0, 0, 0)`. public static Plane PlaneYZ { get { return _planeYZ; } } + + /// + /// A plane that extends in the X and Z axes (normal vector points +Y). + /// + /// Equivalent to `new Plane(0, 1, 0, 0)`. public static Plane PlaneXZ { get { return _planeXZ; } } + + /// + /// A plane that extends in the X and Y axes (normal vector points +Z). + /// + /// Equivalent to `new Plane(0, 0, 1, 0)`. public static Plane PlaneXY { get { return _planeXY; } } - // Constructors + /// + /// Constructs a plane from four values. `a`, `b` and `c` become the + /// components of the resulting plane's vector. + /// `d` becomes the plane's distance from the origin. + /// + /// The X component of the plane's normal vector. + /// The Y component of the plane's normal vector. + /// The Z component of the plane's normal vector. + /// The plane's distance from the origin. This value is typically non-negative. public Plane(real_t a, real_t b, real_t c, real_t d) { _normal = new Vector3(a, b, c); this.D = d; } + + /// + /// Constructs a plane from a normal vector and the plane's distance to the origin. + /// + /// The normal of the plane, must be normalized. + /// The plane's distance from the origin. This value is typically non-negative. public Plane(Vector3 normal, real_t d) { this._normal = normal; this.D = d; } + /// + /// Constructs a plane from the three points, given in clockwise order. + /// + /// The first point. + /// The second point. + /// The third point. public Plane(Vector3 v1, Vector3 v2, Vector3 v3) { _normal = (v1 - v3).Cross(v1 - v2); @@ -207,6 +348,12 @@ namespace Godot return _normal == other._normal && D == other.D; } + /// + /// Returns true if this plane and `other` are approximately equal, by running + /// on each component. + /// + /// The other plane to compare. + /// Whether or not the planes are approximately equal. public bool IsEqualApprox(Plane other) { return _normal.IsEqualApprox(other._normal) && Mathf.IsEqualApprox(D, other.D); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs index 6702634c510..902c6b4662a 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs @@ -8,15 +8,51 @@ using real_t = System.Single; namespace Godot { + /// + /// A unit quaternion used for representing 3D rotations. + /// Quaternions need to be normalized to be used for rotation. + /// + /// It is similar to Basis, which implements matrix representation of + /// rotations, and can be parametrized using both an axis-angle pair + /// or Euler angles. Basis stores rotation, scale, and shearing, + /// while Quat only stores rotation. + /// + /// Due to its compactness and the way it is stored in memory, certain + /// operations (obtaining axis-angle and performing SLERP, in particular) + /// are more efficient and robust against floating-point errors. + /// [Serializable] [StructLayout(LayoutKind.Sequential)] public struct Quat : IEquatable { + /// + /// X component of the quaternion (imaginary `i` axis part). + /// Quaternion components should usually not be manipulated directly. + /// public real_t x; + + /// + /// Y component of the quaternion (imaginary `j` axis part). + /// Quaternion components should usually not be manipulated directly. + /// public real_t y; + + /// + /// Z component of the quaternion (imaginary `k` axis part). + /// Quaternion components should usually not be manipulated directly. + /// public real_t z; + + /// + /// W component of the quaternion (real part). + /// Quaternion components should usually not be manipulated directly. + /// public real_t w; + /// + /// Access quaternion components using their index. + /// + /// `[0]` is equivalent to `.x`, `[1]` is equivalent to `.y`, `[2]` is equivalent to `.z`, `[3]` is equivalent to `.w`. public real_t this[int index] { get @@ -57,16 +93,35 @@ namespace Godot } } + /// + /// Returns the length (magnitude) of the quaternion. + /// + /// Equivalent to `Mathf.Sqrt(LengthSquared)`. public real_t Length { get { return Mathf.Sqrt(LengthSquared); } } + /// + /// Returns the squared length (squared magnitude) of the quaternion. + /// This method runs faster than , so prefer it if + /// you need to compare quaternions or need the squared length for some formula. + /// + /// Equivalent to `Dot(this)`. public real_t LengthSquared { get { return Dot(this); } } + /// + /// Performs a cubic spherical interpolation between quaternions `preA`, + /// this vector, `b`, and `postB`, by the given amount `t`. + /// + /// The destination quaternion. + /// A quaternion before this quaternion. + /// A quaternion after `b`. + /// A value on the range of 0.0 to 1.0, representing the amount of interpolation. + /// The interpolated quaternion. public Quat CubicSlerp(Quat b, Quat preA, Quat postB, real_t t) { real_t t2 = (1.0f - t) * t * 2f; @@ -75,30 +130,63 @@ namespace Godot return sp.Slerpni(sq, t2); } + /// + /// Returns the dot product of two quaternions. + /// + /// The other quaternion. + /// The dot product. public real_t Dot(Quat b) { return x * b.x + y * b.y + z * b.z + w * b.w; } + /// + /// Returns Euler angles (in the YXZ convention: when decomposing, + /// first Z, then X, and Y last) corresponding to the rotation + /// represented by the unit quaternion. Returned vector contains + /// the rotation angles in the format (X angle, Y angle, Z angle). + /// + /// The Euler angle representation of this quaternion. public Vector3 GetEuler() { #if DEBUG if (!IsNormalized()) + { throw new InvalidOperationException("Quat is not normalized"); + } #endif var basis = new Basis(this); return basis.GetEuler(); } + /// + /// Returns the inverse of the quaternion. + /// + /// The inverse quaternion. public Quat Inverse() { #if DEBUG if (!IsNormalized()) + { throw new InvalidOperationException("Quat is not normalized"); + } #endif return new Quat(-x, -y, -z, w); } + /// + /// Returns whether the quaternion is normalized or not. + /// + /// A bool for whether the quaternion is normalized or not. + public bool IsNormalized() + { + return Mathf.Abs(LengthSquared - 1) <= Mathf.Epsilon; + } + + /// + /// Returns a copy of the quaternion, normalized to unit length. + /// + /// The normalized quaternion. public Quat Normalized() { return this / Length; @@ -131,56 +219,69 @@ namespace Godot this = new Quat(eulerYXZ); } - public Quat Slerp(Quat b, real_t t) + /// + /// Returns the result of the spherical linear interpolation between + /// this quaternion and `to` by amount `weight`. + /// + /// Note: Both quaternions must be normalized. + /// + /// The destination quaternion for interpolation. Must be normalized. + /// A value on the range of 0.0 to 1.0, representing the amount of interpolation. + /// The resulting quaternion of the interpolation. + public Quat Slerp(Quat to, real_t weight) { #if DEBUG if (!IsNormalized()) + { throw new InvalidOperationException("Quat is not normalized"); - if (!b.IsNormalized()) - throw new ArgumentException("Argument is not normalized", nameof(b)); + } + if (!to.IsNormalized()) + { + throw new ArgumentException("Argument is not normalized", nameof(to)); + } #endif - // Calculate cosine - real_t cosom = x * b.x + y * b.y + z * b.z + w * b.w; + // Calculate cosine. + real_t cosom = x * to.x + y * to.y + z * to.z + w * to.w; var to1 = new Quat(); - // Adjust signs if necessary + // Adjust signs if necessary. if (cosom < 0.0) { cosom = -cosom; - to1.x = -b.x; - to1.y = -b.y; - to1.z = -b.z; - to1.w = -b.w; + to1.x = -to.x; + to1.y = -to.y; + to1.z = -to.z; + to1.w = -to.w; } else { - to1.x = b.x; - to1.y = b.y; - to1.z = b.z; - to1.w = b.w; + to1.x = to.x; + to1.y = to.y; + to1.z = to.z; + to1.w = to.w; } real_t sinom, scale0, scale1; - // Calculate coefficients + // Calculate coefficients. if (1.0 - cosom > Mathf.Epsilon) { - // Standard case (Slerp) + // Standard case (Slerp). real_t omega = Mathf.Acos(cosom); sinom = Mathf.Sin(omega); - scale0 = Mathf.Sin((1.0f - t) * omega) / sinom; - scale1 = Mathf.Sin(t * omega) / sinom; + scale0 = Mathf.Sin((1.0f - weight) * omega) / sinom; + scale1 = Mathf.Sin(weight * omega) / sinom; } else { - // Quaternions are very close so we can do a linear interpolation - scale0 = 1.0f - t; - scale1 = t; + // Quaternions are very close so we can do a linear interpolation. + scale0 = 1.0f - weight; + scale1 = weight; } - // Calculate final values + // Calculate final values. return new Quat ( scale0 * x + scale1 * to1.x, @@ -190,9 +291,17 @@ namespace Godot ); } - public Quat Slerpni(Quat b, real_t t) + /// + /// Returns the result of the spherical linear interpolation between + /// this quaternion and `to` by amount `weight`, but without + /// checking if the rotation path is not bigger than 90 degrees. + /// + /// The destination quaternion for interpolation. Must be normalized. + /// A value on the range of 0.0 to 1.0, representing the amount of interpolation. + /// The resulting quaternion of the interpolation. + public Quat Slerpni(Quat to, real_t weight) { - real_t dot = Dot(b); + real_t dot = Dot(to); if (Mathf.Abs(dot) > 0.9999f) { @@ -201,33 +310,54 @@ namespace Godot real_t theta = Mathf.Acos(dot); real_t sinT = 1.0f / Mathf.Sin(theta); - real_t newFactor = Mathf.Sin(t * theta) * sinT; - real_t invFactor = Mathf.Sin((1.0f - t) * theta) * sinT; + real_t newFactor = Mathf.Sin(weight * theta) * sinT; + real_t invFactor = Mathf.Sin((1.0f - weight) * theta) * sinT; return new Quat ( - invFactor * x + newFactor * b.x, - invFactor * y + newFactor * b.y, - invFactor * z + newFactor * b.z, - invFactor * w + newFactor * b.w + invFactor * x + newFactor * to.x, + invFactor * y + newFactor * to.y, + invFactor * z + newFactor * to.z, + invFactor * w + newFactor * to.w ); } + /// + /// Returns a vector transformed (multiplied) by this quaternion. + /// + /// A vector to transform. + /// The transfomed vector. public Vector3 Xform(Vector3 v) { #if DEBUG if (!IsNormalized()) + { throw new InvalidOperationException("Quat is not normalized"); + } #endif var u = new Vector3(x, y, z); Vector3 uv = u.Cross(v); return v + ((uv * w) + u.Cross(uv)) * 2; } - // Static Readonly Properties - public static Quat Identity { get; } = new Quat(0f, 0f, 0f, 1f); + // Constants + private static readonly Quat _identity = new Quat(0, 0, 0, 1); - // Constructors + /// + /// The identity quaternion, representing no rotation. + /// Equivalent to an identity matrix. If a vector is transformed by + /// an identity quaternion, it will not change. + /// + /// Equivalent to `new Quat(0, 0, 0, 1)`. + public static Quat Identity { get { return _identity; } } + + /// + /// Constructs a quaternion defined by the given values. + /// + /// X component of the quaternion (imaginary `i` axis part). + /// Y component of the quaternion (imaginary `j` axis part). + /// Z component of the quaternion (imaginary `k` axis part). + /// W component of the quaternion (real part). public Quat(real_t x, real_t y, real_t z, real_t w) { this.x = x; @@ -236,21 +366,31 @@ namespace Godot this.w = w; } - public bool IsNormalized() - { - return Mathf.Abs(LengthSquared - 1) <= Mathf.Epsilon; - } - + /// + /// Constructs a quaternion from the given quaternion. + /// + /// The existing quaternion. public Quat(Quat q) { this = q; } + /// + /// Constructs a quaternion from the given . + /// + /// The basis to construct from. public Quat(Basis basis) { this = basis.Quat(); } + /// + /// Constructs a quaternion that will perform a rotation specified by + /// Euler angles (in the YXZ convention: when decomposing, + /// first Z, then X, and Y last), + /// given in the vector format as (X angle, Y angle, Z angle). + /// + /// public Quat(Vector3 eulerYXZ) { real_t half_a1 = eulerYXZ.y * 0.5f; @@ -274,11 +414,19 @@ namespace Godot w = sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3; } + /// + /// Constructs a quaternion that will rotate around the given axis + /// by the specified angle. The axis must be a normalized vector. + /// + /// The axis to rotate around. Must be normalized. + /// The angle to rotate, in radians. public Quat(Vector3 axis, real_t angle) { #if DEBUG if (!axis.IsNormalized()) + { throw new ArgumentException("Argument is not normalized", nameof(axis)); + } #endif real_t d = axis.Length(); @@ -391,6 +539,12 @@ namespace Godot return x == other.x && y == other.y && z == other.z && w == other.w; } + /// + /// Returns true if this quaternion and `other` are approximately equal, by running + /// on each component. + /// + /// The other quaternion to compare. + /// Whether or not the quaternions are approximately equal. public bool IsEqualApprox(Quat other) { return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z) && Mathf.IsEqualApprox(w, other.w); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs index 1098ffe4e5f..f7703c77cc4 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs @@ -8,6 +8,10 @@ using real_t = System.Single; namespace Godot { + /// + /// 2D axis-aligned bounding box. Rect2 consists of a position, a size, and + /// several utility functions. It is typically used for fast overlap tests. + /// [Serializable] [StructLayout(LayoutKind.Sequential)] public struct Rect2 : IEquatable @@ -15,29 +19,52 @@ namespace Godot private Vector2 _position; private Vector2 _size; + /// + /// Beginning corner. Typically has values lower than End. + /// + /// Directly uses a private field. public Vector2 Position { get { return _position; } set { _position = value; } } + /// + /// Size from Position to End. Typically all components are positive. + /// If the size is negative, you can use to fix it. + /// + /// Directly uses a private field. public Vector2 Size { get { return _size; } set { _size = value; } } + /// + /// Ending corner. This is calculated as plus + /// . Setting this value will change the size. + /// + /// Getting is equivalent to `value = Position + Size`, setting is equivalent to `Size = value - Position`. public Vector2 End { get { return _position + _size; } set { _size = value - _position; } } + /// + /// The area of this rect. + /// + /// Equivalent to . public real_t Area { get { return GetArea(); } } + /// + /// Returns a Rect2 with equivalent position and size, modified so that + /// the top-left corner is the origin and width and height are positive. + /// + /// The modified rect. public Rect2 Abs() { Vector2 end = End; @@ -45,12 +72,19 @@ namespace Godot return new Rect2(topLeft, _size.Abs()); } + /// + /// Returns the intersection of this Rect2 and `b`. + /// + /// The other rect. + /// The clipped rect. public Rect2 Clip(Rect2 b) { var newRect = b; if (!Intersects(newRect)) + { return new Rect2(); + } newRect._position.x = Mathf.Max(b._position.x, _position.x); newRect._position.y = Mathf.Max(b._position.y, _position.y); @@ -64,6 +98,11 @@ namespace Godot return newRect; } + /// + /// Returns true if this Rect2 completely encloses another one. + /// + /// The other rect that may be enclosed. + /// A bool for whether or not this rect encloses `b`. public bool Encloses(Rect2 b) { return b._position.x >= _position.x && b._position.y >= _position.y && @@ -71,6 +110,11 @@ namespace Godot b._position.y + b._size.y < _position.y + _size.y; } + /// + /// Returns this Rect2 expanded to include a given point. + /// + /// The point to include. + /// The expanded rect. public Rect2 Expand(Vector2 to) { var expanded = this; @@ -79,14 +123,22 @@ namespace Godot Vector2 end = expanded._position + expanded._size; if (to.x < begin.x) + { begin.x = to.x; + } if (to.y < begin.y) + { begin.y = to.y; + } if (to.x > end.x) + { end.x = to.x; + } if (to.y > end.y) + { end.y = to.y; + } expanded._position = begin; expanded._size = end - begin; @@ -94,11 +146,20 @@ namespace Godot return expanded; } + /// + /// Returns the area of the Rect2. + /// + /// The area. public real_t GetArea() { return _size.x * _size.y; } + /// + /// Returns a copy of the Rect2 grown a given amount of units towards all the sides. + /// + /// The amount to grow by. + /// The grown rect. public Rect2 Grow(real_t by) { var g = this; @@ -111,6 +172,14 @@ namespace Godot return g; } + /// + /// Returns a copy of the Rect2 grown a given amount of units towards each direction individually. + /// + /// The amount to grow by on the left. + /// The amount to grow by on the top. + /// The amount to grow by on the right. + /// The amount to grow by on the bottom. + /// The grown rect. public Rect2 GrowIndividual(real_t left, real_t top, real_t right, real_t bottom) { var g = this; @@ -123,6 +192,12 @@ namespace Godot return g; } + /// + /// Returns a copy of the Rect2 grown a given amount of units towards the direction. + /// + /// The direction to grow in. + /// The amount to grow by. + /// The grown rect. public Rect2 GrowMargin(Margin margin, real_t by) { var g = this; @@ -135,11 +210,20 @@ namespace Godot return g; } + /// + /// Returns true if the Rect2 is flat or empty, or false otherwise. + /// + /// A bool for whether or not the rect has area. public bool HasNoArea() { return _size.x <= 0 || _size.y <= 0; } + /// + /// Returns true if the Rect2 contains a point, or false otherwise. + /// + /// The point to check. + /// A bool for whether or not the rect contains `point`. public bool HasPoint(Vector2 point) { if (point.x < _position.x) @@ -155,20 +239,65 @@ namespace Godot return true; } - public bool Intersects(Rect2 b) + /// + /// Returns true if the Rect2 overlaps with `b` + /// (i.e. they have at least one point in common). + /// + /// If `includeBorders` is true, they will also be considered overlapping + /// if their borders touch, even without intersection. + /// + /// The other rect to check for intersections with. + /// Whether or not to consider borders. + /// A bool for whether or not they are intersecting. + public bool Intersects(Rect2 b, bool includeBorders = 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; + 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; + } + } return true; } + /// + /// Returns a larger Rect2 that contains this Rect2 and `b`. + /// + /// The other rect. + /// The merged rect. public Rect2 Merge(Rect2 b) { Rect2 newRect; @@ -179,27 +308,53 @@ namespace Godot newRect._size.x = Mathf.Max(b._position.x + b._size.x, _position.x + _size.x); newRect._size.y = Mathf.Max(b._position.y + b._size.y, _position.y + _size.y); - newRect._size = newRect._size - newRect._position; // Make relative again + newRect._size -= newRect._position; // Make relative again return newRect; } - // Constructors + /// + /// Constructs a Rect2 from a position and size. + /// + /// The position. + /// The size. public Rect2(Vector2 position, Vector2 size) { _position = position; _size = size; } + + /// + /// Constructs a Rect2 from a position, width, and height. + /// + /// The position. + /// The width. + /// The height. public Rect2(Vector2 position, real_t width, real_t height) { _position = position; _size = new Vector2(width, height); } + + /// + /// Constructs a Rect2 from x, y, and size. + /// + /// The position's X coordinate. + /// The position's Y coordinate. + /// The size. public Rect2(real_t x, real_t y, Vector2 size) { _position = new Vector2(x, y); _size = size; } + + /// + /// Constructs a Rect2 from x, y, width, and height. + /// + /// The position's X coordinate. + /// The position's Y coordinate. + /// The width. + /// The height. public Rect2(real_t x, real_t y, real_t width, real_t height) { _position = new Vector2(x, y); @@ -231,6 +386,12 @@ namespace Godot return _position.Equals(other._position) && _size.Equals(other._size); } + /// + /// Returns true if this rect and `other` are approximately equal, by running + /// on each component. + /// + /// The other rect to compare. + /// Whether or not the rects are approximately equal. public bool IsEqualApprox(Rect2 other) { return _position.IsEqualApprox(other._position) && _size.IsEqualApprox(other.Size); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform.cs index aa8815d1aa4..b176111d6ad 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform.cs @@ -8,11 +8,28 @@ using real_t = System.Single; namespace Godot { + /// + /// 3×4 matrix (3 rows, 4 columns) used for 3D linear transformations. + /// It can represent transformations such as translation, rotation, or scaling. + /// It consists of a (first 3 columns) and a + /// for the origin (last column). + /// + /// For more information, read this documentation article: + /// https://docs.godotengine.org/en/latest/tutorials/math/matrices_and_transforms.html + /// [Serializable] [StructLayout(LayoutKind.Sequential)] public struct Transform : IEquatable { + /// + /// The of this transform. Contains the X, Y, and Z basis + /// vectors (columns 0 to 2) and is responsible for rotation and scale. + /// public Basis basis; + + /// + /// The origin vector (column 3, the fourth column). Equivalent to array index `[3]`. + /// public Vector3 origin; /// @@ -85,13 +102,24 @@ namespace Godot } } + /// + /// Returns the inverse of the transform, under the assumption that + /// the transformation is composed of rotation, scaling, and translation. + /// + /// The inverse transformation matrix. public Transform AffineInverse() { Basis basisInv = basis.Inverse(); return new Transform(basisInv, basisInv.Xform(-origin)); } - public Transform InterpolateWith(Transform transform, real_t c) + /// + /// Interpolates this transform to the other `transform` by `weight`. + /// + /// The other transform. + /// A value on the range of 0.0 to 1.0, representing the amount of interpolation. + /// The interpolated transform. + public Transform InterpolateWith(Transform transform, real_t weight) { /* not sure if very "efficient" but good enough? */ @@ -104,18 +132,37 @@ namespace Godot Vector3 destinationLocation = transform.origin; var interpolated = new Transform(); - interpolated.basis.SetQuatScale(sourceRotation.Slerp(destinationRotation, c).Normalized(), sourceScale.LinearInterpolate(destinationScale, c)); - interpolated.origin = sourceLocation.LinearInterpolate(destinationLocation, c); + interpolated.basis.SetQuatScale(sourceRotation.Slerp(destinationRotation, weight).Normalized(), sourceScale.LinearInterpolate(destinationScale, weight)); + interpolated.origin = sourceLocation.LinearInterpolate(destinationLocation, weight); return interpolated; } + /// + /// Returns the inverse of the transform, under the assumption that + /// the transformation is composed of rotation and translation + /// (no scaling, use for transforms with scaling). + /// + /// The inverse matrix. public Transform Inverse() { Basis basisTr = basis.Transposed(); return new Transform(basisTr, basisTr.Xform(-origin)); } + /// + /// Returns a copy of the transform rotated such that its + /// -Z axis (forward) points towards the target position. + /// + /// The transform will first be rotated around the given up vector, + /// and then fully aligned to the target by a further rotation around + /// an axis perpendicular to both the target and up vectors. + /// + /// Operations take place in global space. + /// + /// The object to look at. + /// The relative up direction + /// The resulting transform. public Transform LookingAt(Vector3 target, Vector3 up) { var t = this; @@ -123,16 +170,33 @@ namespace Godot return t; } + /// + /// Returns the transform with the basis orthogonal (90 degrees), + /// and normalized axis vectors (scale of 1 or -1). + /// + /// The orthonormalized transform. public Transform Orthonormalized() { return new Transform(basis.Orthonormalized(), origin); } + /// + /// Rotates the transform around the given `axis` by `phi` (in radians), + /// using matrix multiplication. The axis must be a normalized vector. + /// + /// The axis to rotate around. Must be normalized. + /// The angle to rotate, in radians. + /// The rotated transformation matrix. public Transform Rotated(Vector3 axis, real_t phi) { return new Transform(new Basis(axis, phi), new Vector3()) * this; } + /// + /// Scales the transform by the given 3D scaling factor, using matrix multiplication. + /// + /// The scale to introduce. + /// The scaled transformation matrix. public Transform Scaled(Vector3 scale) { return new Transform(basis.Scaled(scale), origin * scale); @@ -161,16 +225,30 @@ namespace Godot origin = eye; } - public Transform Translated(Vector3 ofs) + /// + /// Translates the transform by the given `offset`, + /// relative to the transform's basis vectors. + /// + /// Unlike and , + /// this does not use matrix multiplication. + /// + /// The offset to translate by. + /// The translated matrix. + public Transform Translated(Vector3 offset) { return new Transform(basis, new Vector3 ( - origin[0] += basis.Row0.Dot(ofs), - origin[1] += basis.Row1.Dot(ofs), - origin[2] += basis.Row2.Dot(ofs) + origin[0] += basis.Row0.Dot(offset), + origin[1] += basis.Row1.Dot(offset), + origin[2] += basis.Row2.Dot(offset) )); } + /// + /// Returns a vector transformed (multiplied) by this transformation matrix. + /// + /// A vector to transform. + /// The transfomed vector. public Vector3 Xform(Vector3 v) { return new Vector3 @@ -181,6 +259,14 @@ namespace Godot ); } + /// + /// Returns a vector transformed (multiplied) by the transposed transformation matrix. + /// + /// Note: This results in a multiplication by the inverse of the + /// transformation matrix only if it represents a rotation-reflection. + /// + /// A vector to inversely transform. + /// The inversely transfomed vector. public Vector3 XformInv(Vector3 v) { Vector3 vInv = v - origin; @@ -199,24 +285,58 @@ namespace Godot private static readonly Transform _flipY = new Transform(new Basis(1, 0, 0, 0, -1, 0, 0, 0, 1), Vector3.Zero); private static readonly Transform _flipZ = new Transform(new Basis(1, 0, 0, 0, 1, 0, 0, 0, -1), Vector3.Zero); + /// + /// The identity transform, with no translation, rotation, or scaling applied. + /// This is used as a replacement for `Transform()` in GDScript. + /// Do not use `new Transform()` with no arguments in C#, because it sets all values to zero. + /// + /// Equivalent to `new Transform(Vector3.Right, Vector3.Up, Vector3.Back, Vector3.Zero)`. public static Transform Identity { get { return _identity; } } + /// + /// The transform that will flip something along the X axis. + /// + /// Equivalent to `new Transform(Vector3.Left, Vector3.Up, Vector3.Back, Vector3.Zero)`. public static Transform FlipX { get { return _flipX; } } + /// + /// The transform that will flip something along the Y axis. + /// + /// Equivalent to `new Transform(Vector3.Right, Vector3.Down, Vector3.Back, Vector3.Zero)`. public static Transform FlipY { get { return _flipY; } } + /// + /// The transform that will flip something along the Z axis. + /// + /// Equivalent to `new Transform(Vector3.Right, Vector3.Up, Vector3.Forward, Vector3.Zero)`. public static Transform FlipZ { get { return _flipZ; } } - // Constructors + /// + /// Constructs a transformation matrix from 4 vectors (matrix columns). + /// + /// The X vector, or column index 0. + /// The Y vector, or column index 1. + /// The Z vector, or column index 2. + /// The origin vector, or column index 3. public Transform(Vector3 column0, Vector3 column1, Vector3 column2, Vector3 origin) { basis = new Basis(column0, column1, column2); this.origin = origin; } + /// + /// Constructs a transformation matrix from the given quaternion and origin vector. + /// + /// The to create the basis from. + /// The origin vector, or column index 3. public Transform(Quat quat, Vector3 origin) { basis = new Basis(quat); this.origin = origin; } + /// + /// Constructs a transformation matrix from the given basis and origin vector. + /// + /// The to create the basis from. + /// The origin vector, or column index 3. public Transform(Basis basis, Vector3 origin) { this.basis = basis; @@ -255,6 +375,12 @@ namespace Godot return basis.Equals(other.basis) && origin.Equals(other.origin); } + /// + /// Returns true if this transform and `other` are approximately equal, by running + /// on each component. + /// + /// The other transform to compare. + /// Whether or not the matrices are approximately equal. public bool IsEqualApprox(Transform other) { return basis.IsEqualApprox(other.basis) && origin.IsEqualApprox(other.origin); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs index e72a44809a7..4e069c2f4be 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs @@ -8,25 +8,44 @@ using real_t = System.Single; namespace Godot { + /// + /// 2×3 matrix (2 rows, 3 columns) used for 2D linear transformations. + /// It can represent transformations such as translation, rotation, or scaling. + /// It consists of a three values: x, y, and the origin. + /// + /// For more information, read this documentation article: + /// https://docs.godotengine.org/en/latest/tutorials/math/matrices_and_transforms.html + /// [Serializable] [StructLayout(LayoutKind.Sequential)] public struct Transform2D : IEquatable { + /// + /// The basis matrix's X vector (column 0). Equivalent to array index `[0]`. + /// + /// public Vector2 x; + + /// + /// The basis matrix's Y vector (column 1). Equivalent to array index `[1]`. + /// public Vector2 y; + + /// + /// The origin vector (column 2, the third column). Equivalent to array index `[2]`. + /// The origin vector represents translation. + /// public Vector2 origin; + /// + /// The rotation of this transformation matrix. + /// + /// Getting is equivalent to calling with the values of . public real_t Rotation { get { - real_t det = BasisDeterminant(); - Transform2D t = Orthonormalized(); - if (det < 0) - { - t.ScaleBasis(new Vector2(1, -1)); - } - return Mathf.Atan2(t.x.y, t.x.x); + return Mathf.Atan2(x.y, x.x); } set { @@ -38,6 +57,10 @@ namespace Godot } } + /// + /// The scale of this transformation matrix. + /// + /// Equivalent to the lengths of each column vector, but Y is negative if the determinant is negative. public Vector2 Scale { get @@ -47,8 +70,7 @@ namespace Godot } set { - x = x.Normalized(); - y = y.Normalized(); + value /= Scale; // Value becomes what's called "delta_scale" in core. x *= value.x; y *= value.y; } @@ -112,6 +134,11 @@ namespace Godot } } + /// + /// Returns the inverse of the transform, under the assumption that + /// the transformation is composed of rotation, scaling, and translation. + /// + /// The inverse transformation matrix. public Transform2D AffineInverse() { real_t det = BasisDeterminant(); @@ -135,28 +162,58 @@ namespace Godot return inv; } + /// + /// Returns the determinant of the basis matrix. If the basis is + /// uniformly scaled, its determinant is the square of the scale. + /// + /// A negative determinant means the Y scale is negative. + /// A zero determinant means the basis isn't invertible, + /// and is usually considered invalid. + /// + /// The determinant of the basis matrix. private real_t BasisDeterminant() { return x.x * y.y - x.y * y.x; } + /// + /// Returns a vector transformed (multiplied) by the basis matrix. + /// This method does not account for translation (the origin vector). + /// + /// A vector to transform. + /// The transfomed vector. public Vector2 BasisXform(Vector2 v) { return new Vector2(Tdotx(v), Tdoty(v)); } + /// + /// Returns a vector transformed (multiplied) by the inverse basis matrix. + /// This method does not account for translation (the origin vector). + /// + /// Note: This results in a multiplication by the inverse of the + /// basis matrix only if it represents a rotation-reflection. + /// + /// A vector to inversely transform. + /// The inversely transfomed vector. public Vector2 BasisXformInv(Vector2 v) { return new Vector2(x.Dot(v), y.Dot(v)); } - public Transform2D InterpolateWith(Transform2D m, real_t c) + /// + /// Interpolates this transform to the other `transform` by `weight`. + /// + /// The other transform. + /// A value on the range of 0.0 to 1.0, representing the amount of interpolation. + /// The interpolated transform. + public Transform2D InterpolateWith(Transform2D transform, real_t weight) { real_t r1 = Rotation; - real_t r2 = m.Rotation; + real_t r2 = transform.Rotation; Vector2 s1 = Scale; - Vector2 s2 = m.Scale; + Vector2 s2 = transform.Scale; // Slerp rotation var v1 = new Vector2(Mathf.Cos(r1), Mathf.Sin(r1)); @@ -172,28 +229,34 @@ namespace Godot if (dot > 0.9995f) { // Linearly interpolate to avoid numerical precision issues - v = v1.LinearInterpolate(v2, c).Normalized(); + v = v1.LinearInterpolate(v2, weight).Normalized(); } else { - real_t angle = c * Mathf.Acos(dot); + real_t angle = weight * Mathf.Acos(dot); Vector2 v3 = (v2 - v1 * dot).Normalized(); v = v1 * Mathf.Cos(angle) + v3 * Mathf.Sin(angle); } // Extract parameters Vector2 p1 = origin; - Vector2 p2 = m.origin; + Vector2 p2 = transform.origin; // Construct matrix - var res = new Transform2D(Mathf.Atan2(v.y, v.x), p1.LinearInterpolate(p2, c)); - Vector2 scale = s1.LinearInterpolate(s2, c); + var res = new Transform2D(Mathf.Atan2(v.y, v.x), p1.LinearInterpolate(p2, weight)); + Vector2 scale = s1.LinearInterpolate(s2, weight); res.x *= scale; res.y *= scale; return res; } + /// + /// Returns the inverse of the transform, under the assumption that + /// the transformation is composed of rotation and translation + /// (no scaling, use for transforms with scaling). + /// + /// The inverse matrix. public Transform2D Inverse() { var inv = this; @@ -208,6 +271,11 @@ namespace Godot return inv; } + /// + /// Returns the transform with the basis orthogonal (90 degrees), + /// and normalized axis vectors (scale of 1 or -1). + /// + /// The orthonormalized transform. public Transform2D Orthonormalized() { var on = this; @@ -225,11 +293,21 @@ namespace Godot return on; } + /// + /// Rotates the transform by `phi` (in radians), using matrix multiplication. + /// + /// The angle to rotate, in radians. + /// The rotated transformation matrix. public Transform2D Rotated(real_t phi) { return this * new Transform2D(phi, new Vector2()); } + /// + /// Scales the transform by the given scaling factor, using matrix multiplication. + /// + /// The scale to introduce. + /// The scaled transformation matrix. public Transform2D Scaled(Vector2 scale) { var copy = this; @@ -257,6 +335,15 @@ namespace Godot return this[0, 1] * with[0] + this[1, 1] * with[1]; } + /// + /// Translates the transform by the given `offset`, + /// relative to the transform's basis vectors. + /// + /// Unlike and , + /// this does not use matrix multiplication. + /// + /// The offset to translate by. + /// The translated matrix. public Transform2D Translated(Vector2 offset) { var copy = this; @@ -264,11 +351,21 @@ namespace Godot return copy; } + /// + /// Returns a vector transformed (multiplied) by this transformation matrix. + /// + /// A vector to transform. + /// The transfomed vector. public Vector2 Xform(Vector2 v) { return new Vector2(Tdotx(v), Tdoty(v)) + origin; } + /// + /// Returns a vector transformed (multiplied) by the inverse transformation matrix. + /// + /// A vector to inversely transform. + /// The inversely transfomed vector. public Vector2 XformInv(Vector2 v) { Vector2 vInv = v - origin; @@ -280,11 +377,30 @@ namespace Godot private static readonly Transform2D _flipX = new Transform2D(-1, 0, 0, 1, 0, 0); private static readonly Transform2D _flipY = new Transform2D(1, 0, 0, -1, 0, 0); - public static Transform2D Identity => _identity; - public static Transform2D FlipX => _flipX; - public static Transform2D FlipY => _flipY; + /// + /// The identity transform, with no translation, rotation, or scaling applied. + /// This is used as a replacement for `Transform2D()` in GDScript. + /// Do not use `new Transform2D()` with no arguments in C#, because it sets all values to zero. + /// + /// Equivalent to `new Transform2D(Vector2.Right, Vector2.Down, Vector2.Zero)`. + public static Transform2D Identity { get { return _identity; } } + /// + /// The transform that will flip something along the X axis. + /// + /// Equivalent to `new Transform2D(Vector2.Left, Vector2.Down, Vector2.Zero)`. + public static Transform2D FlipX { get { return _flipX; } } + /// + /// The transform that will flip something along the Y axis. + /// + /// Equivalent to `new Transform2D(Vector2.Right, Vector2.Up, Vector2.Zero)`. + public static Transform2D FlipY { get { return _flipY; } } - // Constructors + /// + /// Constructs a transformation matrix from 3 vectors (matrix columns). + /// + /// The X vector, or column index 0. + /// The Y vector, or column index 1. + /// The origin vector, or column index 2. public Transform2D(Vector2 xAxis, Vector2 yAxis, Vector2 originPos) { x = xAxis; @@ -292,7 +408,16 @@ namespace Godot origin = originPos; } - // Arguments are named such that xy is equal to calling x.y + /// + /// Constructs a transformation matrix from the given components. + /// Arguments are named such that xy is equal to calling x.y + /// + /// The X component of the X column vector, accessed via `t.x.x` or `[0][0]` + /// The Y component of the X column vector, accessed via `t.x.y` or `[0][1]` + /// The X component of the Y column vector, accessed via `t.y.x` or `[1][0]` + /// The Y component of the Y column vector, accessed via `t.y.y` or `[1][1]` + /// The X component of the origin vector, accessed via `t.origin.x` or `[2][0]` + /// The Y component of the origin vector, accessed via `t.origin.y` or `[2][1]` public Transform2D(real_t xx, real_t xy, real_t yx, real_t yy, real_t ox, real_t oy) { x = new Vector2(xx, xy); @@ -300,6 +425,11 @@ namespace Godot origin = new Vector2(ox, oy); } + /// + /// Constructs a transformation matrix from a rotation value and origin vector. + /// + /// The rotation of the new transform, in radians. + /// The origin vector, or column index 2. public Transform2D(real_t rot, Vector2 pos) { x.x = y.y = Mathf.Cos(rot); @@ -345,6 +475,12 @@ namespace Godot return x.Equals(other.x) && y.Equals(other.y) && origin.Equals(other.origin); } + /// + /// Returns true if this transform and `other` are approximately equal, by running + /// on each component. + /// + /// The other transform to compare. + /// Whether or not the matrices are approximately equal. public bool IsEqualApprox(Transform2D other) { return x.IsEqualApprox(other.x) && y.IsEqualApprox(other.y) && origin.IsEqualApprox(other.origin); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs index f92453f546c..5e64f01d04e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs @@ -21,15 +21,29 @@ namespace Godot [StructLayout(LayoutKind.Sequential)] public struct Vector2 : IEquatable { + /// + /// Enumerated index values for the axes. + /// Returned by and . + /// public enum Axis { X = 0, Y } + /// + /// The vector's X component. Also accessible by using the index position `[0]`. + /// public real_t x; + /// + /// The vector's Y component. Also accessible by using the index position `[1]`. + /// public real_t y; + /// + /// Access vector components using their index. + /// + /// `[0]` is equivalent to `.x`, `[1]` is equivalent to `.y`. public real_t this[int index] { get @@ -76,46 +90,80 @@ namespace Godot } } - public real_t Cross(Vector2 b) - { - return x * b.y - y * b.x; - } - + /// + /// Returns a new vector with all components in absolute values (i.e. positive). + /// + /// A vector with called on each component. public Vector2 Abs() { return new Vector2(Mathf.Abs(x), Mathf.Abs(y)); } + /// + /// Returns this vector's angle with respect to the X axis, or (1, 0) vector, in radians. + /// + /// Equivalent to the result of when + /// called with the vector's `y` and `x` as parameters: `Mathf.Atan2(v.y, v.x)`. + /// + /// The angle of this vector, in radians. public real_t Angle() { return Mathf.Atan2(y, x); } + /// + /// Returns the angle to the given vector, in radians. + /// + /// The other vector to compare this vector to. + /// The angle between the two vectors, in radians. public real_t AngleTo(Vector2 to) { return Mathf.Atan2(Cross(to), Dot(to)); } + /// + /// Returns the angle between the line connecting the two points and the X axis, in radians. + /// + /// The other vector to compare this vector to. + /// The angle between the two vectors, in radians. public real_t AngleToPoint(Vector2 to) { return Mathf.Atan2(y - to.y, x - to.x); } + /// + /// Returns the aspect ratio of this vector, the ratio of `x` to `y`. + /// + /// The `x` component divided by the `y` component. public real_t Aspect() { return x / y; } - public Vector2 Bounce(Vector2 n) + /// + /// Returns the vector "bounced off" from a plane defined by the given normal. + /// + /// The normal vector defining the plane to bounce off. Must be normalized. + /// The bounced vector. + public Vector2 Bounce(Vector2 normal) { - return -Reflect(n); + return -Reflect(normal); } + /// + /// Returns a new vector with all components rounded up (towards positive infinity). + /// + /// A vector with called on each component. public Vector2 Ceil() { return new Vector2(Mathf.Ceil(x), Mathf.Ceil(y)); } + /// + /// Returns the vector with a maximum length by limiting its length to `length`. + /// + /// The length to limit to. + /// The vector with its length limited. public Vector2 Clamped(real_t length) { var v = this; @@ -130,12 +178,30 @@ namespace Godot return v; } + /// + /// Returns the cross product of this vector and `b`. + /// + /// The other vector. + /// The cross product value. + public real_t Cross(Vector2 b) + { + return x * b.y - y * b.x; + } + + /// + /// Performs a cubic interpolation between vectors `preA`, this vector, `b`, and `postB`, by the given amount `t`. + /// + /// The destination vector. + /// A vector before this vector. + /// A vector after `b`. + /// A value on the range of 0.0 to 1.0, representing the amount of interpolation. + /// The interpolated vector. public Vector2 CubicInterpolate(Vector2 b, Vector2 preA, Vector2 postB, real_t t) { - var p0 = preA; - var p1 = this; - var p2 = b; - var p3 = postB; + Vector2 p0 = preA; + Vector2 p1 = this; + Vector2 p2 = b; + Vector2 p3 = postB; real_t t2 = t * t; real_t t3 = t2 * t; @@ -146,56 +212,153 @@ namespace Godot (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3); } + /// + /// Returns the normalized vector pointing from this vector to `b`. + /// + /// The other vector to point towards. + /// The direction from this vector to `b`. public Vector2 DirectionTo(Vector2 b) { return new Vector2(b.x - x, b.y - y).Normalized(); } + /// + /// Returns the squared distance between this vector and `to`. + /// This method runs faster than , so prefer it if + /// you need to compare vectors or need the squared distance for some formula. + /// + /// The other vector to use. + /// The squared distance between the two vectors. public real_t DistanceSquaredTo(Vector2 to) { return (x - to.x) * (x - to.x) + (y - to.y) * (y - to.y); } + /// + /// Returns the distance between this vector and `to`. + /// + /// The other vector to use. + /// The distance between the two vectors. public real_t DistanceTo(Vector2 to) { return Mathf.Sqrt((x - to.x) * (x - to.x) + (y - to.y) * (y - to.y)); } + /// + /// Returns the dot product of this vector and `with`. + /// + /// The other vector to use. + /// The dot product of the two vectors. public real_t Dot(Vector2 with) { return x * with.x + y * with.y; } + /// + /// Returns a new vector with all components rounded down (towards negative infinity). + /// + /// A vector with called on each component. public Vector2 Floor() { return new Vector2(Mathf.Floor(x), Mathf.Floor(y)); } + /// + /// Returns the inverse of this vector. This is the same as `new Vector2(1 / v.x, 1 / v.y)`. + /// + /// The inverse of this vector. + public Vector2 Inverse() + { + return new Vector2(1 / x, 1 / y); + } + + /// + /// Returns true if the vector is normalized, and false otherwise. + /// + /// A bool indicating whether or not the vector is normalized. public bool IsNormalized() { return Mathf.Abs(LengthSquared() - 1.0f) < Mathf.Epsilon; } + /// + /// Returns the length (magnitude) of this vector. + /// + /// The length of this vector. public real_t Length() { return Mathf.Sqrt(x * x + y * y); } + /// + /// Returns the squared length (squared magnitude) of this vector. + /// This method runs faster than , so prefer it if + /// you need to compare vectors or need the squared length for some formula. + /// + /// The squared length of this vector. public real_t LengthSquared() { return x * x + y * y; } - public Vector2 LinearInterpolate(Vector2 b, real_t t) + /// + /// Returns the result of the linear interpolation between + /// this vector and `to` by amount `weight`. + /// + /// The destination vector for interpolation. + /// A value on the range of 0.0 to 1.0, representing the amount of interpolation. + /// The resulting vector of the interpolation. + public Vector2 LinearInterpolate(Vector2 to, real_t weight) { - var res = this; - - res.x += t * (b.x - x); - res.y += t * (b.y - y); - - return res; + return new Vector2 + ( + Mathf.Lerp(x, to.x, weight), + Mathf.Lerp(y, to.y, weight) + ); } + /// + /// Returns the result of the linear interpolation between + /// this vector and `to` by the vector amount `weight`. + /// + /// The destination vector for interpolation. + /// A vector with components on the range of 0.0 to 1.0, representing the amount of interpolation. + /// The resulting vector of the interpolation. + public Vector2 LinearInterpolate(Vector2 to, Vector2 weight) + { + return new Vector2 + ( + Mathf.Lerp(x, to.x, weight.x), + Mathf.Lerp(y, to.y, weight.y) + ); + } + + /// + /// Returns the axis of the vector's largest value. See . + /// If both components are equal, this method returns . + /// + /// The index of the largest axis. + public Axis MaxAxis() + { + return x < y ? Axis.Y : Axis.X; + } + + /// + /// Returns the axis of the vector's smallest value. See . + /// If both components are equal, this method returns . + /// + /// The index of the smallest axis. + public Axis MinAxis() + { + return x < y ? Axis.X : Axis.Y; + } + + /// + /// Moves this vector toward `to` by the fixed `delta` amount. + /// + /// The vector to move towards. + /// The amount to move towards by. + /// The resulting vector. public Vector2 MoveToward(Vector2 to, real_t delta) { var v = this; @@ -204,6 +367,10 @@ namespace Godot return len <= delta || len < Mathf.Epsilon ? to : v + vd / len * delta; } + /// + /// Returns the vector scaled to unit length. Equivalent to `v / v.Length()`. + /// + /// A normalized version of the vector. public Vector2 Normalized() { var v = this; @@ -211,6 +378,21 @@ namespace Godot return v; } + /// + /// Returns a perpendicular vector rotated 90 degrees counter-clockwise + /// compared to the original, with the same length. + /// + /// The perpendicular vector. + public Vector2 Perpendicular() + { + return new Vector2(y, -x); + } + + /// + /// Returns a vector composed of the of this vector's components and `mod`. + /// + /// A value representing the divisor of the operation. + /// A vector with each component by `mod`. public Vector2 PosMod(real_t mod) { Vector2 v; @@ -219,6 +401,11 @@ namespace Godot return v; } + /// + /// Returns a vector composed of the of this vector's components and `modv`'s components. + /// + /// A vector representing the divisors of the operation. + /// A vector with each component by `modv`'s components. public Vector2 PosMod(Vector2 modv) { Vector2 v; @@ -227,22 +414,48 @@ namespace Godot return v; } + /// + /// Returns this vector projected onto another vector. + /// + /// The vector to project onto. + /// The projected vector. public Vector2 Project(Vector2 onNormal) { return onNormal * (Dot(onNormal) / onNormal.LengthSquared()); } - public Vector2 Reflect(Vector2 n) + /// + /// Returns this vector reflected from a plane defined by the given `normal`. + /// + /// The normal vector defining the plane to reflect from. Must be normalized. + /// The reflected vector. + public Vector2 Reflect(Vector2 normal) { - return 2.0f * n * Dot(n) - this; +#if DEBUG + if (!normal.IsNormalized()) + { + throw new ArgumentException("Argument is not normalized", nameof(normal)); + } +#endif + return 2 * Dot(normal) * normal - this; } + /// + /// Rotates this vector by `phi` radians. + /// + /// The angle to rotate by, in radians. + /// The rotated vector. public Vector2 Rotated(real_t phi) { real_t rads = Angle() + phi; return new Vector2(Mathf.Cos(rads), Mathf.Sin(rads)) * Length(); } + /// + /// Returns this vector with all components rounded to the nearest integer, + /// with halfway cases rounded towards the nearest multiple of two. + /// + /// The rounded vector. public Vector2 Round() { return new Vector2(Mathf.Round(x), Mathf.Round(y)); @@ -261,6 +474,12 @@ namespace Godot y = v.y; } + /// + /// 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 on each component. + /// + /// A vector with all components as either `1`, `-1`, or `0`. public Vector2 Sign() { Vector2 v; @@ -269,22 +488,57 @@ namespace Godot return v; } - public Vector2 Slerp(Vector2 b, real_t t) + /// + /// Returns the result of the spherical linear interpolation between + /// this vector and `to` by amount `weight`. + /// + /// Note: Both vectors must be normalized. + /// + /// The destination vector for interpolation. Must be normalized. + /// A value on the range of 0.0 to 1.0, representing the amount of interpolation. + /// The resulting vector of the interpolation. + public Vector2 Slerp(Vector2 to, real_t weight) { - real_t theta = AngleTo(b); - return Rotated(theta * t); +#if DEBUG + if (!IsNormalized()) + { + throw new InvalidOperationException("Vector2.Slerp: From vector is not normalized."); + } + if (!to.IsNormalized()) + { + throw new InvalidOperationException("Vector2.Slerp: `to` is not normalized."); + } +#endif + return Rotated(AngleTo(to) * weight); } - public Vector2 Slide(Vector2 n) + /// + /// Returns this vector slid along a plane defined by the given normal. + /// + /// The normal vector defining the plane to slide on. + /// The slid vector. + public Vector2 Slide(Vector2 normal) { - return this - n * Dot(n); + return this - normal * Dot(normal); } - public Vector2 Snapped(Vector2 by) + /// + /// Returns this vector with each component snapped to the nearest multiple of `step`. + /// This can also be used to round to an arbitrary number of decimals. + /// + /// A vector value representing the step size to snap to. + /// The snapped vector. + public Vector2 Snapped(Vector2 step) { - return new Vector2(Mathf.Stepify(x, by.x), Mathf.Stepify(y, by.y)); + return new Vector2(Mathf.Stepify(x, step.x), Mathf.Stepify(y, step.y)); } + /// + /// Returns a perpendicular vector rotated 90 degrees counter-clockwise + /// compared to the original, with the same length. + /// Deprecated, will be replaced by in 4.0. + /// + /// The perpendicular vector. public Vector2 Tangent() { return new Vector2(y, -x); @@ -301,22 +555,63 @@ namespace Godot private static readonly Vector2 _right = new Vector2(1, 0); private static readonly Vector2 _left = new Vector2(-1, 0); + /// + /// Zero vector, a vector with all components set to `0`. + /// + /// Equivalent to `new Vector2(0, 0)` public static Vector2 Zero { get { return _zero; } } + /// + /// Deprecated, please use a negative sign with instead. + /// + /// Equivalent to `new Vector2(-1, -1)` public static Vector2 NegOne { get { return _negOne; } } + /// + /// One vector, a vector with all components set to `1`. + /// + /// Equivalent to `new Vector2(1, 1)` public static Vector2 One { get { return _one; } } + /// + /// Infinity vector, a vector with all components set to `Mathf.Inf`. + /// + /// Equivalent to `new Vector2(Mathf.Inf, Mathf.Inf)` public static Vector2 Inf { get { return _inf; } } + /// + /// Up unit vector. Y is down in 2D, so this vector points -Y. + /// + /// Equivalent to `new Vector2(0, -1)` public static Vector2 Up { get { return _up; } } + /// + /// Down unit vector. Y is down in 2D, so this vector points +Y. + /// + /// Equivalent to `new Vector2(0, 1)` public static Vector2 Down { get { return _down; } } + /// + /// Right unit vector. Represents the direction of right. + /// + /// Equivalent to `new Vector2(1, 0)` public static Vector2 Right { get { return _right; } } + /// + /// Left unit vector. Represents the direction of left. + /// + /// Equivalent to `new Vector2(-1, 0)` public static Vector2 Left { get { return _left; } } - // Constructors + /// + /// Constructs a new with the given components. + /// + /// The vector's X component. + /// The vector's Y component. public Vector2(real_t x, real_t y) { this.x = x; this.y = y; } + + /// + /// Constructs a new from an existing . + /// + /// The existing . public Vector2(Vector2 v) { x = v.x; @@ -365,18 +660,18 @@ namespace Godot return left; } - public static Vector2 operator /(Vector2 vec, real_t scale) + public static Vector2 operator /(Vector2 vec, real_t divisor) { - vec.x /= scale; - vec.y /= scale; + vec.x /= divisor; + vec.y /= divisor; return vec; } - public static Vector2 operator /(Vector2 left, Vector2 right) + public static Vector2 operator /(Vector2 vec, Vector2 divisorv) { - left.x /= right.x; - left.y /= right.y; - return left; + vec.x /= divisorv.x; + vec.y /= divisorv.y; + return vec; } public static Vector2 operator %(Vector2 vec, real_t divisor) @@ -458,6 +753,12 @@ namespace Godot return x == other.x && y == other.y; } + /// + /// Returns true if this vector and `other` are approximately equal, by running + /// on each component. + /// + /// The other vector to compare. + /// Whether or not the vectors are approximately equal. public bool IsEqualApprox(Vector2 other) { return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs index fded34002df..fe36ac81d1c 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs @@ -21,6 +21,10 @@ namespace Godot [StructLayout(LayoutKind.Sequential)] public struct Vector3 : IEquatable { + /// + /// Enumerated index values for the axes. + /// Returned by and . + /// public enum Axis { X = 0, @@ -28,10 +32,23 @@ namespace Godot Z } + /// + /// The vector's X component. Also accessible by using the index position `[0]`. + /// public real_t x; + /// + /// The vector's Y component. Also accessible by using the index position `[1]`. + /// public real_t y; + /// + /// The vector's Z component. Also accessible by using the index position `[2]`. + /// public real_t z; + /// + /// Access vector components using their index. + /// + /// `[0]` is equivalent to `.x`, `[1]` is equivalent to `.y`, `[2]` is equivalent to `.z`. public real_t this[int index] { get @@ -84,26 +101,49 @@ namespace Godot } } + /// + /// Returns a new vector with all components in absolute values (i.e. positive). + /// + /// A vector with called on each component. public Vector3 Abs() { return new Vector3(Mathf.Abs(x), Mathf.Abs(y), Mathf.Abs(z)); } + /// + /// Returns the minimum angle to the given vector, in radians. + /// + /// The other vector to compare this vector to. + /// The angle between the two vectors, in radians. public real_t AngleTo(Vector3 to) { return Mathf.Atan2(Cross(to).Length(), Dot(to)); } - public Vector3 Bounce(Vector3 n) + /// + /// Returns this vector "bounced off" from a plane defined by the given normal. + /// + /// The normal vector defining the plane to bounce off. Must be normalized. + /// The bounced vector. + public Vector3 Bounce(Vector3 normal) { - return -Reflect(n); + return -Reflect(normal); } + /// + /// Returns a new vector with all components rounded up (towards positive infinity). + /// + /// A vector with called on each component. public Vector3 Ceil() { return new Vector3(Mathf.Ceil(x), Mathf.Ceil(y), Mathf.Ceil(z)); } + /// + /// Returns the cross product of this vector and `b`. + /// + /// The other vector. + /// The cross product vector. public Vector3 Cross(Vector3 b) { return new Vector3 @@ -114,12 +154,21 @@ namespace Godot ); } + /// + /// Performs a cubic interpolation between vectors `preA`, this vector, + /// `b`, and `postB`, by the given amount `t`. + /// + /// The destination vector. + /// A vector before this vector. + /// A vector after `b`. + /// A value on the range of 0.0 to 1.0, representing the amount of interpolation. + /// The interpolated vector. public Vector3 CubicInterpolate(Vector3 b, Vector3 preA, Vector3 postB, real_t t) { - var p0 = preA; - var p1 = this; - var p2 = b; - var p3 = postB; + Vector3 p0 = preA; + Vector3 p1 = this; + Vector3 p2 = b; + Vector3 p3 = postB; real_t t2 = t * t; real_t t3 = t2 * t; @@ -131,41 +180,79 @@ namespace Godot ); } + /// + /// Returns the normalized vector pointing from this vector to `b`. + /// + /// The other vector to point towards. + /// The direction from this vector to `b`. public Vector3 DirectionTo(Vector3 b) { return new Vector3(b.x - x, b.y - y, b.z - z).Normalized(); } + /// + /// Returns the squared distance between this vector and `b`. + /// This method runs faster than , so prefer it if + /// you need to compare vectors or need the squared distance for some formula. + /// + /// The other vector to use. + /// The squared distance between the two vectors. public real_t DistanceSquaredTo(Vector3 b) { return (b - this).LengthSquared(); } + /// + /// Returns the distance between this vector and `b`. + /// + /// The other vector to use. + /// The distance between the two vectors. public real_t DistanceTo(Vector3 b) { return (b - this).Length(); } + /// + /// Returns the dot product of this vector and `b`. + /// + /// The other vector to use. + /// The dot product of the two vectors. public real_t Dot(Vector3 b) { return x * b.x + y * b.y + z * b.z; } + /// + /// Returns a new vector with all components rounded down (towards negative infinity). + /// + /// A vector with called on each component. public Vector3 Floor() { return new Vector3(Mathf.Floor(x), Mathf.Floor(y), Mathf.Floor(z)); } + /// + /// Returns the inverse of this vector. This is the same as `new Vector3(1 / v.x, 1 / v.y, 1 / v.z)`. + /// + /// The inverse of this vector. public Vector3 Inverse() { - return new Vector3(1.0f / x, 1.0f / y, 1.0f / z); + return new Vector3(1 / x, 1 / y, 1 / z); } + /// + /// Returns true if the vector is normalized, and false otherwise. + /// + /// A bool indicating whether or not the vector is normalized. public bool IsNormalized() { return Mathf.Abs(LengthSquared() - 1.0f) < Mathf.Epsilon; } + /// + /// Returns the length (magnitude) of this vector. + /// + /// The length of this vector. public real_t Length() { real_t x2 = x * x; @@ -175,6 +262,12 @@ namespace Godot return Mathf.Sqrt(x2 + y2 + z2); } + /// + /// Returns the squared length (squared magnitude) of this vector. + /// This method runs faster than , so prefer it if + /// you need to compare vectors or need the squared length for some formula. + /// + /// The squared length of this vector. public real_t LengthSquared() { real_t x2 = x * x; @@ -184,16 +277,66 @@ namespace Godot return x2 + y2 + z2; } - public Vector3 LinearInterpolate(Vector3 b, real_t t) + /// + /// Returns the result of the linear interpolation between + /// this vector and `to` by amount `weight`. + /// + /// The destination vector for interpolation. + /// A value on the range of 0.0 to 1.0, representing the amount of interpolation. + /// The resulting vector of the interpolation. + public Vector3 LinearInterpolate(Vector3 to, real_t weight) { return new Vector3 ( - x + t * (b.x - x), - y + t * (b.y - y), - z + t * (b.z - z) + Mathf.Lerp(x, to.x, weight), + Mathf.Lerp(y, to.y, weight), + Mathf.Lerp(z, to.z, weight) ); } + /// + /// Returns the result of the linear interpolation between + /// this vector and `to` by the vector amount `weight`. + /// + /// The destination vector for interpolation. + /// A vector with components on the range of 0.0 to 1.0, representing the amount of interpolation. + /// The resulting vector of the interpolation. + public Vector3 LinearInterpolate(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) + ); + } + + /// + /// Returns the axis of the vector's largest value. See . + /// If all components are equal, this method returns . + /// + /// The index of the largest axis. + public Axis MaxAxis() + { + return x < y ? (y < z ? Axis.Z : Axis.Y) : (x < z ? Axis.Z : Axis.X); + } + + /// + /// Returns the axis of the vector's smallest value. See . + /// If all components are equal, this method returns . + /// + /// The index of the smallest axis. + public Axis MinAxis() + { + return x < y ? (x < z ? Axis.X : Axis.Z) : (y < z ? Axis.Y : Axis.Z); + } + + /// + /// Moves this vector toward `to` by the fixed `delta` amount. + /// + /// The vector to move towards. + /// The amount to move towards by. + /// The resulting vector. public Vector3 MoveToward(Vector3 to, real_t delta) { var v = this; @@ -202,16 +345,10 @@ namespace Godot return len <= delta || len < Mathf.Epsilon ? to : v + vd / len * delta; } - public Axis MaxAxis() - { - return x < y ? (y < z ? Axis.Z : Axis.Y) : (x < z ? Axis.Z : Axis.X); - } - - public Axis MinAxis() - { - return x < y ? (x < z ? Axis.X : Axis.Z) : (y < z ? Axis.Y : Axis.Z); - } - + /// + /// Returns the vector scaled to unit length. Equivalent to `v / v.Length()`. + /// + /// A normalized version of the vector. public Vector3 Normalized() { var v = this; @@ -219,6 +356,11 @@ namespace Godot return v; } + /// + /// Returns the outer product with `b`. + /// + /// The other vector. + /// A representing the outer product matrix. public Basis Outer(Vector3 b) { return new Basis( @@ -228,6 +370,11 @@ namespace Godot ); } + /// + /// Returns a vector composed of the of this vector's components and `mod`. + /// + /// A value representing the divisor of the operation. + /// A vector with each component by `mod`. public Vector3 PosMod(real_t mod) { Vector3 v; @@ -237,6 +384,11 @@ namespace Godot return v; } + /// + /// Returns a vector composed of the of this vector's components and `modv`'s components. + /// + /// A vector representing the divisors of the operation. + /// A vector with each component by `modv`'s components. public Vector3 PosMod(Vector3 modv) { Vector3 v; @@ -246,30 +398,60 @@ namespace Godot return v; } + /// + /// Returns this vector projected onto another vector `b`. + /// + /// The vector to project onto. + /// The projected vector. public Vector3 Project(Vector3 onNormal) { return onNormal * (Dot(onNormal) / onNormal.LengthSquared()); } - public Vector3 Reflect(Vector3 n) + /// + /// Returns this vector reflected from a plane defined by the given `normal`. + /// + /// The normal vector defining the plane to reflect from. Must be normalized. + /// The reflected vector. + public Vector3 Reflect(Vector3 normal) { #if DEBUG - if (!n.IsNormalized()) - throw new ArgumentException("Argument is not normalized", nameof(n)); + if (!normal.IsNormalized()) + { + throw new ArgumentException("Argument is not normalized", nameof(normal)); + } #endif - return 2.0f * n * Dot(n) - this; + return 2.0f * Dot(normal) * normal - this; } + /// + /// Rotates this vector around a given `axis` vector by `phi` radians. + /// The `axis` vector must be a normalized vector. + /// + /// The vector to rotate around. Must be normalized. + /// The angle to rotate by, in radians. + /// The rotated vector. + public Vector3 Rotated(Vector3 axis, real_t phi) + { +#if DEBUG + if (!axis.IsNormalized()) + { + throw new ArgumentException("Argument is not normalized", nameof(axis)); + } +#endif + return new Basis(axis, phi).Xform(this); + } + + /// + /// Returns this vector with all components rounded to the nearest integer, + /// with halfway cases rounded towards the nearest multiple of two. + /// + /// The rounded vector. public Vector3 Round() { return new Vector3(Mathf.Round(x), Mathf.Round(y), Mathf.Round(z)); } - public Vector3 Rotated(Vector3 axis, real_t phi) - { - return new Basis(axis, phi).Xform(this); - } - [Obsolete("Set is deprecated. Use the Vector3(" + nameof(real_t) + ", " + nameof(real_t) + ", " + nameof(real_t) + ") constructor instead.", error: true)] public void Set(real_t x, real_t y, real_t z) { @@ -285,6 +467,12 @@ namespace Godot z = v.z; } + /// + /// 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 on each component. + /// + /// A vector with all components as either `1`, `-1`, or `0`. public Vector3 Sign() { Vector3 v; @@ -294,37 +482,70 @@ namespace Godot return v; } - public Vector3 Slerp(Vector3 b, real_t t) + /// + /// Returns the result of the spherical linear interpolation between + /// this vector and `to` by amount `weight`. + /// + /// Note: Both vectors must be normalized. + /// + /// The destination vector for interpolation. Must be normalized. + /// A value on the range of 0.0 to 1.0, representing the amount of interpolation. + /// The resulting vector of the interpolation. + public Vector3 Slerp(Vector3 to, real_t weight) { #if DEBUG if (!IsNormalized()) - throw new InvalidOperationException("Vector3 is not normalized"); + { + throw new InvalidOperationException("Vector3.Slerp: From vector is not normalized."); + } + if (!to.IsNormalized()) + { + throw new InvalidOperationException("Vector3.Slerp: `to` is not normalized."); + } #endif - real_t theta = AngleTo(b); - return Rotated(Cross(b), theta * t); + real_t theta = AngleTo(to); + return Rotated(Cross(to), theta * weight); } - public Vector3 Slide(Vector3 n) + /// + /// Returns this vector slid along a plane defined by the given normal. + /// + /// The normal vector defining the plane to slide on. + /// The slid vector. + public Vector3 Slide(Vector3 normal) { - return this - n * Dot(n); + return this - normal * Dot(normal); } - public Vector3 Snapped(Vector3 by) + /// + /// Returns this vector with each component snapped to the nearest multiple of `step`. + /// This can also be used to round to an arbitrary number of decimals. + /// + /// A vector value representing the step size to snap to. + /// The snapped vector. + public Vector3 Snapped(Vector3 step) { return new Vector3 ( - Mathf.Stepify(x, by.x), - Mathf.Stepify(y, by.y), - Mathf.Stepify(z, by.z) + Mathf.Stepify(x, step.x), + Mathf.Stepify(y, step.y), + Mathf.Stepify(z, step.z) ); } + /// + /// Returns a diagonal matrix with the vector as main diagonal. + /// + /// This is equivalent to a Basis with no rotation or shearing and + /// this vector's components set as the scale. + /// + /// A Basis with the vector as its main diagonal. public Basis ToDiagonalMatrix() { return new Basis( - x, 0f, 0f, - 0f, y, 0f, - 0f, 0f, z + x, 0, 0, + 0, y, 0, + 0, 0, z ); } @@ -341,25 +562,79 @@ namespace Godot private static readonly Vector3 _forward = new Vector3(0, 0, -1); private static readonly Vector3 _back = new Vector3(0, 0, 1); + /// + /// Zero vector, a vector with all components set to `0`. + /// + /// Equivalent to `new Vector3(0, 0, 0)` public static Vector3 Zero { get { return _zero; } } + /// + /// One vector, a vector with all components set to `1`. + /// + /// Equivalent to `new Vector3(1, 1, 1)` public static Vector3 One { get { return _one; } } + /// + /// Deprecated, please use a negative sign with instead. + /// + /// Equivalent to `new Vector3(-1, -1, -1)` public static Vector3 NegOne { get { return _negOne; } } + /// + /// Infinity vector, a vector with all components set to `Mathf.Inf`. + /// + /// Equivalent to `new Vector3(Mathf.Inf, Mathf.Inf, Mathf.Inf)` public static Vector3 Inf { get { return _inf; } } + /// + /// Up unit vector. + /// + /// Equivalent to `new Vector3(0, 1, 0)` public static Vector3 Up { get { return _up; } } + /// + /// Down unit vector. + /// + /// Equivalent to `new Vector3(0, -1, 0)` public static Vector3 Down { get { return _down; } } + /// + /// Right unit vector. Represents the local direction of right, + /// and the global direction of east. + /// + /// Equivalent to `new Vector3(1, 0, 0)` public static Vector3 Right { get { return _right; } } + /// + /// Left unit vector. Represents the local direction of left, + /// and the global direction of west. + /// + /// Equivalent to `new Vector3(-1, 0, 0)` public static Vector3 Left { get { return _left; } } + /// + /// Forward unit vector. Represents the local direction of forward, + /// and the global direction of north. + /// + /// Equivalent to `new Vector3(0, 0, -1)` public static Vector3 Forward { get { return _forward; } } + /// + /// Back unit vector. Represents the local direction of back, + /// and the global direction of south. + /// + /// Equivalent to `new Vector3(0, 0, 1)` public static Vector3 Back { get { return _back; } } - // Constructors + /// + /// Constructs a new with the given components. + /// + /// The vector's X component. + /// The vector's Y component. + /// The vector's Z component. public Vector3(real_t x, real_t y, real_t z) { this.x = x; this.y = y; this.z = z; } + + /// + /// Constructs a new from an existing . + /// + /// The existing . public Vector3(Vector3 v) { x = v.x; @@ -415,20 +690,20 @@ namespace Godot return left; } - public static Vector3 operator /(Vector3 vec, real_t scale) + public static Vector3 operator /(Vector3 vec, real_t divisor) { - vec.x /= scale; - vec.y /= scale; - vec.z /= scale; + vec.x /= divisor; + vec.y /= divisor; + vec.z /= divisor; return vec; } - public static Vector3 operator /(Vector3 left, Vector3 right) + public static Vector3 operator /(Vector3 vec, Vector3 divisorv) { - left.x /= right.x; - left.y /= right.y; - left.z /= right.z; - return left; + vec.x /= divisorv.x; + vec.y /= divisorv.y; + vec.z /= divisorv.z; + return vec; } public static Vector3 operator %(Vector3 vec, real_t divisor) @@ -520,6 +795,12 @@ namespace Godot return x == other.x && y == other.y && z == other.z; } + /// + /// Returns true if this vector and `other` are approximately equal, by running + /// on each component. + /// + /// The other vector to compare. + /// Whether or not the vectors are approximately equal. public bool IsEqualApprox(Vector3 other) { return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z);