Compare commits

...

21 Commits

Author SHA1 Message Date
0bf38234c6 feat: async serializer methods 2025-05-04 19:00:54 +03:00
ed6969c16a feat: progression trackers added 2025-05-04 18:57:26 +03:00
b0b421151f refactor: TypeFactory ReloadTypes made multithread friendly 2025-05-04 18:57:01 +03:00
41c5def097 refactor: renamed DelegateHelpers to DelegateExtensions 2025-05-04 18:52:47 +03:00
fbbdfb07fa chore: bumped .netcore version to 9 2025-05-04 18:46:21 +03:00
bf283d804c chore: updated Shape2D tween to look more aesthetic by choosing more linearly distributed vertices instead of the last vertex 2025-05-03 23:31:06 +03:00
063ea08707 feat: added RoundToInt RoundMode for midway values 2025-05-03 23:30:02 +03:00
fd11a94ddf refactor: easings have a singleton base so we don't create an unnecessary instance or cache everytime 2025-05-03 22:38:40 +03:00
be2295b92d feat: added engine member tween extensions 2025-05-03 22:23:52 +03:00
a93e55619c refactor: extracted interface from TweenManager 2025-05-03 22:23:28 +03:00
48ae24af47 chore: added safeguard value clamps for color operations 2025-05-03 22:21:58 +03:00
1366a417f1 feat: added Math.OneMinus method 2025-05-03 22:16:14 +03:00
4bfe98852c refactor: tween extensions method spacings fixed 2025-05-03 20:46:20 +03:00
98edbe1af5 chore: disabled all ImplicitUsings 2025-05-03 20:41:26 +03:00
3725a3b0fd feat: added preserver class & method to preserve assembly loading 2025-05-03 20:22:35 +03:00
f43ab36742 feat: added loggers 2025-05-03 17:01:58 +03:00
c7aafd85bc refactor: renamed assert helper and moved to Debug subfolder 2025-05-03 15:37:52 +03:00
5de08b8fe4 refactor: primitives now use Core.Math for math 2025-05-02 18:57:42 +03:00
16e4077d40 chore: HSV hue is normalized between 0 and 1 2025-05-02 18:54:08 +03:00
fc3c1ed1f9 refactor: Shape2D converted into a class as it has a reference type 2025-05-02 12:46:23 +03:00
b100b5c2fe feat: added color primitives 2025-05-02 00:51:58 +03:00
72 changed files with 1321 additions and 141 deletions

View File

@ -482,3 +482,5 @@ $RECYCLE.BIN/
# Vim temporary swap files
*.swp
!Debug

View File

