diff options
author | Ignacio Etcheverry <ignalfonsore@gmail.com> | 2018-09-12 21:22:43 +0200 |
---|---|---|
committer | Ignacio Etcheverry <ignalfonsore@gmail.com> | 2018-09-12 22:03:36 +0200 |
commit | 995a40e8efa9f7a868a6706e30c476305e16180b (patch) | |
tree | 95c4187543fd237695ae0d5da5018776c1626867 /modules/mono/glue/Managed/Files | |
parent | 5e57beebb1a43c65d949fdfe5081f9c27f181c80 (diff) |
Move modules/mono/glue/cs_files to modules/mono/glue/Managed/Files
Added dummy MSBuild project and solution to get tooling help when editing these files.
Diffstat (limited to 'modules/mono/glue/Managed/Files')
34 files changed, 7067 insertions, 0 deletions
diff --git a/modules/mono/glue/Managed/Files/AABB.cs b/modules/mono/glue/Managed/Files/AABB.cs new file mode 100644 index 0000000000..66490b5e25 --- /dev/null +++ b/modules/mono/glue/Managed/Files/AABB.cs @@ -0,0 +1,466 @@ +// file: core/math/aabb.h +// commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451 +// file: core/math/aabb.cpp +// commit: bd282ff43f23fe845f29a3e25c8efc01bd65ffb0 +// file: core/variant_call.cpp +// commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685 +using System; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + +namespace Godot +{ + public struct AABB : IEquatable<AABB> + { + private Vector3 _position; + private Vector3 _size; + + public Vector3 Position + { + get { return _position; } + set { _position = value; } + } + + public Vector3 Size + { + get { return _size; } + set { _size = value; } + } + + public Vector3 End + { + get { return _position + _size; } + set { _size = value - _position; } + } + + public bool Encloses(AABB with) + { + Vector3 src_min = _position; + Vector3 src_max = _position + _size; + Vector3 dst_min = with._position; + Vector3 dst_max = with._position + with._size; + + return src_min.x <= dst_min.x && + src_max.x > dst_max.x && + src_min.y <= dst_min.y && + src_max.y > dst_max.y && + src_min.z <= dst_min.z && + src_max.z > dst_max.z; + } + + 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); + } + + public real_t GetArea() + { + return _size.x * _size.y * _size.z; + } + + public Vector3 GetEndpoint(int idx) + { + switch (idx) + { + case 0: + return new Vector3(_position.x, _position.y, _position.z); + case 1: + return new Vector3(_position.x, _position.y, _position.z + _size.z); + case 2: + return new Vector3(_position.x, _position.y + _size.y, _position.z); + case 3: + return new Vector3(_position.x, _position.y + _size.y, _position.z + _size.z); + case 4: + return new Vector3(_position.x + _size.x, _position.y, _position.z); + case 5: + return new Vector3(_position.x + _size.x, _position.y, _position.z + _size.z); + case 6: + return new Vector3(_position.x + _size.x, _position.y + _size.y, _position.z); + case 7: + return new Vector3(_position.x + _size.x, _position.y + _size.y, _position.z + _size.z); + default: + throw new ArgumentOutOfRangeException(nameof(idx), String.Format("Index is {0}, but a value from 0 to 7 is expected.", idx)); + } + } + + public Vector3 GetLongestAxis() + { + var axis = new Vector3(1f, 0f, 0f); + real_t max_size = _size.x; + + if (_size.y > max_size) + { + axis = new Vector3(0f, 1f, 0f); + max_size = _size.y; + } + + if (_size.z > max_size) + { + axis = new Vector3(0f, 0f, 1f); + } + + return axis; + } + + public Vector3.Axis GetLongestAxisIndex() + { + var axis = Vector3.Axis.X; + real_t max_size = _size.x; + + if (_size.y > max_size) + { + axis = Vector3.Axis.Y; + max_size = _size.y; + } + + if (_size.z > max_size) + { + axis = Vector3.Axis.Z; + } + + return axis; + } + + public real_t GetLongestAxisSize() + { + real_t max_size = _size.x; + + if (_size.y > max_size) + max_size = _size.y; + + if (_size.z > max_size) + max_size = _size.z; + + return max_size; + } + + public Vector3 GetShortestAxis() + { + var axis = new Vector3(1f, 0f, 0f); + real_t max_size = _size.x; + + if (_size.y < max_size) + { + axis = new Vector3(0f, 1f, 0f); + max_size = _size.y; + } + + if (_size.z < max_size) + { + axis = new Vector3(0f, 0f, 1f); + } + + return axis; + } + + public Vector3.Axis GetShortestAxisIndex() + { + var axis = Vector3.Axis.X; + real_t max_size = _size.x; + + if (_size.y < max_size) + { + axis = Vector3.Axis.Y; + max_size = _size.y; + } + + if (_size.z < max_size) + { + axis = Vector3.Axis.Z; + } + + return axis; + } + + public real_t GetShortestAxisSize() + { + real_t max_size = _size.x; + + if (_size.y < max_size) + max_size = _size.y; + + if (_size.z < max_size) + max_size = _size.z; + + return max_size; + } + + public Vector3 GetSupport(Vector3 dir) + { + Vector3 half_extents = _size * 0.5f; + Vector3 ofs = _position + half_extents; + + return ofs + new Vector3( + dir.x > 0f ? -half_extents.x : half_extents.x, + dir.y > 0f ? -half_extents.y : half_extents.y, + dir.z > 0f ? -half_extents.z : half_extents.z); + } + + public AABB Grow(real_t by) + { + var res = this; + + res._position.x -= by; + res._position.y -= by; + res._position.z -= by; + res._size.x += 2.0f * by; + res._size.y += 2.0f * by; + res._size.z += 2.0f * by; + + return res; + } + + public bool HasNoArea() + { + return _size.x <= 0f || _size.y <= 0f || _size.z <= 0f; + } + + public bool HasNoSurface() + { + return _size.x <= 0f && _size.y <= 0f && _size.z <= 0f; + } + + public bool HasPoint(Vector3 point) + { + if (point.x < _position.x) + return false; + if (point.y < _position.y) + return false; + if (point.z < _position.z) + return false; + if (point.x > _position.x + _size.x) + return false; + if (point.y > _position.y + _size.y) + return false; + if (point.z > _position.z + _size.z) + return false; + + return true; + } + + public AABB Intersection(AABB with) + { + Vector3 src_min = _position; + Vector3 src_max = _position + _size; + Vector3 dst_min = with._position; + Vector3 dst_max = with._position + with._size; + + Vector3 min, max; + + if (src_min.x > dst_max.x || src_max.x < dst_min.x) + { + return new AABB(); + } + + min.x = src_min.x > dst_min.x ? src_min.x : dst_min.x; + max.x = src_max.x < dst_max.x ? src_max.x : dst_max.x; + + if (src_min.y > dst_max.y || src_max.y < dst_min.y) + { + return new AABB(); + } + + min.y = src_min.y > dst_min.y ? src_min.y : dst_min.y; + max.y = src_max.y < dst_max.y ? src_max.y : dst_max.y; + + if (src_min.z > dst_max.z || src_max.z < dst_min.z) + { + return new AABB(); + } + + min.z = src_min.z > dst_min.z ? src_min.z : dst_min.z; + max.z = src_max.z < dst_max.z ? src_max.z : dst_max.z; + + return new AABB(min, max - min); + } + + public bool Intersects(AABB with) + { + 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; + } + + public bool IntersectsPlane(Plane plane) + { + Vector3[] points = + { + new Vector3(_position.x, _position.y, _position.z), + new Vector3(_position.x, _position.y, _position.z + _size.z), + new Vector3(_position.x, _position.y + _size.y, _position.z), + new Vector3(_position.x, _position.y + _size.y, _position.z + _size.z), + new Vector3(_position.x + _size.x, _position.y, _position.z), + new Vector3(_position.x + _size.x, _position.y, _position.z + _size.z), + new Vector3(_position.x + _size.x, _position.y + _size.y, _position.z), + new Vector3(_position.x + _size.x, _position.y + _size.y, _position.z + _size.z) + }; + + bool over = false; + bool under = false; + + for (int i = 0; i < 8; i++) + { + if (plane.DistanceTo(points[i]) > 0) + over = true; + else + under = true; + } + + return under && over; + } + + public bool IntersectsSegment(Vector3 from, Vector3 to) + { + real_t min = 0f; + real_t max = 1f; + + for (int i = 0; i < 3; i++) + { + real_t segFrom = from[i]; + real_t segTo = to[i]; + real_t boxBegin = _position[i]; + real_t boxEnd = boxBegin + _size[i]; + real_t cmin, cmax; + + if (segFrom < segTo) + { + if (segFrom > boxEnd || segTo < boxBegin) + return false; + + real_t length = segTo - segFrom; + cmin = segFrom < boxBegin ? (boxBegin - segFrom) / length : 0f; + cmax = segTo > boxEnd ? (boxEnd - segFrom) / length : 1f; + } + else + { + if (segTo > boxEnd || segFrom < boxBegin) + return false; + + real_t length = segTo - segFrom; + cmin = segFrom > boxEnd ? (boxEnd - segFrom) / length : 0f; + cmax = segTo < boxBegin ? (boxBegin - segFrom) / length : 1f; + } + + if (cmin > min) + { + min = cmin; + } + + if (cmax < max) + max = cmax; + if (max < min) + return false; + } + + return true; + } + + public AABB Merge(AABB with) + { + Vector3 beg1 = _position; + Vector3 beg2 = with._position; + var end1 = new Vector3(_size.x, _size.y, _size.z) + beg1; + var end2 = new Vector3(with._size.x, with._size.y, with._size.z) + beg2; + + var min = new Vector3( + beg1.x < beg2.x ? beg1.x : beg2.x, + beg1.y < beg2.y ? beg1.y : beg2.y, + beg1.z < beg2.z ? beg1.z : beg2.z + ); + + var max = new Vector3( + end1.x > end2.x ? end1.x : end2.x, + end1.y > end2.y ? end1.y : end2.y, + end1.z > end2.z ? end1.z : end2.z + ); + + return new AABB(min, max - min); + } + + // Constructors + public AABB(Vector3 position, Vector3 size) + { + _position = position; + _size = size; + } + + public static bool operator ==(AABB left, AABB right) + { + return left.Equals(right); + } + + public static bool operator !=(AABB left, AABB right) + { + return !left.Equals(right); + } + + public override bool Equals(object obj) + { + if (obj is AABB) + { + return Equals((AABB)obj); + } + + return false; + } + + public bool Equals(AABB other) + { + return _position == other._position && _size == other._size; + } + + public override int GetHashCode() + { + return _position.GetHashCode() ^ _size.GetHashCode(); + } + + public override string ToString() + { + return String.Format("{0} - {1}", new object[] + { + _position.ToString(), + _size.ToString() + }); + } + + public string ToString(string format) + { + return String.Format("{0} - {1}", new object[] + { + _position.ToString(format), + _size.ToString(format) + }); + } + } +} diff --git a/modules/mono/glue/Managed/Files/Array.cs b/modules/mono/glue/Managed/Files/Array.cs new file mode 100644 index 0000000000..c80cb7cc83 --- /dev/null +++ b/modules/mono/glue/Managed/Files/Array.cs @@ -0,0 +1,335 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Godot.Collections +{ + class ArraySafeHandle : SafeHandle + { + public ArraySafeHandle(IntPtr handle) : base(IntPtr.Zero, true) + { + this.handle = handle; + } + + public override bool IsInvalid + { + get + { + return handle == IntPtr.Zero; + } + } + + protected override bool ReleaseHandle() + { + Array.godot_icall_Array_Dtor(handle); + return true; + } + } + + public class Array : IList<object>, ICollection<object>, IEnumerable<object>, IDisposable + { + ArraySafeHandle safeHandle; + bool disposed = false; + + public Array() + { + safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor()); + } + + internal Array(ArraySafeHandle handle) + { + safeHandle = handle; + } + + internal Array(IntPtr handle) + { + safeHandle = new ArraySafeHandle(handle); + } + + internal IntPtr GetPtr() + { + return safeHandle.DangerousGetHandle(); + } + + public void Dispose() + { + if (disposed) + return; + + if (safeHandle != null) + { + safeHandle.Dispose(); + safeHandle = null; + } + + disposed = true; + } + + public object this[int index] + { + get + { + return godot_icall_Array_At(GetPtr(), index); + } + set + { + godot_icall_Array_SetAt(GetPtr(), index, value); + } + } + + public int Count + { + get + { + return godot_icall_Array_Count(GetPtr()); + } + } + + public bool IsReadOnly + { + get + { + return false; + } + } + + public void Add(object item) + { + godot_icall_Array_Add(GetPtr(), item); + } + + public void Clear() + { + godot_icall_Array_Clear(GetPtr()); + } + + public bool Contains(object item) + { + return godot_icall_Array_Contains(GetPtr(), item); + } + + public void CopyTo(object[] array, int arrayIndex) + { + if (array == null) + throw new ArgumentNullException(nameof(array), "Value cannot be null."); + + if (arrayIndex < 0) + throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension."); + + // Internal call may throw ArgumentException + godot_icall_Array_CopyTo(GetPtr(), array, arrayIndex); + } + + public IEnumerator<object> GetEnumerator() + { + int count = Count; + + for (int i = 0; i < count; i++) + { + yield return godot_icall_Array_At(GetPtr(), i); + } + } + + public int IndexOf(object item) + { + return godot_icall_Array_IndexOf(GetPtr(), item); + } + + public void Insert(int index, object item) + { + godot_icall_Array_Insert(GetPtr(), index, item); + } + + public bool Remove(object item) + { + return godot_icall_Array_Remove(GetPtr(), item); + } + + public void RemoveAt(int index) + { + godot_icall_Array_RemoveAt(GetPtr(), index); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Array_Ctor(); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_Dtor(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static object godot_icall_Array_At(IntPtr ptr, int index); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_SetAt(IntPtr ptr, int index, object value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_Array_Count(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_Add(IntPtr ptr, object item); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_Clear(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Array_Contains(IntPtr ptr, object item); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_CopyTo(IntPtr ptr, object[] array, int arrayIndex); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_Array_IndexOf(IntPtr ptr, object item); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_Insert(IntPtr ptr, int index, object item); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Array_Remove(IntPtr ptr, object item); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_RemoveAt(IntPtr ptr, int index); + } + + public class Array<T> : IList<T>, ICollection<T>, IEnumerable<T> + { + Array objectArray; + + public Array() + { + objectArray = new Array(); + } + + public Array(Array array) + { + objectArray = array; + } + + internal Array(IntPtr handle) + { + objectArray = new Array(handle); + } + + internal Array(ArraySafeHandle handle) + { + objectArray = new Array(handle); + } + + public static explicit operator Array(Array<T> from) + { + return from.objectArray; + } + + public T this[int index] + { + get + { + return (T)objectArray[index]; + } + set + { + objectArray[index] = value; + } + } + + public int Count + { + get + { + return objectArray.Count; + } + } + + public bool IsReadOnly + { + get + { + return objectArray.IsReadOnly; + } + } + + public void Add(T item) + { + objectArray.Add(item); + } + + public void Clear() + { + objectArray.Clear(); + } + + public bool Contains(T item) + { + return objectArray.Contains(item); + } + + public void CopyTo(T[] array, int arrayIndex) + { + if (array == null) + throw new ArgumentNullException(nameof(array), "Value cannot be null."); + + if (arrayIndex < 0) + throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension."); + + // TODO This may be quite slow because every element access is an internal call. + // It could be moved entirely to an internal call if we find out how to do the cast there. + + int count = objectArray.Count; + + if (array.Length < (arrayIndex + count)) + throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); + + for (int i = 0; i < count; i++) + { + array[arrayIndex] = (T)objectArray[i]; + arrayIndex++; + } + } + + public IEnumerator<T> GetEnumerator() + { + int count = objectArray.Count; + + for (int i = 0; i < count; i++) + { + yield return (T)objectArray[i]; + } + } + + public int IndexOf(T item) + { + return objectArray.IndexOf(item); + } + + public void Insert(int index, T item) + { + objectArray.Insert(index, item); + } + + public bool Remove(T item) + { + return objectArray.Remove(item); + } + + public void RemoveAt(int index) + { + objectArray.RemoveAt(index); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + internal IntPtr GetPtr() + { + return objectArray.GetPtr(); + } + } +} diff --git a/modules/mono/glue/Managed/Files/Attributes/ExportAttribute.cs b/modules/mono/glue/Managed/Files/Attributes/ExportAttribute.cs new file mode 100644 index 0000000000..6adf044886 --- /dev/null +++ b/modules/mono/glue/Managed/Files/Attributes/ExportAttribute.cs @@ -0,0 +1,17 @@ +using System; + +namespace Godot +{ + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] + public class ExportAttribute : Attribute + { + private PropertyHint hint; + private string hintString; + + public ExportAttribute(PropertyHint hint = PropertyHint.None, string hintString = "") + { + this.hint = hint; + this.hintString = hintString; + } + } +} diff --git a/modules/mono/glue/Managed/Files/Attributes/GodotMethodAttribute.cs b/modules/mono/glue/Managed/Files/Attributes/GodotMethodAttribute.cs new file mode 100644 index 0000000000..55848769d5 --- /dev/null +++ b/modules/mono/glue/Managed/Files/Attributes/GodotMethodAttribute.cs @@ -0,0 +1,17 @@ +using System; + +namespace Godot +{ + [AttributeUsage(AttributeTargets.Method)] + internal class GodotMethodAttribute : Attribute + { + private string methodName; + + public string MethodName { get { return methodName; } } + + public GodotMethodAttribute(string methodName) + { + this.methodName = methodName; + } + } +} diff --git a/modules/mono/glue/Managed/Files/Attributes/RPCAttributes.cs b/modules/mono/glue/Managed/Files/Attributes/RPCAttributes.cs new file mode 100644 index 0000000000..6bf9560bfa --- /dev/null +++ b/modules/mono/glue/Managed/Files/Attributes/RPCAttributes.cs @@ -0,0 +1,25 @@ +using System; + +namespace Godot +{ + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] + public class RemoteAttribute : Attribute {} + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] + public class SyncAttribute : Attribute {} + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] + public class MasterAttribute : Attribute {} + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] + public class SlaveAttribute : Attribute {} + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] + public class RemoteSyncAttribute : Attribute {} + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] + public class MasterSyncAttribute : Attribute {} + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)] + public class SlaveSyncAttribute : Attribute {} +} diff --git a/modules/mono/glue/Managed/Files/Attributes/SignalAttribute.cs b/modules/mono/glue/Managed/Files/Attributes/SignalAttribute.cs new file mode 100644 index 0000000000..3957387be9 --- /dev/null +++ b/modules/mono/glue/Managed/Files/Attributes/SignalAttribute.cs @@ -0,0 +1,9 @@ +using System; + +namespace Godot +{ + [AttributeUsage(AttributeTargets.Delegate)] + public class SignalAttribute : Attribute + { + } +} diff --git a/modules/mono/glue/Managed/Files/Attributes/ToolAttribute.cs b/modules/mono/glue/Managed/Files/Attributes/ToolAttribute.cs new file mode 100644 index 0000000000..d0437409af --- /dev/null +++ b/modules/mono/glue/Managed/Files/Attributes/ToolAttribute.cs @@ -0,0 +1,7 @@ +using System; + +namespace Godot +{ + [AttributeUsage(AttributeTargets.Class)] + public class ToolAttribute : Attribute {} +} diff --git a/modules/mono/glue/Managed/Files/Basis.cs b/modules/mono/glue/Managed/Files/Basis.cs new file mode 100644 index 0000000000..ec96a9e2fa --- /dev/null +++ b/modules/mono/glue/Managed/Files/Basis.cs @@ -0,0 +1,572 @@ +using System; +using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + +namespace Godot +{ + [StructLayout(LayoutKind.Sequential)] + public struct Basis : IEquatable<Basis> + { + private static readonly Basis identity = new Basis + ( + new Vector3(1f, 0f, 0f), + new Vector3(0f, 1f, 0f), + new Vector3(0f, 0f, 1f) + ); + + private static readonly Basis[] orthoBases = { + new Basis(1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f), + new Basis(0f, -1f, 0f, 1f, 0f, 0f, 0f, 0f, 1f), + new Basis(-1f, 0f, 0f, 0f, -1f, 0f, 0f, 0f, 1f), + new Basis(0f, 1f, 0f, -1f, 0f, 0f, 0f, 0f, 1f), + new Basis(1f, 0f, 0f, 0f, 0f, -1f, 0f, 1f, 0f), + new Basis(0f, 0f, 1f, 1f, 0f, 0f, 0f, 1f, 0f), + new Basis(-1f, 0f, 0f, 0f, 0f, 1f, 0f, 1f, 0f), + new Basis(0f, 0f, -1f, -1f, 0f, 0f, 0f, 1f, 0f), + new Basis(1f, 0f, 0f, 0f, -1f, 0f, 0f, 0f, -1f), + new Basis(0f, 1f, 0f, 1f, 0f, 0f, 0f, 0f, -1f), + new Basis(-1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, -1f), + new Basis(0f, -1f, 0f, -1f, 0f, 0f, 0f, 0f, -1f), + new Basis(1f, 0f, 0f, 0f, 0f, 1f, 0f, -1f, 0f), + new Basis(0f, 0f, -1f, 1f, 0f, 0f, 0f, -1f, 0f), + new Basis(-1f, 0f, 0f, 0f, 0f, -1f, 0f, -1f, 0f), + new Basis(0f, 0f, 1f, -1f, 0f, 0f, 0f, -1f, 0f), + new Basis(0f, 0f, 1f, 0f, 1f, 0f, -1f, 0f, 0f), + new Basis(0f, -1f, 0f, 0f, 0f, 1f, -1f, 0f, 0f), + new Basis(0f, 0f, -1f, 0f, -1f, 0f, -1f, 0f, 0f), + new Basis(0f, 1f, 0f, 0f, 0f, -1f, -1f, 0f, 0f), + new Basis(0f, 0f, 1f, 0f, -1f, 0f, 1f, 0f, 0f), + new Basis(0f, 1f, 0f, 0f, 0f, 1f, 1f, 0f, 0f), + new Basis(0f, 0f, -1f, 0f, 1f, 0f, 1f, 0f, 0f), + new Basis(0f, -1f, 0f, 0f, 0f, -1f, 1f, 0f, 0f) + }; + + public Vector3 x + { + get { return GetAxis(0); } + set { SetAxis(0, value); } + } + + public Vector3 y + { + get { return GetAxis(1); } + set { SetAxis(1, value); } + } + + public Vector3 z + { + get { return GetAxis(2); } + set { SetAxis(2, value); } + } + + private Vector3 _x; + private Vector3 _y; + private Vector3 _z; + + public static Basis Identity + { + get { return identity; } + } + + public Vector3 Scale + { + get + { + return new Vector3 + ( + new Vector3(this[0, 0], this[1, 0], this[2, 0]).Length(), + new Vector3(this[0, 1], this[1, 1], this[2, 1]).Length(), + new Vector3(this[0, 2], this[1, 2], this[2, 2]).Length() + ); + } + } + + public Vector3 this[int index] + { + get + { + switch (index) + { + case 0: + return _x; + case 1: + return _y; + case 2: + return _z; + default: + throw new IndexOutOfRangeException(); + } + } + set + { + switch (index) + { + case 0: + _x = value; + return; + case 1: + _y = value; + return; + case 2: + _z = value; + return; + default: + throw new IndexOutOfRangeException(); + } + } + } + + public real_t this[int index, int axis] + { + get + { + switch (index) + { + case 0: + return _x[axis]; + case 1: + return _y[axis]; + case 2: + return _z[axis]; + default: + throw new IndexOutOfRangeException(); + } + } + set + { + switch (index) + { + case 0: + _x[axis] = value; + return; + case 1: + _y[axis] = value; + return; + case 2: + _z[axis] = value; + return; + default: + throw new IndexOutOfRangeException(); + } + } + } + + internal static Basis CreateFromAxes(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis) + { + return new Basis + ( + new Vector3(xAxis.x, yAxis.x, zAxis.x), + new Vector3(xAxis.y, yAxis.y, zAxis.y), + new Vector3(xAxis.z, yAxis.z, zAxis.z) + ); + } + + public real_t Determinant() + { + return this[0, 0] * (this[1, 1] * this[2, 2] - this[2, 1] * this[1, 2]) - + this[1, 0] * (this[0, 1] * this[2, 2] - this[2, 1] * this[0, 2]) + + this[2, 0] * (this[0, 1] * this[1, 2] - this[1, 1] * this[0, 2]); + } + + public Vector3 GetAxis(int axis) + { + return new Vector3(this[0, axis], this[1, axis], this[2, axis]); + } + + public void SetAxis(int axis, Vector3 value) + { + this[0, axis] = value.x; + this[1, axis] = value.y; + this[2, axis] = value.z; + } + + public Vector3 GetEuler() + { + Basis m = Orthonormalized(); + + Vector3 euler; + euler.z = 0.0f; + + real_t mxy = m[1, 2]; + + + if (mxy < 1.0f) + { + if (mxy > -1.0f) + { + euler.x = Mathf.Asin(-mxy); + euler.y = Mathf.Atan2(m[0, 2], m[2, 2]); + euler.z = Mathf.Atan2(m[1, 0], m[1, 1]); + } + else + { + euler.x = Mathf.Pi * 0.5f; + euler.y = -Mathf.Atan2(-m[0, 1], m[0, 0]); + } + } + else + { + euler.x = -Mathf.Pi * 0.5f; + euler.y = -Mathf.Atan2(-m[0, 1], m[0, 0]); + } + + return euler; + } + + public int GetOrthogonalIndex() + { + var orth = this; + + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + { + real_t v = orth[i, j]; + + if (v > 0.5f) + v = 1.0f; + else if (v < -0.5f) + v = -1.0f; + else + v = 0f; + + orth[i, j] = v; + } + } + + for (int i = 0; i < 24; i++) + { + if (orthoBases[i] == orth) + return i; + } + + return 0; + } + + public Basis Inverse() + { + var inv = this; + + real_t[] co = { + inv[1, 1] * inv[2, 2] - inv[1, 2] * inv[2, 1], + inv[1, 2] * inv[2, 0] - inv[1, 0] * inv[2, 2], + inv[1, 0] * inv[2, 1] - inv[1, 1] * inv[2, 0] + }; + + real_t det = inv[0, 0] * co[0] + inv[0, 1] * co[1] + inv[0, 2] * co[2]; + + if (det == 0) + { + return new Basis + ( + real_t.NaN, real_t.NaN, real_t.NaN, + real_t.NaN, real_t.NaN, real_t.NaN, + real_t.NaN, real_t.NaN, real_t.NaN + ); + } + + real_t s = 1.0f / det; + + inv = new Basis + ( + co[0] * s, + inv[0, 2] * inv[2, 1] - inv[0, 1] * inv[2, 2] * s, + inv[0, 1] * inv[1, 2] - inv[0, 2] * inv[1, 1] * s, + co[1] * s, + inv[0, 0] * inv[2, 2] - inv[0, 2] * inv[2, 0] * s, + inv[0, 2] * inv[1, 0] - inv[0, 0] * inv[1, 2] * s, + co[2] * s, + inv[0, 1] * inv[2, 0] - inv[0, 0] * inv[2, 1] * s, + inv[0, 0] * inv[1, 1] - inv[0, 1] * inv[1, 0] * s + ); + + return inv; + } + + public Basis Orthonormalized() + { + Vector3 xAxis = GetAxis(0); + Vector3 yAxis = GetAxis(1); + Vector3 zAxis = GetAxis(2); + + xAxis.Normalize(); + yAxis = yAxis - xAxis * xAxis.Dot(yAxis); + yAxis.Normalize(); + zAxis = zAxis - xAxis * xAxis.Dot(zAxis) - yAxis * yAxis.Dot(zAxis); + zAxis.Normalize(); + + return CreateFromAxes(xAxis, yAxis, zAxis); + } + + public Basis Rotated(Vector3 axis, real_t phi) + { + return new Basis(axis, phi) * this; + } + + public Basis Scaled(Vector3 scale) + { + var m = this; + + m[0, 0] *= scale.x; + m[0, 1] *= scale.x; + m[0, 2] *= scale.x; + m[1, 0] *= scale.y; + m[1, 1] *= scale.y; + m[1, 2] *= scale.y; + m[2, 0] *= scale.z; + m[2, 1] *= scale.z; + m[2, 2] *= scale.z; + + return m; + } + + public real_t Tdotx(Vector3 with) + { + return this[0, 0] * with[0] + this[1, 0] * with[1] + this[2, 0] * with[2]; + } + + public real_t Tdoty(Vector3 with) + { + return this[0, 1] * with[0] + this[1, 1] * with[1] + this[2, 1] * with[2]; + } + + public real_t Tdotz(Vector3 with) + { + return this[0, 2] * with[0] + this[1, 2] * with[1] + this[2, 2] * with[2]; + } + + public Basis Transposed() + { + var tr = this; + + real_t temp = tr[0, 1]; + tr[0, 1] = tr[1, 0]; + tr[1, 0] = temp; + + temp = tr[0, 2]; + tr[0, 2] = tr[2, 0]; + tr[2, 0] = temp; + + temp = tr[1, 2]; + tr[1, 2] = tr[2, 1]; + tr[2, 1] = temp; + + return tr; + } + + public Vector3 Xform(Vector3 v) + { + return new Vector3 + ( + this[0].Dot(v), + this[1].Dot(v), + this[2].Dot(v) + ); + } + + public Vector3 XformInv(Vector3 v) + { + return new Vector3 + ( + this[0, 0] * v.x + this[1, 0] * v.y + this[2, 0] * v.z, + this[0, 1] * v.x + this[1, 1] * v.y + this[2, 1] * v.z, + this[0, 2] * v.x + this[1, 2] * v.y + this[2, 2] * v.z + ); + } + + public Quat Quat() { + real_t trace = _x[0] + _y[1] + _z[2]; + + if (trace > 0.0f) { + real_t s = Mathf.Sqrt(trace + 1.0f) * 2f; + real_t inv_s = 1f / s; + return new Quat( + (_z[1] - _y[2]) * inv_s, + (_x[2] - _z[0]) * inv_s, + (_y[0] - _x[1]) * inv_s, + s * 0.25f + ); + } + + if (_x[0] > _y[1] && _x[0] > _z[2]) { + real_t s = Mathf.Sqrt(_x[0] - _y[1] - _z[2] + 1.0f) * 2f; + real_t inv_s = 1f / s; + return new Quat( + s * 0.25f, + (_x[1] + _y[0]) * inv_s, + (_x[2] + _z[0]) * inv_s, + (_z[1] - _y[2]) * inv_s + ); + } + + if (_y[1] > _z[2]) { + real_t s = Mathf.Sqrt(-_x[0] + _y[1] - _z[2] + 1.0f) * 2f; + real_t inv_s = 1f / s; + return new Quat( + (_x[1] + _y[0]) * inv_s, + s * 0.25f, + (_y[2] + _z[1]) * inv_s, + (_x[2] - _z[0]) * inv_s + ); + } else { + real_t s = Mathf.Sqrt(-_x[0] - _y[1] + _z[2] + 1.0f) * 2f; + real_t inv_s = 1f / s; + return new Quat( + (_x[2] + _z[0]) * inv_s, + (_y[2] + _z[1]) * inv_s, + s * 0.25f, + (_y[0] - _x[1]) * inv_s + ); + } + } + + public Basis(Quat quat) + { + real_t s = 2.0f / quat.LengthSquared; + + real_t xs = quat.x * s; + real_t ys = quat.y * s; + real_t zs = quat.z * s; + real_t wx = quat.w * xs; + real_t wy = quat.w * ys; + real_t wz = quat.w * zs; + real_t xx = quat.x * xs; + real_t xy = quat.x * ys; + real_t xz = quat.x * zs; + real_t yy = quat.y * ys; + real_t yz = quat.y * zs; + real_t zz = quat.z * zs; + + _x = new Vector3(1.0f - (yy + zz), xy - wz, xz + wy); + _y = new Vector3(xy + wz, 1.0f - (xx + zz), yz - wx); + _z = new Vector3(xz - wy, yz + wx, 1.0f - (xx + yy)); + } + + public Basis(Vector3 euler) + { + real_t c; + real_t s; + + c = Mathf.Cos(euler.x); + s = Mathf.Sin(euler.x); + var xmat = new Basis(1, 0, 0, 0, c, -s, 0, s, c); + + c = Mathf.Cos(euler.y); + s = Mathf.Sin(euler.y); + var ymat = new Basis(c, 0, s, 0, 1, 0, -s, 0, c); + + c = Mathf.Cos(euler.z); + s = Mathf.Sin(euler.z); + var zmat = new Basis(c, -s, 0, s, c, 0, 0, 0, 1); + + this = ymat * xmat * zmat; + } + + public Basis(Vector3 axis, real_t phi) + { + var axis_sq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z); + + real_t cosine = Mathf.Cos( phi); + real_t sine = Mathf.Sin( phi); + + _x = new Vector3 + ( + axis_sq.x + cosine * (1.0f - axis_sq.x), + axis.x * axis.y * (1.0f - cosine) - axis.z * sine, + axis.z * axis.x * (1.0f - cosine) + axis.y * sine + ); + + _y = new Vector3 + ( + axis.x * axis.y * (1.0f - cosine) + axis.z * sine, + axis_sq.y + cosine * (1.0f - axis_sq.y), + axis.y * axis.z * (1.0f - cosine) - axis.x * sine + ); + + _z = new Vector3 + ( + axis.z * axis.x * (1.0f - cosine) - axis.y * sine, + axis.y * axis.z * (1.0f - cosine) + axis.x * sine, + axis_sq.z + cosine * (1.0f - axis_sq.z) + ); + } + + public Basis(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis) + { + _x = xAxis; + _y = yAxis; + _z = zAxis; + } + + public Basis(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz) + { + _x = new Vector3(xx, xy, xz); + _y = new Vector3(yx, yy, yz); + _z = new Vector3(zx, zy, zz); + } + + public static Basis operator *(Basis left, Basis right) + { + return new Basis + ( + right.Tdotx(left[0]), right.Tdoty(left[0]), right.Tdotz(left[0]), + right.Tdotx(left[1]), right.Tdoty(left[1]), right.Tdotz(left[1]), + right.Tdotx(left[2]), right.Tdoty(left[2]), right.Tdotz(left[2]) + ); + } + + public static bool operator ==(Basis left, Basis right) + { + return left.Equals(right); + } + + public static bool operator !=(Basis left, Basis right) + { + return !left.Equals(right); + } + + public override bool Equals(object obj) + { + if (obj is Basis) + { + return Equals((Basis)obj); + } + + return false; + } + + public bool Equals(Basis other) + { + return _x.Equals(other[0]) && _y.Equals(other[1]) && _z.Equals(other[2]); + } + + public override int GetHashCode() + { + return _x.GetHashCode() ^ _y.GetHashCode() ^ _z.GetHashCode(); + } + + public override string ToString() + { + return String.Format("({0}, {1}, {2})", new object[] + { + _x.ToString(), + _y.ToString(), + _z.ToString() + }); + } + + public string ToString(string format) + { + return String.Format("({0}, {1}, {2})", new object[] + { + _x.ToString(format), + _y.ToString(format), + _z.ToString(format) + }); + } + } +} diff --git a/modules/mono/glue/Managed/Files/Color.cs b/modules/mono/glue/Managed/Files/Color.cs new file mode 100644 index 0000000000..88cb8524b8 --- /dev/null +++ b/modules/mono/glue/Managed/Files/Color.cs @@ -0,0 +1,647 @@ +using System; + +namespace Godot +{ + public struct Color : IEquatable<Color> + { + public float r; + public float g; + public float b; + public float a; + + public int r8 + { + get + { + return (int)(r * 255.0f); + } + } + + public int g8 + { + get + { + return (int)(g * 255.0f); + } + } + + public int b8 + { + get + { + return (int)(b * 255.0f); + } + } + + public int a8 + { + get + { + return (int)(a * 255.0f); + } + } + + public float h + { + get + { + float max = Math.Max(r, Math.Max(g, b)); + float min = Math.Min(r, Math.Min(g, b)); + + 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; + } + set + { + this = FromHsv(value, s, v); + } + } + + public float s + { + get + { + float max = Math.Max(r, Math.Max(g, b)); + float min = Math.Min(r, Math.Min(g, b)); + + float delta = max - min; + + return max != 0 ? delta / max : 0; + } + set + { + this = FromHsv(h, value, v); + } + } + + public float v + { + get + { + return Math.Max(r, Math.Max(g, b)); + } + set + { + this = FromHsv(h, s, value); + } + } + + private static readonly Color black = new Color(0f, 0f, 0f); + + public Color Black + { + get + { + return black; + } + } + + public float this [int index] + { + get + { + switch (index) + { + case 0: + return r; + case 1: + return g; + case 2: + return b; + case 3: + return a; + default: + throw new IndexOutOfRangeException(); + } + } + set + { + switch (index) + { + case 0: + r = value; + return; + case 1: + g = value; + return; + case 2: + b = value; + return; + case 3: + a = value; + return; + default: + throw new IndexOutOfRangeException(); + } + } + } + + public static void ToHsv(Color color, out float hue, out float saturation, out float value) + { + int max = Mathf.Max(color.r8, Mathf.Max(color.g8, color.b8)); + int min = Mathf.Min(color.r8, Mathf.Min(color.g8, color.b8)); + + float delta = max - min; + + if (delta == 0) + { + hue = 0; + } + else + { + if (color.r == max) + hue = (color.g - color.b) / delta; // Between yellow & magenta + else if (color.g == max) + hue = 2 + (color.b - color.r) / delta; // Between cyan & yellow + else + hue = 4 + (color.r - color.g) / delta; // Between magenta & cyan + + hue /= 6.0f; + + if (hue < 0) + hue += 1.0f; + } + + saturation = max == 0 ? 0 : 1f - 1f * min / max; + value = max / 255f; + } + + public static Color FromHsv(float hue, float saturation, float value, float alpha = 1.0f) + { + if (saturation == 0) + { + // acp_hromatic (grey) + return new Color(value, value, value, alpha); + } + + int i; + float f, p, q, t; + + hue *= 6.0f; + hue %= 6f; + i = (int)hue; + + f = hue - i; + p = value * (1 - saturation); + q = value * (1 - saturation * f); + t = value * (1 - saturation * (1 - f)); + + switch (i) + { + case 0: // Red is the dominant color + return new Color(value, t, p, alpha); + case 1: // Green is the dominant color + return new Color(q, value, p, alpha); + case 2: + return new Color(p, value, t, alpha); + case 3: // Blue is the dominant color + return new Color(p, q, value, alpha); + case 4: + return new Color(t, p, value, alpha); + default: // (5) Red is the dominant color + return new Color(value, p, q, alpha); + } + } + + public Color Blend(Color over) + { + Color res; + + float sa = 1.0f - over.a; + res.a = a * sa + over.a; + + if (res.a == 0) + { + return new Color(0, 0, 0, 0); + } + + res.r = (r * a * sa + over.r * over.a) / res.a; + res.g = (g * a * sa + over.g * over.a) / res.a; + res.b = (b * a * sa + over.b * over.a) / res.a; + + return res; + } + + public Color Contrasted() + { + return new Color( + (r + 0.5f) % 1.0f, + (g + 0.5f) % 1.0f, + (b + 0.5f) % 1.0f + ); + } + + public Color Darkened(float amount) + { + Color res = this; + res.r = res.r * (1.0f - amount); + res.g = res.g * (1.0f - amount); + res.b = res.b * (1.0f - amount); + return res; + } + + public Color Inverted() + { + return new Color( + 1.0f - r, + 1.0f - g, + 1.0f - b + ); + } + + public Color Lightened(float amount) + { + Color res = this; + res.r = res.r + (1.0f - res.r) * amount; + res.g = res.g + (1.0f - res.g) * amount; + res.b = res.b + (1.0f - res.b) * amount; + return res; + } + + public Color LinearInterpolate(Color c, float t) + { + 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; + } + + public int ToAbgr32() + { + int c = (byte)Math.Round(a * 255); + c <<= 8; + c |= (byte)Math.Round(b * 255); + c <<= 8; + c |= (byte)Math.Round(g * 255); + c <<= 8; + c |= (byte)Math.Round(r * 255); + + return c; + } + + public long ToAbgr64() + { + long c = (ushort)Math.Round(a * 65535); + c <<= 16; + c |= (ushort)Math.Round(b * 65535); + c <<= 16; + c |= (ushort)Math.Round(g * 65535); + c <<= 16; + c |= (ushort)Math.Round(r * 65535); + + return c; + } + + public int ToArgb32() + { + int c = (byte)Math.Round(a * 255); + c <<= 8; + c |= (byte)Math.Round(r * 255); + c <<= 8; + c |= (byte)Math.Round(g * 255); + c <<= 8; + c |= (byte)Math.Round(b * 255); + + return c; + } + + public long ToArgb64() + { + long c = (ushort)Math.Round(a * 65535); + c <<= 16; + c |= (ushort)Math.Round(r * 65535); + c <<= 16; + c |= (ushort)Math.Round(g * 65535); + c <<= 16; + c |= (ushort)Math.Round(b * 65535); + + return c; + } + + public int ToRgba32() + { + int c = (byte)Math.Round(r * 255); + c <<= 8; + c |= (byte)Math.Round(g * 255); + c <<= 8; + c |= (byte)Math.Round(b * 255); + c <<= 8; + c |= (byte)Math.Round(a * 255); + + return c; + } + + public long ToRgba64() + { + long c = (ushort)Math.Round(r * 65535); + c <<= 16; + c |= (ushort)Math.Round(g * 65535); + c <<= 16; + c |= (ushort)Math.Round(b * 65535); + c <<= 16; + c |= (ushort)Math.Round(a * 65535); + + return c; + } + + public string ToHtml(bool include_alpha = true) + { + var txt = string.Empty; + + txt += ToHex32(r); + txt += ToHex32(g); + txt += ToHex32(b); + + if (include_alpha) + txt = ToHex32(a) + txt; + + return txt; + } + + // Constructors + public Color(float r, float g, float b, float a = 1.0f) + { + this.r = r; + this.g = g; + this.b = b; + this.a = a; + } + + public Color(int rgba) + { + a = (rgba & 0xFF) / 255.0f; + rgba >>= 8; + b = (rgba & 0xFF) / 255.0f; + rgba >>= 8; + g = (rgba & 0xFF) / 255.0f; + rgba >>= 8; + r = (rgba & 0xFF) / 255.0f; + } + + public Color(long rgba) + { + a = (rgba & 0xFFFF) / 65535.0f; + rgba >>= 16; + b = (rgba & 0xFFFF) / 65535.0f; + rgba >>= 16; + g = (rgba & 0xFFFF) / 65535.0f; + rgba >>= 16; + r = (rgba & 0xFFFF) / 65535.0f; + } + + private static int ParseCol8(string str, int ofs) + { + int ig = 0; + + for (int i = 0; i < 2; i++) + { + int c = str[i + ofs]; + int v; + + if (c >= '0' && c <= '9') + { + v = c - '0'; + } + else if (c >= 'a' && c <= 'f') + { + v = c - 'a'; + v += 10; + } + else if (c >= 'A' && c <= 'F') + { + v = c - 'A'; + v += 10; + } + else + { + return -1; + } + + if (i == 0) + ig += v * 16; + else + ig += v; + } + + return ig; + } + + private String ToHex32(float val) + { + int v = Mathf.RoundToInt(Mathf.Clamp(val * 255, 0, 255)); + + var ret = string.Empty; + + for (int i = 0; i < 2; i++) + { + char[] c = { (char)0, (char)0 }; + int lv = v & 0xF; + + if (lv < 10) + c[0] = (char)('0' + lv); + else + c[0] = (char)('a' + lv - 10); + + v >>= 4; + ret = c + ret; + } + + return ret; + } + + internal static bool HtmlIsValid(string color) + { + if (color.Length == 0) + return false; + + if (color[0] == '#') + color = color.Substring(1, color.Length - 1); + + bool alpha; + + if (color.Length == 8) + alpha = true; + else if (color.Length == 6) + alpha = false; + else + return false; + + if (alpha) + { + if (ParseCol8(color, 0) < 0) + return false; + } + + int from = alpha ? 2 : 0; + + if (ParseCol8(color, from + 0) < 0) + return false; + if (ParseCol8(color, from + 2) < 0) + return false; + if (ParseCol8(color, from + 4) < 0) + return false; + + return true; + } + + public static Color Color8(byte r8, byte g8, byte b8, byte a8) + { + return new Color(r8 / 255f, g8 / 255f, b8 / 255f, a8 / 255f); + } + + public Color(string rgba) + { + if (rgba.Length == 0) + { + r = 0f; + g = 0f; + b = 0f; + a = 1.0f; + return; + } + + if (rgba[0] == '#') + rgba = rgba.Substring(1); + + bool alpha; + + if (rgba.Length == 8) + { + alpha = true; + } + else if (rgba.Length == 6) + { + alpha = false; + } + else + { + throw new ArgumentOutOfRangeException("Invalid color code. Length is " + rgba.Length + " but a length of 6 or 8 is expected: " + rgba); + } + + if (alpha) + { + a = ParseCol8(rgba, 0) / 255f; + + if (a < 0) + throw new ArgumentOutOfRangeException("Invalid color code. Alpha part is not valid hexadecimal: " + rgba); + } + else + { + a = 1.0f; + } + + int from = alpha ? 2 : 0; + + r = ParseCol8(rgba, from + 0) / 255f; + + if (r < 0) + throw new ArgumentOutOfRangeException("Invalid color code. Red part is not valid hexadecimal: " + rgba); + + g = ParseCol8(rgba, from + 2) / 255f; + + if (g < 0) + throw new ArgumentOutOfRangeException("Invalid color code. Green part is not valid hexadecimal: " + rgba); + + b = ParseCol8(rgba, from + 4) / 255f; + + if (b < 0) + throw new ArgumentOutOfRangeException("Invalid color code. Blue part is not valid hexadecimal: " + rgba); + } + + public static bool operator ==(Color left, Color right) + { + return left.Equals(right); + } + + public static bool operator !=(Color left, Color right) + { + return !left.Equals(right); + } + + public static bool operator <(Color left, Color right) + { + if (left.r == right.r) + { + if (left.g == right.g) + { + if (left.b == right.b) + return left.a < right.a; + return left.b < right.b; + } + + return left.g < right.g; + } + + return left.r < right.r; + } + + public static bool operator >(Color left, Color right) + { + if (left.r == right.r) + { + if (left.g == right.g) + { + if (left.b == right.b) + return left.a > right.a; + return left.b > right.b; + } + + return left.g > right.g; + } + + return left.r > right.r; + } + + public override bool Equals(object obj) + { + if (obj is Color) + { + return Equals((Color)obj); + } + + return false; + } + + public bool Equals(Color other) + { + return r == other.r && g == other.g && b == other.b && a == other.a; + } + + public override int GetHashCode() + { + return r.GetHashCode() ^ g.GetHashCode() ^ b.GetHashCode() ^ a.GetHashCode(); + } + + public override string ToString() + { + return String.Format("{0},{1},{2},{3}", r.ToString(), g.ToString(), b.ToString(), a.ToString()); + } + + public string ToString(string format) + { + return String.Format("{0},{1},{2},{3}", r.ToString(format), g.ToString(format), b.ToString(format), a.ToString(format)); + } + } +} diff --git a/modules/mono/glue/Managed/Files/DebuggingUtils.cs b/modules/mono/glue/Managed/Files/DebuggingUtils.cs new file mode 100644 index 0000000000..b27816084e --- /dev/null +++ b/modules/mono/glue/Managed/Files/DebuggingUtils.cs @@ -0,0 +1,83 @@ +using System; +using System.Diagnostics; +using System.Reflection; +using System.Text; + +namespace Godot +{ + internal static class DebuggingUtils + { + internal static void AppendTypeName(this StringBuilder sb, Type type) + { + if (type.IsPrimitive) + sb.Append(type.Name); + else if (type == typeof(void)) + sb.Append("void"); + else + sb.Append(type); + + sb.Append(" "); + } + + public static void GetStackFrameInfo(StackFrame frame, out string fileName, out int fileLineNumber, out string methodDecl) + { + fileName = frame.GetFileName(); + fileLineNumber = frame.GetFileLineNumber(); + + MethodBase methodBase = frame.GetMethod(); + + if (methodBase == null) + { + methodDecl = string.Empty; + return; + } + + var sb = new StringBuilder(); + + if (methodBase is MethodInfo) + sb.AppendTypeName(((MethodInfo)methodBase).ReturnType); + + sb.Append(methodBase.DeclaringType.FullName); + sb.Append("."); + sb.Append(methodBase.Name); + + if (methodBase.IsGenericMethod) + { + Type[] genericParams = methodBase.GetGenericArguments(); + + sb.Append("<"); + + for (int j = 0; j < genericParams.Length; j++) + { + if (j > 0) + sb.Append(", "); + + sb.AppendTypeName(genericParams[j]); + } + + sb.Append(">"); + } + + sb.Append("("); + + bool varArgs = (methodBase.CallingConvention & CallingConventions.VarArgs) != 0; + + ParameterInfo[] parameter = methodBase.GetParameters(); + + for (int i = 0; i < parameter.Length; i++) + { + if (i > 0) + sb.Append(", "); + + if (i == parameter.Length - 1 && varArgs) + sb.Append("params "); + + sb.AppendTypeName(parameter[i].ParameterType); + } + + sb.Append(")"); + + methodDecl = sb.ToString(); + } + } +} diff --git a/modules/mono/glue/Managed/Files/Dictionary.cs b/modules/mono/glue/Managed/Files/Dictionary.cs new file mode 100644 index 0000000000..523e48c31a --- /dev/null +++ b/modules/mono/glue/Managed/Files/Dictionary.cs @@ -0,0 +1,400 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Godot.Collections +{ + class DictionarySafeHandle : SafeHandle + { + public DictionarySafeHandle(IntPtr handle) : base(IntPtr.Zero, true) + { + this.handle = handle; + } + + public override bool IsInvalid + { + get + { + return handle == IntPtr.Zero; + } + } + + protected override bool ReleaseHandle() + { + Dictionary.godot_icall_Dictionary_Dtor(handle); + return true; + } + } + + public class Dictionary : + IDictionary<object, object>, + ICollection<KeyValuePair<object, object>>, + IEnumerable<KeyValuePair<object, object>>, + IDisposable + { + DictionarySafeHandle safeHandle; + bool disposed = false; + + public Dictionary() + { + safeHandle = new DictionarySafeHandle(godot_icall_Dictionary_Ctor()); + } + + internal Dictionary(DictionarySafeHandle handle) + { + safeHandle = handle; + } + + internal Dictionary(IntPtr handle) + { + safeHandle = new DictionarySafeHandle(handle); + } + + internal IntPtr GetPtr() + { + return safeHandle.DangerousGetHandle(); + } + + public void Dispose() + { + if (disposed) + return; + + if (safeHandle != null) + { + safeHandle.Dispose(); + safeHandle = null; + } + + disposed = true; + } + + public object this[object key] + { + get + { + return godot_icall_Dictionary_GetValue(GetPtr(), key); + } + set + { + godot_icall_Dictionary_SetValue(GetPtr(), key, value); + } + } + + public ICollection<object> Keys + { + get + { + IntPtr handle = godot_icall_Dictionary_Keys(GetPtr()); + return new Array(new ArraySafeHandle(handle)); + } + } + + public ICollection<object> Values + { + get + { + IntPtr handle = godot_icall_Dictionary_Values(GetPtr()); + return new Array(new ArraySafeHandle(handle)); + } + } + + public int Count + { + get + { + return godot_icall_Dictionary_Count(GetPtr()); + } + } + + public bool IsReadOnly + { + get + { + return false; + } + } + + public void Add(object key, object value) + { + godot_icall_Dictionary_Add(GetPtr(), key, value); + } + + public void Add(KeyValuePair<object, object> item) + { + Add(item.Key, item.Value); + } + + public void Clear() + { + godot_icall_Dictionary_Clear(GetPtr()); + } + + public bool Contains(KeyValuePair<object, object> item) + { + return godot_icall_Dictionary_Contains(GetPtr(), item.Key, item.Value); + } + + public bool ContainsKey(object key) + { + return godot_icall_Dictionary_ContainsKey(GetPtr(), key); + } + + public void CopyTo(KeyValuePair<object, object>[] array, int arrayIndex) + { + // TODO 3 internal calls, can reduce to 1 + Array keys = (Array)Keys; + Array values = (Array)Values; + int count = Count; + + for (int i = 0; i < count; i++) + { + // TODO 2 internal calls, can reduce to 1 + array[arrayIndex] = new KeyValuePair<object, object>(keys[i], values[i]); + arrayIndex++; + } + } + + public IEnumerator<KeyValuePair<object, object>> GetEnumerator() + { + // TODO 3 internal calls, can reduce to 1 + Array keys = (Array)Keys; + Array values = (Array)Values; + int count = Count; + + for (int i = 0; i < count; i++) + { + // TODO 2 internal calls, can reduce to 1 + yield return new KeyValuePair<object, object>(keys[i], values[i]); + } + } + + public bool Remove(object key) + { + return godot_icall_Dictionary_RemoveKey(GetPtr(), key); + } + + public bool Remove(KeyValuePair<object, object> item) + { + return godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value); + } + + public bool TryGetValue(object key, out object value) + { + object retValue; + bool found = godot_icall_Dictionary_TryGetValue(GetPtr(), key, out retValue); + value = found ? retValue : default(object); + return found; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Dictionary_Ctor(); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Dictionary_Dtor(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static object godot_icall_Dictionary_GetValue(IntPtr ptr, object key); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Dictionary_SetValue(IntPtr ptr, object key, object value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Dictionary_Keys(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Dictionary_Values(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_Dictionary_Count(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Dictionary_Add(IntPtr ptr, object key, object value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Dictionary_Clear(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Dictionary_Contains(IntPtr ptr, object key, object value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Dictionary_ContainsKey(IntPtr ptr, object key); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Dictionary_RemoveKey(IntPtr ptr, object key); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Dictionary_Remove(IntPtr ptr, object key, object value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Dictionary_TryGetValue(IntPtr ptr, object key, out object value); + } + + public class Dictionary<TKey, TValue> : + IDictionary<TKey, TValue>, + ICollection<KeyValuePair<TKey, TValue>>, + IEnumerable<KeyValuePair<TKey, TValue>> + { + Dictionary objectDict; + + public Dictionary() + { + objectDict = new Dictionary(); + } + + public Dictionary(Dictionary dictionary) + { + objectDict = dictionary; + } + + internal Dictionary(IntPtr handle) + { + objectDict = new Dictionary(handle); + } + + internal Dictionary(DictionarySafeHandle handle) + { + objectDict = new Dictionary(handle); + } + + public static explicit operator Dictionary(Dictionary<TKey, TValue> from) + { + return from.objectDict; + } + + public TValue this[TKey key] + { + get + { + return (TValue)objectDict[key]; + } + set + { + objectDict[key] = value; + } + } + + public ICollection<TKey> Keys + { + get + { + IntPtr handle = Dictionary.godot_icall_Dictionary_Keys(objectDict.GetPtr()); + return new Array<TKey>(new ArraySafeHandle(handle)); + } + } + + public ICollection<TValue> Values + { + get + { + IntPtr handle = Dictionary.godot_icall_Dictionary_Values(objectDict.GetPtr()); + return new Array<TValue>(new ArraySafeHandle(handle)); + } + } + + public int Count + { + get + { + return objectDict.Count; + } + } + + public bool IsReadOnly + { + get + { + return objectDict.IsReadOnly; + } + } + + public void Add(TKey key, TValue value) + { + objectDict.Add(key, value); + } + + public void Add(KeyValuePair<TKey, TValue> item) + { + objectDict.Add(item.Key, item.Value); + } + + public void Clear() + { + objectDict.Clear(); + } + + public bool Contains(KeyValuePair<TKey, TValue> item) + { + return objectDict.Contains(new KeyValuePair<object, object>(item.Key, item.Value)); + } + + public bool ContainsKey(TKey key) + { + return objectDict.ContainsKey(key); + } + + public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) + { + // TODO 3 internal calls, can reduce to 1 + Array<TKey> keys = (Array<TKey>)Keys; + Array<TValue> values = (Array<TValue>)Values; + int count = Count; + + for (int i = 0; i < count; i++) + { + // TODO 2 internal calls, can reduce to 1 + array[arrayIndex] = new KeyValuePair<TKey, TValue>(keys[i], values[i]); + arrayIndex++; + } + } + + public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() + { + // TODO 3 internal calls, can reduce to 1 + Array<TKey> keys = (Array<TKey>)Keys; + Array<TValue> values = (Array<TValue>)Values; + int count = Count; + + for (int i = 0; i < count; i++) + { + // TODO 2 internal calls, can reduce to 1 + yield return new KeyValuePair<TKey, TValue>(keys[i], values[i]); + } + } + + public bool Remove(TKey key) + { + return objectDict.Remove(key); + } + + public bool Remove(KeyValuePair<TKey, TValue> item) + { + return objectDict.Remove(new KeyValuePair<object, object>(item.Key, item.Value)); + } + + public bool TryGetValue(TKey key, out TValue value) + { + object retValue; + bool found = objectDict.TryGetValue(key, out retValue); + value = found ? (TValue)retValue : default(TValue); + return found; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + internal IntPtr GetPtr() + { + return objectDict.GetPtr(); + } + } +} diff --git a/modules/mono/glue/Managed/Files/Extensions/NodeExtensions.cs b/modules/mono/glue/Managed/Files/Extensions/NodeExtensions.cs new file mode 100644 index 0000000000..71534d7782 --- /dev/null +++ b/modules/mono/glue/Managed/Files/Extensions/NodeExtensions.cs @@ -0,0 +1,45 @@ +namespace Godot +{ + public partial class Node + { + public T GetNode<T>(NodePath path) where T : Godot.Node + { + return (T)GetNode(path); + } + + public T GetNodeOrNull<T>(NodePath path) where T : Godot.Node + { + return GetNode(path) as T; + } + + public T GetChild<T>(int idx) where T : Godot.Node + { + return (T)GetChild(idx); + } + + public T GetChildOrNull<T>(int idx) where T : Godot.Node + { + return GetChild(idx) as T; + } + + public T GetOwner<T>() where T : Godot.Node + { + return (T)GetOwner(); + } + + public T GetOwnerOrNull<T>() where T : Godot.Node + { + return GetOwner() as T; + } + + public T GetParent<T>() where T : Godot.Node + { + return (T)GetParent(); + } + + public T GetParentOrNull<T>() where T : Godot.Node + { + return GetParent() as T; + } + } +} diff --git a/modules/mono/glue/Managed/Files/Extensions/ObjectExtensions.cs b/modules/mono/glue/Managed/Files/Extensions/ObjectExtensions.cs new file mode 100644 index 0000000000..9ef0959750 --- /dev/null +++ b/modules/mono/glue/Managed/Files/Extensions/ObjectExtensions.cs @@ -0,0 +1,21 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Godot +{ + public partial class Object + { + public static bool IsInstanceValid(Object instance) + { + return instance != null && instance.NativeInstance != IntPtr.Zero; + } + + public static WeakRef WeakRef(Object obj) + { + return godot_icall_Object_weakref(Object.GetPtr(obj)); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static WeakRef godot_icall_Object_weakref(IntPtr obj); + } +} diff --git a/modules/mono/glue/Managed/Files/Extensions/ResourceLoaderExtensions.cs b/modules/mono/glue/Managed/Files/Extensions/ResourceLoaderExtensions.cs new file mode 100644 index 0000000000..ceecc589e6 --- /dev/null +++ b/modules/mono/glue/Managed/Files/Extensions/ResourceLoaderExtensions.cs @@ -0,0 +1,10 @@ +namespace Godot +{ + public static partial class ResourceLoader + { + public static T Load<T>(string path) where T : Godot.Resource + { + return (T) Load(path); + } + } +} diff --git a/modules/mono/glue/Managed/Files/GD.cs b/modules/mono/glue/Managed/Files/GD.cs new file mode 100644 index 0000000000..264be23588 --- /dev/null +++ b/modules/mono/glue/Managed/Files/GD.cs @@ -0,0 +1,242 @@ +using System; +using System.Runtime.CompilerServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + +// TODO: Add comments describing what this class does. It is not obvious. + +namespace Godot +{ + public static partial class GD + { + public static object Bytes2Var(byte[] bytes) + { + return godot_icall_GD_bytes2var(bytes); + } + + public static object Convert(object what, int type) + { + return godot_icall_GD_convert(what, type); + } + + public static real_t Db2Linear(real_t db) + { + return (real_t)Math.Exp(db * 0.11512925464970228420089957273422); + } + + public static real_t DecTime(real_t value, real_t amount, real_t step) + { + real_t sgn = Mathf.Sign(value); + real_t val = Mathf.Abs(value); + val -= amount * step; + if (val < 0) + val = 0; + return val * sgn; + } + + public static FuncRef FuncRef(Object instance, string funcname) + { + var ret = new FuncRef(); + ret.SetInstance(instance); + ret.SetFunction(funcname); + return ret; + } + + public static int Hash(object var) + { + return godot_icall_GD_hash(var); + } + + public static Object InstanceFromId(int instanceId) + { + return godot_icall_GD_instance_from_id(instanceId); + } + + public static real_t Linear2Db(real_t linear) + { + return (real_t)(Math.Log(linear) * 8.6858896380650365530225783783321); + } + + public static Resource Load(string path) + { + return ResourceLoader.Load(path); + } + + public static T Load<T>(string path) where T : Godot.Resource + { + return (T) ResourceLoader.Load(path); + } + + public static void Print(params object[] what) + { + godot_icall_GD_print(what); + } + + public static void PrintStack() + { + Print(System.Environment.StackTrace); + } + + public static void PrintErr(params object[] what) + { + godot_icall_GD_printerr(what); + } + + public static void PrintRaw(params object[] what) + { + godot_icall_GD_printraw(what); + } + + public static void PrintS(params object[] what) + { + godot_icall_GD_prints(what); + } + + public static void PrintT(params object[] what) + { + godot_icall_GD_printt(what); + } + + public static int[] Range(int length) + { + var ret = new int[length]; + + for (int i = 0; i < length; i++) + { + ret[i] = i; + } + + return ret; + } + + public static int[] Range(int from, int to) + { + if (to < from) + return new int[0]; + + var ret = new int[to - from]; + + for (int i = from; i < to; i++) + { + ret[i - from] = i; + } + + return ret; + } + + public static int[] Range(int from, int to, int increment) + { + if (to < from && increment > 0) + return new int[0]; + if (to > from && increment < 0) + return new int[0]; + + // Calculate count + int count; + + if (increment > 0) + count = (to - from - 1) / increment + 1; + else + count = (from - to - 1) / -increment + 1; + + var ret = new int[count]; + + if (increment > 0) + { + int idx = 0; + for (int i = from; i < to; i += increment) + { + ret[idx++] = i; + } + } + else + { + int idx = 0; + for (int i = from; i > to; i += increment) + { + ret[idx++] = i; + } + } + + return ret; + } + + public static void Seed(int seed) + { + godot_icall_GD_seed(seed); + } + + public static string Str(params object[] what) + { + return godot_icall_GD_str(what); + } + + public static object Str2Var(string str) + { + return godot_icall_GD_str2var(str); + } + + public static bool TypeExists(string type) + { + return godot_icall_GD_type_exists(type); + } + + public static byte[] Var2Bytes(object var) + { + return godot_icall_GD_var2bytes(var); + } + + public static string Var2Str(object var) + { + return godot_icall_GD_var2str(var); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static object godot_icall_GD_bytes2var(byte[] bytes); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static object godot_icall_GD_convert(object what, int type); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_GD_hash(object var); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static Object godot_icall_GD_instance_from_id(int instance_id); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_GD_print(object[] what); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_GD_printerr(object[] what); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_GD_printraw(object[] what); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_GD_prints(object[] what); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_GD_printt(object[] what); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_GD_seed(int seed); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_GD_str(object[] what); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static object godot_icall_GD_str2var(string str); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_GD_type_exists(string type); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static byte[] godot_icall_GD_var2bytes(object what); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_GD_var2str(object var); + } +} diff --git a/modules/mono/glue/Managed/Files/GodotSynchronizationContext.cs b/modules/mono/glue/Managed/Files/GodotSynchronizationContext.cs new file mode 100644 index 0000000000..e727781d63 --- /dev/null +++ b/modules/mono/glue/Managed/Files/GodotSynchronizationContext.cs @@ -0,0 +1,25 @@ +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading; + +namespace Godot +{ + public class GodotSynchronizationContext : SynchronizationContext + { + private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> queue = new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>(); + + public override void Post(SendOrPostCallback d, object state) + { + queue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state)); + } + + public void ExecutePendingContinuations() + { + KeyValuePair<SendOrPostCallback, object> workItem; + while (queue.TryTake(out workItem)) + { + workItem.Key(workItem.Value); + } + } + } +} diff --git a/modules/mono/glue/Managed/Files/GodotTaskScheduler.cs b/modules/mono/glue/Managed/Files/GodotTaskScheduler.cs new file mode 100644 index 0000000000..9a40fef5a9 --- /dev/null +++ b/modules/mono/glue/Managed/Files/GodotTaskScheduler.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Godot +{ + public class GodotTaskScheduler : TaskScheduler + { + private GodotSynchronizationContext Context { get; set; } + private readonly LinkedList<Task> _tasks = new LinkedList<Task>(); + + public GodotTaskScheduler() + { + Context = new GodotSynchronizationContext(); + SynchronizationContext.SetSynchronizationContext(Context); + } + + protected sealed override void QueueTask(Task task) + { + lock (_tasks) + { + _tasks.AddLast(task); + } + } + + protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) + { + if (SynchronizationContext.Current != Context) + { + return false; + } + + if (taskWasPreviouslyQueued) + { + TryDequeue(task); + } + + return TryExecuteTask(task); + } + + protected sealed override bool TryDequeue(Task task) + { + lock (_tasks) + { + return _tasks.Remove(task); + } + } + + protected sealed override IEnumerable<Task> GetScheduledTasks() + { + lock (_tasks) + { + return _tasks.ToArray(); + } + } + + public void Activate() + { + ExecuteQueuedTasks(); + Context.ExecutePendingContinuations(); + } + + private void ExecuteQueuedTasks() + { + while (true) + { + Task task; + + lock (_tasks) + { + if (_tasks.Any()) + { + task = _tasks.First.Value; + _tasks.RemoveFirst(); + } + else + { + break; + } + } + + if (task != null) + { + if (!TryExecuteTask(task)) + { + throw new InvalidOperationException(); + } + } + } + } + } +} diff --git a/modules/mono/glue/Managed/Files/Interfaces/IAwaitable.cs b/modules/mono/glue/Managed/Files/Interfaces/IAwaitable.cs new file mode 100644 index 0000000000..0397957d00 --- /dev/null +++ b/modules/mono/glue/Managed/Files/Interfaces/IAwaitable.cs @@ -0,0 +1,12 @@ +namespace Godot +{ + public interface IAwaitable + { + IAwaiter GetAwaiter(); + } + + public interface IAwaitable<out TResult> + { + IAwaiter<TResult> GetAwaiter(); + } +} diff --git a/modules/mono/glue/Managed/Files/Interfaces/IAwaiter.cs b/modules/mono/glue/Managed/Files/Interfaces/IAwaiter.cs new file mode 100644 index 0000000000..d3be9d781c --- /dev/null +++ b/modules/mono/glue/Managed/Files/Interfaces/IAwaiter.cs @@ -0,0 +1,18 @@ +using System.Runtime.CompilerServices; + +namespace Godot +{ + public interface IAwaiter : INotifyCompletion + { + bool IsCompleted { get; } + + void GetResult(); + } + + public interface IAwaiter<out TResult> : INotifyCompletion + { + bool IsCompleted { get; } + + TResult GetResult(); + } +} diff --git a/modules/mono/glue/Managed/Files/MarshalUtils.cs b/modules/mono/glue/Managed/Files/MarshalUtils.cs new file mode 100644 index 0000000000..f7699a15bf --- /dev/null +++ b/modules/mono/glue/Managed/Files/MarshalUtils.cs @@ -0,0 +1,18 @@ +using System; +using Godot.Collections; + +namespace Godot +{ + static class MarshalUtils + { + static bool IsArrayGenericType(Type type) + { + return type.GetGenericTypeDefinition() == typeof(Array<>); + } + + static bool IsDictionaryGenericType(Type type) + { + return type.GetGenericTypeDefinition() == typeof(Dictionary<, >); + } + } +} diff --git a/modules/mono/glue/Managed/Files/Mathf.cs b/modules/mono/glue/Managed/Files/Mathf.cs new file mode 100644 index 0000000000..a89dfe5f27 --- /dev/null +++ b/modules/mono/glue/Managed/Files/Mathf.cs @@ -0,0 +1,301 @@ +using System; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + +namespace Godot +{ + public static partial class Mathf + { + // Define constants with Decimal precision and cast down to double or float. + + public const real_t Tau = (real_t) 6.2831853071795864769252867666M; // 6.2831855f and 6.28318530717959 + public const real_t Pi = (real_t) 3.1415926535897932384626433833M; // 3.1415927f and 3.14159265358979 + public const real_t Inf = real_t.PositiveInfinity; + 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 + + public static real_t Abs(real_t s) + { + return Math.Abs(s); + } + + public static int Abs(int s) + { + return Math.Abs(s); + } + + public static real_t Acos(real_t s) + { + return (real_t)Math.Acos(s); + } + + public static real_t Asin(real_t s) + { + return (real_t)Math.Asin(s); + } + + public static real_t Atan(real_t s) + { + return (real_t)Math.Atan(s); + } + + public static real_t Atan2(real_t x, real_t y) + { + return (real_t)Math.Atan2(x, y); + } + + public static Vector2 Cartesian2Polar(real_t x, real_t y) + { + return new Vector2(Sqrt(x * x + y * y), Atan2(y, x)); + } + + public static real_t Ceil(real_t s) + { + return (real_t)Math.Ceiling(s); + } + + public static int Clamp(int value, int min, int max) + { + return value < min ? min : value > max ? max : value; + } + + public static real_t Clamp(real_t value, real_t min, real_t max) + { + return value < min ? min : value > max ? max : value; + } + + public static real_t Cos(real_t s) + { + return (real_t)Math.Cos(s); + } + + public static real_t Cosh(real_t s) + { + return (real_t)Math.Cosh(s); + } + + public static int Decimals(real_t step) + { + return Decimals((decimal)step); + } + + public static int Decimals(decimal step) + { + return BitConverter.GetBytes(decimal.GetBits(step)[3])[2]; + } + + public static real_t Deg2Rad(real_t deg) + { + return deg * Deg2RadConst; + } + + public static real_t Ease(real_t s, real_t curve) + { + if (s < 0f) + { + s = 0f; + } + else if (s > 1.0f) + { + s = 1.0f; + } + + if (curve > 0f) + { + if (curve < 1.0f) + { + return 1.0f - Pow(1.0f - s, 1.0f / curve); + } + + return Pow(s, curve); + } + + if (curve < 0f) + { + if (s < 0.5f) + { + return Pow(s * 2.0f, -curve) * 0.5f; + } + + return (1.0f - Pow(1.0f - (s - 0.5f) * 2.0f, -curve)) * 0.5f + 0.5f; + } + + return 0f; + } + + public static real_t Exp(real_t s) + { + return (real_t)Math.Exp(s); + } + + public static real_t Floor(real_t s) + { + return (real_t)Math.Floor(s); + } + + public static real_t InverseLerp(real_t from, real_t to, real_t weight) + { + return (weight - from) / (to - from); + } + + public static bool IsInf(real_t s) + { + return real_t.IsInfinity(s); + } + + public static bool IsNaN(real_t s) + { + return real_t.IsNaN(s); + } + + public static real_t Lerp(real_t from, real_t to, real_t weight) + { + return from + (to - from) * weight; + } + + public static real_t Log(real_t s) + { + return (real_t)Math.Log(s); + } + + public static int Max(int a, int b) + { + return a > b ? a : b; + } + + public static real_t Max(real_t a, real_t b) + { + return a > b ? a : b; + } + + public static int Min(int a, int b) + { + return a < b ? a : b; + } + + public static real_t Min(real_t a, real_t b) + { + return a < b ? a : b; + } + + public static int NearestPo2(int value) + { + value--; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value++; + return value; + } + + public static Vector2 Polar2Cartesian(real_t r, real_t th) + { + return new Vector2(r * Cos(th), r * Sin(th)); + } + + /// <summary> + /// Performs a canonical Modulus operation, where the output is on the range [0, b). + /// </summary> + public static real_t PosMod(real_t a, real_t b) + { + real_t c = a % b; + if ((c < 0 && b > 0) || (c > 0 && b < 0)) + { + c += b; + } + return c; + } + + /// <summary> + /// Performs a canonical Modulus operation, where the output is on the range [0, b). + /// </summary> + public static int PosMod(int a, int b) + { + int c = a % b; + if ((c < 0 && b > 0) || (c > 0 && b < 0)) + { + c += b; + } + return c; + } + + public static real_t Pow(real_t x, real_t y) + { + return (real_t)Math.Pow(x, y); + } + + public static real_t Rad2Deg(real_t rad) + { + return rad * Rad2DegConst; + } + + public static real_t Round(real_t s) + { + return (real_t)Math.Round(s); + } + + public static int Sign(int s) + { + return s < 0 ? -1 : 1; + } + + public static real_t Sign(real_t s) + { + return s < 0f ? -1f : 1f; + } + + public static real_t Sin(real_t s) + { + return (real_t)Math.Sin(s); + } + + public static real_t Sinh(real_t s) + { + return (real_t)Math.Sinh(s); + } + + public static real_t Sqrt(real_t s) + { + return (real_t)Math.Sqrt(s); + } + + public static real_t Stepify(real_t s, real_t step) + { + if (step != 0f) + { + s = Floor(s / step + 0.5f) * step; + } + + return s; + } + + public static real_t Tan(real_t s) + { + return (real_t)Math.Tan(s); + } + + public static real_t Tanh(real_t s) + { + return (real_t)Math.Tanh(s); + } + + public static int Wrap(int value, int min, int max) + { + int rng = max - min; + return min + ((value - min) % rng + rng) % rng; + } + + public static real_t Wrap(real_t value, real_t min, real_t max) + { + real_t rng = max - min; + return min + ((value - min) % rng + rng) % rng; + } + } +} diff --git a/modules/mono/glue/Managed/Files/MathfEx.cs b/modules/mono/glue/Managed/Files/MathfEx.cs new file mode 100644 index 0000000000..739b7fb568 --- /dev/null +++ b/modules/mono/glue/Managed/Files/MathfEx.cs @@ -0,0 +1,39 @@ +using System; + +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + +namespace Godot +{ + public static partial class Mathf + { + // Define constants with Decimal precision and cast down to double or float. + + public const real_t E = (real_t) 2.7182818284590452353602874714M; // 2.7182817f and 2.718281828459045 + public const real_t Sqrt2 = (real_t) 1.4142135623730950488016887242M; // 1.4142136f and 1.414213562373095 + +#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 + + public static int CeilToInt(real_t s) + { + return (int)Math.Ceiling(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); + } + } +}
\ No newline at end of file diff --git a/modules/mono/glue/Managed/Files/NodePath.cs b/modules/mono/glue/Managed/Files/NodePath.cs new file mode 100644 index 0000000000..2c89bec87f --- /dev/null +++ b/modules/mono/glue/Managed/Files/NodePath.cs @@ -0,0 +1,147 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Godot +{ + public partial class NodePath : IDisposable + { + private bool disposed = false; + + internal IntPtr ptr; + + internal static IntPtr GetPtr(NodePath instance) + { + return instance == null ? IntPtr.Zero : instance.ptr; + } + + ~NodePath() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposed) + return; + + if (ptr != IntPtr.Zero) + { + godot_icall_NodePath_Dtor(ptr); + ptr = IntPtr.Zero; + } + + disposed = true; + } + + internal NodePath(IntPtr ptr) + { + this.ptr = ptr; + } + + public IntPtr NativeInstance + { + get { return ptr; } + } + + public NodePath() : this(string.Empty) {} + + public NodePath(string path) + { + this.ptr = godot_icall_NodePath_Ctor(path); + } + + public static implicit operator NodePath(string from) + { + return new NodePath(from); + } + + public static implicit operator string(NodePath from) + { + return godot_icall_NodePath_operator_String(NodePath.GetPtr(from)); + } + + public override string ToString() + { + return (string)this; + } + + public NodePath GetAsPropertyPath() + { + return new NodePath(godot_icall_NodePath_get_as_property_path(NodePath.GetPtr(this))); + } + + public string GetConcatenatedSubnames() + { + return godot_icall_NodePath_get_concatenated_subnames(NodePath.GetPtr(this)); + } + + public string GetName(int idx) + { + return godot_icall_NodePath_get_name(NodePath.GetPtr(this), idx); + } + + public int GetNameCount() + { + return godot_icall_NodePath_get_name_count(NodePath.GetPtr(this)); + } + + public string GetSubname(int idx) + { + return godot_icall_NodePath_get_subname(NodePath.GetPtr(this), idx); + } + + public int GetSubnameCount() + { + return godot_icall_NodePath_get_subname_count(NodePath.GetPtr(this)); + } + + public bool IsAbsolute() + { + return godot_icall_NodePath_is_absolute(NodePath.GetPtr(this)); + } + + public bool IsEmpty() + { + return godot_icall_NodePath_is_empty(NodePath.GetPtr(this)); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_NodePath_Ctor(string path); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_NodePath_Dtor(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_NodePath_operator_String(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_NodePath_get_as_property_path(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_NodePath_get_concatenated_subnames(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_NodePath_get_name(IntPtr ptr, int arg1); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_NodePath_get_name_count(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_NodePath_get_subname(IntPtr ptr, int arg1); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_NodePath_get_subname_count(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_NodePath_is_absolute(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_NodePath_is_empty(IntPtr ptr); + } +} diff --git a/modules/mono/glue/Managed/Files/Object.base.cs b/modules/mono/glue/Managed/Files/Object.base.cs new file mode 100644 index 0000000000..30490a715f --- /dev/null +++ b/modules/mono/glue/Managed/Files/Object.base.cs @@ -0,0 +1,88 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Godot +{ + public partial class Object : IDisposable + { + private bool disposed = false; + + private const string nativeName = "Object"; + + internal IntPtr ptr; + internal bool memoryOwn; + + public Object() : this(false) + { + if (ptr == IntPtr.Zero) + ptr = godot_icall_Object_Ctor(this); + } + + internal Object(bool memoryOwn) + { + this.memoryOwn = memoryOwn; + } + + public IntPtr NativeInstance + { + get { return ptr; } + } + + internal static IntPtr GetPtr(Object instance) + { + return instance == null ? IntPtr.Zero : instance.ptr; + } + + ~Object() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposed) + return; + + if (ptr != IntPtr.Zero) + { + if (memoryOwn) + { + memoryOwn = false; + godot_icall_Reference_Disposed(this, ptr, !disposing); + } + else + { + godot_icall_Object_Disposed(this, ptr); + } + + this.ptr = IntPtr.Zero; + } + + disposed = true; + } + + public SignalAwaiter ToSignal(Object source, string signal) + { + return new SignalAwaiter(source, signal, this); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Object_Ctor(Object obj); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Object_Disposed(Object obj, IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Reference_Disposed(Object obj, IntPtr ptr, bool isFinalizer); + + // Used by the generated API + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Object_ClassDB_get_method(string type, string method); + } +} diff --git a/modules/mono/glue/Managed/Files/Plane.cs b/modules/mono/glue/Managed/Files/Plane.cs new file mode 100644 index 0000000000..9611dce11e --- /dev/null +++ b/modules/mono/glue/Managed/Files/Plane.cs @@ -0,0 +1,229 @@ +using System; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + +namespace Godot +{ + public struct Plane : IEquatable<Plane> + { + private Vector3 _normal; + + public Vector3 Normal + { + get { return _normal; } + set { _normal = value; } + } + + public real_t x + { + get + { + return _normal.x; + } + set + { + _normal.x = value; + } + } + + public real_t y + { + get + { + return _normal.y; + } + set + { + _normal.y = value; + } + } + + public real_t z + { + get + { + return _normal.z; + } + set + { + _normal.z = value; + } + } + + public real_t D { get; set; } + + public Vector3 Center + { + get + { + return _normal * D; + } + } + + public real_t DistanceTo(Vector3 point) + { + return _normal.Dot(point) - D; + } + + public Vector3 GetAnyPoint() + { + return _normal * D; + } + + public bool HasPoint(Vector3 point, real_t epsilon = Mathf.Epsilon) + { + real_t dist = _normal.Dot(point) - D; + return Mathf.Abs(dist) <= epsilon; + } + + public Vector3 Intersect3(Plane b, Plane c) + { + real_t denom = _normal.Cross(b._normal).Dot(c._normal); + + if (Mathf.Abs(denom) <= Mathf.Epsilon) + return new Vector3(); + + Vector3 result = b._normal.Cross(c._normal) * D + + c._normal.Cross(_normal) * b.D + + _normal.Cross(b._normal) * c.D; + + return result / denom; + } + + public Vector3 IntersectRay(Vector3 from, Vector3 dir) + { + real_t den = _normal.Dot(dir); + + if (Mathf.Abs(den) <= Mathf.Epsilon) + return new Vector3(); + + 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 new Vector3(); + + return from + dir * -dist; + } + + public Vector3 IntersectSegment(Vector3 begin, Vector3 end) + { + Vector3 segment = begin - end; + real_t den = _normal.Dot(segment); + + if (Mathf.Abs(den) <= Mathf.Epsilon) + return new Vector3(); + + real_t dist = (_normal.Dot(begin) - D) / den; + + if (dist < -Mathf.Epsilon || dist > 1.0f + Mathf.Epsilon) + return new Vector3(); + + return begin + segment * -dist; + } + + public bool IsPointOver(Vector3 point) + { + return _normal.Dot(point) > D; + } + + 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); + } + + public Vector3 Project(Vector3 point) + { + return point - _normal * DistanceTo(point); + } + + // Constants + private static readonly Plane _planeYZ = new Plane(1, 0, 0, 0); + private static readonly Plane _planeXZ = new Plane(0, 1, 0, 0); + private static readonly Plane _planeXY = new Plane(0, 0, 1, 0); + + public static Plane PlaneYZ { get { return _planeYZ; } } + public static Plane PlaneXZ { get { return _planeXZ; } } + public static Plane PlaneXY { get { return _planeXY; } } + + // Constructors + public Plane(real_t a, real_t b, real_t c, real_t d) + { + _normal = new Vector3(a, b, c); + this.D = d; + } + public Plane(Vector3 normal, real_t d) + { + this._normal = normal; + this.D = d; + } + + public Plane(Vector3 v1, Vector3 v2, Vector3 v3) + { + _normal = (v1 - v3).Cross(v1 - v2); + _normal.Normalize(); + D = _normal.Dot(v1); + } + + public static Plane operator -(Plane plane) + { + return new Plane(-plane._normal, -plane.D); + } + + public static bool operator ==(Plane left, Plane right) + { + return left.Equals(right); + } + + public static bool operator !=(Plane left, Plane right) + { + return !left.Equals(right); + } + + public override bool Equals(object obj) + { + if (obj is Plane) + { + return Equals((Plane)obj); + } + + return false; + } + + public bool Equals(Plane other) + { + return _normal == other._normal && D == other.D; + } + + public override int GetHashCode() + { + return _normal.GetHashCode() ^ D.GetHashCode(); + } + + public override string ToString() + { + return String.Format("({0}, {1})", new object[] + { + _normal.ToString(), + D.ToString() + }); + } + + public string ToString(string format) + { + return String.Format("({0}, {1})", new object[] + { + _normal.ToString(format), + D.ToString(format) + }); + } + } +} diff --git a/modules/mono/glue/Managed/Files/Quat.cs b/modules/mono/glue/Managed/Files/Quat.cs new file mode 100644 index 0000000000..eaa027eb69 --- /dev/null +++ b/modules/mono/glue/Managed/Files/Quat.cs @@ -0,0 +1,378 @@ +using System; +using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + +namespace Godot +{ + [StructLayout(LayoutKind.Sequential)] + public struct Quat : IEquatable<Quat> + { + public real_t x; + public real_t y; + public real_t z; + public real_t w; + + public real_t this[int index] + { + get + { + switch (index) + { + case 0: + return x; + case 1: + return y; + case 2: + return z; + case 3: + return w; + default: + throw new IndexOutOfRangeException(); + } + } + set + { + switch (index) + { + case 0: + x = value; + break; + case 1: + y = value; + break; + case 2: + z = value; + break; + case 3: + w = value; + break; + default: + throw new IndexOutOfRangeException(); + } + } + } + + public real_t Length + { + get { return Mathf.Sqrt(LengthSquared); } + } + + public real_t LengthSquared + { + get { return Dot(this); } + } + + public Quat CubicSlerp(Quat b, Quat preA, Quat postB, real_t t) + { + real_t t2 = (1.0f - t) * t * 2f; + Quat sp = Slerp(b, t); + Quat sq = preA.Slerpni(postB, t); + return sp.Slerpni(sq, t2); + } + + public real_t Dot(Quat b) + { + return x * b.x + y * b.y + z * b.z + w * b.w; + } + + public Vector3 GetEuler() + { + var basis = new Basis(this); + return basis.GetEuler(); + } + + public Quat Inverse() + { + return new Quat(-x, -y, -z, w); + } + + public Quat Normalized() + { + return this / Length; + } + + public void Set(real_t x, real_t y, real_t z, real_t w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + public void Set(Quat q) + { + this = q; + } + + public void SetAxisAngle(Vector3 axis, real_t angle) + { + this = new Quat(axis, angle); + } + + public void SetEuler(Vector3 eulerYXZ) + { + this = new Quat(eulerYXZ); + } + + public Quat Slerp(Quat b, real_t t) + { + // Calculate cosine + real_t cosom = x * b.x + y * b.y + z * b.z + w * b.w; + + var to1 = new real_t[4]; + + // Adjust signs if necessary + if (cosom < 0.0) + { + cosom = -cosom; to1[0] = -b.x; + to1[1] = -b.y; + to1[2] = -b.z; + to1[3] = -b.w; + } + else + { + to1[0] = b.x; + to1[1] = b.y; + to1[2] = b.z; + to1[3] = b.w; + } + + real_t sinom, scale0, scale1; + + // Calculate coefficients + if (1.0 - cosom > Mathf.Epsilon) + { + // 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; + } + else + { + // Quaternions are very close so we can do a linear interpolation + scale0 = 1.0f - t; + scale1 = t; + } + + // Calculate final values + return new Quat + ( + scale0 * x + scale1 * to1[0], + scale0 * y + scale1 * to1[1], + scale0 * z + scale1 * to1[2], + scale0 * w + scale1 * to1[3] + ); + } + + public Quat Slerpni(Quat b, real_t t) + { + real_t dot = Dot(b); + + if (Mathf.Abs(dot) > 0.9999f) + { + return this; + } + + 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; + + return new Quat + ( + invFactor * x + newFactor * b.x, + invFactor * y + newFactor * b.y, + invFactor * z + newFactor * b.z, + invFactor * w + newFactor * b.w + ); + } + + public Vector3 Xform(Vector3 v) + { + Quat q = this * v; + q *= Inverse(); + return new Vector3(q.x, q.y, q.z); + } + + // Static Readonly Properties + public static Quat Identity { get; } = new Quat(0f, 0f, 0f, 1f); + + // Constructors + public Quat(real_t x, real_t y, real_t z, real_t w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + public bool IsNormalized() + { + return Mathf.Abs(LengthSquared - 1) <= Mathf.Epsilon; + } + + public Quat(Quat q) + { + this = q; + } + + public Quat(Basis basis) + { + this = basis.Quat(); + } + + public Quat(Vector3 eulerYXZ) + { + real_t half_a1 = eulerYXZ.y * (real_t)0.5; + real_t half_a2 = eulerYXZ.x * (real_t)0.5; + real_t half_a3 = eulerYXZ.z * (real_t)0.5; + + // R = Y(a1).X(a2).Z(a3) convention for Euler angles. + // Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-6) + // a3 is the angle of the first rotation, following the notation in this reference. + + real_t cos_a1 = Mathf.Cos(half_a1); + real_t sin_a1 = Mathf.Sin(half_a1); + real_t cos_a2 = Mathf.Cos(half_a2); + real_t sin_a2 = Mathf.Sin(half_a2); + real_t cos_a3 = Mathf.Cos(half_a3); + real_t sin_a3 = Mathf.Sin(half_a3); + + x = sin_a1 * cos_a2 * sin_a3 + cos_a1 * sin_a2 * cos_a3; + y = sin_a1 * cos_a2 * cos_a3 - cos_a1 * sin_a2 * sin_a3; + z = -sin_a1 * sin_a2 * cos_a3 + cos_a1 * cos_a2 * sin_a3; + w = sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3; + } + + public Quat(Vector3 axis, real_t angle) + { + real_t d = axis.Length(); + real_t angle_t = angle; + + if (d == 0f) + { + x = 0f; + y = 0f; + z = 0f; + w = 0f; + } + else + { + real_t s = Mathf.Sin(angle_t * 0.5f) / d; + + x = axis.x * s; + y = axis.y * s; + z = axis.z * s; + w = Mathf.Cos(angle_t * 0.5f); + } + } + + public static Quat operator *(Quat left, Quat right) + { + return new Quat + ( + left.w * right.x + left.x * right.w + left.y * right.z - left.z * right.y, + left.w * right.y + left.y * right.w + left.z * right.x - left.x * right.z, + left.w * right.z + left.z * right.w + left.x * right.y - left.y * right.x, + left.w * right.w - left.x * right.x - left.y * right.y - left.z * right.z + ); + } + + public static Quat operator +(Quat left, Quat right) + { + return new Quat(left.x + right.x, left.y + right.y, left.z + right.z, left.w + right.w); + } + + public static Quat operator -(Quat left, Quat right) + { + return new Quat(left.x - right.x, left.y - right.y, left.z - right.z, left.w - right.w); + } + + public static Quat operator -(Quat left) + { + return new Quat(-left.x, -left.y, -left.z, -left.w); + } + + public static Quat operator *(Quat left, Vector3 right) + { + return new Quat + ( + left.w * right.x + left.y * right.z - left.z * right.y, + left.w * right.y + left.z * right.x - left.x * right.z, + left.w * right.z + left.x * right.y - left.y * right.x, + -left.x * right.x - left.y * right.y - left.z * right.z + ); + } + + public static Quat operator *(Vector3 left, Quat right) + { + return new Quat + ( + right.w * left.x + right.y * left.z - right.z * left.y, + right.w * left.y + right.z * left.x - right.x * left.z, + right.w * left.z + right.x * left.y - right.y * left.x, + -right.x * left.x - right.y * left.y - right.z * left.z + ); + } + + public static Quat operator *(Quat left, real_t right) + { + return new Quat(left.x * right, left.y * right, left.z * right, left.w * right); + } + + public static Quat operator *(real_t left, Quat right) + { + return new Quat(right.x * left, right.y * left, right.z * left, right.w * left); + } + + public static Quat operator /(Quat left, real_t right) + { + return left * (1.0f / right); + } + + public static bool operator ==(Quat left, Quat right) + { + return left.Equals(right); + } + + public static bool operator !=(Quat left, Quat right) + { + return !left.Equals(right); + } + + public override bool Equals(object obj) + { + if (obj is Vector2) + { + return Equals((Vector2)obj); + } + + return false; + } + + public bool Equals(Quat other) + { + return x == other.x && y == other.y && z == other.z && w == other.w; + } + + public override int GetHashCode() + { + return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode(); + } + + public override string ToString() + { + return String.Format("({0}, {1}, {2}, {3})", x.ToString(), y.ToString(), z.ToString(), w.ToString()); + } + + public string ToString(string format) + { + return String.Format("({0}, {1}, {2}, {3})", x.ToString(format), y.ToString(format), z.ToString(format), w.ToString(format)); + } + } +} diff --git a/modules/mono/glue/Managed/Files/RID.cs b/modules/mono/glue/Managed/Files/RID.cs new file mode 100644 index 0000000000..b862b8cac0 --- /dev/null +++ b/modules/mono/glue/Managed/Files/RID.cs @@ -0,0 +1,76 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Godot +{ + public partial class RID : IDisposable + { + private bool disposed = false; + + internal IntPtr ptr; + + internal static IntPtr GetPtr(RID instance) + { + return instance == null ? IntPtr.Zero : instance.ptr; + } + + ~RID() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposed) + return; + + if (ptr != IntPtr.Zero) + { + godot_icall_RID_Dtor(ptr); + ptr = IntPtr.Zero; + } + + disposed = true; + } + + internal RID(IntPtr ptr) + { + this.ptr = ptr; + } + + public IntPtr NativeInstance + { + get { return ptr; } + } + + internal RID() + { + this.ptr = IntPtr.Zero; + } + + public RID(Object from) + { + this.ptr = godot_icall_RID_Ctor(Object.GetPtr(from)); + } + + public int GetId() + { + return godot_icall_RID_get_id(RID.GetPtr(this)); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_RID_Ctor(IntPtr from); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_RID_Dtor(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_RID_get_id(IntPtr ptr); + } +} diff --git a/modules/mono/glue/Managed/Files/Rect2.cs b/modules/mono/glue/Managed/Files/Rect2.cs new file mode 100644 index 0000000000..cb25c267bc --- /dev/null +++ b/modules/mono/glue/Managed/Files/Rect2.cs @@ -0,0 +1,256 @@ +using System; +using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + +namespace Godot +{ + [StructLayout(LayoutKind.Sequential)] + public struct Rect2 : IEquatable<Rect2> + { + private Vector2 _position; + private Vector2 _size; + + public Vector2 Position + { + get { return _position; } + set { _position = value; } + } + + public Vector2 Size + { + get { return _size; } + set { _size = value; } + } + + public Vector2 End + { + get { return _position + _size; } + set { _size = value - _position; } + } + + public real_t Area + { + get { return GetArea(); } + } + + public Rect2 Abs() + { + Vector2 end = End; + Vector2 topLeft = new Vector2(Mathf.Min(_position.x, end.x), Mathf.Min(_position.y, end.y)); + return new Rect2(topLeft, _size.Abs()); + } + + 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); + + Vector2 bEnd = b._position + b._size; + Vector2 end = _position + _size; + + newRect._size.x = Mathf.Min(bEnd.x, end.x) - newRect._position.x; + newRect._size.y = Mathf.Min(bEnd.y, end.y) - newRect._position.y; + + return newRect; + } + + public bool Encloses(Rect2 b) + { + return b._position.x >= _position.x && b._position.y >= _position.y && + b._position.x + b._size.x < _position.x + _size.x && + b._position.y + b._size.y < _position.y + _size.y; + } + + public Rect2 Expand(Vector2 to) + { + var expanded = this; + + Vector2 begin = expanded._position; + 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; + + return expanded; + } + + public real_t GetArea() + { + return _size.x * _size.y; + } + + public Rect2 Grow(real_t by) + { + var g = this; + + g._position.x -= by; + g._position.y -= by; + g._size.x += by * 2; + g._size.y += by * 2; + + return g; + } + + public Rect2 GrowIndividual(real_t left, real_t top, real_t right, real_t bottom) + { + var g = this; + + g._position.x -= left; + g._position.y -= top; + g._size.x += left + right; + g._size.y += top + bottom; + + return g; + } + + public Rect2 GrowMargin(Margin margin, real_t by) + { + var g = this; + + g.GrowIndividual(Margin.Left == margin ? by : 0, + Margin.Top == margin ? by : 0, + Margin.Right == margin ? by : 0, + Margin.Bottom == margin ? by : 0); + + return g; + } + + public bool HasNoArea() + { + return _size.x <= 0 || _size.y <= 0; + } + + public bool HasPoint(Vector2 point) + { + if (point.x < _position.x) + return false; + if (point.y < _position.y) + return false; + + if (point.x >= _position.x + _size.x) + return false; + if (point.y >= _position.y + _size.y) + return false; + + return true; + } + + public bool Intersects(Rect2 b) + { + 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; + } + + public Rect2 Merge(Rect2 b) + { + Rect2 newRect; + + newRect._position.x = Mathf.Min(b._position.x, _position.x); + newRect._position.y = Mathf.Min(b._position.y, _position.y); + + 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 + + return newRect; + } + + // Constructors + public Rect2(Vector2 position, Vector2 size) + { + _position = position; + _size = size; + } + public Rect2(Vector2 position, real_t width, real_t height) + { + _position = position; + _size = new Vector2(width, height); + } + public Rect2(real_t x, real_t y, Vector2 size) + { + _position = new Vector2(x, y); + _size = size; + } + public Rect2(real_t x, real_t y, real_t width, real_t height) + { + _position = new Vector2(x, y); + _size = new Vector2(width, height); + } + + public static bool operator ==(Rect2 left, Rect2 right) + { + return left.Equals(right); + } + + public static bool operator !=(Rect2 left, Rect2 right) + { + return !left.Equals(right); + } + + public override bool Equals(object obj) + { + if (obj is Rect2) + { + return Equals((Rect2)obj); + } + + return false; + } + + public bool Equals(Rect2 other) + { + return _position.Equals(other._position) && _size.Equals(other._size); + } + + public override int GetHashCode() + { + return _position.GetHashCode() ^ _size.GetHashCode(); + } + + public override string ToString() + { + return String.Format("({0}, {1})", new object[] + { + _position.ToString(), + _size.ToString() + }); + } + + public string ToString(string format) + { + return String.Format("({0}, {1})", new object[] + { + _position.ToString(format), + _size.ToString(format) + }); + } + } +} diff --git a/modules/mono/glue/Managed/Files/SignalAwaiter.cs b/modules/mono/glue/Managed/Files/SignalAwaiter.cs new file mode 100644 index 0000000000..9483b6ffb4 --- /dev/null +++ b/modules/mono/glue/Managed/Files/SignalAwaiter.cs @@ -0,0 +1,60 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Godot +{ + public class SignalAwaiter : IAwaiter<object[]>, IAwaitable<object[]> + { + private bool completed; + private object[] result; + private Action action; + + public SignalAwaiter(Object source, string signal, Object target) + { + godot_icall_SignalAwaiter_connect(Object.GetPtr(source), signal, Object.GetPtr(target), this); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static Error godot_icall_SignalAwaiter_connect(IntPtr source, string signal, IntPtr target, SignalAwaiter awaiter); + + public bool IsCompleted + { + get + { + return completed; + } + } + + public void OnCompleted(Action action) + { + this.action = action; + } + + public object[] GetResult() + { + return result; + } + + public IAwaiter<object[]> GetAwaiter() + { + return this; + } + + internal void SignalCallback(object[] args) + { + completed = true; + result = args; + + if (action != null) + { + action(); + } + } + + internal void FailureCallback() + { + action = null; + completed = true; + } + } +} diff --git a/modules/mono/glue/Managed/Files/StringExtensions.cs b/modules/mono/glue/Managed/Files/StringExtensions.cs new file mode 100644 index 0000000000..21c9be98c1 --- /dev/null +++ b/modules/mono/glue/Managed/Files/StringExtensions.cs @@ -0,0 +1,981 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Security; +using System.Text; +using System.Text.RegularExpressions; + +namespace Godot +{ + public static class StringExtensions + { + private static int GetSliceCount(this string instance, string splitter) + { + if (instance.Empty() || splitter.Empty()) + return 0; + + int pos = 0; + int slices = 1; + + while ((pos = instance.Find(splitter, pos)) >= 0) + { + slices++; + pos += splitter.Length; + } + + return slices; + } + + private static string GetSliceCharacter(this string instance, char splitter, int slice) + { + if (!instance.Empty() && slice >= 0) + { + int i = 0; + int prev = 0; + int count = 0; + + while (true) + { + bool end = instance.Length <= i; + + if (end || instance[i] == splitter) + { + if (slice == count) + { + return instance.Substring(prev, i - prev); + } + else if (end) + { + return string.Empty; + } + + count++; + prev = i + 1; + } + + i++; + } + } + + return string.Empty; + } + + // <summary> + // If the string is a path to a file, return the path to the file without the extension. + // </summary> + public static string BaseName(this string instance) + { + int index = instance.LastIndexOf('.'); + + if (index > 0) + return instance.Substring(0, index); + + return instance; + } + + // <summary> + // Return true if the strings begins with the given string. + // </summary> + public static bool BeginsWith(this string instance, string text) + { + return instance.StartsWith(text); + } + + // <summary> + // Return the bigrams (pairs of consecutive letters) of this string. + // </summary> + public static string[] Bigrams(this string instance) + { + var b = new string[instance.Length - 1]; + + for (int i = 0; i < b.Length; i++) + { + b[i] = instance.Substring(i, 2); + } + + return b; + } + + // <summary> + // Return a copy of the string with special characters escaped using the C language standard. + // </summary> + public static string CEscape(this string instance) + { + var sb = new StringBuilder(string.Copy(instance)); + + sb.Replace("\\", "\\\\"); + sb.Replace("\a", "\\a"); + sb.Replace("\b", "\\b"); + sb.Replace("\f", "\\f"); + sb.Replace("\n", "\\n"); + sb.Replace("\r", "\\r"); + sb.Replace("\t", "\\t"); + sb.Replace("\v", "\\v"); + sb.Replace("\'", "\\'"); + sb.Replace("\"", "\\\""); + sb.Replace("?", "\\?"); + + return sb.ToString(); + } + + // <summary> + // Return a copy of the string with escaped characters replaced by their meanings according to the C language standard. + // </summary> + public static string CUnescape(this string instance) + { + var sb = new StringBuilder(string.Copy(instance)); + + sb.Replace("\\a", "\a"); + sb.Replace("\\b", "\b"); + sb.Replace("\\f", "\f"); + sb.Replace("\\n", "\n"); + sb.Replace("\\r", "\r"); + sb.Replace("\\t", "\t"); + sb.Replace("\\v", "\v"); + sb.Replace("\\'", "\'"); + sb.Replace("\\\"", "\""); + sb.Replace("\\?", "?"); + sb.Replace("\\\\", "\\"); + + return sb.ToString(); + } + + // <summary> + // Change the case of some letters. Replace underscores with spaces, convert all letters to lowercase then capitalize first and every letter following the space character. For [code]capitalize camelCase mixed_with_underscores[/code] it will return [code]Capitalize Camelcase Mixed With Underscores[/code]. + // </summary> + public static string Capitalize(this string instance) + { + string aux = instance.Replace("_", " ").ToLower(); + var cap = string.Empty; + + for (int i = 0; i < aux.GetSliceCount(" "); i++) + { + string slice = aux.GetSliceCharacter(' ', i); + if (slice.Length > 0) + { + slice = char.ToUpper(slice[0]) + slice.Substring(1); + if (i > 0) + cap += " "; + cap += slice; + } + } + + return cap; + } + + // <summary> + // Perform a case-sensitive comparison to another string, return -1 if less, 0 if equal and +1 if greater. + // </summary> + public static int CasecmpTo(this string instance, string to) + { + return instance.CompareTo(to, true); + } + + // <summary> + // Perform a comparison to another string, return -1 if less, 0 if equal and +1 if greater. + // </summary> + public static int CompareTo(this string instance, string to, bool caseSensitive = true) + { + if (instance.Empty()) + return to.Empty() ? 0 : -1; + + if (to.Empty()) + return 1; + + int instanceIndex = 0; + int toIndex = 0; + + if (caseSensitive) // Outside while loop to avoid checking multiple times, despite some code duplication. + { + while (true) + { + if (to[toIndex] == 0 && instance[instanceIndex] == 0) + return 0; // We're equal + if (instance[instanceIndex] == 0) + return -1; // If this is empty, and the other one is not, then we're less... I think? + if (to[toIndex] == 0) + return 1; // Otherwise the other one is smaller... + if (instance[instanceIndex] < to[toIndex]) // More than + return -1; + if (instance[instanceIndex] > to[toIndex]) // Less than + return 1; + + instanceIndex++; + toIndex++; + } + } else + { + while (true) + { + if (to[toIndex] == 0 && instance[instanceIndex] == 0) + return 0; // We're equal + if (instance[instanceIndex] == 0) + return -1; // If this is empty, and the other one is not, then we're less... I think? + if (to[toIndex] == 0) + return 1; // Otherwise the other one is smaller.. + if (char.ToUpper(instance[instanceIndex]) < char.ToUpper(to[toIndex])) // More than + return -1; + if (char.ToUpper(instance[instanceIndex]) > char.ToUpper(to[toIndex])) // Less than + return 1; + + instanceIndex++; + toIndex++; + } + } + } + + // <summary> + // Return true if the string is empty. + // </summary> + public static bool Empty(this string instance) + { + return string.IsNullOrEmpty(instance); + } + + // <summary> + // Return true if the strings ends with the given string. + // </summary> + public static bool EndsWith(this string instance, string text) + { + return instance.EndsWith(text); + } + + // <summary> + // Erase [code]chars[/code] characters from the string starting from [code]pos[/code]. + // </summary> + public static void Erase(this StringBuilder instance, int pos, int chars) + { + instance.Remove(pos, chars); + } + + // <summary> + // If the string is a path to a file, return the extension. + // </summary> + public static string Extension(this string instance) + { + int pos = instance.FindLast("."); + + if (pos < 0) + return instance; + + return instance.Substring(pos + 1); + } + + // <summary> + // Find the first occurrence of a substring, return the starting position of the substring or -1 if not found. Optionally, the initial search index can be passed. + // </summary> + public static int Find(this string instance, string what, int from = 0) + { + return instance.IndexOf(what, StringComparison.OrdinalIgnoreCase); + } + + // <summary> + // Find the last occurrence of a substring, return the starting position of the substring or -1 if not found. Optionally, the initial search index can be passed. + // </summary> + public static int FindLast(this string instance, string what) + { + return instance.LastIndexOf(what, StringComparison.OrdinalIgnoreCase); + } + + // <summary> + // Find the first occurrence of a substring but search as case-insensitive, return the starting position of the substring or -1 if not found. Optionally, the initial search index can be passed. + // </summary> + public static int FindN(this string instance, string what, int from = 0) + { + return instance.IndexOf(what, StringComparison.Ordinal); + } + + // <summary> + // If the string is a path to a file, return the base directory. + // </summary> + public static string GetBaseDir(this string instance) + { + int basepos = instance.Find("://"); + + string rs; + var @base = string.Empty; + + if (basepos != -1) + { + var end = basepos + 3; + rs = instance.Substring(end, instance.Length); + @base = instance.Substring(0, end); + } + else + { + if (instance.BeginsWith("/")) + { + rs = instance.Substring(1, instance.Length); + @base = "/"; + } + else + { + rs = instance; + } + } + + int sep = Mathf.Max(rs.FindLast("/"), rs.FindLast("\\")); + + if (sep == -1) + return @base; + + return @base + rs.Substr(0, sep); + } + + // <summary> + // If the string is a path to a file, return the file and ignore the base directory. + // </summary> + public static string GetFile(this string instance) + { + int sep = Mathf.Max(instance.FindLast("/"), instance.FindLast("\\")); + + if (sep == -1) + return instance; + + return instance.Substring(sep + 1, instance.Length); + } + + // <summary> + // Hash the string and return a 32 bits integer. + // </summary> + public static int Hash(this string instance) + { + int index = 0; + int hashv = 5381; + int c; + + while ((c = instance[index++]) != 0) + hashv = (hashv << 5) + hashv + c; // hash * 33 + c + + return hashv; + } + + // <summary> + // Convert a string containing an hexadecimal number into an int. + // </summary> + public static int HexToInt(this string instance) + { + int sign = 1; + + if (instance[0] == '-') + { + sign = -1; + instance = instance.Substring(1); + } + + if (!instance.StartsWith("0x")) + return 0; + + return sign * int.Parse(instance.Substring(2), NumberStyles.HexNumber); + } + + // <summary> + // Insert a substring at a given position. + // </summary> + public static string Insert(this string instance, int pos, string what) + { + return instance.Insert(pos, what); + } + + // <summary> + // If the string is a path to a file or directory, return true if the path is absolute. + // </summary> + public static bool IsAbsPath(this string instance) + { + return System.IO.Path.IsPathRooted(instance); + } + + // <summary> + // If the string is a path to a file or directory, return true if the path is relative. + // </summary> + public static bool IsRelPath(this string instance) + { + return !System.IO.Path.IsPathRooted(instance); + } + + // <summary> + // Check whether this string is a subsequence of the given string. + // </summary> + public static bool IsSubsequenceOf(this string instance, string text, bool caseSensitive = true) + { + int len = instance.Length; + + if (len == 0) + return true; // Technically an empty string is subsequence of any string + + if (len > text.Length) + return false; + + int source = 0; + int target = 0; + + while (instance[source] != 0 && text[target] != 0) + { + bool match; + + if (!caseSensitive) + { + char sourcec = char.ToLower(instance[source]); + char targetc = char.ToLower(text[target]); + match = sourcec == targetc; + } + else + { + match = instance[source] == text[target]; + } + if (match) + { + source++; + if (instance[source] == 0) + return true; + } + + target++; + } + + return false; + } + + // <summary> + // Check whether this string is a subsequence of the given string, ignoring case differences. + // </summary> + public static bool IsSubsequenceOfI(this string instance, string text) + { + return instance.IsSubsequenceOf(text, false); + } + + // <summary> + // Check whether the string contains a valid float. + // </summary> + public static bool IsValidFloat(this string instance) + { + float f; + return float.TryParse(instance, out f); + } + + // <summary> + // Check whether the string contains a valid color in HTML notation. + // </summary> + public static bool IsValidHtmlColor(this string instance) + { + return Color.HtmlIsValid(instance); + } + + // <summary> + // Check whether the string is a valid identifier. As is common in programming languages, a valid identifier may contain only letters, digits and underscores (_) and the first character may not be a digit. + // </summary> + public static bool IsValidIdentifier(this string instance) + { + int len = instance.Length; + + if (len == 0) + return false; + + for (int i = 0; i < len; i++) + { + if (i == 0) + { + if (instance[0] >= '0' && instance[0] <= '9') + return false; // Don't start with number plz + } + + bool validChar = instance[i] >= '0' && + instance[i] <= '9' || instance[i] >= 'a' && + instance[i] <= 'z' || instance[i] >= 'A' && + instance[i] <= 'Z' || instance[i] == '_'; + + if (!validChar) + return false; + } + + return true; + } + + // <summary> + // Check whether the string contains a valid integer. + // </summary> + public static bool IsValidInteger(this string instance) + { + int f; + return int.TryParse(instance, out f); + } + + // <summary> + // Check whether the string contains a valid IP address. + // </summary> + public static bool IsValidIPAddress(this string instance) + { + // TODO: Support IPv6 addresses + string[] ip = instance.Split("."); + + if (ip.Length != 4) + return false; + + for (int i = 0; i < ip.Length; i++) + { + string n = ip[i]; + if (!n.IsValidInteger()) + return false; + + int val = n.ToInt(); + if (val < 0 || val > 255) + return false; + } + + return true; + } + + // <summary> + // Return a copy of the string with special characters escaped using the JSON standard. + // </summary> + public static string JSONEscape(this string instance) + { + var sb = new StringBuilder(string.Copy(instance)); + + sb.Replace("\\", "\\\\"); + sb.Replace("\b", "\\b"); + sb.Replace("\f", "\\f"); + sb.Replace("\n", "\\n"); + sb.Replace("\r", "\\r"); + sb.Replace("\t", "\\t"); + sb.Replace("\v", "\\v"); + sb.Replace("\"", "\\\""); + + return sb.ToString(); + } + + // <summary> + // Return an amount of characters from the left of the string. + // </summary> + public static string Left(this string instance, int pos) + { + if (pos <= 0) + return string.Empty; + + if (pos >= instance.Length) + return instance; + + return instance.Substring(0, pos); + } + + /// <summary> + /// Return the length of the string in characters. + /// </summary> + public static int Length(this string instance) + { + return instance.Length; + } + + // <summary> + // Do a simple expression match, where '*' matches zero or more arbitrary characters and '?' matches any single character except '.'. + // </summary> + public static bool ExprMatch(this string instance, string expr, bool caseSensitive) + { + if (expr.Length == 0 || instance.Length == 0) + return false; + + switch (expr[0]) + { + case '\0': + return instance[0] == 0; + case '*': + return ExprMatch(expr + 1, instance, caseSensitive) || instance[0] != 0 && ExprMatch(expr, instance + 1, caseSensitive); + case '?': + return instance[0] != 0 && instance[0] != '.' && ExprMatch(expr + 1, instance + 1, caseSensitive); + default: + return (caseSensitive ? instance[0] == expr[0] : char.ToUpper(instance[0]) == char.ToUpper(expr[0])) && + ExprMatch(expr + 1, instance + 1, caseSensitive); + } + } + + // <summary> + // Do a simple case sensitive expression match, using ? and * wildcards (see [method expr_match]). + // </summary> + public static bool Match(this string instance, string expr, bool caseSensitive = true) + { + return instance.ExprMatch(expr, caseSensitive); + } + + // <summary> + // Do a simple case insensitive expression match, using ? and * wildcards (see [method expr_match]). + // </summary> + public static bool MatchN(this string instance, string expr) + { + return instance.ExprMatch(expr, false); + } + + // <summary> + // Return the MD5 hash of the string as an array of bytes. + // </summary> + public static byte[] MD5Buffer(this string instance) + { + return godot_icall_String_md5_buffer(instance); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static byte[] godot_icall_String_md5_buffer(string str); + + // <summary> + // Return the MD5 hash of the string as a string. + // </summary> + public static string MD5Text(this string instance) + { + return godot_icall_String_md5_text(instance); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_String_md5_text(string str); + + // <summary> + // Perform a case-insensitive comparison to another string, return -1 if less, 0 if equal and +1 if greater. + // </summary> + public static int NocasecmpTo(this string instance, string to) + { + return instance.CompareTo(to, false); + } + + // <summary> + // Return the character code at position [code]at[/code]. + // </summary> + public static int OrdAt(this string instance, int at) + { + return instance[at]; + } + + // <summary> + // Format a number to have an exact number of [code]digits[/code] after the decimal point. + // </summary> + public static string PadDecimals(this string instance, int digits) + { + int c = instance.Find("."); + + if (c == -1) + { + if (digits <= 0) + return instance; + + instance += "."; + c = instance.Length - 1; + } + else + { + if (digits <= 0) + return instance.Substring(0, c); + } + + if (instance.Length - (c + 1) > digits) + { + instance = instance.Substring(0, c + digits + 1); + } + else + { + while (instance.Length - (c + 1) < digits) + { + instance += "0"; + } + } + + return instance; + } + + // <summary> + // Format a number to have an exact number of [code]digits[/code] before the decimal point. + // </summary> + public static string PadZeros(this string instance, int digits) + { + string s = instance; + int end = s.Find("."); + + if (end == -1) + end = s.Length; + + if (end == 0) + return s; + + int begin = 0; + + while (begin < end && (s[begin] < '0' || s[begin] > '9')) + { + begin++; + } + + if (begin >= end) + return s; + + while (end - begin < digits) + { + s = s.Insert(begin, "0"); + end++; + } + + return s; + } + + // <summary> + // Decode a percent-encoded string. See [method percent_encode]. + // </summary> + public static string PercentDecode(this string instance) + { + return Uri.UnescapeDataString(instance); + } + + // <summary> + // Percent-encode a string. This is meant to encode parameters in a URL when sending a HTTP GET request and bodies of form-urlencoded POST request. + // </summary> + public static string PercentEncode(this string instance) + { + return Uri.EscapeDataString(instance); + } + + // <summary> + // If the string is a path, this concatenates [code]file[/code] at the end of the string as a subpath. E.g. [code]"this/is".plus_file("path") == "this/is/path"[/code]. + // </summary> + public static string PlusFile(this string instance, string file) + { + if (instance.Length > 0 && instance[instance.Length - 1] == '/') + return instance + file; + return instance + "/" + file; + } + + // <summary> + // Replace occurrences of a substring for different ones inside the string. + // </summary> + public static string Replace(this string instance, string what, string forwhat) + { + return instance.Replace(what, forwhat); + } + + // <summary> + // Replace occurrences of a substring for different ones inside the string, but search case-insensitive. + // </summary> + public static string ReplaceN(this string instance, string what, string forwhat) + { + return Regex.Replace(instance, what, forwhat, RegexOptions.IgnoreCase); + } + + // <summary> + // Perform a search for a substring, but start from the end of the string instead of the beginning. + // </summary> + public static int RFind(this string instance, string what, int from = -1) + { + return godot_icall_String_rfind(instance, what, from); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_String_rfind(string str, string what, int from); + + // <summary> + // Perform a search for a substring, but start from the end of the string instead of the beginning. Also search case-insensitive. + // </summary> + public static int RFindN(this string instance, string what, int from = -1) + { + return godot_icall_String_rfindn(instance, what, from); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_String_rfindn(string str, string what, int from); + + // <summary> + // Return the right side of the string from a given position. + // </summary> + public static string Right(this string instance, int pos) + { + if (pos >= instance.Length) + return instance; + + if (pos < 0) + return string.Empty; + + return instance.Substring(pos, instance.Length - pos); + } + + public static byte[] SHA256Buffer(this string instance) + { + return godot_icall_String_sha256_buffer(instance); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static byte[] godot_icall_String_sha256_buffer(string str); + + // <summary> + // Return the SHA-256 hash of the string as a string. + // </summary> + public static string SHA256Text(this string instance) + { + return godot_icall_String_sha256_text(instance); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_String_sha256_text(string str); + + // <summary> + // Return the similarity index of the text compared to this string. 1 means totally similar and 0 means totally dissimilar. + // </summary> + public static float Similarity(this string instance, string text) + { + if (instance == text) + { + // Equal strings are totally similar + return 1.0f; + } + if (instance.Length < 2 || text.Length < 2) + { + // No way to calculate similarity without a single bigram + return 0.0f; + } + + string[] sourceBigrams = instance.Bigrams(); + string[] targetBigrams = text.Bigrams(); + + int sourceSize = sourceBigrams.Length; + int targetSize = targetBigrams.Length; + + float sum = sourceSize + targetSize; + float inter = 0; + + for (int i = 0; i < sourceSize; i++) + { + for (int j = 0; j < targetSize; j++) + { + if (sourceBigrams[i] == targetBigrams[j]) + { + inter++; + break; + } + } + } + + return 2.0f * inter / sum; + } + + // <summary> + // Split the string by a divisor string, return an array of the substrings. Example "One,Two,Three" will return ["One","Two","Three"] if split by ",". + // </summary> + public static string[] Split(this string instance, string divisor, bool allowEmpty = true) + { + return instance.Split(new[] { divisor }, StringSplitOptions.RemoveEmptyEntries); + } + + // <summary> + // Split the string in floats by using a divisor string, return an array of the substrings. Example "1,2.5,3" will return [1,2.5,3] if split by ",". + // </summary> + public static float[] SplitFloats(this string instance, string divisor, bool allowEmpty = true) + { + var ret = new List<float>(); + int from = 0; + int len = instance.Length; + + while (true) + { + int end = instance.Find(divisor, from); + if (end < 0) + end = len; + if (allowEmpty || end > from) + ret.Add(float.Parse(instance.Substring(from))); + if (end == len) + break; + + from = end + divisor.Length; + } + + return ret.ToArray(); + } + + private static readonly char[] _nonPrintable = { + (char)00, (char)01, (char)02, (char)03, (char)04, (char)05, + (char)06, (char)07, (char)08, (char)09, (char)10, (char)11, + (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, + (char)18, (char)19, (char)20, (char)21, (char)22, (char)23, + (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, + (char)30, (char)31, (char)32 + }; + + // <summary> + // Return a copy of the string stripped of any non-printable character at the beginning and the end. The optional arguments are used to toggle stripping on the left and right edges respectively. + // </summary> + public static string StripEdges(this string instance, bool left = true, bool right = true) + { + if (left) + { + if (right) + return instance.Trim(_nonPrintable); + return instance.TrimStart(_nonPrintable); + } + + return instance.TrimEnd(_nonPrintable); + } + + // <summary> + // Return part of the string from the position [code]from[/code], with length [code]len[/code]. + // </summary> + public static string Substr(this string instance, int from, int len) + { + return instance.Substring(from, len); + } + + // <summary> + // Convert the String (which is a character array) to PoolByteArray (which is an array of bytes). The conversion is speeded up in comparison to to_utf8() with the assumption that all the characters the String contains are only ASCII characters. + // </summary> + public static byte[] ToAscii(this string instance) + { + return Encoding.ASCII.GetBytes(instance); + } + + // <summary> + // Convert a string, containing a decimal number, into a [code]float[/code]. + // </summary> + public static float ToFloat(this string instance) + { + return float.Parse(instance); + } + + // <summary> + // Convert a string, containing an integer number, into an [code]int[/code]. + // </summary> + public static int ToInt(this string instance) + { + return int.Parse(instance); + } + + // <summary> + // Return the string converted to lowercase. + // </summary> + public static string ToLower(this string instance) + { + return instance.ToLower(); + } + + // <summary> + // Return the string converted to uppercase. + // </summary> + public static string ToUpper(this string instance) + { + return instance.ToUpper(); + } + + // <summary> + // Convert the String (which is an array of characters) to PoolByteArray (which is an array of bytes). The conversion is a bit slower than to_ascii(), but supports all UTF-8 characters. Therefore, you should prefer this function over to_ascii(). + // </summary> + public static byte[] ToUTF8(this string instance) + { + return Encoding.UTF8.GetBytes(instance); + } + + // <summary> + // Return a copy of the string with special characters escaped using the XML standard. + // </summary> + public static string XMLEscape(this string instance) + { + return SecurityElement.Escape(instance); + } + + // <summary> + // Return a copy of the string with escaped characters replaced by their meanings according to the XML standard. + // </summary> + public static string XMLUnescape(this string instance) + { + return SecurityElement.FromString(instance).Text; + } + } +} diff --git a/modules/mono/glue/Managed/Files/Transform.cs b/modules/mono/glue/Managed/Files/Transform.cs new file mode 100644 index 0000000000..e432d5b52c --- /dev/null +++ b/modules/mono/glue/Managed/Files/Transform.cs @@ -0,0 +1,191 @@ +using System; +using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + +namespace Godot +{ + [StructLayout(LayoutKind.Sequential)] + public struct Transform : IEquatable<Transform> + { + public Basis basis; + public Vector3 origin; + + public Transform AffineInverse() + { + Basis basisInv = basis.Inverse(); + return new Transform(basisInv, basisInv.Xform(-origin)); + } + + public Transform Inverse() + { + Basis basisTr = basis.Transposed(); + return new Transform(basisTr, basisTr.Xform(-origin)); + } + + public Transform LookingAt(Vector3 target, Vector3 up) + { + var t = this; + t.SetLookAt(origin, target, up); + return t; + } + + public Transform Orthonormalized() + { + return new Transform(basis.Orthonormalized(), origin); + } + + public Transform Rotated(Vector3 axis, real_t phi) + { + return new Transform(new Basis(axis, phi), new Vector3()) * this; + } + + public Transform Scaled(Vector3 scale) + { + return new Transform(basis.Scaled(scale), origin * scale); + } + + public void SetLookAt(Vector3 eye, Vector3 target, Vector3 up) + { + // Make rotation matrix + // Z vector + Vector3 zAxis = eye - target; + + zAxis.Normalize(); + + Vector3 yAxis = up; + + Vector3 xAxis = yAxis.Cross(zAxis); + + // Recompute Y = Z cross X + yAxis = zAxis.Cross(xAxis); + + xAxis.Normalize(); + yAxis.Normalize(); + + basis = Basis.CreateFromAxes(xAxis, yAxis, zAxis); + + origin = eye; + } + + public Transform Translated(Vector3 ofs) + { + return new Transform(basis, new Vector3 + ( + origin[0] += basis[0].Dot(ofs), + origin[1] += basis[1].Dot(ofs), + origin[2] += basis[2].Dot(ofs) + )); + } + + public Vector3 Xform(Vector3 v) + { + return new Vector3 + ( + basis[0].Dot(v) + origin.x, + basis[1].Dot(v) + origin.y, + basis[2].Dot(v) + origin.z + ); + } + + public Vector3 XformInv(Vector3 v) + { + Vector3 vInv = v - origin; + + return new Vector3 + ( + basis[0, 0] * vInv.x + basis[1, 0] * vInv.y + basis[2, 0] * vInv.z, + basis[0, 1] * vInv.x + basis[1, 1] * vInv.y + basis[2, 1] * vInv.z, + basis[0, 2] * vInv.x + basis[1, 2] * vInv.y + basis[2, 2] * vInv.z + ); + } + + // Constants + private static readonly Transform _identity = new Transform(Basis.Identity, Vector3.Zero); + private static readonly Transform _flipX = new Transform(new Basis(new Vector3(-1, 0, 0), new Vector3(0, 1, 0), new Vector3(0, 0, 1)), Vector3.Zero); + private static readonly Transform _flipY = new Transform(new Basis(new Vector3(1, 0, 0), new Vector3(0, -1, 0), new Vector3(0, 0, 1)), Vector3.Zero); + private static readonly Transform _flipZ = new Transform(new Basis(new Vector3(1, 0, 0), new Vector3(0, 1, 0), new Vector3(0, 0, -1)), Vector3.Zero); + + public static Transform Identity { get { return _identity; } } + public static Transform FlipX { get { return _flipX; } } + public static Transform FlipY { get { return _flipY; } } + public static Transform FlipZ { get { return _flipZ; } } + + // Constructors + public Transform(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis, Vector3 origin) + { + basis = Basis.CreateFromAxes(xAxis, yAxis, zAxis); + this.origin = origin; + } + + public Transform(Quat quat, Vector3 origin) + { + basis = new Basis(quat); + this.origin = origin; + } + + public Transform(Basis basis, Vector3 origin) + { + this.basis = basis; + this.origin = origin; + } + + public static Transform operator *(Transform left, Transform right) + { + left.origin = left.Xform(right.origin); + left.basis *= right.basis; + return left; + } + + public static bool operator ==(Transform left, Transform right) + { + return left.Equals(right); + } + + public static bool operator !=(Transform left, Transform right) + { + return !left.Equals(right); + } + + public override bool Equals(object obj) + { + if (obj is Transform) + { + return Equals((Transform)obj); + } + + return false; + } + + public bool Equals(Transform other) + { + return basis.Equals(other.basis) && origin.Equals(other.origin); + } + + public override int GetHashCode() + { + return basis.GetHashCode() ^ origin.GetHashCode(); + } + + public override string ToString() + { + return String.Format("{0} - {1}", new object[] + { + basis.ToString(), + origin.ToString() + }); + } + + public string ToString(string format) + { + return String.Format("{0} - {1}", new object[] + { + basis.ToString(format), + origin.ToString(format) + }); + } + } +} diff --git a/modules/mono/glue/Managed/Files/Transform2D.cs b/modules/mono/glue/Managed/Files/Transform2D.cs new file mode 100644 index 0000000000..8d30833066 --- /dev/null +++ b/modules/mono/glue/Managed/Files/Transform2D.cs @@ -0,0 +1,360 @@ +using System; +using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + +namespace Godot +{ + [StructLayout(LayoutKind.Sequential)] + public struct Transform2D : IEquatable<Transform2D> + { + public Vector2 x; + public Vector2 y; + public Vector2 o; + + public Vector2 Origin + { + get { return o; } + } + + public real_t Rotation + { + get { return Mathf.Atan2(y.x, o.y); } + } + + public Vector2 Scale + { + get { return new Vector2(x.Length(), y.Length()); } + } + + public Vector2 this[int index] + { + get + { + switch (index) + { + case 0: + return x; + case 1: + return y; + case 2: + return o; + default: + throw new IndexOutOfRangeException(); + } + } + set + { + switch (index) + { + case 0: + x = value; + return; + case 1: + y = value; + return; + case 2: + o = value; + return; + default: + throw new IndexOutOfRangeException(); + } + } + } + + + public real_t this[int index, int axis] + { + get + { + switch (index) + { + case 0: + return x[axis]; + case 1: + return y[axis]; + default: + throw new IndexOutOfRangeException(); + } + } + set + { + switch (index) + { + case 0: + x[axis] = value; + return; + case 1: + y[axis] = value; + return; + default: + throw new IndexOutOfRangeException(); + } + } + } + + public Transform2D AffineInverse() + { + var inv = this; + + real_t det = this[0, 0] * this[1, 1] - this[1, 0] * this[0, 1]; + + if (det == 0) + { + return new Transform2D + ( + float.NaN, float.NaN, + float.NaN, float.NaN, + float.NaN, float.NaN + ); + } + + real_t idet = 1.0f / det; + + real_t temp = this[0, 0]; + this[0, 0] = this[1, 1]; + this[1, 1] = temp; + + this[0] *= new Vector2(idet, -idet); + this[1] *= new Vector2(-idet, idet); + + this[2] = BasisXform(-this[2]); + + return inv; + } + + public Vector2 BasisXform(Vector2 v) + { + return new Vector2(Tdotx(v), Tdoty(v)); + } + + public Vector2 BasisXformInv(Vector2 v) + { + return new Vector2(x.Dot(v), y.Dot(v)); + } + + public Transform2D InterpolateWith(Transform2D m, real_t c) + { + real_t r1 = Rotation; + real_t r2 = m.Rotation; + + Vector2 s1 = Scale; + Vector2 s2 = m.Scale; + + // Slerp rotation + var v1 = new Vector2(Mathf.Cos(r1), Mathf.Sin(r1)); + var v2 = new Vector2(Mathf.Cos(r2), Mathf.Sin(r2)); + + real_t dot = v1.Dot(v2); + + // Clamp dot to [-1, 1] + dot = dot < -1.0f ? -1.0f : (dot > 1.0f ? 1.0f : dot); + + Vector2 v; + + if (dot > 0.9995f) + { + // Linearly interpolate to avoid numerical precision issues + v = v1.LinearInterpolate(v2, c).Normalized(); + } + else + { + real_t angle = c * 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; + + // Construct matrix + var res = new Transform2D(Mathf.Atan2(v.y, v.x), p1.LinearInterpolate(p2, c)); + Vector2 scale = s1.LinearInterpolate(s2, c); + res.x *= scale; + res.y *= scale; + + return res; + } + + public Transform2D Inverse() + { + var inv = this; + + // Swap + real_t temp = inv.x.y; + inv.x.y = inv.y.x; + inv.y.x = temp; + + inv.o = inv.BasisXform(-inv.o); + + return inv; + } + + public Transform2D Orthonormalized() + { + var on = this; + + Vector2 onX = on.x; + Vector2 onY = on.y; + + onX.Normalize(); + onY = onY - onX * onX.Dot(onY); + onY.Normalize(); + + on.x = onX; + on.y = onY; + + return on; + } + + public Transform2D Rotated(real_t phi) + { + return this * new Transform2D(phi, new Vector2()); + } + + public Transform2D Scaled(Vector2 scale) + { + var copy = this; + copy.x *= scale; + copy.y *= scale; + copy.o *= scale; + return copy; + } + + private real_t Tdotx(Vector2 with) + { + return this[0, 0] * with[0] + this[1, 0] * with[1]; + } + + private real_t Tdoty(Vector2 with) + { + return this[0, 1] * with[0] + this[1, 1] * with[1]; + } + + public Transform2D Translated(Vector2 offset) + { + var copy = this; + copy.o += copy.BasisXform(offset); + return copy; + } + + public Vector2 Xform(Vector2 v) + { + return new Vector2(Tdotx(v), Tdoty(v)) + o; + } + + public Vector2 XformInv(Vector2 v) + { + Vector2 vInv = v - o; + return new Vector2(x.Dot(vInv), y.Dot(vInv)); + } + + // Constants + private static readonly Transform2D _identity = new Transform2D(new Vector2(1f, 0f), new Vector2(0f, 1f), Vector2.Zero); + private static readonly Transform2D _flipX = new Transform2D(new Vector2(-1f, 0f), new Vector2(0f, 1f), Vector2.Zero); + private static readonly Transform2D _flipY = new Transform2D(new Vector2(1f, 0f), new Vector2(0f, -1f), Vector2.Zero); + + public static Transform2D Identity { get { return _identity; } } + public static Transform2D FlipX { get { return _flipX; } } + public static Transform2D FlipY { get { return _flipY; } } + + // Constructors + public Transform2D(Vector2 xAxis, Vector2 yAxis, Vector2 origin) + { + x = xAxis; + y = yAxis; + o = origin; + } + + 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); + y = new Vector2(yx, yy); + o = new Vector2(ox, oy); + } + + public Transform2D(real_t rot, Vector2 pos) + { + real_t cr = Mathf.Cos(rot); + real_t sr = Mathf.Sin(rot); + x.x = cr; + y.y = cr; + x.y = -sr; + y.x = sr; + o = pos; + } + + public static Transform2D operator *(Transform2D left, Transform2D right) + { + left.o = left.Xform(right.o); + + real_t x0, x1, y0, y1; + + x0 = left.Tdotx(right.x); + x1 = left.Tdoty(right.x); + y0 = left.Tdotx(right.y); + y1 = left.Tdoty(right.y); + + left.x.x = x0; + left.x.y = x1; + left.y.x = y0; + left.y.y = y1; + + return left; + } + + public static bool operator ==(Transform2D left, Transform2D right) + { + return left.Equals(right); + } + + public static bool operator !=(Transform2D left, Transform2D right) + { + return !left.Equals(right); + } + + public override bool Equals(object obj) + { + if (obj is Transform2D) + { + return Equals((Transform2D)obj); + } + + return false; + } + + public bool Equals(Transform2D other) + { + return x.Equals(other.x) && y.Equals(other.y) && o.Equals(other.o); + } + + public override int GetHashCode() + { + return x.GetHashCode() ^ y.GetHashCode() ^ o.GetHashCode(); + } + + public override string ToString() + { + return String.Format("({0}, {1}, {2})", new object[] + { + x.ToString(), + y.ToString(), + o.ToString() + }); + } + + public string ToString(string format) + { + return String.Format("({0}, {1}, {2})", new object[] + { + x.ToString(format), + y.ToString(format), + o.ToString(format) + }); + } + } +} diff --git a/modules/mono/glue/Managed/Files/Vector2.cs b/modules/mono/glue/Managed/Files/Vector2.cs new file mode 100644 index 0000000000..080b8802ba --- /dev/null +++ b/modules/mono/glue/Managed/Files/Vector2.cs @@ -0,0 +1,417 @@ +// file: core/math/math_2d.h +// commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451 +// file: core/math/math_2d.cpp +// commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451 +// file: core/variant_call.cpp +// commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685 +using System; +using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + +namespace Godot +{ + [StructLayout(LayoutKind.Sequential)] + public struct Vector2 : IEquatable<Vector2> + { + public real_t x; + public real_t y; + + public real_t this[int index] + { + get + { + switch (index) + { + case 0: + return x; + case 1: + return y; + default: + throw new IndexOutOfRangeException(); + } + } + set + { + switch (index) + { + case 0: + x = value; + return; + case 1: + y = value; + return; + default: + throw new IndexOutOfRangeException(); + } + } + } + + internal void Normalize() + { + real_t length = x * x + y * y; + + if (length != 0f) + { + length = Mathf.Sqrt(length); + x /= length; + y /= length; + } + } + + public real_t Cross(Vector2 b) + { + return x * b.y - y * b.x; + } + + public Vector2 Abs() + { + return new Vector2(Mathf.Abs(x), Mathf.Abs(y)); + } + + public real_t Angle() + { + return Mathf.Atan2(y, x); + } + + public real_t AngleTo(Vector2 to) + { + return Mathf.Atan2(Cross(to), Dot(to)); + } + + public real_t AngleToPoint(Vector2 to) + { + return Mathf.Atan2(x - to.x, y - to.y); + } + + public real_t Aspect() + { + return x / y; + } + + public Vector2 Bounce(Vector2 n) + { + return -Reflect(n); + } + + public Vector2 Ceil() + { + return new Vector2(Mathf.Ceil(x), Mathf.Ceil(y)); + } + + public Vector2 Clamped(real_t length) + { + var v = this; + real_t l = Length(); + + if (l > 0 && length < l) + { + v /= l; + v *= length; + } + + return v; + } + + public Vector2 CubicInterpolate(Vector2 b, Vector2 preA, Vector2 postB, real_t t) + { + var p0 = preA; + var p1 = this; + var p2 = b; + var p3 = postB; + + real_t t2 = t * t; + real_t t3 = t2 * t; + + return 0.5f * (p1 * 2.0f + + (-p0 + p2) * t + + (2.0f * p0 - 5.0f * p1 + 4 * p2 - p3) * t2 + + (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3); + } + + public real_t DistanceSquaredTo(Vector2 to) + { + return (x - to.x) * (x - to.x) + (y - to.y) * (y - to.y); + } + + public real_t DistanceTo(Vector2 to) + { + return Mathf.Sqrt((x - to.x) * (x - to.x) + (y - to.y) * (y - to.y)); + } + + public real_t Dot(Vector2 with) + { + return x * with.x + y * with.y; + } + + public Vector2 Floor() + { + return new Vector2(Mathf.Floor(x), Mathf.Floor(y)); + } + + public bool IsNormalized() + { + return Mathf.Abs(LengthSquared() - 1.0f) < Mathf.Epsilon; + } + + public real_t Length() + { + return Mathf.Sqrt(x * x + y * y); + } + + public real_t LengthSquared() + { + return x * x + y * y; + } + + public Vector2 LinearInterpolate(Vector2 b, real_t t) + { + var res = this; + + res.x += t * (b.x - x); + res.y += t * (b.y - y); + + return res; + } + + public Vector2 Normalized() + { + var result = this; + result.Normalize(); + return result; + } + + public Vector2 Project(Vector2 onNormal) + { + return onNormal * (Dot(onNormal) / onNormal.LengthSquared()); + } + + public Vector2 Reflect(Vector2 n) + { + return 2.0f * n * Dot(n) - this; + } + + public Vector2 Rotated(real_t phi) + { + real_t rads = Angle() + phi; + return new Vector2(Mathf.Cos(rads), Mathf.Sin(rads)) * Length(); + } + + public Vector2 Round() + { + return new Vector2(Mathf.Round(x), Mathf.Round(y)); + } + + public void Set(real_t x, real_t y) + { + this.x = x; + this.y = y; + } + public void Set(Vector2 v) + { + x = v.x; + y = v.y; + } + + public Vector2 Slerp(Vector2 b, real_t t) + { + real_t theta = AngleTo(b); + return Rotated(theta * t); + } + + public Vector2 Slide(Vector2 n) + { + return this - n * Dot(n); + } + + public Vector2 Snapped(Vector2 by) + { + return new Vector2(Mathf.Stepify(x, by.x), Mathf.Stepify(y, by.y)); + } + + public Vector2 Tangent() + { + return new Vector2(y, -x); + } + + // Constants + private static readonly Vector2 _zero = new Vector2(0, 0); + private static readonly Vector2 _one = new Vector2(1, 1); + private static readonly Vector2 _negOne = new Vector2(-1, -1); + private static readonly Vector2 _inf = new Vector2(Mathf.Inf, Mathf.Inf); + + private static readonly Vector2 _up = new Vector2(0, -1); + private static readonly Vector2 _down = new Vector2(0, 1); + private static readonly Vector2 _right = new Vector2(1, 0); + private static readonly Vector2 _left = new Vector2(-1, 0); + + public static Vector2 Zero { get { return _zero; } } + public static Vector2 NegOne { get { return _negOne; } } + public static Vector2 One { get { return _one; } } + public static Vector2 Inf { get { return _inf; } } + + public static Vector2 Up { get { return _up; } } + public static Vector2 Down { get { return _down; } } + public static Vector2 Right { get { return _right; } } + public static Vector2 Left { get { return _left; } } + + // Constructors + public Vector2(real_t x, real_t y) + { + this.x = x; + this.y = y; + } + public Vector2(Vector2 v) + { + x = v.x; + y = v.y; + } + + public static Vector2 operator +(Vector2 left, Vector2 right) + { + left.x += right.x; + left.y += right.y; + return left; + } + + public static Vector2 operator -(Vector2 left, Vector2 right) + { + left.x -= right.x; + left.y -= right.y; + return left; + } + + public static Vector2 operator -(Vector2 vec) + { + vec.x = -vec.x; + vec.y = -vec.y; + return vec; + } + + public static Vector2 operator *(Vector2 vec, real_t scale) + { + vec.x *= scale; + vec.y *= scale; + return vec; + } + + public static Vector2 operator *(real_t scale, Vector2 vec) + { + vec.x *= scale; + vec.y *= scale; + return vec; + } + + public static Vector2 operator *(Vector2 left, Vector2 right) + { + left.x *= right.x; + left.y *= right.y; + return left; + } + + public static Vector2 operator /(Vector2 vec, real_t scale) + { + vec.x /= scale; + vec.y /= scale; + return vec; + } + + public static Vector2 operator /(Vector2 left, Vector2 right) + { + left.x /= right.x; + left.y /= right.y; + return left; + } + + public static bool operator ==(Vector2 left, Vector2 right) + { + return left.Equals(right); + } + + public static bool operator !=(Vector2 left, Vector2 right) + { + return !left.Equals(right); + } + + public static bool operator <(Vector2 left, Vector2 right) + { + if (left.x.Equals(right.x)) + { + return left.y < right.y; + } + + return left.x < right.x; + } + + public static bool operator >(Vector2 left, Vector2 right) + { + if (left.x.Equals(right.x)) + { + return left.y > right.y; + } + + return left.x > right.x; + } + + public static bool operator <=(Vector2 left, Vector2 right) + { + if (left.x.Equals(right.x)) + { + return left.y <= right.y; + } + + return left.x <= right.x; + } + + public static bool operator >=(Vector2 left, Vector2 right) + { + if (left.x.Equals(right.x)) + { + return left.y >= right.y; + } + + return left.x >= right.x; + } + + public override bool Equals(object obj) + { + if (obj is Vector2) + { + return Equals((Vector2)obj); + } + + return false; + } + + public bool Equals(Vector2 other) + { + return x == other.x && y == other.y; + } + + public override int GetHashCode() + { + return y.GetHashCode() ^ x.GetHashCode(); + } + + public override string ToString() + { + return String.Format("({0}, {1})", new object[] + { + x.ToString(), + y.ToString() + }); + } + + public string ToString(string format) + { + return String.Format("({0}, {1})", new object[] + { + x.ToString(format), + y.ToString(format) + }); + } + } +} diff --git a/modules/mono/glue/Managed/Files/Vector3.cs b/modules/mono/glue/Managed/Files/Vector3.cs new file mode 100644 index 0000000000..6fffe5e4d6 --- /dev/null +++ b/modules/mono/glue/Managed/Files/Vector3.cs @@ -0,0 +1,481 @@ +// file: core/math/vector3.h +// commit: bd282ff43f23fe845f29a3e25c8efc01bd65ffb0 +// file: core/math/vector3.cpp +// commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451 +// file: core/variant_call.cpp +// commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685 +using System; +using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + +namespace Godot +{ + [StructLayout(LayoutKind.Sequential)] + public struct Vector3 : IEquatable<Vector3> + { + public enum Axis + { + X = 0, + Y, + Z + } + + public real_t x; + public real_t y; + public real_t z; + + public real_t this[int index] + { + get + { + switch (index) + { + case 0: + return x; + case 1: + return y; + case 2: + return z; + default: + throw new IndexOutOfRangeException(); + } + } + set + { + switch (index) + { + case 0: + x = value; + return; + case 1: + y = value; + return; + case 2: + z = value; + return; + default: + throw new IndexOutOfRangeException(); + } + } + } + + internal void Normalize() + { + real_t length = Length(); + + if (length == 0f) + { + x = y = z = 0f; + } + else + { + x /= length; + y /= length; + z /= length; + } + } + + public Vector3 Abs() + { + return new Vector3(Mathf.Abs(x), Mathf.Abs(y), Mathf.Abs(z)); + } + + public real_t AngleTo(Vector3 to) + { + return Mathf.Atan2(Cross(to).Length(), Dot(to)); + } + + public Vector3 Bounce(Vector3 n) + { + return -Reflect(n); + } + + public Vector3 Ceil() + { + return new Vector3(Mathf.Ceil(x), Mathf.Ceil(y), Mathf.Ceil(z)); + } + + public Vector3 Cross(Vector3 b) + { + return new Vector3 + ( + y * b.z - z * b.y, + z * b.x - x * b.z, + x * b.y - y * b.x + ); + } + + public Vector3 CubicInterpolate(Vector3 b, Vector3 preA, Vector3 postB, real_t t) + { + var p0 = preA; + var p1 = this; + var p2 = b; + var p3 = postB; + + real_t t2 = t * t; + real_t t3 = t2 * t; + + return 0.5f * ( + p1 * 2.0f + (-p0 + p2) * t + + (2.0f * p0 - 5.0f * p1 + 4f * p2 - p3) * t2 + + (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3 + ); + } + + public real_t DistanceSquaredTo(Vector3 b) + { + return (b - this).LengthSquared(); + } + + public real_t DistanceTo(Vector3 b) + { + return (b - this).Length(); + } + + public real_t Dot(Vector3 b) + { + return x * b.x + y * b.y + z * b.z; + } + + public Vector3 Floor() + { + return new Vector3(Mathf.Floor(x), Mathf.Floor(y), Mathf.Floor(z)); + } + + public Vector3 Inverse() + { + return new Vector3(1.0f / x, 1.0f / y, 1.0f / z); + } + + public bool IsNormalized() + { + return Mathf.Abs(LengthSquared() - 1.0f) < Mathf.Epsilon; + } + + public real_t Length() + { + real_t x2 = x * x; + real_t y2 = y * y; + real_t z2 = z * z; + + return Mathf.Sqrt(x2 + y2 + z2); + } + + public real_t LengthSquared() + { + real_t x2 = x * x; + real_t y2 = y * y; + real_t z2 = z * z; + + return x2 + y2 + z2; + } + + public Vector3 LinearInterpolate(Vector3 b, real_t t) + { + return new Vector3 + ( + x + t * (b.x - x), + y + t * (b.y - y), + z + t * (b.z - z) + ); + } + + 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); + } + + public Vector3 Normalized() + { + var v = this; + v.Normalize(); + return v; + } + + public Basis Outer(Vector3 b) + { + return new Basis( + new Vector3(x * b.x, x * b.y, x * b.z), + new Vector3(y * b.x, y * b.y, y * b.z), + new Vector3(z * b.x, z * b.y, z * b.z) + ); + } + + public Vector3 Project(Vector3 onNormal) + { + return onNormal * (Dot(onNormal) / onNormal.LengthSquared()); + } + + public Vector3 Reflect(Vector3 n) + { +#if DEBUG + if (!n.IsNormalized()) + throw new ArgumentException(String.Format("{0} is not normalized", n), nameof(n)); +#endif + return 2.0f * n * Dot(n) - this; + } + + 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); + } + + public void Set(real_t x, real_t y, real_t z) + { + this.x = x; + this.y = y; + this.z = z; + } + public void Set(Vector3 v) + { + x = v.x; + y = v.y; + z = v.z; + } + + public Vector3 Slerp(Vector3 b, real_t t) + { + real_t theta = AngleTo(b); + return Rotated(Cross(b), theta * t); + } + + public Vector3 Slide(Vector3 n) + { + return this - n * Dot(n); + } + + public Vector3 Snapped(Vector3 by) + { + return new Vector3 + ( + Mathf.Stepify(x, by.x), + Mathf.Stepify(y, by.y), + Mathf.Stepify(z, by.z) + ); + } + + public Basis ToDiagonalMatrix() + { + return new Basis( + x, 0f, 0f, + 0f, y, 0f, + 0f, 0f, z + ); + } + + // Constants + private static readonly Vector3 _zero = new Vector3(0, 0, 0); + private static readonly Vector3 _one = new Vector3(1, 1, 1); + private static readonly Vector3 _negOne = new Vector3(-1, -1, -1); + private static readonly Vector3 _inf = new Vector3(Mathf.Inf, Mathf.Inf, Mathf.Inf); + + private static readonly Vector3 _up = new Vector3(0, 1, 0); + private static readonly Vector3 _down = new Vector3(0, -1, 0); + private static readonly Vector3 _right = new Vector3(1, 0, 0); + private static readonly Vector3 _left = new Vector3(-1, 0, 0); + private static readonly Vector3 _forward = new Vector3(0, 0, -1); + private static readonly Vector3 _back = new Vector3(0, 0, 1); + + public static Vector3 Zero { get { return _zero; } } + public static Vector3 One { get { return _one; } } + public static Vector3 NegOne { get { return _negOne; } } + public static Vector3 Inf { get { return _inf; } } + + public static Vector3 Up { get { return _up; } } + public static Vector3 Down { get { return _down; } } + public static Vector3 Right { get { return _right; } } + public static Vector3 Left { get { return _left; } } + public static Vector3 Forward { get { return _forward; } } + public static Vector3 Back { get { return _back; } } + + // Constructors + public Vector3(real_t x, real_t y, real_t z) + { + this.x = x; + this.y = y; + this.z = z; + } + public Vector3(Vector3 v) + { + x = v.x; + y = v.y; + z = v.z; + } + + public static Vector3 operator +(Vector3 left, Vector3 right) + { + left.x += right.x; + left.y += right.y; + left.z += right.z; + return left; + } + + public static Vector3 operator -(Vector3 left, Vector3 right) + { + left.x -= right.x; + left.y -= right.y; + left.z -= right.z; + return left; + } + + public static Vector3 operator -(Vector3 vec) + { + vec.x = -vec.x; + vec.y = -vec.y; + vec.z = -vec.z; + return vec; + } + + public static Vector3 operator *(Vector3 vec, real_t scale) + { + vec.x *= scale; + vec.y *= scale; + vec.z *= scale; + return vec; + } + + public static Vector3 operator *(real_t scale, Vector3 vec) + { + vec.x *= scale; + vec.y *= scale; + vec.z *= scale; + return vec; + } + + public static Vector3 operator *(Vector3 left, Vector3 right) + { + left.x *= right.x; + left.y *= right.y; + left.z *= right.z; + return left; + } + + public static Vector3 operator /(Vector3 vec, real_t scale) + { + vec.x /= scale; + vec.y /= scale; + vec.z /= scale; + return vec; + } + + public static Vector3 operator /(Vector3 left, Vector3 right) + { + left.x /= right.x; + left.y /= right.y; + left.z /= right.z; + return left; + } + + public static bool operator ==(Vector3 left, Vector3 right) + { + return left.Equals(right); + } + + public static bool operator !=(Vector3 left, Vector3 right) + { + return !left.Equals(right); + } + + public static bool operator <(Vector3 left, Vector3 right) + { + if (left.x == right.x) + { + if (left.y == right.y) + return left.z < right.z; + return left.y < right.y; + } + + return left.x < right.x; + } + + public static bool operator >(Vector3 left, Vector3 right) + { + if (left.x == right.x) + { + if (left.y == right.y) + return left.z > right.z; + return left.y > right.y; + } + + return left.x > right.x; + } + + public static bool operator <=(Vector3 left, Vector3 right) + { + if (left.x == right.x) + { + if (left.y == right.y) + return left.z <= right.z; + return left.y < right.y; + } + + return left.x < right.x; + } + + public static bool operator >=(Vector3 left, Vector3 right) + { + if (left.x == right.x) + { + if (left.y == right.y) + return left.z >= right.z; + return left.y > right.y; + } + + return left.x > right.x; + } + + public override bool Equals(object obj) + { + if (obj is Vector3) + { + return Equals((Vector3)obj); + } + + return false; + } + + public bool Equals(Vector3 other) + { + return x == other.x && y == other.y && z == other.z; + } + + public override int GetHashCode() + { + return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode(); + } + + public override string ToString() + { + return String.Format("({0}, {1}, {2})", new object[] + { + x.ToString(), + y.ToString(), + z.ToString() + }); + } + + public string ToString(string format) + { + return String.Format("({0}, {1}, {2})", new object[] + { + x.ToString(format), + y.ToString(format), + z.ToString(format) + }); + } + } +} |