Compare commits
74 Commits
tests
...
2ef7fa6577
| Author | SHA1 | Date | |
|---|---|---|---|
| 2ef7fa6577 | |||
| 193b2c5af0 | |||
| a859c0cbff | |||
| fba64c3854 | |||
| a975cbb56b | |||
| 1664a9ccf7 | |||
| 988a6f67f2 | |||
| 2f32038f04 | |||
| 4b756fa232 | |||
| 0db2cae1bb | |||
| 896f7876c1 | |||
| 56d3112e35 | |||
| 32ec2325dc | |||
| b42f1f1881 | |||
| f8096377b2 | |||
| a9fc819268 | |||
| 1d63391975 | |||
| 61ff0887e2 | |||
| 16344dccc7 | |||
| dc4bea3eef | |||
| d1b2723a70 | |||
| 2f5c04e66b | |||
| f753da1f87 | |||
| 6901159106 | |||
| 7469c9ab0c | |||
| ede90adbdc | |||
| eeaca3a6c7 | |||
| 3b984a0a4b | |||
| c5afb70b18 | |||
| 9d2a192f04 | |||
| 598debc233 | |||
| ab05a89175 | |||
| dbd15cbbc2 | |||
| e051f5cfb4 | |||
| e70b7f112f | |||
| f55ba499b6 | |||
| b75f30f864 | |||
| 6f1f30bd53 | |||
| 92a5c276a4 | |||
| 69bc6573d1 | |||
| 28bc022587 | |||
| 25db60e436 | |||
| 7c62440bba | |||
| 4bec7bce6e | |||
| 8d31372c24 | |||
| a2e704916e | |||
| c7d170fad9 | |||
| 9ccf7b754d | |||
| e3d4899112 | |||
| 566c16d09c | |||
| ae9d4f02ef | |||
| e77772cbc2 | |||
| 4c542df401 | |||
| 28ca343b43 | |||
| 651b0614c4 | |||
| f47488c6f1 | |||
| 6d159330a1 | |||
| 8e314f3269 | |||
| f5a7077570 | |||
| 746d29fb7a | |||
| cf68f6ca6f | |||
| a4b83679b1 | |||
| a31b39fd1d | |||
| 0205354202 | |||
| 949dfeb3d9 | |||
| 620ef911fa | |||
| efed24de20 | |||
| 3912706d27 | |||
| d78c42a653 | |||
| b04e0f81cd | |||
| 65dcb0c564 | |||
| 3d183b21cd | |||
| 1644a751bb | |||
| 6631cae7b0 |
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -1,3 +1,3 @@
|
|||||||
[submodule "Engine.Serializers/YamlDotNet"]
|
[submodule "Engine.Integration/YamlDotNet"]
|
||||||
path = Engine.Serializers/YamlDotNet
|
path = Engine.Integration/YamlDotNet
|
||||||
url = git@github.com:Syntriax/YamlDotNet.git
|
url = https://github.com/Syntriax/YamlDotNet.git
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates the class implementing it has Assignable fields that are necessary for the engine to work properly.
|
/// Indicates the class implementing it has Assignable fields that are necessary for the engine to work properly.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IBehaviourController"/> field.
|
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IBehaviourController"/> field.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IEntity"/> field.
|
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IEntity"/> field.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IStateEnable"/> field.
|
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IStateEnable"/> field.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IUniverse"/> field.
|
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IUniverse"/> field.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IUniverseObject"/> field.
|
/// Indicates the object is an <see cref="IAssignable"/> with an assignable <see cref="IUniverseObject"/> field.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an entity which can be active or not.
|
/// Represents an entity which can be active or not.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a behaviour that any object in the engine that might use to interact with itself or other objects.
|
/// Represents a behaviour that any object in the engine that might use to interact with itself or other objects.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
public interface IBehaviour2D : IBehaviour
|
public interface IBehaviour2D : IBehaviour
|
||||||
{
|
{
|
||||||
|
|||||||
6
Engine.Core/Abstract/IBehaviour3D.cs
Normal file
6
Engine.Core/Abstract/IBehaviour3D.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Engine.Core;
|
||||||
|
|
||||||
|
public interface IBehaviour3D : IBehaviour
|
||||||
|
{
|
||||||
|
ITransform3D Transform { get; }
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a collector for the class type of <typeparamref name="T"/>.
|
/// Represents a collector for the class type of <typeparamref name="T"/>.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a controller for managing <see cref="IBehaviour"/>s. Connected to an <see cref="IUniverseObject"/>.
|
/// Represents a controller for managing <see cref="IBehaviour"/>s. Connected to an <see cref="IUniverseObject"/>.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a 2D camera in the engine.
|
/// Represents a 2D camera in the engine.
|
||||||
|
|||||||
26
Engine.Core/Abstract/ICamera3D.cs
Normal file
26
Engine.Core/Abstract/ICamera3D.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
namespace Engine.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a 3D camera in the engine.
|
||||||
|
/// </summary>
|
||||||
|
public interface ICamera3D : IBehaviour3D
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Field of View (FOV) value of the camera
|
||||||
|
/// </summary>
|
||||||
|
float FieldOfView { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a position from screen coordinates to a <see cref="Ray3D"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="screenPosition">The position in screen coordinates.</param>
|
||||||
|
/// <returns>The <see cref="Ray3D"/> originating from the camera to the screen position in world coordinates.</returns>
|
||||||
|
Ray3D ScreenToWorldRay(Vector2D screenPosition);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a position from world coordinates to screen coordinates.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="worldPosition">The position in world coordinates.</param>
|
||||||
|
/// <returns>The position in screen coordinates.</returns>
|
||||||
|
Vector2D WorldToScreenPosition(Vector3D worldPosition);
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
public interface ICoroutineYield
|
public interface ICoroutineYield
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a basic entity in the engine.
|
/// Represents a basic entity in the engine.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an entity that can be initialized and finalized. This information is useful for objects we know that are not in use and can be either recycled or dropped for garbage collection.
|
/// Represents an entity that can be initialized and finalized. This information is useful for objects we know that are not in use and can be either recycled or dropped for garbage collection.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an entity with a name.
|
/// Represents an entity with a name.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an entity with an enable state that can be toggled.
|
/// Represents an entity with an enable state that can be toggled.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the transformation properties of an object such as position, scale, and rotation in 2D space.
|
/// Represents the transformation properties of an object such as position, scale, and rotation in 2D space.
|
||||||
@@ -16,10 +16,15 @@ public interface ITransform2D : IBehaviour
|
|||||||
Event<ITransform2D, ScaleChangedArguments> OnScaleChanged { get; }
|
Event<ITransform2D, ScaleChangedArguments> OnScaleChanged { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event triggered when the <see cref="Rotation"/> of the <see cref="ITransform"/> changes.
|
/// Event triggered when the <see cref="Rotation"/> of the <see cref="ITransform2D"/> changes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Event<ITransform2D, RotationChangedArguments> OnRotationChanged { get; }
|
Event<ITransform2D, RotationChangedArguments> OnRotationChanged { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when any of the properties of the <see cref="ITransform2D"/> gets updated.
|
||||||
|
/// </summary>
|
||||||
|
Event<ITransform2D> OnTransformUpdated { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The <see cref="Vector2D"/> pointing upwards in world space.
|
/// The <see cref="Vector2D"/> pointing upwards in world space.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
105
Engine.Core/Abstract/ITransform3D.cs
Normal file
105
Engine.Core/Abstract/ITransform3D.cs
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
namespace Engine.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the transformation properties of an object such as position, scale, and rotation in 3D space.
|
||||||
|
/// </summary>
|
||||||
|
public interface ITransform3D : IBehaviour
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when the <see cref="Position"/> of the <see cref="ITransform3D"/> changes.
|
||||||
|
/// </summary>
|
||||||
|
Event<ITransform3D, PositionChangedArguments> OnPositionChanged { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when the <see cref="Scale"/> of the <see cref="ITransform3D"/> changes.
|
||||||
|
/// </summary>
|
||||||
|
Event<ITransform3D, ScaleChangedArguments> OnScaleChanged { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when the <see cref="Rotation"/> of the <see cref="ITransform3D"/> changes.
|
||||||
|
/// </summary>
|
||||||
|
Event<ITransform3D, RotationChangedArguments> OnRotationChanged { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event triggered when any of the properties of the <see cref="ITransform3D"/> gets updated.
|
||||||
|
/// </summary>
|
||||||
|
Event<ITransform3D> OnTransformUpdated { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="Vector3D"/> pointing upwards in world space.
|
||||||
|
/// </summary>
|
||||||
|
Vector3D Up { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="Vector3D"/> pointing upwards in world space.
|
||||||
|
/// </summary>
|
||||||
|
Vector3D Down { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="Vector3D"/> pointing upwards in world space.
|
||||||
|
/// </summary>
|
||||||
|
Vector3D Left { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="Vector3D"/> pointing upwards in world space.
|
||||||
|
/// </summary>
|
||||||
|
Vector3D Right { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="Vector3D"/> pointing forwards in world space.
|
||||||
|
/// </summary>
|
||||||
|
Vector3D Forward { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="Vector3D"/> pointing backwards in world space.
|
||||||
|
/// </summary>
|
||||||
|
Vector3D Backward { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The world position of the <see cref="ITransform3D"/> in 3D space.
|
||||||
|
/// </summary>
|
||||||
|
Vector3D Position { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The world scale of the <see cref="ITransform3D"/>.
|
||||||
|
/// </summary>
|
||||||
|
Vector3D Scale { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The world rotation of the <see cref="ITransform3D"/> in degrees.
|
||||||
|
/// </summary>
|
||||||
|
Quaternion Rotation { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The local position of the <see cref="ITransform3D"/> in 3D space.
|
||||||
|
/// </summary>
|
||||||
|
Vector3D LocalPosition { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The local scale of the <see cref="ITransform3D"/>.
|
||||||
|
/// </summary>
|
||||||
|
Vector3D LocalScale { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The local rotation of the <see cref="ITransform3D"/> in degrees.
|
||||||
|
/// </summary>
|
||||||
|
Quaternion LocalRotation { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Arguments for the event triggered when the <see cref="ITransform3D"/>'s rotation changes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="PreviousPosition">The previous <see cref="Position"/> of the <see cref="ITransform3D"/>.</param>
|
||||||
|
readonly record struct PositionChangedArguments(Vector3D PreviousPosition);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Arguments for the event triggered when the <see cref="ITransform3D"/>'s rotation changes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="PreviousScale">The previous <see cref="Scale"/> of the <see cref="ITransform3D"/>.</param>
|
||||||
|
readonly record struct ScaleChangedArguments(Vector3D PreviousScale);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Arguments for the event triggered when the <see cref="ITransform3D"/>'s rotation changes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="PreviousRotation">The previous <see cref="Rotation"/> of the <see cref="ITransform3D"/>.</param>
|
||||||
|
readonly record struct RotationChangedArguments(Quaternion PreviousRotation);
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a universe responsible for managing <see cref="IUniverseObject"/>s.
|
/// Represents a universe responsible for managing <see cref="IUniverseObject"/>s.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an <see cref="IEntity"/> that can enter and exit a universe within the <see cref="IUniverse"/> system.
|
/// Represents an <see cref="IEntity"/> that can enter and exit a universe within the <see cref="IUniverse"/> system.
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
|
||||||
|
|
||||||
public class ActiveBehaviourCollectorSorted<T> : ActiveBehaviourCollector<T> where T : class, IBehaviour
|
|
||||||
{
|
|
||||||
private readonly Event<IBehaviour, IBehaviour.PriorityChangedArguments>.EventHandler delegateOnPriorityChanged = null!;
|
|
||||||
|
|
||||||
private IComparer<T>? _sortBy = null;
|
|
||||||
public IComparer<T>? SortBy
|
|
||||||
{
|
|
||||||
get => _sortBy;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_sortBy = value;
|
|
||||||
|
|
||||||
if (value is not null)
|
|
||||||
activeBehaviours.Sort(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void AddBehaviour(T behaviour)
|
|
||||||
{
|
|
||||||
if (SortBy is null)
|
|
||||||
{
|
|
||||||
activeBehaviours.Add(behaviour);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int insertionIndex = activeBehaviours.BinarySearch(behaviour, SortBy);
|
|
||||||
|
|
||||||
if (insertionIndex < 0)
|
|
||||||
insertionIndex = ~insertionIndex;
|
|
||||||
|
|
||||||
activeBehaviours.Insert(insertionIndex, behaviour);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnBehaviourAdd(IBehaviour behaviour)
|
|
||||||
{
|
|
||||||
behaviour.OnPriorityChanged.AddListener(delegateOnPriorityChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnBehaviourRemove(IBehaviour behaviour)
|
|
||||||
{
|
|
||||||
behaviour.OnPriorityChanged.RemoveListener(delegateOnPriorityChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnPriorityChanged(IBehaviour sender, IBehaviour.PriorityChangedArguments args)
|
|
||||||
{
|
|
||||||
T behaviour = (T)sender;
|
|
||||||
activeBehaviours.Remove(behaviour);
|
|
||||||
AddBehaviour(behaviour);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ActiveBehaviourCollectorSorted()
|
|
||||||
{
|
|
||||||
delegateOnPriorityChanged = OnPriorityChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ActiveBehaviourCollectorSorted(IUniverse universe, Comparison<T> sortBy) : base(universe)
|
|
||||||
{
|
|
||||||
delegateOnPriorityChanged = OnPriorityChanged;
|
|
||||||
|
|
||||||
SortBy = Comparer<T>.Create(sortBy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
public abstract class BaseEntity : IEntity
|
public abstract class BaseEntity : IEntity
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,50 +1,103 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
public abstract class Behaviour : BehaviourBase
|
[System.Diagnostics.DebuggerDisplay("{GetType().Name, nq}, Priority: {Priority}, Initialized: {Initialized}")]
|
||||||
|
public abstract class Behaviour : BaseEntity, IBehaviour
|
||||||
{
|
{
|
||||||
private readonly Event<IUniverseObject, IUniverseObject.EnteredUniverseArguments>.EventHandler delegateEnteredUniverse = null!;
|
public Event<IBehaviour, IBehaviour.PriorityChangedArguments> OnPriorityChanged { get; } = new();
|
||||||
private readonly Event<IUniverseObject, IUniverseObject.ExitedUniverseArguments>.EventHandler delegateExitedUniverse = null!;
|
public Event<IActive, IActive.ActiveChangedArguments> OnActiveChanged { get; } = new();
|
||||||
|
public Event<IHasBehaviourController> OnBehaviourControllerAssigned { get; } = new();
|
||||||
|
|
||||||
public Behaviour()
|
private readonly Event<IHasUniverseObject>.EventHandler delegateOnUniverseObjectAssigned = null!;
|
||||||
|
private readonly Event<IActive, IActive.ActiveChangedArguments>.EventHandler delegateOnUniverseObjectActiveChanged = null!;
|
||||||
|
private readonly Event<IStateEnable, IStateEnable.EnabledChangedArguments>.EventHandler delegateOnStateEnabledChanged = null!;
|
||||||
|
|
||||||
|
public IUniverse Universe => BehaviourController.UniverseObject.Universe;
|
||||||
|
public IUniverseObject UniverseObject => BehaviourController.UniverseObject;
|
||||||
|
|
||||||
|
private IBehaviourController _behaviourController = null!;
|
||||||
|
public IBehaviourController BehaviourController => _behaviourController;
|
||||||
|
|
||||||
|
private int _priority = 0;
|
||||||
|
public int Priority
|
||||||
{
|
{
|
||||||
OnInitialized.AddListener(OnInitialize);
|
get => _priority;
|
||||||
OnFinalized.AddListener(OnFinalize);
|
set
|
||||||
OnUnassigned.AddListener(OnUnassign);
|
{
|
||||||
|
if (value == _priority)
|
||||||
|
return;
|
||||||
|
|
||||||
delegateEnteredUniverse = EnteredUniverse;
|
int previousPriority = _priority;
|
||||||
delegateExitedUniverse = ExitedUniverse;
|
_priority = value;
|
||||||
|
OnPriorityChanged?.Invoke(this, new(previousPriority));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnUnassign() { }
|
private bool _isActive = false;
|
||||||
protected void OnUnassign(IAssignable assignable) => OnUnassign();
|
public bool IsActive => _isActive;
|
||||||
|
|
||||||
protected virtual void OnInitialize() { }
|
protected virtual void OnAssign(IBehaviourController behaviourController) { }
|
||||||
protected void OnInitialize(IInitializable _)
|
public bool Assign(IBehaviourController behaviourController)
|
||||||
{
|
{
|
||||||
BehaviourController.UniverseObject.OnEnteredUniverse.AddListener(delegateEnteredUniverse);
|
if (IsInitialized)
|
||||||
BehaviourController.UniverseObject.OnExitedUniverse.AddListener(delegateExitedUniverse);
|
return false;
|
||||||
|
|
||||||
OnInitialize();
|
_behaviourController = behaviourController;
|
||||||
|
OnAssign(behaviourController);
|
||||||
if (UniverseObject.IsInUniverse)
|
behaviourController.OnUniverseObjectAssigned.AddListener(delegateOnUniverseObjectAssigned);
|
||||||
EnteredUniverse(UniverseObject, new(Universe));
|
behaviourController.StateEnable.OnEnabledChanged.AddListener(delegateOnStateEnabledChanged);
|
||||||
|
if (behaviourController.UniverseObject is not null)
|
||||||
|
OnUniverseObjectAssigned(behaviourController);
|
||||||
|
OnBehaviourControllerAssigned?.Invoke(this);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnFinalize() { }
|
private void OnUniverseObjectAssigned(IHasUniverseObject sender)
|
||||||
protected void OnFinalize(IInitializable _)
|
|
||||||
{
|
{
|
||||||
BehaviourController.UniverseObject.OnEnteredUniverse.RemoveListener(delegateEnteredUniverse);
|
sender.UniverseObject.OnActiveChanged.AddListener(delegateOnUniverseObjectActiveChanged);
|
||||||
BehaviourController.UniverseObject.OnExitedUniverse.RemoveListener(delegateExitedUniverse);
|
UpdateActive();
|
||||||
|
|
||||||
OnFinalize();
|
|
||||||
|
|
||||||
if (UniverseObject.IsInUniverse)
|
|
||||||
ExitedUniverse(UniverseObject, new(Universe));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnEnteredUniverse(IUniverse universe) { }
|
protected override void OnAssign(IStateEnable stateEnable)
|
||||||
protected void EnteredUniverse(IUniverseObject sender, IUniverseObject.EnteredUniverseArguments args) => OnEnteredUniverse(args.Universe);
|
{
|
||||||
|
base.OnAssign(stateEnable);
|
||||||
|
|
||||||
protected virtual void OnExitedUniverse(IUniverse universe) { }
|
stateEnable.OnEnabledChanged.AddListener(delegateOnStateEnabledChanged);
|
||||||
protected void ExitedUniverse(IUniverseObject sender, IUniverseObject.ExitedUniverseArguments args) => OnExitedUniverse(args.Universe);
|
}
|
||||||
|
|
||||||
|
protected override void UnassignInternal()
|
||||||
|
{
|
||||||
|
BehaviourController.UniverseObject.OnActiveChanged.RemoveListener(delegateOnUniverseObjectActiveChanged);
|
||||||
|
StateEnable.OnEnabledChanged.RemoveListener(delegateOnStateEnabledChanged);
|
||||||
|
BehaviourController.OnUniverseObjectAssigned.RemoveListener(delegateOnUniverseObjectAssigned);
|
||||||
|
BehaviourController.StateEnable.OnEnabledChanged.RemoveListener(delegateOnStateEnabledChanged);
|
||||||
|
base.UnassignInternal();
|
||||||
|
_behaviourController = null!;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void InitializeInternal()
|
||||||
|
{
|
||||||
|
Debug.Assert.AssertBehaviourControllerAssigned(this);
|
||||||
|
Debug.Assert.AssertStateEnableAssigned(this);
|
||||||
|
|
||||||
|
UpdateActive();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStateEnabledChanged(IStateEnable sender, IStateEnable.EnabledChangedArguments args) => UpdateActive();
|
||||||
|
private void OnUniverseObjectActiveChanged(IActive sender, IActive.ActiveChangedArguments args) => UpdateActive();
|
||||||
|
|
||||||
|
private void UpdateActive()
|
||||||
|
{
|
||||||
|
bool previousActive = IsActive;
|
||||||
|
_isActive = StateEnable.Enabled && _behaviourController.StateEnable.Enabled && _behaviourController.UniverseObject.IsActive;
|
||||||
|
|
||||||
|
if (previousActive != IsActive)
|
||||||
|
OnActiveChanged?.Invoke(this, new(previousActive));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Behaviour()
|
||||||
|
{
|
||||||
|
delegateOnUniverseObjectAssigned = OnUniverseObjectAssigned;
|
||||||
|
delegateOnUniverseObjectActiveChanged = OnUniverseObjectActiveChanged;
|
||||||
|
delegateOnStateEnabledChanged = OnStateEnabledChanged;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
public abstract class Behaviour2D : Behaviour, IBehaviour2D
|
// TODO this should not use independent behaviour, the OnInitialize usage for getting the transform can cause very unexpected issues
|
||||||
|
public abstract class Behaviour2D : Internal.BehaviourIndependent, IBehaviour2D
|
||||||
{
|
{
|
||||||
public ITransform2D Transform { get; private set; } = null!;
|
public ITransform2D Transform { get; private set; } = null!;
|
||||||
|
|
||||||
|
|||||||
@@ -1,103 +0,0 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
|
||||||
|
|
||||||
[System.Diagnostics.DebuggerDisplay("{GetType().Name, nq}, Priority: {Priority}, Initialized: {Initialized}")]
|
|
||||||
public abstract class BehaviourBase : BaseEntity, IBehaviour
|
|
||||||
{
|
|
||||||
public Event<IBehaviour, IBehaviour.PriorityChangedArguments> OnPriorityChanged { get; } = new();
|
|
||||||
public Event<IActive, IActive.ActiveChangedArguments> OnActiveChanged { get; } = new();
|
|
||||||
public Event<IHasBehaviourController> OnBehaviourControllerAssigned { get; } = new();
|
|
||||||
|
|
||||||
private readonly Event<IHasUniverseObject>.EventHandler delegateOnUniverseObjectAssigned = null!;
|
|
||||||
private readonly Event<IActive, IActive.ActiveChangedArguments>.EventHandler delegateOnUniverseObjectActiveChanged = null!;
|
|
||||||
private readonly Event<IStateEnable, IStateEnable.EnabledChangedArguments>.EventHandler delegateOnStateEnabledChanged = null!;
|
|
||||||
|
|
||||||
public IUniverse Universe => BehaviourController.UniverseObject.Universe;
|
|
||||||
public IUniverseObject UniverseObject => BehaviourController.UniverseObject;
|
|
||||||
|
|
||||||
private IBehaviourController _behaviourController = null!;
|
|
||||||
public IBehaviourController BehaviourController => _behaviourController;
|
|
||||||
|
|
||||||
private int _priority = 0;
|
|
||||||
public int Priority
|
|
||||||
{
|
|
||||||
get => _priority;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == _priority)
|
|
||||||
return;
|
|
||||||
|
|
||||||
int previousPriority = _priority;
|
|
||||||
_priority = value;
|
|
||||||
OnPriorityChanged?.Invoke(this, new(previousPriority));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _isActive = false;
|
|
||||||
public bool IsActive => _isActive;
|
|
||||||
|
|
||||||
protected virtual void OnAssign(IBehaviourController behaviourController) { }
|
|
||||||
public bool Assign(IBehaviourController behaviourController)
|
|
||||||
{
|
|
||||||
if (IsInitialized)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
_behaviourController = behaviourController;
|
|
||||||
OnAssign(behaviourController);
|
|
||||||
behaviourController.OnUniverseObjectAssigned.AddListener(delegateOnUniverseObjectAssigned);
|
|
||||||
behaviourController.StateEnable.OnEnabledChanged.AddListener(delegateOnStateEnabledChanged);
|
|
||||||
if (behaviourController.UniverseObject is not null)
|
|
||||||
OnUniverseObjectAssigned(behaviourController);
|
|
||||||
OnBehaviourControllerAssigned?.Invoke(this);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnUniverseObjectAssigned(IHasUniverseObject sender)
|
|
||||||
{
|
|
||||||
sender.UniverseObject.OnActiveChanged.AddListener(delegateOnUniverseObjectActiveChanged);
|
|
||||||
UpdateActive();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnAssign(IStateEnable stateEnable)
|
|
||||||
{
|
|
||||||
base.OnAssign(stateEnable);
|
|
||||||
|
|
||||||
stateEnable.OnEnabledChanged.AddListener(delegateOnStateEnabledChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void UnassignInternal()
|
|
||||||
{
|
|
||||||
BehaviourController.UniverseObject.OnActiveChanged.RemoveListener(delegateOnUniverseObjectActiveChanged);
|
|
||||||
StateEnable.OnEnabledChanged.RemoveListener(delegateOnStateEnabledChanged);
|
|
||||||
BehaviourController.OnUniverseObjectAssigned.RemoveListener(delegateOnUniverseObjectAssigned);
|
|
||||||
BehaviourController.StateEnable.OnEnabledChanged.RemoveListener(delegateOnStateEnabledChanged);
|
|
||||||
base.UnassignInternal();
|
|
||||||
_behaviourController = null!;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void InitializeInternal()
|
|
||||||
{
|
|
||||||
Debug.Assert.AssertBehaviourControllerAssigned(this);
|
|
||||||
Debug.Assert.AssertStateEnableAssigned(this);
|
|
||||||
|
|
||||||
UpdateActive();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnStateEnabledChanged(IStateEnable sender, IStateEnable.EnabledChangedArguments args) => UpdateActive();
|
|
||||||
private void OnUniverseObjectActiveChanged(IActive sender, IActive.ActiveChangedArguments args) => UpdateActive();
|
|
||||||
|
|
||||||
private void UpdateActive()
|
|
||||||
{
|
|
||||||
bool previousActive = IsActive;
|
|
||||||
_isActive = StateEnable.Enabled && _behaviourController.StateEnable.Enabled && _behaviourController.UniverseObject.IsActive;
|
|
||||||
|
|
||||||
if (previousActive != IsActive)
|
|
||||||
OnActiveChanged?.Invoke(this, new(previousActive));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected BehaviourBase()
|
|
||||||
{
|
|
||||||
delegateOnUniverseObjectAssigned = OnUniverseObjectAssigned;
|
|
||||||
delegateOnUniverseObjectActiveChanged = OnUniverseObjectActiveChanged;
|
|
||||||
delegateOnStateEnabledChanged = OnStateEnabledChanged;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
|
||||||
|
|
||||||
public class BehaviourCollectorSorted<T> : BehaviourCollector<T> where T : class
|
|
||||||
{
|
|
||||||
private readonly Event<IBehaviour, IBehaviour.PriorityChangedArguments>.EventHandler delegateOnPriorityChanged = null!;
|
|
||||||
|
|
||||||
private IComparer<T>? _sortBy = null;
|
|
||||||
public IComparer<T>? SortBy
|
|
||||||
{
|
|
||||||
get => _sortBy;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_sortBy = value;
|
|
||||||
|
|
||||||
if (value is not null)
|
|
||||||
behaviours.Sort(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void AddBehaviour(T behaviour)
|
|
||||||
{
|
|
||||||
if (SortBy is null)
|
|
||||||
{
|
|
||||||
behaviours.Add(behaviour);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int insertionIndex = behaviours.BinarySearch(behaviour, SortBy);
|
|
||||||
|
|
||||||
if (insertionIndex < 0)
|
|
||||||
insertionIndex = ~insertionIndex;
|
|
||||||
|
|
||||||
behaviours.Insert(insertionIndex, behaviour);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnBehaviourAdd(IBehaviour behaviour)
|
|
||||||
{
|
|
||||||
behaviour.OnPriorityChanged.AddListener(delegateOnPriorityChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnBehaviourRemove(IBehaviour behaviour)
|
|
||||||
{
|
|
||||||
behaviour.OnPriorityChanged.RemoveListener(delegateOnPriorityChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnPriorityChanged(IBehaviour sender, IBehaviour.PriorityChangedArguments args)
|
|
||||||
{
|
|
||||||
T behaviour = (T)sender;
|
|
||||||
behaviours.Remove(behaviour);
|
|
||||||
AddBehaviour(behaviour);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BehaviourCollectorSorted()
|
|
||||||
{
|
|
||||||
delegateOnPriorityChanged = OnPriorityChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BehaviourCollectorSorted(IUniverse universe, Comparison<T> sortBy) : base(universe)
|
|
||||||
{
|
|
||||||
delegateOnPriorityChanged = OnPriorityChanged;
|
|
||||||
|
|
||||||
SortBy = Comparer<T>.Create(sortBy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
[System.Diagnostics.DebuggerDisplay("Behaviour Count: {behaviours.Count}")]
|
[System.Diagnostics.DebuggerDisplay("Behaviour Count: {behaviours.Count}")]
|
||||||
public class BehaviourController : BaseEntity, IBehaviourController
|
public class BehaviourController : BaseEntity, IBehaviourController
|
||||||
@@ -10,7 +10,7 @@ public class BehaviourController : BaseEntity, IBehaviourController
|
|||||||
public Event<IBehaviourController, IBehaviourController.BehaviourRemovedArguments> OnBehaviourRemoved { get; } = new();
|
public Event<IBehaviourController, IBehaviourController.BehaviourRemovedArguments> OnBehaviourRemoved { get; } = new();
|
||||||
public Event<IHasUniverseObject> OnUniverseObjectAssigned { get; } = new();
|
public Event<IHasUniverseObject> OnUniverseObjectAssigned { get; } = new();
|
||||||
|
|
||||||
private readonly List<IBehaviour> behaviours = new(Constants.BEHAVIOURS_SIZE_INITIAL);
|
private readonly FastList<IBehaviour> behaviours = new(Constants.BEHAVIOURS_SIZE_INITIAL);
|
||||||
|
|
||||||
private IUniverseObject _universeObject = null!;
|
private IUniverseObject _universeObject = null!;
|
||||||
|
|
||||||
|
|||||||
56
Engine.Core/BehaviourInternal.cs
Normal file
56
Engine.Core/BehaviourInternal.cs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
namespace Engine.Core.Internal;
|
||||||
|
|
||||||
|
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||||
|
/// <summary>
|
||||||
|
/// This behaviour can be used for core managers like <see cref="UpdateManager"/> etc. which need to be able to work even
|
||||||
|
/// in a very bare minimum setup without the presence of <see cref="UniverseEntranceManager"/> to set themselves up on universe entrance.
|
||||||
|
/// I recommend not using this unless you know what you are doing but it might come in handy for some use cases.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class BehaviourIndependent : Behaviour
|
||||||
|
{
|
||||||
|
private readonly Event<IUniverseObject, IUniverseObject.EnteredUniverseArguments>.EventHandler delegateEnteredUniverse = null!;
|
||||||
|
private readonly Event<IUniverseObject, IUniverseObject.ExitedUniverseArguments>.EventHandler delegateExitedUniverse = null!;
|
||||||
|
|
||||||
|
public BehaviourIndependent()
|
||||||
|
{
|
||||||
|
OnInitialized.AddListener(OnInitialize);
|
||||||
|
OnFinalized.AddListener(OnFinalize);
|
||||||
|
OnUnassigned.AddListener(OnUnassign);
|
||||||
|
|
||||||
|
delegateEnteredUniverse = EnteredUniverse;
|
||||||
|
delegateExitedUniverse = ExitedUniverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnUnassign() { }
|
||||||
|
protected void OnUnassign(IAssignable assignable) => OnUnassign();
|
||||||
|
|
||||||
|
protected virtual void OnInitialize() { }
|
||||||
|
protected void OnInitialize(IInitializable _)
|
||||||
|
{
|
||||||
|
BehaviourController.UniverseObject.OnEnteredUniverse.AddListener(delegateEnteredUniverse);
|
||||||
|
BehaviourController.UniverseObject.OnExitedUniverse.AddListener(delegateExitedUniverse);
|
||||||
|
|
||||||
|
OnInitialize();
|
||||||
|
|
||||||
|
if (UniverseObject.IsInUniverse)
|
||||||
|
EnteredUniverse(UniverseObject, new(Universe));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnFinalize() { }
|
||||||
|
protected void OnFinalize(IInitializable _)
|
||||||
|
{
|
||||||
|
BehaviourController.UniverseObject.OnEnteredUniverse.RemoveListener(delegateEnteredUniverse);
|
||||||
|
BehaviourController.UniverseObject.OnExitedUniverse.RemoveListener(delegateExitedUniverse);
|
||||||
|
|
||||||
|
OnFinalize();
|
||||||
|
|
||||||
|
if (UniverseObject.IsInUniverse)
|
||||||
|
ExitedUniverse(UniverseObject, new(Universe));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnEnteredUniverse(IUniverse universe) { }
|
||||||
|
protected void EnteredUniverse(IUniverseObject sender, IUniverseObject.EnteredUniverseArguments args) => OnEnteredUniverse(args.Universe);
|
||||||
|
|
||||||
|
protected virtual void OnExitedUniverse(IUniverse universe) { }
|
||||||
|
protected void ExitedUniverse(IUniverseObject sender, IUniverseObject.ExitedUniverseArguments args) => OnExitedUniverse(args.Universe);
|
||||||
|
}
|
||||||
16
Engine.Core/Collectors/ActiveBehaviourCollector.cs
Normal file
16
Engine.Core/Collectors/ActiveBehaviourCollector.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Engine.Core;
|
||||||
|
|
||||||
|
public class ActiveBehaviourCollector<T> : ActiveBehaviourCollectorBase<T> where T : class, IBehaviour
|
||||||
|
{
|
||||||
|
protected readonly FastList<T> activeBehaviours = new(32);
|
||||||
|
public override T this[Index index] => activeBehaviours[index];
|
||||||
|
public override int Count => activeBehaviours.Count;
|
||||||
|
|
||||||
|
public ActiveBehaviourCollector() { }
|
||||||
|
public ActiveBehaviourCollector(IUniverse universe) : base(universe) { }
|
||||||
|
|
||||||
|
protected override void AddBehaviour(T behaviour) => activeBehaviours.Add(behaviour);
|
||||||
|
protected override bool RemoveBehaviour(T tBehaviour) => activeBehaviours.Remove(tBehaviour);
|
||||||
|
}
|
||||||
@@ -1,14 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : class, IBehaviour
|
public abstract class ActiveBehaviourCollectorBase<T> : IBehaviourCollector<T> where T : class, IBehaviour
|
||||||
{
|
{
|
||||||
public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourCollectedArguments> OnCollected { get; } = new();
|
protected readonly Dictionary<IActive, T> monitoringActiveToBehaviour = new(32);
|
||||||
public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourRemovedArguments> OnRemoved { get; } = new();
|
protected readonly FastList<T> monitoringBehaviours = new(32);
|
||||||
public Event<IHasUniverse> OnUniverseAssigned { get; } = new();
|
|
||||||
public Event<IAssignable>? OnUnassigned { get; } = new();
|
|
||||||
|
|
||||||
private readonly Event<IBehaviourController, IBehaviourController.BehaviourAddedArguments>.EventHandler delegateOnBehaviourAdded = null!;
|
private readonly Event<IBehaviourController, IBehaviourController.BehaviourAddedArguments>.EventHandler delegateOnBehaviourAdded = null!;
|
||||||
private readonly Event<IBehaviourController, IBehaviourController.BehaviourRemovedArguments>.EventHandler delegateOnBehaviourRemoved = null!;
|
private readonly Event<IBehaviourController, IBehaviourController.BehaviourRemovedArguments>.EventHandler delegateOnBehaviourRemoved = null!;
|
||||||
@@ -16,80 +14,16 @@ public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : clas
|
|||||||
private readonly Event<IUniverse, IUniverse.UniverseObjectRegisteredArguments>.EventHandler delegateOnUniverseObjectRegistered = null!;
|
private readonly Event<IUniverse, IUniverse.UniverseObjectRegisteredArguments>.EventHandler delegateOnUniverseObjectRegistered = null!;
|
||||||
private readonly Event<IUniverse, IUniverse.UniverseObjectUnRegisteredArguments>.EventHandler delegateOnUniverseObjectUnregistered = null!;
|
private readonly Event<IUniverse, IUniverse.UniverseObjectUnRegisteredArguments>.EventHandler delegateOnUniverseObjectUnregistered = null!;
|
||||||
|
|
||||||
private readonly List<T> monitoringBehaviours = new(32);
|
public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourCollectedArguments> OnCollected { get; } = new();
|
||||||
protected readonly List<T> activeBehaviours = new(32);
|
public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourRemovedArguments> OnRemoved { get; } = new();
|
||||||
protected readonly Dictionary<IActive, T> monitoringActiveToBehaviour = new(32);
|
public Event<IHasUniverse> OnUniverseAssigned { get; } = new();
|
||||||
|
public Event<IAssignable>? OnUnassigned { get; } = new();
|
||||||
|
|
||||||
|
public abstract int Count { get; }
|
||||||
|
|
||||||
|
public abstract T this[Index index] { get; }
|
||||||
public IUniverse Universe { get; private set; } = null!;
|
public IUniverse Universe { get; private set; } = null!;
|
||||||
|
|
||||||
private void OnUniverseObjectRegistered(IUniverse manager, IUniverse.UniverseObjectRegisteredArguments args)
|
|
||||||
{
|
|
||||||
IUniverseObject universeObject = args.UniverseObjectRegistered;
|
|
||||||
|
|
||||||
universeObject.BehaviourController.OnBehaviourAdded.AddListener(delegateOnBehaviourAdded);
|
|
||||||
universeObject.BehaviourController.OnBehaviourRemoved.AddListener(delegateOnBehaviourRemoved);
|
|
||||||
|
|
||||||
for (int i = 0; i < universeObject.BehaviourController.Count; i++)
|
|
||||||
OnBehaviourAdded(universeObject.BehaviourController, new(universeObject.BehaviourController[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnUniverseObjectUnregistered(IUniverse manager, IUniverse.UniverseObjectUnRegisteredArguments args)
|
|
||||||
{
|
|
||||||
IUniverseObject universeObject = args.UniverseObjectUnregistered;
|
|
||||||
|
|
||||||
universeObject.BehaviourController.OnBehaviourAdded.RemoveListener(delegateOnBehaviourAdded);
|
|
||||||
universeObject.BehaviourController.OnBehaviourRemoved.RemoveListener(delegateOnBehaviourRemoved);
|
|
||||||
|
|
||||||
for (int i = 0; i < universeObject.BehaviourController.Count; i++)
|
|
||||||
OnBehaviourRemoved(universeObject.BehaviourController, new(universeObject.BehaviourController[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnBehaviourAdd(IBehaviour behaviour) { }
|
|
||||||
private void OnBehaviourAdded(IBehaviourController controller, IBehaviourController.BehaviourAddedArguments args)
|
|
||||||
{
|
|
||||||
if (args.BehaviourAdded is not T tBehaviour)
|
|
||||||
return;
|
|
||||||
|
|
||||||
monitoringBehaviours.Add(tBehaviour);
|
|
||||||
monitoringActiveToBehaviour.Add(tBehaviour, tBehaviour);
|
|
||||||
tBehaviour.OnActiveChanged.AddListener(delegateOnBehaviourStateChanged);
|
|
||||||
OnBehaviourStateChanged(tBehaviour, new(!tBehaviour.IsActive));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void AddBehaviour(T behaviour) => activeBehaviours.Add(behaviour);
|
|
||||||
private void OnBehaviourStateChanged(IActive sender, IActive.ActiveChangedArguments args)
|
|
||||||
{
|
|
||||||
T behaviour = monitoringActiveToBehaviour[sender];
|
|
||||||
if (sender.IsActive)
|
|
||||||
{
|
|
||||||
AddBehaviour(behaviour);
|
|
||||||
OnBehaviourAdd(behaviour);
|
|
||||||
OnCollected?.Invoke(this, new(behaviour));
|
|
||||||
}
|
|
||||||
else if (activeBehaviours.Remove(behaviour))
|
|
||||||
{
|
|
||||||
OnBehaviourRemove(behaviour);
|
|
||||||
OnRemoved?.Invoke(this, new(behaviour));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnBehaviourRemove(IBehaviour behaviour) { }
|
|
||||||
private void OnBehaviourRemoved(IBehaviourController controller, IBehaviourController.BehaviourRemovedArguments args)
|
|
||||||
{
|
|
||||||
if (args.BehaviourRemoved is not T tBehaviour)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!monitoringBehaviours.Remove(tBehaviour) || !monitoringActiveToBehaviour.Remove(tBehaviour))
|
|
||||||
return;
|
|
||||||
|
|
||||||
tBehaviour.OnActiveChanged.RemoveListener(delegateOnBehaviourStateChanged);
|
|
||||||
if (activeBehaviours.Remove(tBehaviour))
|
|
||||||
{
|
|
||||||
OnBehaviourRemove(tBehaviour);
|
|
||||||
OnRemoved?.Invoke(this, new(tBehaviour));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Assign(IUniverse universe)
|
public bool Assign(IUniverse universe)
|
||||||
{
|
{
|
||||||
if (Universe is not null)
|
if (Universe is not null)
|
||||||
@@ -123,10 +57,75 @@ public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : clas
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Count => activeBehaviours.Count;
|
protected abstract void AddBehaviour(T behaviour);
|
||||||
public T this[Index index] => activeBehaviours[index];
|
protected virtual void OnBehaviourAdd(IBehaviour behaviour) { }
|
||||||
|
private void OnBehaviourAdded(IBehaviourController controller, IBehaviourController.BehaviourAddedArguments args)
|
||||||
|
{
|
||||||
|
if (args.BehaviourAdded is not T tBehaviour)
|
||||||
|
return;
|
||||||
|
|
||||||
public ActiveBehaviourCollector()
|
monitoringBehaviours.Add(tBehaviour);
|
||||||
|
monitoringActiveToBehaviour.Add(tBehaviour, tBehaviour);
|
||||||
|
tBehaviour.OnActiveChanged.AddListener(delegateOnBehaviourStateChanged);
|
||||||
|
OnBehaviourStateChanged(tBehaviour, new(!tBehaviour.IsActive));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract bool RemoveBehaviour(T behaviour);
|
||||||
|
protected virtual void OnBehaviourRemove(IBehaviour behaviour) { }
|
||||||
|
private void OnBehaviourRemoved(IBehaviourController controller, IBehaviourController.BehaviourRemovedArguments args)
|
||||||
|
{
|
||||||
|
if (args.BehaviourRemoved is not T tBehaviour)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!monitoringBehaviours.Remove(tBehaviour) || !monitoringActiveToBehaviour.Remove(tBehaviour))
|
||||||
|
return;
|
||||||
|
|
||||||
|
tBehaviour.OnActiveChanged.RemoveListener(delegateOnBehaviourStateChanged);
|
||||||
|
if (!RemoveBehaviour(tBehaviour))
|
||||||
|
return;
|
||||||
|
|
||||||
|
OnBehaviourRemove(tBehaviour);
|
||||||
|
OnRemoved?.Invoke(this, new(tBehaviour));
|
||||||
|
}
|
||||||
|
private void OnBehaviourStateChanged(IActive sender, IActive.ActiveChangedArguments args)
|
||||||
|
{
|
||||||
|
T behaviour = monitoringActiveToBehaviour[sender];
|
||||||
|
if (sender.IsActive)
|
||||||
|
{
|
||||||
|
AddBehaviour(behaviour);
|
||||||
|
OnBehaviourAdd(behaviour);
|
||||||
|
OnCollected?.Invoke(this, new(behaviour));
|
||||||
|
}
|
||||||
|
else if (RemoveBehaviour(behaviour))
|
||||||
|
{
|
||||||
|
OnBehaviourRemove(behaviour);
|
||||||
|
OnRemoved?.Invoke(this, new(behaviour));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUniverseObjectRegistered(IUniverse manager, IUniverse.UniverseObjectRegisteredArguments args)
|
||||||
|
{
|
||||||
|
IUniverseObject universeObject = args.UniverseObjectRegistered;
|
||||||
|
|
||||||
|
universeObject.BehaviourController.OnBehaviourAdded.AddListener(delegateOnBehaviourAdded);
|
||||||
|
universeObject.BehaviourController.OnBehaviourRemoved.AddListener(delegateOnBehaviourRemoved);
|
||||||
|
|
||||||
|
for (int i = 0; i < universeObject.BehaviourController.Count; i++)
|
||||||
|
OnBehaviourAdded(universeObject.BehaviourController, new(universeObject.BehaviourController[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUniverseObjectUnregistered(IUniverse manager, IUniverse.UniverseObjectUnRegisteredArguments args)
|
||||||
|
{
|
||||||
|
IUniverseObject universeObject = args.UniverseObjectUnregistered;
|
||||||
|
|
||||||
|
universeObject.BehaviourController.OnBehaviourAdded.RemoveListener(delegateOnBehaviourAdded);
|
||||||
|
universeObject.BehaviourController.OnBehaviourRemoved.RemoveListener(delegateOnBehaviourRemoved);
|
||||||
|
|
||||||
|
for (int i = 0; i < universeObject.BehaviourController.Count; i++)
|
||||||
|
OnBehaviourRemoved(universeObject.BehaviourController, new(universeObject.BehaviourController[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActiveBehaviourCollectorBase()
|
||||||
{
|
{
|
||||||
delegateOnBehaviourAdded = OnBehaviourAdded;
|
delegateOnBehaviourAdded = OnBehaviourAdded;
|
||||||
delegateOnBehaviourRemoved = OnBehaviourRemoved;
|
delegateOnBehaviourRemoved = OnBehaviourRemoved;
|
||||||
@@ -135,7 +134,7 @@ public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : clas
|
|||||||
delegateOnUniverseObjectUnregistered = OnUniverseObjectUnregistered;
|
delegateOnUniverseObjectUnregistered = OnUniverseObjectUnregistered;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ActiveBehaviourCollector(IUniverse universe)
|
public ActiveBehaviourCollectorBase(IUniverse universe)
|
||||||
{
|
{
|
||||||
delegateOnBehaviourAdded = OnBehaviourAdded;
|
delegateOnBehaviourAdded = OnBehaviourAdded;
|
||||||
delegateOnBehaviourRemoved = OnBehaviourRemoved;
|
delegateOnBehaviourRemoved = OnBehaviourRemoved;
|
||||||
104
Engine.Core/Collectors/ActiveBehaviourCollectorOrdered.cs
Normal file
104
Engine.Core/Collectors/ActiveBehaviourCollectorOrdered.cs
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Engine.Core;
|
||||||
|
|
||||||
|
public class ActiveBehaviourCollectorOrdered<TIndex, TItem> : ActiveBehaviourCollector<TItem> where TItem : class, IBehaviour where TIndex : IComparable
|
||||||
|
{
|
||||||
|
private readonly Event<IBehaviour, IBehaviour.PriorityChangedArguments>.EventHandler delegateOnPriorityChanged = null!;
|
||||||
|
|
||||||
|
private readonly SortedDictionary<TIndex, FastList<TItem>> behaviours = null!;
|
||||||
|
|
||||||
|
private readonly Func<TItem, TIndex> getIndexFunc = null!;
|
||||||
|
private readonly IComparer<TIndex> sortBy = null!;
|
||||||
|
|
||||||
|
private int count = 0;
|
||||||
|
public override int Count => count;
|
||||||
|
|
||||||
|
public override TItem this[Index index]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
int actualIndex = index.IsFromEnd
|
||||||
|
? count - index.Value
|
||||||
|
: index.Value;
|
||||||
|
|
||||||
|
if (actualIndex < 0 || actualIndex >= count)
|
||||||
|
throw new IndexOutOfRangeException();
|
||||||
|
|
||||||
|
int leftIndex = actualIndex;
|
||||||
|
foreach ((TIndex i, FastList<TItem> list) in behaviours)
|
||||||
|
{
|
||||||
|
if (leftIndex < list.Count)
|
||||||
|
return list[leftIndex];
|
||||||
|
leftIndex -= list.Count;
|
||||||
|
}
|
||||||
|
throw new IndexOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool RemoveBehaviour(TItem tBehaviour)
|
||||||
|
{
|
||||||
|
TIndex index = getIndexFunc(tBehaviour);
|
||||||
|
if (!behaviours.TryGetValue(index, out FastList<TItem>? list))
|
||||||
|
throw new Exceptions.NotFoundException($"Index of '{index}' is not found in the collector");
|
||||||
|
|
||||||
|
if (!list.Remove(tBehaviour))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
count--;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void AddBehaviour(TItem behaviour)
|
||||||
|
{
|
||||||
|
TIndex key = getIndexFunc(behaviour);
|
||||||
|
if (!behaviours.TryGetValue(key, out FastList<TItem>? list))
|
||||||
|
behaviours[key] = list = [];
|
||||||
|
|
||||||
|
count++;
|
||||||
|
list.Add(behaviour);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnBehaviourAdd(IBehaviour behaviour) => behaviour.OnPriorityChanged.AddListener(delegateOnPriorityChanged);
|
||||||
|
protected override void OnBehaviourRemove(IBehaviour behaviour) => behaviour.OnPriorityChanged.RemoveListener(delegateOnPriorityChanged);
|
||||||
|
|
||||||
|
private void OnPriorityChanged(IBehaviour sender, IBehaviour.PriorityChangedArguments args)
|
||||||
|
{
|
||||||
|
TItem behaviour = (TItem)sender;
|
||||||
|
RemoveBehaviour(behaviour);
|
||||||
|
AddBehaviour(behaviour);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActiveBehaviourCollectorOrdered(Func<TItem, TIndex> getIndexFunc, Comparison<TIndex> sortBy)
|
||||||
|
{
|
||||||
|
delegateOnPriorityChanged = OnPriorityChanged;
|
||||||
|
this.getIndexFunc = getIndexFunc;
|
||||||
|
this.sortBy = Comparer<TIndex>.Create(sortBy);
|
||||||
|
behaviours = new(this.sortBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActiveBehaviourCollectorOrdered(IUniverse universe, Func<TItem, TIndex> getIndexFunc, Comparison<TIndex> sortBy) : base(universe)
|
||||||
|
{
|
||||||
|
delegateOnPriorityChanged = OnPriorityChanged;
|
||||||
|
this.getIndexFunc = getIndexFunc;
|
||||||
|
this.sortBy = Comparer<TIndex>.Create(sortBy);
|
||||||
|
behaviours = new(this.sortBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActiveBehaviourCollectorOrdered(Func<TItem, TIndex> getIndexFunc, IComparer<TIndex> sortBy)
|
||||||
|
{
|
||||||
|
this.getIndexFunc = getIndexFunc;
|
||||||
|
delegateOnPriorityChanged = OnPriorityChanged;
|
||||||
|
this.sortBy = sortBy;
|
||||||
|
behaviours = new(sortBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActiveBehaviourCollectorOrdered(IUniverse universe, Func<TItem, TIndex> getIndexFunc, IComparer<TIndex> sortBy) : base(universe)
|
||||||
|
{
|
||||||
|
delegateOnPriorityChanged = OnPriorityChanged;
|
||||||
|
this.getIndexFunc = getIndexFunc;
|
||||||
|
this.sortBy = sortBy;
|
||||||
|
behaviours = new(sortBy);
|
||||||
|
}
|
||||||
|
}
|
||||||
17
Engine.Core/Collectors/BehaviourCollector.cs
Normal file
17
Engine.Core/Collectors/BehaviourCollector.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Engine.Core;
|
||||||
|
|
||||||
|
public class BehaviourCollector<T> : BehaviourCollectorBase<T> where T : class
|
||||||
|
{
|
||||||
|
protected readonly FastList<T> behaviours = new(32);
|
||||||
|
|
||||||
|
public override T this[Index index] => behaviours[index];
|
||||||
|
public override int Count => behaviours.Count;
|
||||||
|
|
||||||
|
protected override void AddBehaviour(T behaviour) => behaviours.Add(behaviour);
|
||||||
|
protected override bool RemoveBehaviour(T tBehaviour) => behaviours.Remove(tBehaviour);
|
||||||
|
|
||||||
|
public BehaviourCollector() { }
|
||||||
|
public BehaviourCollector(IUniverse universe) : base(universe) { }
|
||||||
|
}
|
||||||
@@ -1,72 +1,25 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
|
public abstract class BehaviourCollectorBase<T> : IBehaviourCollector<T> where T : class
|
||||||
{
|
{
|
||||||
public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourCollectedArguments> OnCollected { get; } = new();
|
|
||||||
public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourRemovedArguments> OnRemoved { get; } = new();
|
|
||||||
public Event<IHasUniverse> OnUniverseAssigned { get; } = new();
|
|
||||||
public Event<IAssignable>? OnUnassigned { get; } = new();
|
|
||||||
|
|
||||||
private readonly Event<IBehaviourController, IBehaviourController.BehaviourAddedArguments>.EventHandler delegateOnBehaviourAdded = null!;
|
private readonly Event<IBehaviourController, IBehaviourController.BehaviourAddedArguments>.EventHandler delegateOnBehaviourAdded = null!;
|
||||||
private readonly Event<IBehaviourController, IBehaviourController.BehaviourRemovedArguments>.EventHandler delegateOnBehaviourRemoved = null!;
|
private readonly Event<IBehaviourController, IBehaviourController.BehaviourRemovedArguments>.EventHandler delegateOnBehaviourRemoved = null!;
|
||||||
private readonly Event<IUniverse, IUniverse.UniverseObjectRegisteredArguments>.EventHandler delegateOnUniverseObjectRegistered = null!;
|
private readonly Event<IUniverse, IUniverse.UniverseObjectRegisteredArguments>.EventHandler delegateOnUniverseObjectRegistered = null!;
|
||||||
private readonly Event<IUniverse, IUniverse.UniverseObjectUnRegisteredArguments>.EventHandler delegateOnUniverseObjectUnregistered = null!;
|
private readonly Event<IUniverse, IUniverse.UniverseObjectUnRegisteredArguments>.EventHandler delegateOnUniverseObjectUnregistered = null!;
|
||||||
|
|
||||||
protected readonly List<T> behaviours = new(32);
|
public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourCollectedArguments> OnCollected { get; } = new();
|
||||||
|
public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourRemovedArguments> OnRemoved { get; } = new();
|
||||||
|
public Event<IHasUniverse> OnUniverseAssigned { get; } = new();
|
||||||
|
public Event<IAssignable>? OnUnassigned { get; } = new();
|
||||||
|
|
||||||
public IUniverse Universe { get; private set; } = null!;
|
public IUniverse Universe { get; private set; } = null!;
|
||||||
|
|
||||||
private void OnUniverseObjectRegistered(IUniverse manager, IUniverse.UniverseObjectRegisteredArguments args)
|
public abstract int Count { get; }
|
||||||
{
|
|
||||||
IUniverseObject universeObject = args.UniverseObjectRegistered;
|
|
||||||
|
|
||||||
universeObject.BehaviourController.OnBehaviourAdded.AddListener(delegateOnBehaviourAdded);
|
public abstract T this[Index index] { get; }
|
||||||
universeObject.BehaviourController.OnBehaviourRemoved.AddListener(delegateOnBehaviourRemoved);
|
|
||||||
|
|
||||||
for (int i = 0; i < universeObject.BehaviourController.Count; i++)
|
|
||||||
OnBehaviourAdded(universeObject.BehaviourController, new(universeObject.BehaviourController[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnUniverseObjectUnregistered(IUniverse manager, IUniverse.UniverseObjectUnRegisteredArguments args)
|
|
||||||
{
|
|
||||||
IUniverseObject universeObject = args.UniverseObjectUnregistered;
|
|
||||||
|
|
||||||
universeObject.BehaviourController.OnBehaviourAdded.RemoveListener(delegateOnBehaviourAdded);
|
|
||||||
universeObject.BehaviourController.OnBehaviourRemoved.RemoveListener(delegateOnBehaviourRemoved);
|
|
||||||
|
|
||||||
for (int i = 0; i < universeObject.BehaviourController.Count; i++)
|
|
||||||
OnBehaviourRemoved(universeObject.BehaviourController, new(universeObject.BehaviourController[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void AddBehaviour(T behaviour) => behaviours.Add(behaviour);
|
|
||||||
protected virtual void OnBehaviourAdd(IBehaviour behaviour) { }
|
|
||||||
private void OnBehaviourAdded(IBehaviourController controller, IBehaviourController.BehaviourAddedArguments args)
|
|
||||||
{
|
|
||||||
if (args.BehaviourAdded is not T tBehaviour)
|
|
||||||
return;
|
|
||||||
|
|
||||||
AddBehaviour(tBehaviour);
|
|
||||||
OnBehaviourAdd(args.BehaviourAdded);
|
|
||||||
OnCollected?.Invoke(this, new(tBehaviour));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnBehaviourRemove(IBehaviour behaviour) { }
|
|
||||||
private void OnBehaviourRemoved(IBehaviourController controller, IBehaviourController.BehaviourRemovedArguments args)
|
|
||||||
{
|
|
||||||
if (args.BehaviourRemoved is not T tBehaviour)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!behaviours.Remove(tBehaviour))
|
|
||||||
return;
|
|
||||||
|
|
||||||
OnBehaviourRemove(args.BehaviourRemoved);
|
|
||||||
OnRemoved?.Invoke(this, new(tBehaviour));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnAssign(IUniverse universe) { }
|
|
||||||
public bool Assign(IUniverse universe)
|
public bool Assign(IUniverse universe)
|
||||||
{
|
{
|
||||||
if (Universe is not null)
|
if (Universe is not null)
|
||||||
@@ -101,10 +54,57 @@ public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Count => behaviours.Count;
|
protected virtual void OnAssign(IUniverse universe) { }
|
||||||
public T this[Index index] => behaviours[index];
|
|
||||||
|
|
||||||
public BehaviourCollector()
|
protected abstract void AddBehaviour(T behaviour);
|
||||||
|
protected virtual void OnBehaviourAdd(IBehaviour behaviour) { }
|
||||||
|
private void OnBehaviourAdded(IBehaviourController controller, IBehaviourController.BehaviourAddedArguments args)
|
||||||
|
{
|
||||||
|
if (args.BehaviourAdded is not T tBehaviour)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AddBehaviour(tBehaviour);
|
||||||
|
OnBehaviourAdd(args.BehaviourAdded);
|
||||||
|
OnCollected?.Invoke(this, new(tBehaviour));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract bool RemoveBehaviour(T tBehaviour);
|
||||||
|
protected virtual void OnBehaviourRemove(IBehaviour behaviour) { }
|
||||||
|
private void OnBehaviourRemoved(IBehaviourController controller, IBehaviourController.BehaviourRemovedArguments args)
|
||||||
|
{
|
||||||
|
if (args.BehaviourRemoved is not T tBehaviour)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!RemoveBehaviour(tBehaviour))
|
||||||
|
return;
|
||||||
|
|
||||||
|
OnBehaviourRemove(args.BehaviourRemoved);
|
||||||
|
OnRemoved?.Invoke(this, new(tBehaviour));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUniverseObjectRegistered(IUniverse manager, IUniverse.UniverseObjectRegisteredArguments args)
|
||||||
|
{
|
||||||
|
IUniverseObject universeObject = args.UniverseObjectRegistered;
|
||||||
|
|
||||||
|
universeObject.BehaviourController.OnBehaviourAdded.AddListener(delegateOnBehaviourAdded);
|
||||||
|
universeObject.BehaviourController.OnBehaviourRemoved.AddListener(delegateOnBehaviourRemoved);
|
||||||
|
|
||||||
|
for (int i = 0; i < universeObject.BehaviourController.Count; i++)
|
||||||
|
OnBehaviourAdded(universeObject.BehaviourController, new(universeObject.BehaviourController[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUniverseObjectUnregistered(IUniverse manager, IUniverse.UniverseObjectUnRegisteredArguments args)
|
||||||
|
{
|
||||||
|
IUniverseObject universeObject = args.UniverseObjectUnregistered;
|
||||||
|
|
||||||
|
universeObject.BehaviourController.OnBehaviourAdded.RemoveListener(delegateOnBehaviourAdded);
|
||||||
|
universeObject.BehaviourController.OnBehaviourRemoved.RemoveListener(delegateOnBehaviourRemoved);
|
||||||
|
|
||||||
|
for (int i = 0; i < universeObject.BehaviourController.Count; i++)
|
||||||
|
OnBehaviourRemoved(universeObject.BehaviourController, new(universeObject.BehaviourController[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public BehaviourCollectorBase()
|
||||||
{
|
{
|
||||||
delegateOnBehaviourAdded = OnBehaviourAdded;
|
delegateOnBehaviourAdded = OnBehaviourAdded;
|
||||||
delegateOnBehaviourRemoved = OnBehaviourRemoved;
|
delegateOnBehaviourRemoved = OnBehaviourRemoved;
|
||||||
@@ -112,7 +112,7 @@ public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
|
|||||||
delegateOnUniverseObjectUnregistered = OnUniverseObjectUnregistered;
|
delegateOnUniverseObjectUnregistered = OnUniverseObjectUnregistered;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BehaviourCollector(IUniverse universe)
|
public BehaviourCollectorBase(IUniverse universe)
|
||||||
{
|
{
|
||||||
delegateOnBehaviourAdded = OnBehaviourAdded;
|
delegateOnBehaviourAdded = OnBehaviourAdded;
|
||||||
delegateOnBehaviourRemoved = OnBehaviourRemoved;
|
delegateOnBehaviourRemoved = OnBehaviourRemoved;
|
||||||
104
Engine.Core/Collectors/BehaviourCollectorOrdered.cs
Normal file
104
Engine.Core/Collectors/BehaviourCollectorOrdered.cs
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Engine.Core;
|
||||||
|
|
||||||
|
public class BehaviourCollectorOrdered<TIndex, TItem> : BehaviourCollectorBase<TItem> where TItem : class where TIndex : IComparable
|
||||||
|
{
|
||||||
|
private readonly Event<IBehaviour, IBehaviour.PriorityChangedArguments>.EventHandler delegateOnPriorityChanged = null!;
|
||||||
|
|
||||||
|
private readonly SortedDictionary<TIndex, FastList<TItem>> behaviours = null!;
|
||||||
|
|
||||||
|
private readonly Func<TItem, TIndex> getIndexFunc = null!;
|
||||||
|
private readonly IComparer<TIndex> sortBy = null!;
|
||||||
|
|
||||||
|
private int count = 0;
|
||||||
|
public override int Count => count;
|
||||||
|
|
||||||
|
public override TItem this[Index index]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
int actualIndex = index.IsFromEnd
|
||||||
|
? count - index.Value
|
||||||
|
: index.Value;
|
||||||
|
|
||||||
|
if (actualIndex < 0 || actualIndex >= count)
|
||||||
|
throw new IndexOutOfRangeException();
|
||||||
|
|
||||||
|
int leftIndex = actualIndex;
|
||||||
|
foreach ((TIndex i, FastList<TItem> list) in behaviours)
|
||||||
|
{
|
||||||
|
if (leftIndex < list.Count)
|
||||||
|
return list[leftIndex];
|
||||||
|
leftIndex -= list.Count;
|
||||||
|
}
|
||||||
|
throw new IndexOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool RemoveBehaviour(TItem tBehaviour)
|
||||||
|
{
|
||||||
|
TIndex index = getIndexFunc(tBehaviour);
|
||||||
|
if (!behaviours.TryGetValue(index, out FastList<TItem>? list))
|
||||||
|
throw new Exceptions.NotFoundException($"Index of '{index}' is not found in the collector");
|
||||||
|
|
||||||
|
if (!list.Remove(tBehaviour))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
count--;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void AddBehaviour(TItem behaviour)
|
||||||
|
{
|
||||||
|
TIndex key = getIndexFunc(behaviour);
|
||||||
|
if (!behaviours.TryGetValue(key, out FastList<TItem>? list))
|
||||||
|
behaviours[key] = list = [];
|
||||||
|
|
||||||
|
count++;
|
||||||
|
list.Add(behaviour);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnBehaviourAdd(IBehaviour behaviour) => behaviour.OnPriorityChanged.AddListener(delegateOnPriorityChanged);
|
||||||
|
protected override void OnBehaviourRemove(IBehaviour behaviour) => behaviour.OnPriorityChanged.RemoveListener(delegateOnPriorityChanged);
|
||||||
|
|
||||||
|
private void OnPriorityChanged(IBehaviour sender, IBehaviour.PriorityChangedArguments args)
|
||||||
|
{
|
||||||
|
TItem behaviour = (TItem)sender;
|
||||||
|
RemoveBehaviour(behaviour);
|
||||||
|
AddBehaviour(behaviour);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BehaviourCollectorOrdered(Func<TItem, TIndex> getIndexFunc, Comparison<TIndex> sortBy)
|
||||||
|
{
|
||||||
|
delegateOnPriorityChanged = OnPriorityChanged;
|
||||||
|
this.getIndexFunc = getIndexFunc;
|
||||||
|
this.sortBy = Comparer<TIndex>.Create(sortBy);
|
||||||
|
behaviours = new(this.sortBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BehaviourCollectorOrdered(IUniverse universe, Func<TItem, TIndex> getIndexFunc, Comparison<TIndex> sortBy) : base(universe)
|
||||||
|
{
|
||||||
|
delegateOnPriorityChanged = OnPriorityChanged;
|
||||||
|
this.getIndexFunc = getIndexFunc;
|
||||||
|
this.sortBy = Comparer<TIndex>.Create(sortBy);
|
||||||
|
behaviours = new(this.sortBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BehaviourCollectorOrdered(Func<TItem, TIndex> getIndexFunc, IComparer<TIndex> sortBy)
|
||||||
|
{
|
||||||
|
this.getIndexFunc = getIndexFunc;
|
||||||
|
delegateOnPriorityChanged = OnPriorityChanged;
|
||||||
|
this.sortBy = sortBy;
|
||||||
|
behaviours = new(sortBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BehaviourCollectorOrdered(IUniverse universe, Func<TItem, TIndex> getIndexFunc, IComparer<TIndex> sortBy) : base(universe)
|
||||||
|
{
|
||||||
|
delegateOnPriorityChanged = OnPriorityChanged;
|
||||||
|
this.getIndexFunc = getIndexFunc;
|
||||||
|
this.sortBy = sortBy;
|
||||||
|
behaviours = new(sortBy);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,25 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Debug;
|
namespace Engine.Core.Debug;
|
||||||
|
|
||||||
public static class Assert
|
public static class Assert
|
||||||
{
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void AssertTrue(bool value, string errorMessage)
|
||||||
|
=> System.Diagnostics.Debug.Assert(value, errorMessage);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void AssertFalse(bool value, string errorMessage)
|
||||||
|
=> System.Diagnostics.Debug.Assert(!value, errorMessage);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void AssertNull(object? value, string errorMessage)
|
||||||
|
=> System.Diagnostics.Debug.Assert(value is null, errorMessage);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void AssertNotNull(object? value, string errorMessage)
|
||||||
|
=> System.Diagnostics.Debug.Assert(value is not null, errorMessage);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void AssertInitialized(IInitializable initializable)
|
public static void AssertInitialized(IInitializable initializable)
|
||||||
=> System.Diagnostics.Debug.Assert(initializable.IsInitialized, $"{initializable.GetType().Name} must be initialized");
|
=> System.Diagnostics.Debug.Assert(initializable.IsInitialized, $"{initializable.GetType().Name} must be initialized");
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Debug;
|
namespace Engine.Core.Debug;
|
||||||
|
|
||||||
public class ConsoleLogger : LoggerBase
|
public class ConsoleLogger : LoggerBase
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Debug;
|
namespace Engine.Core.Debug;
|
||||||
|
|
||||||
public class FileLogger : LoggerBase
|
public class FileLogger : LoggerBase
|
||||||
{
|
{
|
||||||
@@ -14,6 +14,9 @@ public class FileLogger : LoggerBase
|
|||||||
|
|
||||||
public FileLogger(string filePath)
|
public FileLogger(string filePath)
|
||||||
{
|
{
|
||||||
|
if (!filePath.EndsWith(".log"))
|
||||||
|
filePath += ".log";
|
||||||
|
|
||||||
FilePath = filePath;
|
FilePath = filePath;
|
||||||
|
|
||||||
bool isRelativePath = Path.GetFullPath(filePath).CompareTo(filePath) != 0;
|
bool isRelativePath = Path.GetFullPath(filePath).CompareTo(filePath) != 0;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core.Debug;
|
namespace Engine.Core.Debug;
|
||||||
|
|
||||||
public interface ILogger
|
public interface ILogger
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Debug;
|
namespace Engine.Core.Debug;
|
||||||
|
|
||||||
public abstract class LoggerBase : ILogger
|
public abstract class LoggerBase : ILogger
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core.Debug;
|
namespace Engine.Core.Debug;
|
||||||
|
|
||||||
public class LoggerContainer : Behaviour, ILogger
|
public class LoggerContainer : Behaviour, ILogger
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Debug;
|
namespace Engine.Core.Debug;
|
||||||
|
|
||||||
public static class LoggerExtensions
|
public static class LoggerExtensions
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core.Debug;
|
namespace Engine.Core.Debug;
|
||||||
|
|
||||||
public class LoggerWrapper(ILogger firstLogger, ILogger secondLogger) : ILogger
|
public class LoggerWrapper(ILogger firstLogger, ILogger secondLogger) : ILogger
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core.Debug;
|
namespace Engine.Core.Debug;
|
||||||
|
|
||||||
public static class LoggerWrapperExtensions
|
public static class LoggerWrapperExtensions
|
||||||
{
|
{
|
||||||
|
|||||||
71
Engine.Core/Debug/RotatingFileLogger.cs
Normal file
71
Engine.Core/Debug/RotatingFileLogger.cs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Engine.Core.Debug;
|
||||||
|
|
||||||
|
public class RotatingFileLogger : ILogger
|
||||||
|
{
|
||||||
|
public readonly FileLogger FileLogger = null!;
|
||||||
|
public readonly string Directory = string.Empty;
|
||||||
|
public readonly int RotateLength = 3;
|
||||||
|
|
||||||
|
public RotatingFileLogger(string directory, string namePrefix, string nameSuffix = "", int rotateLength = 3)
|
||||||
|
{
|
||||||
|
RotateLength = rotateLength;
|
||||||
|
|
||||||
|
string fileName = Path.Combine(directory, namePrefix);
|
||||||
|
if (!string.IsNullOrWhiteSpace(nameSuffix))
|
||||||
|
fileName += $"_{nameSuffix}";
|
||||||
|
|
||||||
|
bool isRelativePath = Path.GetFullPath(fileName).CompareTo(fileName) != 0;
|
||||||
|
|
||||||
|
if (isRelativePath)
|
||||||
|
fileName = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName));
|
||||||
|
|
||||||
|
if (File.Exists($"{fileName}.log"))
|
||||||
|
RenameExistingLogs(fileName, RotateLength);
|
||||||
|
|
||||||
|
FileLogger = new(fileName);
|
||||||
|
|
||||||
|
Directory = Path.GetDirectoryName(fileName) ?? throw new("Unexpected error on getting directory of logger path");
|
||||||
|
RotateLastLogs(Directory, namePrefix, RotateLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RenameExistingLogs(string filePath, int rotateLength)
|
||||||
|
{
|
||||||
|
for (int i = rotateLength - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
string source = i == 0
|
||||||
|
? $"{filePath}.log"
|
||||||
|
: $"{filePath}_{i}.log";
|
||||||
|
|
||||||
|
string dest = $"{filePath}_{i + 1}.log";
|
||||||
|
|
||||||
|
if (!File.Exists(source))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (File.Exists(dest))
|
||||||
|
File.Delete(dest);
|
||||||
|
|
||||||
|
File.Move(source, dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RotateLastLogs(string directory, string prefix, int rotateLength)
|
||||||
|
{
|
||||||
|
IOrderedEnumerable<string> logs = System.IO.Directory.GetFiles(directory, $"{prefix}*.log")
|
||||||
|
.OrderBy(File.GetCreationTime);
|
||||||
|
|
||||||
|
foreach (string file in logs.Skip(rotateLength))
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ILogger.Shared.Log($"Removing log file located at \"{file}\" during rotation.");
|
||||||
|
File.Delete(file);
|
||||||
|
}
|
||||||
|
catch (Exception e) { ILogger.Shared.LogException($"Failed to rotate log file at \"{file}\"", e); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public ILogger.Level FilterLevel { get => FileLogger.FilterLevel; set => FileLogger.FilterLevel = value; }
|
||||||
|
public void Log(string message, ILogger.Level level = ILogger.Level.Info, bool force = false) => FileLogger.Log(message, level, force);
|
||||||
|
}
|
||||||
@@ -4,7 +4,8 @@
|
|||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<ImplicitUsings>false</ImplicitUsings>
|
<ImplicitUsings>false</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<RootNamespace>Syntriax.Engine.Core</RootNamespace>
|
<RootNamespace>Engine.Core</RootNamespace>
|
||||||
|
<AssemblyName>Engine.Core</AssemblyName>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Exceptions;
|
namespace Engine.Core.Exceptions;
|
||||||
|
|
||||||
public class AssignFailedException(string? message) : Exception(message)
|
public class AssignFailedException(string? message) : Exception(message)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
namespace Syntriax.Engine.Core.Exceptions;
|
namespace Engine.Core.Exceptions;
|
||||||
|
|
||||||
public class BehaviourNotFoundException(string? message) : NotFoundException(message);
|
public class BehaviourNotFoundException(string? message) : NotFoundException(message);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Exceptions;
|
namespace Engine.Core.Exceptions;
|
||||||
|
|
||||||
public class NotAssignedException(string? message) : Exception(message)
|
public class NotAssignedException(string? message) : Exception(message)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Exceptions;
|
namespace Engine.Core.Exceptions;
|
||||||
|
|
||||||
public class NotFoundException(string? message) : Exception(message)
|
public class NotFoundException(string? message) : Exception(message)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
namespace Syntriax.Engine.Core.Exceptions;
|
namespace Engine.Core.Exceptions;
|
||||||
|
|
||||||
public class UniverseObjectNotFoundException(string? message) : NotFoundException(message);
|
public class UniverseObjectNotFoundException(string? message) : NotFoundException(message);
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
using Syntriax.Engine.Core.Exceptions;
|
using Engine.Core.Exceptions;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
public static class BehaviourControllerExtensions
|
public static class BehaviourControllerExtensions
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
public static class EnumExtensions
|
public static class EnumExtensions
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
public static class FloatExtensions
|
public static class FloatExtensions
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
public static class TransformExtensions
|
public static class TransformExtensions
|
||||||
{
|
{
|
||||||
@@ -14,4 +14,17 @@ public static class TransformExtensions
|
|||||||
if (localScale.HasValue) transform.LocalScale = localScale.Value;
|
if (localScale.HasValue) transform.LocalScale = localScale.Value;
|
||||||
return transform;
|
return transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ITransform3D SetTransform(this ITransform3D transform,
|
||||||
|
Vector3D? position = null, Quaternion? rotation = null, Vector3D? scale = null,
|
||||||
|
Vector3D? localPosition = null, Quaternion? localRotation = null, Vector3D? localScale = null)
|
||||||
|
{
|
||||||
|
if (position.HasValue) transform.Position = position.Value;
|
||||||
|
if (rotation.HasValue) transform.Rotation = rotation.Value;
|
||||||
|
if (scale.HasValue) transform.Scale = scale.Value;
|
||||||
|
if (localPosition.HasValue) transform.LocalPosition = localPosition.Value;
|
||||||
|
if (localRotation.HasValue) transform.LocalRotation = localRotation.Value;
|
||||||
|
if (localScale.HasValue) transform.LocalScale = localScale.Value;
|
||||||
|
return transform;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using Syntriax.Engine.Core.Exceptions;
|
using Engine.Core.Exceptions;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
public static class UniverseExtensions
|
public static class UniverseExtensions
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
using Syntriax.Engine.Core.Exceptions;
|
using Engine.Core.Exceptions;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
public static class UniverseObjectExtensions
|
public static class UniverseObjectExtensions
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core.Factory.Abstract;
|
namespace Engine.Core.Factory.Abstract;
|
||||||
|
|
||||||
public interface IFactory<TInterface> where TInterface : class
|
public interface IFactory<TInterface> where TInterface : class
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using Syntriax.Engine.Core.Exceptions;
|
using Engine.Core.Exceptions;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Factory;
|
namespace Engine.Core.Factory;
|
||||||
|
|
||||||
public class BehaviourControllerFactory
|
public class BehaviourControllerFactory
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using Syntriax.Engine.Core.Exceptions;
|
using Engine.Core.Exceptions;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Factory;
|
namespace Engine.Core.Factory;
|
||||||
|
|
||||||
public class BehaviourFactory
|
public class BehaviourFactory
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using Syntriax.Engine.Core.Factory.Abstract;
|
using Engine.Core.Factory.Abstract;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Factory;
|
namespace Engine.Core.Factory;
|
||||||
|
|
||||||
public abstract class FactoryBase<TInterface> : IFactory<TInterface>
|
public abstract class FactoryBase<TInterface> : IFactory<TInterface>
|
||||||
where TInterface : class
|
where TInterface : class
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using Syntriax.Engine.Core.Exceptions;
|
using Engine.Core.Exceptions;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Factory;
|
namespace Engine.Core.Factory;
|
||||||
|
|
||||||
public class StateEnableFactory
|
public class StateEnableFactory
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core.Factory;
|
namespace Engine.Core.Factory;
|
||||||
|
|
||||||
public class TransformFactory
|
public class TransformFactory
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using System.Collections.Concurrent;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Factory;
|
namespace Engine.Core.Factory;
|
||||||
|
|
||||||
public static class TypeFactory
|
public static class TypeFactory
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using Syntriax.Engine.Core.Exceptions;
|
using Engine.Core.Exceptions;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core.Factory;
|
namespace Engine.Core.Factory;
|
||||||
|
|
||||||
public class UniverseObjectFactory
|
public class UniverseObjectFactory
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
using Syntriax.Engine.Core.Debug;
|
using Engine.Core.Debug;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
|
// TODO!: every reverse loop has a chance to have more than 1 unsubscription,
|
||||||
|
// for (int i = listeners.Count - 1; i >= 0; i--)
|
||||||
|
// can be replaced with
|
||||||
|
// for (int i = listeners.Count - 1; i >= 0; i = Math.Min(i - 1, listeners.Count - 1))
|
||||||
|
// but this would causes possible double calls on already called callbacks, find a better method.
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a simple event with no parameters.
|
/// Represents a simple event with no parameters.
|
||||||
|
|||||||
102
Engine.Core/Helpers/FastList.cs
Normal file
102
Engine.Core/Helpers/FastList.cs
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Engine.Core;
|
||||||
|
|
||||||
|
public class FastList<T> : IList<T>, IReadOnlyList<T>, IEnumerable<T> where T : notnull
|
||||||
|
{
|
||||||
|
private readonly List<T> items = [];
|
||||||
|
private readonly Dictionary<T, int> indexMap = [];
|
||||||
|
|
||||||
|
public bool IsReadOnly { get; set; } = false;
|
||||||
|
public int Count => items.Count;
|
||||||
|
public T this[int index]
|
||||||
|
{
|
||||||
|
get => items[index];
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
throw new System.Data.ReadOnlyException();
|
||||||
|
|
||||||
|
items[index] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(T item)
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
throw new System.Data.ReadOnlyException();
|
||||||
|
|
||||||
|
indexMap[item] = items.Count;
|
||||||
|
items.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveAt(int i) => Remove(items[i], i);
|
||||||
|
public bool Remove(T item)
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
throw new System.Data.ReadOnlyException();
|
||||||
|
|
||||||
|
if (!indexMap.TryGetValue(item, out int index))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Remove(item, index);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Remove(T item, int index)
|
||||||
|
{
|
||||||
|
int lastIndex = items.Count - 1;
|
||||||
|
T lastItem = items[lastIndex];
|
||||||
|
|
||||||
|
items[index] = lastItem;
|
||||||
|
indexMap[lastItem] = index;
|
||||||
|
|
||||||
|
items.RemoveAt(lastIndex);
|
||||||
|
indexMap.Remove(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Insert(int index, T item)
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
throw new System.Data.ReadOnlyException();
|
||||||
|
|
||||||
|
items.Insert(index, item);
|
||||||
|
|
||||||
|
for (int i = index; i < items.Count; i++)
|
||||||
|
indexMap[items[i]] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
throw new System.Data.ReadOnlyException();
|
||||||
|
|
||||||
|
items.Clear();
|
||||||
|
indexMap.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(T item) => indexMap.ContainsKey(item);
|
||||||
|
public int IndexOf(T item) => items.IndexOf(item);
|
||||||
|
public int BinarySearch(T item, IComparer<T>? comparer = null) => items.BinarySearch(item, comparer);
|
||||||
|
|
||||||
|
public void Sort(IComparer<T> comparer)
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
throw new System.Data.ReadOnlyException();
|
||||||
|
|
||||||
|
items.Sort(comparer);
|
||||||
|
|
||||||
|
for (int i = 0; i < items.Count; i++)
|
||||||
|
indexMap[items[i]] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CopyTo(T[] array, int arrayIndex) => items.CopyTo(array, arrayIndex);
|
||||||
|
|
||||||
|
public IEnumerator<T> GetEnumerator() => items.GetEnumerator();
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
|
||||||
|
public FastList() { }
|
||||||
|
public FastList(int count) { items.Capacity = count; }
|
||||||
|
}
|
||||||
172
Engine.Core/Helpers/FastListOrdered.cs
Normal file
172
Engine.Core/Helpers/FastListOrdered.cs
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Engine.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// TODO This is VEERY experimental, and doesn't work well with the indices access. Use with caution
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TIndex"></typeparam>
|
||||||
|
/// <typeparam name="TItem"></typeparam>
|
||||||
|
public class FastListOrdered<TIndex, TItem> : IList<TItem>, IReadOnlyList<TItem>, IEnumerable<TItem> where TItem : notnull where TIndex : IComparable
|
||||||
|
{
|
||||||
|
private readonly SortedDictionary<TIndex, FastList<TItem>> items = null!;
|
||||||
|
|
||||||
|
private readonly Func<TItem, TIndex> getIndexFunc = null!;
|
||||||
|
private readonly IComparer<TIndex> sortBy = null!;
|
||||||
|
|
||||||
|
private int count = 0;
|
||||||
|
public int Count => count;
|
||||||
|
|
||||||
|
public bool IsReadOnly { get; set; } = false;
|
||||||
|
|
||||||
|
public TItem this[int index]
|
||||||
|
{
|
||||||
|
get { (TIndex tIndex, int i) = GetAt(index); return items[tIndex][i]; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
throw new System.Data.ReadOnlyException();
|
||||||
|
(TIndex tIndex, int i) = GetAt(index); items[tIndex][i] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private (TIndex TIndex, int i) GetAt(Index index)
|
||||||
|
{
|
||||||
|
int actualIndex = index.IsFromEnd
|
||||||
|
? count - index.Value
|
||||||
|
: index.Value;
|
||||||
|
|
||||||
|
if (actualIndex < 0 || actualIndex >= count)
|
||||||
|
throw new IndexOutOfRangeException();
|
||||||
|
|
||||||
|
int leftIndex = actualIndex;
|
||||||
|
foreach ((TIndex i, FastList<TItem> list) in items)
|
||||||
|
{
|
||||||
|
if (leftIndex < list.Count)
|
||||||
|
return (i, leftIndex);
|
||||||
|
leftIndex -= list.Count;
|
||||||
|
}
|
||||||
|
throw new IndexOutOfRangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int IndexOf(TItem item)
|
||||||
|
{
|
||||||
|
int indexCounter = 0;
|
||||||
|
foreach ((TIndex index, FastList<TItem> list) in items)
|
||||||
|
{
|
||||||
|
int i = list.IndexOf(item);
|
||||||
|
if (i != -1)
|
||||||
|
return indexCounter + i;
|
||||||
|
indexCounter += list.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(TItem item)
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
throw new System.Data.ReadOnlyException();
|
||||||
|
|
||||||
|
TIndex key = getIndexFunc(item);
|
||||||
|
if (!items.TryGetValue(key, out FastList<TItem>? list))
|
||||||
|
items[key] = list = [];
|
||||||
|
|
||||||
|
list.Add(item);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Insert(int index, TItem item)
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
throw new System.Data.ReadOnlyException();
|
||||||
|
|
||||||
|
TIndex tIndex = getIndexFunc(item);
|
||||||
|
if (!items.TryGetValue(tIndex, out FastList<TItem>? list))
|
||||||
|
items[tIndex] = list = [];
|
||||||
|
|
||||||
|
list.Insert(index, item);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Remove(TItem item)
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
throw new System.Data.ReadOnlyException();
|
||||||
|
|
||||||
|
TIndex index = getIndexFunc(item);
|
||||||
|
if (!items.TryGetValue(index, out FastList<TItem>? list))
|
||||||
|
throw new Exceptions.NotFoundException($"Index of '{index}' is not found in the collector");
|
||||||
|
|
||||||
|
if (!list.Remove(item))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
count--;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveAt(int index)
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
throw new System.Data.ReadOnlyException();
|
||||||
|
|
||||||
|
(TIndex tIndex, int i) = GetAt(index);
|
||||||
|
items[tIndex].RemoveAt(i);
|
||||||
|
count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
throw new System.Data.ReadOnlyException();
|
||||||
|
|
||||||
|
foreach ((TIndex index, FastList<TItem> list) in items)
|
||||||
|
list.Clear();
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(TItem item)
|
||||||
|
{
|
||||||
|
foreach ((TIndex index, FastList<TItem> list) in items)
|
||||||
|
if (list.Contains(item))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CopyTo(TItem[] array, int arrayIndex)
|
||||||
|
{
|
||||||
|
int indexCounter = 0;
|
||||||
|
|
||||||
|
foreach ((TIndex index, FastList<TItem> list) in items)
|
||||||
|
{
|
||||||
|
list.CopyTo(array, indexCounter);
|
||||||
|
indexCounter += list.Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<TItem> GetEnumerator()
|
||||||
|
{
|
||||||
|
foreach ((TIndex index, FastList<TItem> list) in items)
|
||||||
|
foreach (TItem item in list)
|
||||||
|
yield return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
|
||||||
|
public FastListOrdered(Func<TItem, TIndex> getIndexFunc, Comparison<TIndex> sortBy)
|
||||||
|
{
|
||||||
|
this.getIndexFunc = getIndexFunc;
|
||||||
|
this.sortBy = Comparer<TIndex>.Create(sortBy);
|
||||||
|
items = new(this.sortBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FastListOrdered(Func<TItem, TIndex> getIndexFunc, IComparer<TIndex> sortBy)
|
||||||
|
{
|
||||||
|
this.getIndexFunc = getIndexFunc;
|
||||||
|
this.sortBy = sortBy;
|
||||||
|
items = new(sortBy);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
public interface IPool<T>
|
public interface IPool<T>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
public class ListPool<T> : IPool<List<T>>
|
public class ListPool<T> : IPool<List<T>>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
public class Pool<T> : IPool<T>
|
public class Pool<T> : IPool<T>
|
||||||
{
|
{
|
||||||
@@ -10,22 +10,25 @@ public class Pool<T> : IPool<T>
|
|||||||
|
|
||||||
private readonly Func<T> generator = null!;
|
private readonly Func<T> generator = null!;
|
||||||
private readonly Queue<T> queue = new();
|
private readonly Queue<T> queue = new();
|
||||||
|
private readonly HashSet<T> queuedHashes = [];
|
||||||
|
|
||||||
public T Get()
|
public T Get()
|
||||||
{
|
{
|
||||||
if (!queue.TryDequeue(out T? result))
|
if (!queue.TryDequeue(out T? result))
|
||||||
result = generator();
|
result = generator();
|
||||||
|
|
||||||
|
queuedHashes.Remove(result);
|
||||||
OnRemoved?.Invoke(this, result);
|
OnRemoved?.Invoke(this, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Return(T item)
|
public void Return(T item)
|
||||||
{
|
{
|
||||||
if (queue.Contains(item))
|
if (queuedHashes.Contains(item))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
queue.Enqueue(item);
|
queue.Enqueue(item);
|
||||||
|
queuedHashes.Add(item);
|
||||||
OnReturned?.Invoke(this, item);
|
OnReturned?.Invoke(this, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
public interface IProgressionTracker : IReadOnlyProgressionTracker
|
public interface IProgressionTracker : IReadOnlyProgressionTracker
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
public interface IReadOnlyProgressionTracker
|
public interface IReadOnlyProgressionTracker
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
public class ProgressionTracker : IProgressionTracker
|
public class ProgressionTracker : IProgressionTracker
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
public record struct ProgressiveTask<T>(IReadOnlyProgressionTracker ProgressionTracker, Task<T> Task)
|
public record struct ProgressiveTask<T>(IReadOnlyProgressionTracker ProgressionTracker, Task<T> Task)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
public static class Math
|
public static class Math
|
||||||
{
|
{
|
||||||
@@ -240,21 +240,33 @@ public static class Math
|
|||||||
public static T Lerp<T>(T x, T y, T t) where T : IFloatingPoint<T> => x + (y - x) * t;
|
public static T Lerp<T>(T x, T y, T t) where T : IFloatingPoint<T> => x + (y - x) * t;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Rounds a number to a specified number of fractional digits.
|
/// Rounds a number to the closest integer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="x">The number to round.</param>
|
/// <param name="x">The number to round.</param>
|
||||||
/// <param name="digits">The number of fractional digits in the return value.</param>
|
/// <param name="roundMode">Specification for how to round <paramref name="x"/> if it is midway between two other numbers.</param>
|
||||||
/// <param name="mode">Specification for how to round <paramref name="x"/> if it is midway between two other numbers.</param>
|
/// <returns>The number <paramref name="x"/> rounded to the closest integer.</returns>
|
||||||
/// <returns>The number <paramref name="x"/> rounded to <paramref name="digits"/> fractional digits.</returns>
|
public static float Round(float x, RoundMode roundMode) => RoundToInt(x, roundMode);
|
||||||
public static float Round(float x, int digits, MidpointRounding mode) => MathF.Round(x, digits, mode);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Rounds a number to an integer.
|
/// Rounds a number to the closest integer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="x">The number to round.</param>
|
/// <param name="x">The number to round.</param>
|
||||||
/// <param name="roundMode">Specification for how to round <paramref name="x"/> if it's midway between two numbers</param>
|
/// <param name="roundMode">Specification for how to round <paramref name="x"/> if it's midway between two numbers</param>
|
||||||
/// <returns></returns>
|
/// <returns>The number <paramref name="x"/> rounded to the closest integer.</returns>
|
||||||
public static int RoundToInt(float x, RoundMode roundMode = RoundMode.Ceil) => (int)MathF.Round(x, 0, roundMode == RoundMode.Ceil ? MidpointRounding.ToPositiveInfinity : MidpointRounding.ToNegativeInfinity);
|
public static int RoundToInt(float x, RoundMode roundMode = RoundMode.Ceil)
|
||||||
|
{
|
||||||
|
float remainder = x.Mod(1f);
|
||||||
|
|
||||||
|
if (remainder == .5f)
|
||||||
|
if (roundMode == RoundMode.Floor)
|
||||||
|
return (int)x;
|
||||||
|
else
|
||||||
|
return (int)(x + .5f);
|
||||||
|
|
||||||
|
if (x < 0f)
|
||||||
|
return (int)(x - .5f);
|
||||||
|
return (int)(x + .5f);
|
||||||
|
}
|
||||||
public enum RoundMode { Ceil, Floor };
|
public enum RoundMode { Ceil, Floor };
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
public static class MathExtensions
|
public static class MathExtensions
|
||||||
{
|
{
|
||||||
@@ -81,7 +81,7 @@ public static class MathExtensions
|
|||||||
public static T Lerp<T>(this T x, T y, T t) where T : IFloatingPoint<T> => Math.Lerp(x, y, t);
|
public static T Lerp<T>(this T x, T y, T t) where T : IFloatingPoint<T> => Math.Lerp(x, y, t);
|
||||||
|
|
||||||
/// <inheritdoc cref="Math.Round(float, int, MidpointRounding)" />
|
/// <inheritdoc cref="Math.Round(float, int, MidpointRounding)" />
|
||||||
public static float Round(this float x, int digits, MidpointRounding mode) => Math.Round(x, digits, mode);
|
public static float Round(this float x, Math.RoundMode mode) => Math.Round(x, mode);
|
||||||
|
|
||||||
/// <inheritdoc cref="Math.RoundToInt(float, Math.RoundMode)" />
|
/// <inheritdoc cref="Math.RoundToInt(float, Math.RoundMode)" />
|
||||||
public static int RoundToInt(this float x, Math.RoundMode roundMode = Math.RoundMode.Ceil) => Math.RoundToInt(x, roundMode);
|
public static int RoundToInt(this float x, Math.RoundMode roundMode = Math.RoundMode.Ceil) => Math.RoundToInt(x, roundMode);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Syntriax.Engine.Core
|
namespace Engine.Core
|
||||||
{
|
{
|
||||||
// This is pretty much so the assembly gets loaded automatically because
|
// This is pretty much so the assembly gets loaded automatically because
|
||||||
// the builds include the assembly but sometimes doesn't link load it at startup.
|
// the builds include the assembly but sometimes doesn't link load it at startup.
|
||||||
|
|||||||
@@ -1,109 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents an Axis-Aligned Bounding Box (AABB) in 2D space.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="lowerBoundary">The lower boundary of the <see cref="AABB"/>.</param>
|
|
||||||
/// <param name="upperBoundary">The upper boundary of the <see cref="AABB"/>.</param>
|
|
||||||
/// <remarks>
|
|
||||||
/// Initializes a new instance of the <see cref="AABB"/> struct with the specified lower and upper boundaries.
|
|
||||||
/// </remarks>
|
|
||||||
[System.Diagnostics.DebuggerDisplay("LowerBoundary: {LowerBoundary.ToString(), nq}, UpperBoundary: {UpperBoundary.ToString(), nq}")]
|
|
||||||
public readonly struct AABB(Vector2D lowerBoundary, Vector2D upperBoundary)
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The lower boundary of the <see cref="AABB"/>.
|
|
||||||
/// </summary>
|
|
||||||
public readonly Vector2D LowerBoundary = lowerBoundary;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The upper boundary of the <see cref="AABB"/>.
|
|
||||||
/// </summary>
|
|
||||||
public readonly Vector2D UpperBoundary = upperBoundary;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the center point of the <see cref="AABB"/>.
|
|
||||||
/// </summary>
|
|
||||||
public readonly Vector2D Center => (LowerBoundary + UpperBoundary) * .5f;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the size of the <see cref="AABB"/>.
|
|
||||||
/// </summary>
|
|
||||||
public readonly Vector2D Size => LowerBoundary.FromTo(UpperBoundary).Abs();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets half the size of the <see cref="AABB"/>.
|
|
||||||
/// </summary>
|
|
||||||
public readonly Vector2D SizeHalf => Size * .5f;
|
|
||||||
|
|
||||||
public static bool operator ==(AABB left, AABB right) => left.UpperBoundary == right.UpperBoundary && left.LowerBoundary == right.LowerBoundary;
|
|
||||||
public static bool operator !=(AABB left, AABB right) => left.UpperBoundary != right.UpperBoundary || left.LowerBoundary != right.LowerBoundary;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates an <see cref="AABB"/> from a collection of <see cref="Vector2D"/>s.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="vectors">The collection of <see cref="Vector2D"/>s.</param>
|
|
||||||
/// <returns>An <see cref="AABB"/> that bounds all the <see cref="Vector2D"/>s.</returns>
|
|
||||||
public static AABB FromVectors(IEnumerable<Vector2D> vectors)
|
|
||||||
{
|
|
||||||
int counter = 0;
|
|
||||||
|
|
||||||
Vector2D lowerBoundary = new(float.MaxValue, float.MaxValue);
|
|
||||||
Vector2D upperBoundary = new(float.MinValue, float.MinValue);
|
|
||||||
|
|
||||||
foreach (Vector2D vector in vectors)
|
|
||||||
{
|
|
||||||
lowerBoundary = Vector2D.Min(lowerBoundary, vector);
|
|
||||||
upperBoundary = Vector2D.Max(upperBoundary, vector);
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (counter < 2)
|
|
||||||
throw new System.ArgumentException($"Parameter {nameof(vectors)} must have at least 2 items.");
|
|
||||||
|
|
||||||
return new(lowerBoundary, upperBoundary);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if two <see cref="AABB"/>s are approximately equal.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="left">The first <see cref="AABB"/>.</param>
|
|
||||||
/// <param name="right">The second <see cref="AABB"/>.</param>
|
|
||||||
/// <param name="epsilon">The epsilon range.</param>
|
|
||||||
/// <returns><see cref="true"/> if the <see cref="AABB"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
|
||||||
public static bool ApproximatelyEquals(AABB left, AABB right, float epsilon = float.Epsilon)
|
|
||||||
=> left.LowerBoundary.ApproximatelyEquals(right.LowerBoundary, epsilon) && left.UpperBoundary.ApproximatelyEquals(right.UpperBoundary, epsilon);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the specified object is equal to the current <see cref="AABB"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj">The object to compare with the current <see cref="AABB"/>.</param>
|
|
||||||
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="AABB"/>; otherwise, <see cref="false"/>.</returns>
|
|
||||||
public override bool Equals(object? obj) => obj is AABB aabb && this == aabb;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Generates a hash code for the <see cref="AABB"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A hash code for the <see cref="AABB"/>.</returns>
|
|
||||||
public override int GetHashCode() => System.HashCode.Combine(LowerBoundary, UpperBoundary);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts the <see cref="AABB"/> to its string representation.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A string representation of the <see cref="AABB"/>.</returns>
|
|
||||||
public override string ToString() => $"{nameof(AABB)}({LowerBoundary}, {UpperBoundary})";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Provides extension methods for the <see cref="AABB"/> struct.
|
|
||||||
/// </summary>
|
|
||||||
public static class AABBExtensions
|
|
||||||
{
|
|
||||||
/// <inheritdoc cref="AABB.ToAABB" />
|
|
||||||
public static AABB ToAABB(this IEnumerable<Vector2D> vectors) => AABB.FromVectors(vectors);
|
|
||||||
|
|
||||||
/// <inheritdoc cref="AABB.ApproximatelyEquals" />
|
|
||||||
public static bool ApproximatelyEquals(this AABB left, AABB right, float epsilon = float.Epsilon) => AABB.ApproximatelyEquals(left, right, epsilon);
|
|
||||||
}
|
|
||||||
114
Engine.Core/Primitives/AABB2D.cs
Normal file
114
Engine.Core/Primitives/AABB2D.cs
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Engine.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an Axis-Aligned Bounding Box (AABB) in 2D space.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lowerBoundary">The lower boundary of the <see cref="AABB2D"/>.</param>
|
||||||
|
/// <param name="upperBoundary">The upper boundary of the <see cref="AABB2D"/>.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// Initializes a new instance of the <see cref="AABB2D"/> struct with the specified lower and upper boundaries.
|
||||||
|
/// </remarks>
|
||||||
|
[System.Diagnostics.DebuggerDisplay("LowerBoundary: {LowerBoundary.ToString(), nq}, UpperBoundary: {UpperBoundary.ToString(), nq}")]
|
||||||
|
public readonly struct AABB2D(Vector2D lowerBoundary, Vector2D upperBoundary) : IEquatable<AABB2D>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The lower boundary of the <see cref="AABB2D"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Vector2D LowerBoundary = lowerBoundary;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The upper boundary of the <see cref="AABB2D"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Vector2D UpperBoundary = upperBoundary;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the center point of the <see cref="AABB2D"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Vector2D Center => (LowerBoundary + UpperBoundary) * .5f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the size of the <see cref="AABB2D"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Vector2D Size => LowerBoundary.FromTo(UpperBoundary).Abs();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets half the size of the <see cref="AABB2D"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Vector2D SizeHalf => Size * .5f;
|
||||||
|
|
||||||
|
public static bool operator ==(AABB2D left, AABB2D right) => left.UpperBoundary == right.UpperBoundary && left.LowerBoundary == right.LowerBoundary;
|
||||||
|
public static bool operator !=(AABB2D left, AABB2D right) => left.UpperBoundary != right.UpperBoundary || left.LowerBoundary != right.LowerBoundary;
|
||||||
|
|
||||||
|
public static implicit operator AABB2D(Circle circle) => new(circle.Center - new Vector2D(circle.Radius, circle.Radius), circle.Center + new Vector2D(circle.Radius, circle.Radius));
|
||||||
|
public static implicit operator AABB2D(Shape2D shape) => FromVectors(shape.Vertices);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an <see cref="AABB2D"/> from a collection of <see cref="Vector2D"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="vectors">The collection of <see cref="Vector2D"/>s.</param>
|
||||||
|
/// <returns>An <see cref="AABB2D"/> that bounds all the <see cref="Vector2D"/>s.</returns>
|
||||||
|
public static AABB2D FromVectors(IEnumerable<Vector2D> vectors)
|
||||||
|
{
|
||||||
|
int counter = 0;
|
||||||
|
|
||||||
|
Vector2D lowerBoundary = new(float.MaxValue, float.MaxValue);
|
||||||
|
Vector2D upperBoundary = new(float.MinValue, float.MinValue);
|
||||||
|
|
||||||
|
foreach (Vector2D vector in vectors)
|
||||||
|
{
|
||||||
|
lowerBoundary = Vector2D.Min(lowerBoundary, vector);
|
||||||
|
upperBoundary = Vector2D.Max(upperBoundary, vector);
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (counter < 2)
|
||||||
|
throw new ArgumentException($"Parameter {nameof(vectors)} must have at least 2 items.");
|
||||||
|
|
||||||
|
return new(lowerBoundary, upperBoundary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if two <see cref="AABB2D"/>s are approximately equal.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The first <see cref="AABB2D"/>.</param>
|
||||||
|
/// <param name="right">The second <see cref="AABB2D"/>.</param>
|
||||||
|
/// <param name="epsilon">The epsilon range.</param>
|
||||||
|
/// <returns><see cref="true"/> if the <see cref="AABB2D"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public static bool ApproximatelyEquals(AABB2D left, AABB2D right, float epsilon = float.Epsilon)
|
||||||
|
=> left.LowerBoundary.ApproximatelyEquals(right.LowerBoundary, epsilon) && left.UpperBoundary.ApproximatelyEquals(right.UpperBoundary, epsilon);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified object is equal to the current <see cref="AABB2D"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to compare with the current <see cref="AABB2D"/>.</param>
|
||||||
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="AABB2D"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public override bool Equals(object? obj) => obj is AABB2D aabb && this == aabb;
|
||||||
|
public bool Equals(AABB2D other) => this == other;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a hash code for the <see cref="AABB2D"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A hash code for the <see cref="AABB2D"/>.</returns>
|
||||||
|
public override int GetHashCode() => System.HashCode.Combine(LowerBoundary, UpperBoundary);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="AABB2D"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="AABB2D"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(AABB2D)}({LowerBoundary}, {UpperBoundary})";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides extension methods for the <see cref="AABB2D"/> struct.
|
||||||
|
/// </summary>
|
||||||
|
public static class AABBExtensions
|
||||||
|
{
|
||||||
|
/// <inheritdoc cref="AABB2D.FromVectors" />
|
||||||
|
public static AABB2D ToAABB(this IEnumerable<Vector2D> vectors) => AABB2D.FromVectors(vectors);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="AABB2D.ApproximatelyEquals" />
|
||||||
|
public static bool ApproximatelyEquals(this AABB2D left, AABB2D right, float epsilon = float.Epsilon) => AABB2D.ApproximatelyEquals(left, right, epsilon);
|
||||||
|
}
|
||||||
113
Engine.Core/Primitives/AABB3D.cs
Normal file
113
Engine.Core/Primitives/AABB3D.cs
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Engine.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an Axis-Aligned Bounding Box (AABB) in 3D space.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lowerBoundary">The lower boundary of the <see cref="AABB3D"/>.</param>
|
||||||
|
/// <param name="upperBoundary">The upper boundary of the <see cref="AABB3D"/>.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// Initializes a new instance of the <see cref="AABB3D"/> struct with the specified lower and upper boundaries.
|
||||||
|
/// </remarks>
|
||||||
|
[System.Diagnostics.DebuggerDisplay("LowerBoundary: {LowerBoundary.ToString(), nq}, UpperBoundary: {UpperBoundary.ToString(), nq}")]
|
||||||
|
public readonly struct AABB3D(Vector3D lowerBoundary, Vector3D upperBoundary) : IEquatable<AABB3D>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The lower boundary of the <see cref="AABB3D"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Vector3D LowerBoundary = lowerBoundary;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The upper boundary of the <see cref="AABB3D"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Vector3D UpperBoundary = upperBoundary;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the center point of the <see cref="AABB3D"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Vector3D Center => (LowerBoundary + UpperBoundary) * .5f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the size of the <see cref="AABB3D"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Vector3D Size => LowerBoundary.FromTo(UpperBoundary).Abs();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets half the size of the <see cref="AABB3D"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Vector3D SizeHalf => Size * .5f;
|
||||||
|
|
||||||
|
public static bool operator ==(AABB3D left, AABB3D right) => left.UpperBoundary == right.UpperBoundary && left.LowerBoundary == right.LowerBoundary;
|
||||||
|
public static bool operator !=(AABB3D left, AABB3D right) => left.UpperBoundary != right.UpperBoundary || left.LowerBoundary != right.LowerBoundary;
|
||||||
|
|
||||||
|
public static implicit operator AABB3D(Sphere3D sphere) => new(sphere.Center - new Vector3D(sphere.Radius, sphere.Radius, sphere.Radius), sphere.Center + new Vector3D(sphere.Radius, sphere.Radius, sphere.Radius));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an <see cref="AABB3D"/> from a collection of <see cref="Vector3D"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="vectors">The collection of <see cref="Vector3D"/>s.</param>
|
||||||
|
/// <returns>An <see cref="AABB3D"/> that bounds all the <see cref="Vector3D"/>s.</returns>
|
||||||
|
public static AABB3D FromVectors(IEnumerable<Vector3D> vectors)
|
||||||
|
{
|
||||||
|
int counter = 0;
|
||||||
|
|
||||||
|
Vector3D lowerBoundary = new(float.MaxValue, float.MaxValue, float.MaxValue);
|
||||||
|
Vector3D upperBoundary = new(float.MinValue, float.MinValue, float.MinValue);
|
||||||
|
|
||||||
|
foreach (Vector3D vector in vectors)
|
||||||
|
{
|
||||||
|
lowerBoundary = Vector3D.Min(lowerBoundary, vector);
|
||||||
|
upperBoundary = Vector3D.Max(upperBoundary, vector);
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (counter < 2)
|
||||||
|
throw new ArgumentException($"Parameter {nameof(vectors)} must have at least 2 items.");
|
||||||
|
|
||||||
|
return new(lowerBoundary, upperBoundary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if two <see cref="AABB3D"/>s are approximately equal.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The first <see cref="AABB3D"/>.</param>
|
||||||
|
/// <param name="right">The second <see cref="AABB3D"/>.</param>
|
||||||
|
/// <param name="epsilon">The epsilon range.</param>
|
||||||
|
/// <returns><see cref="true"/> if the <see cref="AABB3D"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public static bool ApproximatelyEquals(AABB3D left, AABB3D right, float epsilon = float.Epsilon)
|
||||||
|
=> left.LowerBoundary.ApproximatelyEquals(right.LowerBoundary, epsilon) && left.UpperBoundary.ApproximatelyEquals(right.UpperBoundary, epsilon);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified object is equal to the current <see cref="AABB3D"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to compare with the current <see cref="AABB3D"/>.</param>
|
||||||
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="AABB3D"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public override bool Equals(object? obj) => obj is AABB3D aabb && this == aabb;
|
||||||
|
public bool Equals(AABB3D other) => this == other;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a hash code for the <see cref="AABB3D"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A hash code for the <see cref="AABB3D"/>.</returns>
|
||||||
|
public override int GetHashCode() => System.HashCode.Combine(LowerBoundary, UpperBoundary);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="AABB3D"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="AABB3D"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(AABB3D)}({LowerBoundary}, {UpperBoundary})";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides extension methods for the <see cref="AABB3D"/> struct.
|
||||||
|
/// </summary>
|
||||||
|
public static class AABB3DExtensions
|
||||||
|
{
|
||||||
|
/// <inheritdoc cref="AABB3D.FromVectors" />
|
||||||
|
public static AABB3D ToAABB3D(this IEnumerable<Vector3D> vectors) => AABB3D.FromVectors(vectors);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="AABB3D.ApproximatelyEquals" />
|
||||||
|
public static bool ApproximatelyEquals(this AABB3D left, AABB3D right, float epsilon = float.Epsilon) => AABB3D.ApproximatelyEquals(left, right, epsilon);
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a 2D circle.
|
/// Represents a 2D circle.
|
||||||
@@ -11,10 +12,10 @@ namespace Syntriax.Engine.Core;
|
|||||||
/// Initializes a new instance of the <see cref="Circle"/> struct with the specified center and radius.
|
/// Initializes a new instance of the <see cref="Circle"/> struct with the specified center and radius.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[DebuggerDisplay("Center: {Center.ToString(),nq}, Radius: {Radius}")]
|
[DebuggerDisplay("Center: {Center.ToString(),nq}, Radius: {Radius}")]
|
||||||
public readonly struct Circle(Vector2D center, float radius)
|
public readonly struct Circle(Vector2D center, float radius) : IEquatable<Circle>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The center of the circle.
|
/// The center of the <see cref="Circle"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly Vector2D Center = center;
|
public readonly Vector2D Center = center;
|
||||||
|
|
||||||
@@ -41,6 +42,8 @@ public readonly struct Circle(Vector2D center, float radius)
|
|||||||
public static bool operator ==(Circle left, Circle right) => left.Center == right.Center && left.Radius == right.Radius;
|
public static bool operator ==(Circle left, Circle right) => left.Center == right.Center && left.Radius == right.Radius;
|
||||||
public static bool operator !=(Circle left, Circle right) => left.Center != right.Center || left.Radius != right.Radius;
|
public static bool operator !=(Circle left, Circle right) => left.Center != right.Center || left.Radius != right.Radius;
|
||||||
|
|
||||||
|
public static implicit operator Circle(Sphere3D sphere) => new(sphere.Center, sphere.Radius);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the center of the <see cref="Circle"/>.
|
/// Sets the center of the <see cref="Circle"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -87,6 +90,7 @@ public readonly struct Circle(Vector2D center, float radius)
|
|||||||
/// <param name="obj">The object to compare with the current <see cref="Circle"/>.</param>
|
/// <param name="obj">The object to compare with the current <see cref="Circle"/>.</param>
|
||||||
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Circle"/>; otherwise, <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Circle"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
public override bool Equals(object? obj) => obj is Circle circle && this == circle;
|
public override bool Equals(object? obj) => obj is Circle circle && this == circle;
|
||||||
|
public bool Equals(Circle other) => this == other;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a hash code for the <see cref="Circle"/>.
|
/// Generates a hash code for the <see cref="Circle"/>.
|
||||||
@@ -116,7 +120,7 @@ public static class CircleExtensions
|
|||||||
public static Circle Displace(this Circle circle, Vector2D displaceVector) => Circle.Displace(circle, displaceVector);
|
public static Circle Displace(this Circle circle, Vector2D displaceVector) => Circle.Displace(circle, displaceVector);
|
||||||
|
|
||||||
/// <inheritdoc cref="Circle.Project(Circle, Vector2D)" />
|
/// <inheritdoc cref="Circle.Project(Circle, Vector2D)" />
|
||||||
public static Projection1D ToProjection(this Circle circle, Vector2D projectionVector) => Circle.Project(circle, projectionVector);
|
public static Projection1D ProjectTo(this Circle circle, Vector2D projectionVector) => Circle.Project(circle, projectionVector);
|
||||||
|
|
||||||
/// <inheritdoc cref="Circle.Transform(ITransform2D, Circle)" />
|
/// <inheritdoc cref="Circle.Transform(ITransform2D, Circle)" />
|
||||||
public static Circle Transform(this ITransform2D transform, Circle circle) => Circle.Transform(transform, circle);
|
public static Circle Transform(this ITransform2D transform, Circle circle) => Circle.Transform(transform, circle);
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
using System;
|
||||||
|
|
||||||
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an HSV color.
|
/// Represents an HSV color.
|
||||||
@@ -10,7 +12,7 @@ namespace Syntriax.Engine.Core;
|
|||||||
/// Initializes a new instance of the <see cref="ColorHSV"/> struct with the specified values.
|
/// Initializes a new instance of the <see cref="ColorHSV"/> struct with the specified values.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
|
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
|
||||||
public readonly struct ColorHSV(float hue, float saturation, float value)
|
public readonly struct ColorHSV(float hue, float saturation, float value) : IEquatable<ColorHSV>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Hue value of the <see cref="ColorHSV"/>.
|
/// The Hue value of the <see cref="ColorHSV"/>.
|
||||||
@@ -112,6 +114,7 @@ public readonly struct ColorHSV(float hue, float saturation, float value)
|
|||||||
/// <param name="obj">The object to compare with the current <see cref="ColorHSV"/>.</param>
|
/// <param name="obj">The object to compare with the current <see cref="ColorHSV"/>.</param>
|
||||||
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="ColorHSV"/>; otherwise, <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="ColorHSV"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
public override bool Equals(object? obj) => obj is ColorHSV colorHSV && this == colorHSV;
|
public override bool Equals(object? obj) => obj is ColorHSV colorHSV && this == colorHSV;
|
||||||
|
public bool Equals(ColorHSV other) => this == other;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a hash code for the <see cref="ColorHSV"/>.
|
/// Generates a hash code for the <see cref="ColorHSV"/>.
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
using System;
|
||||||
|
|
||||||
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an HSV color.
|
/// Represents an HSV color.
|
||||||
@@ -11,7 +13,7 @@ namespace Syntriax.Engine.Core;
|
|||||||
/// Initializes a new instance of the <see cref="ColorHSVA"/> struct with the specified values.
|
/// Initializes a new instance of the <see cref="ColorHSVA"/> struct with the specified values.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
|
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
|
||||||
public readonly struct ColorHSVA(float hue, float saturation, float value, float alpha = 1)
|
public readonly struct ColorHSVA(float hue, float saturation, float value, float alpha = 1) : IEquatable<ColorHSVA>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Hue value of the <see cref="ColorHSVA"/>.
|
/// The Hue value of the <see cref="ColorHSVA"/>.
|
||||||
@@ -150,6 +152,7 @@ public readonly struct ColorHSVA(float hue, float saturation, float value, float
|
|||||||
/// <param name="obj">The object to compare with the current <see cref="ColorHSVA"/>.</param>
|
/// <param name="obj">The object to compare with the current <see cref="ColorHSVA"/>.</param>
|
||||||
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="ColorHSVA"/>; otherwise, <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="ColorHSVA"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
public override bool Equals(object? obj) => obj is ColorHSVA colorHSVA && this == colorHSVA;
|
public override bool Equals(object? obj) => obj is ColorHSVA colorHSVA && this == colorHSVA;
|
||||||
|
public bool Equals(ColorHSVA other) => this == other;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a hash code for the <see cref="ColorHSVA"/>.
|
/// Generates a hash code for the <see cref="ColorHSVA"/>.
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
using System;
|
||||||
|
|
||||||
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an RGB color.
|
/// Represents an RGB color.
|
||||||
@@ -10,7 +12,7 @@ namespace Syntriax.Engine.Core;
|
|||||||
/// Initializes a new instance of the <see cref="ColorRGB"/> struct with the specified values.
|
/// Initializes a new instance of the <see cref="ColorRGB"/> struct with the specified values.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
|
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
|
||||||
public readonly struct ColorRGB(byte r, byte g, byte b)
|
public readonly struct ColorRGB(byte r, byte g, byte b) : IEquatable<ColorRGB>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Red value of the <see cref="ColorRGB"/>.
|
/// The Red value of the <see cref="ColorRGB"/>.
|
||||||
@@ -102,6 +104,7 @@ public readonly struct ColorRGB(byte r, byte g, byte b)
|
|||||||
/// <param name="obj">The object to compare with the current <see cref="ColorRGB"/>.</param>
|
/// <param name="obj">The object to compare with the current <see cref="ColorRGB"/>.</param>
|
||||||
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="ColorRGB"/>; otherwise, <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="ColorRGB"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
public override bool Equals(object? obj) => obj is ColorRGB colorRGB && this == colorRGB;
|
public override bool Equals(object? obj) => obj is ColorRGB colorRGB && this == colorRGB;
|
||||||
|
public bool Equals(ColorRGB other) => this == other;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a hash code for the <see cref="ColorRGB"/>.
|
/// Generates a hash code for the <see cref="ColorRGB"/>.
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
using System;
|
||||||
|
|
||||||
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an RGBA color.
|
/// Represents an RGBA color.
|
||||||
@@ -11,7 +13,7 @@ namespace Syntriax.Engine.Core;
|
|||||||
/// Initializes a new instance of the <see cref="ColorRGBA"/> struct with the specified values.
|
/// Initializes a new instance of the <see cref="ColorRGBA"/> struct with the specified values.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
|
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
|
||||||
public readonly struct ColorRGBA(byte r, byte g, byte b, byte a = 255)
|
public readonly struct ColorRGBA(byte r, byte g, byte b, byte a = 255) : IEquatable<ColorRGBA>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Red value of the <see cref="ColorRGBA"/>.
|
/// The Red value of the <see cref="ColorRGBA"/>.
|
||||||
@@ -132,6 +134,7 @@ public readonly struct ColorRGBA(byte r, byte g, byte b, byte a = 255)
|
|||||||
/// <param name="obj">The object to compare with the current <see cref="ColorRGBA"/>.</param>
|
/// <param name="obj">The object to compare with the current <see cref="ColorRGBA"/>.</param>
|
||||||
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="ColorRGBA"/>; otherwise, <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="ColorRGBA"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
public override bool Equals(object? obj) => obj is ColorRGBA colorRGBA && this == colorRGBA;
|
public override bool Equals(object? obj) => obj is ColorRGBA colorRGBA && this == colorRGBA;
|
||||||
|
public bool Equals(ColorRGBA other) => this == other;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a hash code for the <see cref="ColorRGBA"/>.
|
/// Generates a hash code for the <see cref="ColorRGBA"/>.
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
|
using System;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a 2D line segment defined by two endpoints.
|
/// Represents a 2D line segment defined by two endpoints.
|
||||||
@@ -11,7 +12,7 @@ namespace Syntriax.Engine.Core;
|
|||||||
/// Initializes a new instance of the <see cref="Line2D"/> struct with the specified endpoints.
|
/// Initializes a new instance of the <see cref="Line2D"/> struct with the specified endpoints.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[System.Diagnostics.DebuggerDisplay("From: {From.ToString(),nq}, To: {To.ToString(),nq}, Direction: {Direction.ToString(),nq}, Length: {Length}")]
|
[System.Diagnostics.DebuggerDisplay("From: {From.ToString(),nq}, To: {To.ToString(),nq}, Direction: {Direction.ToString(),nq}, Length: {Length}")]
|
||||||
public readonly struct Line2D(Vector2D from, Vector2D to)
|
public readonly struct Line2D(Vector2D from, Vector2D to) : IEquatable<Line2D>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The starting point of the <see cref="Line2D"/> segment.
|
/// The starting point of the <see cref="Line2D"/> segment.
|
||||||
@@ -49,15 +50,7 @@ public readonly struct Line2D(Vector2D from, Vector2D to)
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The equation of the <see cref="Line2D"/> defined by this <see cref="Line2D"/> segment.
|
/// The equation of the <see cref="Line2D"/> defined by this <see cref="Line2D"/> segment.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Line2DEquation GetLineEquation(Line2D line)
|
public static Line2DEquation GetLineEquation(Line2D line) => line;
|
||||||
{
|
|
||||||
Vector2D slopeVector = line.From.FromTo(line.To);
|
|
||||||
float slope = slopeVector.Y / slopeVector.X;
|
|
||||||
|
|
||||||
float yOffset = line.From.Y - (slope * line.From.X);
|
|
||||||
|
|
||||||
return new Line2DEquation(slope, yOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the specified <see cref="Vector2D"/> lies on the <see cref="Line2D"/>.
|
/// Determines whether the specified <see cref="Vector2D"/> lies on the <see cref="Line2D"/>.
|
||||||
@@ -196,6 +189,7 @@ public readonly struct Line2D(Vector2D from, Vector2D to)
|
|||||||
/// <param name="obj">The object to compare with the current <see cref="Line2D"/>.</param>
|
/// <param name="obj">The object to compare with the current <see cref="Line2D"/>.</param>
|
||||||
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Line2D"/>; otherwise, <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Line2D"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
public override bool Equals(object? obj) => obj is Line2D line2D && this == line2D;
|
public override bool Equals(object? obj) => obj is Line2D line2D && this == line2D;
|
||||||
|
public bool Equals(Line2D other) => this == other;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a hash code for the <see cref="Line2D"/>.
|
/// Generates a hash code for the <see cref="Line2D"/>.
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
using System;
|
||||||
|
|
||||||
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a <see cref="Line2DEquation"/> in the form y = mx + b.
|
/// Represents a <see cref="Line2DEquation"/> in the form y = mx + b.
|
||||||
@@ -9,7 +11,7 @@ namespace Syntriax.Engine.Core;
|
|||||||
/// Initializes a new instance of the <see cref="Line2DEquation"/> struct with the specified slope and Y intercept.
|
/// Initializes a new instance of the <see cref="Line2DEquation"/> struct with the specified slope and Y intercept.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[System.Diagnostics.DebuggerDisplay("y = {Slope}x + {OffsetY}")]
|
[System.Diagnostics.DebuggerDisplay("y = {Slope}x + {OffsetY}")]
|
||||||
public readonly struct Line2DEquation(float slope, float offsetY)
|
public readonly struct Line2DEquation(float slope, float offsetY) : IEquatable<Line2DEquation>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The slope of the <see cref="Line2DEquation"/>.
|
/// The slope of the <see cref="Line2DEquation"/>.
|
||||||
@@ -24,6 +26,16 @@ public readonly struct Line2DEquation(float slope, float offsetY)
|
|||||||
public static bool operator ==(Line2DEquation left, Line2DEquation right) => left.Slope == right.Slope && left.OffsetY == right.OffsetY;
|
public static bool operator ==(Line2DEquation left, Line2DEquation right) => left.Slope == right.Slope && left.OffsetY == right.OffsetY;
|
||||||
public static bool operator !=(Line2DEquation left, Line2DEquation right) => left.Slope != right.Slope || left.OffsetY != right.OffsetY;
|
public static bool operator !=(Line2DEquation left, Line2DEquation right) => left.Slope != right.Slope || left.OffsetY != right.OffsetY;
|
||||||
|
|
||||||
|
public static implicit operator Line2DEquation(Line2D line)
|
||||||
|
{
|
||||||
|
Vector2D slopeVector = line.From.FromTo(line.To);
|
||||||
|
float slope = slopeVector.Y / slopeVector.X;
|
||||||
|
|
||||||
|
float yOffset = line.From.Y - (slope * line.From.X);
|
||||||
|
|
||||||
|
return new Line2DEquation(slope, yOffset);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resolves the Y coordinate for a given X coordinate using the <see cref="Line2DEquation"/>.
|
/// Resolves the Y coordinate for a given X coordinate using the <see cref="Line2DEquation"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -48,6 +60,7 @@ public readonly struct Line2DEquation(float slope, float offsetY)
|
|||||||
/// <param name="obj">The object to compare with the current <see cref="Line2DEquation"/>.</param>
|
/// <param name="obj">The object to compare with the current <see cref="Line2DEquation"/>.</param>
|
||||||
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Line2DEquation"/>; otherwise, <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Line2DEquation"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
public override bool Equals(object? obj) => obj is Line2DEquation lineEquation && this == lineEquation;
|
public override bool Equals(object? obj) => obj is Line2DEquation lineEquation && this == lineEquation;
|
||||||
|
public bool Equals(Line2DEquation other) => this == other;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a hash code for the <see cref="Line2DEquation"/>.
|
/// Generates a hash code for the <see cref="Line2DEquation"/>.
|
||||||
|
|||||||
114
Engine.Core/Primitives/Line3D.cs
Normal file
114
Engine.Core/Primitives/Line3D.cs
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Engine.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a 3D line segment defined by two endpoints.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="from">The starting point of the <see cref="Line3D"/> segment.</param>
|
||||||
|
/// <param name="to">The ending point of the <see cref="Line3D"/> segment.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// Initializes a new instance of the <see cref="Line3D"/> struct with the specified endpoints.
|
||||||
|
/// </remarks>
|
||||||
|
[System.Diagnostics.DebuggerDisplay("From: {From.ToString(),nq}, To: {To.ToString(),nq}, Direction: {Direction.ToString(),nq}, Length: {Length}")]
|
||||||
|
public readonly struct Line3D(Vector3D from, Vector3D to) : IEquatable<Line3D>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The starting point of the <see cref="Line3D"/> segment.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Vector3D From = from;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The ending point of the <see cref="Line3D"/> segment.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Vector3D To = to;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The reversed <see cref="Line3D"/> segment.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Line3D Reversed => new(To, From);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The normalized direction <see cref="Vector3D"/> of the <see cref="Line3D"/> segment.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Vector3D Direction => From.FromTo(To).Normalize();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The length of the <see cref="Line3D"/> segment.
|
||||||
|
/// </summary>
|
||||||
|
public readonly float Length => From.FromTo(To).Length();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The squared length of the <see cref="Line3D"/> segment.
|
||||||
|
/// </summary>
|
||||||
|
public readonly float LengthSquared => From.FromTo(To).LengthSquared();
|
||||||
|
|
||||||
|
public static bool operator ==(Line3D left, Line3D right) => left.From == right.From && left.To == right.To;
|
||||||
|
public static bool operator !=(Line3D left, Line3D right) => left.From != right.From || left.To != right.To;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Linearly interpolates between the two endpoints of the <see cref="Line3D"/> segment using parameter 't'.
|
||||||
|
/// </summary>
|
||||||
|
public static Vector3D Lerp(Line3D line, float t)
|
||||||
|
=> Vector3D.Lerp(line.From, line.To, t);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates the closest point on the <see cref="Line3D"/> segment to the specified point.
|
||||||
|
/// </summary>
|
||||||
|
public static Vector3D ClosestPointTo(Line3D line, Vector3D point)
|
||||||
|
{
|
||||||
|
Vector3D lineRelativeVector = line.From.FromTo(line.To);
|
||||||
|
|
||||||
|
Vector3D lineDirection = lineRelativeVector.Normalized;
|
||||||
|
Vector3D pointVector = line.From.FromTo(point);
|
||||||
|
|
||||||
|
float dot = lineDirection.Dot(pointVector).Clamp(0f, lineRelativeVector.Magnitude);
|
||||||
|
|
||||||
|
return lineDirection * dot;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if two <see cref="Line3D"/> segments are approximately equal.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The first <see cref="Line3D"/>.</param>
|
||||||
|
/// <param name="right">The second <see cref="Line3D"/>.</param>
|
||||||
|
/// <param name="epsilon">The epsilon range.</param>
|
||||||
|
/// <returns><see cref="true"/> if the <see cref="Line3D"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public static bool ApproximatelyEquals(Line3D left, Line3D right, float epsilon = float.Epsilon)
|
||||||
|
=> left.From.ApproximatelyEquals(right.From, epsilon) && left.To.ApproximatelyEquals(right.To, epsilon);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified object is equal to the current <see cref="Line3D"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to compare with the current <see cref="Line3D"/>.</param>
|
||||||
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Line3D"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public override bool Equals(object? obj) => obj is Line3D line3D && this == line3D;
|
||||||
|
public bool Equals(Line3D other) => this == other;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a hash code for the <see cref="Line3D"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A hash code for the <see cref="Line3D"/>.</returns>
|
||||||
|
public override int GetHashCode() => System.HashCode.Combine(From, To);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="Line3D"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="Line3D"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(Line3D)}({From}, {To})";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides extension methods for the <see cref="Line3D"/> struct.
|
||||||
|
/// </summary>
|
||||||
|
public static class Line3DExtensions
|
||||||
|
{
|
||||||
|
/// <inheritdoc cref="Line3D.Lerp(Line3D, float)" />
|
||||||
|
public static Vector3D Lerp(this Line3D line, float t) => Line3D.Lerp(line, t);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Line3D.ClosestPointTo(Line3D, Vector3D)" />
|
||||||
|
public static Vector3D ClosestPointTo(this Line3D line, Vector3D point) => Line3D.ClosestPointTo(line, point);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Line3D.ApproximatelyEquals(Line3D, Line3D, float)" />
|
||||||
|
public static bool ApproximatelyEquals(this Line3D left, Line3D right, float epsilon = float.Epsilon) => Line3D.ApproximatelyEquals(left, right, epsilon);
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
using System;
|
||||||
|
|
||||||
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a range of values along a single axis.
|
/// Represents a range of values along a single axis.
|
||||||
@@ -9,7 +11,7 @@ namespace Syntriax.Engine.Core;
|
|||||||
/// Initializes a new instance of the <see cref="Projection1D"/> struct with the specified minimum and maximum values.
|
/// Initializes a new instance of the <see cref="Projection1D"/> struct with the specified minimum and maximum values.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[System.Diagnostics.DebuggerDisplay("Min: {Min}, Max: {Max}")]
|
[System.Diagnostics.DebuggerDisplay("Min: {Min}, Max: {Max}")]
|
||||||
public readonly struct Projection1D(float min, float max)
|
public readonly struct Projection1D(float min, float max) : IEquatable<Projection1D>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the minimum value of the projection.
|
/// Gets the minimum value of the projection.
|
||||||
@@ -90,6 +92,7 @@ public readonly struct Projection1D(float min, float max)
|
|||||||
/// <param name="obj">The object to compare with the current <see cref="Projection1D"/>.</param>
|
/// <param name="obj">The object to compare with the current <see cref="Projection1D"/>.</param>
|
||||||
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Projection1D"/>; otherwise, <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Projection1D"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
public override bool Equals(object? obj) => obj is Projection1D projection1D && this == projection1D;
|
public override bool Equals(object? obj) => obj is Projection1D projection1D && this == projection1D;
|
||||||
|
public bool Equals(Projection1D other) => this == other;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a hash code for the <see cref="Projection1D"/>.
|
/// Generates a hash code for the <see cref="Projection1D"/>.
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
using System;
|
||||||
|
|
||||||
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a 3D space rotation.
|
/// Represents a 3D space rotation.
|
||||||
@@ -11,7 +13,7 @@ namespace Syntriax.Engine.Core;
|
|||||||
/// Initializes a new instance of the <see cref="Quaternion"/> struct with the specified positions.
|
/// Initializes a new instance of the <see cref="Quaternion"/> struct with the specified positions.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized.ToString(),nq}")]
|
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized.ToString(),nq}")]
|
||||||
public readonly struct Quaternion(float x, float y, float z, float w)
|
public readonly struct Quaternion(float x, float y, float z, float w) : IEquatable<Quaternion>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The X(i) imaginary of the <see cref="Quaternion"/>.
|
/// The X(i) imaginary of the <see cref="Quaternion"/>.
|
||||||
@@ -77,6 +79,30 @@ public readonly struct Quaternion(float x, float y, float z, float w)
|
|||||||
public static implicit operator Quaternion(System.Numerics.Quaternion quaternion) => new(quaternion.X, quaternion.Y, quaternion.Z, quaternion.W);
|
public static implicit operator Quaternion(System.Numerics.Quaternion quaternion) => new(quaternion.X, quaternion.Y, quaternion.Z, quaternion.W);
|
||||||
public static implicit operator System.Numerics.Quaternion(Quaternion quaternion) => new(quaternion.X, quaternion.Y, quaternion.Z, quaternion.W);
|
public static implicit operator System.Numerics.Quaternion(Quaternion quaternion) => new(quaternion.X, quaternion.Y, quaternion.Z, quaternion.W);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the Pitch, Yaw and Roll of the <see cref="Quaternion"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static Vector3D ToAngles(Quaternion quaternion)
|
||||||
|
{
|
||||||
|
// Quaternion to Euler angles (in 3-2-1 sequence) conversion
|
||||||
|
float sinr_cosp = 2f * (quaternion.W * quaternion.X + quaternion.Y * quaternion.Z);
|
||||||
|
float cosr_cosp = 1f - 2f * (quaternion.X * quaternion.X + quaternion.Y * quaternion.Y);
|
||||||
|
float pitch = MathF.Atan2(sinr_cosp, cosr_cosp);
|
||||||
|
|
||||||
|
float sinp = 2f * (quaternion.W * quaternion.Y - quaternion.Z * quaternion.X);
|
||||||
|
float yaw;
|
||||||
|
if (MathF.Abs(sinp) >= 1f)
|
||||||
|
yaw = MathF.CopySign(MathF.PI / 2f, sinp);
|
||||||
|
else
|
||||||
|
yaw = MathF.Asin(sinp);
|
||||||
|
|
||||||
|
float siny_cosp = 2f * (quaternion.W * quaternion.Z + quaternion.X * quaternion.Y);
|
||||||
|
float cosy_cosp = 1f - 2f * (quaternion.Y * quaternion.Y + quaternion.Z * quaternion.Z);
|
||||||
|
float roll = MathF.Atan2(siny_cosp, cosy_cosp);
|
||||||
|
|
||||||
|
return new Vector3D(pitch, yaw, roll) * Math.RadianToDegree;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates the length of the <see cref="Quaternion"/>.
|
/// Calculates the length of the <see cref="Quaternion"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -130,6 +156,15 @@ public readonly struct Quaternion(float x, float y, float z, float w)
|
|||||||
/// <returns>The normalized <see cref="Quaternion"/>.</returns>
|
/// <returns>The normalized <see cref="Quaternion"/>.</returns>
|
||||||
public static Quaternion Normalize(Quaternion quaternion) => quaternion / Length(quaternion);
|
public static Quaternion Normalize(Quaternion quaternion) => quaternion / Length(quaternion);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rotates a <see cref="Quaternion"/> around a axis by the specified angle (in radians).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="vector">The <see cref="Quaternion"/> to rotate.</param>
|
||||||
|
/// <param name="axis">The <see cref="Quaternion"/> to rotate around.</param>
|
||||||
|
/// <param name="angleInRadian">The angle to rotate by, in radians.</param>
|
||||||
|
/// <returns>The rotated <see cref="Quaternion"/>.</returns>
|
||||||
|
public static Quaternion Rotate(Quaternion vector, Vector3D axis, float angleInRadian) => vector * Quaternion.FromAxisAngle(axis, angleInRadian);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Inverts the direction of the <see cref="Quaternion"/>.
|
/// Inverts the direction of the <see cref="Quaternion"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -152,8 +187,12 @@ public readonly struct Quaternion(float x, float y, float z, float w)
|
|||||||
/// <returns>The rotated <see cref="Vector3D"/>.</returns>
|
/// <returns>The rotated <see cref="Vector3D"/>.</returns>
|
||||||
public static Vector3D RotateVector(Vector3D vector, Quaternion quaternion)
|
public static Vector3D RotateVector(Vector3D vector, Quaternion quaternion)
|
||||||
{
|
{
|
||||||
Quaternion rotation = quaternion * new Quaternion(vector.X, vector.Y, vector.Z, 0) * Invert(quaternion);
|
// https://blog.molecular-matters.com/2013/05/24/a-faster-quaternion-vector-multiplication/
|
||||||
return new(rotation.X, rotation.Y, rotation.Z);
|
// t = 2 * cross(q.xyz, v)
|
||||||
|
// v' = v + q.w * t + cross(q.xyz, t)
|
||||||
|
Vector3D quaternionVector = new(quaternion.X, quaternion.Y, quaternion.Z);
|
||||||
|
Vector3D t = 2f * quaternionVector.Cross(vector);
|
||||||
|
return vector + quaternion.W * t + quaternionVector.Cross(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -208,9 +247,9 @@ public readonly struct Quaternion(float x, float y, float z, float w)
|
|||||||
/// <param name="axis">The axis of the rotation in <see cref="Vector3D"/>.</param>
|
/// <param name="axis">The axis of the rotation in <see cref="Vector3D"/>.</param>
|
||||||
/// <param name="angle">The angle in radians.</param>
|
/// <param name="angle">The angle in radians.</param>
|
||||||
/// <returns>The rotation <see cref="Quaternion"/> calculated by the given parameters.</returns>
|
/// <returns>The rotation <see cref="Quaternion"/> calculated by the given parameters.</returns>
|
||||||
public static Quaternion FromAxisAngle(Vector3D axis, float angle)
|
public static Quaternion FromAxisAngle(Vector3D axis, float angleInRadian)
|
||||||
{
|
{
|
||||||
float halfAngle = angle * .5f;
|
float halfAngle = angleInRadian * .5f;
|
||||||
float sinHalf = Math.Sin(halfAngle);
|
float sinHalf = Math.Sin(halfAngle);
|
||||||
return new Quaternion(axis.X * sinHalf, axis.Y * sinHalf, axis.Z * sinHalf, Math.Cos(halfAngle));
|
return new Quaternion(axis.X * sinHalf, axis.Y * sinHalf, axis.Z * sinHalf, Math.Cos(halfAngle));
|
||||||
}
|
}
|
||||||
@@ -288,18 +327,19 @@ public readonly struct Quaternion(float x, float y, float z, float w)
|
|||||||
/// <param name="obj">The object to compare with the current <see cref="Quaternion"/>.</param>
|
/// <param name="obj">The object to compare with the current <see cref="Quaternion"/>.</param>
|
||||||
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Quaternion"/>; otherwise, <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Quaternion"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
public override bool Equals(object? obj) => obj is Quaternion quaternion && this == quaternion;
|
public override bool Equals(object? obj) => obj is Quaternion quaternion && this == quaternion;
|
||||||
|
public bool Equals(Quaternion other) => this == other;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a hash code for the <see cref="Quaternion"/>.
|
/// Generates a hash code for the <see cref="Quaternion"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A hash code for the <see cref="Quaternion"/>.</returns>
|
/// <returns>A hash code for the <see cref="Quaternion"/>.</returns>
|
||||||
public override int GetHashCode() => System.HashCode.Combine(W, X, Y, Z);
|
public override int GetHashCode() => System.HashCode.Combine(X, Y, Z, W);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts the <see cref="Quaternion"/> to its string representation.
|
/// Converts the <see cref="Quaternion"/> to its string representation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A string representation of the <see cref="Quaternion"/>.</returns>
|
/// <returns>A string representation of the <see cref="Quaternion"/>.</returns>
|
||||||
public override string ToString() => $"{nameof(Quaternion)}({W}, {X}, {Y}, {Z})";
|
public override string ToString() => $"{nameof(Quaternion)}({X}, {Y}, {Z}, {W})";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -307,6 +347,9 @@ public readonly struct Quaternion(float x, float y, float z, float w)
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class QuaternionExtensions
|
public static class QuaternionExtensions
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc cref="Quaternion.ToAngles(Quaternion)" />
|
||||||
|
public static Vector3D ToAngles(this Quaternion quaternion) => Quaternion.ToAngles(quaternion);
|
||||||
|
|
||||||
/// <inheritdoc cref="Quaternion.Length(Quaternion)" />
|
/// <inheritdoc cref="Quaternion.Length(Quaternion)" />
|
||||||
public static float Length(this Quaternion quaternion) => Quaternion.Length(quaternion);
|
public static float Length(this Quaternion quaternion) => Quaternion.Length(quaternion);
|
||||||
|
|
||||||
@@ -328,6 +371,9 @@ public static class QuaternionExtensions
|
|||||||
/// <inheritdoc cref="Quaternion.Normalize(Quaternion)" />
|
/// <inheritdoc cref="Quaternion.Normalize(Quaternion)" />
|
||||||
public static Quaternion Normalize(this Quaternion quaternion) => Quaternion.Normalize(quaternion);
|
public static Quaternion Normalize(this Quaternion quaternion) => Quaternion.Normalize(quaternion);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Quaternion.Rotate(Quaternion, Vector3D, float)" />
|
||||||
|
public static Quaternion Rotate(this Quaternion vector, Vector3D normal, float angleInRadian) => Quaternion.Rotate(vector, normal, angleInRadian);
|
||||||
|
|
||||||
/// <inheritdoc cref="Quaternion.Invert(Quaternion)" />
|
/// <inheritdoc cref="Quaternion.Invert(Quaternion)" />
|
||||||
public static Quaternion Invert(this Quaternion quaternion) => Quaternion.Invert(quaternion);
|
public static Quaternion Invert(this Quaternion quaternion) => Quaternion.Invert(quaternion);
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
using System;
|
||||||
|
|
||||||
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an infinite ray in 2D space.
|
/// Represents an infinite ray in 2D space.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="Origin">The <see cref="Vector2D"/> in 2D space where the ray starts from.</param>
|
/// <param name="Origin">The <see cref="Vector2D"/> in 2D space where the ray starts from.</param>
|
||||||
/// <param name="Direction">Normalized <see cref="Vector2D"/> indicating the ray's is direction.</param>
|
/// <param name="Direction">Normalized <see cref="Vector2D"/> indicating the ray's is direction.</param>
|
||||||
public readonly struct Ray2D(Vector2D Origin, Vector2D Direction)
|
public readonly struct Ray2D(Vector2D Origin, Vector2D Direction) : IEquatable<Ray2D>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The starting point of the <see cref="Ray2D"/>.
|
/// The starting point of the <see cref="Ray2D"/>.
|
||||||
@@ -72,6 +74,7 @@ public readonly struct Ray2D(Vector2D Origin, Vector2D Direction)
|
|||||||
/// <param name="obj">The object to compare with the current <see cref="Ray2D"/>.</param>
|
/// <param name="obj">The object to compare with the current <see cref="Ray2D"/>.</param>
|
||||||
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Ray2D"/>; otherwise, <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Ray2D"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
public override bool Equals(object? obj) => obj is Ray2D ray2D && this == ray2D;
|
public override bool Equals(object? obj) => obj is Ray2D ray2D && this == ray2D;
|
||||||
|
public bool Equals(Ray2D other) => this == other;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a hash code for the <see cref="Ray2D"/>.
|
/// Generates a hash code for the <see cref="Ray2D"/>.
|
||||||
|
|||||||
109
Engine.Core/Primitives/Ray3D.cs
Normal file
109
Engine.Core/Primitives/Ray3D.cs
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Engine.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an infinite ray in 3D space.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Origin">The <see cref="Vector3D"/> in 3D space where the ray starts from.</param>
|
||||||
|
/// <param name="Direction">Normalized <see cref="Vector3D"/> indicating the ray's is direction.</param>
|
||||||
|
public readonly struct Ray3D(Vector3D Origin, Vector3D Direction) : IEquatable<Ray3D>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The starting point of the <see cref="Ray3D"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Vector3D Origin = Origin;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The direction in which the <see cref="Ray3D"/> points. Should be a normalized vector.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Vector3D Direction = Direction;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <see cref="Ray3D"/> with the same origin but with the direction reversed.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Ray3D Reversed => new(Origin, -Direction);
|
||||||
|
|
||||||
|
public static bool operator ==(Ray3D left, Ray3D right) => left.Origin == right.Origin && left.Direction == right.Direction;
|
||||||
|
public static bool operator !=(Ray3D left, Ray3D right) => left.Origin != right.Origin || left.Direction != right.Direction;
|
||||||
|
|
||||||
|
public static implicit operator Ray3D(Line3D line) => new(line.From, line.From.FromTo(line.To).Normalized);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a <see cref="Line3D"/> from a <see cref="Ray3D"/>, extending from its origin in the <see cref="Ray3D"/>'s direction for a given distance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ray">The source <see cref="Ray3D"/>.</param>
|
||||||
|
/// <param name="distance">The length of the line segment to create from the <see cref="Ray3D"/>.</param>
|
||||||
|
/// <returns>A <see cref="Line3D"/> representing the segment of the <see cref="Ray3D"/>.</returns>
|
||||||
|
public static Line3D GetLine(Ray3D ray, float distance)
|
||||||
|
=> new(ray.Origin, ray.Origin + ray.Direction * distance);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Evaluates the point on the <see cref="Ray3D"/> at a specified distance from its origin.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ray">The <see cref="Ray3D"/> to evaluate.</param>
|
||||||
|
/// <param name="distanceFromOrigin">The distance from the origin along the <see cref="Ray3D"/>'s direction.</param>
|
||||||
|
/// <returns>A <see cref="Vector3D"/> representing the point at the given distance on the <see cref="Ray3D"/>.</returns>
|
||||||
|
public static Vector3D Evaluate(Ray3D ray, float distanceFromOrigin)
|
||||||
|
=> ray.Origin + ray.Direction * distanceFromOrigin;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates the closest point on the <see cref="Ray3D"/> to the specified point.
|
||||||
|
/// </summary>
|
||||||
|
public static Vector3D ClosestPointTo(Ray3D ray, Vector3D point)
|
||||||
|
{
|
||||||
|
Vector3D originToPoint = ray.Origin.FromTo(point);
|
||||||
|
|
||||||
|
float dot = ray.Direction.Dot(originToPoint);
|
||||||
|
|
||||||
|
return ray.Origin + ray.Direction * dot;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if two <see cref="Ray3D"/>s are approximately equal within a specified epsilon range.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The first <see cref="Ray3D"/>.</param>
|
||||||
|
/// <param name="right">The second <see cref="Ray3D"/>.</param>
|
||||||
|
/// <param name="epsilon">The epsilon range.</param>
|
||||||
|
/// <returns><see cref="true"/> if the <see cref="Ray3D"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public static bool ApproximatelyEquals(Ray3D left, Ray3D right, float epsilon = float.Epsilon)
|
||||||
|
=> left.Origin.ApproximatelyEquals(right.Origin, epsilon) && left.Direction.ApproximatelyEquals(right.Direction, epsilon);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified object is equal to the current <see cref="Ray3D"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to compare with the current <see cref="Ray3D"/>.</param>
|
||||||
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Ray3D"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public override bool Equals(object? obj) => obj is Ray3D ray3D && this == ray3D;
|
||||||
|
public bool Equals(Ray3D other) => this == other;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a hash code for the <see cref="Ray3D"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A hash code for the <see cref="Ray3D"/>.</returns>
|
||||||
|
public override int GetHashCode() => System.HashCode.Combine(Origin, Direction);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="Ray3D"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="Ray3D"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(Ray3D)}({Origin}, {Direction})";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides extension methods for the <see cref="Ray3D"/> struct.
|
||||||
|
/// </summary>
|
||||||
|
public static class Ray3DExtensions
|
||||||
|
{
|
||||||
|
/// <inheritdoc cref="Ray3D.GetLine(Ray3D, float) />
|
||||||
|
public static Line3D ToLine(this Ray3D ray, float distance) => Ray3D.GetLine(ray, distance);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Ray3D.Evaluate(Ray3D, float) />
|
||||||
|
public static Vector3D Evaluate(this Ray3D ray, float distanceFromOrigin) => Ray3D.Evaluate(ray, distanceFromOrigin);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Ray3D.ClosestPointTo(Ray3D, Vector3D) />
|
||||||
|
public static Vector3D ClosestPointTo(this Ray3D ray, Vector3D point) => Ray3D.ClosestPointTo(ray, point);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Ray3D.ApproximatelyEquals(Ray3D, Ray3D, float)" />
|
||||||
|
public static bool ApproximatelyEquals(this Ray3D left, Ray3D right, float epsilon = float.Epsilon) => Ray3D.ApproximatelyEquals(left, right, epsilon);
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Syntriax.Engine.Core;
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a shape defined by a collection of vertices.
|
/// Represents a shape defined by a collection of vertices.
|
||||||
|
|||||||
131
Engine.Core/Primitives/Sphere3D.cs
Normal file
131
Engine.Core/Primitives/Sphere3D.cs
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Engine.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a 3D sphere.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="center">The center of the sphere.</param>
|
||||||
|
/// <param name="radius">The radius of the sphere.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// Initializes a new instance of the <see cref="Sphere3D"/> struct with the specified center and radius.
|
||||||
|
/// </remarks>
|
||||||
|
[DebuggerDisplay("Center: {Center.ToString(),nq}, Radius: {Radius}")]
|
||||||
|
public readonly struct Sphere3D(Vector3D center, float radius) : IEquatable<Sphere3D>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The center of the <see cref="Sphere3D"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Vector3D Center = center;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The radius of the <see cref="Sphere3D"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly float Radius = radius;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the squared radius of the <see cref="Sphere3D"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly float RadiusSquared => Radius * Radius;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the diameter of the <see cref="Sphere3D"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly float Diameter => 2f * Radius;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A predefined unit <see cref="Sphere3D"/> with a center at the origin and a radius of 1.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly Sphere3D UnitSphere = new(Vector3D.Zero, 1f);
|
||||||
|
|
||||||
|
public static bool operator ==(Sphere3D left, Sphere3D right) => left.Center == right.Center && left.Radius == right.Radius;
|
||||||
|
public static bool operator !=(Sphere3D left, Sphere3D right) => left.Center != right.Center || left.Radius != right.Radius;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the center of the <see cref="Sphere3D"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static Sphere3D SetCenter(Sphere3D sphere, Vector3D center) => new(center, sphere.Radius);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the radius of the <see cref="Sphere3D"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static Sphere3D SetRadius(Sphere3D sphere, float radius) => new(sphere.Center, radius);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Displaces the <see cref="Sphere3D"/> by the specified <see cref="Vector3D"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static Sphere3D Displace(Sphere3D sphere, Vector3D displaceVector) => new(sphere.Center + displaceVector, sphere.Radius);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Projects the <see cref="Sphere3D"/> onto the specified <see cref="Vector3D"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static Projection1D Project(Sphere3D sphere, Vector3D projectionVector)
|
||||||
|
{
|
||||||
|
float projectedCenter = sphere.Center.Dot(projectionVector);
|
||||||
|
return new(projectedCenter - sphere.Radius, projectedCenter + sphere.Radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Transforms the <see cref="Sphere3D"/> by the specified <see cref="ITransform3D"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static Sphere3D Transform(ITransform3D transform, Sphere3D sphere)
|
||||||
|
=> new(transform.Transform(sphere.Center), sphere.Radius * (transform.Scale.Magnitude / Vector3D.One.Magnitude));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if two <see cref="Sphere3D"/>s are approximately equal.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The first <see cref="Sphere3D"/>.</param>
|
||||||
|
/// <param name="right">The second <see cref="Sphere3D"/>.</param>
|
||||||
|
/// <param name="epsilon">The epsilon range.</param>
|
||||||
|
/// <returns><see cref="true"/> if the <see cref="Sphere3D"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public static bool ApproximatelyEquals(Sphere3D left, Sphere3D right, float epsilon = float.Epsilon)
|
||||||
|
=> left.Center.ApproximatelyEquals(right.Center, epsilon) && left.Radius.ApproximatelyEquals(right.Radius, epsilon);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified object is equal to the current <see cref="Sphere3D"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to compare with the current <see cref="Sphere3D"/>.</param>
|
||||||
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Sphere3D"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
|
public override bool Equals(object? obj) => obj is Sphere3D sphere && this == sphere;
|
||||||
|
public bool Equals(Sphere3D other) => this == other;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a hash code for the <see cref="Sphere3D"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A hash code for the <see cref="Sphere3D"/>.</returns>
|
||||||
|
public override int GetHashCode() => System.HashCode.Combine(Center, Radius);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the <see cref="Sphere3D"/> to its string representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representation of the <see cref="Sphere3D"/>.</returns>
|
||||||
|
public override string ToString() => $"{nameof(Sphere3D)}({Center}, {Radius})";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides extension methods for the <see cref="Sphere3D"/> struct.
|
||||||
|
/// </summary>
|
||||||
|
public static class SphereExtensions
|
||||||
|
{
|
||||||
|
/// <inheritdoc cref="Sphere3D.SetCenter(Sphere3D, Vector3D)" />
|
||||||
|
public static Sphere3D SetCenter(this Sphere3D sphere, Vector3D center) => Sphere3D.SetCenter(sphere, center);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Sphere3D.SetRadius(Sphere3D, float)" />
|
||||||
|
public static Sphere3D SetRadius(this Sphere3D sphere, float radius) => Sphere3D.SetRadius(sphere, radius);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Sphere3D.Displace(Sphere3D, Vector3D)" />
|
||||||
|
public static Sphere3D Displace(this Sphere3D sphere, Vector3D displaceVector) => Sphere3D.Displace(sphere, displaceVector);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Sphere3D.Project(Sphere3D, Vector3D)" />
|
||||||
|
public static Projection1D ProjectTo(this Sphere3D sphere, Vector3D projectionVector) => Sphere3D.Project(sphere, projectionVector);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Sphere3D.Transform(ITransform3D, Sphere3D)" />
|
||||||
|
public static Sphere3D Transform(this ITransform3D transform, Sphere3D sphere) => Sphere3D.Transform(transform, sphere);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Sphere3D.Transform(ITransform3D, Sphere3D)" />
|
||||||
|
public static Sphere3D Transform(this Sphere3D sphere, ITransform3D transform) => Sphere3D.Transform(transform, sphere);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Sphere3D.ApproximatelyEquals(Sphere3D, Sphere3D, float)" />
|
||||||
|
public static bool ApproximatelyEquals(this Sphere3D left, Sphere3D right, float epsilon = float.Epsilon) => Sphere3D.ApproximatelyEquals(left, right, epsilon);
|
||||||
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
using System;
|
||||||
|
|
||||||
|
namespace Engine.Core;
|
||||||
|
|
||||||
[System.Diagnostics.DebuggerDisplay("A: {A.ToString(), nq}, B: {B.ToString(), nq}, B: {C.ToString(), nq}")]
|
[System.Diagnostics.DebuggerDisplay("A: {A.ToString(), nq}, B: {B.ToString(), nq}, B: {C.ToString(), nq}")]
|
||||||
public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C)
|
public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C) : IEquatable<Triangle>
|
||||||
{
|
{
|
||||||
public readonly Vector2D A { get; init; } = A;
|
public readonly Vector2D A { get; init; } = A;
|
||||||
public readonly Vector2D B { get; init; } = B;
|
public readonly Vector2D B { get; init; } = B;
|
||||||
@@ -54,6 +56,7 @@ public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C)
|
|||||||
/// <param name="obj">The object to compare with the current <see cref="Triangle"/>.</param>
|
/// <param name="obj">The object to compare with the current <see cref="Triangle"/>.</param>
|
||||||
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Triangle"/>; otherwise, <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Triangle"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
public override bool Equals(object? obj) => obj is Triangle triangle && this == triangle;
|
public override bool Equals(object? obj) => obj is Triangle triangle && this == triangle;
|
||||||
|
public bool Equals(Triangle other) => this == other;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a hash code for the <see cref="Triangle"/>.
|
/// Generates a hash code for the <see cref="Triangle"/>.
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace Syntriax.Engine.Core;
|
using System;
|
||||||
|
|
||||||
|
namespace Engine.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a two-dimensional vector.
|
/// Represents a two-dimensional vector.
|
||||||
@@ -9,7 +11,7 @@ namespace Syntriax.Engine.Core;
|
|||||||
/// Initializes a new instance of the <see cref="Vector2D"/> struct with the specified positions.
|
/// Initializes a new instance of the <see cref="Vector2D"/> struct with the specified positions.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized.ToString(),nq}")]
|
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized.ToString(),nq}")]
|
||||||
public readonly struct Vector2D(float x, float y)
|
public readonly struct Vector2D(float x, float y) : IEquatable<Vector2D>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The X coordinate of the <see cref="Vector2D"/>.
|
/// The X coordinate of the <see cref="Vector2D"/>.
|
||||||
@@ -82,6 +84,7 @@ public readonly struct Vector2D(float x, float y)
|
|||||||
|
|
||||||
public static implicit operator System.Numerics.Vector2(Vector2D vector) => new(vector.X, vector.Y);
|
public static implicit operator System.Numerics.Vector2(Vector2D vector) => new(vector.X, vector.Y);
|
||||||
public static implicit operator Vector2D(System.Numerics.Vector2 vector) => new(vector.X, vector.Y);
|
public static implicit operator Vector2D(System.Numerics.Vector2 vector) => new(vector.X, vector.Y);
|
||||||
|
public static implicit operator Vector2D(Vector2DInt vector) => new(vector.X, vector.Y);
|
||||||
public static implicit operator Vector2D(Vector3D vector) => new(vector.X, vector.Y);
|
public static implicit operator Vector2D(Vector3D vector) => new(vector.X, vector.Y);
|
||||||
public static implicit operator Vector2D(System.Numerics.Vector3 vector) => new(vector.X, vector.Y);
|
public static implicit operator Vector2D(System.Numerics.Vector3 vector) => new(vector.X, vector.Y);
|
||||||
|
|
||||||
@@ -308,6 +311,7 @@ public readonly struct Vector2D(float x, float y)
|
|||||||
/// <param name="obj">The object to compare with the current <see cref="Vector2D"/>.</param>
|
/// <param name="obj">The object to compare with the current <see cref="Vector2D"/>.</param>
|
||||||
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Vector2D"/>; otherwise, <see cref="false"/>.</returns>
|
/// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Vector2D"/>; otherwise, <see cref="false"/>.</returns>
|
||||||
public override bool Equals(object? obj) => obj is Vector2D vector2D && this == vector2D;
|
public override bool Equals(object? obj) => obj is Vector2D vector2D && this == vector2D;
|
||||||
|
public bool Equals(Vector2D other) => this == other;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a hash code for the <see cref="Vector2D"/>.
|
/// Generates a hash code for the <see cref="Vector2D"/>.
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user