@ -53,7 +53,7 @@ public abstract class Behaviour : BehaviourBase
protected virtual void OnPreUpdate() { }
protected virtual void PreUpdate(IBehaviourController _)
{
Debug.AssertHelpers.AssertInitialized(this);
Debug.Assert.AssertInitialized(this);
OnPreUpdatePreActiveCheck();
@ -77,7 +77,7 @@ public abstract class Behaviour : BehaviourBase
protected virtual void OnUpdate() { }
protected virtual void Update(IBehaviourController _)
{
Debug.AssertHelpers.AssertInitialized(this);
Debug.Assert.AssertInitialized(this);
OnUpdatePreActiveCheck();
@ -91,7 +91,7 @@ public abstract class Behaviour : BehaviourBase
protected virtual void OnPreDraw() { }
protected virtual void PreDraw(IBehaviourController _)
{
Debug.AssertHelpers.AssertInitialized(this);
Debug.Assert.AssertInitialized(this);
OnPreDrawPreActiveCheck();

View File

@ -66,8 +66,8 @@ public abstract class BehaviourBase : BaseEntity, IBehaviour
protected override void InitializeInternal()
{
Debug.AssertHelpers.AssertBehaviourControllerAssigned(this);
Debug.AssertHelpers.AssertStateEnableAssigned(this);
Debug.Assert.AssertBehaviourControllerAssigned(this);
Debug.Assert.AssertStateEnableAssigned(this);
}
private void OnStateEnabledChanged(IStateEnable sender, bool previousState) => UpdateActive();

View File

@ -113,7 +113,7 @@ public class BehaviourController : BaseEntity, IBehaviourController
protected override void InitializeInternal()
{
Debug.AssertHelpers.AssertUniverseObjectAssigned(this);
Debug.Assert.AssertUniverseObjectAssigned(this);
foreach (IBehaviour behaviour in behaviours)
behaviour.Initialize();
@ -127,7 +127,7 @@ public class BehaviourController : BaseEntity, IBehaviourController
public void Update()
{
Debug.AssertHelpers.AssertInitialized(this);
Debug.Assert.AssertInitialized(this);
if (!UniverseObject.StateEnable.Enabled || !StateEnable.Enabled)
return;
@ -138,7 +138,7 @@ public class BehaviourController : BaseEntity, IBehaviourController
public void UpdatePreDraw()
{
Debug.AssertHelpers.AssertInitialized(this);
Debug.Assert.AssertInitialized(this);
if (!UniverseObject.StateEnable.Enabled || !StateEnable.Enabled)
return;

View File

@ -2,7 +2,7 @@ using System.Runtime.CompilerServices;
namespace Syntriax.Engine.Core.Debug;
public class AssertHelpers
public static class Assert
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AssertInitialized(IInitializable initializable)

View File

@ -0,0 +1,8 @@
using System;
namespace Syntriax.Engine.Core.Debug;
public class ConsoleLogger : LoggerBase
{
protected override void Write(string message) => Console.WriteLine(message);
}

View File

@ -0,0 +1,20 @@
using System;
using System.IO;
namespace Syntriax.Engine.Core.Debug;
public class FileLogger : LoggerBase
{
public readonly string FilePath;
public FileLogger(string filePath)
{
FilePath = filePath;
File.Open(filePath, FileMode.Create).Close();
}
protected override void Write(string message)
{
File.AppendAllTextAsync(FilePath, $"{message}{Environment.NewLine}");
}
}

View File

@ -0,0 +1,15 @@
namespace Syntriax.Engine.Core.Debug;
public interface ILogger
{
Level FilterLevel { get; set; }
void Log(string message, Level level = Level.Info, bool force = false);
enum Level
{
Info,
Warning,
Error,
};
}

View File

@ -0,0 +1,20 @@
using System;
namespace Syntriax.Engine.Core.Debug;
public abstract class LoggerBase : ILogger
{
public ILogger.Level FilterLevel { get; set; } = ILogger.Level.Info;
public void Log(string message, ILogger.Level level = ILogger.Level.Info, bool force = false)
{
if (!force && level < FilterLevel)
return;
string timestamp = DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss tt");
Write($"[{timestamp}] [{level}] \t{message}");
}
protected abstract void Write(string message);
}

View File

@ -0,0 +1,28 @@
using System;
using System.Diagnostics;
namespace Syntriax.Engine.Core.Debug;
public static class LoggerExtensions
{
public static void Log<T>(this ILogger logger, T caller, string message, ILogger.Level level = ILogger.Level.Info, bool force = false)
{
string body = $"{caller?.GetType().Name ?? typeof(T).Name}: {message}";
logger.Log(body, level, force);
}
public static void LogWarning<T>(this ILogger logger, T caller, string message, bool force = false) => Log(logger, caller, message, ILogger.Level.Info, force);
public static void LogError<T>(this ILogger logger, T caller, string message, bool force = false)
{
Log(logger, caller, message, ILogger.Level.Error, force);
Log(logger, caller, $"{nameof(StackTrace)}:{Environment.NewLine}{new StackTrace()}");
}
public static void LogException<T>(this ILogger logger, T caller, Exception exception, bool force = false)
{
Log(logger, caller, $"Message: {exception.Message}", ILogger.Level.Error, force);
Log(logger, caller, $"InnerException: {exception.InnerException}", ILogger.Level.Error, force);
Log(logger, caller, $"{nameof(StackTrace)}:{Environment.NewLine}{exception.StackTrace}");
}
}

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>false</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Syntriax.Engine.Core</RootNamespace>

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
@ -6,7 +7,7 @@ namespace Syntriax.Engine.Core.Factory;
public static class TypeFactory
{
private static readonly Dictionary<string, Type> registeredTypes = [];
private static readonly ConcurrentDictionary<string, Type> registeredTypes = [];
public static string GetTypeName(Type type) => type.FullName ?? throw new ArgumentException($"{type.Name} must be a resolvable type");

View File

@ -2,7 +2,7 @@ using System;
namespace Syntriax.Engine.Core;
public static class DelegateHelpers
public static class DelegateExtensions
{
public static void InvokeSafe(this Delegate @delegate, params object?[] args)
{

View File

@ -0,0 +1,7 @@
namespace Syntriax.Engine.Core;
public interface IProgressionTracker : IReadOnlyProgressionTracker
{
void Set(float progression, string status);
void Reset();
}

View File

@ -0,0 +1,13 @@
namespace Syntriax.Engine.Core;
public interface IReadOnlyProgressionTracker
{
event ProgressionUpdatedEventHandler? OnUpdated;
event ProgressionEndedEventHandler? OnEnded;
float Progression { get; }
string Status { get; }
delegate void ProgressionUpdatedEventHandler(IReadOnlyProgressionTracker sender, float previousProgression, string previousStatus);
delegate void ProgressionEndedEventHandler(IReadOnlyProgressionTracker sender);
}

View File

@ -0,0 +1,36 @@
namespace Syntriax.Engine.Core;
public class ProgressionTracker : IProgressionTracker
{
public event IReadOnlyProgressionTracker.ProgressionUpdatedEventHandler? OnUpdated = null;
public event IReadOnlyProgressionTracker.ProgressionEndedEventHandler? OnEnded = null;
public float Progression { get; private set; } = 0f;
public string Status { get; private set; } = "Default";
void IProgressionTracker.Set(float progression, string status)
{
if (Progression >= 1f)
return;
float previousProgression = Progression;
string previousStatus = Status;
Progression = progression.Clamp(Progression, 1f);
Status = status;
OnUpdated?.InvokeSafe(this, previousProgression, previousStatus);
if (progression >= 1f)
OnEnded?.InvokeSafe(this);
}
void IProgressionTracker.Reset()
{
Progression = 0f;
Status = "Default";
OnUpdated = null;
OnEnded = null;
}
}

View File

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Syntriax.Engine.Core;
public record struct ProgressiveTask<T>(IReadOnlyProgressionTracker ProgressionTracker, Task<T> Task)
{
public static implicit operator (IReadOnlyProgressionTracker progressionTracker, Task<T> task)(ProgressiveTask<T> value) => (value.ProgressionTracker, value.Task);
public static implicit operator ProgressiveTask<T>((IReadOnlyProgressionTracker progressionTracker, Task<T> task) value) => new(value.progressionTracker, value.task);
}

View File

@ -30,6 +30,13 @@ public static class Math
/// </summary>
public const float DegreeToRadian = PI / 180f;
/// <summary>
/// Gets one minus of given <see cref="T"/>.
/// </summary>
/// <param name="value">The value <see cref="T"/>.</param>
/// <returns>One minus of given <see cref="T"/>.</returns>
public static T OneMinus<T>(T value) where T : INumber<T> => T.One - value;
/// <summary>
/// Adds two <see cref="T"/>s.
/// </summary>
@ -242,13 +249,13 @@ public static class Math
public static float Round(float x, int digits, MidpointRounding mode) => MathF.Round(x, digits, mode);
/// <summary>
/// Rounds a number to a specified number of fractional digits.
/// Rounds a number to an integer.
/// </summary>
/// <param name="x">The number to round.</param>
/// <param name="digits">The number of fractional digits in the return value.</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 <paramref name="digits"/> fractional digits.</returns>
public static int RoundToInt(float x) => (int)MathF.Round(x, 0, MidpointRounding.ToEven);
/// <param name="roundMode">Specification for how to round <paramref name="x"/> if it's midway between two numbers</param>
/// <returns></returns>
public static int RoundToInt(float x, RoundMode roundMode = RoundMode.Ceil) => (int)MathF.Round(x, 0, roundMode == RoundMode.Ceil ? MidpointRounding.ToPositiveInfinity : MidpointRounding.ToNegativeInfinity);
public enum RoundMode { Ceil, Floor };
/// <summary>
/// Returns the square of a number.

View File

@ -5,6 +5,9 @@ namespace Syntriax.Engine.Core;
public static class MathExtensions
{
/// <inheritdoc cref="Math.OneMinus{T}(T)" />
public static T OneMinus<T>(this T value) where T : INumber<T> => Math.OneMinus(value);
/// <inheritdoc cref="Math.Add{T}(T, T)" />
public static T Add<T>(this T left, T value) where T : INumber<T> => Math.Add(left, value);
@ -80,8 +83,8 @@ public static class MathExtensions
/// <inheritdoc cref="Math.Round(float, int, MidpointRounding)" />
public static float Round(this float x, int digits, MidpointRounding mode) => Math.Round(x, digits, mode);
/// <inheritdoc cref="Math.RoundToInt(float)" />
public static int RoundToInt(this float x) => Math.RoundToInt(x);
/// <inheritdoc cref="Math.RoundToInt(float, Math.RoundMode)" />
public static int RoundToInt(this float x, Math.RoundMode roundMode = Math.RoundMode.Ceil) => Math.RoundToInt(x, roundMode);
/// <inheritdoc cref="Math.Sqr{T}(T)" />
public static T Sqr<T>(this T x) where T : INumber<T> => Math.Sqr(x);

10
Engine.Core/Preserver.cs Normal file
View File

@ -0,0 +1,10 @@
namespace Syntriax.Engine.Core
{
// 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.
// I will hopefully one day fix it and remove this.
public static class Preserver
{
public static void Preserve() { }
}
}

View File

@ -0,0 +1,185 @@
namespace Syntriax.Engine.Core;
/// <summary>
/// Represents an HSV color.
/// </summary>
/// <param name="hue">Hue of the <see cref="ColorHSV"/>.</param>
/// <param name="saturation">Saturation of the <see cref="ColorHSV"/>.</param>
/// <param name="value">Value of the <see cref="ColorHSV"/>.</param>
/// <remarks>
/// Initializes a new instance of the <see cref="ColorHSV"/> struct with the specified values.
/// </remarks>
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
public readonly struct ColorHSV(float hue, float saturation, float value)
{
/// <summary>
/// The Hue value of the <see cref="ColorHSV"/>.
/// </summary>
public readonly float Hue = hue.Clamp(0f, 1f);
/// <summary>
/// The Saturation value of the <see cref="ColorHSV"/>.
/// </summary>
public readonly float Saturation = saturation.Clamp(0f, 1f);
/// <summary>
/// The Value value of the <see cref="ColorHSV"/>.
/// </summary>
public readonly float Value = value.Clamp(0f, 1f);
public static ColorHSV operator -(ColorHSV color) => new(color.Hue.OneMinus().Clamp(0f, 1f), color.Saturation.OneMinus().Clamp(0f, 1f), color.Value.OneMinus().Clamp(0f, 1f));
public static ColorHSV operator +(ColorHSV left, ColorHSV right) => new((left.Hue + right.Hue).Clamp(0f, 1f), (left.Saturation + right.Saturation).Clamp(0f, 1f), (left.Value + right.Value).Clamp(0f, 1f));
public static ColorHSV operator -(ColorHSV left, ColorHSV right) => new((left.Hue - right.Hue).Clamp(0f, 1f), (left.Saturation - right.Saturation).Clamp(0f, 1f), (left.Value - right.Value).Clamp(0f, 1f));
public static ColorHSV operator *(ColorHSV left, ColorHSV right) => new((left.Hue * right.Hue).Clamp(0f, 1f), (left.Saturation * right.Saturation).Clamp(0f, 1f), (left.Value * right.Value).Clamp(0f, 1f));
public static ColorHSV operator *(ColorHSV color, float value) => new((color.Hue * value).Clamp(0f, 1f), (color.Saturation * value).Clamp(0f, 1f), (color.Value * value).Clamp(0f, 1f));
public static ColorHSV operator *(float value, ColorHSV color) => new((color.Hue * value).Clamp(0f, 1f), (color.Saturation * value).Clamp(0f, 1f), (color.Value * value).Clamp(0f, 1f));
public static ColorHSV operator /(ColorHSV color, float value) => new((color.Hue / value).Clamp(0f, 1f), (color.Saturation / value).Clamp(0f, 1f), (color.Value / value).Clamp(0f, 1f));
public static bool operator ==(ColorHSV left, ColorHSV right) => left.Hue.ApproximatelyEquals(right.Hue) && left.Saturation.ApproximatelyEquals(right.Saturation) && left.Value.ApproximatelyEquals(right.Value);
public static bool operator !=(ColorHSV left, ColorHSV right) => !left.Hue.ApproximatelyEquals(right.Hue) || !left.Saturation.ApproximatelyEquals(right.Saturation) || !left.Value.ApproximatelyEquals(right.Value);
public static implicit operator ColorHSV(ColorRGBA rgba) => (ColorRGB)rgba;
public static implicit operator ColorHSV(ColorRGB rgb)
{
float hue;
float saturation;
float value;
float rd = rgb.R / 255f;
float gd = rgb.G / 255f;
float bd = rgb.B / 255f;
float max = Math.Max(rd, Math.Max(gd, bd));
float min = Math.Min(rd, Math.Min(gd, bd));
float delta = max - min;
if (delta.ApproximatelyEquals(0))
hue = 0f;
else if (max.ApproximatelyEquals(rd))
hue = 60f * ((gd - bd) / delta % 6f);
else if (max.ApproximatelyEquals(gd))
hue = 60f * (((bd - rd) / delta) + 2f);
else
hue = 60f * (((rd - gd) / delta) + 4f);
if (hue < 0f)
hue += 360f;
hue /= 360f;
saturation = max.ApproximatelyEquals(0f) ? 0f : delta / max;
value = max;
return new(hue, saturation, value);
}
/// <summary>
/// Inverts the given <see cref="ColorHSV"/>.
/// </summary>
/// <param name="color">The <see cref="ColorHSV"/>.</param>
/// <returns>The inverted <see cref="ColorHSV"/>.</returns>
public static ColorHSV Invert(ColorHSV color) => -color;
/// <summary>
/// Adds two <see cref="ColorHSV"/>s.
/// </summary>
/// <param name="left">The first <see cref="ColorHSV"/>.</param>
/// <param name="right">The second <see cref="ColorHSV"/>.</param>
/// <returns>The sum of the two <see cref="ColorHSV"/>s.</returns>
public static ColorHSV Add(ColorHSV left, ColorHSV right) => left + right;
/// <summary>
/// Subtracts one <see cref="ColorHSV"/> from another.
/// </summary>
/// <param name="left">The <see cref="ColorHSV"/> to subtract from.</param>
/// <param name="right">The <see cref="ColorHSV"/> to subtract.</param>
/// <returns>The result of subtracting the second <see cref="ColorHSV"/> from the first.</returns>
public static ColorHSV Subtract(ColorHSV left, ColorHSV right) => left - right;
/// <summary>
/// Multiplies a <see cref="ColorHSV"/> by a scalar value.
/// </summary>
/// <param name="color">The <see cref="ColorHSV"/>.</param>
/// <param name="value">The scalar value.</param>
/// <returns>The result of multiplying the <see cref="ColorHSV"/> by the scalar value.</returns>
public static ColorHSV Multiply(ColorHSV color, float value) => color * value;
/// <summary>
/// Divides a <see cref="ColorHSV"/> by a scalar value.
/// </summary>
/// <param name="color">The <see cref="ColorHSV"/>.</param>
/// <param name="value">The scalar value.</param>
/// <returns>The result of dividing the <see cref="ColorHSV"/> by the scalar value.</returns>
public static ColorHSV Divide(ColorHSV color, float value) => color / value;
/// <summary>
/// Calculates the <see cref="ColorHSV"/> from one point to another.
/// </summary>
/// <param name="from">The starting point.</param>
/// <param name="to">The ending point.</param>
/// <returns>The <see cref="ColorHSV"/> from the starting point to the ending point.</returns>
public static ColorHSV FromTo(ColorHSV from, ColorHSV to) => to - from;
/// <summary>
/// Performs linear interpolation between two <see cref="ColorHSV"/>s.
/// </summary>
/// <param name="from">The starting <see cref="ColorHSV"/> (t = 0).</param>
/// <param name="to">The ending <see cref="ColorHSV"/> (t = 1).</param>
/// <param name="t">The interpolation parameter.</param>
/// <returns>The interpolated <see cref="ColorHSV"/>.</returns>
public static ColorHSV Lerp(ColorHSV from, ColorHSV to, float t) => from + FromTo(from, to) * t;
/// <summary>
/// Converts the <see cref="ColorHSV"/> to its string representation.
/// </summary>
/// <returns>A string representation of the <see cref="ColorHSV"/>.</returns>
public override string ToString() => $"{nameof(ColorHSV)}({Hue}, {Saturation}, {Value})";
/// <summary>
/// Checks if two <see cref="ColorHSV"/>s are approximately equal within a specified epsilon range.
/// </summary>
/// <param name="left">The first <see cref="ColorHSV"/>.</param>
/// <param name="right">The second <see cref="ColorHSV"/>.</param>
/// <param name="epsilon">The epsilon range.</param>
/// <returns><see cref="true"/> if the <see cref="ColorHSV"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
public static bool ApproximatelyEquals(ColorHSV left, ColorHSV right, float epsilon = float.Epsilon)
=> left.Hue.ApproximatelyEquals(right.Hue, epsilon) && left.Saturation.ApproximatelyEquals(right.Saturation, epsilon) && left.Value.ApproximatelyEquals(right.Value, epsilon);
/// <summary>
/// Determines whether the specified object is equal to the current <see cref="ColorHSV"/>.
/// </summary>
/// <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>
public override bool Equals(object? obj) => obj is ColorHSV objVec && Hue.Equals(objVec.Hue) && Saturation.Equals(objVec.Saturation) && Value.Equals(objVec.Value);
/// <summary>
/// Generates a hash code for the <see cref="ColorHSV"/>.
/// </summary>
/// <returns>A hash code for the <see cref="ColorHSV"/>.</returns>
public override int GetHashCode() => System.HashCode.Combine(Hue, Saturation, Value);
}
/// <summary>
/// Provides extension methods for <see cref="ColorHSV"/> type.
/// </summary>
public static class ColorHSVExtensions
{
/// <inheritdoc cref="ColorHSV.Add(ColorHSV, ColorHSV)" />
public static ColorHSV Add(this ColorHSV color, ColorHSV value) => ColorHSV.Add(color, value);
/// <inheritdoc cref="ColorHSV.Subtract(ColorHSV, ColorHSV)" />
public static ColorHSV Subtract(this ColorHSV color, ColorHSV value) => ColorHSV.Subtract(color, value);
/// <inheritdoc cref="ColorHSV.Multiply(ColorHSV, ColorHSV)" />
public static ColorHSV Multiply(this ColorHSV color, float value) => ColorHSV.Multiply(color, value);
/// <inheritdoc cref="ColorHSV.Divide(ColorHSV, ColorHSV)" />
public static ColorHSV Divide(this ColorHSV color, float value) => ColorHSV.Divide(color, value);
/// <inheritdoc cref="ColorHSV.FromTo(ColorHSV, ColorHSV)" />
public static ColorHSV FromTo(this ColorHSV from, ColorHSV to) => ColorHSV.FromTo(from, to);
/// <inheritdoc cref="ColorHSV.Lerp(ColorHSV, ColorHSV, float)" />
public static ColorHSV Lerp(this ColorHSV from, ColorHSV to, float t) => ColorHSV.Lerp(from, to, t);
/// <inheritdoc cref="ColorHSV.ApproximatelyEquals(ColorHSV, ColorHSV, float) " />
public static bool ApproximatelyEquals(this ColorHSV left, ColorHSV right, float epsilon = float.Epsilon) => ColorHSV.ApproximatelyEquals(left, right, epsilon);
}

View File

@ -0,0 +1,164 @@
namespace Syntriax.Engine.Core;
/// <summary>
/// Represents an RGB color.
/// </summary>
/// <param name="r">Red value of the <see cref="ColorRGB"/>.</param>
/// <param name="g">Green value of the <see cref="ColorRGB"/>.</param>
/// <param name="b">Blue value of the <see cref="ColorRGB"/>.</param>
/// <remarks>
/// Initializes a new instance of the <see cref="ColorRGB"/> struct with the specified values.
/// </remarks>
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
public readonly struct ColorRGB(byte r, byte g, byte b)
{
/// <summary>
/// The Red value of the <see cref="ColorRGB"/>.
/// </summary>
public readonly byte R = r;
/// <summary>
/// The Green value of the <see cref="ColorRGB"/>.
/// </summary>
public readonly byte G = g;
/// <summary>
/// The Blue value of the <see cref="ColorRGB"/>.
/// </summary>
public readonly byte B = b;
public static ColorRGB operator -(ColorRGB color) => new((byte)(255 - color.R), (byte)(255 - color.G), (byte)(255 - color.B));
public static ColorRGB operator +(ColorRGB left, ColorRGB right) => new((byte)(left.R + right.R).Clamp(0, 255), (byte)(left.G + right.G).Clamp(0, 255), (byte)(left.B + right.B).Clamp(0, 255));
public static ColorRGB operator -(ColorRGB left, ColorRGB right) => new((byte)(left.R - right.R).Clamp(0, 255), (byte)(left.G - right.G).Clamp(0, 255), (byte)(left.B - right.B).Clamp(0, 255));
public static ColorRGB operator *(ColorRGB left, ColorRGB right) => new((byte)(left.R * right.R).Clamp(0, 255), (byte)(left.G * right.G).Clamp(0, 255), (byte)(left.B * right.B).Clamp(0, 255));
public static ColorRGB operator *(ColorRGB color, float value) => new((byte)(color.R * value).Clamp(0, 255), (byte)(color.G * value).Clamp(0, 255), (byte)(color.B * value).Clamp(0, 255));
public static ColorRGB operator *(float value, ColorRGB color) => new((byte)(color.R * value).Clamp(0, 255), (byte)(color.G * value).Clamp(0, 255), (byte)(color.B * value).Clamp(0, 255));
public static ColorRGB operator /(ColorRGB color, float value) => new((byte)(color.R / value).Clamp(0, 255), (byte)(color.G / value).Clamp(0, 255), (byte)(color.B / value).Clamp(0, 255));
public static bool operator ==(ColorRGB left, ColorRGB right) => left.R == right.R && left.G == right.G && left.B == right.B;
public static bool operator !=(ColorRGB left, ColorRGB right) => left.R != right.R || left.G != right.G || left.B != right.B;
public static implicit operator ColorRGB(ColorRGBA rgba) => new(rgba.R, rgba.G, rgba.B);
public static implicit operator ColorRGB(ColorHSV hsv)
{
float hue = hsv.Hue * 360f;
float chroma = hsv.Value * hsv.Saturation;
float x = chroma * (1f - Math.Abs(hue / 60f % 2f - 1f));
float m = hsv.Value - chroma;
float r1 = 0f;
float g1 = 0f;
float b1 = 0f;
if (hue < 60) { r1 = chroma; g1 = x; b1 = 0; }
else if (hue < 120) { r1 = x; g1 = chroma; b1 = 0; }
else if (hue < 180) { r1 = 0; g1 = chroma; b1 = x; }
else if (hue < 240) { r1 = 0; g1 = x; b1 = chroma; }
else if (hue < 300) { r1 = x; g1 = 0; b1 = chroma; }
else if (hue <= 360) { r1 = chroma; g1 = 0; b1 = x; }
byte r = (byte)Math.RoundToInt((r1 + m) * 255);
byte g = (byte)Math.RoundToInt((g1 + m) * 255);
byte b = (byte)Math.RoundToInt((b1 + m) * 255);
return new(r, g, b);
}
/// <summary>
/// Inverts the given <see cref="ColorRGB"/>.
/// </summary>
/// <param name="color">The <see cref="ColorRGB"/>.</param>
/// <returns>The inverted <see cref="ColorRGB"/>.</returns>
public static ColorRGB Invert(ColorRGB color) => -color;
/// <summary>
/// Adds two <see cref="ColorRGB"/>s.
/// </summary>
/// <param name="left">The first <see cref="ColorRGB"/>.</param>
/// <param name="right">The second <see cref="ColorRGB"/>.</param>
/// <returns>The sum of the two <see cref="ColorRGB"/>s.</returns>
public static ColorRGB Add(ColorRGB left, ColorRGB right) => left + right;
/// <summary>
/// Subtracts one <see cref="ColorRGB"/> from another.
/// </summary>
/// <param name="left">The <see cref="ColorRGB"/> to subtract from.</param>
/// <param name="right">The <see cref="ColorRGB"/> to subtract.</param>
/// <returns>The result of subtracting the second <see cref="ColorRGB"/> from the first.</returns>
public static ColorRGB Subtract(ColorRGB left, ColorRGB right) => left - right;
/// <summary>
/// Multiplies a <see cref="ColorRGB"/> by a scalar value.
/// </summary>
/// <param name="color">The <see cref="ColorRGB"/>.</param>
/// <param name="value">The scalar value.</param>
/// <returns>The result of multiplying the <see cref="ColorRGB"/> by the scalar value.</returns>
public static ColorRGB Multiply(ColorRGB color, float value) => color * value;
/// <summary>
/// Divides a <see cref="ColorRGB"/> by a scalar value.
/// </summary>
/// <param name="color">The <see cref="ColorRGB"/>.</param>
/// <param name="value">The scalar value.</param>
/// <returns>The result of dividing the <see cref="ColorRGB"/> by the scalar value.</returns>
public static ColorRGB Divide(ColorRGB color, float value) => color / value;
/// <summary>
/// Calculates the <see cref="ColorRGB"/> from one point to another.
/// </summary>
/// <param name="from">The starting point.</param>
/// <param name="to">The ending point.</param>
/// <returns>The <see cref="ColorRGB"/> from the starting point to the ending point.</returns>
public static ColorRGB FromTo(ColorRGB from, ColorRGB to) => to - from;
/// <summary>
/// Performs linear interpolation between two <see cref="ColorRGB"/>s.
/// </summary>
/// <param name="from">The starting <see cref="ColorRGB"/> (t = 0).</param>
/// <param name="to">The ending <see cref="ColorRGB"/> (t = 1).</param>
/// <param name="t">The interpolation parameter.</param>
/// <returns>The interpolated <see cref="ColorRGB"/>.</returns>
public static ColorRGB Lerp(ColorRGB from, ColorRGB to, float t) => from + FromTo(from, to) * t;
/// <summary>
/// Converts the <see cref="ColorRGB"/> to its string representation.
/// </summary>
/// <returns>A string representation of the <see cref="ColorRGB"/>.</returns>
public override string ToString() => $"{nameof(ColorRGB)}({R}, {G}, {B})";
/// <summary>
/// Determines whether the specified object is equal to the current <see cref="ColorRGB"/>.
/// </summary>
/// <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>
public override bool Equals(object? obj) => obj is ColorRGB objVec && R.Equals(objVec.R) && G.Equals(objVec.G) && B.Equals(objVec.B);
/// <summary>
/// Generates a hash code for the <see cref="ColorRGB"/>.
/// </summary>
/// <returns>A hash code for the <see cref="ColorRGB"/>.</returns>
public override int GetHashCode() => System.HashCode.Combine(R, G, B);
}
/// <summary>
/// Provides extension methods for <see cref="ColorRGB"/> type.
/// </summary>
public static class ColorRGBExtensions
{
/// <inheritdoc cref="ColorRGB.Add(ColorRGB, ColorRGB)" />
public static ColorRGB Add(this ColorRGB color, ColorRGB value) => ColorRGB.Add(color, value);
/// <inheritdoc cref="ColorRGB.Subtract(ColorRGB, ColorRGB)" />
public static ColorRGB Subtract(this ColorRGB color, ColorRGB value) => ColorRGB.Subtract(color, value);
/// <inheritdoc cref="ColorRGB.Multiply(ColorRGB, ColorRGB)" />
public static ColorRGB Multiply(this ColorRGB color, float value) => ColorRGB.Multiply(color, value);
/// <inheritdoc cref="ColorRGB.Divide(ColorRGB, ColorRGB)" />
public static ColorRGB Divide(this ColorRGB color, float value) => ColorRGB.Divide(color, value);
/// <inheritdoc cref="ColorRGB.FromTo(ColorRGB, ColorRGB)" />
public static ColorRGB FromTo(this ColorRGB from, ColorRGB to) => ColorRGB.FromTo(from, to);
/// <inheritdoc cref="ColorRGB.Lerp(ColorRGB, ColorRGB, float)" />
public static ColorRGB Lerp(this ColorRGB from, ColorRGB to, float t) => ColorRGB.Lerp(from, to, t);
}

View File

@ -0,0 +1,147 @@
namespace Syntriax.Engine.Core;
/// <summary>
/// Represents an RGBA color.
/// </summary>
/// <param name="r">Red value of the <see cref="ColorRGBA"/>.</param>
/// <param name="g">Green value of the <see cref="ColorRGBA"/>.</param>
/// <param name="b">Blue value of the <see cref="ColorRGBA"/>.</param>
/// <param name="a">Alpha value of the <see cref="ColorRGBA"/>.</param>
/// <remarks>
/// Initializes a new instance of the <see cref="ColorRGBA"/> struct with the specified values.
/// </remarks>
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
public readonly struct ColorRGBA(byte r, byte g, byte b, byte a = 255)
{
/// <summary>
/// The Red value of the <see cref="ColorRGBA"/>.
/// </summary>
public readonly byte R = r;
/// <summary>
/// The Green value of the <see cref="ColorRGBA"/>.
/// </summary>
public readonly byte G = g;
/// <summary>
/// The Blue value of the <see cref="ColorRGBA"/>.
/// </summary>
public readonly byte B = b;
/// <summary>
/// The Alpha value of the <see cref="ColorRGBA"/>.
/// </summary>
public readonly byte A = a;
public static ColorRGBA operator -(ColorRGBA color) => new((byte)(255 - color.R), (byte)(255 - color.G), (byte)(255 - color.B), color.A);
public static ColorRGBA operator +(ColorRGBA left, ColorRGBA right) => new((byte)(left.R + right.R).Clamp(0, 255), (byte)(left.G + right.G).Clamp(0, 255), (byte)(left.B + right.B).Clamp(0, 255), (byte)(left.A + right.A).Clamp(0, 255));
public static ColorRGBA operator -(ColorRGBA left, ColorRGBA right) => new((byte)(left.R - right.R).Clamp(0, 255), (byte)(left.G - right.G).Clamp(0, 255), (byte)(left.B - right.B).Clamp(0, 255), (byte)(left.A - right.A).Clamp(0, 255));
public static ColorRGBA operator *(ColorRGBA left, ColorRGBA right) => new((byte)(left.R * right.R).Clamp(0, 255), (byte)(left.G * right.G).Clamp(0, 255), (byte)(left.B * right.B).Clamp(0, 255), (byte)(left.A * right.A).Clamp(0, 255));
public static ColorRGBA operator *(ColorRGBA color, float value) => new((byte)(color.R * value).Clamp(0, 255), (byte)(color.G * value).Clamp(0, 255), (byte)(color.B * value).Clamp(0, 255), (byte)(color.A * value).Clamp(0, 255));
public static ColorRGBA operator *(float value, ColorRGBA color) => new((byte)(color.R * value).Clamp(0, 255), (byte)(color.G * value).Clamp(0, 255), (byte)(color.B * value).Clamp(0, 255), (byte)(color.A * value).Clamp(0, 255));
public static ColorRGBA operator /(ColorRGBA color, float value) => new((byte)(color.R / value).Clamp(0, 255), (byte)(color.G / value).Clamp(0, 255), (byte)(color.B / value).Clamp(0, 255), (byte)(color.A / value).Clamp(0, 255));
public static bool operator ==(ColorRGBA left, ColorRGBA right) => left.R == right.R && left.G == right.G && left.B == right.B && left.A == right.A;
public static bool operator !=(ColorRGBA left, ColorRGBA right) => left.R != right.R || left.G != right.G || left.B != right.B || left.A != right.A;
public static implicit operator ColorRGBA(ColorRGB rgb) => new(rgb.R, rgb.G, rgb.B, 255);
public static implicit operator ColorRGBA(ColorHSV hsv) => (ColorRGB)hsv;
/// <summary>
/// Inverts the given <see cref="ColorRGBA"/>.
/// </summary>
/// <param name="color">The <see cref="ColorRGBA"/>.</param>
/// <returns>The inverted <see cref="ColorRGBA"/>.</returns>
public static ColorRGBA Invert(ColorRGBA color) => -color;
/// <summary>
/// Adds two <see cref="ColorRGBA"/>s.
/// </summary>
/// <param name="left">The first <see cref="ColorRGBA"/>.</param>
/// <param name="right">The second <see cref="ColorRGBA"/>.</param>
/// <returns>The sum of the two <see cref="ColorRGBA"/>s.</returns>
public static ColorRGBA Add(ColorRGBA left, ColorRGBA right) => left + right;
/// <summary>
/// Subtracts one <see cref="ColorRGBA"/> from another.
/// </summary>
/// <param name="left">The <see cref="ColorRGBA"/> to subtract from.</param>
/// <param name="right">The <see cref="ColorRGBA"/> to subtract.</param>
/// <returns>The result of subtracting the second <see cref="ColorRGBA"/> from the first.</returns>
public static ColorRGBA Subtract(ColorRGBA left, ColorRGBA right) => left - right;
/// <summary>
/// Multiplies a <see cref="ColorRGBA"/> by a scalar value.
/// </summary>
/// <param name="color">The <see cref="ColorRGBA"/>.</param>
/// <param name="value">The scalar value.</param>
/// <returns>The result of multiplying the <see cref="ColorRGBA"/> by the scalar value.</returns>
public static ColorRGBA Multiply(ColorRGBA color, float value) => color * value;
/// <summary>
/// Divides a <see cref="ColorRGBA"/> by a scalar value.
/// </summary>
/// <param name="color">The <see cref="ColorRGBA"/>.</param>
/// <param name="value">The scalar value.</param>
/// <returns>The result of dividing the <see cref="ColorRGBA"/> by the scalar value.</returns>
public static ColorRGBA Divide(ColorRGBA color, float value) => color / value;
/// <summary>
/// Calculates the <see cref="ColorRGBA"/> from one point to another.
/// </summary>
/// <param name="from">The starting point.</param>
/// <param name="to">The ending point.</param>
/// <returns>The <see cref="ColorRGBA"/> from the starting point to the ending point.</returns>
public static ColorRGBA FromTo(ColorRGBA from, ColorRGBA to) => to - from;
/// <summary>
/// Performs linear interpolation between two <see cref="ColorRGBA"/>s.
/// </summary>
/// <param name="from">The starting <see cref="ColorRGBA"/> (t = 0).</param>
/// <param name="to">The ending <see cref="ColorRGBA"/> (t = 1).</param>
/// <param name="t">The interpolation parameter.</param>
/// <returns>The interpolated <see cref="ColorRGBA"/>.</returns>
public static ColorRGBA Lerp(ColorRGBA from, ColorRGBA to, float t) => from + FromTo(from, to) * t;
/// <summary>
/// Converts the <see cref="ColorRGBA"/> to its string representation.
/// </summary>
/// <returns>A string representation of the <see cref="ColorRGBA"/>.</returns>
public override string ToString() => $"{nameof(ColorRGBA)}({R}, {G}, {B}, {A})";
/// <summary>
/// Determines whether the specified object is equal to the current <see cref="ColorRGBA"/>.
/// </summary>
/// <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>
public override bool Equals(object? obj) => obj is ColorRGBA objVec && R.Equals(objVec.R) && G.Equals(objVec.G) && B.Equals(objVec.B) && A.Equals(objVec.A);
/// <summary>
/// Generates a hash code for the <see cref="ColorRGBA"/>.
/// </summary>
/// <returns>A hash code for the <see cref="ColorRGBA"/>.</returns>
public override int GetHashCode() => System.HashCode.Combine(R, G, B, A);
}
/// <summary>
/// Provides extension methods for <see cref="ColorRGBA"/> type.
/// </summary>
public static class ColorRGBAExtensions
{
/// <inheritdoc cref="ColorRGBA.Add(ColorRGBA, ColorRGBA)" />
public static ColorRGBA Add(this ColorRGBA color, ColorRGBA value) => ColorRGBA.Add(color, value);
/// <inheritdoc cref="ColorRGBA.Subtract(ColorRGBA, ColorRGBA)" />
public static ColorRGBA Subtract(this ColorRGBA color, ColorRGBA value) => ColorRGBA.Subtract(color, value);
/// <inheritdoc cref="ColorRGBA.Multiply(ColorRGBA, ColorRGBA)" />
public static ColorRGBA Multiply(this ColorRGBA color, float value) => ColorRGBA.Multiply(color, value);
/// <inheritdoc cref="ColorRGBA.Divide(ColorRGBA, ColorRGBA)" />
public static ColorRGBA Divide(this ColorRGBA color, float value) => ColorRGBA.Divide(color, value);
/// <inheritdoc cref="ColorRGBA.FromTo(ColorRGBA, ColorRGBA)" />
public static ColorRGBA FromTo(this ColorRGBA from, ColorRGBA to) => ColorRGBA.FromTo(from, to);
/// <inheritdoc cref="ColorRGBA.Lerp(ColorRGBA, ColorRGBA, float)" />
public static ColorRGBA Lerp(this ColorRGBA from, ColorRGBA to, float t) => ColorRGBA.Lerp(from, to, t);
}

View File

@ -1,4 +1,3 @@
using System;
using System.Diagnostics.CodeAnalysis;
namespace Syntriax.Engine.Core;
@ -68,12 +67,12 @@ public readonly struct Line2D(Vector2D from, Vector2D to)
/// </summary>
public static float GetT(Line2D line, Vector2D point)
{
float fromX = MathF.Abs(line.From.X);
float toX = MathF.Abs(line.To.X);
float pointX = MathF.Abs(point.X);
float fromX = Math.Abs(line.From.X);
float toX = Math.Abs(line.To.X);
float pointX = Math.Abs(point.X);
float min = MathF.Min(fromX, toX);
float max = MathF.Max(fromX, toX) - min;
float min = Math.Min(fromX, toX);
float max = Math.Max(fromX, toX) - min;
pointX -= min;
@ -114,8 +113,8 @@ public readonly struct Line2D(Vector2D from, Vector2D to)
/// </summary>
public static bool OnSegment(Line2D line, Vector2D point)
{
if (point.X <= MathF.Max(line.From.X, line.To.X) && point.X >= MathF.Min(line.From.X, line.To.X) &&
point.Y <= MathF.Max(line.From.Y, line.To.Y) && point.Y >= MathF.Min(line.From.Y, line.To.Y))
if (point.X <= Math.Max(line.From.X, line.To.X) && point.X >= Math.Min(line.From.X, line.To.X) &&
point.Y <= Math.Max(line.From.Y, line.To.Y) && point.Y >= Math.Min(line.From.Y, line.To.Y))
return true;
return false;
@ -173,7 +172,7 @@ public readonly struct Line2D(Vector2D from, Vector2D to)
float t = (pointVector.X * edgeVector.X + pointVector.Y * edgeVector.Y) / (edgeVector.X * edgeVector.X + edgeVector.Y * edgeVector.Y);
t = MathF.Max(0, MathF.Min(1, t));
t = Math.Max(0, Math.Min(1, t));
float closestX = line.From.X + t * edgeVector.X;
float closestY = line.From.Y + t * edgeVector.Y;

View File

@ -1,5 +1,3 @@
using System;
namespace Syntriax.Engine.Core;
/// <summary>
@ -178,11 +176,11 @@ public readonly struct Quaternion(float x, float y, float z, float w)
if (dot > 0.9995f)
return Lerp(from, to, t);
float angle = MathF.Acos(dot);
float sinAngle = MathF.Sin(angle);
float angle = Math.Acos(dot);
float sinAngle = Math.Sin(angle);
float fromWeight = MathF.Sin((1f - t) * angle) / sinAngle;
float toWeight = MathF.Sin(t * angle) / sinAngle;
float fromWeight = Math.Sin((1f - t) * angle) / sinAngle;
float toWeight = Math.Sin(t * angle) / sinAngle;
return from * fromWeight + to * toWeight;
}
@ -213,8 +211,8 @@ public readonly struct Quaternion(float x, float y, float z, float w)
public static Quaternion FromAxisAngle(Vector3D axis, float angle)
{
float halfAngle = angle * .5f;
float sinHalf = MathF.Sin(halfAngle);
return new Quaternion(axis.X * sinHalf, axis.Y * sinHalf, axis.Z * sinHalf, MathF.Cos(halfAngle));
float sinHalf = Math.Sin(halfAngle);
return new Quaternion(axis.X * sinHalf, axis.Y * sinHalf, axis.Z * sinHalf, Math.Cos(halfAngle));
}
/// <summary>
@ -301,7 +299,7 @@ public readonly struct Quaternion(float x, float y, float z, float w)
/// Generates a hash code for the <see cref="Quaternion"/>.
/// </summary>
/// <returns>A hash code for the <see cref="Quaternion"/>.</returns>
public override int GetHashCode() => HashCode.Combine(X, Y, Z);
public override int GetHashCode() => System.HashCode.Combine(X, Y, Z);
}
/// <summary>

View File

@ -11,19 +11,33 @@ namespace Syntriax.Engine.Core;
/// Initializes a new instance of a <see cref="Shape2D"/> struct with the specified vertices.
/// </remarks>
[System.Diagnostics.DebuggerDisplay("Vertices Count: {Vertices.Count}")]
public readonly struct Shape2D(List<Vector2D> vertices) : IEnumerable<Vector2D>
public class Shape2D(List<Vector2D> vertices) : IEnumerable<Vector2D>
{
public static readonly Shape2D Triangle = CreateNgon(3, Vector2D.Up);
public static readonly Shape2D Square = CreateNgon(4, Vector2D.One);
public static readonly Shape2D Pentagon = CreateNgon(5, Vector2D.Up);
public static readonly Shape2D Hexagon = CreateNgon(6, Vector2D.Right);
public static Shape2D Triangle => CreateNgon(3, Vector2D.Up);
public static Shape2D Square => CreateNgon(4, Vector2D.One);
public static Shape2D Pentagon => CreateNgon(5, Vector2D.Up);
public static Shape2D Hexagon => CreateNgon(6, Vector2D.Right);
private readonly List<Vector2D> _verticesList = vertices;
public event ShapeUpdatedEventHandler? OnShapeUpdated = null;
private List<Vector2D> _vertices = vertices;
/// <summary>
/// Gets the vertices of the <see cref="Shape2D"/>.
/// </summary>
public IReadOnlyList<Vector2D> Vertices => _verticesList;
public IReadOnlyList<Vector2D> Vertices
{
get => _vertices;
set
{
_vertices.Clear();
foreach (Vector2D vertex in value)
_vertices.Add(vertex);
OnShapeUpdated?.InvokeSafe(this);
}
}
/// <summary>
/// The vertex at the specified index.
@ -207,13 +221,15 @@ public readonly struct Shape2D(List<Vector2D> vertices) : IEnumerable<Vector2D>
/// <param name="from">The <see cref="Shape2D"/> to transform.</param>
/// <param name="transform">The <see cref="ITransform2D"/> to apply.</param>
/// <param name="to">The transformed <see cref="Shape2D"/>.</param>
public static void Transform(Shape2D from, ITransform2D transform, ref Shape2D to)
public static void Transform(Shape2D from, ITransform2D transform, Shape2D to)
{
to._verticesList.Clear();
to._vertices.Clear();
int count = from._verticesList.Count;
int count = from._vertices.Count;
for (int i = 0; i < count; i++)
to._verticesList.Add(transform.Transform(from[i]));
to._vertices.Add(transform.Transform(from[i]));
to.OnShapeUpdated?.InvokeSafe(to);
}
/// <summary>
@ -240,6 +256,8 @@ public readonly struct Shape2D(List<Vector2D> vertices) : IEnumerable<Vector2D>
/// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator() => Vertices.GetEnumerator();
public delegate void ShapeUpdatedEventHandler(Shape2D shape2D);
}
/// <summary>
@ -275,13 +293,13 @@ public static class Shape2DExtensions
public static Shape2D Transform(this ITransform2D transform, Shape2D shape) => Shape2D.Transform(shape, transform);
/// <inheritdoc cref="Shape2D.Transform(Shape2D, ITransform2D, Shape2D)" />
public static void Transform(this ITransform2D transform, Shape2D from, ref Shape2D to) => Shape2D.Transform(from, transform, ref to);
public static void Transform(this ITransform2D transform, Shape2D from, Shape2D to) => Shape2D.Transform(from, transform, to);
/// <inheritdoc cref="Shape2D.Transform(Shape2D, ITransform2D)" />
public static Shape2D Transform(this Shape2D shape, ITransform2D transform) => Shape2D.Transform(shape, transform);
/// <inheritdoc cref="Shape2D.Transform(Shape2D, ITransform2D, ref Shape2D)" />
public static void Transform(this Shape2D from, ITransform2D transform, ref Shape2D to) => Shape2D.Transform(from, transform, ref to);
/// <inheritdoc cref="Shape2D.Transform(Shape2D, ITransform2D,Shape2D)" />
public static void Transform(this Shape2D from, ITransform2D transform, Shape2D to) => Shape2D.Transform(from, transform, to);
/// <inheritdoc cref="Shape2D.ApproximatelyEquals(Shape2D, Shape2D, float)" />
public static bool ApproximatelyEquals(this Shape2D left, Shape2D right, float epsilon = float.Epsilon) => Shape2D.ApproximatelyEquals(left, right, epsilon);

View File

@ -1,5 +1,3 @@
using System;
namespace Syntriax.Engine.Core;
[System.Diagnostics.DebuggerDisplay("A: {A.ToString(), nq}, B: {B.ToString(), nq}, B: {C.ToString(), nq}")]
@ -10,7 +8,7 @@ public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C)
public readonly Vector2D C { get; init; } = C;
public readonly float Area
=> .5f * MathF.Abs(
=> .5f * Math.Abs(
A.X * (B.Y - C.Y) +
B.X * (C.Y - A.Y) +
C.X * (A.Y - B.Y)
@ -25,7 +23,7 @@ public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C)
float slopeBC = (triangle.C.Y - triangle.B.Y) / (triangle.C.X - triangle.B.X);
Vector2D center;
if (MathF.Abs(slopeAB - slopeBC) > float.Epsilon)
if (Math.Abs(slopeAB - slopeBC) > float.Epsilon)
{
float x = (slopeAB * slopeBC * (triangle.A.Y - triangle.C.Y) + slopeBC * (triangle.A.X + triangle.B.X) - slopeAB * (triangle.B.X + triangle.C.X)) / (2f * (slopeBC - slopeAB));
float y = -(x - (triangle.A.X + triangle.B.X) / 2f) / slopeAB + (triangle.A.Y + triangle.B.Y) / 2f;

View File

@ -1,5 +1,3 @@
using System;
namespace Syntriax.Engine.Core;
/// <summary>
@ -316,7 +314,7 @@ public readonly struct Vector2D(float x, float y)
/// Generates a hash code for the <see cref="Vector2D"/>.
/// </summary>
/// <returns>A hash code for the <see cref="Vector2D"/>.</returns>
public override int GetHashCode() => HashCode.Combine(X, Y);
public override int GetHashCode() => System.HashCode.Combine(X, Y);
}
/// <summary>

View File

@ -1,5 +1,3 @@
using System;
namespace Syntriax.Engine.Core;
/// <summary>
@ -290,7 +288,7 @@ public readonly struct Vector3D(float x, float y, float z)
/// Generates a hash code for the <see cref="Vector3D"/>.
/// </summary>
/// <returns>A hash code for the <see cref="Vector3D"/>.</returns>
public override int GetHashCode() => HashCode.Combine(X, Y, Z);
public override int GetHashCode() => System.HashCode.Combine(X, Y, Z);
}
/// <summary>

View File

@ -9,4 +9,10 @@ public interface ISerializer
T Deserialize<T>(string configuration);
string Serialize(object instance);
ProgressiveTask<object> DeserializeAsync(string configuration);
ProgressiveTask<object> DeserializeAsync(string configuration, Type type);
ProgressiveTask<T> DeserializeAsync<T>(string configuration);
ProgressiveTask<string> SerializeAsync(object instance);
}

View File

@ -110,7 +110,7 @@ public class Universe : BaseEntity, IUniverse
public void Update(UniverseTime engineTime)
{
Debug.AssertHelpers.AssertInitialized(this);
Debug.Assert.AssertInitialized(this);
UnscaledTime = engineTime;
Time = new(TimeSpan.FromTicks((long)(Time.TimeSinceStart.Ticks + engineTime.DeltaSpan.Ticks * TimeScale)), TimeSpan.FromTicks((long)(engineTime.DeltaSpan.Ticks * TimeScale)));
@ -125,7 +125,7 @@ public class Universe : BaseEntity, IUniverse
public void PreDraw()
{
Debug.AssertHelpers.AssertInitialized(this);
Debug.Assert.AssertInitialized(this);
for (int i = 0; i < UniverseObjects.Count; i++)
UniverseObjects[i].BehaviourController.UpdatePreDraw();

View File

@ -18,7 +18,7 @@ public class Collider2DShapeBehaviour : Collider2DBehaviourBase, IShapeCollider2
private Shape2D _shapeWorld = Shape2D.Square.CreateCopy();
private Shape2D _shapeLocal = Shape2D.Square;
public override void CalculateCollider() => Transform.Transform(ShapeLocal, ref _shapeWorld);
public override void CalculateCollider() => ShapeLocal.Transform(Transform, _shapeWorld);
public Collider2DShapeBehaviour() { }
public Collider2DShapeBehaviour(Shape2D shape)

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Syntriax.Engine.Physics2D</RootNamespace>

View File

@ -0,0 +1,10 @@
namespace Syntriax.Engine.Physics2D
{
// 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.
// I will hopefully one day fix it and remove this.
public static class Preserver
{
public static void Preserve() { }
}
}

View File

@ -1,3 +1,4 @@
using Syntriax.Engine.Core;
using Syntriax.Engine.Core.Serialization;
using YamlDotNet.Serialization;
@ -8,4 +9,5 @@ public interface IEngineTypeYamlConverter : IYamlTypeConverter
{
YamlSerializer Serializer { get; set; }
EntityRegistry EntityRegistry { get; set; }
IProgressionTracker ProgressionTracker { get; set; }
}

View File

@ -17,6 +17,9 @@ public class BehaviourControllerConverter : EngineTypeYamlSerializerBase<IBehavi
public override IBehaviourController? Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Reading behaviour controller");
string id;
IBehaviourController behaviourController;
@ -32,6 +35,7 @@ public class BehaviourControllerConverter : EngineTypeYamlSerializerBase<IBehavi
if (parser.Consume<Scalar>().Value.CompareTo(SERIALIZED_SCALAR_NAME) != 0)
throw new();
SerializedClass instanceSerializedClass = (SerializedClass)rootDeserializer(typeof(SerializedClass))!;
ProgressionTracker.Set(isTrackingController ? .5f : ProgressionTracker.Progression, $"Creating {instanceSerializedClass.Type}");
behaviourController = (IBehaviourController)instanceSerializedClass.CreateInstance(EntityRegistry);
string value = parser.Consume<Scalar>().Value;
@ -53,6 +57,7 @@ public class BehaviourControllerConverter : EngineTypeYamlSerializerBase<IBehavi
foreach (IBehaviour behaviour in behaviours)
behaviourController.AddBehaviour(behaviour);
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Created {instanceSerializedClass.Type}");
return behaviourController;
}
@ -60,6 +65,9 @@ public class BehaviourControllerConverter : EngineTypeYamlSerializerBase<IBehavi
{
IBehaviourController behaviourController = (IBehaviourController)value!;
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Serializing behaviour controller");
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(IBehaviourController.Id)));
@ -74,6 +82,7 @@ public class BehaviourControllerConverter : EngineTypeYamlSerializerBase<IBehavi
emitter.Emit(new Scalar(BEHAVIOURS_SCALAR_NAME));
serializer(behaviourController.GetBehaviours<IBehaviour>().Where(b => !b.GetType().HasAttribute<IgnoreSerializationAttribute>()));
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Serialized behaviour controller");
emitter.Emit(new MappingEnd());
}
}

View File

@ -13,6 +13,9 @@ public class BehaviourConverter : EngineTypeYamlSerializerBase<IBehaviour>
{
public override IBehaviour? Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Reading behaviour information");
string id;
int priority;
@ -32,6 +35,7 @@ public class BehaviourConverter : EngineTypeYamlSerializerBase<IBehaviour>
if (parser.Consume<Scalar>().Value.CompareTo(SERIALIZED_SCALAR_NAME) != 0)
throw new();
SerializedClass instanceSerializedClass = (SerializedClass)rootDeserializer(typeof(SerializedClass))!;
ProgressionTracker.Set(isTrackingController ? .5f : ProgressionTracker.Progression, $"Creating {instanceSerializedClass.Type}");
behaviour = (IBehaviour)instanceSerializedClass.CreateInstance(EntityRegistry);
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IBehaviour.StateEnable)) != 0)
@ -46,6 +50,7 @@ public class BehaviourConverter : EngineTypeYamlSerializerBase<IBehaviour>
stateEnable.Assign(behaviour);
behaviour.Assign(stateEnable);
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Created {instanceSerializedClass.Type}");
return behaviour;
}
@ -53,6 +58,9 @@ public class BehaviourConverter : EngineTypeYamlSerializerBase<IBehaviour>
{
IBehaviour behaviour = (IBehaviour)value!;
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Serializing behaviour");
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(IBehaviour.Id)));
@ -67,6 +75,7 @@ public class BehaviourConverter : EngineTypeYamlSerializerBase<IBehaviour>
emitter.Emit(new Scalar(nameof(IBehaviour.StateEnable)));
serializer(behaviour.StateEnable);
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Serialized behaviour");
emitter.Emit(new MappingEnd());
}
}

View File

@ -14,6 +14,7 @@ public abstract class EngineTypeYamlSerializerBase<T> : IEngineTypeYamlConverter
public EntityRegistry EntityRegistry { get; set; } = null!;
public YamlSerializer Serializer { get; set; } = null!;
public IProgressionTracker ProgressionTracker { get; set; } = null!;
public bool Accepts(Type type) => typeof(T).IsAssignableFrom(type);

View File

@ -0,0 +1,28 @@
using System;
using Syntriax.Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Syntriax.Engine.Serializers.Yaml;
public class ColorHSVConverter : EngineTypeYamlSerializerBase<ColorHSV>
{
private static readonly int SUBSTRING_START_LENGTH = nameof(ColorHSV).Length + 1;
public override ColorHSV Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
string value = parser.Consume<Scalar>().Value;
string insideParenthesis = value[SUBSTRING_START_LENGTH..^1];
string[] values = insideParenthesis.Split(", ");
return new ColorHSV(float.Parse(values[0]), float.Parse(values[1]), float.Parse(values[2]));
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
ColorHSV hsv = (ColorHSV)value!;
emitter.Emit(new Scalar($"{nameof(ColorHSV)}({hsv.Hue}, {hsv.Saturation}, {hsv.Value})"));
}
}

View File

@ -0,0 +1,28 @@
using System;
using Syntriax.Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Syntriax.Engine.Serializers.Yaml;
public class ColorRGBAConverter : EngineTypeYamlSerializerBase<ColorRGBA>
{
private static readonly int SUBSTRING_START_LENGTH = nameof(ColorRGBA).Length + 1;
public override ColorRGBA Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
string value = parser.Consume<Scalar>().Value;
string insideParenthesis = value[SUBSTRING_START_LENGTH..^1];
string[] values = insideParenthesis.Split(", ");
return new ColorRGBA(byte.Parse(values[0]), byte.Parse(values[1]), byte.Parse(values[2]), byte.Parse(values[3]));
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
ColorRGBA rgb = (ColorRGBA)value!;
emitter.Emit(new Scalar($"{nameof(ColorRGBA)}({rgb.R}, {rgb.G}, {rgb.B}, {rgb.A})"));
}
}

View File

@ -0,0 +1,28 @@
using System;
using Syntriax.Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Syntriax.Engine.Serializers.Yaml;
public class ColorRGBConverter : EngineTypeYamlSerializerBase<ColorRGB>
{
private static readonly int SUBSTRING_START_LENGTH = nameof(ColorRGB).Length + 1;
public override ColorRGB Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
string value = parser.Consume<Scalar>().Value;
string insideParenthesis = value[SUBSTRING_START_LENGTH..^1];
string[] values = insideParenthesis.Split(", ");
return new ColorRGB(byte.Parse(values[0]), byte.Parse(values[1]), byte.Parse(values[2]));
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
ColorRGB rgb = (ColorRGB)value!;
emitter.Emit(new Scalar($"{nameof(ColorRGB)}({rgb.R}, {rgb.G}, {rgb.B})"));
}
}

View File

@ -2,8 +2,9 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Syntriax.Engine.Core.Serialization;
using Syntriax.Engine.Core;
using Syntriax.Engine.Core.Factory;
using Syntriax.Engine.Core.Serialization;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
@ -15,6 +16,9 @@ public class SerializedClassConverter : EngineTypeYamlSerializerBase<SerializedC
{
public override SerializedClass? Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .1f : ProgressionTracker.Progression, $"Reading class");
SerializedClass serializedClass = new();
Dictionary<string, TypeContainer> publicDictionary = [];
Dictionary<string, TypeContainer> privateDictionary = [];
@ -39,6 +43,7 @@ public class SerializedClassConverter : EngineTypeYamlSerializerBase<SerializedC
foreach ((string key, TypeContainer typeContainer) in privateDictionary)
serializedClass.Private.Add(key, Serializer.InternalDeserialize(typeContainer.Value!.ToString()!, TypeFactory.GetType(typeContainer.Type)));
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Read {serializedClass.Type}");
return serializedClass;
}
@ -46,6 +51,9 @@ public class SerializedClassConverter : EngineTypeYamlSerializerBase<SerializedC
{
SerializedClass serializedClass = (SerializedClass)value!;
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Serializing {serializedClass.Type}");
Dictionary<string, TypeContainer> publics = [];
Dictionary<string, TypeContainer> privates = [];
@ -72,6 +80,7 @@ public class SerializedClassConverter : EngineTypeYamlSerializerBase<SerializedC
serializer(privates);
}
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Serialized {serializedClass.Type}");
emitter.Emit(new MappingEnd());
}
}

View File

@ -13,6 +13,9 @@ public class StateEnableConverter : EngineTypeYamlSerializerBase<IStateEnable>
{
public override IStateEnable? Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Reading state enable");
bool enabled;
IStateEnable stateEnable;
@ -32,6 +35,7 @@ public class StateEnableConverter : EngineTypeYamlSerializerBase<IStateEnable>
stateEnable.Enabled = enabled;
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Read state enable");
return stateEnable;
}
@ -39,6 +43,9 @@ public class StateEnableConverter : EngineTypeYamlSerializerBase<IStateEnable>
{
IStateEnable stateEnable = (IStateEnable)value!;
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Serializing state enable");
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(IStateEnable.Enabled)));
@ -47,6 +54,7 @@ public class StateEnableConverter : EngineTypeYamlSerializerBase<IStateEnable>
emitter.Emit(new Scalar(SERIALIZED_SCALAR_NAME));
serializer(new SerializedClass(stateEnable));
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Serialized state enable");
emitter.Emit(new MappingEnd());
}
}

View File

@ -15,6 +15,9 @@ public class UniverseConverter : EngineTypeYamlSerializerBase<IUniverse>
{
public override IUniverse? Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? 0.1f : ProgressionTracker.Progression, "Reading universe");
string id;
IUniverse universe;
@ -30,12 +33,14 @@ public class UniverseConverter : EngineTypeYamlSerializerBase<IUniverse>
if (parser.Consume<Scalar>().Value.CompareTo(SERIALIZED_SCALAR_NAME) != 0)
throw new();
SerializedClass instanceSerializedClass = (SerializedClass)rootDeserializer(typeof(SerializedClass))!;
ProgressionTracker.Set(isTrackingController ? .2f : ProgressionTracker.Progression, $"Creating {instanceSerializedClass.Type}");
universe = (IUniverse)instanceSerializedClass.CreateInstance(EntityRegistry);
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IUniverse.StateEnable)) != 0)
throw new();
stateEnable = (IStateEnable)rootDeserializer(typeof(IStateEnable))!;
ProgressionTracker.Set(isTrackingController ? .5f : ProgressionTracker.Progression, $"Reading universe objects");
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IUniverse.UniverseObjects)) != 0)
throw new();
universeObjects = (List<IUniverseObject>)rootDeserializer(typeof(List<IUniverseObject>))!;
@ -47,9 +52,16 @@ public class UniverseConverter : EngineTypeYamlSerializerBase<IUniverse>
stateEnable.Assign(universe);
universe.Assign(stateEnable);
foreach (IUniverseObject uo in universeObjects)
universe.Register(uo);
ProgressionTracker.Set(isTrackingController ? .9f : ProgressionTracker.Progression, "Registering universe objects");
for (int i = 0; i < universeObjects.Count; i++)
{
IUniverseObject uo = universeObjects[i];
ProgressionTracker.Set(isTrackingController ? .9f + .1f * ((float)i / universeObjects.Count) : ProgressionTracker.Progression, $"Registering {uo.Name}");
universe.Register(uo);
}
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Created {instanceSerializedClass.Type}");
return universe;
}
@ -57,16 +69,10 @@ public class UniverseConverter : EngineTypeYamlSerializerBase<IUniverse>
{
IUniverse universe = (IUniverse)value!;
IEnumerable<IUniverseObject> rootUniverseObjects = universe.UniverseObjects
.Select(uo =>
{
IUniverseObject root = uo;
while (root.Parent is IUniverseObject parent)
root = parent;
return root;
})
.Where(uo => !uo.GetType().HasAttribute<IgnoreSerializationAttribute>())
.Distinct();
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Serializing universe");
IEnumerable<IUniverseObject> rootUniverseObjects = universe.UniverseObjects.Where(uo => uo.Parent is null);
emitter.Emit(new MappingStart());
@ -82,6 +88,7 @@ public class UniverseConverter : EngineTypeYamlSerializerBase<IUniverse>
emitter.Emit(new Scalar(nameof(IUniverse.UniverseObjects)));
serializer(rootUniverseObjects);
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Serialized universe");
emitter.Emit(new MappingEnd());
}
}

View File

@ -15,6 +15,8 @@ public class UniverseObjectSerializer : EngineTypeYamlSerializerBase<IUniverseOb
{
public override IUniverseObject? Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
string name;
string id;
@ -28,6 +30,7 @@ public class UniverseObjectSerializer : EngineTypeYamlSerializerBase<IUniverseOb
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IUniverseObject.Name)) != 0)
throw new();
name = parser.Consume<Scalar>().Value;
ProgressionTracker.Set(isTrackingController ? .1f : ProgressionTracker.Progression, $"Reading {name}");
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IUniverseObject.Id)) != 0)
throw new();
@ -36,6 +39,7 @@ public class UniverseObjectSerializer : EngineTypeYamlSerializerBase<IUniverseOb
if (parser.Consume<Scalar>().Value.CompareTo(SERIALIZED_SCALAR_NAME) != 0)
throw new();
SerializedClass instanceSerializedClass = (SerializedClass)rootDeserializer(typeof(SerializedClass))!;
ProgressionTracker.Set(isTrackingController ? .3f : ProgressionTracker.Progression, $"Creating {instanceSerializedClass.Type}");
universeObject = (IUniverseObject)instanceSerializedClass.CreateInstance(EntityRegistry);
if (parser.Consume<Scalar>().Value.CompareTo(nameof(IUniverseObject.StateEnable)) != 0)
@ -64,6 +68,7 @@ public class UniverseObjectSerializer : EngineTypeYamlSerializerBase<IUniverseOb
foreach (IUniverseObject child in children)
universeObject.AddChild(child);
ProgressionTracker.Set(isTrackingController ? .3f : ProgressionTracker.Progression, $"Created {name}");
return universeObject;
}
@ -71,6 +76,9 @@ public class UniverseObjectSerializer : EngineTypeYamlSerializerBase<IUniverseOb
{
IUniverseObject universeObject = (IUniverseObject)value!;
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Serializing universe object");
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(IUniverseObject.Name)));
@ -91,6 +99,7 @@ public class UniverseObjectSerializer : EngineTypeYamlSerializerBase<IUniverseOb
emitter.Emit(new Scalar(nameof(IUniverseObject.Children)));
serializer(universeObject.Children.Where(c => !c.GetType().HasAttribute<IgnoreSerializationAttribute>()));
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Serializing universe object");
emitter.Emit(new MappingEnd());
}
}

View File

@ -1,8 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>false</ImplicitUsings>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Syntriax.Engine.Serializers.Yaml</RootNamespace>
</PropertyGroup>

View File

@ -0,0 +1,6 @@
using System;
namespace Syntriax.Engine.Serializers.Yaml;
[Serializable]
public class SerializerInProgressException() : Exception("There's already a running deserialization in progress.");

View File

@ -2,7 +2,9 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Syntriax.Engine.Core;
using Syntriax.Engine.Core.Serialization;
using YamlDotNet.Serialization;
@ -16,10 +18,14 @@ public class YamlSerializer : Core.Serialization.ISerializer
private readonly YamlDotNet.Serialization.IDeserializer deserializer = null!;
private readonly EntityRegistry entityRegistry = null!;
private readonly IProgressionTracker progressionTracker = null!;
private readonly System.Threading.Lock Lock = new();
public YamlSerializer()
{
entityRegistry = new();
progressionTracker = new ProgressionTracker();
SerializerBuilder serializerBuilder = new SerializerBuilder()
.WithNamingConvention(PascalCaseNamingConvention.Instance)
@ -32,6 +38,7 @@ public class YamlSerializer : Core.Serialization.ISerializer
{
typeConverter.Serializer = this;
typeConverter.EntityRegistry = entityRegistry;
typeConverter.ProgressionTracker = progressionTracker;
deserializerBuilder = deserializerBuilder.WithTypeConverter(typeConverter);
serializerBuilder = serializerBuilder.WithTypeConverter(typeConverter);
@ -49,35 +56,87 @@ public class YamlSerializer : Core.Serialization.ISerializer
public string Serialize(object instance)
{
return serializer.Serialize(instance);
lock (Lock)
{
return serializer.Serialize(instance);
}
}
public object Deserialize(string yaml)
public object Deserialize(string configuration)
{
entityRegistry.Reset();
object result = deserializer.Deserialize(yaml)!;
entityRegistry.AssignAll();
return result;
lock (Lock)
{
entityRegistry.Reset();
object result = deserializer.Deserialize(configuration)!;
entityRegistry.AssignAll();
return result;
}
}
public object Deserialize(string yaml, Type type)
public object Deserialize(string configuration, Type type)
{
entityRegistry.Reset();
object result = deserializer.Deserialize(yaml, type)!;
entityRegistry.AssignAll();
return result;
lock (Lock)
{
entityRegistry.Reset();
object result = deserializer.Deserialize(configuration, type)!;
entityRegistry.AssignAll();
return result;
}
}
public T Deserialize<T>(string yaml)
public T Deserialize<T>(string configuration)
{
entityRegistry.Reset();
T result = deserializer.Deserialize<T>(yaml);
entityRegistry.AssignAll();
return result;
lock (Lock)
{
entityRegistry.Reset();
T result = deserializer.Deserialize<T>(configuration);
entityRegistry.AssignAll();
return result;
}
}
internal object InternalDeserialize(string yaml, Type type)
public ProgressiveTask<object> DeserializeAsync(string configuration)
{
return deserializer.Deserialize(yaml, type)!;
lock (Lock)
{
progressionTracker.Reset();
Task<object> task = Task.Run(() => Deserialize(configuration));
return new ProgressiveTask<object>(progressionTracker, task);
}
}
public ProgressiveTask<object> DeserializeAsync(string configuration, Type type)
{
lock (Lock)
{
progressionTracker.Reset();
Task<object> task = Task.Run(() => Deserialize(configuration, type));
return new ProgressiveTask<object>(progressionTracker, task);
}
}
public ProgressiveTask<T> DeserializeAsync<T>(string configuration)
{
lock (Lock)
{
progressionTracker.Reset();
Task<T> task = Task.Run(() => Deserialize<T>(configuration));
return new ProgressiveTask<T>(progressionTracker, task);
}
}
public ProgressiveTask<string> SerializeAsync(object instance)
{
lock (Lock)
{
progressionTracker.Reset();
Task<string> task = Task.Run(() => Serialize(instance));
return new ProgressiveTask<string>(progressionTracker, task);
}
}
internal object InternalDeserialize(string configuration, Type type)
{
return deserializer.Deserialize(configuration, type)!;
}
}

View File

@ -1,8 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Syntriax.Engine.Systems</RootNamespace>
</PropertyGroup>

View File

@ -0,0 +1,10 @@
namespace Syntriax.Engine.Systems
{
// 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.
// I will hopefully one day fix it and remove this.
public static class Preserver
{
public static void Preserve() { }
}
}

View File

@ -1,3 +1,5 @@
using System.Collections.Generic;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.StateMachine;

View File

@ -1,3 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Syntriax.Engine.Systems.StateMachine;
public readonly record struct StateTransition(IState State, IReadOnlyList<Func<bool>> Conditions)

View File

@ -1,5 +1,7 @@
// Reference: https://easings.net
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
internal static class EaseConstants
@ -7,52 +9,54 @@ internal static class EaseConstants
internal const float c1 = 1.70158f;
internal const float c2 = c1 * 1.525f;
internal const float c3 = c1 + 1f;
internal const float c4 = 2f * Core.Math.PI / 3f;
internal const float c5 = 2f * Core.Math.PI / 4.5f;
internal const float c4 = 2f * Math.PI / 3f;
internal const float c5 = 2f * Math.PI / 4.5f;
}
public readonly struct EaseLinear : IEasing { public readonly float Evaluate(float x) => x; }
public abstract class EasingBase<T> where T : IEasing, new() { public static readonly T Instance = new(); }
public readonly struct EaseInQuad : IEasing { public readonly float Evaluate(float x) => x * x; }
public readonly struct EaseOutQuad : IEasing { public readonly float Evaluate(float x) => 1f - (1f - x) * (1f - x); }
public readonly struct EaseInOutQuad : IEasing { public readonly float Evaluate(float x) => x < .5f ? 2f * x * x : 1f - Core.Math.Pow(-2f * x + 2f, 2f) * .5f; }
public class EaseLinear : EasingBase<EaseLinear>, IEasing { public float Evaluate(float x) => x; }
public readonly struct EaseInCubic : IEasing { public readonly float Evaluate(float x) => x * x * x; }
public readonly struct EaseOutCubic : IEasing { public readonly float Evaluate(float x) => 1f - Core.Math.Pow(1f - x, 3f); }
public readonly struct EaseInOutCubic : IEasing { public readonly float Evaluate(float x) => x < .5f ? 4f * x * x * x : 1f - Core.Math.Pow(-2f * x + 2f, 3f) * .5f; }
public class EaseInQuad : EasingBase<EaseInQuad>, IEasing { public float Evaluate(float x) => x * x; }
public class EaseOutQuad : EasingBase<EaseOutQuad>, IEasing { public float Evaluate(float x) => 1f - (1f - x) * (1f - x); }
public class EaseInOutQuad : EasingBase<EaseInOutQuad>, IEasing { public float Evaluate(float x) => x < .5f ? 2f * x * x : 1f - Math.Pow(-2f * x + 2f, 2f) * .5f; }
public readonly struct EaseInQuart : IEasing { public readonly float Evaluate(float x) => x * x * x * x; }
public readonly struct EaseOutQuart : IEasing { public readonly float Evaluate(float x) => 1f - Core.Math.Pow(1f - x, 4f); }
public readonly struct EaseInOutQuart : IEasing { public readonly float Evaluate(float x) => x < .5f ? 8f * x * x * x * x : 1f - Core.Math.Pow(-2f * x + 2f, 4f) * .5f; }
public class EaseInCubic : EasingBase<EaseInCubic>, IEasing { public float Evaluate(float x) => x * x * x; }
public class EaseOutCubic : EasingBase<EaseOutCubic>, IEasing { public float Evaluate(float x) => 1f - Math.Pow(1f - x, 3f); }
public class EaseInOutCubic : EasingBase<EaseInOutCubic>, IEasing { public float Evaluate(float x) => x < .5f ? 4f * x * x * x : 1f - Math.Pow(-2f * x + 2f, 3f) * .5f; }
public readonly struct EaseInQuint : IEasing { public readonly float Evaluate(float x) => x * x * x * x * x; }
public readonly struct EaseOutQuint : IEasing { public readonly float Evaluate(float x) => 1f - Core.Math.Pow(1f - x, 5f); }
public readonly struct EaseInOutQuint : IEasing { public readonly float Evaluate(float x) => x < .5f ? 16f * x * x * x * x * x : 1f - Core.Math.Pow(-2f * x + 2f, 5f) * .5f; }
public class EaseInQuart : EasingBase<EaseInQuart>, IEasing { public float Evaluate(float x) => x * x * x * x; }
public class EaseOutQuart : EasingBase<EaseOutQuart>, IEasing { public float Evaluate(float x) => 1f - Math.Pow(1f - x, 4f); }
public class EaseInOutQuart : EasingBase<EaseInOutQuart>, IEasing { public float Evaluate(float x) => x < .5f ? 8f * x * x * x * x : 1f - Math.Pow(-2f * x + 2f, 4f) * .5f; }
public readonly struct EaseInSine : IEasing { public readonly float Evaluate(float x) => 1f - Core.Math.Cos(x * Core.Math.PI * .5f); }
public readonly struct EaseOutSine : IEasing { public readonly float Evaluate(float x) => Core.Math.Sin(x * Core.Math.PI * .5f); }
public readonly struct EaseInOutSine : IEasing { public readonly float Evaluate(float x) => -(Core.Math.Cos(Core.Math.PI * x) - 1f) * .5f; }
public class EaseInQuint : EasingBase<EaseInQuint>, IEasing { public float Evaluate(float x) => x * x * x * x * x; }
public class EaseOutQuint : EasingBase<EaseOutQuint>, IEasing { public float Evaluate(float x) => 1f - Math.Pow(1f - x, 5f); }
public class EaseInOutQuint : EasingBase<EaseInOutQuint>, IEasing { public float Evaluate(float x) => x < .5f ? 16f * x * x * x * x * x : 1f - Math.Pow(-2f * x + 2f, 5f) * .5f; }
public readonly struct EaseInExpo : IEasing { public readonly float Evaluate(float x) => x == 0f ? 0f : Core.Math.Pow(2f, 10f * x - 10f); }
public readonly struct EaseOutExpo : IEasing { public readonly float Evaluate(float x) => x == 1f ? 1f : 1f - Core.Math.Pow(2f, -10f * x); }
public readonly struct EaseInOutExpo : IEasing { public readonly float Evaluate(float x) => x == 0f ? 0f : x == 1f ? 1f : x < .5f ? Core.Math.Pow(2f, 20f * x - 10f) * .5f : (2f - Core.Math.Pow(2f, -20f * x + 10f)) * .5f; }
public class EaseInSine : EasingBase<EaseInSine>, IEasing { public float Evaluate(float x) => 1f - Math.Cos(x * Math.PI * .5f); }
public class EaseOutSine : EasingBase<EaseOutSine>, IEasing { public float Evaluate(float x) => Math.Sin(x * Math.PI * .5f); }
public class EaseInOutSine : EasingBase<EaseInOutSine>, IEasing { public float Evaluate(float x) => -(Math.Cos(Math.PI * x) - 1f) * .5f; }
public readonly struct EaseInCirc : IEasing { public readonly float Evaluate(float x) => 1f - Core.Math.Sqrt(1f - Core.Math.Pow(x, 2f)); }
public readonly struct EaseOutCirc : IEasing { public readonly float Evaluate(float x) => Core.Math.Sqrt(1f - Core.Math.Pow(x - 1f, 2f)); }
public readonly struct EaseInOutCirc : IEasing { public readonly float Evaluate(float x) => x < .5f ? (1f - Core.Math.Sqrt(1f - Core.Math.Pow(2f * x, 2f))) * .5f : (Core.Math.Sqrt(1f - Core.Math.Pow(-2f * x + 2f, 2f)) + 1f) * .5f; }
public class EaseInExpo : EasingBase<EaseInExpo>, IEasing { public float Evaluate(float x) => x == 0f ? 0f : Math.Pow(2f, 10f * x - 10f); }
public class EaseOutExpo : EasingBase<EaseOutExpo>, IEasing { public float Evaluate(float x) => x == 1f ? 1f : 1f - Math.Pow(2f, -10f * x); }
public class EaseInOutExpo : EasingBase<EaseInOutExpo>, IEasing { public float Evaluate(float x) => x == 0f ? 0f : x == 1f ? 1f : x < .5f ? Math.Pow(2f, 20f * x - 10f) * .5f : (2f - Math.Pow(2f, -20f * x + 10f)) * .5f; }
public readonly struct EaseInBack : IEasing { public readonly float Evaluate(float x) => EaseConstants.c3 * x * x * x - EaseConstants.c1 * x * x; }
public readonly struct EaseOutBack : IEasing { public readonly float Evaluate(float x) => 1f + EaseConstants.c3 * Core.Math.Pow(x - 1f, 3f) + EaseConstants.c1 * Core.Math.Pow(x - 1f, 2f); }
public readonly struct EaseInOutBack : IEasing { public readonly float Evaluate(float x) => x < .5f ? Core.Math.Pow(2f * x, 2f) * ((EaseConstants.c2 + 1f) * 2f * x - EaseConstants.c2) * .5f : (Core.Math.Pow(2f * x - 2f, 2f) * ((EaseConstants.c2 + 1f) * (x * 2f - 2f) + EaseConstants.c2) + 2f) * .5f; }
public class EaseInCirc : EasingBase<EaseInCirc>, IEasing { public float Evaluate(float x) => 1f - Math.Sqrt(1f - Math.Pow(x, 2f)); }
public class EaseOutCirc : EasingBase<EaseOutCirc>, IEasing { public float Evaluate(float x) => Math.Sqrt(1f - Math.Pow(x - 1f, 2f)); }
public class EaseInOutCirc : EasingBase<EaseInOutCirc>, IEasing { public float Evaluate(float x) => x < .5f ? (1f - Math.Sqrt(1f - Math.Pow(2f * x, 2f))) * .5f : (Math.Sqrt(1f - Math.Pow(-2f * x + 2f, 2f)) + 1f) * .5f; }
public readonly struct EaseInElastic : IEasing { public readonly float Evaluate(float x) => x == 0 ? 0 : x == 1f ? 1f : -Core.Math.Pow(2f, 10f * x - 10f) * Core.Math.Sin((x * 10f - 10.75f) * EaseConstants.c4); }
public readonly struct EaseOutElastic : IEasing { public readonly float Evaluate(float x) => x == 0 ? 0 : x == 1f ? 1f : Core.Math.Pow(2f, -10f * x) * Core.Math.Sin((x * 10f - .75f) * EaseConstants.c4) + 1f; }
public readonly struct EaseInOutElastic : IEasing { public readonly float Evaluate(float x) => x == 0 ? 0 : x == 1f ? 1f : x < .5f ? -(Core.Math.Pow(2f, 20f * x - 10f) * Core.Math.Sin((20f * x - 11.125f) * EaseConstants.c5)) * .5f : Core.Math.Pow(2f, -20f * x + 10f) * Core.Math.Sin((20f * x - 11.125f) * EaseConstants.c5) * .5f + 1f; }
public class EaseInBack : EasingBase<EaseInBack>, IEasing { public float Evaluate(float x) => EaseConstants.c3 * x * x * x - EaseConstants.c1 * x * x; }
public class EaseOutBack : EasingBase<EaseOutBack>, IEasing { public float Evaluate(float x) => 1f + EaseConstants.c3 * Math.Pow(x - 1f, 3f) + EaseConstants.c1 * Math.Pow(x - 1f, 2f); }
public class EaseInOutBack : EasingBase<EaseInOutBack>, IEasing { public float Evaluate(float x) => x < .5f ? Math.Pow(2f * x, 2f) * ((EaseConstants.c2 + 1f) * 2f * x - EaseConstants.c2) * .5f : (Math.Pow(2f * x - 2f, 2f) * ((EaseConstants.c2 + 1f) * (x * 2f - 2f) + EaseConstants.c2) + 2f) * .5f; }
public readonly struct EaseInBounce : IEasing { public readonly float Evaluate(float x) => 1f - new EaseOutBounce().Evaluate(1f - x); }
public readonly struct EaseOutBounce : IEasing
public class EaseInElastic : EasingBase<EaseInElastic>, IEasing { public float Evaluate(float x) => x == 0 ? 0 : x == 1f ? 1f : -Math.Pow(2f, 10f * x - 10f) * Math.Sin((x * 10f - 10.75f) * EaseConstants.c4); }
public class EaseOutElastic : EasingBase<EaseOutElastic>, IEasing { public float Evaluate(float x) => x == 0 ? 0 : x == 1f ? 1f : Math.Pow(2f, -10f * x) * Math.Sin((x * 10f - .75f) * EaseConstants.c4) + 1f; }
public class EaseInOutElastic : EasingBase<EaseInOutElastic>, IEasing { public float Evaluate(float x) => x == 0 ? 0 : x == 1f ? 1f : x < .5f ? -(Math.Pow(2f, 20f * x - 10f) * Math.Sin((20f * x - 11.125f) * EaseConstants.c5)) * .5f : Math.Pow(2f, -20f * x + 10f) * Math.Sin((20f * x - 11.125f) * EaseConstants.c5) * .5f + 1f; }
public class EaseInBounce : EasingBase<EaseInBounce>, IEasing { public float Evaluate(float x) => 1f - EaseOutBounce.Instance.Evaluate(1f - x); }
public class EaseOutBounce : EasingBase<EaseOutBounce>, IEasing
{
public readonly float Evaluate(float x)
public float Evaluate(float x)
{
const float n1 = 7.5625f;
const float d1 = 2.75f;
@ -69,4 +73,4 @@ public readonly struct EaseOutBounce : IEasing
return n1 * (x -= 2.625f / d1) * x + .984375f;
}
}
public readonly struct EaseInOutBounce : IEasing { public readonly float Evaluate(float x) => x < .5f ? (1f - new EaseOutBounce().Evaluate(1f - 2f * x)) * .5f : (1f + new EaseOutBounce().Evaluate(2f * x - 1f)) * .5f; }
public class EaseInOutBounce : IEasing { public float Evaluate(float x) => x < .5f ? (1f - EaseOutBounce.Instance.Evaluate(1f - 2f * x)) * .5f : (1f + EaseOutBounce.Instance.Evaluate(2f * x - 1f)) * .5f; }

View File

@ -0,0 +1,10 @@
using System;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
public static class TweenAABBExtensions
{
public static ITween TweenAABB(this AABB initialAABB, ITweenManager tweenManager, float duration, AABB targetAABB, Action<AABB> setMethod)
=> tweenManager.StartTween(duration, t => setMethod?.InvokeSafe(new AABB(initialAABB.LowerBoundary.Lerp(targetAABB.LowerBoundary, t), initialAABB.UpperBoundary.Lerp(targetAABB.UpperBoundary, t))));
}

View File

@ -0,0 +1,12 @@
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
public static class TweenCamera2DExtensions
{
public static ITween TweenZoom(this ICamera2D camera2D, ITweenManager tweenManager, float duration, float targetZoom)
{
float initialZoom = camera2D.Zoom;
return tweenManager.StartTween(duration, t => camera2D.Zoom = initialZoom.Lerp(targetZoom, t));
}
}

View File

@ -0,0 +1,16 @@
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
public static class TweenCircleExtensions
{
public static ITween TweenCircle(this Circle initialCircle, ITweenManager tweenManager, float duration, Circle targetCircle, System.Action<Circle> setMethod)
=> tweenManager.StartTween(duration,
t => setMethod?.InvokeSafe(
new Circle(
initialCircle.Center.Lerp(targetCircle.Center, t),
initialCircle.Diameter.Lerp(targetCircle.Diameter, t)
)
)
);
}

View File

@ -0,0 +1,15 @@
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
public static class TweenColorExtensions
{
public static ITween TweenColor(this ColorRGB initialColorRGB, ITweenManager tweenManager, float duration, ColorRGB targetColorRGB, System.Action<ColorRGB> setMethod)
=> tweenManager.StartTween(duration, t => setMethod?.InvokeSafe(initialColorRGB.Lerp(targetColorRGB, t)));
public static ITween TweenColor(this ColorRGBA initialColorRGBA, ITweenManager tweenManager, float duration, ColorRGBA targetColorRGBA, System.Action<ColorRGBA> setMethod)
=> tweenManager.StartTween(duration, t => setMethod?.InvokeSafe(initialColorRGBA.Lerp(targetColorRGBA, t)));
public static ITween TweenColor(this ColorHSV initialColorHSV, ITweenManager tweenManager, float duration, ColorHSV targetColorHSV, System.Action<ColorHSV> setMethod)
=> tweenManager.StartTween(duration, t => setMethod?.InvokeSafe(initialColorHSV.Lerp(targetColorHSV, t)));
}

View File

@ -0,0 +1,16 @@
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
public static class TweenLine2DEquationExtensions
{
public static ITween TweenLine2DEquation(this Line2DEquation initialLine2DEquation, ITweenManager tweenManager, float duration, Line2DEquation targetLine2DEquation, System.Action<Line2DEquation> setMethod)
=> tweenManager.StartTween(duration,
t => setMethod?.InvokeSafe(
new Line2DEquation(
initialLine2DEquation.Slope.Lerp(targetLine2DEquation.Slope, t),
initialLine2DEquation.OffsetY.Lerp(targetLine2DEquation.OffsetY, t)
)
)
);
}

View File

@ -0,0 +1,16 @@
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
public static class TweenLine2DExtensions
{
public static ITween TweenLine2D(this Line2D initialLine2D, ITweenManager tweenManager, float duration, Line2D targetLine2D, System.Action<Line2D> setMethod)
=> tweenManager.StartTween(duration,
t => setMethod?.InvokeSafe(
new Line2D(
initialLine2D.From.Lerp(targetLine2D.From, t),
initialLine2D.To.Lerp(targetLine2D.To, t)
)
)
);
}

View File

@ -0,0 +1,17 @@
using System;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
public static class TweenProjection1DExtensions
{
public static ITween TweenProjection1D(this Projection1D initialProjection1D, ITweenManager tweenManager, float duration, Projection1D targetProjection1D, Action<Projection1D> setMethod)
=> tweenManager.StartTween(duration,
t => setMethod?.InvokeSafe(
new Projection1D(
initialProjection1D.Min.Lerp(targetProjection1D.Min, t),
initialProjection1D.Max.Lerp(targetProjection1D.Max, t)
)
)
);
}

View File

@ -0,0 +1,10 @@
using System;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
public static class TweenQuaternionExtensions
{
public static ITween TweenQuaternion(this Quaternion initialQuaternion, ITweenManager tweenManager, float duration, Quaternion targetQuaternion, Action<Quaternion> setMethod)
=> tweenManager.StartTween(duration, t => setMethod?.InvokeSafe(initialQuaternion.SLerp(targetQuaternion, t)));
}

View File

@ -0,0 +1,34 @@
using System.Collections.Generic;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
public static class TweenShape2DExtensions
{
public static ITween TweenShape2D(this Shape2D shape, ITweenManager tweenManager, float duration, Shape2D targetShape2D)
{
List<Vector2D> initialVertices = new(shape.Vertices);
List<Vector2D> shapeVertices = new(shape.Vertices);
return tweenManager.StartTween(duration,
t =>
{
shapeVertices.Clear();
int maxCount = initialVertices.Count.Max(targetShape2D.Vertices.Count);
for (int i = 0; i < maxCount; i++)
{
int initialIndex = (i * (initialVertices.Count / (float)maxCount)).RoundToInt(Math.RoundMode.Floor);
int targetIndex = (i * (targetShape2D.Vertices.Count / (float)maxCount)).RoundToInt(Math.RoundMode.Floor);
shapeVertices.Add(targetShape2D.Vertices[targetIndex].Lerp(initialVertices[initialIndex], 1f - t));
}
shape.Vertices = shapeVertices;
}
).OnComplete(() =>
{
shapeVertices.Clear();
for (int i = 0; i < targetShape2D.Vertices.Count; i++)
shapeVertices.Add(targetShape2D.Vertices[i]);
shape.Vertices = shapeVertices;
});
}
}

View File

@ -0,0 +1,42 @@
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
public static class TweenTransform2DExtensions
{
public static ITween TweenPosition(this ITransform2D transform2D, ITweenManager tweenManager, float duration, Vector2D targetPosition)
{
Vector2D initialPosition = transform2D.Position;
return tweenManager.StartTween(duration, t => transform2D.Position = initialPosition.Lerp(targetPosition, t));
}
public static ITween TweenScale(this ITransform2D transform2D, ITweenManager tweenManager, float duration, Vector2D targetScale)
{
Vector2D initialScale = transform2D.Scale;
return tweenManager.StartTween(duration, t => transform2D.Scale = initialScale.Lerp(targetScale, t));
}
public static ITween TweenRotation(this ITransform2D transform2D, ITweenManager tweenManager, float duration, float targetRotation)
{
float initialRotation = transform2D.Rotation;
return tweenManager.StartTween(duration, t => transform2D.Rotation = initialRotation.Lerp(targetRotation, t));
}
public static ITween TweenLocalPosition(this ITransform2D transform2D, ITweenManager tweenManager, float duration, Vector2D targetLocalPosition)
{
Vector2D initialLocalPosition = transform2D.LocalPosition;
return tweenManager.StartTween(duration, t => transform2D.LocalPosition = initialLocalPosition.Lerp(targetLocalPosition, t));
}
public static ITween TweenLocalScale(this ITransform2D transform2D, ITweenManager tweenManager, float duration, Vector2D targetLocalScale)
{
Vector2D initialLocalScale = transform2D.LocalScale;
return tweenManager.StartTween(duration, t => transform2D.LocalScale = initialLocalScale.Lerp(targetLocalScale, t));
}
public static ITween TweenLocalRotation(this ITransform2D transform2D, ITweenManager tweenManager, float duration, float targetLocalRotation)
{
float initialLocalRotation = transform2D.LocalRotation;
return tweenManager.StartTween(duration, t => transform2D.LocalRotation = initialLocalRotation.Lerp(targetLocalRotation, t));
}
}

View File

@ -0,0 +1,18 @@
using System;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
public static class TweenTriangleExtensions
{
public static ITween TweenTriangle(this Triangle initialTriangle, ITweenManager tweenManager, float duration, Triangle targetTriangle, Action<Triangle> setMethod)
=> tweenManager.StartTween(duration,
t => setMethod?.InvokeSafe(
new Triangle(
initialTriangle.A.Lerp(targetTriangle.A, t),
initialTriangle.B.Lerp(targetTriangle.B, t),
initialTriangle.C.Lerp(targetTriangle.C, t)
)
)
);
}

View File

@ -0,0 +1,9 @@
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
public static class TweenVector2DExtensions
{
public static ITween TweenVector2D(this Vector2D initialVector2D, ITweenManager tweenManager, float duration, Vector2D targetVector2D, System.Action<Vector2D> setMethod)
=> tweenManager.StartTween(duration, t => setMethod?.InvokeSafe(initialVector2D.Lerp(targetVector2D, t)));
}

View File

@ -0,0 +1,9 @@
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
public static class TweenVector3DExtensions
{
public static ITween TweenVector3D(this Vector3D initialVector3D, ITweenManager tweenManager, float duration, Vector3D targetVector3D, System.Action<Vector3D> setMethod)
=> tweenManager.StartTween(duration, t => setMethod?.InvokeSafe(initialVector3D.Lerp(targetVector3D, t)));
}

View File

@ -0,0 +1,9 @@
namespace Syntriax.Engine.Systems.Tween;
public interface ITweenManager
{
ITween StartTween(float duration, TweenSetCallback? setCallback = null);
void CancelTween(ITween tween);
delegate void TweenSetCallback(float t);
}

View File

@ -43,7 +43,7 @@ internal class Tween : ITween
public float Progress { get; internal set; } = 0f;
private float _counter = 0f;
public IEasing Easing { get; set; } = new EaseLinear();
public IEasing Easing { get; set; } = EaseLinear.Instance;
public float Value => Easing.Evaluate(Progress);
public float Counter

View File

@ -1,3 +1,5 @@
using System;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
@ -47,42 +49,49 @@ public static class TweenExtensions
tweenConcrete.OnStarted += _ => callback.InvokeSafe();
return tween;
}
public static ITween OnPause(this ITween tween, Action callback)
{
Tween tweenConcrete = (Tween)tween;
tweenConcrete.OnPaused += _ => callback.InvokeSafe();
return tween;
}
public static ITween OnResume(this ITween tween, Action callback)
{
Tween tweenConcrete = (Tween)tween;
tweenConcrete.OnResumed += _ => callback.InvokeSafe();
return tween;
}
public static ITween OnCancel(this ITween tween, Action callback)
{
Tween tweenConcrete = (Tween)tween;
tweenConcrete.OnCancelled += _ => callback.InvokeSafe();
return tween;
}
public static ITween OnComplete(this ITween tween, Action callback)
{
Tween tweenConcrete = (Tween)tween;
tweenConcrete.OnCompleted += _ => callback.InvokeSafe();
return tween;
}
public static ITween OnEnd(this ITween tween, Action callback)
{
Tween tweenConcrete = (Tween)tween;
tweenConcrete.OnEnded += _ => callback.InvokeSafe();
return tween;
}
public static ITween OnUpdate(this ITween tween, Action callback)
{
Tween tweenConcrete = (Tween)tween;
tweenConcrete.OnUpdated += _ => callback.InvokeSafe();
return tween;
}
public static ITween OnDeltaUpdate(this ITween tween, Action<float> callback)
{
Tween tweenConcrete = (Tween)tween;

View File

@ -1,16 +1,17 @@
using System.Collections;
using System.Collections.Generic;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Systems.Tween;
public class TweenManager : UniverseObject
public class TweenManager : UniverseObject, ITweenManager
{
private CoroutineManager coroutineManager = null!;
private readonly Dictionary<ITween, IEnumerator> runningCoroutines = [];
public ITween StartTween(float duration, TweenSetCallback? setCallback = null)
public ITween StartTween(float duration, ITweenManager.TweenSetCallback? setCallback = null)
{
Tween tween = new(duration);
tween.OnUpdated += tween => setCallback?.InvokeSafe(tween.Value);
@ -60,6 +61,4 @@ public class TweenManager : UniverseObject
{
coroutineManager = null!;
}
public delegate void TweenSetCallback(float t);
}

View File

@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Syntriax.Engine</RootNamespace>
</PropertyGroup>
<ItemGroup>

15
Engine/Preserver.cs Normal file
View File

@ -0,0 +1,15 @@
namespace Syntriax.Engine
{
// 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.
// I will hopefully one day fix it and remove this.
public static class Preserver
{
public static void Preserve()
{
Core.Preserver.Preserve();
Physics2D.Preserver.Preserve();
Systems.Preserver.Preserve();
}
}
}