Version 2.1.1 #10

Merged
That-One-Nerd merged 3 commits from v2.1 into main 2022-04-17 17:58:34 -04:00
34 changed files with 2809 additions and 3064 deletions

View File

@ -1,84 +1,12 @@
# Nerd_STF v2.1.0 # Nerd_STF v2.1.1
This update adds some operations from calculus, as well as many new geometry structures. This update doesn't add any new features, simply a code simplification, using some of the .net 6 tools, such as global usings and file-scoped namespace declarations.
``` ```
* Nerd_STF * Nerd_STF
+ Exceptions = Removed unused or unrequired usings in all files.
+ Nerd_STFException = Replaced all namespace declarations with file-scoped declarations.
+ DifferingVertCountException * Miscellaneous
+ DisconnectedLinesException * GlobalUsings
+ Miscellaneous + global using System.Diagnostics.CodeAnalysis
+ `GlobalUsings.cs`
+ IClosest<T>
+ IContainer<T>
* Logger
* DefaultLogHandler(LogMessage)
= Replaced a `throw new Exception` with a `throw new ArgumentException`
* Mathematics
+ Angle
+ Calculus
+ delegate double Equation(double)
* Double2
= Made `CompareTo(Double2)` better
* Double3
= Made `CompareTo(Double3)` better
* Double4
= Made `CompareTo(Double4)` better
* Int2
+ operator &(Int2, Int2)
+ operator |(Int2, Int2)
+ operator ^(Int2, Int2)
= Made `CompareTo(Int2)` better
* Int3
+ operator &(Int3, Int3)
+ operator |(Int3, Int3)
+ operator ^(Int3, Int3)
= Made `CompareTo(Int3)` better
* Int4
+ operator &(Int4, Int4)
+ operator |(Int4, Int4)
+ operator ^(Int4, Int4)
= Made `CompareTo(Int4)` better
* Mathf
+ Average(Equation, double, double, double)
+ GetValues(Equation)
+ MakeEquation(Dictionary<double, double>)
+ Max(Equation, double, double, double)
+ Min(Equation, double, double, double)
= Swapped the names of "RadToDeg" and "DegToRad"
* Geometry
+ Box2D
+ Box3D
+ Polygon
+ Quadrilateral
+ Sphere
+ ISubdividable
* ITriangulatable
+ Triangle[] TriangulateAll(params ITriangulatable[])
* Line
+ : IComparable<Line>
+ : IContainer<Vert>
+ : IClosest<Vert>
+ : ISubdividable<Line[]>
+ ClosestTo(Vert)
+ ClosestTo(Vert, double)
+ CompareTo(Line)
+ Contains(Vert)
+ Subdivide()
+ operator -(Line)
+ operator >(Line)
+ operator <(Line)
+ operator >=(Line)
+ operator <=(Line)
= Renamed all instances of "start" to "a"
= Renamed all instances of "end" to "b"
* Triangle
+ operator -(Triangle)
+ ToDoubleArrayAll(params Triangle[])
= Replaced the variable assignings in the Triangle to not re-assign the lines.
= Now uses custom exception in line constructor
= Renamed "L1" to "AB"
= Renamed "L2" to "BC"
= Renamed "L3" to "CA"
``` ```

View File

@ -1,30 +1,28 @@
using System.Runtime.Serialization; using System.Runtime.Serialization;
namespace Nerd_STF.Exceptions namespace Nerd_STF.Exceptions;
[Serializable]
public class DifferingVertCountException : Nerd_STFException
{ {
public string? ParamName;
public Polygon[]? Polygons;
[Serializable] public DifferingVertCountException() : base("Not all polygons have the same vert count.") { }
public class DifferingVertCountException : Nerd_STFException public DifferingVertCountException(Exception inner) : base("Not all polygons have the same vert count.", inner) { }
public DifferingVertCountException(string paramName) : this() => ParamName = paramName;
public DifferingVertCountException(string paramName, Exception inner) : this(inner) => ParamName = paramName;
public DifferingVertCountException(params Polygon[] polys) : this() => Polygons = polys;
public DifferingVertCountException(Polygon[] polys, Exception inner) : this(inner) => Polygons = polys;
public DifferingVertCountException(string paramName, Polygon[] polys) : this()
{ {
public string? ParamName; ParamName = paramName;
public Polygon[]? Polygons; Polygons = polys;
public DifferingVertCountException() : base("Not all polygons have the same vert count.") { }
public DifferingVertCountException(Exception inner) : base("Not all polygons have the same vert count.", inner) { }
public DifferingVertCountException(string paramName) : this() => ParamName = paramName;
public DifferingVertCountException(string paramName, Exception inner) : this(inner) => ParamName = paramName;
public DifferingVertCountException(params Polygon[] polys) : this() => Polygons = polys;
public DifferingVertCountException(Polygon[] polys, Exception inner) : this(inner) => Polygons = polys;
public DifferingVertCountException(string paramName, Polygon[] polys) : this()
{
ParamName = paramName;
Polygons = polys;
}
public DifferingVertCountException(string paramName, Polygon[] polys, Exception inner) : this(inner)
{
ParamName = paramName;
Polygons = polys;
}
protected DifferingVertCountException(SerializationInfo info, StreamingContext context) : base(info, context) { }
} }
public DifferingVertCountException(string paramName, Polygon[] polys, Exception inner) : this(inner)
{
ParamName = paramName;
Polygons = polys;
}
protected DifferingVertCountException(SerializationInfo info, StreamingContext context) : base(info, context) { }
} }

View File

@ -1,35 +1,28 @@
using Nerd_STF.Mathematics.Geometry; using System.Runtime.Serialization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
namespace Nerd_STF.Exceptions namespace Nerd_STF.Exceptions;
[Serializable]
public class DisconnectedLinesException : Nerd_STFException
{ {
[Serializable] public string? ParamName;
public class DisconnectedLinesException : Nerd_STFException public Line[]? Lines;
{
public string? ParamName;
public Line[]? Lines;
public DisconnectedLinesException() : base("Lines are not connected.") { } public DisconnectedLinesException() : base("Lines are not connected.") { }
public DisconnectedLinesException(Exception inner) : base("Lines are not connected.", inner) { } public DisconnectedLinesException(Exception inner) : base("Lines are not connected.", inner) { }
public DisconnectedLinesException(string paramName) : this() => ParamName = paramName; public DisconnectedLinesException(string paramName) : this() => ParamName = paramName;
public DisconnectedLinesException(string paramName, Exception inner) : this(inner) => ParamName = paramName; public DisconnectedLinesException(string paramName, Exception inner) : this(inner) => ParamName = paramName;
public DisconnectedLinesException(params Line[] lines) : this() => Lines = lines; public DisconnectedLinesException(params Line[] lines) : this() => Lines = lines;
public DisconnectedLinesException(Line[] lines, Exception inner) : this(inner) => Lines = lines; public DisconnectedLinesException(Line[] lines, Exception inner) : this(inner) => Lines = lines;
public DisconnectedLinesException(string paramName, Line[] lines) : this() public DisconnectedLinesException(string paramName, Line[] lines) : this()
{ {
ParamName = paramName; ParamName = paramName;
Lines = lines; Lines = lines;
} }
public DisconnectedLinesException(string paramName, Line[] lines, Exception inner) : this(inner) public DisconnectedLinesException(string paramName, Line[] lines, Exception inner) : this(inner)
{ {
ParamName = paramName; ParamName = paramName;
Lines = lines; Lines = lines;
} }
protected DisconnectedLinesException(SerializationInfo info, StreamingContext context) : base(info, context) { } protected DisconnectedLinesException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}
} }

View File

@ -1,18 +1,12 @@
using System; using System.Runtime.Serialization;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
namespace Nerd_STF.Exceptions namespace Nerd_STF.Exceptions;
[Serializable]
public class Nerd_STFException : Exception
{ {
[Serializable] public Nerd_STFException() { }
public class Nerd_STFException : Exception public Nerd_STFException(string message) : base(message) { }
{ public Nerd_STFException(string message, Exception inner) : base(message, inner) { }
public Nerd_STFException() { } protected Nerd_STFException(SerializationInfo info, StreamingContext context) : base(info, context) { }
public Nerd_STFException(string message) : base(message) { }
public Nerd_STFException(string message, Exception inner) : base(message, inner) { }
protected Nerd_STFException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}
} }

View File

@ -1,4 +1,3 @@
namespace Nerd_STF namespace Nerd_STF;
{
public delegate T Fill<T>(int index); public delegate T Fill<T>(int index);
}

View File

@ -1,13 +1,6 @@
using System; namespace Nerd_STF;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Nerd_STF public interface IClosest<T> where T : IEquatable<T>
{ {
public interface IClosest<T> where T : IEquatable<T> public T ClosestTo(T item);
{
public T ClosestTo(T item);
}
} }

View File

@ -1,13 +1,6 @@
using System; namespace Nerd_STF;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Nerd_STF public interface IContainer<T> where T : IEquatable<T>
{ {
public interface IContainer<T> where T : IEquatable<T> public bool Contains(T item);
{
public bool Contains(T item);
}
} }

View File

@ -1,13 +1,6 @@
using System; namespace Nerd_STF;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Nerd_STF public interface IEncapsulator<T, TE> : IContainer<TE> where T : IEquatable<T> where TE : IEquatable<TE>
{ {
public interface IEncapsulator<T, TE> : IContainer<TE> where T : IEquatable<T> where TE : IEquatable<TE> public T Encapsulate(TE val);
{
public T Encapsulate(TE val);
}
} }

View File

@ -1,8 +1,7 @@
namespace Nerd_STF namespace Nerd_STF;
public interface IGroup<T> : IEnumerable<T>
{ {
public interface IGroup<T> : IEnumerable<T> public T[] ToArray();
{ public List<T> ToList();
public T[] ToArray();
public List<T> ToList();
}
} }

View File

@ -1,25 +1,18 @@
using System; namespace Nerd_STF;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Nerd_STF public struct LogMessage
{ {
public struct LogMessage public string Message;
public LogSeverity Severity;
public DateTime Timestamp;
public LogMessage() : this("", LogSeverity.Information, null) { }
public LogMessage(string msg, LogSeverity severity, DateTime? time = null)
{ {
public string Message; Message = msg;
public LogSeverity Severity; Severity = severity;
public DateTime Timestamp; Timestamp = time ?? DateTime.Now;
public LogMessage() : this("", LogSeverity.Information, null) { }
public LogMessage(string msg, LogSeverity severity, DateTime? time = null)
{
Message = msg;
Severity = severity;
Timestamp = time ?? DateTime.Now;
}
public override string ToString() => Timestamp + " " + Severity.ToString().ToUpper() + ": " + Message;
} }
public override string ToString() => Timestamp + " " + Severity.ToString().ToUpper() + ": " + Message;
} }

View File

@ -1,11 +1,10 @@
namespace Nerd_STF namespace Nerd_STF;
public enum LogSeverity
{ {
public enum LogSeverity Debug = 1,
{ Information = 2,
Debug = 1, Warning = 4,
Information = 2, Error = 8,
Warning = 4, Fatal = 16,
Error = 8,
Fatal = 16,
}
} }

View File

@ -1,72 +1,71 @@
using System.Text; using System.Text;
namespace Nerd_STF namespace Nerd_STF;
public class Logger
{ {
public class Logger public event Action<LogMessage> OnMessageRecieved = DefaultLogHandler;
public LogMessage[] Cache => msgs.ToArray();
public int CacheSize = 64;
public List<LogSeverity> IncludeSeverities = new()
{ {
public event Action<LogMessage> OnMessageRecieved = DefaultLogHandler; LogSeverity.Warning,
LogSeverity.Error,
LogSeverity.Fatal,
};
public Stream? LogStream;
public int WriteSize;
public LogMessage[] Cache => msgs.ToArray(); private readonly List<LogMessage> msgs;
public int CacheSize = 64; private readonly List<string> writeCache;
public List<LogSeverity> IncludeSeverities = new()
public Logger(Stream? logStream = null, int cacheSize = 64, int writeSize = 1)
{
CacheSize = cacheSize;
LogStream = logStream;
WriteSize = writeSize;
msgs = new(CacheSize);
writeCache = new();
}
public void Send(LogMessage msg)
{
if (!IncludeSeverities.Contains(msg.Severity)) return;
msgs.Insert(0, msg);
writeCache.Add(msg.ToString());
while (msgs.Count > CacheSize) msgs.RemoveAt(CacheSize);
OnMessageRecieved(msg);
if (writeCache.Count >= WriteSize && LogStream != null)
{ {
LogSeverity.Warning, string s = "";
LogSeverity.Error, foreach (string cache in writeCache) s += cache + "\n" + (cache.Contains('\n') ? "\n" : "");
LogSeverity.Fatal, LogStream.Write(Encoding.Default.GetBytes(s));
}; LogStream.Flush();
public Stream? LogStream; writeCache.Clear();
public int WriteSize;
private readonly List<LogMessage> msgs;
private readonly List<string> writeCache;
public Logger(Stream? logStream = null, int cacheSize = 64, int writeSize = 1)
{
CacheSize = cacheSize;
LogStream = logStream;
WriteSize = writeSize;
msgs = new(CacheSize);
writeCache = new();
}
public void Send(LogMessage msg)
{
if (!IncludeSeverities.Contains(msg.Severity)) return;
msgs.Insert(0, msg);
writeCache.Add(msg.ToString());
while (msgs.Count > CacheSize) msgs.RemoveAt(CacheSize);
OnMessageRecieved(msg);
if (writeCache.Count >= WriteSize && LogStream != null)
{
string s = "";
foreach (string cache in writeCache) s += cache + "\n" + (cache.Contains('\n') ? "\n" : "");
LogStream.Write(Encoding.Default.GetBytes(s));
LogStream.Flush();
writeCache.Clear();
}
}
public static void DefaultLogHandler(LogMessage msg)
{
ConsoleColor color = msg.Severity switch
{
LogSeverity.Debug => ConsoleColor.DarkGray,
LogSeverity.Information => ConsoleColor.White,
LogSeverity.Warning => ConsoleColor.DarkYellow,
LogSeverity.Error => ConsoleColor.Red,
LogSeverity.Fatal => ConsoleColor.DarkRed,
_ => throw new ArgumentException("Unknown log severity " + msg.Severity, nameof(msg)),
};
ConsoleColor originalCol = Console.ForegroundColor;
Console.ForegroundColor = color;
Console.WriteLine(msg.ToString());
Console.ForegroundColor = originalCol;
} }
} }
public static void DefaultLogHandler(LogMessage msg)
{
ConsoleColor color = msg.Severity switch
{
LogSeverity.Debug => ConsoleColor.DarkGray,
LogSeverity.Information => ConsoleColor.White,
LogSeverity.Warning => ConsoleColor.DarkYellow,
LogSeverity.Error => ConsoleColor.Red,
LogSeverity.Fatal => ConsoleColor.DarkRed,
_ => throw new ArgumentException("Unknown log severity " + msg.Severity, nameof(msg)),
};
ConsoleColor originalCol = Console.ForegroundColor;
Console.ForegroundColor = color;
Console.WriteLine(msg.ToString());
Console.ForegroundColor = originalCol;
}
} }

View File

@ -1,124 +1,116 @@
using System; namespace Nerd_STF.Mathematics;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Nerd_STF.Mathematics public struct Angle : ICloneable, IComparable<Angle>, IEquatable<Angle>
{ {
public struct Angle : ICloneable, IComparable<Angle>, IEquatable<Angle> public static Angle Full => new(360);
{ public static Angle Half => new(180);
public static Angle Full => new(360); public static Angle One => new(1);
public static Angle Half => new(180); public static Angle Quarter => new(90);
public static Angle One => new(1); public static Angle Zero => new(0);
public static Angle Quarter => new(90);
public static Angle Zero => new(0);
public double Degrees public double Degrees
{ {
get => p_deg; get => p_deg;
set => p_deg = value; set => p_deg = value;
} }
public double Gradians public double Gradians
{ {
get => p_deg * 1.11111111111; // Reciprocal of 9/10 as a constant (10/9) get => p_deg * 1.11111111111; // Reciprocal of 9/10 as a constant (10/9)
set => p_deg = value * 0.9; set => p_deg = value * 0.9;
} }
public double Radians public double Radians
{ {
get => p_deg * Mathf.DegToRad; get => p_deg * Mathf.DegToRad;
set => p_deg = value * Mathf.RadToDeg; set => p_deg = value * Mathf.RadToDeg;
} }
public Angle Bounded => new(p_deg % 360); public Angle Bounded => new(p_deg % 360);
private double p_deg; private double p_deg;
public Angle(double value, Type valueType = Type.Degrees) public Angle(double value, Type valueType = Type.Degrees)
{
p_deg = valueType switch
{ {
p_deg = valueType switch Type.Degrees => value,
Type.Gradians => value * 0.9,
Type.Radians => value * Mathf.RadToDeg,
_ => throw new ArgumentException("Unknown type.", nameof(valueType)),
};
}
public static Angle Absolute(Angle val) => new(Mathf.Absolute(val.p_deg));
public static Angle Average(params Angle[] vals) => new(Mathf.Average(ToDoubles(Type.Degrees, vals)));
public static Angle Ceiling(Angle val) => new(Mathf.Ceiling(val.p_deg));
public static Angle Clamp(Angle val, Angle min, Angle max) => new(Mathf.Clamp(val.p_deg, min.p_deg, max.p_deg));
public static Angle Floor(Angle val) => new(Mathf.Ceiling(val.p_deg));
public static Angle Lerp(Angle a, Angle b, double t, bool clamp = true) =>
new(Mathf.Lerp(a.p_deg, b.p_deg, t, clamp));
public static Angle Max(params Angle[] vals) => new(Mathf.Max(ToDoubles(Type.Degrees, vals)));
public static Angle Median(params Angle[] vals) => new(Mathf.Median(ToDoubles(Type.Degrees, vals)));
public static Angle Min(params Angle[] vals) => new(Mathf.Min(ToDoubles(Type.Degrees, vals)));
public static double[] ToDoubles(Type outputType, params Angle[] vals)
{
double[] res = new double[vals.Length];
for (int i = 0; i < vals.Length; i++)
{
res[i] = outputType switch
{ {
Type.Degrees => value, Type.Degrees => vals[i].Degrees,
Type.Gradians => value * 0.9, Type.Gradians => vals[i].Gradians,
Type.Radians => value * Mathf.RadToDeg, Type.Radians => vals[i].Radians,
_ => throw new ArgumentException("Unknown type.", nameof(valueType)), _ => throw new ArgumentException("Unknown type.", nameof(outputType)),
}; };
} }
return res;
}
public static Angle Absolute(Angle val) => new(Mathf.Absolute(val.p_deg)); public int CompareTo(Angle other) => p_deg.CompareTo(other.p_deg);
public static Angle Average(params Angle[] vals) => new(Mathf.Average(ToDoubles(Type.Degrees, vals))); public override bool Equals([NotNullWhen(true)] object? obj)
public static Angle Ceiling(Angle val) => new(Mathf.Ceiling(val.p_deg)); {
public static Angle Clamp(Angle val, Angle min, Angle max) => new(Mathf.Clamp(val.p_deg, min.p_deg, max.p_deg)); if (obj == null || obj.GetType() != typeof(Angle)) return false;
public static Angle Floor(Angle val) => new(Mathf.Ceiling(val.p_deg)); return Equals((Angle)obj);
public static Angle Lerp(Angle a, Angle b, double t, bool clamp = true) => }
new(Mathf.Lerp(a.p_deg, b.p_deg, t, clamp)); public bool Equals(Angle other) => p_deg == other.p_deg;
public static Angle Max(params Angle[] vals) => new(Mathf.Max(ToDoubles(Type.Degrees, vals))); public override int GetHashCode() => Degrees.GetHashCode() ^ Gradians.GetHashCode() ^ Radians.GetHashCode();
public static Angle Median(params Angle[] vals) => new(Mathf.Median(ToDoubles(Type.Degrees, vals))); public override string ToString() => ToString((string?)null);
public static Angle Min(params Angle[] vals) => new(Mathf.Min(ToDoubles(Type.Degrees, vals))); public string ToString(Type outputType) => ToString((string?)null, outputType);
public string ToString(string? provider, Type outputType = Type.Degrees) => outputType switch
{
Type.Degrees => p_deg.ToString(provider),
Type.Gradians => Gradians.ToString(provider),
Type.Radians => Radians.ToString(provider),
_ => throw new ArgumentException("Unknown type.", nameof(outputType)),
};
public string ToString(IFormatProvider provider, Type outputType = Type.Degrees) => outputType switch
{
Type.Degrees => p_deg.ToString(provider),
Type.Gradians => Gradians.ToString(provider),
Type.Radians => Radians.ToString(provider),
_ => throw new ArgumentException("Unknown type.", nameof(outputType)),
};
public static double[] ToDoubles(Type outputType, params Angle[] vals) public object Clone() => new Angle(p_deg);
{
double[] res = new double[vals.Length];
for (int i = 0; i < vals.Length; i++)
{
res[i] = outputType switch
{
Type.Degrees => vals[i].Degrees,
Type.Gradians => vals[i].Gradians,
Type.Radians => vals[i].Radians,
_ => throw new ArgumentException("Unknown type.", nameof(outputType)),
};
}
return res;
}
public int CompareTo(Angle other) => p_deg.CompareTo(other.p_deg); public static Angle operator +(Angle a, Angle b) => new(a.p_deg + b.p_deg);
public override bool Equals([NotNullWhen(true)] object? obj) public static Angle operator -(Angle a) => new(-a.p_deg);
{ public static Angle operator -(Angle a, Angle b) => new(a.p_deg - b.p_deg);
if (obj == null || obj.GetType() != typeof(Angle)) return false; public static Angle operator *(Angle a, Angle b) => new(a.p_deg * b.p_deg);
return Equals((Angle)obj); public static Angle operator *(Angle a, double b) => new(a.p_deg * b);
} public static Angle operator /(Angle a, Angle b) => new(a.p_deg / b.p_deg);
public bool Equals(Angle other) => p_deg == other.p_deg; public static Angle operator /(Angle a, double b) => new(a.p_deg / b);
public override int GetHashCode() => Degrees.GetHashCode() ^ Gradians.GetHashCode() ^ Radians.GetHashCode(); public static bool operator ==(Angle a, Angle b) => a.Equals(b);
public override string ToString() => ToString((string?)null); public static bool operator !=(Angle a, Angle b) => !a.Equals(b);
public string ToString(Type outputType) => ToString((string?)null, outputType); public static bool operator >(Angle a, Angle b) => a.CompareTo(b) > 0;
public string ToString(string? provider, Type outputType = Type.Degrees) => outputType switch public static bool operator <(Angle a, Angle b) => a.CompareTo(b) < 0;
{ public static bool operator >=(Angle a, Angle b) => a == b || a > b;
Type.Degrees => p_deg.ToString(provider), public static bool operator <=(Angle a, Angle b) => a == b || a < b;
Type.Gradians => Gradians.ToString(provider),
Type.Radians => Radians.ToString(provider),
_ => throw new ArgumentException("Unknown type.", nameof(outputType)),
};
public string ToString(IFormatProvider provider, Type outputType = Type.Degrees) => outputType switch
{
Type.Degrees => p_deg.ToString(provider),
Type.Gradians => Gradians.ToString(provider),
Type.Radians => Radians.ToString(provider),
_ => throw new ArgumentException("Unknown type.", nameof(outputType)),
};
public object Clone() => new Angle(p_deg); public enum Type
{
public static Angle operator +(Angle a, Angle b) => new(a.p_deg + b.p_deg); Degrees,
public static Angle operator -(Angle a) => new(-a.p_deg); Gradians,
public static Angle operator -(Angle a, Angle b) => new(a.p_deg - b.p_deg); Radians,
public static Angle operator *(Angle a, Angle b) => new(a.p_deg * b.p_deg);
public static Angle operator *(Angle a, double b) => new(a.p_deg * b);
public static Angle operator /(Angle a, Angle b) => new(a.p_deg / b.p_deg);
public static Angle operator /(Angle a, double b) => new(a.p_deg / b);
public static bool operator ==(Angle a, Angle b) => a.Equals(b);
public static bool operator !=(Angle a, Angle b) => !a.Equals(b);
public static bool operator >(Angle a, Angle b) => a.CompareTo(b) > 0;
public static bool operator <(Angle a, Angle b) => a.CompareTo(b) < 0;
public static bool operator >=(Angle a, Angle b) => a == b || a > b;
public static bool operator <=(Angle a, Angle b) => a == b || a < b;
public enum Type
{
Degrees,
Gradians,
Radians,
}
} }
} }

View File

@ -1,41 +1,34 @@
using System; namespace Nerd_STF.Mathematics;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Nerd_STF.Mathematics public static class Calculus
{ {
public static class Calculus public const double DefaultStep = 0.001;
public static Equation GetDerivative(Equation equ, double min, double max, double step = DefaultStep)
{ {
public const double DefaultStep = 0.001; Dictionary<double, double> vals = new();
for (double x = min; x <= max; x += step)
public static Equation GetDerivative(Equation equ, double min, double max, double step = DefaultStep)
{ {
Dictionary<double, double> vals = new(); double val1 = equ(x), val2 = equ(x + step), change = (val2 - val1) / step;
for (double x = min; x <= max; x += step) vals.Add(x, change);
{
double val1 = equ(x), val2 = equ(x + step), change = (val2 - val1) / step;
vals.Add(x, change);
}
return Mathf.MakeEquation(vals);
} }
public static double GetDerivativeAtPoint(Equation equ, double x, double step = DefaultStep) => return Mathf.MakeEquation(vals);
(equ(x + DefaultStep) - equ(x)) / step; }
public static double GetDerivativeAtPoint(Equation equ, double x, double step = DefaultStep) =>
(equ(x + DefaultStep) - equ(x)) / step;
public static double GetIntegral(Equation equ, double lowerBound, double upperBound, double step = DefaultStep) public static double GetIntegral(Equation equ, double lowerBound, double upperBound, double step = DefaultStep)
{ {
double val = 0; double val = 0;
for (double x = lowerBound; x <= upperBound; x += step) val += equ(x) * step; for (double x = lowerBound; x <= upperBound; x += step) val += equ(x) * step;
return val; return val;
} }
public static double GradientDescent(Equation equ, double initial, double rate, double stepCount = 1000, public static double GradientDescent(Equation equ, double initial, double rate, double stepCount = 1000,
double step = DefaultStep) double step = DefaultStep)
{ {
double val = initial; double val = initial;
for (int i = 0; i < stepCount; i++) val -= GetDerivativeAtPoint(equ, val, step) * rate; for (int i = 0; i < stepCount; i++) val -= GetDerivativeAtPoint(equ, val, step) * rate;
return val; return val;
}
} }
} }

View File

@ -1,186 +1,181 @@
using Nerd_STF.Mathematics.Geometry; namespace Nerd_STF.Mathematics;
using System.Collections;
using System.Diagnostics.CodeAnalysis;
namespace Nerd_STF.Mathematics public struct Double2 : ICloneable, IComparable<Double2>, IEquatable<Double2>, IGroup<double>
{ {
public struct Double2 : ICloneable, IComparable<Double2>, IEquatable<Double2>, IGroup<double> public static Double2 Down => new(0, -1);
public static Double2 Left => new(-1, 0);
public static Double2 Right => new(1, 0);
public static Double2 Up => new(0, 1);
public static Double2 One => new(1, 1);
public static Double2 Zero => new(0, 0);
public double Magnitude => Mathf.Sqrt(x * x + y * y);
public Double2 Normalized => this / Magnitude;
public double x, y;
public Double2(double all) : this(all, all) { }
public Double2(double x, double y)
{ {
public static Double2 Down => new(0, -1); this.x = x;
public static Double2 Left => new(-1, 0); this.y = y;
public static Double2 Right => new(1, 0);
public static Double2 Up => new(0, 1);
public static Double2 One => new(1, 1);
public static Double2 Zero => new(0, 0);
public double Magnitude => Mathf.Sqrt(x * x + y * y);
public Double2 Normalized => this / Magnitude;
public double x, y;
public Double2(double all) : this(all, all) { }
public Double2(double x, double y)
{
this.x = x;
this.y = y;
}
public Double2(Fill<double> fill) : this(fill(0), fill(1)) { }
public Double2(Fill<int> fill) : this(fill(0), fill(1)) { }
public double this[int index]
{
get => index switch
{
0 => x,
1 => y,
_ => throw new IndexOutOfRangeException(nameof(index)),
};
set
{
switch (index)
{
case 0:
x = value;
break;
case 1:
y = value;
break;
default: throw new IndexOutOfRangeException(nameof(index));
}
}
}
public static Double2 Absolute(Double2 val) =>
new(Mathf.Absolute(val.x), Mathf.Absolute(val.y));
public static Double2 Average(params Double2[] vals) => Sum(vals) / vals.Length;
public static Double2 Ceiling(Double2 val) =>
new(Mathf.Ceiling(val.x), Mathf.Ceiling(val.y));
public static Double2 Clamp(Double2 val, Double2 min, Double2 max) =>
new(Mathf.Clamp(val.x, min.x, max.x),
Mathf.Clamp(val.y, min.y, max.y));
public static Double2 ClampMagnitude(Double2 val, double minMag, double maxMag)
{
if (maxMag < minMag) throw new ArgumentOutOfRangeException(nameof(maxMag),
nameof(maxMag) + " must be greater than or equal to " + nameof(minMag));
double mag = val.Magnitude;
if (mag >= minMag && mag <= maxMag) return val;
val = val.Normalized;
if (mag < minMag) val *= minMag;
else if (mag > maxMag) val *= maxMag;
return val;
}
public static Double3 Cross(Double2 a, Double2 b, bool normalized = false) =>
Double3.Cross(a, b, normalized);
public static Double2 Divide(Double2 num, params Double2[] vals)
{
foreach (Double2 d in vals) num /= d;
return num;
}
public static double Dot(Double2 a, Double2 b) => a.x * b.x + a.y * b.y;
public static double Dot(params Double2[] vals)
{
if (vals.Length < 1) return 0;
double x = 1, y = 1;
foreach (Double2 d in vals)
{
x *= d.x;
y *= d.y;
}
return x + y;
}
public static Double2 Floor(Double2 val) =>
new(Mathf.Floor(val.x), Mathf.Floor(val.y));
public static Double2 Lerp(Double2 a, Double2 b, double t, bool clamp = true) =>
new(Mathf.Lerp(a.x, b.x, t, clamp), Mathf.Lerp(a.y, b.y, t, clamp));
public static Double2 Median(params Double2[] vals)
{
double index = Mathf.Average(0, vals.Length - 1);
Double2 valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)];
return Average(valA, valB);
}
public static Double2 Max(params Double2[] vals)
{
if (vals.Length < 1) return Zero;
Double2 val = vals[0];
foreach (Double2 d in vals) val = d > val ? d : val;
return val;
}
public static Double2 Min(params Double2[] vals)
{
if (vals.Length < 1) return Zero;
Double2 val = vals[0];
foreach (Double2 d in vals) val = d < val ? d : val;
return val;
}
public static Double2 Multiply(params Double2[] vals)
{
if (vals.Length < 1) return Zero;
Double2 val = One;
foreach (Double2 d in vals) val *= d;
return val;
}
public static Double2 Subtract(Double2 num, params Double2[] vals)
{
foreach (Double2 d in vals) num -= d;
return num;
}
public static Double2 Sum(params Double2[] vals)
{
Double2 val = Zero;
foreach (Double2 d in vals) val += d;
return val;
}
public int CompareTo(Double2 other) => Magnitude.CompareTo(other.Magnitude);
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null || obj.GetType() != typeof(Double2)) return false;
return Equals((Double2)obj);
}
public bool Equals(Double2 other) => x == other.x && y == other.y;
public override int GetHashCode() => x.GetHashCode() ^ y.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider);
public string ToString(IFormatProvider provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider);
public object Clone() => new Double2(x, y);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<double> GetEnumerator()
{
yield return x;
yield return y;
}
public double[] ToArray() => new[] { x, y };
public List<double> ToList() => new() { x, y };
public static Double2 operator +(Double2 a, Double2 b) => new(a.x + b.x, a.y + b.y);
public static Double2 operator -(Double2 d) => new(-d.x, -d.y);
public static Double2 operator -(Double2 a, Double2 b) => new(a.x - b.x, a.y - b.y);
public static Double2 operator *(Double2 a, Double2 b) => new(a.x * b.x, a.y * b.y);
public static Double2 operator *(Double2 a, double b) => new(a.x * b, a.y * b);
public static Double2 operator /(Double2 a, Double2 b) => new(a.x / b.x, a.y / b.y);
public static Double2 operator /(Double2 a, double b) => new(a.x / b, a.y / b);
public static bool operator ==(Double2 a, Double2 b) => a.Equals(b);
public static bool operator !=(Double2 a, Double2 b) => !a.Equals(b);
public static bool operator >(Double2 a, Double2 b) => a.CompareTo(b) > 0;
public static bool operator <(Double2 a, Double2 b) => a.CompareTo(b) < 0;
public static bool operator >=(Double2 a, Double2 b) => a == b || a > b;
public static bool operator <=(Double2 a, Double2 b) => a == b || a < b;
public static explicit operator Double2(Double3 val) => new(val.x, val.y);
public static explicit operator Double2(Double4 val) => new(val.x, val.y);
public static implicit operator Double2(Int2 val) => new(val.x, val.y);
public static explicit operator Double2(Int3 val) => new(val.x, val.y);
public static explicit operator Double2(Int4 val) => new(val.x, val.y);
public static explicit operator Double2(Vert val) => new(val.position.x, val.position.y);
public static implicit operator Double2(Fill<double> fill) => new(fill);
public static implicit operator Double2(Fill<int> fill) => new(fill);
} }
public Double2(Fill<double> fill) : this(fill(0), fill(1)) { }
public Double2(Fill<int> fill) : this(fill(0), fill(1)) { }
public double this[int index]
{
get => index switch
{
0 => x,
1 => y,
_ => throw new IndexOutOfRangeException(nameof(index)),
};
set
{
switch (index)
{
case 0:
x = value;
break;
case 1:
y = value;
break;
default: throw new IndexOutOfRangeException(nameof(index));
}
}
}
public static Double2 Absolute(Double2 val) =>
new(Mathf.Absolute(val.x), Mathf.Absolute(val.y));
public static Double2 Average(params Double2[] vals) => Sum(vals) / vals.Length;
public static Double2 Ceiling(Double2 val) =>
new(Mathf.Ceiling(val.x), Mathf.Ceiling(val.y));
public static Double2 Clamp(Double2 val, Double2 min, Double2 max) =>
new(Mathf.Clamp(val.x, min.x, max.x),
Mathf.Clamp(val.y, min.y, max.y));
public static Double2 ClampMagnitude(Double2 val, double minMag, double maxMag)
{
if (maxMag < minMag) throw new ArgumentOutOfRangeException(nameof(maxMag),
nameof(maxMag) + " must be greater than or equal to " + nameof(minMag));
double mag = val.Magnitude;
if (mag >= minMag && mag <= maxMag) return val;
val = val.Normalized;
if (mag < minMag) val *= minMag;
else if (mag > maxMag) val *= maxMag;
return val;
}
public static Double3 Cross(Double2 a, Double2 b, bool normalized = false) =>
Double3.Cross(a, b, normalized);
public static Double2 Divide(Double2 num, params Double2[] vals)
{
foreach (Double2 d in vals) num /= d;
return num;
}
public static double Dot(Double2 a, Double2 b) => a.x * b.x + a.y * b.y;
public static double Dot(params Double2[] vals)
{
if (vals.Length < 1) return 0;
double x = 1, y = 1;
foreach (Double2 d in vals)
{
x *= d.x;
y *= d.y;
}
return x + y;
}
public static Double2 Floor(Double2 val) =>
new(Mathf.Floor(val.x), Mathf.Floor(val.y));
public static Double2 Lerp(Double2 a, Double2 b, double t, bool clamp = true) =>
new(Mathf.Lerp(a.x, b.x, t, clamp), Mathf.Lerp(a.y, b.y, t, clamp));
public static Double2 Median(params Double2[] vals)
{
double index = Mathf.Average(0, vals.Length - 1);
Double2 valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)];
return Average(valA, valB);
}
public static Double2 Max(params Double2[] vals)
{
if (vals.Length < 1) return Zero;
Double2 val = vals[0];
foreach (Double2 d in vals) val = d > val ? d : val;
return val;
}
public static Double2 Min(params Double2[] vals)
{
if (vals.Length < 1) return Zero;
Double2 val = vals[0];
foreach (Double2 d in vals) val = d < val ? d : val;
return val;
}
public static Double2 Multiply(params Double2[] vals)
{
if (vals.Length < 1) return Zero;
Double2 val = One;
foreach (Double2 d in vals) val *= d;
return val;
}
public static Double2 Subtract(Double2 num, params Double2[] vals)
{
foreach (Double2 d in vals) num -= d;
return num;
}
public static Double2 Sum(params Double2[] vals)
{
Double2 val = Zero;
foreach (Double2 d in vals) val += d;
return val;
}
public int CompareTo(Double2 other) => Magnitude.CompareTo(other.Magnitude);
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null || obj.GetType() != typeof(Double2)) return false;
return Equals((Double2)obj);
}
public bool Equals(Double2 other) => x == other.x && y == other.y;
public override int GetHashCode() => x.GetHashCode() ^ y.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider);
public string ToString(IFormatProvider provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider);
public object Clone() => new Double2(x, y);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<double> GetEnumerator()
{
yield return x;
yield return y;
}
public double[] ToArray() => new[] { x, y };
public List<double> ToList() => new() { x, y };
public static Double2 operator +(Double2 a, Double2 b) => new(a.x + b.x, a.y + b.y);
public static Double2 operator -(Double2 d) => new(-d.x, -d.y);
public static Double2 operator -(Double2 a, Double2 b) => new(a.x - b.x, a.y - b.y);
public static Double2 operator *(Double2 a, Double2 b) => new(a.x * b.x, a.y * b.y);
public static Double2 operator *(Double2 a, double b) => new(a.x * b, a.y * b);
public static Double2 operator /(Double2 a, Double2 b) => new(a.x / b.x, a.y / b.y);
public static Double2 operator /(Double2 a, double b) => new(a.x / b, a.y / b);
public static bool operator ==(Double2 a, Double2 b) => a.Equals(b);
public static bool operator !=(Double2 a, Double2 b) => !a.Equals(b);
public static bool operator >(Double2 a, Double2 b) => a.CompareTo(b) > 0;
public static bool operator <(Double2 a, Double2 b) => a.CompareTo(b) < 0;
public static bool operator >=(Double2 a, Double2 b) => a == b || a > b;
public static bool operator <=(Double2 a, Double2 b) => a == b || a < b;
public static explicit operator Double2(Double3 val) => new(val.x, val.y);
public static explicit operator Double2(Double4 val) => new(val.x, val.y);
public static implicit operator Double2(Int2 val) => new(val.x, val.y);
public static explicit operator Double2(Int3 val) => new(val.x, val.y);
public static explicit operator Double2(Int4 val) => new(val.x, val.y);
public static explicit operator Double2(Vert val) => new(val.position.x, val.position.y);
public static implicit operator Double2(Fill<double> fill) => new(fill);
public static implicit operator Double2(Fill<int> fill) => new(fill);
} }

View File

@ -1,207 +1,202 @@
using Nerd_STF.Mathematics.Geometry; namespace Nerd_STF.Mathematics;
using System.Collections;
using System.Diagnostics.CodeAnalysis;
namespace Nerd_STF.Mathematics public struct Double3 : ICloneable, IComparable<Double3>, IEquatable<Double3>, IGroup<double>
{ {
public struct Double3 : ICloneable, IComparable<Double3>, IEquatable<Double3>, IGroup<double> public static Double3 Back => new(0, 0, -1);
public static Double3 Down => new(0, -1, 0);
public static Double3 Forward => new(0, 0, 1);
public static Double3 Left => new(-1, 0, 0);
public static Double3 Right => new(1, 0, 0);
public static Double3 Up => new(0, 1, 0);
public static Double3 One => new(1, 1, 1);
public static Double3 Zero => new(0, 0, 0);
public double Magnitude => Mathf.Sqrt(x * x + y * y + z * z);
public Double3 Normalized => this / Magnitude;
public Double2 XY => new(x, y);
public Double2 XZ => new(x, z);
public Double2 YZ => new(y, z);
public double x, y, z;
public Double3(double all) : this(all, all, all) { }
public Double3(double x, double y) : this(x, y, 0) { }
public Double3(double x, double y, double z)
{ {
public static Double3 Back => new(0, 0, -1); this.x = x;
public static Double3 Down => new(0, -1, 0); this.y = y;
public static Double3 Forward => new(0, 0, 1); this.z = z;
public static Double3 Left => new(-1, 0, 0);
public static Double3 Right => new(1, 0, 0);
public static Double3 Up => new(0, 1, 0);
public static Double3 One => new(1, 1, 1);
public static Double3 Zero => new(0, 0, 0);
public double Magnitude => Mathf.Sqrt(x * x + y * y + z * z);
public Double3 Normalized => this / Magnitude;
public Double2 XY => new(x, y);
public Double2 XZ => new(x, z);
public Double2 YZ => new(y, z);
public double x, y, z;
public Double3(double all) : this(all, all, all) { }
public Double3(double x, double y) : this(x, y, 0) { }
public Double3(double x, double y, double z)
{
this.x = x;
this.y = y;
this.z = z;
}
public Double3(Fill<double> fill) : this(fill(0), fill(1), fill(2)) { }
public Double3(Fill<int> fill) : this(fill(0), fill(1), fill(2)) { }
public double this[int index]
{
get => index switch
{
0 => x,
1 => y,
2 => z,
_ => throw new IndexOutOfRangeException(nameof(index)),
};
set
{
switch (index)
{
case 0:
x = value;
break;
case 1:
y = value;
break;
case 2:
z = value;
break;
default: throw new IndexOutOfRangeException(nameof(index));
}
}
}
public static Double3 Absolute(Double3 val) =>
new(Mathf.Absolute(val.x), Mathf.Absolute(val.y), Mathf.Absolute(val.z));
public static Double3 Average(params Double3[] vals) => Sum(vals) / vals.Length;
public static Double3 Ceiling(Double3 val) =>
new(Mathf.Ceiling(val.x), Mathf.Ceiling(val.y), Mathf.Ceiling(val.z));
public static Double3 Clamp(Double3 val, Double3 min, Double3 max) =>
new(Mathf.Clamp(val.x, min.x, max.x),
Mathf.Clamp(val.y, min.y, max.y),
Mathf.Clamp(val.z, min.z, max.z));
public static Double3 ClampMagnitude(Double3 val, double minMag, double maxMag)
{
if (maxMag < minMag) throw new ArgumentOutOfRangeException(nameof(maxMag),
nameof(maxMag) + " must be greater than or equal to " + nameof(minMag));
double mag = val.Magnitude;
if (mag >= minMag && mag <= maxMag) return val;
val = val.Normalized;
if (mag < minMag) val *= minMag;
else if (mag > maxMag) val *= maxMag;
return val;
}
public static Double3 Cross(Double3 a, Double3 b, bool normalized = false)
{
Double3 val = new(a.y * b.z - b.y * a.z,
b.x * a.z - a.x * b.z,
a.x * b.y - b.x * a.y);
return normalized ? val.Normalized : val;
}
public static Double3 Divide(Double3 num, params Double3[] vals)
{
foreach (Double3 d in vals) num /= d;
return num;
}
public static double Dot(Double3 a, Double3 b) => a.x * b.x + a.y * b.y + a.z * b.z;
public static double Dot(params Double3[] vals)
{
if (vals.Length < 1) return 0;
double x = 1, y = 1, z = 1;
foreach (Double3 d in vals)
{
x *= d.x;
y *= d.y;
z *= d.z;
}
return x + y + z;
}
public static Double3 Floor(Double3 val) =>
new(Mathf.Floor(val.x), Mathf.Floor(val.y), Mathf.Floor(val.z));
public static Double3 Lerp(Double3 a, Double3 b, double t, bool clamp = true) =>
new(Mathf.Lerp(a.x, b.x, t, clamp), Mathf.Lerp(a.y, b.y, t, clamp), Mathf.Lerp(a.z, b.z, t, clamp));
public static Double3 Median(params Double3[] vals)
{
double index = Mathf.Average(0, vals.Length - 1);
Double3 valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)];
return Average(valA, valB);
}
public static Double3 Max(params Double3[] vals)
{
if (vals.Length < 1) return Zero;
Double3 val = vals[0];
foreach (Double3 d in vals) val = d > val ? d : val;
return val;
}
public static Double3 Min(params Double3[] vals)
{
if (vals.Length < 1) return Zero;
Double3 val = vals[0];
foreach (Double3 d in vals) val = d < val ? d : val;
return val;
}
public static Double3 Multiply(params Double3[] vals)
{
if (vals.Length < 1) return Zero;
Double3 val = One;
foreach (Double3 d in vals) val *= d;
return val;
}
public static Double3 Subtract(Double3 num, params Double3[] vals)
{
foreach (Double3 d in vals) num -= d;
return num;
}
public static Double3 Sum(params Double3[] vals)
{
Double3 val = Zero;
foreach (Double3 d in vals) val += d;
return val;
}
public int CompareTo(Double3 other) => Magnitude.CompareTo(other.Magnitude);
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null || obj.GetType() != typeof(Double3)) return false;
return Equals((Double3)obj);
}
public bool Equals(Double3 other) => x == other.x && y == other.y && z == other.z;
public override int GetHashCode() => x.GetHashCode() ^ y.GetHashCode() ^ z.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider) + " Z: " + z.ToString(provider);
public string ToString(IFormatProvider provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider) + " Z: " + z.ToString(provider);
public object Clone() => new Double3(x, y, z);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<double> GetEnumerator()
{
yield return x;
yield return y;
yield return z;
}
public double[] ToArray() => new[] { x, y, z };
public List<double> ToList() => new() { x, y, z };
public static Double3 operator +(Double3 a, Double3 b) => new(a.x + b.x, a.y + b.y, a.z + b.z);
public static Double3 operator -(Double3 d) => new(-d.x, -d.y, -d.z);
public static Double3 operator -(Double3 a, Double3 b) => new(a.x - b.x, a.y - b.y, a.z - b.z);
public static Double3 operator *(Double3 a, Double3 b) => new(a.x * b.x, a.y * b.y, a.z * b.z);
public static Double3 operator *(Double3 a, double b) => new(a.x * b, a.y * b, a.z * b);
public static Double3 operator /(Double3 a, Double3 b) => new(a.x / b.x, a.y / b.y, a.z / b.z);
public static Double3 operator /(Double3 a, double b) => new(a.x / b, a.y / b, a.z / b);
public static bool operator ==(Double3 a, Double3 b) => a.Equals(b);
public static bool operator !=(Double3 a, Double3 b) => !a.Equals(b);
public static bool operator >(Double3 a, Double3 b) => a.CompareTo(b) > 0;
public static bool operator <(Double3 a, Double3 b) => a.CompareTo(b) < 0;
public static bool operator >=(Double3 a, Double3 b) => a == b || a > b;
public static bool operator <=(Double3 a, Double3 b) => a == b || a < b;
public static implicit operator Double3(Double2 val) => new(val.x, val.y, 0);
public static explicit operator Double3(Double4 val) => new(val.x, val.y, val.z);
public static implicit operator Double3(Int2 val) => new(val.x, val.y, 0);
public static implicit operator Double3(Int3 val) => new(val.x, val.y, val.z);
public static explicit operator Double3(Int4 val) => new(val.x, val.y, val.z);
public static implicit operator Double3(Vert val) => new(val.position.x, val.position.y, val.position.z);
public static implicit operator Double3(Fill<double> fill) => new(fill);
public static implicit operator Double3(Fill<int> fill) => new(fill);
} }
public Double3(Fill<double> fill) : this(fill(0), fill(1), fill(2)) { }
public Double3(Fill<int> fill) : this(fill(0), fill(1), fill(2)) { }
public double this[int index]
{
get => index switch
{
0 => x,
1 => y,
2 => z,
_ => throw new IndexOutOfRangeException(nameof(index)),
};
set
{
switch (index)
{
case 0:
x = value;
break;
case 1:
y = value;
break;
case 2:
z = value;
break;
default: throw new IndexOutOfRangeException(nameof(index));
}
}
}
public static Double3 Absolute(Double3 val) =>
new(Mathf.Absolute(val.x), Mathf.Absolute(val.y), Mathf.Absolute(val.z));
public static Double3 Average(params Double3[] vals) => Sum(vals) / vals.Length;
public static Double3 Ceiling(Double3 val) =>
new(Mathf.Ceiling(val.x), Mathf.Ceiling(val.y), Mathf.Ceiling(val.z));
public static Double3 Clamp(Double3 val, Double3 min, Double3 max) =>
new(Mathf.Clamp(val.x, min.x, max.x),
Mathf.Clamp(val.y, min.y, max.y),
Mathf.Clamp(val.z, min.z, max.z));
public static Double3 ClampMagnitude(Double3 val, double minMag, double maxMag)
{
if (maxMag < minMag) throw new ArgumentOutOfRangeException(nameof(maxMag),
nameof(maxMag) + " must be greater than or equal to " + nameof(minMag));
double mag = val.Magnitude;
if (mag >= minMag && mag <= maxMag) return val;
val = val.Normalized;
if (mag < minMag) val *= minMag;
else if (mag > maxMag) val *= maxMag;
return val;
}
public static Double3 Cross(Double3 a, Double3 b, bool normalized = false)
{
Double3 val = new(a.y * b.z - b.y * a.z,
b.x * a.z - a.x * b.z,
a.x * b.y - b.x * a.y);
return normalized ? val.Normalized : val;
}
public static Double3 Divide(Double3 num, params Double3[] vals)
{
foreach (Double3 d in vals) num /= d;
return num;
}
public static double Dot(Double3 a, Double3 b) => a.x * b.x + a.y * b.y + a.z * b.z;
public static double Dot(params Double3[] vals)
{
if (vals.Length < 1) return 0;
double x = 1, y = 1, z = 1;
foreach (Double3 d in vals)
{
x *= d.x;
y *= d.y;
z *= d.z;
}
return x + y + z;
}
public static Double3 Floor(Double3 val) =>
new(Mathf.Floor(val.x), Mathf.Floor(val.y), Mathf.Floor(val.z));
public static Double3 Lerp(Double3 a, Double3 b, double t, bool clamp = true) =>
new(Mathf.Lerp(a.x, b.x, t, clamp), Mathf.Lerp(a.y, b.y, t, clamp), Mathf.Lerp(a.z, b.z, t, clamp));
public static Double3 Median(params Double3[] vals)
{
double index = Mathf.Average(0, vals.Length - 1);
Double3 valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)];
return Average(valA, valB);
}
public static Double3 Max(params Double3[] vals)
{
if (vals.Length < 1) return Zero;
Double3 val = vals[0];
foreach (Double3 d in vals) val = d > val ? d : val;
return val;
}
public static Double3 Min(params Double3[] vals)
{
if (vals.Length < 1) return Zero;
Double3 val = vals[0];
foreach (Double3 d in vals) val = d < val ? d : val;
return val;
}
public static Double3 Multiply(params Double3[] vals)
{
if (vals.Length < 1) return Zero;
Double3 val = One;
foreach (Double3 d in vals) val *= d;
return val;
}
public static Double3 Subtract(Double3 num, params Double3[] vals)
{
foreach (Double3 d in vals) num -= d;
return num;
}
public static Double3 Sum(params Double3[] vals)
{
Double3 val = Zero;
foreach (Double3 d in vals) val += d;
return val;
}
public int CompareTo(Double3 other) => Magnitude.CompareTo(other.Magnitude);
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null || obj.GetType() != typeof(Double3)) return false;
return Equals((Double3)obj);
}
public bool Equals(Double3 other) => x == other.x && y == other.y && z == other.z;
public override int GetHashCode() => x.GetHashCode() ^ y.GetHashCode() ^ z.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider) + " Z: " + z.ToString(provider);
public string ToString(IFormatProvider provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider) + " Z: " + z.ToString(provider);
public object Clone() => new Double3(x, y, z);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<double> GetEnumerator()
{
yield return x;
yield return y;
yield return z;
}
public double[] ToArray() => new[] { x, y, z };
public List<double> ToList() => new() { x, y, z };
public static Double3 operator +(Double3 a, Double3 b) => new(a.x + b.x, a.y + b.y, a.z + b.z);
public static Double3 operator -(Double3 d) => new(-d.x, -d.y, -d.z);
public static Double3 operator -(Double3 a, Double3 b) => new(a.x - b.x, a.y - b.y, a.z - b.z);
public static Double3 operator *(Double3 a, Double3 b) => new(a.x * b.x, a.y * b.y, a.z * b.z);
public static Double3 operator *(Double3 a, double b) => new(a.x * b, a.y * b, a.z * b);
public static Double3 operator /(Double3 a, Double3 b) => new(a.x / b.x, a.y / b.y, a.z / b.z);
public static Double3 operator /(Double3 a, double b) => new(a.x / b, a.y / b, a.z / b);
public static bool operator ==(Double3 a, Double3 b) => a.Equals(b);
public static bool operator !=(Double3 a, Double3 b) => !a.Equals(b);
public static bool operator >(Double3 a, Double3 b) => a.CompareTo(b) > 0;
public static bool operator <(Double3 a, Double3 b) => a.CompareTo(b) < 0;
public static bool operator >=(Double3 a, Double3 b) => a == b || a > b;
public static bool operator <=(Double3 a, Double3 b) => a == b || a < b;
public static implicit operator Double3(Double2 val) => new(val.x, val.y, 0);
public static explicit operator Double3(Double4 val) => new(val.x, val.y, val.z);
public static implicit operator Double3(Int2 val) => new(val.x, val.y, 0);
public static implicit operator Double3(Int3 val) => new(val.x, val.y, val.z);
public static explicit operator Double3(Int4 val) => new(val.x, val.y, val.z);
public static implicit operator Double3(Vert val) => new(val.position.x, val.position.y, val.position.z);
public static implicit operator Double3(Fill<double> fill) => new(fill);
public static implicit operator Double3(Fill<int> fill) => new(fill);
} }

View File

@ -1,223 +1,218 @@
using Nerd_STF.Mathematics.Geometry; namespace Nerd_STF.Mathematics;
using System.Collections;
using System.Diagnostics.CodeAnalysis;
namespace Nerd_STF.Mathematics public struct Double4 : ICloneable, IComparable<Double4>, IEquatable<Double4>, IGroup<double>
{ {
public struct Double4 : ICloneable, IComparable<Double4>, IEquatable<Double4>, IGroup<double> public static Double4 Back => new(0, 0, -1, 0);
public static Double4 Deep => new(0, 0, 0, -1);
public static Double4 Down => new(0, -1, 0, 0);
public static Double4 Far => new(0, 0, 0, 1);
public static Double4 Forward => new(0, 0, 1, 0);
public static Double4 Left => new(-1, 0, 0, 0);
public static Double4 Right => new(1, 0, 0, 0);
public static Double4 Up => new(0, 1, 0, 0);
public static Double4 One => new(1, 1, 1, 1);
public static Double4 Zero => new(0, 0, 0, 0);
public double Magnitude => Mathf.Sqrt(x * x + y * y + z * z + w * w);
public Double4 Normalized => this / Magnitude;
public Double2 XY => new(x, y);
public Double2 XZ => new(x, z);
public Double2 XW => new(x, w);
public Double2 YW => new(y, w);
public Double2 YZ => new(y, z);
public Double2 ZW => new(z, w);
public Double3 XYW => new(x, y, w);
public Double3 XYZ => new(x, y, z);
public Double3 YZW => new(y, z, w);
public Double3 XZW => new(x, z, w);
public double x, y, z, w;
public Double4(double all) : this(all, all, all, all) { }
public Double4(double x, double y) : this(x, y, 0, 0) { }
public Double4(double x, double y, double z) : this(x, y, z, 0) { }
public Double4(double x, double y, double z, double w)
{ {
public static Double4 Back => new(0, 0, -1, 0); this.x = x;
public static Double4 Deep => new(0, 0, 0, -1); this.y = y;
public static Double4 Down => new(0, -1, 0, 0); this.z = z;
public static Double4 Far => new(0, 0, 0, 1); this.w = w;
public static Double4 Forward => new(0, 0, 1, 0);
public static Double4 Left => new(-1, 0, 0, 0);
public static Double4 Right => new(1, 0, 0, 0);
public static Double4 Up => new(0, 1, 0, 0);
public static Double4 One => new(1, 1, 1, 1);
public static Double4 Zero => new(0, 0, 0, 0);
public double Magnitude => Mathf.Sqrt(x * x + y * y + z * z + w * w);
public Double4 Normalized => this / Magnitude;
public Double2 XY => new(x, y);
public Double2 XZ => new(x, z);
public Double2 XW => new(x, w);
public Double2 YW => new(y, w);
public Double2 YZ => new(y, z);
public Double2 ZW => new(z, w);
public Double3 XYW => new(x, y, w);
public Double3 XYZ => new(x, y, z);
public Double3 YZW => new(y, z, w);
public Double3 XZW => new(x, z, w);
public double x, y, z, w;
public Double4(double all) : this(all, all, all, all) { }
public Double4(double x, double y) : this(x, y, 0, 0) { }
public Double4(double x, double y, double z) : this(x, y, z, 0) { }
public Double4(double x, double y, double z, double w)
{
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
public Double4(Fill<double> fill) : this(fill(0), fill(1), fill(2), fill(3)) { }
public Double4(Fill<int> fill) : this(fill(0), fill(1), fill(2), fill(3)) { }
public double this[int index]
{
get => index switch
{
0 => x,
1 => y,
2 => z,
3 => w,
_ => throw new IndexOutOfRangeException(nameof(index)),
};
set
{
switch (index)
{
case 0:
x = value;
break;
case 1:
y = value;
break;
case 2:
z = value;
break;
case 3:
w = value;
break;
default: throw new IndexOutOfRangeException(nameof(index));
}
}
}
public static Double4 Absolute(Double4 val) =>
new(Mathf.Absolute(val.x), Mathf.Absolute(val.y), Mathf.Absolute(val.z), Mathf.Absolute(val.w));
public static Double4 Average(params Double4[] vals) => Sum(vals) / vals.Length;
public static Double4 Ceiling(Double4 val) =>
new(Mathf.Ceiling(val.x), Mathf.Ceiling(val.y), Mathf.Ceiling(val.z), Mathf.Ceiling(val.w));
public static Double4 Clamp(Double4 val, Double4 min, Double4 max) =>
new(Mathf.Clamp(val.x, min.x, max.x),
Mathf.Clamp(val.y, min.y, max.y),
Mathf.Clamp(val.z, min.z, max.z),
Mathf.Clamp(val.w, min.w, max.w));
public static Double4 ClampMagnitude(Double4 val, double minMag, double maxMag)
{
if (maxMag < minMag) throw new ArgumentOutOfRangeException(nameof(maxMag),
nameof(maxMag) + " must be greater than or equal to " + nameof(minMag));
double mag = val.Magnitude;
if (mag >= minMag && mag <= maxMag) return val;
val = val.Normalized;
if (mag < minMag) val *= minMag;
else if (mag > maxMag) val *= maxMag;
return val;
}
public static Double4 Divide(Double4 num, params Double4[] vals)
{
foreach (Double4 d in vals) num /= d;
return num;
}
public static double Dot(Double4 a, Double4 b) => a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
public static double Dot(params Double4[] vals)
{
if (vals.Length < 1) return 0;
double x = 1, y = 1, z = 1, w = 1;
foreach (Double4 d in vals)
{
x *= d.x;
y *= d.y;
z *= d.z;
w *= d.w;
}
return x + y + z;
}
public static Double4 Floor(Double4 val) =>
new(Mathf.Floor(val.x), Mathf.Floor(val.y), Mathf.Floor(val.z), Mathf.Floor(val.w));
public static Double4 Lerp(Double4 a, Double4 b, double t, bool clamp = true) =>
new(Mathf.Lerp(a.x, b.x, t, clamp), Mathf.Lerp(a.y, b.y, t, clamp), Mathf.Lerp(a.z, b.z, t, clamp),
Mathf.Lerp(a.w, b.w, t, clamp));
public static Double4 Median(params Double4[] vals)
{
double index = Mathf.Average(0, vals.Length - 1);
Double4 valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)];
return Average(valA, valB);
}
public static Double4 Max(params Double4[] vals)
{
if (vals.Length < 1) return Zero;
Double4 val = vals[0];
foreach (Double4 d in vals) val = d > val ? d : val;
return val;
}
public static Double4 Min(params Double4[] vals)
{
if (vals.Length < 1) return Zero;
Double4 val = vals[0];
foreach (Double4 d in vals) val = d < val ? d : val;
return val;
}
public static Double4 Multiply(params Double4[] vals)
{
if (vals.Length < 1) return Zero;
Double4 val = One;
foreach (Double4 d in vals) val *= d;
return val;
}
public static Double4 Subtract(Double4 num, params Double4[] vals)
{
foreach (Double4 d in vals) num -= d;
return num;
}
public static Double4 Sum(params Double4[] vals)
{
Double4 val = Zero;
foreach (Double4 d in vals) val += d;
return val;
}
public int CompareTo(Double4 other) => Magnitude.CompareTo(other.Magnitude);
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null || obj.GetType() != typeof(Double4)) return false;
return Equals((Double4)obj);
}
public bool Equals(Double4 other) => x == other.x && y == other.y && z == other.z && w == other.w;
public override int GetHashCode() => x.GetHashCode() ^ y.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider) + " Z: " + z.ToString(provider)
+ " W: " + w.ToString(provider);
public string ToString(IFormatProvider provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider) + " Z: " + z.ToString(provider)
+ " W: " + w.ToString(provider);
public object Clone() => new Double4(x, y, z, w);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<double> GetEnumerator()
{
yield return x;
yield return y;
yield return z;
yield return w;
}
public double[] ToArray() => new[] { x, y, z, w };
public List<double> ToList() => new() { x, y, z, w };
public static Double4 operator +(Double4 a, Double4 b) => new(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
public static Double4 operator -(Double4 d) => new(-d.x, -d.y, -d.z, -d.w);
public static Double4 operator -(Double4 a, Double4 b) => new(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
public static Double4 operator *(Double4 a, Double4 b) => new(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w);
public static Double4 operator *(Double4 a, double b) => new(a.x * b, a.y * b, a.z * b, a.w * b);
public static Double4 operator /(Double4 a, Double4 b) => new(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w);
public static Double4 operator /(Double4 a, double b) => new(a.x / b, a.y / b, a.z / b, a.w / b);
public static bool operator ==(Double4 a, Double4 b) => a.Equals(b);
public static bool operator !=(Double4 a, Double4 b) => !a.Equals(b);
public static bool operator >(Double4 a, Double4 b) => a.CompareTo(b) > 0;
public static bool operator <(Double4 a, Double4 b) => a.CompareTo(b) < 0;
public static bool operator >=(Double4 a, Double4 b) => a == b || a > b;
public static bool operator <=(Double4 a, Double4 b) => a == b || a < b;
public static implicit operator Double4(Double2 val) => new(val.x, val.y, 0, 0);
public static implicit operator Double4(Double3 val) => new(val.x, val.y, val.z, 0);
public static implicit operator Double4(Int2 val) => new(val.x, val.y, 0, 0);
public static implicit operator Double4(Int3 val) => new(val.x, val.y, val.z, 0);
public static implicit operator Double4(Int4 val) => new(val.x, val.y, val.z, val.w);
public static implicit operator Double4(Vert val) => new(val.position.x, val.position.y, val.position.z, 0);
public static implicit operator Double4(Fill<double> fill) => new(fill);
public static implicit operator Double4(Fill<int> fill) => new(fill);
} }
public Double4(Fill<double> fill) : this(fill(0), fill(1), fill(2), fill(3)) { }
public Double4(Fill<int> fill) : this(fill(0), fill(1), fill(2), fill(3)) { }
public double this[int index]
{
get => index switch
{
0 => x,
1 => y,
2 => z,
3 => w,
_ => throw new IndexOutOfRangeException(nameof(index)),
};
set
{
switch (index)
{
case 0:
x = value;
break;
case 1:
y = value;
break;
case 2:
z = value;
break;
case 3:
w = value;
break;
default: throw new IndexOutOfRangeException(nameof(index));
}
}
}
public static Double4 Absolute(Double4 val) =>
new(Mathf.Absolute(val.x), Mathf.Absolute(val.y), Mathf.Absolute(val.z), Mathf.Absolute(val.w));
public static Double4 Average(params Double4[] vals) => Sum(vals) / vals.Length;
public static Double4 Ceiling(Double4 val) =>
new(Mathf.Ceiling(val.x), Mathf.Ceiling(val.y), Mathf.Ceiling(val.z), Mathf.Ceiling(val.w));
public static Double4 Clamp(Double4 val, Double4 min, Double4 max) =>
new(Mathf.Clamp(val.x, min.x, max.x),
Mathf.Clamp(val.y, min.y, max.y),
Mathf.Clamp(val.z, min.z, max.z),
Mathf.Clamp(val.w, min.w, max.w));
public static Double4 ClampMagnitude(Double4 val, double minMag, double maxMag)
{
if (maxMag < minMag) throw new ArgumentOutOfRangeException(nameof(maxMag),
nameof(maxMag) + " must be greater than or equal to " + nameof(minMag));
double mag = val.Magnitude;
if (mag >= minMag && mag <= maxMag) return val;
val = val.Normalized;
if (mag < minMag) val *= minMag;
else if (mag > maxMag) val *= maxMag;
return val;
}
public static Double4 Divide(Double4 num, params Double4[] vals)
{
foreach (Double4 d in vals) num /= d;
return num;
}
public static double Dot(Double4 a, Double4 b) => a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
public static double Dot(params Double4[] vals)
{
if (vals.Length < 1) return 0;
double x = 1, y = 1, z = 1, w = 1;
foreach (Double4 d in vals)
{
x *= d.x;
y *= d.y;
z *= d.z;
w *= d.w;
}
return x + y + z;
}
public static Double4 Floor(Double4 val) =>
new(Mathf.Floor(val.x), Mathf.Floor(val.y), Mathf.Floor(val.z), Mathf.Floor(val.w));
public static Double4 Lerp(Double4 a, Double4 b, double t, bool clamp = true) =>
new(Mathf.Lerp(a.x, b.x, t, clamp), Mathf.Lerp(a.y, b.y, t, clamp), Mathf.Lerp(a.z, b.z, t, clamp),
Mathf.Lerp(a.w, b.w, t, clamp));
public static Double4 Median(params Double4[] vals)
{
double index = Mathf.Average(0, vals.Length - 1);
Double4 valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)];
return Average(valA, valB);
}
public static Double4 Max(params Double4[] vals)
{
if (vals.Length < 1) return Zero;
Double4 val = vals[0];
foreach (Double4 d in vals) val = d > val ? d : val;
return val;
}
public static Double4 Min(params Double4[] vals)
{
if (vals.Length < 1) return Zero;
Double4 val = vals[0];
foreach (Double4 d in vals) val = d < val ? d : val;
return val;
}
public static Double4 Multiply(params Double4[] vals)
{
if (vals.Length < 1) return Zero;
Double4 val = One;
foreach (Double4 d in vals) val *= d;
return val;
}
public static Double4 Subtract(Double4 num, params Double4[] vals)
{
foreach (Double4 d in vals) num -= d;
return num;
}
public static Double4 Sum(params Double4[] vals)
{
Double4 val = Zero;
foreach (Double4 d in vals) val += d;
return val;
}
public int CompareTo(Double4 other) => Magnitude.CompareTo(other.Magnitude);
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null || obj.GetType() != typeof(Double4)) return false;
return Equals((Double4)obj);
}
public bool Equals(Double4 other) => x == other.x && y == other.y && z == other.z && w == other.w;
public override int GetHashCode() => x.GetHashCode() ^ y.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider) + " Z: " + z.ToString(provider)
+ " W: " + w.ToString(provider);
public string ToString(IFormatProvider provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider) + " Z: " + z.ToString(provider)
+ " W: " + w.ToString(provider);
public object Clone() => new Double4(x, y, z, w);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<double> GetEnumerator()
{
yield return x;
yield return y;
yield return z;
yield return w;
}
public double[] ToArray() => new[] { x, y, z, w };
public List<double> ToList() => new() { x, y, z, w };
public static Double4 operator +(Double4 a, Double4 b) => new(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
public static Double4 operator -(Double4 d) => new(-d.x, -d.y, -d.z, -d.w);
public static Double4 operator -(Double4 a, Double4 b) => new(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
public static Double4 operator *(Double4 a, Double4 b) => new(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w);
public static Double4 operator *(Double4 a, double b) => new(a.x * b, a.y * b, a.z * b, a.w * b);
public static Double4 operator /(Double4 a, Double4 b) => new(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w);
public static Double4 operator /(Double4 a, double b) => new(a.x / b, a.y / b, a.z / b, a.w / b);
public static bool operator ==(Double4 a, Double4 b) => a.Equals(b);
public static bool operator !=(Double4 a, Double4 b) => !a.Equals(b);
public static bool operator >(Double4 a, Double4 b) => a.CompareTo(b) > 0;
public static bool operator <(Double4 a, Double4 b) => a.CompareTo(b) < 0;
public static bool operator >=(Double4 a, Double4 b) => a == b || a > b;
public static bool operator <=(Double4 a, Double4 b) => a == b || a < b;
public static implicit operator Double4(Double2 val) => new(val.x, val.y, 0, 0);
public static implicit operator Double4(Double3 val) => new(val.x, val.y, val.z, 0);
public static implicit operator Double4(Int2 val) => new(val.x, val.y, 0, 0);
public static implicit operator Double4(Int3 val) => new(val.x, val.y, val.z, 0);
public static implicit operator Double4(Int4 val) => new(val.x, val.y, val.z, val.w);
public static implicit operator Double4(Vert val) => new(val.position.x, val.position.y, val.position.z, 0);
public static implicit operator Double4(Fill<double> fill) => new(fill);
public static implicit operator Double4(Fill<int> fill) => new(fill);
} }

View File

@ -1,10 +1,3 @@
using System; namespace Nerd_STF.Mathematics;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Nerd_STF.Mathematics public delegate double Equation(double x);
{
public delegate double Equation(double x);
}

View File

@ -1,130 +1,122 @@
using System; namespace Nerd_STF.Mathematics.Geometry;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Nerd_STF.Mathematics.Geometry public struct Box2D : ICloneable, IContainer<Vert>, IEquatable<Box2D>
{ {
public struct Box2D : ICloneable, IContainer<Vert>, IEquatable<Box2D> public static Box2D Unit => new(Vert.Zero, Double2.One);
public Vert MaxVert
{ {
public static Box2D Unit => new(Vert.Zero, Double2.One); get => center + (size / 2);
set
public Vert MaxVert
{ {
get => center + (size / 2); Vert diff = center - value;
set size = (Double2)diff.position * 2;
{
Vert diff = center - value;
size = (Double2)diff.position * 2;
}
} }
public Vert MinVert
{
get => center - (size / 2);
set
{
Vert diff = center + value;
size = (Double2)diff.position * 2;
}
}
public double Area => size.x * size.y;
public double Perimeter => size.x * 2 + size.y * 2;
public Vert center;
public Double2 size;
public Box2D(Vert min, Vert max) : this(Vert.Average(min, max), (Double2)(min - max)) { }
public Box2D(Vert center, Double2 size)
{
this.center = center;
this.size = size;
}
public Box2D(Fill<double> fill) : this(fill, new Double2(fill(3), fill(4))) { }
public double this[int index]
{
get => size[index];
set => size[index] = value;
}
public static Box2D Absolute(Box2D val) => new(Vert.Absolute(val.MinVert), Vert.Absolute(val.MaxVert));
public static Box2D Average(params Box2D[] vals)
{
(Vert[] centers, Double2[] sizes) = SplitArray(vals);
return new(Vert.Average(centers), Double2.Average(sizes));
}
public static Box2D Ceiling(Box2D val) => new(Vert.Ceiling(val.center), Double2.Ceiling(val.size));
public static Box2D Clamp(Box2D val, Box2D min, Box2D max) =>
new(Vert.Clamp(val.center, min.center, max.center), Double2.Clamp(val.size, min.size, max.size));
public static Box2D Floor(Box2D val) => new(Vert.Floor(val.center), Double2.Floor(val.size));
public static Box2D Lerp(Box2D a, Box2D b, float t, bool clamp = true) =>
new(Vert.Lerp(a.center, b.center, t, clamp), Double2.Lerp(a.size, b.size, t, clamp));
public static Box2D Median(params Box2D[] vals)
{
(Vert[] verts, Double2[] sizes) = SplitArray(vals);
return new(Vert.Median(verts), Double2.Median(sizes));
}
public static Box2D Max(params Box2D[] vals)
{
(Vert[] verts, Double2[] sizes) = SplitArray(vals);
return new(Vert.Max(verts), Double2.Max(sizes));
}
public static Box2D Min(params Box2D[] vals)
{
(Vert[] verts, Double2[] sizes) = SplitArray(vals);
return new(Vert.Min(verts), Double2.Min(sizes));
}
public static (Vert[] centers, Double2[] sizes) SplitArray(params Box2D[] vals)
{
Vert[] centers = new Vert[vals.Length];
Double2[] sizes = new Double2[vals.Length];
for (int i = 0; i < vals.Length; i++)
{
centers[i] = vals[i].center;
sizes[i] = vals[i].size;
}
return (centers, sizes);
}
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null || obj.GetType() != typeof(Box2D)) return false;
return Equals((Box2D)obj);
}
public bool Equals(Box2D other) => center == other.center && size == other.size;
public override int GetHashCode() => center.GetHashCode() ^ size.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
"Min: " + MinVert.ToString(provider) + " Max: " + MaxVert.ToString(provider);
public string ToString(IFormatProvider provider) =>
"Min: " + MinVert.ToString(provider) + " Max: " + MaxVert.ToString(provider);
public bool Contains(Vert vert)
{
Double2 diff = Double2.Absolute((Double2)(center - vert));
return diff.x <= size.x && diff.y <= size.y;
}
public object Clone() => new Box2D(center, size);
public static Box2D operator +(Box2D a, Vert b) => new(a.center + b, a.size);
public static Box2D operator +(Box2D a, Double2 b) => new(a.center, a.size + b);
public static Box2D operator -(Box2D b) => new(-b.MaxVert, -b.MinVert);
public static Box2D operator -(Box2D a, Vert b) => new(a.center - b, a.size);
public static Box2D operator -(Box2D a, Double2 b) => new(a.center, a.size - b);
public static Box2D operator *(Box2D a, double b) => new(a.center * b, a.size * b);
public static Box2D operator *(Box2D a, Double2 b) => new(a.center, a.size * b);
public static Box2D operator /(Box2D a, double b) => new(a.center / b, a.size / b);
public static Box2D operator /(Box2D a, Double2 b) => new(a.center, a.size / b);
public static bool operator ==(Box2D a, Box2D b) => a.Equals(b);
public static bool operator !=(Box2D a, Box2D b) => !a.Equals(b);
public static implicit operator Box2D(Fill<double> fill) => new(fill);
public static explicit operator Box2D(Box3D box) => new(box.center, (Double2)box.size);
} }
public Vert MinVert
{
get => center - (size / 2);
set
{
Vert diff = center + value;
size = (Double2)diff.position * 2;
}
}
public double Area => size.x * size.y;
public double Perimeter => size.x * 2 + size.y * 2;
public Vert center;
public Double2 size;
public Box2D(Vert min, Vert max) : this(Vert.Average(min, max), (Double2)(min - max)) { }
public Box2D(Vert center, Double2 size)
{
this.center = center;
this.size = size;
}
public Box2D(Fill<double> fill) : this(fill, new Double2(fill(3), fill(4))) { }
public double this[int index]
{
get => size[index];
set => size[index] = value;
}
public static Box2D Absolute(Box2D val) => new(Vert.Absolute(val.MinVert), Vert.Absolute(val.MaxVert));
public static Box2D Average(params Box2D[] vals)
{
(Vert[] centers, Double2[] sizes) = SplitArray(vals);
return new(Vert.Average(centers), Double2.Average(sizes));
}
public static Box2D Ceiling(Box2D val) => new(Vert.Ceiling(val.center), Double2.Ceiling(val.size));
public static Box2D Clamp(Box2D val, Box2D min, Box2D max) =>
new(Vert.Clamp(val.center, min.center, max.center), Double2.Clamp(val.size, min.size, max.size));
public static Box2D Floor(Box2D val) => new(Vert.Floor(val.center), Double2.Floor(val.size));
public static Box2D Lerp(Box2D a, Box2D b, float t, bool clamp = true) =>
new(Vert.Lerp(a.center, b.center, t, clamp), Double2.Lerp(a.size, b.size, t, clamp));
public static Box2D Median(params Box2D[] vals)
{
(Vert[] verts, Double2[] sizes) = SplitArray(vals);
return new(Vert.Median(verts), Double2.Median(sizes));
}
public static Box2D Max(params Box2D[] vals)
{
(Vert[] verts, Double2[] sizes) = SplitArray(vals);
return new(Vert.Max(verts), Double2.Max(sizes));
}
public static Box2D Min(params Box2D[] vals)
{
(Vert[] verts, Double2[] sizes) = SplitArray(vals);
return new(Vert.Min(verts), Double2.Min(sizes));
}
public static (Vert[] centers, Double2[] sizes) SplitArray(params Box2D[] vals)
{
Vert[] centers = new Vert[vals.Length];
Double2[] sizes = new Double2[vals.Length];
for (int i = 0; i < vals.Length; i++)
{
centers[i] = vals[i].center;
sizes[i] = vals[i].size;
}
return (centers, sizes);
}
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null || obj.GetType() != typeof(Box2D)) return false;
return Equals((Box2D)obj);
}
public bool Equals(Box2D other) => center == other.center && size == other.size;
public override int GetHashCode() => center.GetHashCode() ^ size.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
"Min: " + MinVert.ToString(provider) + " Max: " + MaxVert.ToString(provider);
public string ToString(IFormatProvider provider) =>
"Min: " + MinVert.ToString(provider) + " Max: " + MaxVert.ToString(provider);
public bool Contains(Vert vert)
{
Double2 diff = Double2.Absolute((Double2)(center - vert));
return diff.x <= size.x && diff.y <= size.y;
}
public object Clone() => new Box2D(center, size);
public static Box2D operator +(Box2D a, Vert b) => new(a.center + b, a.size);
public static Box2D operator +(Box2D a, Double2 b) => new(a.center, a.size + b);
public static Box2D operator -(Box2D b) => new(-b.MaxVert, -b.MinVert);
public static Box2D operator -(Box2D a, Vert b) => new(a.center - b, a.size);
public static Box2D operator -(Box2D a, Double2 b) => new(a.center, a.size - b);
public static Box2D operator *(Box2D a, double b) => new(a.center * b, a.size * b);
public static Box2D operator *(Box2D a, Double2 b) => new(a.center, a.size * b);
public static Box2D operator /(Box2D a, double b) => new(a.center / b, a.size / b);
public static Box2D operator /(Box2D a, Double2 b) => new(a.center, a.size / b);
public static bool operator ==(Box2D a, Box2D b) => a.Equals(b);
public static bool operator !=(Box2D a, Box2D b) => !a.Equals(b);
public static implicit operator Box2D(Fill<double> fill) => new(fill);
public static explicit operator Box2D(Box3D box) => new(box.center, (Double2)box.size);
} }

View File

@ -1,131 +1,122 @@
using System; namespace Nerd_STF.Mathematics.Geometry;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Nerd_STF.Mathematics.Geometry public struct Box3D : ICloneable, IContainer<Vert>, IEquatable<Box3D>
{ {
public struct Box3D : ICloneable, IContainer<Vert>, IEquatable<Box3D> public static Box3D Unit => new(Vert.Zero, Double3.One);
public Vert MaxVert
{ {
public static Box3D Unit => new(Vert.Zero, Double3.One); get => center + (Vert)(size / 2);
set
public Vert MaxVert
{ {
get => center + (Vert)(size / 2); Vert diff = center - value;
set size = diff.position * 2;
{
Vert diff = center - value;
size = diff.position * 2;
}
} }
public Vert MinVert
{
get => center - (Vert)(size / 2);
set
{
Vert diff = center + value;
size = diff.position * 2;
}
}
public double Area => size.x * size.y * size.z;
public double Perimeter => size.x * 2 + size.y * 2 + size.z * 2;
public Vert center;
public Double3 size;
public Box3D(Box2D box) : this(box.center, (Double3)box.size) { }
public Box3D(Vert min, Vert max) : this(Vert.Average(min, max), (Double3)(min - max)) { }
public Box3D(Vert center, Double3 size)
{
this.center = center;
this.size = size;
}
public Box3D(Fill<double> fill) : this(fill, new Double3(fill(3), fill(4), fill(5))) { }
public double this[int index]
{
get => size[index];
set => size[index] = value;
}
public static Box3D Absolute(Box3D val) => new(Vert.Absolute(val.MinVert), Vert.Absolute(val.MaxVert));
public static Box3D Average(params Box3D[] vals)
{
(Vert[] centers, Double3[] sizes) = SplitArray(vals);
return new(Vert.Average(centers), Double3.Average(sizes));
}
public static Box3D Ceiling(Box3D val) => new(Vert.Ceiling(val.center), Double3.Ceiling(val.size));
public static Box3D Clamp(Box3D val, Box3D min, Box3D max) =>
new(Vert.Clamp(val.center, min.center, max.center), Double3.Clamp(val.size, min.size, max.size));
public static Box3D Floor(Box3D val) => new(Vert.Floor(val.center), Double3.Floor(val.size));
public static Box3D Lerp(Box3D a, Box3D b, float t, bool clamp = true) =>
new(Vert.Lerp(a.center, b.center, t, clamp), Double3.Lerp(a.size, b.size, t, clamp));
public static Box3D Median(params Box3D[] vals)
{
(Vert[] verts, Double3[] sizes) = SplitArray(vals);
return new(Vert.Median(verts), Double3.Median(sizes));
}
public static Box3D Max(params Box3D[] vals)
{
(Vert[] verts, Double3[] sizes) = SplitArray(vals);
return new(Vert.Max(verts), Double3.Max(sizes));
}
public static Box3D Min(params Box3D[] vals)
{
(Vert[] verts, Double3[] sizes) = SplitArray(vals);
return new(Vert.Min(verts), Double3.Min(sizes));
}
public static (Vert[] centers, Double3[] sizes) SplitArray(params Box3D[] vals)
{
Vert[] centers = new Vert[vals.Length];
Double3[] sizes = new Double3[vals.Length];
for (int i = 0; i < vals.Length; i++)
{
centers[i] = vals[i].center;
sizes[i] = vals[i].size;
}
return (centers, sizes);
}
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null || obj.GetType() != typeof(Box3D)) return false;
return Equals((Box3D)obj);
}
public bool Equals(Box3D other) => center == other.center && size == other.size;
public override int GetHashCode() => center.GetHashCode() ^ size.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
"Min: " + MinVert.ToString(provider) + " Max: " + MaxVert.ToString(provider);
public string ToString(IFormatProvider provider) =>
"Min: " + MinVert.ToString(provider) + " Max: " + MaxVert.ToString(provider);
public bool Contains(Vert vert)
{
Double3 diff = Double3.Absolute(center - vert);
return diff.x <= size.x && diff.y <= size.y && diff.z <= size.z;
}
public object Clone() => new Box3D(center, size);
public static Box3D operator +(Box3D a, Vert b) => new(a.center + b, a.size);
public static Box3D operator +(Box3D a, Double3 b) => new(a.center, a.size + b);
public static Box3D operator -(Box3D b) => new(-b.MaxVert, -b.MinVert);
public static Box3D operator -(Box3D a, Vert b) => new(a.center - b, a.size);
public static Box3D operator -(Box3D a, Double3 b) => new(a.center, a.size - b);
public static Box3D operator *(Box3D a, double b) => new(a.center * b, a.size * b);
public static Box3D operator *(Box3D a, Double3 b) => new(a.center, a.size * b);
public static Box3D operator /(Box3D a, double b) => new(a.center / b, a.size / b);
public static Box3D operator /(Box3D a, Double3 b) => new(a.center, a.size / b);
public static bool operator ==(Box3D a, Box3D b) => a.Equals(b);
public static bool operator !=(Box3D a, Box3D b) => !a.Equals(b);
public static implicit operator Box3D(Fill<double> fill) => new(fill);
public static implicit operator Box3D(Box2D box) => new(box);
} }
public Vert MinVert
{
get => center - (Vert)(size / 2);
set
{
Vert diff = center + value;
size = diff.position * 2;
}
}
public double Area => size.x * size.y * size.z;
public double Perimeter => size.x * 2 + size.y * 2 + size.z * 2;
public Vert center;
public Double3 size;
public Box3D(Box2D box) : this(box.center, (Double3)box.size) { }
public Box3D(Vert min, Vert max) : this(Vert.Average(min, max), (Double3)(min - max)) { }
public Box3D(Vert center, Double3 size)
{
this.center = center;
this.size = size;
}
public Box3D(Fill<double> fill) : this(fill, new Double3(fill(3), fill(4), fill(5))) { }
public double this[int index]
{
get => size[index];
set => size[index] = value;
}
public static Box3D Absolute(Box3D val) => new(Vert.Absolute(val.MinVert), Vert.Absolute(val.MaxVert));
public static Box3D Average(params Box3D[] vals)
{
(Vert[] centers, Double3[] sizes) = SplitArray(vals);
return new(Vert.Average(centers), Double3.Average(sizes));
}
public static Box3D Ceiling(Box3D val) => new(Vert.Ceiling(val.center), Double3.Ceiling(val.size));
public static Box3D Clamp(Box3D val, Box3D min, Box3D max) =>
new(Vert.Clamp(val.center, min.center, max.center), Double3.Clamp(val.size, min.size, max.size));
public static Box3D Floor(Box3D val) => new(Vert.Floor(val.center), Double3.Floor(val.size));
public static Box3D Lerp(Box3D a, Box3D b, float t, bool clamp = true) =>
new(Vert.Lerp(a.center, b.center, t, clamp), Double3.Lerp(a.size, b.size, t, clamp));
public static Box3D Median(params Box3D[] vals)
{
(Vert[] verts, Double3[] sizes) = SplitArray(vals);
return new(Vert.Median(verts), Double3.Median(sizes));
}
public static Box3D Max(params Box3D[] vals)
{
(Vert[] verts, Double3[] sizes) = SplitArray(vals);
return new(Vert.Max(verts), Double3.Max(sizes));
}
public static Box3D Min(params Box3D[] vals)
{
(Vert[] verts, Double3[] sizes) = SplitArray(vals);
return new(Vert.Min(verts), Double3.Min(sizes));
}
public static (Vert[] centers, Double3[] sizes) SplitArray(params Box3D[] vals)
{
Vert[] centers = new Vert[vals.Length];
Double3[] sizes = new Double3[vals.Length];
for (int i = 0; i < vals.Length; i++)
{
centers[i] = vals[i].center;
sizes[i] = vals[i].size;
}
return (centers, sizes);
}
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null || obj.GetType() != typeof(Box3D)) return false;
return Equals((Box3D)obj);
}
public bool Equals(Box3D other) => center == other.center && size == other.size;
public override int GetHashCode() => center.GetHashCode() ^ size.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
"Min: " + MinVert.ToString(provider) + " Max: " + MaxVert.ToString(provider);
public string ToString(IFormatProvider provider) =>
"Min: " + MinVert.ToString(provider) + " Max: " + MaxVert.ToString(provider);
public bool Contains(Vert vert)
{
Double3 diff = Double3.Absolute(center - vert);
return diff.x <= size.x && diff.y <= size.y && diff.z <= size.z;
}
public object Clone() => new Box3D(center, size);
public static Box3D operator +(Box3D a, Vert b) => new(a.center + b, a.size);
public static Box3D operator +(Box3D a, Double3 b) => new(a.center, a.size + b);
public static Box3D operator -(Box3D b) => new(-b.MaxVert, -b.MinVert);
public static Box3D operator -(Box3D a, Vert b) => new(a.center - b, a.size);
public static Box3D operator -(Box3D a, Double3 b) => new(a.center, a.size - b);
public static Box3D operator *(Box3D a, double b) => new(a.center * b, a.size * b);
public static Box3D operator *(Box3D a, Double3 b) => new(a.center, a.size * b);
public static Box3D operator /(Box3D a, double b) => new(a.center / b, a.size / b);
public static Box3D operator /(Box3D a, Double3 b) => new(a.center, a.size / b);
public static bool operator ==(Box3D a, Box3D b) => a.Equals(b);
public static bool operator !=(Box3D a, Box3D b) => !a.Equals(b);
public static implicit operator Box3D(Fill<double> fill) => new(fill);
public static implicit operator Box3D(Box2D box) => new(box);
} }

View File

@ -1,14 +1,7 @@
using System; namespace Nerd_STF.Mathematics.Geometry;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Nerd_STF.Mathematics.Geometry public interface ISubdividable<T>
{ {
public interface ISubdividable<T> public T Subdivide();
{ public T Subdivide(int iterations);
public T Subdivide();
public T Subdivide(int iterations);
}
} }

View File

@ -1,20 +1,13 @@
using System; namespace Nerd_STF.Mathematics.Geometry;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Nerd_STF.Mathematics.Geometry public interface ITriangulatable
{ {
public interface ITriangulatable public static Triangle[] TriangulateAll(params ITriangulatable[] triangulatables)
{ {
public static Triangle[] TriangulateAll(params ITriangulatable[] triangulatables) List<Triangle> res = new();
{ foreach (ITriangulatable triangulatable in triangulatables) res.AddRange(triangulatable.Triangulate());
List<Triangle> res = new(); return res.ToArray();
foreach (ITriangulatable triangulatable in triangulatables) res.AddRange(triangulatable.Triangulate());
return res.ToArray();
}
public Triangle[] Triangulate();
} }
public Triangle[] Triangulate();
} }

View File

@ -1,202 +1,193 @@
using System; namespace Nerd_STF.Mathematics.Geometry;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Nerd_STF.Mathematics.Geometry public struct Line : ICloneable, IClosest<Vert>, IComparable<Line>, IContainer<Vert>, IEquatable<Line>,
IGroup<Vert>, ISubdividable<Line[]>
{ {
public struct Line : ICloneable, IClosest<Vert>, IComparable<Line>, IContainer<Vert>, IEquatable<Line>, public static Line Back => new(Vert.Zero, Vert.Back);
IGroup<Vert>, ISubdividable<Line[]> public static Line Down => new(Vert.Zero, Vert.Down);
public static Line Forward => new(Vert.Zero, Vert.Forward);
public static Line Left => new(Vert.Zero, Vert.Left);
public static Line Right => new(Vert.Zero, Vert.Right);
public static Line Up => new(Vert.Zero, Vert.Up);
public static Line One => new(Vert.Zero, Vert.One);
public static Line Zero => new(Vert.Zero, Vert.Zero);
public double Length => (b - a).Magnitude;
public Vert a, b;
public Line(Vert a, Vert b)
{ {
public static Line Back => new(Vert.Zero, Vert.Back); this.a = a;
public static Line Down => new(Vert.Zero, Vert.Down); this.b = b;
public static Line Forward => new(Vert.Zero, Vert.Forward);
public static Line Left => new(Vert.Zero, Vert.Left);
public static Line Right => new(Vert.Zero, Vert.Right);
public static Line Up => new(Vert.Zero, Vert.Up);
public static Line One => new(Vert.Zero, Vert.One);
public static Line Zero => new(Vert.Zero, Vert.Zero);
public double Length => (b - a).Magnitude;
public Vert a, b;
public Line(Vert a, Vert b)
{
this.a = a;
this.b = b;
}
public Line(double x1, double y1, double x2, double y2) : this(new(x1, y1), new(x2, y2)) { }
public Line(double x1, double y1, double z1, double x2, double y2, double z2)
: this(new(x1, y1, z1), new(x2, y2, z2)) { }
public Line(Fill<Vert> fill) : this(fill(0), fill(1)) { }
public Line(Fill<Double3> fill) : this(new(fill(0)), new(fill(1))) { }
public Line(Fill<Int3> fill) : this(new(fill(0)), new(fill(1))) { }
public Line(Fill<double> fill) : this(new(fill(0), fill(1), fill(2)), new(fill(3), fill(4), fill(5))) { }
public Line(Fill<int> fill) : this(new(fill(0), fill(1), fill(2)), new(fill(3), fill(4), fill(5))) { }
public Vert this[int index]
{
get => index switch
{
0 => a,
1 => b,
_ => throw new IndexOutOfRangeException(nameof(index)),
};
set
{
switch (index)
{
case 0:
a = value;
break;
case 1:
b = value;
break;
default: throw new IndexOutOfRangeException(nameof(index));
}
}
}
public static Line Absolute(Line val) => new(Vert.Absolute(val.a), Vert.Absolute(val.b));
public static Line Average(params Line[] vals)
{
(Vert[] starts, Vert[] ends) = SplitArray(vals);
return new(Vert.Average(starts), Vert.Average(ends));
}
public static Line Ceiling(Line val) => new(Vert.Ceiling(val.a), Vert.Ceiling(val.b));
public static Line Clamp(Line val, Line min, Line max) =>
new(Vert.Clamp(val.a, min.a, max.a), Vert.Clamp(val.b, min.b, max.b));
public static Line Floor(Line val) => new(Vert.Floor(val.a), Vert.Floor(val.b));
public static Line Lerp(Line a, Line b, double t, bool clamp = true) =>
new(Vert.Lerp(a.a, b.a, t, clamp), Vert.Lerp(a.b, b.b, t, clamp));
public static Line Median(params Line[] vals)
{
(Vert[] starts, Vert[] ends) = SplitArray(vals);
return new(Vert.Median(starts), Vert.Median(ends));
}
public static Line Max(params Line[] vals)
{
(Vert[] starts, Vert[] ends) = SplitArray(vals);
return new(Vert.Max(starts), Vert.Max(ends));
}
public static Line Min(params Line[] vals)
{
(Vert[] starts, Vert[] ends) = SplitArray(vals);
return new(Vert.Min(starts), Vert.Min(ends));
}
public static (Vert[] starts, Vert[] ends) SplitArray(params Line[] lines)
{
Vert[] starts = new Vert[lines.Length], ends = new Vert[lines.Length];
for (int i = 0; i < lines.Length; i++)
{
starts[i] = lines[i].a;
ends[i] = lines[i].b;
}
return (starts, ends);
}
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null || obj.GetType() != typeof(Line)) return false;
return Equals((Line)obj);
}
public bool Equals(Line other) => a == other.a && b == other.b;
public override int GetHashCode() => a.GetHashCode() ^ b.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
"A: " + a.ToString(provider) + " B: " + b.ToString(provider);
public string ToString(IFormatProvider provider) =>
"A: " + a.ToString(provider) + " B: " + b.ToString(provider);
public object Clone() => new Line(a, b);
public int CompareTo(Line line) => Length.CompareTo(line.Length);
public bool Contains(Vert vert)
{
Double3 diffA = a - vert, diffB = a - b;
double lerpVal = diffA.Magnitude / diffB.Magnitude;
return Vert.Lerp(a, b, lerpVal) == vert;
}
public Vert ClosestTo(Vert vert) => ClosestTo(vert, Calculus.DefaultStep);
public Vert ClosestTo(Vert vert, double step)
{
Vert closestA = a, closestB = b;
for (double t = 0; t <= 1; t += step)
{
Vert valA = Vert.Lerp(a, b, t);
Vert valB = Vert.Lerp(b, a, t);
closestA = (valA - vert).Magnitude < (closestA - vert).Magnitude ? valA : closestA;
closestB = (valB - vert).Magnitude < (closestB - vert).Magnitude ? valB : closestB;
}
return (closestA - vert).Magnitude >= (closestB - vert).Magnitude ? closestA : closestB;
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<Vert> GetEnumerator()
{
yield return a;
yield return b;
}
public Line[] Subdivide()
{
Vert middle = Vert.Lerp(a, b, 0.5);
return new Line[] { new(a, middle), new(middle, b) };
}
public Line[] Subdivide(int iterations)
{
if (iterations < 1) return Array.Empty<Line>();
List<Line> lines = new(Subdivide());
for (int i = 1; i < iterations; i++)
{
List<Line> add = new();
for (int j = 0; j < lines.Count; j++) add.AddRange(lines[j].Subdivide());
lines = add;
}
return lines.ToArray();
}
public Vert[] ToArray() => new Vert[] { a, b };
public List<Vert> ToList() => new() { a, b };
public double[] ToDoubleArray() => new double[] { a.position.x, a.position.y, a.position.z,
b.position.x, b.position.y, b.position.z };
public List<double> ToDoubleList() => new() { a.position.x, a.position.y, a.position.z,
b.position.x, b.position.y, b.position.z };
public static Line operator +(Line a, Line b) => new(a.a + b.a, a.b + b.b);
public static Line operator +(Line a, Vert b) => new(a.a + b, a.b + b);
public static Line operator -(Line l) => new(-l.a, -l.b);
public static Line operator -(Line a, Line b) => new(a.a - b.a, a.b - b.b);
public static Line operator -(Line a, Vert b) => new(a.a - b, a.b - b);
public static Line operator *(Line a, Line b) => new(a.a * b.a, a.b * b.b);
public static Line operator *(Line a, Vert b) => new(a.a * b, a.b * b);
public static Line operator *(Line a, double b) => new(a.a * b, a.b * b);
public static Line operator /(Line a, Line b) => new(a.a / b.a, a.b / b.b);
public static Line operator /(Line a, Vert b) => new(a.a / b, a.b / b);
public static Line operator /(Line a, double b) => new(a.a / b, a.b / b);
public static bool operator ==(Line a, Line b) => a.Equals(b);
public static bool operator !=(Line a, Line b) => !a.Equals(b);
public static bool operator >(Line a, Line b) => a.CompareTo(b) > 0;
public static bool operator <(Line a, Line b) => a.CompareTo(b) < 0;
public static bool operator >=(Line a, Line b) => a > b || a == b;
public static bool operator <=(Line a, Line b) => a < b || a == b;
public static implicit operator Line(Fill<Vert> fill) => new(fill);
public static implicit operator Line(Fill<Double3> fill) => new(fill);
public static implicit operator Line(Fill<Int3> fill) => new(fill);
public static implicit operator Line(Fill<double> fill) => new(fill);
public static implicit operator Line(Fill<int> fill) => new(fill);
} }
public Line(double x1, double y1, double x2, double y2) : this(new(x1, y1), new(x2, y2)) { }
public Line(double x1, double y1, double z1, double x2, double y2, double z2)
: this(new(x1, y1, z1), new(x2, y2, z2)) { }
public Line(Fill<Vert> fill) : this(fill(0), fill(1)) { }
public Line(Fill<Double3> fill) : this(new(fill(0)), new(fill(1))) { }
public Line(Fill<Int3> fill) : this(new(fill(0)), new(fill(1))) { }
public Line(Fill<double> fill) : this(new(fill(0), fill(1), fill(2)), new(fill(3), fill(4), fill(5))) { }
public Line(Fill<int> fill) : this(new(fill(0), fill(1), fill(2)), new(fill(3), fill(4), fill(5))) { }
public Vert this[int index]
{
get => index switch
{
0 => a,
1 => b,
_ => throw new IndexOutOfRangeException(nameof(index)),
};
set
{
switch (index)
{
case 0:
a = value;
break;
case 1:
b = value;
break;
default: throw new IndexOutOfRangeException(nameof(index));
}
}
}
public static Line Absolute(Line val) => new(Vert.Absolute(val.a), Vert.Absolute(val.b));
public static Line Average(params Line[] vals)
{
(Vert[] starts, Vert[] ends) = SplitArray(vals);
return new(Vert.Average(starts), Vert.Average(ends));
}
public static Line Ceiling(Line val) => new(Vert.Ceiling(val.a), Vert.Ceiling(val.b));
public static Line Clamp(Line val, Line min, Line max) =>
new(Vert.Clamp(val.a, min.a, max.a), Vert.Clamp(val.b, min.b, max.b));
public static Line Floor(Line val) => new(Vert.Floor(val.a), Vert.Floor(val.b));
public static Line Lerp(Line a, Line b, double t, bool clamp = true) =>
new(Vert.Lerp(a.a, b.a, t, clamp), Vert.Lerp(a.b, b.b, t, clamp));
public static Line Median(params Line[] vals)
{
(Vert[] starts, Vert[] ends) = SplitArray(vals);
return new(Vert.Median(starts), Vert.Median(ends));
}
public static Line Max(params Line[] vals)
{
(Vert[] starts, Vert[] ends) = SplitArray(vals);
return new(Vert.Max(starts), Vert.Max(ends));
}
public static Line Min(params Line[] vals)
{
(Vert[] starts, Vert[] ends) = SplitArray(vals);
return new(Vert.Min(starts), Vert.Min(ends));
}
public static (Vert[] starts, Vert[] ends) SplitArray(params Line[] lines)
{
Vert[] starts = new Vert[lines.Length], ends = new Vert[lines.Length];
for (int i = 0; i < lines.Length; i++)
{
starts[i] = lines[i].a;
ends[i] = lines[i].b;
}
return (starts, ends);
}
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null || obj.GetType() != typeof(Line)) return false;
return Equals((Line)obj);
}
public bool Equals(Line other) => a == other.a && b == other.b;
public override int GetHashCode() => a.GetHashCode() ^ b.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
"A: " + a.ToString(provider) + " B: " + b.ToString(provider);
public string ToString(IFormatProvider provider) =>
"A: " + a.ToString(provider) + " B: " + b.ToString(provider);
public object Clone() => new Line(a, b);
public int CompareTo(Line line) => Length.CompareTo(line.Length);
public bool Contains(Vert vert)
{
Double3 diffA = a - vert, diffB = a - b;
double lerpVal = diffA.Magnitude / diffB.Magnitude;
return Vert.Lerp(a, b, lerpVal) == vert;
}
public Vert ClosestTo(Vert vert) => ClosestTo(vert, Calculus.DefaultStep);
public Vert ClosestTo(Vert vert, double step)
{
Vert closestA = a, closestB = b;
for (double t = 0; t <= 1; t += step)
{
Vert valA = Vert.Lerp(a, b, t);
Vert valB = Vert.Lerp(b, a, t);
closestA = (valA - vert).Magnitude < (closestA - vert).Magnitude ? valA : closestA;
closestB = (valB - vert).Magnitude < (closestB - vert).Magnitude ? valB : closestB;
}
return (closestA - vert).Magnitude >= (closestB - vert).Magnitude ? closestA : closestB;
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<Vert> GetEnumerator()
{
yield return a;
yield return b;
}
public Line[] Subdivide()
{
Vert middle = Vert.Lerp(a, b, 0.5);
return new Line[] { new(a, middle), new(middle, b) };
}
public Line[] Subdivide(int iterations)
{
if (iterations < 1) return Array.Empty<Line>();
List<Line> lines = new(Subdivide());
for (int i = 1; i < iterations; i++)
{
List<Line> add = new();
for (int j = 0; j < lines.Count; j++) add.AddRange(lines[j].Subdivide());
lines = add;
}
return lines.ToArray();
}
public Vert[] ToArray() => new Vert[] { a, b };
public List<Vert> ToList() => new() { a, b };
public double[] ToDoubleArray() => new double[] { a.position.x, a.position.y, a.position.z,
b.position.x, b.position.y, b.position.z };
public List<double> ToDoubleList() => new() { a.position.x, a.position.y, a.position.z,
b.position.x, b.position.y, b.position.z };
public static Line operator +(Line a, Line b) => new(a.a + b.a, a.b + b.b);
public static Line operator +(Line a, Vert b) => new(a.a + b, a.b + b);
public static Line operator -(Line l) => new(-l.a, -l.b);
public static Line operator -(Line a, Line b) => new(a.a - b.a, a.b - b.b);
public static Line operator -(Line a, Vert b) => new(a.a - b, a.b - b);
public static Line operator *(Line a, Line b) => new(a.a * b.a, a.b * b.b);
public static Line operator *(Line a, Vert b) => new(a.a * b, a.b * b);
public static Line operator *(Line a, double b) => new(a.a * b, a.b * b);
public static Line operator /(Line a, Line b) => new(a.a / b.a, a.b / b.b);
public static Line operator /(Line a, Vert b) => new(a.a / b, a.b / b);
public static Line operator /(Line a, double b) => new(a.a / b, a.b / b);
public static bool operator ==(Line a, Line b) => a.Equals(b);
public static bool operator !=(Line a, Line b) => !a.Equals(b);
public static bool operator >(Line a, Line b) => a.CompareTo(b) > 0;
public static bool operator <(Line a, Line b) => a.CompareTo(b) < 0;
public static bool operator >=(Line a, Line b) => a > b || a == b;
public static bool operator <=(Line a, Line b) => a < b || a == b;
public static implicit operator Line(Fill<Vert> fill) => new(fill);
public static implicit operator Line(Fill<Double3> fill) => new(fill);
public static implicit operator Line(Fill<Int3> fill) => new(fill);
public static implicit operator Line(Fill<double> fill) => new(fill);
public static implicit operator Line(Fill<int> fill) => new(fill);
} }

View File

@ -1,16 +1,8 @@
using Nerd_STF.Exceptions; namespace Nerd_STF.Mathematics.Geometry;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Nerd_STF.Mathematics.Geometry public struct Polygon : ICloneable, IEquatable<Polygon>, IGroup<Vert>, ISubdividable<Polygon>, ITriangulatable
{ {
public struct Polygon : ICloneable, IEquatable<Polygon>, IGroup<Vert>, ISubdividable<Polygon>, ITriangulatable public Line[] Lines
{
public Line[] Lines
{ {
get => p_lines; get => p_lines;
set set
@ -19,7 +11,7 @@ namespace Nerd_STF.Mathematics.Geometry
p_verts = GenerateVerts(value); p_verts = GenerateVerts(value);
} }
} }
public Vert[] Verts public Vert[] Verts
{ {
get => p_verts; get => p_verts;
set set
@ -29,21 +21,21 @@ namespace Nerd_STF.Mathematics.Geometry
} }
} }
private Line[] p_lines; private Line[] p_lines;
private Vert[] p_verts; private Vert[] p_verts;
[Obsolete("This method uses the Polygon.Triangulate() function, which has issues. It will be fixed in a " + [Obsolete("This method uses the Polygon.Triangulate() function, which has issues. It will be fixed in a " +
"future update.")] "future update.")]
public double Area public double Area
{
get
{ {
get double val = 0;
{ foreach (Triangle t in Triangulate()) val += t.Area;
double val = 0; return val;
foreach (Triangle t in Triangulate()) val += t.Area;
return val;
}
} }
public double Perimeter }
public double Perimeter
{ {
get get
{ {
@ -53,12 +45,12 @@ namespace Nerd_STF.Mathematics.Geometry
} }
} }
public Polygon() public Polygon()
{ {
p_lines = Array.Empty<Line>(); p_lines = Array.Empty<Line>();
p_verts = Array.Empty<Vert>(); p_verts = Array.Empty<Vert>();
} }
public Polygon(Fill<Vert?> fill) public Polygon(Fill<Vert?> fill)
{ {
List<Vert> verts = new(); List<Vert> verts = new();
int i = 0; int i = 0;
@ -70,7 +62,7 @@ namespace Nerd_STF.Mathematics.Geometry
} }
this = new(verts.ToArray()); this = new(verts.ToArray());
} }
public Polygon(Fill<Double3?> fill) public Polygon(Fill<Double3?> fill)
{ {
List<Vert> verts = new(); List<Vert> verts = new();
int i = 0; int i = 0;
@ -82,7 +74,7 @@ namespace Nerd_STF.Mathematics.Geometry
} }
this = new(verts.ToArray()); this = new(verts.ToArray());
} }
public Polygon(Fill<Line?> fill) public Polygon(Fill<Line?> fill)
{ {
List<Line> lines = new(); List<Line> lines = new();
int i = 0; int i = 0;
@ -94,65 +86,65 @@ namespace Nerd_STF.Mathematics.Geometry
} }
this = new(lines.ToArray()); this = new(lines.ToArray());
} }
public Polygon(Fill<Vert> fill, int length) public Polygon(Fill<Vert> fill, int length)
{ {
List<Vert> verts = new(); List<Vert> verts = new();
for (int i = 0; i < length; i++) verts.Add(fill(i)); for (int i = 0; i < length; i++) verts.Add(fill(i));
this = new(verts.ToArray()); this = new(verts.ToArray());
} }
public Polygon(Fill<Double3> fill, int length) public Polygon(Fill<Double3> fill, int length)
{ {
List<Vert> verts = new(); List<Vert> verts = new();
for (int i = 0; i < length; i++) verts.Add(fill(i)); for (int i = 0; i < length; i++) verts.Add(fill(i));
this = new(verts.ToArray()); this = new(verts.ToArray());
} }
public Polygon(Fill<Line> fill, int length) public Polygon(Fill<Line> fill, int length)
{ {
List<Line> lines = new(); List<Line> lines = new();
for (int i = 0; i < length; i++) lines.Add(fill(i)); for (int i = 0; i < length; i++) lines.Add(fill(i));
this = new(lines.ToArray()); this = new(lines.ToArray());
} }
public Polygon(params Double3[] verts) public Polygon(params Double3[] verts)
{ {
p_verts = new Vert[verts.Length]; p_verts = new Vert[verts.Length];
for (int i = 0; i < verts.Length; i++) p_verts[i] = verts[i]; for (int i = 0; i < verts.Length; i++) p_verts[i] = verts[i];
p_lines = GenerateLines(p_verts); p_lines = GenerateLines(p_verts);
} }
public Polygon(params Vert[] verts) public Polygon(params Vert[] verts)
{ {
p_verts = verts; p_verts = verts;
p_lines = GenerateLines(verts); p_lines = GenerateLines(verts);
} }
public Polygon(params Line[] lines) public Polygon(params Line[] lines)
{ {
p_lines = lines; p_lines = lines;
p_verts = GenerateVerts(lines); p_verts = GenerateVerts(lines);
} }
public Vert this[int index] public Vert this[int index]
{ {
get => Verts[index]; get => Verts[index];
set => Verts[index] = value; set => Verts[index] = value;
} }
public static Polygon CreateCircle(int vertCount) public static Polygon CreateCircle(int vertCount)
{
List<Vert> parts = new();
for (int i = 0; i < vertCount; i++)
{ {
List<Vert> parts = new(); double val = Mathf.Tau * i / vertCount;
for (int i = 0; i < vertCount; i++) parts.Add(new(Mathf.Cos(val), Mathf.Sin(val)));
{
double val = Mathf.Tau * i / vertCount;
parts.Add(new(Mathf.Cos(val), Mathf.Sin(val)));
}
return new(parts.ToArray());
} }
return new(parts.ToArray());
}
public static Polygon Absolute(Polygon val) public static Polygon Absolute(Polygon val)
{ {
Vert[] v = val.Verts; Vert[] v = val.Verts;
for (int i = 0; i < v.Length; i++) v[i] = Vert.Absolute(v[i]); for (int i = 0; i < v.Length; i++) v[i] = Vert.Absolute(v[i]);
return new(v); return new(v);
} }
public static Polygon Average(params Polygon[] vals) public static Polygon Average(params Polygon[] vals)
{ {
if (!CheckVerts(vals)) throw new DifferingVertCountException(nameof(vals), vals); if (!CheckVerts(vals)) throw new DifferingVertCountException(nameof(vals), vals);
if (vals.Length < 1) return default; if (vals.Length < 1) return default;
@ -170,13 +162,13 @@ namespace Nerd_STF.Mathematics.Geometry
return new(res); return new(res);
} }
public static Polygon Ceiling(Polygon val) public static Polygon Ceiling(Polygon val)
{ {
Vert[] v = val.Verts; Vert[] v = val.Verts;
for (int i = 0; i < v.Length; i++) v[i] = Vert.Ceiling(v[i]); for (int i = 0; i < v.Length; i++) v[i] = Vert.Ceiling(v[i]);
return new(v); return new(v);
} }
public static Polygon Clamp(Polygon val, Polygon min, Polygon max) public static Polygon Clamp(Polygon val, Polygon min, Polygon max)
{ {
if (!CheckVerts(val, min, max)) throw new DifferingVertCountException(val, min, max); if (!CheckVerts(val, min, max)) throw new DifferingVertCountException(val, min, max);
Line[][] lines = new Line[3][] { val.Lines, min.Lines, max.Lines }; Line[][] lines = new Line[3][] { val.Lines, min.Lines, max.Lines };
@ -184,13 +176,13 @@ namespace Nerd_STF.Mathematics.Geometry
for (int i = 0; i < res.Length; i++) res[i] = Line.Clamp(lines[0][i], lines[1][i], lines[2][i]); for (int i = 0; i < res.Length; i++) res[i] = Line.Clamp(lines[0][i], lines[1][i], lines[2][i]);
return new(res); return new(res);
} }
public static Polygon Floor(Polygon val) public static Polygon Floor(Polygon val)
{ {
Vert[] v = val.Verts; Vert[] v = val.Verts;
for (int i = 0; i < v.Length; i++) v[i] = Vert.Floor(v[i]); for (int i = 0; i < v.Length; i++) v[i] = Vert.Floor(v[i]);
return new(v); return new(v);
} }
public static Polygon Lerp(Polygon a, Polygon b, double t, bool clamp = true) public static Polygon Lerp(Polygon a, Polygon b, double t, bool clamp = true)
{ {
if (!CheckVerts(a, b)) throw new DifferingVertCountException(a, b); if (!CheckVerts(a, b)) throw new DifferingVertCountException(a, b);
Line[][] lines = new Line[2][] { a.Lines, b.Lines }; Line[][] lines = new Line[2][] { a.Lines, b.Lines };
@ -198,7 +190,7 @@ namespace Nerd_STF.Mathematics.Geometry
for (int i = 0; i < res.Length; i++) res[i] = Line.Lerp(lines[0][i], lines[1][i], t, clamp); for (int i = 0; i < res.Length; i++) res[i] = Line.Lerp(lines[0][i], lines[1][i], t, clamp);
return new(res); return new(res);
} }
public static Polygon Max(params Polygon[] vals) public static Polygon Max(params Polygon[] vals)
{ {
if (!CheckVerts(vals)) throw new DifferingVertCountException(nameof(vals), vals); if (!CheckVerts(vals)) throw new DifferingVertCountException(nameof(vals), vals);
if (vals.Length < 1) return default; if (vals.Length < 1) return default;
@ -216,7 +208,7 @@ namespace Nerd_STF.Mathematics.Geometry
return new(res); return new(res);
} }
public static Polygon Median(params Polygon[] vals) public static Polygon Median(params Polygon[] vals)
{ {
if (!CheckVerts(vals)) throw new DifferingVertCountException(nameof(vals), vals); if (!CheckVerts(vals)) throw new DifferingVertCountException(nameof(vals), vals);
if (vals.Length < 1) return default; if (vals.Length < 1) return default;
@ -234,7 +226,7 @@ namespace Nerd_STF.Mathematics.Geometry
return new(res); return new(res);
} }
public static Polygon Min(params Polygon[] vals) public static Polygon Min(params Polygon[] vals)
{ {
if (!CheckVerts(vals)) throw new DifferingVertCountException(nameof(vals), vals); if (!CheckVerts(vals)) throw new DifferingVertCountException(nameof(vals), vals);
if (vals.Length < 1) return default; if (vals.Length < 1) return default;
@ -253,48 +245,48 @@ namespace Nerd_STF.Mathematics.Geometry
return new(res); return new(res);
} }
public static double[] ToDoubleArrayAll(params Polygon[] polys) => ToDoubleListAll(polys).ToArray(); public static double[] ToDoubleArrayAll(params Polygon[] polys) => ToDoubleListAll(polys).ToArray();
public static List<double> ToDoubleListAll(params Polygon[] polys) public static List<double> ToDoubleListAll(params Polygon[] polys)
{ {
List<double> vals = new(); List<double> vals = new();
foreach (Polygon poly in polys) vals.AddRange(poly.ToDoubleArray()); foreach (Polygon poly in polys) vals.AddRange(poly.ToDoubleArray());
return vals; return vals;
} }
public override bool Equals([NotNullWhen(true)] object? obj) public override bool Equals([NotNullWhen(true)] object? obj)
{ {
if (obj == null || obj.GetType() != typeof(Polygon)) return false; if (obj == null || obj.GetType() != typeof(Polygon)) return false;
return Equals((Polygon)obj); return Equals((Polygon)obj);
} }
public bool Equals(Polygon other) public bool Equals(Polygon other)
{ {
if (!CheckVerts(this, other)) return false; if (!CheckVerts(this, other)) return false;
return Lines == other.Lines; return Lines == other.Lines;
} }
public override int GetHashCode() => Lines.GetHashCode(); public override int GetHashCode() => Lines.GetHashCode();
public override string ToString() => ToString((string?)null); public override string ToString() => ToString((string?)null);
public string ToString(string? provider) public string ToString(string? provider)
{ {
string s = ""; string s = "";
for (int i = 0; i < Lines.Length; i++) s += "L" + i + ": " + Lines[i].ToString(provider) + " "; for (int i = 0; i < Lines.Length; i++) s += "L" + i + ": " + Lines[i].ToString(provider) + " ";
return s; return s;
} }
public string ToString(IFormatProvider provider) public string ToString(IFormatProvider provider)
{ {
string s = ""; string s = "";
for (int i = 0; i < Lines.Length; i++) s += "L" + i + ": " + Lines[i].ToString(provider) + " "; for (int i = 0; i < Lines.Length; i++) s += "L" + i + ": " + Lines[i].ToString(provider) + " ";
return s; return s;
} }
public object Clone() => new Polygon(Lines); public object Clone() => new Polygon(Lines);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<Vert> GetEnumerator() { foreach (Vert v in Verts) yield return v; } public IEnumerator<Vert> GetEnumerator() { foreach (Vert v in Verts) yield return v; }
public Vert[] ToArray() => Verts; public Vert[] ToArray() => Verts;
public List<Vert> ToList() => new(Verts); public List<Vert> ToList() => new(Verts);
public double[] ToDoubleArray() public double[] ToDoubleArray()
{ {
double[] vals = new double[Verts.Length * 3]; double[] vals = new double[Verts.Length * 3];
for (int i = 0; i < Verts.Length; i++) for (int i = 0; i < Verts.Length; i++)
@ -306,57 +298,57 @@ namespace Nerd_STF.Mathematics.Geometry
} }
return vals; return vals;
} }
public List<double> ToDoubleList() => new(ToDoubleArray()); public List<double> ToDoubleList() => new(ToDoubleArray());
public Polygon Subdivide() public Polygon Subdivide()
{ {
Polygon poly = new(); Polygon poly = new();
List<Line> lines = new(); List<Line> lines = new();
for (int i = 0; i < Lines.Length; i++) lines.AddRange(Lines[i].Subdivide()); for (int i = 0; i < Lines.Length; i++) lines.AddRange(Lines[i].Subdivide());
return poly; return poly;
} }
public Polygon Subdivide(int iterations) public Polygon Subdivide(int iterations)
{ {
if (iterations < 1) return new(); if (iterations < 1) return new();
Polygon poly = this; Polygon poly = this;
for (int i = 0; i < iterations; i++) poly = poly.Subdivide(); for (int i = 0; i < iterations; i++) poly = poly.Subdivide();
return poly; return poly;
} }
public Polygon SubdivideCatmullClark(int segments) public Polygon SubdivideCatmullClark(int segments)
{
// Thanks Saalty for making this accidentally.
List<Vert> newVerts = new();
for (int i = 0; i < Verts.Length; i++)
{ {
// Thanks Saalty for making this accidentally. for (int factor = 0; factor < segments; factor++)
List<Vert> newVerts = new();
for (int i = 0; i < Verts.Length; i++)
{ {
for (int factor = 0; factor < segments; factor++) double unit = factor / (double)(segments * 2), unit2 = unit + 0.5, lastUnit = unit * 2;
Vert p1, p2;
if (i == Verts.Length - 1)
{ {
double unit = factor / (double)(segments * 2), unit2 = unit + 0.5, lastUnit = unit * 2; p1 = Verts[^1] + (Verts[0] - Verts[^1]) * unit2;
Vert p1, p2; p2 = Verts[0] + (Verts[1] - Verts[0]) * unit;
if (i == Verts.Length - 1)
{
p1 = Verts[^1] + (Verts[0] - Verts[^1]) * unit2;
p2 = Verts[0] + (Verts[1] - Verts[0]) * unit;
}
else if (i == Verts.Length - 2)
{
p1 = Verts[^2] + (Verts[^1] - Verts[^2]) * unit2;
p2 = Verts[^1] + (Verts[0] - Verts[^1]) * unit;
}
else
{
p1 = Verts[i] + (Verts[i + 1] - Verts[i]) * unit2;
p2 = Verts[i + 1] + (Verts[i + 2] - Verts[i + 1]) * unit;
}
newVerts.Add(p1 + (p2 - p1) * lastUnit);
} }
else if (i == Verts.Length - 2)
{
p1 = Verts[^2] + (Verts[^1] - Verts[^2]) * unit2;
p2 = Verts[^1] + (Verts[0] - Verts[^1]) * unit;
}
else
{
p1 = Verts[i] + (Verts[i + 1] - Verts[i]) * unit2;
p2 = Verts[i + 1] + (Verts[i + 2] - Verts[i + 1]) * unit;
}
newVerts.Add(p1 + (p2 - p1) * lastUnit);
} }
return new(newVerts.ToArray());
} }
return new(newVerts.ToArray());
}
[Obsolete("This method doesn't work very well, and will give very weird results in certain cases. " + [Obsolete("This method doesn't work very well, and will give very weird results in certain cases. " +
"This will be fixed in a future update.")] "This will be fixed in a future update.")]
public Triangle[] Triangulate() public Triangle[] Triangulate()
{ {
// This may cause issues. FIXME // This may cause issues. FIXME
// Tbh, not even sure if this works. This was a bit confusing. // Tbh, not even sure if this works. This was a bit confusing.
@ -397,7 +389,7 @@ namespace Nerd_STF.Mathematics.Geometry
return tris.ToArray(); return tris.ToArray();
} }
private static bool CheckVerts(params Polygon[] polys) private static bool CheckVerts(params Polygon[] polys)
{ {
int len = -1; int len = -1;
foreach (Polygon poly in polys) foreach (Polygon poly in polys)
@ -411,14 +403,14 @@ namespace Nerd_STF.Mathematics.Geometry
} }
return true; return true;
} }
private static Line[] GenerateLines(Vert[] verts) private static Line[] GenerateLines(Vert[] verts)
{ {
Line[] lines = new Line[verts.Length]; Line[] lines = new Line[verts.Length];
for (int i = 0; i < lines.Length; i++) for (int i = 0; i < lines.Length; i++)
lines[i] = new(verts[i], verts[i == lines.Length - 1 ? 0 : i + 1]); lines[i] = new(verts[i], verts[i == lines.Length - 1 ? 0 : i + 1]);
return lines; return lines;
} }
private static Vert[] GenerateVerts(Line[] lines) private static Vert[] GenerateVerts(Line[] lines)
{ {
Vert[] verts = new Vert[lines.Length]; Vert[] verts = new Vert[lines.Length];
for (int i = 0; i < verts.Length; i++) for (int i = 0; i < verts.Length; i++)
@ -430,90 +422,89 @@ namespace Nerd_STF.Mathematics.Geometry
return verts; return verts;
} }
public static Polygon operator +(Polygon a, Polygon b) public static Polygon operator +(Polygon a, Polygon b)
{ {
if (!CheckVerts(a, b)) throw new DifferingVertCountException(a, b); if (!CheckVerts(a, b)) throw new DifferingVertCountException(a, b);
Line[][] lines = new Line[2][] { a.Lines, b.Lines }; Line[][] lines = new Line[2][] { a.Lines, b.Lines };
Line[] res = new Line[a.Lines.Length]; Line[] res = new Line[a.Lines.Length];
for (int i = 0; i < res.Length; i++) res[i] = lines[0][i] + lines[1][i]; for (int i = 0; i < res.Length; i++) res[i] = lines[0][i] + lines[1][i];
return new(res); return new(res);
}
public static Polygon operator +(Polygon a, Vert b)
{
Line[] lines = a.Lines;
for (int i = 0; i < lines.Length; i++) lines[i] += b;
return new(lines);
}
public static Polygon operator -(Polygon p)
{
Line[] lines = p.Lines;
for (int i = 0; i < lines.Length; i++) lines[i] = -lines[i];
return new(lines);
}
public static Polygon operator -(Polygon a, Polygon b)
{
if (!CheckVerts(a, b)) throw new DifferingVertCountException(a, b);
Line[][] lines = new Line[2][] { a.Lines, b.Lines };
Line[] res = new Line[a.Lines.Length];
for (int i = 0; i < res.Length; i++) res[i] = lines[0][i] - lines[1][i];
return new(res);
}
public static Polygon operator -(Polygon a, Vert b)
{
Line[] lines = a.Lines;
for (int i = 0; i < lines.Length; i++) lines[i] -= b;
return new(lines);
}
public static Polygon operator *(Polygon a, Polygon b)
{
if (!CheckVerts(a, b)) throw new DifferingVertCountException(a, b);
Line[][] lines = new Line[2][] { a.Lines, b.Lines };
Line[] res = new Line[a.Lines.Length];
for (int i = 0; i < res.Length; i++) res[i] = lines[0][i] * lines[1][i];
return new(res);
}
public static Polygon operator *(Polygon a, Vert b)
{
Line[] lines = a.Lines;
for (int i = 0; i < lines.Length; i++) lines[i] *= b;
return new(lines);
}
public static Polygon operator *(Polygon a, float b)
{
Line[] lines = a.Lines;
for (int i = 0; i < lines.Length; i++) lines[i] *= b;
return new(lines);
}
public static Polygon operator /(Polygon a, Polygon b)
{
if (!CheckVerts(a, b)) throw new DifferingVertCountException(a, b);
Line[][] lines = new Line[2][] { a.Lines, b.Lines };
Line[] res = new Line[a.Lines.Length];
for (int i = 0; i < res.Length; i++) res[i] = lines[0][i] / lines[1][i];
return new(res);
}
public static Polygon operator /(Polygon a, Vert b)
{
Line[] lines = a.Lines;
for (int i = 0; i < lines.Length; i++) lines[i] /= b;
return new(lines);
}
public static Polygon operator /(Polygon a, float b)
{
Line[] lines = a.Lines;
for (int i = 0; i < lines.Length; i++) lines[i] /= b;
return new(lines);
}
public static bool operator ==(Polygon a, Polygon b) => a.Equals(b);
public static bool operator !=(Polygon a, Polygon b) => !a.Equals(b);
public static implicit operator Polygon(Fill<Vert?> fill) => new(fill);
public static implicit operator Polygon(Fill<Double3?> fill) => new(fill);
public static implicit operator Polygon(Fill<Line?> fill) => new(fill);
public static implicit operator Polygon(Vert[] verts) => new(verts);
public static implicit operator Polygon(Double3[] verts) => new(verts);
public static implicit operator Polygon(Line[] lines) => new(lines);
public static implicit operator Polygon(Triangle tri) => new(tri.AB, tri.BC, tri.CA);
public static implicit operator Polygon(Quadrilateral quad) => new(quad.AB, quad.BC, quad.CD, quad.DA);
} }
public static Polygon operator +(Polygon a, Vert b)
{
Line[] lines = a.Lines;
for (int i = 0; i < lines.Length; i++) lines[i] += b;
return new(lines);
}
public static Polygon operator -(Polygon p)
{
Line[] lines = p.Lines;
for (int i = 0; i < lines.Length; i++) lines[i] = -lines[i];
return new(lines);
}
public static Polygon operator -(Polygon a, Polygon b)
{
if (!CheckVerts(a, b)) throw new DifferingVertCountException(a, b);
Line[][] lines = new Line[2][] { a.Lines, b.Lines };
Line[] res = new Line[a.Lines.Length];
for (int i = 0; i < res.Length; i++) res[i] = lines[0][i] - lines[1][i];
return new(res);
}
public static Polygon operator -(Polygon a, Vert b)
{
Line[] lines = a.Lines;
for (int i = 0; i < lines.Length; i++) lines[i] -= b;
return new(lines);
}
public static Polygon operator *(Polygon a, Polygon b)
{
if (!CheckVerts(a, b)) throw new DifferingVertCountException(a, b);
Line[][] lines = new Line[2][] { a.Lines, b.Lines };
Line[] res = new Line[a.Lines.Length];
for (int i = 0; i < res.Length; i++) res[i] = lines[0][i] * lines[1][i];
return new(res);
}
public static Polygon operator *(Polygon a, Vert b)
{
Line[] lines = a.Lines;
for (int i = 0; i < lines.Length; i++) lines[i] *= b;
return new(lines);
}
public static Polygon operator *(Polygon a, float b)
{
Line[] lines = a.Lines;
for (int i = 0; i < lines.Length; i++) lines[i] *= b;
return new(lines);
}
public static Polygon operator /(Polygon a, Polygon b)
{
if (!CheckVerts(a, b)) throw new DifferingVertCountException(a, b);
Line[][] lines = new Line[2][] { a.Lines, b.Lines };
Line[] res = new Line[a.Lines.Length];
for (int i = 0; i < res.Length; i++) res[i] = lines[0][i] / lines[1][i];
return new(res);
}
public static Polygon operator /(Polygon a, Vert b)
{
Line[] lines = a.Lines;
for (int i = 0; i < lines.Length; i++) lines[i] /= b;
return new(lines);
}
public static Polygon operator /(Polygon a, float b)
{
Line[] lines = a.Lines;
for (int i = 0; i < lines.Length; i++) lines[i] /= b;
return new(lines);
}
public static bool operator ==(Polygon a, Polygon b) => a.Equals(b);
public static bool operator !=(Polygon a, Polygon b) => !a.Equals(b);
public static implicit operator Polygon(Fill<Vert?> fill) => new(fill);
public static implicit operator Polygon(Fill<Double3?> fill) => new(fill);
public static implicit operator Polygon(Fill<Line?> fill) => new(fill);
public static implicit operator Polygon(Vert[] verts) => new(verts);
public static implicit operator Polygon(Double3[] verts) => new(verts);
public static implicit operator Polygon(Line[] lines) => new(lines);
public static implicit operator Polygon(Triangle tri) => new(tri.AB, tri.BC, tri.CA);
public static implicit operator Polygon(Quadrilateral quad) => new(quad.AB, quad.BC, quad.CD, quad.DA);
} }

View File

@ -1,17 +1,8 @@
using Nerd_STF.Exceptions; namespace Nerd_STF.Mathematics.Geometry;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Nerd_STF.Mathematics.Geometry public struct Quadrilateral : ICloneable, IEquatable<Quadrilateral>, IGroup<Vert>, ITriangulatable
{ {
public struct Quadrilateral : ICloneable, IEquatable<Quadrilateral>, IGroup<Vert>, ITriangulatable public Vert A
{
public Vert A
{ {
get => p_a; get => p_a;
set set
@ -21,7 +12,7 @@ namespace Nerd_STF.Mathematics.Geometry
p_da.b = value; p_da.b = value;
} }
} }
public Vert B public Vert B
{ {
get => p_b; get => p_b;
set set
@ -31,7 +22,7 @@ namespace Nerd_STF.Mathematics.Geometry
p_bc.a = value; p_bc.a = value;
} }
} }
public Vert C public Vert C
{ {
get => p_c; get => p_c;
set set
@ -41,7 +32,7 @@ namespace Nerd_STF.Mathematics.Geometry
p_cd.a = value; p_cd.a = value;
} }
} }
public Vert D public Vert D
{ {
get => p_d; get => p_d;
set set
@ -51,7 +42,7 @@ namespace Nerd_STF.Mathematics.Geometry
p_da.a = value; p_da.a = value;
} }
} }
public Line AB public Line AB
{ {
get => p_ab; get => p_ab;
set set
@ -63,7 +54,7 @@ namespace Nerd_STF.Mathematics.Geometry
p_da.b = value.a; p_da.b = value.a;
} }
} }
public Line BC public Line BC
{ {
get => p_bc; get => p_bc;
set set
@ -75,7 +66,7 @@ namespace Nerd_STF.Mathematics.Geometry
p_ab.b = value.a; p_ab.b = value.a;
} }
} }
public Line CD public Line CD
{ {
get => p_cd; get => p_cd;
set set
@ -87,7 +78,7 @@ namespace Nerd_STF.Mathematics.Geometry
p_bc.b = value.a; p_bc.b = value.a;
} }
} }
public Line DA public Line DA
{ {
get => p_da; get => p_da;
set set
@ -100,21 +91,21 @@ namespace Nerd_STF.Mathematics.Geometry
} }
} }
private Vert p_a, p_b, p_c, p_d; private Vert p_a, p_b, p_c, p_d;
private Line p_ab, p_bc, p_cd, p_da; private Line p_ab, p_bc, p_cd, p_da;
public double Area public double Area
{
get
{ {
get double val = 0;
{ foreach (Triangle t in Triangulate()) val += t.Area;
double val = 0; return val;
foreach (Triangle t in Triangulate()) val += t.Area;
return val;
}
} }
public double Perimeter => AB.Length + BC.Length + CD.Length + DA.Length; }
public double Perimeter => AB.Length + BC.Length + CD.Length + DA.Length;
public Quadrilateral(Vert a, Vert b, Vert c, Vert d) public Quadrilateral(Vert a, Vert b, Vert c, Vert d)
{ {
p_a = a; p_a = a;
p_b = b; p_b = b;
@ -125,7 +116,7 @@ namespace Nerd_STF.Mathematics.Geometry
p_cd = new(c, d); p_cd = new(c, d);
p_da = new(d, a); p_da = new(d, a);
} }
public Quadrilateral(Line ab, Line bc, Line cd, Line da) public Quadrilateral(Line ab, Line bc, Line cd, Line da)
{ {
if (ab.a != da.b || ab.b != bc.a || bc.b != cd.a || cd.b != da.a) if (ab.a != da.b || ab.b != bc.a || bc.b != cd.a || cd.b != da.a)
throw new DisconnectedLinesException(ab, bc, cd, da); throw new DisconnectedLinesException(ab, bc, cd, da);
@ -139,21 +130,21 @@ namespace Nerd_STF.Mathematics.Geometry
p_cd = cd; p_cd = cd;
p_da = da; p_da = da;
} }
public Quadrilateral(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) public Quadrilateral(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
: this(new Vert(x1, y1), new(x2, y2), new(x3, y3), new(x4, y4)) { } : this(new Vert(x1, y1), new(x2, y2), new(x3, y3), new(x4, y4)) { }
public Quadrilateral(double x1, double y1, double z1, double x2, double y2, double z2, double x3, double y3, public Quadrilateral(double x1, double y1, double z1, double x2, double y2, double z2, double x3, double y3,
double z3, double x4, double y4, double z4) double z3, double x4, double y4, double z4)
: this(new Vert(x1, y1, z1), new(x2, y2, z2), new(x3, y3, z3), new(x4, y4, z4)) { } : this(new Vert(x1, y1, z1), new(x2, y2, z2), new(x3, y3, z3), new(x4, y4, z4)) { }
public Quadrilateral(Fill<Double3> fill) : this(fill(0), fill(1), fill(2), fill(3)) { } public Quadrilateral(Fill<Double3> fill) : this(fill(0), fill(1), fill(2), fill(3)) { }
public Quadrilateral(Fill<Int3> fill) : this(fill(0), fill(1), fill(2), fill(3)) { } public Quadrilateral(Fill<Int3> fill) : this(fill(0), fill(1), fill(2), fill(3)) { }
public Quadrilateral(Fill<Vert> fill) : this(fill(0), fill(1), fill(2), fill(3)) { } public Quadrilateral(Fill<Vert> fill) : this(fill(0), fill(1), fill(2), fill(3)) { }
public Quadrilateral(Fill<Line> fill) : this(fill(0), fill(1), fill(2), fill(3)) { } public Quadrilateral(Fill<Line> fill) : this(fill(0), fill(1), fill(2), fill(3)) { }
public Quadrilateral(Fill<double> fill) : this(fill(0), fill(1), fill(2), fill(3), fill(4), fill(5), fill(6), public Quadrilateral(Fill<double> fill) : this(fill(0), fill(1), fill(2), fill(3), fill(4), fill(5), fill(6),
fill(7), fill(8), fill(9), fill(10), fill(11)) { } fill(7), fill(8), fill(9), fill(10), fill(11)) { }
public Quadrilateral(Fill<int> fill) : this(fill(0), fill(1), fill(2), fill(3), fill(4), fill(5), fill(6), public Quadrilateral(Fill<int> fill) : this(fill(0), fill(1), fill(2), fill(3), fill(4), fill(5), fill(6),
fill(7), fill(8), fill(9), fill(10), fill(11)) { } fill(7), fill(8), fill(9), fill(10), fill(11)) { }
public Vert this[int index] public Vert this[int index]
{ {
get => index switch get => index switch
{ {
@ -188,40 +179,40 @@ namespace Nerd_STF.Mathematics.Geometry
} }
} }
public static Quadrilateral Absolute(Quadrilateral val) => public static Quadrilateral Absolute(Quadrilateral val) =>
new(Vert.Absolute(val.A), Vert.Absolute(val.B), Vert.Absolute(val.C), Vert.Absolute(val.D)); new(Vert.Absolute(val.A), Vert.Absolute(val.B), Vert.Absolute(val.C), Vert.Absolute(val.D));
public static Quadrilateral Average(params Quadrilateral[] vals) public static Quadrilateral Average(params Quadrilateral[] vals)
{ {
(Vert[] As, Vert[] Bs, Vert[] Cs, Vert[] Ds) = SplitVertArray(vals); (Vert[] As, Vert[] Bs, Vert[] Cs, Vert[] Ds) = SplitVertArray(vals);
return new(Vert.Average(As), Vert.Average(Bs), Vert.Average(Cs), Vert.Average(Ds)); return new(Vert.Average(As), Vert.Average(Bs), Vert.Average(Cs), Vert.Average(Ds));
} }
public static Quadrilateral Ceiling(Quadrilateral val) => public static Quadrilateral Ceiling(Quadrilateral val) =>
new(Vert.Ceiling(val.A), Vert.Ceiling(val.B), Vert.Ceiling(val.C), Vert.Ceiling(val.D)); new(Vert.Ceiling(val.A), Vert.Ceiling(val.B), Vert.Ceiling(val.C), Vert.Ceiling(val.D));
public static Quadrilateral Clamp(Quadrilateral val, Quadrilateral min, Quadrilateral max) => public static Quadrilateral Clamp(Quadrilateral val, Quadrilateral min, Quadrilateral max) =>
new(Vert.Clamp(val.A, min.A, max.A), Vert.Clamp(val.B, min.B, max.B), Vert.Clamp(val.C, min.C, max.C), new(Vert.Clamp(val.A, min.A, max.A), Vert.Clamp(val.B, min.B, max.B), Vert.Clamp(val.C, min.C, max.C),
Vert.Clamp(val.D, min.D, max.D)); Vert.Clamp(val.D, min.D, max.D));
public static Quadrilateral Floor(Quadrilateral val) => public static Quadrilateral Floor(Quadrilateral val) =>
new(Vert.Floor(val.A), Vert.Floor(val.B), Vert.Floor(val.C), Vert.Floor(val.D)); new(Vert.Floor(val.A), Vert.Floor(val.B), Vert.Floor(val.C), Vert.Floor(val.D));
public static Quadrilateral Lerp(Quadrilateral a, Quadrilateral b, double t, bool clamp = true) => public static Quadrilateral Lerp(Quadrilateral a, Quadrilateral b, double t, bool clamp = true) =>
new(Vert.Lerp(a.A, b.A, t, clamp), Vert.Lerp(a.B, b.B, t, clamp), Vert.Lerp(a.C, b.C, t, clamp), new(Vert.Lerp(a.A, b.A, t, clamp), Vert.Lerp(a.B, b.B, t, clamp), Vert.Lerp(a.C, b.C, t, clamp),
Vert.Lerp(a.D, b.D, t, clamp)); Vert.Lerp(a.D, b.D, t, clamp));
public static Quadrilateral Max(params Quadrilateral[] vals) public static Quadrilateral Max(params Quadrilateral[] vals)
{ {
(Vert[] As, Vert[] Bs, Vert[] Cs, Vert[] Ds) = SplitVertArray(vals); (Vert[] As, Vert[] Bs, Vert[] Cs, Vert[] Ds) = SplitVertArray(vals);
return new(Vert.Max(As), Vert.Max(Bs), Vert.Max(Cs), Vert.Max(Ds)); return new(Vert.Max(As), Vert.Max(Bs), Vert.Max(Cs), Vert.Max(Ds));
} }
public static Quadrilateral Median(params Quadrilateral[] vals) public static Quadrilateral Median(params Quadrilateral[] vals)
{ {
(Vert[] As, Vert[] Bs, Vert[] Cs, Vert[] Ds) = SplitVertArray(vals); (Vert[] As, Vert[] Bs, Vert[] Cs, Vert[] Ds) = SplitVertArray(vals);
return new(Vert.Median(As), Vert.Median(Bs), Vert.Median(Cs), Vert.Median(Ds)); return new(Vert.Median(As), Vert.Median(Bs), Vert.Median(Cs), Vert.Median(Ds));
} }
public static Quadrilateral Min(params Quadrilateral[] vals) public static Quadrilateral Min(params Quadrilateral[] vals)
{ {
(Vert[] As, Vert[] Bs, Vert[] Cs, Vert[] Ds) = SplitVertArray(vals); (Vert[] As, Vert[] Bs, Vert[] Cs, Vert[] Ds) = SplitVertArray(vals);
return new(Vert.Min(As), Vert.Min(Bs), Vert.Min(Cs), Vert.Min(Ds)); return new(Vert.Min(As), Vert.Min(Bs), Vert.Min(Cs), Vert.Min(Ds));
} }
public static (Vert[] As, Vert[] Bs, Vert[] Cs, Vert[] Ds) SplitVertArray(params Quadrilateral[] quads) public static (Vert[] As, Vert[] Bs, Vert[] Cs, Vert[] Ds) SplitVertArray(params Quadrilateral[] quads)
{ {
Vert[] a = new Vert[quads.Length], b = new Vert[quads.Length], Vert[] a = new Vert[quads.Length], b = new Vert[quads.Length],
c = new Vert[quads.Length], d = new Vert[quads.Length]; c = new Vert[quads.Length], d = new Vert[quads.Length];
@ -235,7 +226,7 @@ namespace Nerd_STF.Mathematics.Geometry
return (a, b, c, d); return (a, b, c, d);
} }
public static (Line[] ABs, Line[] BCs, Line[] CDs, Line[] DAs) SplitLineArray(params Quadrilateral[] quads) public static (Line[] ABs, Line[] BCs, Line[] CDs, Line[] DAs) SplitLineArray(params Quadrilateral[] quads)
{ {
Line[] ab = new Line[quads.Length], bc = new Line[quads.Length], Line[] ab = new Line[quads.Length], bc = new Line[quads.Length],
cd = new Line[quads.Length], da = new Line[quads.Length]; cd = new Line[quads.Length], da = new Line[quads.Length];
@ -250,7 +241,7 @@ namespace Nerd_STF.Mathematics.Geometry
return (ab, bc, cd, da); return (ab, bc, cd, da);
} }
public static double[] ToDoubleArrayAll(params Quadrilateral[] quads) public static double[] ToDoubleArrayAll(params Quadrilateral[] quads)
{ {
double[] vals = new double[quads.Length * 12]; double[] vals = new double[quads.Length * 12];
for (int i = 0; i < quads.Length; i++) for (int i = 0; i < quads.Length; i++)
@ -271,72 +262,71 @@ namespace Nerd_STF.Mathematics.Geometry
} }
return vals; return vals;
} }
public static List<double> ToDoubleListAll(params Quadrilateral[] quads) => new(ToDoubleArrayAll(quads)); public static List<double> ToDoubleListAll(params Quadrilateral[] quads) => new(ToDoubleArrayAll(quads));
public override bool Equals([NotNullWhen(true)] object? obj) public override bool Equals([NotNullWhen(true)] object? obj)
{ {
if (obj == null || obj.GetType() != typeof(Quadrilateral)) return false; if (obj == null || obj.GetType() != typeof(Quadrilateral)) return false;
return Equals((Quadrilateral)obj); return Equals((Quadrilateral)obj);
} }
public bool Equals(Quadrilateral other) => A == other.A && B == other.B && C == other.C && D == other.D; public bool Equals(Quadrilateral other) => A == other.A && B == other.B && C == other.C && D == other.D;
public override int GetHashCode() => A.GetHashCode() ^ B.GetHashCode() ^ C.GetHashCode() ^ D.GetHashCode(); public override int GetHashCode() => A.GetHashCode() ^ B.GetHashCode() ^ C.GetHashCode() ^ D.GetHashCode();
public override string ToString() => ToString((string?)null); public override string ToString() => ToString((string?)null);
public string ToString(string? provider) => "A: " + A.ToString(provider) + " B: " + B.ToString(provider) public string ToString(string? provider) => "A: " + A.ToString(provider) + " B: " + B.ToString(provider)
+ " C: " + C.ToString(provider) + " D: " + D.ToString(provider); + " C: " + C.ToString(provider) + " D: " + D.ToString(provider);
public string ToString(IFormatProvider provider) => "A: " + A.ToString(provider) + " B: " public string ToString(IFormatProvider provider) => "A: " + A.ToString(provider) + " B: "
+ B.ToString(provider) + " C: " + C.ToString(provider) + " D: " + D.ToString(provider); + B.ToString(provider) + " C: " + C.ToString(provider) + " D: " + D.ToString(provider);
public object Clone() => new Quadrilateral(A, B, C, D); public object Clone() => new Quadrilateral(A, B, C, D);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<Vert> GetEnumerator() public IEnumerator<Vert> GetEnumerator()
{ {
yield return A; yield return A;
yield return B; yield return B;
yield return C; yield return C;
yield return D; yield return D;
} }
public Vert[] ToArray() => new Vert[] { A, B, C, D }; public Vert[] ToArray() => new Vert[] { A, B, C, D };
public List<Vert> ToList() => new() { A, B, C, D }; public List<Vert> ToList() => new() { A, B, C, D };
public double[] ToDoubleArray() => new double[] { A.position.x, A.position.y, A.position.z, public double[] ToDoubleArray() => new double[] { A.position.x, A.position.y, A.position.z,
B.position.x, B.position.y, B.position.z,
C.position.x, C.position.y, C.position.z,
D.position.x, D.position.y, D.position.z };
public List<double> ToDoubleList() => new() { A.position.x, A.position.y, A.position.z,
B.position.x, B.position.y, B.position.z, B.position.x, B.position.y, B.position.z,
C.position.x, C.position.y, C.position.z, C.position.x, C.position.y, C.position.z,
D.position.x, D.position.y, D.position.z }; D.position.x, D.position.y, D.position.z };
public List<double> ToDoubleList() => new() { A.position.x, A.position.y, A.position.z,
B.position.x, B.position.y, B.position.z,
C.position.x, C.position.y, C.position.z,
D.position.x, D.position.y, D.position.z };
public Triangle[] Triangulate() => new Line(A, C).Length > new Line(B, D).Length ? public Triangle[] Triangulate() => new Line(A, C).Length > new Line(B, D).Length ?
new Triangle[] { new(A, B, C), new(C, D, A) } : new Triangle[] { new(B, C, D), new(D, A, B) }; new Triangle[] { new(A, B, C), new(C, D, A) } : new Triangle[] { new(B, C, D), new(D, A, B) };
public static Quadrilateral operator +(Quadrilateral a, Quadrilateral b) => new(a.A + b.A, a.B + b.B, public static Quadrilateral operator +(Quadrilateral a, Quadrilateral b) => new(a.A + b.A, a.B + b.B,
a.C + b.C, a.D + b.D); a.C + b.C, a.D + b.D);
public static Quadrilateral operator +(Quadrilateral a, Vert b) => new(a.A + b, a.B + b, a.C + b, a.D + b); public static Quadrilateral operator +(Quadrilateral a, Vert b) => new(a.A + b, a.B + b, a.C + b, a.D + b);
public static Quadrilateral operator -(Quadrilateral q) => new(-q.A, -q.B, -q.C, -q.D); public static Quadrilateral operator -(Quadrilateral q) => new(-q.A, -q.B, -q.C, -q.D);
public static Quadrilateral operator -(Quadrilateral a, Quadrilateral b) => new(a.A - b.A, a.B - b.B, public static Quadrilateral operator -(Quadrilateral a, Quadrilateral b) => new(a.A - b.A, a.B - b.B,
a.C - b.C, a.D - b.D); a.C - b.C, a.D - b.D);
public static Quadrilateral operator -(Quadrilateral a, Vert b) => new(a.A - b, a.B - b, a.C - b, a.D - b); public static Quadrilateral operator -(Quadrilateral a, Vert b) => new(a.A - b, a.B - b, a.C - b, a.D - b);
public static Quadrilateral operator *(Quadrilateral a, Quadrilateral b) => new(a.A * b.A, a.B * b.B, public static Quadrilateral operator *(Quadrilateral a, Quadrilateral b) => new(a.A * b.A, a.B * b.B,
a.C * b.C, a.D * b.D); a.C * b.C, a.D * b.D);
public static Quadrilateral operator *(Quadrilateral a, Vert b) => new(a.A * b, a.B * b, a.C * b, a.D * b); public static Quadrilateral operator *(Quadrilateral a, Vert b) => new(a.A * b, a.B * b, a.C * b, a.D * b);
public static Quadrilateral operator *(Quadrilateral a, double b) => new(a.A * b, a.B * b, a.C * b, a.D * b); public static Quadrilateral operator *(Quadrilateral a, double b) => new(a.A * b, a.B * b, a.C * b, a.D * b);
public static Quadrilateral operator /(Quadrilateral a, Quadrilateral b) => new(a.A / b.A, a.B / b.B, public static Quadrilateral operator /(Quadrilateral a, Quadrilateral b) => new(a.A / b.A, a.B / b.B,
a.C / b.C, a.D / b.D); a.C / b.C, a.D / b.D);
public static Quadrilateral operator /(Quadrilateral a, Vert b) => new(a.A / b, a.B / b, a.C / b, a.D / b); public static Quadrilateral operator /(Quadrilateral a, Vert b) => new(a.A / b, a.B / b, a.C / b, a.D / b);
public static Quadrilateral operator /(Quadrilateral a, double b) => new(a.A / b, a.B / b, a.C / b, a.D / b); public static Quadrilateral operator /(Quadrilateral a, double b) => new(a.A / b, a.B / b, a.C / b, a.D / b);
public static bool operator ==(Quadrilateral a, Quadrilateral b) => a.Equals(b); public static bool operator ==(Quadrilateral a, Quadrilateral b) => a.Equals(b);
public static bool operator !=(Quadrilateral a, Quadrilateral b) => !a.Equals(b); public static bool operator !=(Quadrilateral a, Quadrilateral b) => !a.Equals(b);
public static implicit operator Quadrilateral(Fill<Vert> fill) => new(fill); public static implicit operator Quadrilateral(Fill<Vert> fill) => new(fill);
public static implicit operator Quadrilateral(Fill<Double3> fill) => new(fill); public static implicit operator Quadrilateral(Fill<Double3> fill) => new(fill);
public static implicit operator Quadrilateral(Fill<Int3> fill) => new(fill); public static implicit operator Quadrilateral(Fill<Int3> fill) => new(fill);
public static implicit operator Quadrilateral(Fill<Line> fill) => new(fill); public static implicit operator Quadrilateral(Fill<Line> fill) => new(fill);
public static implicit operator Quadrilateral(Fill<double> fill) => new(fill); public static implicit operator Quadrilateral(Fill<double> fill) => new(fill);
public static implicit operator Quadrilateral(Fill<int> fill) => new(fill); public static implicit operator Quadrilateral(Fill<int> fill) => new(fill);
public static explicit operator Quadrilateral(Polygon poly) => new(poly.Lines[0], poly.Lines[1], public static explicit operator Quadrilateral(Polygon poly) => new(poly.Lines[0], poly.Lines[1],
poly.Lines[2], poly.Lines[3]); poly.Lines[2], poly.Lines[3]);
}
} }

View File

@ -1,126 +1,118 @@
using System; namespace Nerd_STF.Mathematics.Geometry;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Nerd_STF.Mathematics.Geometry public struct Sphere : ICloneable, IClosest<Vert>, IComparable<Sphere>, IComparable<double>, IContainer<Vert>,
IEquatable<Sphere>, IEquatable<double>
{ {
public struct Sphere : ICloneable, IClosest<Vert>, IComparable<Sphere>, IComparable<double>, IContainer<Vert>, public static Sphere Unit => new(Vert.Zero, 1);
IEquatable<Sphere>, IEquatable<double>
public Vert center;
public double radius;
public double SurfaceArea => 4 * Mathf.Pi * radius * radius;
public double Volume => 4 / 3 * (Mathf.Pi * radius * radius * radius);
public static Sphere FromDiameter(Vert a, Vert b) => new(Vert.Average(a, b), (a - b).Magnitude / 2);
public static Sphere FromRadius(Vert center, Vert radius) => new(center, (center - radius).Magnitude);
public Sphere(Vert center, double radius)
{ {
public static Sphere Unit => new(Vert.Zero, 1); this.center = center;
this.radius = radius;
public Vert center;
public double radius;
public double SurfaceArea => 4 * Mathf.Pi * radius * radius;
public double Volume => 4 / 3 * (Mathf.Pi * radius * radius * radius);
public static Sphere FromDiameter(Vert a, Vert b) => new(Vert.Average(a, b), (a - b).Magnitude / 2);
public static Sphere FromRadius(Vert center, Vert radius) => new(center, (center - radius).Magnitude);
public Sphere(Vert center, double radius)
{
this.center = center;
this.radius = radius;
}
public Sphere(double cX, double cY, double radius) : this(new Vert(cX, cY), radius) { }
public Sphere(double cX, double cY, double cZ, double radius) : this(new Vert(cX, cY, cZ), radius) { }
public Sphere(Fill<double> fill, double radius) : this(new Vert(fill), radius) { }
public Sphere(Fill<double> fill) : this(new Vert(fill), fill(3)) { }
public Sphere(Fill<int> fill, double radius) : this(new Vert(fill), radius) { }
public Sphere(Fill<int> fill) : this(new Vert(fill), fill(3)) { }
public Sphere(Fill<Vert> fill, double radius) : this(fill(0), radius) { }
public Sphere(Fill<Vert> fillA, Fill<double> fillB) : this(fillA(0), fillB(0)) { }
public static Sphere Average(params Sphere[] vals)
{
(Vert[] centers, double[] radii) = SplitArray(vals);
return new(Vert.Average(centers), Mathf.Average(radii));
}
public static Sphere Ceiling(Sphere val) => new(Vert.Ceiling(val.center), Mathf.Ceiling(val.radius));
public static Sphere Clamp(Sphere val, Sphere min, Sphere max) =>
new(Vert.Clamp(val.center, min.center, max.center), Mathf.Clamp(val.radius, min.radius, max.radius));
public static Sphere Floor(Sphere val) => new(Vert.Floor(val.center), Mathf.Floor(val.radius));
public static Sphere Lerp(Sphere a, Sphere b, double t, bool clamp = true) =>
new(Vert.Lerp(a.center, b.center, t, clamp), Mathf.Lerp(a.radius, b.radius, t, clamp));
public static Sphere Max(params Sphere[] vals)
{
(Vert[] centers, double[] radii) = SplitArray(vals);
return new(Vert.Max(centers), Mathf.Max(radii));
}
public static Sphere Median(params Sphere[] vals)
{
(Vert[] centers, double[] radii) = SplitArray(vals);
return new(Vert.Median(centers), Mathf.Median(radii));
}
public static Sphere Min(params Sphere[] vals)
{
(Vert[] centers, double[] radii) = SplitArray(vals);
return new(Vert.Min(centers), Mathf.Min(radii));
}
public static (Vert[] centers, double[] radii) SplitArray(params Sphere[] spheres)
{
Vert[] centers = new Vert[spheres.Length];
double[] radii = new double[spheres.Length];
for (int i = 0; i < spheres.Length; i++)
{
centers[i] = spheres[i].center;
radii[i] = spheres[i].radius;
}
return (centers, radii);
}
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null) return false;
Type type = obj.GetType();
if (type == typeof(Sphere)) return Equals((Sphere)obj);
if (type == typeof(double)) return Equals((double)obj);
return false;
}
public bool Equals(double other) => Volume == other;
public bool Equals(Sphere other) => center == other.center && radius == other.radius;
public override int GetHashCode() => center.GetHashCode() ^ radius.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) => "Center: " + center.ToString(provider)
+ " Radius: " + radius.ToString(provider);
public string ToString(IFormatProvider provider) => "Center: " + center.ToString(provider)
+ " Radius: " + radius.ToString(provider);
public object Clone() => new Sphere(center, radius);
public int CompareTo(Sphere sphere) => Volume.CompareTo(sphere.Volume);
public int CompareTo(double volume) => Volume.CompareTo(volume);
public bool Contains(Vert vert) => (center - vert).Magnitude <= radius;
public Vert ClosestTo(Vert vert) => Contains(vert) ? vert : ((vert - center).Normalized * radius) + vert;
public static Sphere operator +(Sphere a, Sphere b) => new(a.center + b.center, a.radius + b.radius);
public static Sphere operator +(Sphere a, Vert b) => new(a.center + b, a.radius);
public static Sphere operator +(Sphere a, double b) => new(a.center, a.radius + b);
public static Sphere operator -(Sphere a, Sphere b) => new(a.center + b.center, a.radius + b.radius);
public static Sphere operator -(Sphere a, Vert b) => new(a.center + b, a.radius);
public static Sphere operator -(Sphere a, double b) => new(a.center, a.radius + b);
public static Sphere operator *(Sphere a, Sphere b) => new(a.center * b.center, a.radius * b.radius);
public static Sphere operator *(Sphere a, double b) => new(a.center * b, a.radius * b);
public static Sphere operator /(Sphere a, Sphere b) => new(a.center * b.center, a.radius * b.radius);
public static Sphere operator /(Sphere a, double b) => new(a.center * b, a.radius * b);
public static bool operator ==(Sphere a, Sphere b) => a.Equals(b);
public static bool operator !=(Sphere a, Sphere b) => !a.Equals(b);
public static bool operator ==(Sphere a, double b) => a.Equals(b);
public static bool operator !=(Sphere a, double b) => !a.Equals(b);
public static bool operator >(Sphere a, Sphere b) => a.CompareTo(b) > 0;
public static bool operator <(Sphere a, Sphere b) => a.CompareTo(b) < 0;
public static bool operator >(Sphere a, double b) => a.CompareTo(b) > 0;
public static bool operator <(Sphere a, double b) => a.CompareTo(b) < 0;
public static bool operator >=(Sphere a, Sphere b) => a > b || a == b;
public static bool operator <=(Sphere a, Sphere b) => a < b || a == b;
public static bool operator >=(Sphere a, double b) => a > b || a == b;
public static bool operator <=(Sphere a, double b) => a < b || a == b;
} }
public Sphere(double cX, double cY, double radius) : this(new Vert(cX, cY), radius) { }
public Sphere(double cX, double cY, double cZ, double radius) : this(new Vert(cX, cY, cZ), radius) { }
public Sphere(Fill<double> fill, double radius) : this(new Vert(fill), radius) { }
public Sphere(Fill<double> fill) : this(new Vert(fill), fill(3)) { }
public Sphere(Fill<int> fill, double radius) : this(new Vert(fill), radius) { }
public Sphere(Fill<int> fill) : this(new Vert(fill), fill(3)) { }
public Sphere(Fill<Vert> fill, double radius) : this(fill(0), radius) { }
public Sphere(Fill<Vert> fillA, Fill<double> fillB) : this(fillA(0), fillB(0)) { }
public static Sphere Average(params Sphere[] vals)
{
(Vert[] centers, double[] radii) = SplitArray(vals);
return new(Vert.Average(centers), Mathf.Average(radii));
}
public static Sphere Ceiling(Sphere val) => new(Vert.Ceiling(val.center), Mathf.Ceiling(val.radius));
public static Sphere Clamp(Sphere val, Sphere min, Sphere max) =>
new(Vert.Clamp(val.center, min.center, max.center), Mathf.Clamp(val.radius, min.radius, max.radius));
public static Sphere Floor(Sphere val) => new(Vert.Floor(val.center), Mathf.Floor(val.radius));
public static Sphere Lerp(Sphere a, Sphere b, double t, bool clamp = true) =>
new(Vert.Lerp(a.center, b.center, t, clamp), Mathf.Lerp(a.radius, b.radius, t, clamp));
public static Sphere Max(params Sphere[] vals)
{
(Vert[] centers, double[] radii) = SplitArray(vals);
return new(Vert.Max(centers), Mathf.Max(radii));
}
public static Sphere Median(params Sphere[] vals)
{
(Vert[] centers, double[] radii) = SplitArray(vals);
return new(Vert.Median(centers), Mathf.Median(radii));
}
public static Sphere Min(params Sphere[] vals)
{
(Vert[] centers, double[] radii) = SplitArray(vals);
return new(Vert.Min(centers), Mathf.Min(radii));
}
public static (Vert[] centers, double[] radii) SplitArray(params Sphere[] spheres)
{
Vert[] centers = new Vert[spheres.Length];
double[] radii = new double[spheres.Length];
for (int i = 0; i < spheres.Length; i++)
{
centers[i] = spheres[i].center;
radii[i] = spheres[i].radius;
}
return (centers, radii);
}
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null) return false;
Type type = obj.GetType();
if (type == typeof(Sphere)) return Equals((Sphere)obj);
if (type == typeof(double)) return Equals((double)obj);
return false;
}
public bool Equals(double other) => Volume == other;
public bool Equals(Sphere other) => center == other.center && radius == other.radius;
public override int GetHashCode() => center.GetHashCode() ^ radius.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) => "Center: " + center.ToString(provider)
+ " Radius: " + radius.ToString(provider);
public string ToString(IFormatProvider provider) => "Center: " + center.ToString(provider)
+ " Radius: " + radius.ToString(provider);
public object Clone() => new Sphere(center, radius);
public int CompareTo(Sphere sphere) => Volume.CompareTo(sphere.Volume);
public int CompareTo(double volume) => Volume.CompareTo(volume);
public bool Contains(Vert vert) => (center - vert).Magnitude <= radius;
public Vert ClosestTo(Vert vert) => Contains(vert) ? vert : ((vert - center).Normalized * radius) + vert;
public static Sphere operator +(Sphere a, Sphere b) => new(a.center + b.center, a.radius + b.radius);
public static Sphere operator +(Sphere a, Vert b) => new(a.center + b, a.radius);
public static Sphere operator +(Sphere a, double b) => new(a.center, a.radius + b);
public static Sphere operator -(Sphere a, Sphere b) => new(a.center + b.center, a.radius + b.radius);
public static Sphere operator -(Sphere a, Vert b) => new(a.center + b, a.radius);
public static Sphere operator -(Sphere a, double b) => new(a.center, a.radius + b);
public static Sphere operator *(Sphere a, Sphere b) => new(a.center * b.center, a.radius * b.radius);
public static Sphere operator *(Sphere a, double b) => new(a.center * b, a.radius * b);
public static Sphere operator /(Sphere a, Sphere b) => new(a.center * b.center, a.radius * b.radius);
public static Sphere operator /(Sphere a, double b) => new(a.center * b, a.radius * b);
public static bool operator ==(Sphere a, Sphere b) => a.Equals(b);
public static bool operator !=(Sphere a, Sphere b) => !a.Equals(b);
public static bool operator ==(Sphere a, double b) => a.Equals(b);
public static bool operator !=(Sphere a, double b) => !a.Equals(b);
public static bool operator >(Sphere a, Sphere b) => a.CompareTo(b) > 0;
public static bool operator <(Sphere a, Sphere b) => a.CompareTo(b) < 0;
public static bool operator >(Sphere a, double b) => a.CompareTo(b) > 0;
public static bool operator <(Sphere a, double b) => a.CompareTo(b) < 0;
public static bool operator >=(Sphere a, Sphere b) => a > b || a == b;
public static bool operator <=(Sphere a, Sphere b) => a < b || a == b;
public static bool operator >=(Sphere a, double b) => a > b || a == b;
public static bool operator <=(Sphere a, double b) => a < b || a == b;
} }

View File

@ -1,12 +1,8 @@
using Nerd_STF.Exceptions; namespace Nerd_STF.Mathematics.Geometry;
using System.Collections;
using System.Diagnostics.CodeAnalysis;
namespace Nerd_STF.Mathematics.Geometry public struct Triangle : ICloneable, IEquatable<Triangle>, IGroup<Vert>
{ {
public struct Triangle : ICloneable, IEquatable<Triangle>, IGroup<Vert> public Vert A
{
public Vert A
{ {
get => p_a; get => p_a;
set set
@ -16,7 +12,7 @@ namespace Nerd_STF.Mathematics.Geometry
p_ca.b = value; p_ca.b = value;
} }
} }
public Vert B public Vert B
{ {
get => p_b; get => p_b;
set set
@ -26,7 +22,7 @@ namespace Nerd_STF.Mathematics.Geometry
p_bc.a = value; p_bc.a = value;
} }
} }
public Vert C public Vert C
{ {
get => p_c; get => p_c;
set set
@ -36,7 +32,7 @@ namespace Nerd_STF.Mathematics.Geometry
p_ca.a = value; p_ca.a = value;
} }
} }
public Line AB public Line AB
{ {
get => p_ab; get => p_ab;
set set
@ -48,7 +44,7 @@ namespace Nerd_STF.Mathematics.Geometry
p_ca.b = value.a; p_ca.b = value.a;
} }
} }
public Line BC public Line BC
{ {
get => p_bc; get => p_bc;
set set
@ -60,7 +56,7 @@ namespace Nerd_STF.Mathematics.Geometry
p_ab.b = value.a; p_ab.b = value.a;
} }
} }
public Line CA public Line CA
{ {
get => p_ca; get => p_ca;
set set
@ -73,208 +69,202 @@ namespace Nerd_STF.Mathematics.Geometry
} }
} }
private Vert p_a, p_b, p_c; private Vert p_a, p_b, p_c;
private Line p_ab, p_bc, p_ca; private Line p_ab, p_bc, p_ca;
public double Area => Mathf.Absolute((A.position.x * B.position.y) + (B.position.x * C.position.y) + (C.position.x * A.position.y) - public double Area => Mathf.Absolute((A.position.x * B.position.y) + (B.position.x * C.position.y) + (C.position.x * A.position.y) -
((B.position.x * A.position.y) + (C.position.x * B.position.y) + (A.position.x * C.position.y))) * 0.5; ((B.position.x * A.position.y) + (C.position.x * B.position.y) + (A.position.x * C.position.y))) * 0.5;
public double Perimeter => AB.Length + BC.Length + CA.Length; public double Perimeter => AB.Length + BC.Length + CA.Length;
public Triangle(Vert a, Vert b, Vert c) public Triangle(Vert a, Vert b, Vert c)
{
p_a = a;
p_b = b;
p_c = c;
p_ab = new(a, b);
p_bc = new(b, c);
p_ca = new(c, a);
}
public Triangle(Line ab, Line bc, Line ca)
{
if (ab.a != ca.b || ab.b != bc.a || bc.b != ca.a)
throw new DisconnectedLinesException(ab, bc, ca);
p_a = ab.a;
p_b = bc.a;
p_c = ca.a;
p_ab = ab;
p_bc = bc;
p_ca = ca;
}
public Triangle(double x1, double y1, double x2, double y2, double x3, double y3)
: this(new Vert(x1, y1), new Vert(x2, y2), new Vert(x3, y3)) { }
public Triangle(double x1, double y1, double z1, double x2, double y2, double z2, double x3, double y3,
double z3) : this(new Vert(x1, y1, z1), new Vert(x2, y2, z2), new Vert(x3, y3, z3)) { }
public Triangle(Fill<Double3> fill) : this(fill(0), fill(1), fill(2)) { }
public Triangle(Fill<Int3> fill) : this(fill(0), fill(1), fill(2)) { }
public Triangle(Fill<Vert> fill) : this(fill(0), fill(1), fill(2)) { }
public Triangle(Fill<Line> fill) : this(fill(0), fill(1), fill(2)) { }
public Triangle(Fill<double> fill) : this(fill(0), fill(1), fill(2), fill(3), fill(4), fill(5), fill(6),
fill(7), fill(8)) { }
public Triangle(Fill<int> fill) : this(fill(0), fill(1), fill(2), fill(3), fill(4), fill(5), fill(6),
fill(7), fill(8)) { }
public Vert this[int index]
{
get => index switch
{ {
p_a = a; 0 => A,
p_b = b; 1 => B,
p_c = c; 2 => C,
p_ab = new(a, b); _ => throw new IndexOutOfRangeException(nameof(index)),
p_bc = new(b, c); };
p_ca = new(c, a); set
}
public Triangle(Line ab, Line bc, Line ca)
{ {
if (ab.a != ca.b || ab.b != bc.a || bc.b != ca.a) switch (index)
throw new DisconnectedLinesException(ab, bc, ca);
p_a = ab.a;
p_b = bc.a;
p_c = ca.a;
p_ab = ab;
p_bc = bc;
p_ca = ca;
}
public Triangle(double x1, double y1, double x2, double y2, double x3, double y3)
: this(new Vert(x1, y1), new Vert(x2, y2), new Vert(x3, y3)) { }
public Triangle(double x1, double y1, double z1, double x2, double y2, double z2, double x3, double y3,
double z3) : this(new Vert(x1, y1, z1), new Vert(x2, y2, z2), new Vert(x3, y3, z3)) { }
public Triangle(Fill<Double3> fill) : this(fill(0), fill(1), fill(2)) { }
public Triangle(Fill<Int3> fill) : this(fill(0), fill(1), fill(2)) { }
public Triangle(Fill<Vert> fill) : this(fill(0), fill(1), fill(2)) { }
public Triangle(Fill<Line> fill) : this(fill(0), fill(1), fill(2)) { }
public Triangle(Fill<double> fill) : this(fill(0), fill(1), fill(2), fill(3), fill(4), fill(5), fill(6),
fill(7), fill(8))
{ }
public Triangle(Fill<int> fill) : this(fill(0), fill(1), fill(2), fill(3), fill(4), fill(5), fill(6),
fill(7), fill(8))
{ }
public Vert this[int index]
{
get => index switch
{ {
0 => A, case 0:
1 => B, A = value;
2 => C, break;
_ => throw new IndexOutOfRangeException(nameof(index)),
};
set
{
switch (index)
{
case 0:
A = value;
break;
case 1: case 1:
B = value; B = value;
break; break;
case 2: case 2:
C = value; C = value;
break; break;
default: throw new IndexOutOfRangeException(nameof(index)); default: throw new IndexOutOfRangeException(nameof(index));
}
} }
} }
}
public static Triangle Absolute(Triangle val) => public static Triangle Absolute(Triangle val) =>
new(Vert.Absolute(val.A), Vert.Absolute(val.B), Vert.Absolute(val.C)); new(Vert.Absolute(val.A), Vert.Absolute(val.B), Vert.Absolute(val.C));
public static Triangle Average(params Triangle[] vals) public static Triangle Average(params Triangle[] vals)
{
(Vert[] As, Vert[] Bs, Vert[] Cs) = SplitVertArray(vals);
return new(Vert.Average(As), Vert.Average(Bs), Vert.Average(Cs));
}
public static Triangle Ceiling(Triangle val) =>
new(Vert.Ceiling(val.A), Vert.Ceiling(val.B), Vert.Ceiling(val.C));
public static Triangle Clamp(Triangle val, Triangle min, Triangle max) =>
new(Vert.Clamp(val.A, min.A, max.A), Vert.Clamp(val.B, min.B, max.B), Vert.Clamp(val.C, min.C, max.C));
public static Triangle Floor(Triangle val) =>
new(Vert.Floor(val.A), Vert.Floor(val.B), Vert.Floor(val.C));
public static Triangle Lerp(Triangle a, Triangle b, double t, bool clamp = true) =>
new(Vert.Lerp(a.A, b.A, t, clamp), Vert.Lerp(a.B, b.B, t, clamp), Vert.Lerp(a.C, b.C, t, clamp));
public static Triangle Max(params Triangle[] vals)
{
(Vert[] As, Vert[] Bs, Vert[] Cs) = SplitVertArray(vals);
return new(Vert.Max(As), Vert.Max(Bs), Vert.Max(Cs));
}
public static Triangle Median(params Triangle[] vals)
{
(Vert[] As, Vert[] Bs, Vert[] Cs) = SplitVertArray(vals);
return new(Vert.Median(As), Vert.Median(Bs), Vert.Median(Cs));
}
public static Triangle Min(params Triangle[] vals)
{
(Vert[] As, Vert[] Bs, Vert[] Cs) = SplitVertArray(vals);
return new(Vert.Min(As), Vert.Min(Bs), Vert.Min(Cs));
}
public static (Vert[] As, Vert[] Bs, Vert[] Cs) SplitVertArray(params Triangle[] tris)
{
Vert[] a = new Vert[tris.Length], b = new Vert[tris.Length], c = new Vert[tris.Length];
for (int i = 0; i < tris.Length; i++)
{ {
(Vert[] As, Vert[] Bs, Vert[] Cs) = SplitVertArray(vals); a[i] = tris[i].A;
return new(Vert.Average(As), Vert.Average(Bs), Vert.Average(Cs)); b[i] = tris[i].B;
c[i] = tris[i].C;
} }
public static Triangle Ceiling(Triangle val) => return (a, b, c);
new(Vert.Ceiling(val.A), Vert.Ceiling(val.B), Vert.Ceiling(val.C)); }
public static Triangle Clamp(Triangle val, Triangle min, Triangle max) => public static (Line[] ABs, Line[] BCs, Line[] CAs) SplitLineArray(params Triangle[] tris)
new(Vert.Clamp(val.A, min.A, max.A), Vert.Clamp(val.B, min.B, max.B), Vert.Clamp(val.C, min.C, max.C)); {
public static Triangle Floor(Triangle val) => Line[] ab = new Line[tris.Length], bc = new Line[tris.Length], ca = new Line[tris.Length];
new(Vert.Floor(val.A), Vert.Floor(val.B), Vert.Floor(val.C)); for (int i = 0; i < tris.Length; i++)
public static Triangle Lerp(Triangle a, Triangle b, double t, bool clamp = true) =>
new(Vert.Lerp(a.A, b.A, t, clamp), Vert.Lerp(a.B, b.B, t, clamp), Vert.Lerp(a.C, b.C, t, clamp));
public static Triangle Max(params Triangle[] vals)
{ {
(Vert[] As, Vert[] Bs, Vert[] Cs) = SplitVertArray(vals); ab[i] = tris[i].AB;
return new(Vert.Max(As), Vert.Max(Bs), Vert.Max(Cs)); bc[i] = tris[i].BC;
ca[i] = tris[i].CA;
} }
public static Triangle Median(params Triangle[] vals) return (ab, bc, ca);
}
public static double[] ToDoubleArrayAll(params Triangle[] tris)
{
double[] vals = new double[tris.Length * 9];
for (int i = 0; i < tris.Length; i++)
{ {
(Vert[] As, Vert[] Bs, Vert[] Cs) = SplitVertArray(vals); int pos = i * 9;
return new(Vert.Median(As), Vert.Median(Bs), Vert.Median(Cs)); vals[pos + 0] = tris[i].A.position.x;
} vals[pos + 1] = tris[i].A.position.y;
public static Triangle Min(params Triangle[] vals) vals[pos + 2] = tris[i].A.position.z;
{ vals[pos + 3] = tris[i].B.position.x;
(Vert[] As, Vert[] Bs, Vert[] Cs) = SplitVertArray(vals); vals[pos + 4] = tris[i].B.position.y;
return new(Vert.Min(As), Vert.Min(Bs), Vert.Min(Cs)); vals[pos + 5] = tris[i].B.position.z;
vals[pos + 6] = tris[i].C.position.x;
vals[pos + 7] = tris[i].C.position.y;
vals[pos + 8] = tris[i].C.position.z;
} }
return vals;
}
public static List<double> ToDoubleListAll(params Triangle[] tris) => new(ToDoubleArrayAll(tris));
public static (Vert[] As, Vert[] Bs, Vert[] Cs) SplitVertArray(params Triangle[] tris) public override bool Equals([NotNullWhen(true)] object? obj)
{ {
Vert[] a = new Vert[tris.Length], b = new Vert[tris.Length], c = new Vert[tris.Length]; if (obj == null || obj.GetType() != typeof(Triangle)) return false;
for (int i = 0; i < tris.Length; i++) return Equals((Triangle)obj);
{ }
a[i] = tris[i].A; public bool Equals(Triangle other) => A == other.A && B == other.B && C == other.C;
b[i] = tris[i].B; public override int GetHashCode() => A.GetHashCode() ^ B.GetHashCode() ^ C.GetHashCode();
c[i] = tris[i].C; public override string ToString() => ToString((string?)null);
} public string ToString(string? provider) =>
"A: " + A.ToString(provider) + " B: " + B.ToString(provider) + " C: " + C.ToString(provider);
public string ToString(IFormatProvider provider) =>
"A: " + A.ToString(provider) + " B: " + B.ToString(provider) + " C: " + C.ToString(provider);
return (a, b, c); public object Clone() => new Triangle(A, B, C);
}
public static (Line[] ABs, Line[] BCs, Line[] CAs) SplitLineArray(params Triangle[] tris)
{
Line[] ab = new Line[tris.Length], bc = new Line[tris.Length], ca = new Line[tris.Length];
for (int i = 0; i < tris.Length; i++)
{
ab[i] = tris[i].AB;
bc[i] = tris[i].BC;
ca[i] = tris[i].CA;
}
return (ab, bc, ca); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
} public IEnumerator<Vert> GetEnumerator()
{
yield return A;
yield return B;
yield return C;
}
public static double[] ToDoubleArrayAll(params Triangle[] tris) public Vert[] ToArray() => new Vert[] { A, B, C };
{ public List<Vert> ToList() => new() { A, B, C };
double[] vals = new double[tris.Length * 9];
for (int i = 0; i < tris.Length; i++)
{
int pos = i * 9;
vals[pos + 0] = tris[i].A.position.x;
vals[pos + 1] = tris[i].A.position.y;
vals[pos + 2] = tris[i].A.position.z;
vals[pos + 3] = tris[i].B.position.x;
vals[pos + 4] = tris[i].B.position.y;
vals[pos + 5] = tris[i].B.position.z;
vals[pos + 6] = tris[i].C.position.x;
vals[pos + 7] = tris[i].C.position.y;
vals[pos + 8] = tris[i].C.position.z;
}
return vals;
}
public static List<double> ToDoubleListAll(params Triangle[] tris) => new(ToDoubleArrayAll(tris));
public override bool Equals([NotNullWhen(true)] object? obj) public double[] ToDoubleArray() => new double[] { A.position.x, A.position.y, A.position.z,
{
if (obj == null || obj.GetType() != typeof(Triangle)) return false;
return Equals((Triangle)obj);
}
public bool Equals(Triangle other) => A == other.A && B == other.B && C == other.C;
public override int GetHashCode() => A.GetHashCode() ^ B.GetHashCode() ^ C.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
"A: " + A.ToString(provider) + " B: " + B.ToString(provider) + " C: " + C.ToString(provider);
public string ToString(IFormatProvider provider) =>
"A: " + A.ToString(provider) + " B: " + B.ToString(provider) + " C: " + C.ToString(provider);
public object Clone() => new Triangle(A, B, C);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<Vert> GetEnumerator()
{
yield return A;
yield return B;
yield return C;
}
public Vert[] ToArray() => new Vert[] { A, B, C };
public List<Vert> ToList() => new() { A, B, C };
public double[] ToDoubleArray() => new double[] { A.position.x, A.position.y, A.position.z,
B.position.x, B.position.y, B.position.z,
C.position.x, C.position.y, C.position.z };
public List<double> ToDoubleList() => new() { A.position.x, A.position.y, A.position.z,
B.position.x, B.position.y, B.position.z, B.position.x, B.position.y, B.position.z,
C.position.x, C.position.y, C.position.z }; C.position.x, C.position.y, C.position.z };
public List<double> ToDoubleList() => new() { A.position.x, A.position.y, A.position.z,
B.position.x, B.position.y, B.position.z,
C.position.x, C.position.y, C.position.z };
public static Triangle operator +(Triangle a, Triangle b) => new(a.A + b.A, a.B + b.B, a.C + b.C);
public static Triangle operator +(Triangle a, Vert b) => new(a.A + b, a.B + b, a.C + b);
public static Triangle operator -(Triangle t) => new(-t.A, -t.B, -t.C);
public static Triangle operator -(Triangle a, Triangle b) => new(a.A - b.A, a.B - b.B, a.C - b.C);
public static Triangle operator -(Triangle a, Vert b) => new(a.A - b, a.B - b, a.C - b);
public static Triangle operator *(Triangle a, Triangle b) => new(a.A * b.A, a.B * b.B, a.C * b.C);
public static Triangle operator *(Triangle a, Vert b) => new(a.A * b, a.B * b, a.C * b);
public static Triangle operator *(Triangle a, double b) => new(a.A * b, a.B * b, a.C * b);
public static Triangle operator /(Triangle a, Triangle b) => new(a.A / b.A, a.B / b.B, a.C / b.C);
public static Triangle operator /(Triangle a, Vert b) => new(a.A / b, a.B / b, a.C / b);
public static Triangle operator /(Triangle a, double b) => new(a.A / b, a.B / b, a.C / b);
public static bool operator ==(Triangle a, Triangle b) => a.Equals(b);
public static bool operator !=(Triangle a, Triangle b) => !a.Equals(b);
public static Triangle operator +(Triangle a, Triangle b) => new(a.A + b.A, a.B + b.B, a.C + b.C); public static implicit operator Triangle(Fill<Vert> fill) => new(fill);
public static Triangle operator +(Triangle a, Vert b) => new(a.A + b, a.B + b, a.C + b); public static implicit operator Triangle(Fill<Double3> fill) => new(fill);
public static Triangle operator -(Triangle t) => new(-t.A, -t.B, -t.C); public static implicit operator Triangle(Fill<Int3> fill) => new(fill);
public static Triangle operator -(Triangle a, Triangle b) => new(a.A - b.A, a.B - b.B, a.C - b.C); public static implicit operator Triangle(Fill<Line> fill) => new(fill);
public static Triangle operator -(Triangle a, Vert b) => new(a.A - b, a.B - b, a.C - b); public static implicit operator Triangle(Fill<double> fill) => new(fill);
public static Triangle operator *(Triangle a, Triangle b) => new(a.A * b.A, a.B * b.B, a.C * b.C); public static implicit operator Triangle(Fill<int> fill) => new(fill);
public static Triangle operator *(Triangle a, Vert b) => new(a.A * b, a.B * b, a.C * b); public static explicit operator Triangle(Polygon poly) => new(poly.Lines[0], poly.Lines[1], poly.Lines[2]);
public static Triangle operator *(Triangle a, double b) => new(a.A * b, a.B * b, a.C * b);
public static Triangle operator /(Triangle a, Triangle b) => new(a.A / b.A, a.B / b.B, a.C / b.C);
public static Triangle operator /(Triangle a, Vert b) => new(a.A / b, a.B / b, a.C / b);
public static Triangle operator /(Triangle a, double b) => new(a.A / b, a.B / b, a.C / b);
public static bool operator ==(Triangle a, Triangle b) => a.Equals(b);
public static bool operator !=(Triangle a, Triangle b) => !a.Equals(b);
public static implicit operator Triangle(Fill<Vert> fill) => new(fill);
public static implicit operator Triangle(Fill<Double3> fill) => new(fill);
public static implicit operator Triangle(Fill<Int3> fill) => new(fill);
public static implicit operator Triangle(Fill<Line> fill) => new(fill);
public static implicit operator Triangle(Fill<double> fill) => new(fill);
public static implicit operator Triangle(Fill<int> fill) => new(fill);
public static explicit operator Triangle(Polygon poly) => new(poly.Lines[0], poly.Lines[1], poly.Lines[2]);
}
} }

View File

@ -1,102 +1,98 @@
using System.Collections; namespace Nerd_STF.Mathematics.Geometry;
using System.Diagnostics.CodeAnalysis;
namespace Nerd_STF.Mathematics.Geometry public struct Vert : ICloneable, IEquatable<Vert>, IGroup<double>
{ {
public struct Vert : ICloneable, IEquatable<Vert>, IGroup<double> public static Vert Back => new(0, 0, -1);
public static Vert Down => new(0, -1, 0);
public static Vert Forward => new(0, 0, 1);
public static Vert Left => new(-1, 0, 0);
public static Vert Right => new(1, 0, 0);
public static Vert Up => new(0, 1, 0);
public static Vert One => new(1, 1, 1);
public static Vert Zero => new(0, 0, 0);
public double Magnitude => position.Magnitude;
public Vert Normalized => new(this / Magnitude);
public Double3 position;
public Vert(Double2 pos) : this(pos.x, pos.y, 0) { }
public Vert(Double3 pos) => position = pos;
public Vert(double x, double y) : this(new Double2(x, y)) { }
public Vert(double x, double y, double z) : this(new Double3(x, y, z)) { }
public Vert(Fill<double> fill) : this(new Double3(fill)) { }
public Vert(Fill<int> fill) : this(new Double3(fill)) { }
public double this[int index]
{ {
public static Vert Back => new(0, 0, -1); get => position[index];
public static Vert Down => new(0, -1, 0); set => position[index] = value;
public static Vert Forward => new(0, 0, 1);
public static Vert Left => new(-1, 0, 0);
public static Vert Right => new(1, 0, 0);
public static Vert Up => new(0, 1, 0);
public static Vert One => new(1, 1, 1);
public static Vert Zero => new(0, 0, 0);
public double Magnitude => position.Magnitude;
public Vert Normalized => new(this / Magnitude);
public Double3 position;
public Vert(Double2 pos) : this(pos.x, pos.y, 0) { }
public Vert(Double3 pos) => position = pos;
public Vert(double x, double y) : this(new Double2(x, y)) { }
public Vert(double x, double y, double z) : this(new Double3(x, y, z)) { }
public Vert(Fill<double> fill) : this(new Double3(fill)) { }
public Vert(Fill<int> fill) : this(new Double3(fill)) { }
public double this[int index]
{
get => position[index];
set => position[index] = value;
}
public static Vert Absolute(Vert val) => new(Double3.Absolute(val.position));
public static Vert Average(params Vert[] vals) => Double3.Average(ToDouble3Array(vals));
public static Vert Ceiling(Vert val) => new(Double3.Ceiling(val.position));
public static Vert Clamp(Vert val, Vert min, Vert max) =>
new(Double3.Clamp(val.position, min.position, max.position));
public static Vert ClampMagnitude(Vert val, double minMag, double maxMag) =>
new(Double3.ClampMagnitude(val.position, minMag, maxMag));
public static Vert Cross(Vert a, Vert b, bool normalized = false) =>
new(Double3.Cross(a.position, b.position, normalized));
public static double Dot(Vert a, Vert b) => Double3.Dot(a.position, b.position);
public static double Dot(params Vert[] vals) => Double3.Dot(ToDouble3Array(vals));
public static Vert Floor(Vert val) => new(Double3.Floor(val.position));
public static Vert Lerp(Vert a, Vert b, double t, bool clamp = true) =>
new(Double3.Lerp(a.position, b.position, t, clamp));
public static Vert Median(params Vert[] vals) =>
Double3.Median(ToDouble3Array(vals));
public static Vert Max(params Vert[] vals) =>
Double3.Max(ToDouble3Array(vals));
public static Vert Min(params Vert[] vals) =>
Double3.Min(ToDouble3Array(vals));
public static Double3[] ToDouble3Array(params Vert[] vals)
{
Double3[] doubles = new Double3[vals.Length];
for (int i = 0; i < vals.Length; i++) doubles[i] = vals[i].position;
return doubles;
}
public static List<Double3> ToDouble3List(params Vert[] vals) => ToDouble3Array(vals).ToList();
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null || obj.GetType() != typeof(Vert)) return false;
return Equals((Vert)obj);
}
public bool Equals(Vert other) => position == other.position;
public override int GetHashCode() => position.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) => position.ToString(provider);
public string ToString(IFormatProvider provider) => position.ToString(provider);
public object Clone() => new Vert(position);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<double> GetEnumerator() => position.GetEnumerator();
public double[] ToArray() => position.ToArray();
public List<double> ToList() => position.ToList();
public static Vert operator +(Vert a, Vert b) => new(a.position + b.position);
public static Vert operator -(Vert d) => new(-d.position);
public static Vert operator -(Vert a, Vert b) => new(a.position - b.position);
public static Vert operator *(Vert a, Vert b) => new(a.position * b.position);
public static Vert operator *(Vert a, double b) => new(a.position * b);
public static Vert operator /(Vert a, Vert b) => new(a.position / b.position);
public static Vert operator /(Vert a, double b) => new(a.position / b);
public static bool operator ==(Vert a, Vert b) => a.Equals(b);
public static bool operator !=(Vert a, Vert b) => !a.Equals(b);
public static implicit operator Vert(Double2 val) => new(val);
public static implicit operator Vert(Double3 val) => new(val);
public static explicit operator Vert(Double4 val) => new(val.XYZ);
public static implicit operator Vert(Int2 val) => new(val);
public static implicit operator Vert(Int3 val) => new(val);
public static explicit operator Vert(Int4 val) => new(val.XYZ);
public static implicit operator Vert(Fill<double> fill) => new(fill);
public static implicit operator Vert(Fill<int> fill) => new(fill);
} }
public static Vert Absolute(Vert val) => new(Double3.Absolute(val.position));
public static Vert Average(params Vert[] vals) => Double3.Average(ToDouble3Array(vals));
public static Vert Ceiling(Vert val) => new(Double3.Ceiling(val.position));
public static Vert Clamp(Vert val, Vert min, Vert max) =>
new(Double3.Clamp(val.position, min.position, max.position));
public static Vert ClampMagnitude(Vert val, double minMag, double maxMag) =>
new(Double3.ClampMagnitude(val.position, minMag, maxMag));
public static Vert Cross(Vert a, Vert b, bool normalized = false) =>
new(Double3.Cross(a.position, b.position, normalized));
public static double Dot(Vert a, Vert b) => Double3.Dot(a.position, b.position);
public static double Dot(params Vert[] vals) => Double3.Dot(ToDouble3Array(vals));
public static Vert Floor(Vert val) => new(Double3.Floor(val.position));
public static Vert Lerp(Vert a, Vert b, double t, bool clamp = true) =>
new(Double3.Lerp(a.position, b.position, t, clamp));
public static Vert Median(params Vert[] vals) =>
Double3.Median(ToDouble3Array(vals));
public static Vert Max(params Vert[] vals) =>
Double3.Max(ToDouble3Array(vals));
public static Vert Min(params Vert[] vals) =>
Double3.Min(ToDouble3Array(vals));
public static Double3[] ToDouble3Array(params Vert[] vals)
{
Double3[] doubles = new Double3[vals.Length];
for (int i = 0; i < vals.Length; i++) doubles[i] = vals[i].position;
return doubles;
}
public static List<Double3> ToDouble3List(params Vert[] vals) => ToDouble3Array(vals).ToList();
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null || obj.GetType() != typeof(Vert)) return false;
return Equals((Vert)obj);
}
public bool Equals(Vert other) => position == other.position;
public override int GetHashCode() => position.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) => position.ToString(provider);
public string ToString(IFormatProvider provider) => position.ToString(provider);
public object Clone() => new Vert(position);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<double> GetEnumerator() => position.GetEnumerator();
public double[] ToArray() => position.ToArray();
public List<double> ToList() => position.ToList();
public static Vert operator +(Vert a, Vert b) => new(a.position + b.position);
public static Vert operator -(Vert d) => new(-d.position);
public static Vert operator -(Vert a, Vert b) => new(a.position - b.position);
public static Vert operator *(Vert a, Vert b) => new(a.position * b.position);
public static Vert operator *(Vert a, double b) => new(a.position * b);
public static Vert operator /(Vert a, Vert b) => new(a.position / b.position);
public static Vert operator /(Vert a, double b) => new(a.position / b);
public static bool operator ==(Vert a, Vert b) => a.Equals(b);
public static bool operator !=(Vert a, Vert b) => !a.Equals(b);
public static implicit operator Vert(Double2 val) => new(val);
public static implicit operator Vert(Double3 val) => new(val);
public static explicit operator Vert(Double4 val) => new(val.XYZ);
public static implicit operator Vert(Int2 val) => new(val);
public static implicit operator Vert(Int3 val) => new(val);
public static explicit operator Vert(Int4 val) => new(val.XYZ);
public static implicit operator Vert(Fill<double> fill) => new(fill);
public static implicit operator Vert(Fill<int> fill) => new(fill);
} }

View File

@ -1,183 +1,178 @@
using Nerd_STF.Mathematics.Geometry; namespace Nerd_STF.Mathematics;
using System.Collections;
using System.Diagnostics.CodeAnalysis;
namespace Nerd_STF.Mathematics public struct Int2 : ICloneable, IComparable<Int2>, IEquatable<Int2>, IGroup<int>
{ {
public struct Int2 : ICloneable, IComparable<Int2>, IEquatable<Int2>, IGroup<int> public static Int2 Down => new(0, -1);
public static Int2 Left => new(-1, 0);
public static Int2 Right => new(1, 0);
public static Int2 Up => new(0, 1);
public static Int2 One => new(1, 1);
public static Int2 Zero => new(0, 0);
public double Magnitude => Mathf.Sqrt(x * x + y * y);
public Int2 Normalized => (Int2)((Double2)this / Magnitude);
public int x, y;
public Int2(int all) : this(all, all) { }
public Int2(int x, int y)
{ {
public static Int2 Down => new(0, -1); this.x = x;
public static Int2 Left => new(-1, 0); this.y = y;
public static Int2 Right => new(1, 0);
public static Int2 Up => new(0, 1);
public static Int2 One => new(1, 1);
public static Int2 Zero => new(0, 0);
public double Magnitude => Mathf.Sqrt(x * x + y * y);
public Int2 Normalized => (Int2)((Double2)this / Magnitude);
public int x, y;
public Int2(int all) : this(all, all) { }
public Int2(int x, int y)
{
this.x = x;
this.y = y;
}
public Int2(Fill<int> fill) : this(fill(0), fill(1)) { }
public int this[int index]
{
get => index switch
{
0 => x,
1 => y,
_ => throw new IndexOutOfRangeException(nameof(index)),
};
set
{
switch (index)
{
case 0:
x = value;
break;
case 1:
y = value;
break;
default: throw new IndexOutOfRangeException(nameof(index));
}
}
}
public static Int2 Absolute(Int2 val) =>
new(Mathf.Absolute(val.x), Mathf.Absolute(val.y));
public static Int2 Average(params Int2[] vals) => Sum(vals) / vals.Length;
public static Int2 Clamp(Int2 val, Int2 min, Int2 max) =>
new(Mathf.Clamp(val.x, min.x, max.x),
Mathf.Clamp(val.y, min.y, max.y));
public static Int2 ClampMagnitude(Int2 val, int minMag, int maxMag)
{
if (maxMag < minMag) throw new ArgumentOutOfRangeException(nameof(maxMag),
nameof(maxMag) + " must be greater than or equal to " + nameof(minMag));
double mag = val.Magnitude;
if (mag >= minMag && mag <= maxMag) return val;
val = val.Normalized;
if (mag < minMag) val *= minMag;
else if (mag > maxMag) val *= maxMag;
return val;
}
public static Int3 Cross(Int2 a, Int2 b, bool normalized = false) =>
Int3.Cross(a, b, normalized);
public static Int2 Divide(Int2 num, params Int2[] vals)
{
foreach (Int2 d in vals) num /= d;
return num;
}
public static int Dot(Int2 a, Int2 b) => a.x * b.x + a.y * b.y;
public static int Dot(params Int2[] vals)
{
if (vals.Length < 1) return 0;
int x = 1, y = 1;
foreach (Int2 d in vals)
{
x *= d.x;
y *= d.y;
}
return x + y;
}
public static Int2 Lerp(Int2 a, Int2 b, double t, bool clamp = true) =>
new(Mathf.Lerp(a.x, b.x, t, clamp), Mathf.Lerp(a.y, b.y, t, clamp));
public static Int2 Median(params Int2[] vals)
{
int index = Mathf.Average(0, vals.Length - 1);
Int2 valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)];
return Average(valA, valB);
}
public static Int2 Max(params Int2[] vals)
{
if (vals.Length < 1) return Zero;
Int2 val = vals[0];
foreach (Int2 d in vals) val = d > val ? d : val;
return val;
}
public static Int2 Min(params Int2[] vals)
{
if (vals.Length < 1) return Zero;
Int2 val = vals[0];
foreach (Int2 d in vals) val = d < val ? d : val;
return val;
}
public static Int2 Multiply(params Int2[] vals)
{
if (vals.Length < 1) return Zero;
Int2 val = One;
foreach (Int2 d in vals) val *= d;
return val;
}
public static Int2 Subtract(Int2 num, params Int2[] vals)
{
foreach (Int2 d in vals) num -= d;
return num;
}
public static Int2 Sum(params Int2[] vals)
{
Int2 val = Zero;
foreach (Int2 d in vals) val += d;
return val;
}
public int CompareTo(Int2 other) => Magnitude.CompareTo(other.Magnitude);
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null || obj.GetType() != typeof(Int2)) return false;
return Equals((Int2)obj);
}
public bool Equals(Int2 other) => x == other.x && y == other.y;
public override int GetHashCode() => x.GetHashCode() ^ y.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider);
public string ToString(IFormatProvider provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider);
public object Clone() => new Int2(x, y);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<int> GetEnumerator()
{
yield return x;
yield return y;
}
public int[] ToArray() => new[] { x, y };
public List<int> ToList() => new() { x, y };
public static Int2 operator +(Int2 a, Int2 b) => new(a.x + b.x, a.y + b.y);
public static Int2 operator -(Int2 i) => new(-i.x, -i.y);
public static Int2 operator -(Int2 a, Int2 b) => new(a.x - b.x, a.y - b.y);
public static Int2 operator *(Int2 a, Int2 b) => new(a.x * b.x, a.y * b.y);
public static Int2 operator *(Int2 a, int b) => new(a.x * b, a.y * b);
public static Int2 operator /(Int2 a, Int2 b) => new(a.x / b.x, a.y / b.y);
public static Int2 operator /(Int2 a, int b) => new(a.x / b, a.y / b);
public static Int2 operator &(Int2 a, Int2 b) => new(a.x & b.x, a.y & b.y);
public static Int2 operator |(Int2 a, Int2 b) => new(a.x | b.x, a.y | b.y);
public static Int2 operator ^(Int2 a, Int2 b) => new(a.x ^ b.x, a.y ^ b.y);
public static bool operator ==(Int2 a, Int2 b) => a.Equals(b);
public static bool operator !=(Int2 a, Int2 b) => !a.Equals(b);
public static bool operator >(Int2 a, Int2 b) => a.CompareTo(b) > 0;
public static bool operator <(Int2 a, Int2 b) => a.CompareTo(b) < 0;
public static bool operator >=(Int2 a, Int2 b) => a == b || a > b;
public static bool operator <=(Int2 a, Int2 b) => a == b || a < b;
public static explicit operator Int2(Double2 val) => new((int)val.x, (int)val.y);
public static explicit operator Int2(Double3 val) => new((int)val.x, (int)val.y);
public static explicit operator Int2(Double4 val) => new((int)val.x, (int)val.y);
public static explicit operator Int2(Int3 val) => new(val.x, val.y);
public static explicit operator Int2(Int4 val) => new(val.x, val.y);
public static explicit operator Int2(Vert val) => new((int)val.position.x, (int)val.position.y);
public static implicit operator Int2(Fill<int> fill) => new(fill);
} }
public Int2(Fill<int> fill) : this(fill(0), fill(1)) { }
public int this[int index]
{
get => index switch
{
0 => x,
1 => y,
_ => throw new IndexOutOfRangeException(nameof(index)),
};
set
{
switch (index)
{
case 0:
x = value;
break;
case 1:
y = value;
break;
default: throw new IndexOutOfRangeException(nameof(index));
}
}
}
public static Int2 Absolute(Int2 val) =>
new(Mathf.Absolute(val.x), Mathf.Absolute(val.y));
public static Int2 Average(params Int2[] vals) => Sum(vals) / vals.Length;
public static Int2 Clamp(Int2 val, Int2 min, Int2 max) =>
new(Mathf.Clamp(val.x, min.x, max.x),
Mathf.Clamp(val.y, min.y, max.y));
public static Int2 ClampMagnitude(Int2 val, int minMag, int maxMag)
{
if (maxMag < minMag) throw new ArgumentOutOfRangeException(nameof(maxMag),
nameof(maxMag) + " must be greater than or equal to " + nameof(minMag));
double mag = val.Magnitude;
if (mag >= minMag && mag <= maxMag) return val;
val = val.Normalized;
if (mag < minMag) val *= minMag;
else if (mag > maxMag) val *= maxMag;
return val;
}
public static Int3 Cross(Int2 a, Int2 b, bool normalized = false) =>
Int3.Cross(a, b, normalized);
public static Int2 Divide(Int2 num, params Int2[] vals)
{
foreach (Int2 d in vals) num /= d;
return num;
}
public static int Dot(Int2 a, Int2 b) => a.x * b.x + a.y * b.y;
public static int Dot(params Int2[] vals)
{
if (vals.Length < 1) return 0;
int x = 1, y = 1;
foreach (Int2 d in vals)
{
x *= d.x;
y *= d.y;
}
return x + y;
}
public static Int2 Lerp(Int2 a, Int2 b, double t, bool clamp = true) =>
new(Mathf.Lerp(a.x, b.x, t, clamp), Mathf.Lerp(a.y, b.y, t, clamp));
public static Int2 Median(params Int2[] vals)
{
int index = Mathf.Average(0, vals.Length - 1);
Int2 valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)];
return Average(valA, valB);
}
public static Int2 Max(params Int2[] vals)
{
if (vals.Length < 1) return Zero;
Int2 val = vals[0];
foreach (Int2 d in vals) val = d > val ? d : val;
return val;
}
public static Int2 Min(params Int2[] vals)
{
if (vals.Length < 1) return Zero;
Int2 val = vals[0];
foreach (Int2 d in vals) val = d < val ? d : val;
return val;
}
public static Int2 Multiply(params Int2[] vals)
{
if (vals.Length < 1) return Zero;
Int2 val = One;
foreach (Int2 d in vals) val *= d;
return val;
}
public static Int2 Subtract(Int2 num, params Int2[] vals)
{
foreach (Int2 d in vals) num -= d;
return num;
}
public static Int2 Sum(params Int2[] vals)
{
Int2 val = Zero;
foreach (Int2 d in vals) val += d;
return val;
}
public int CompareTo(Int2 other) => Magnitude.CompareTo(other.Magnitude);
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null || obj.GetType() != typeof(Int2)) return false;
return Equals((Int2)obj);
}
public bool Equals(Int2 other) => x == other.x && y == other.y;
public override int GetHashCode() => x.GetHashCode() ^ y.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider);
public string ToString(IFormatProvider provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider);
public object Clone() => new Int2(x, y);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<int> GetEnumerator()
{
yield return x;
yield return y;
}
public int[] ToArray() => new[] { x, y };
public List<int> ToList() => new() { x, y };
public static Int2 operator +(Int2 a, Int2 b) => new(a.x + b.x, a.y + b.y);
public static Int2 operator -(Int2 i) => new(-i.x, -i.y);
public static Int2 operator -(Int2 a, Int2 b) => new(a.x - b.x, a.y - b.y);
public static Int2 operator *(Int2 a, Int2 b) => new(a.x * b.x, a.y * b.y);
public static Int2 operator *(Int2 a, int b) => new(a.x * b, a.y * b);
public static Int2 operator /(Int2 a, Int2 b) => new(a.x / b.x, a.y / b.y);
public static Int2 operator /(Int2 a, int b) => new(a.x / b, a.y / b);
public static Int2 operator &(Int2 a, Int2 b) => new(a.x & b.x, a.y & b.y);
public static Int2 operator |(Int2 a, Int2 b) => new(a.x | b.x, a.y | b.y);
public static Int2 operator ^(Int2 a, Int2 b) => new(a.x ^ b.x, a.y ^ b.y);
public static bool operator ==(Int2 a, Int2 b) => a.Equals(b);
public static bool operator !=(Int2 a, Int2 b) => !a.Equals(b);
public static bool operator >(Int2 a, Int2 b) => a.CompareTo(b) > 0;
public static bool operator <(Int2 a, Int2 b) => a.CompareTo(b) < 0;
public static bool operator >=(Int2 a, Int2 b) => a == b || a > b;
public static bool operator <=(Int2 a, Int2 b) => a == b || a < b;
public static explicit operator Int2(Double2 val) => new((int)val.x, (int)val.y);
public static explicit operator Int2(Double3 val) => new((int)val.x, (int)val.y);
public static explicit operator Int2(Double4 val) => new((int)val.x, (int)val.y);
public static explicit operator Int2(Int3 val) => new(val.x, val.y);
public static explicit operator Int2(Int4 val) => new(val.x, val.y);
public static explicit operator Int2(Vert val) => new((int)val.position.x, (int)val.position.y);
public static implicit operator Int2(Fill<int> fill) => new(fill);
} }

View File

@ -1,205 +1,197 @@
using Nerd_STF.Mathematics.Geometry; namespace Nerd_STF.Mathematics;
using System.Collections;
using System.Diagnostics.CodeAnalysis;
namespace Nerd_STF.Mathematics public struct Int3 : ICloneable, IComparable<Int3>, IEquatable<Int3>, IGroup<int>
{ {
public struct Int3 : ICloneable, IComparable<Int3>, IEquatable<Int3>, IGroup<int> public static Int3 Back => new(0, 0, -1);
public static Int3 Down => new(0, -1, 0);
public static Int3 Forward => new(0, 0, 1);
public static Int3 Left => new(-1, 0, 0);
public static Int3 Right => new(1, 0, 0);
public static Int3 Up => new(0, 1, 0);
public static Int3 One => new(1, 1, 1);
public static Int3 Zero => new(0, 0, 0);
public double Magnitude => Mathf.Sqrt(x * x + y * y + z * z);
public Int3 Normalized => (Int3)((Double3)this / Magnitude);
public Int2 XY => new(x, y);
public Int2 XZ => new(x, z);
public Int2 YZ => new(y, z);
public int x, y, z;
public Int3(int all) : this(all, all, all) { }
public Int3(int x, int y) : this(x, y, 0) { }
public Int3(int x, int y, int z)
{ {
public static Int3 Back => new(0, 0, -1); this.x = x;
public static Int3 Down => new(0, -1, 0); this.y = y;
public static Int3 Forward => new(0, 0, 1); this.z = z;
public static Int3 Left => new(-1, 0, 0);
public static Int3 Right => new(1, 0, 0);
public static Int3 Up => new(0, 1, 0);
public static Int3 One => new(1, 1, 1);
public static Int3 Zero => new(0, 0, 0);
public double Magnitude => Mathf.Sqrt(x * x + y * y + z * z);
public Int3 Normalized => (Int3)((Double3)this / Magnitude);
public Int2 XY => new(x, y);
public Int2 XZ => new(x, z);
public Int2 YZ => new(y, z);
public int x, y, z;
public Int3(int all) : this(all, all, all) { }
public Int3(int x, int y) : this(x, y, 0) { }
public Int3(int x, int y, int z)
{
this.x = x;
this.y = y;
this.z = z;
}
public Int3(Fill<int> fill) : this(fill(0), fill(1), fill(2)) { }
public int this[int index]
{
get => index switch
{
0 => x,
1 => y,
2 => z,
_ => throw new IndexOutOfRangeException(nameof(index)),
};
set
{
switch (index)
{
case 0:
x = value;
break;
case 1:
y = value;
break;
case 2:
z = value;
break;
default: throw new IndexOutOfRangeException(nameof(index));
}
}
}
public static Int3 Absolute(Int3 val) =>
new(Mathf.Absolute(val.x), Mathf.Absolute(val.y), Mathf.Absolute(val.z));
public static Int3 Average(params Int3[] vals) => Sum(vals) / vals.Length;
public static Int3 Clamp(Int3 val, Int3 min, Int3 max) =>
new(Mathf.Clamp(val.x, min.x, max.x),
Mathf.Clamp(val.y, min.y, max.y),
Mathf.Clamp(val.z, min.z, max.z));
public static Int3 ClampMagnitude(Int3 val, int minMag, int maxMag)
{
if (maxMag < minMag) throw new ArgumentOutOfRangeException(nameof(maxMag),
nameof(maxMag) + " must be greater than or equal to " + nameof(minMag));
double mag = val.Magnitude;
if (mag >= minMag && mag <= maxMag) return val;
val = val.Normalized;
if (mag < minMag) val *= minMag;
else if (mag > maxMag) val *= maxMag;
return val;
}
public static Int3 Cross(Int3 a, Int3 b, bool normalized = false)
{
Int3 val = new(a.y * b.z - b.y * a.z,
b.x * a.z - a.x * b.z,
a.x * b.y - b.x * a.y);
return normalized ? val.Normalized : val;
}
public static Int3 Divide(Int3 num, params Int3[] vals)
{
foreach (Int3 d in vals) num /= d;
return num;
}
public static int Dot(Int3 a, Int3 b) => a.x * b.x + a.y * b.y + a.z * b.z;
public static int Dot(params Int3[] vals)
{
if (vals.Length < 1) return 0;
int x = 1, y = 1, z = 1;
foreach (Int3 d in vals)
{
x *= d.x;
y *= d.y;
z *= d.z;
}
return x + y + z;
}
public static Int3 Lerp(Int3 a, Int3 b, double t, bool clamp = true) =>
new(Mathf.Lerp(a.x, b.x, t, clamp), Mathf.Lerp(a.y, b.y, t, clamp), Mathf.Lerp(a.z, b.z, t, clamp));
public static Int3 Median(params Int3[] vals)
{
int index = Mathf.Average(0, vals.Length - 1);
Int3 valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)];
return Average(valA, valB);
}
public static Int3 Max(params Int3[] vals)
{
if (vals.Length < 1) return Zero;
Int3 val = vals[0];
foreach (Int3 d in vals) val = d > val ? d : val;
return val;
}
public static Int3 Min(params Int3[] vals)
{
if (vals.Length < 1) return Zero;
Int3 val = vals[0];
foreach (Int3 d in vals) val = d < val ? d : val;
return val;
}
public static Int3 Multiply(params Int3[] vals)
{
if (vals.Length < 1) return Zero;
Int3 val = One;
foreach (Int3 d in vals) val *= d;
return val;
}
public static Int3 Subtract(Int3 num, params Int3[] vals)
{
foreach (Int3 d in vals) num -= d;
return num;
}
public static Int3 Sum(params Int3[] vals)
{
Int3 val = Zero;
foreach (Int3 d in vals) val += d;
return val;
}
public int CompareTo(Int3 other) => Magnitude.CompareTo(other.Magnitude);
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null || obj.GetType() != typeof(Int3)) return false;
return Equals((Int3)obj);
}
public bool Equals(Int3 other) => x == other.x && y == other.y && z == other.z;
public override int GetHashCode() => x.GetHashCode() ^ y.GetHashCode() ^ z.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider) + " Z: " + z.ToString(provider);
public string ToString(IFormatProvider provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider) + " Z: " + z.ToString(provider);
public object Clone() => new Int3(x, y, z);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<int> GetEnumerator()
{
yield return x;
yield return y;
yield return z;
}
public int[] ToArray() => new[] { x, y, z };
public List<int> ToList() => new() { x, y, z };
public static Int3 operator +(Int3 a, Int3 b) => new(a.x + b.x, a.y + b.y, a.z + b.z);
public static Int3 operator -(Int3 i) => new(-i.x, -i.y, -i.z);
public static Int3 operator -(Int3 a, Int3 b) => new(a.x - b.x, a.y - b.y, a.z - b.z);
public static Int3 operator *(Int3 a, Int3 b) => new(a.x * b.x, a.y * b.y, a.z * b.z);
public static Int3 operator *(Int3 a, int b) => new(a.x * b, a.y * b, a.z * b);
public static Int3 operator /(Int3 a, Int3 b) => new(a.x / b.x, a.y / b.y, a.z / b.z);
public static Int3 operator /(Int3 a, int b) => new(a.x / b, a.y / b, a.z / b);
public static Int3 operator &(Int3 a, Int3 b) => new(a.x & b.x, a.y & b.y, a.z & b.z);
public static Int3 operator |(Int3 a, Int3 b) => new(a.x | b.x, a.y | b.y, a.z | b.z);
public static Int3 operator ^(Int3 a, Int3 b) => new(a.x ^ b.x, a.y ^ b.y, a.z ^ b.z);
public static bool operator ==(Int3 a, Int3 b) => a.Equals(b);
public static bool operator !=(Int3 a, Int3 b) => !a.Equals(b);
public static bool operator >(Int3 a, Int3 b) => a.CompareTo(b) > 0;
public static bool operator <(Int3 a, Int3 b) => a.CompareTo(b) < 0;
public static bool operator >=(Int3 a, Int3 b) => a == b || a > b;
public static bool operator <=(Int3 a, Int3 b) => a == b || a < b;
public static explicit operator Int3(Double2 val) => new((int)val.x, (int)val.y, 0);
public static explicit operator Int3(Double3 val) => new((int)val.x, (int)val.y, (int)val.z);
public static explicit operator Int3(Double4 val) => new((int)val.x, (int)val.y, (int)val.z);
public static implicit operator Int3(Int2 val) => new(val.x, val.y, 0);
public static explicit operator Int3(Int4 val) => new(val.x, val.y, val.z);
public static explicit operator Int3(Vert val) => new((int)val.position.x, (int)val.position.y,
(int)val.position.z);
public static implicit operator Int3(Fill<int> fill) => new(fill);
} }
public Int3(Fill<int> fill) : this(fill(0), fill(1), fill(2)) { }
public int this[int index]
{
get => index switch
{
0 => x,
1 => y,
2 => z,
_ => throw new IndexOutOfRangeException(nameof(index)),
};
set
{
switch (index)
{
case 0:
x = value;
break;
case 1:
y = value;
break;
case 2:
z = value;
break;
default: throw new IndexOutOfRangeException(nameof(index));
}
}
}
public static Int3 Absolute(Int3 val) =>
new(Mathf.Absolute(val.x), Mathf.Absolute(val.y), Mathf.Absolute(val.z));
public static Int3 Average(params Int3[] vals) => Sum(vals) / vals.Length;
public static Int3 Clamp(Int3 val, Int3 min, Int3 max) =>
new(Mathf.Clamp(val.x, min.x, max.x),
Mathf.Clamp(val.y, min.y, max.y),
Mathf.Clamp(val.z, min.z, max.z));
public static Int3 ClampMagnitude(Int3 val, int minMag, int maxMag)
{
if (maxMag < minMag) throw new ArgumentOutOfRangeException(nameof(maxMag),
nameof(maxMag) + " must be greater than or equal to " + nameof(minMag));
double mag = val.Magnitude;
if (mag >= minMag && mag <= maxMag) return val;
val = val.Normalized;
if (mag < minMag) val *= minMag;
else if (mag > maxMag) val *= maxMag;
return val;
}
public static Int3 Cross(Int3 a, Int3 b, bool normalized = false)
{
Int3 val = new(a.y * b.z - b.y * a.z,
b.x * a.z - a.x * b.z,
a.x * b.y - b.x * a.y);
return normalized ? val.Normalized : val;
}
public static Int3 Divide(Int3 num, params Int3[] vals)
{
foreach (Int3 d in vals) num /= d;
return num;
}
public static int Dot(Int3 a, Int3 b) => a.x * b.x + a.y * b.y + a.z * b.z;
public static int Dot(params Int3[] vals)
{
if (vals.Length < 1) return 0;
int x = 1, y = 1, z = 1;
foreach (Int3 d in vals)
{
x *= d.x;
y *= d.y;
z *= d.z;
}
return x + y + z;
}
public static Int3 Lerp(Int3 a, Int3 b, double t, bool clamp = true) =>
new(Mathf.Lerp(a.x, b.x, t, clamp), Mathf.Lerp(a.y, b.y, t, clamp), Mathf.Lerp(a.z, b.z, t, clamp));
public static Int3 Median(params Int3[] vals)
{
int index = Mathf.Average(0, vals.Length - 1);
Int3 valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)];
return Average(valA, valB);
}
public static Int3 Max(params Int3[] vals)
{
if (vals.Length < 1) return Zero;
Int3 val = vals[0];
foreach (Int3 d in vals) val = d > val ? d : val;
return val;
}
public static Int3 Min(params Int3[] vals)
{
if (vals.Length < 1) return Zero;
Int3 val = vals[0];
foreach (Int3 d in vals) val = d < val ? d : val;
return val;
}
public static Int3 Multiply(params Int3[] vals)
{
if (vals.Length < 1) return Zero;
Int3 val = One;
foreach (Int3 d in vals) val *= d;
return val;
}
public static Int3 Subtract(Int3 num, params Int3[] vals)
{
foreach (Int3 d in vals) num -= d;
return num;
}
public static Int3 Sum(params Int3[] vals)
{
Int3 val = Zero;
foreach (Int3 d in vals) val += d;
return val;
}
public int CompareTo(Int3 other) => Magnitude.CompareTo(other.Magnitude);
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null || obj.GetType() != typeof(Int3)) return false;
return Equals((Int3)obj);
}
public bool Equals(Int3 other) => x == other.x && y == other.y && z == other.z;
public override int GetHashCode() => x.GetHashCode() ^ y.GetHashCode() ^ z.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider) + " Z: " + z.ToString(provider);
public string ToString(IFormatProvider provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider) + " Z: " + z.ToString(provider);
public object Clone() => new Int3(x, y, z);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<int> GetEnumerator()
{
yield return x;
yield return y;
yield return z;
}
public int[] ToArray() => new[] { x, y, z };
public List<int> ToList() => new() { x, y, z };
public static Int3 operator +(Int3 a, Int3 b) => new(a.x + b.x, a.y + b.y, a.z + b.z);
public static Int3 operator -(Int3 i) => new(-i.x, -i.y, -i.z);
public static Int3 operator -(Int3 a, Int3 b) => new(a.x - b.x, a.y - b.y, a.z - b.z);
public static Int3 operator *(Int3 a, Int3 b) => new(a.x * b.x, a.y * b.y, a.z * b.z);
public static Int3 operator *(Int3 a, int b) => new(a.x * b, a.y * b, a.z * b);
public static Int3 operator /(Int3 a, Int3 b) => new(a.x / b.x, a.y / b.y, a.z / b.z);
public static Int3 operator /(Int3 a, int b) => new(a.x / b, a.y / b, a.z / b);
public static Int3 operator &(Int3 a, Int3 b) => new(a.x & b.x, a.y & b.y, a.z & b.z);
public static Int3 operator |(Int3 a, Int3 b) => new(a.x | b.x, a.y | b.y, a.z | b.z);
public static Int3 operator ^(Int3 a, Int3 b) => new(a.x ^ b.x, a.y ^ b.y, a.z ^ b.z);
public static bool operator ==(Int3 a, Int3 b) => a.Equals(b);
public static bool operator !=(Int3 a, Int3 b) => !a.Equals(b);
public static bool operator >(Int3 a, Int3 b) => a.CompareTo(b) > 0;
public static bool operator <(Int3 a, Int3 b) => a.CompareTo(b) < 0;
public static bool operator >=(Int3 a, Int3 b) => a == b || a > b;
public static bool operator <=(Int3 a, Int3 b) => a == b || a < b;
public static explicit operator Int3(Double2 val) => new((int)val.x, (int)val.y, 0);
public static explicit operator Int3(Double3 val) => new((int)val.x, (int)val.y, (int)val.z);
public static explicit operator Int3(Double4 val) => new((int)val.x, (int)val.y, (int)val.z);
public static implicit operator Int3(Int2 val) => new(val.x, val.y, 0);
public static explicit operator Int3(Int4 val) => new(val.x, val.y, val.z);
public static explicit operator Int3(Vert val) => new((int)val.position.x, (int)val.position.y,
(int)val.position.z);
public static implicit operator Int3(Fill<int> fill) => new(fill);
} }

View File

@ -1,221 +1,216 @@
using Nerd_STF.Mathematics.Geometry; namespace Nerd_STF.Mathematics;
using System.Collections;
using System.Diagnostics.CodeAnalysis;
namespace Nerd_STF.Mathematics public struct Int4 : ICloneable, IComparable<Int4>, IEquatable<Int4>, IGroup<int>
{ {
public struct Int4 : ICloneable, IComparable<Int4>, IEquatable<Int4>, IGroup<int> public static Int4 Back => new(0, 0, -1, 0);
public static Int4 Deep => new(0, 0, 0, -1);
public static Int4 Down => new(0, -1, 0, 0);
public static Int4 Far => new(0, 0, 0, 1);
public static Int4 Forward => new(0, 0, 1, 0);
public static Int4 Left => new(-1, 0, 0, 0);
public static Int4 Right => new(1, 0, 0, 0);
public static Int4 Up => new(0, 1, 0, 0);
public static Int4 One => new(1, 1, 1, 1);
public static Int4 Zero => new(0, 0, 0, 0);
public double Magnitude => Mathf.Sqrt(x * x + y * y + z * z + w * w);
public Int4 Normalized => (Int4)((Double4)this / Magnitude);
public Int2 XY => new(x, y);
public Int2 XZ => new(x, z);
public Int2 XW => new(x, w);
public Int2 YW => new(y, w);
public Int2 YZ => new(y, z);
public Int2 ZW => new(z, w);
public Int3 XYW => new(x, y, w);
public Int3 XYZ => new(x, y, z);
public Int3 YZW => new(y, z, w);
public Int3 XZW => new(x, z, w);
public int x, y, z, w;
public Int4(int all) : this(all, all, all, all) { }
public Int4(int x, int y) : this(x, y, 0, 0) { }
public Int4(int x, int y, int z) : this(x, y, z, 0) { }
public Int4(int x, int y, int z, int w)
{ {
public static Int4 Back => new(0, 0, -1, 0); this.x = x;
public static Int4 Deep => new(0, 0, 0, -1); this.y = y;
public static Int4 Down => new(0, -1, 0, 0); this.z = z;
public static Int4 Far => new(0, 0, 0, 1); this.w = w;
public static Int4 Forward => new(0, 0, 1, 0);
public static Int4 Left => new(-1, 0, 0, 0);
public static Int4 Right => new(1, 0, 0, 0);
public static Int4 Up => new(0, 1, 0, 0);
public static Int4 One => new(1, 1, 1, 1);
public static Int4 Zero => new(0, 0, 0, 0);
public double Magnitude => Mathf.Sqrt(x * x + y * y + z * z + w * w);
public Int4 Normalized => (Int4)((Double4)this / Magnitude);
public Int2 XY => new(x, y);
public Int2 XZ => new(x, z);
public Int2 XW => new(x, w);
public Int2 YW => new(y, w);
public Int2 YZ => new(y, z);
public Int2 ZW => new(z, w);
public Int3 XYW => new(x, y, w);
public Int3 XYZ => new(x, y, z);
public Int3 YZW => new(y, z, w);
public Int3 XZW => new(x, z, w);
public int x, y, z, w;
public Int4(int all) : this(all, all, all, all) { }
public Int4(int x, int y) : this(x, y, 0, 0) { }
public Int4(int x, int y, int z) : this(x, y, z, 0) { }
public Int4(int x, int y, int z, int w)
{
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
public Int4(Fill<int> fill) : this(fill(0), fill(1), fill(2), fill(3)) { }
public int this[int index]
{
get => index switch
{
0 => x,
1 => y,
2 => z,
3 => w,
_ => throw new IndexOutOfRangeException(nameof(index)),
};
set
{
switch (index)
{
case 0:
x = value;
break;
case 1:
y = value;
break;
case 2:
z = value;
break;
case 3:
w = value;
break;
default: throw new IndexOutOfRangeException(nameof(index));
}
}
}
public static Int4 Absolute(Int4 val) =>
new(Mathf.Absolute(val.x), Mathf.Absolute(val.y), Mathf.Absolute(val.z), Mathf.Absolute(val.w));
public static Int4 Average(params Int4[] vals) => Sum(vals) / vals.Length;
public static Int4 Clamp(Int4 val, Int4 min, Int4 max) =>
new(Mathf.Clamp(val.x, min.x, max.x),
Mathf.Clamp(val.y, min.y, max.y),
Mathf.Clamp(val.z, min.z, max.z),
Mathf.Clamp(val.w, min.w, max.w));
public static Int4 ClampMagnitude(Int4 val, int minMag, int maxMag)
{
if (maxMag < minMag) throw new ArgumentOutOfRangeException(nameof(maxMag),
nameof(maxMag) + " must be greater than or equal to " + nameof(minMag));
double mag = val.Magnitude;
if (mag >= minMag && mag <= maxMag) return val;
val = val.Normalized;
if (mag < minMag) val *= minMag;
else if (mag > maxMag) val *= maxMag;
return val;
}
public static Int4 Divide(Int4 num, params Int4[] vals)
{
foreach (Int4 d in vals) num /= d;
return num;
}
public static int Dot(Int4 a, Int4 b) => a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
public static int Dot(params Int4[] vals)
{
if (vals.Length < 1) return 0;
int x = 1, y = 1, z = 1, w = 1;
foreach (Int4 d in vals)
{
x *= d.x;
y *= d.y;
z *= d.z;
w *= d.w;
}
return x + y + z;
}
public static Int4 Lerp(Int4 a, Int4 b, double t, bool clamp = true) =>
new(Mathf.Lerp(a.x, b.x, t, clamp), Mathf.Lerp(a.y, b.y, t, clamp), Mathf.Lerp(a.z, b.z, t, clamp),
Mathf.Lerp(a.w, b.w, t, clamp));
public static Int4 Median(params Int4[] vals)
{
int index = Mathf.Average(0, vals.Length - 1);
Int4 valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)];
return Average(valA, valB);
}
public static Int4 Max(params Int4[] vals)
{
if (vals.Length < 1) return Zero;
Int4 val = vals[0];
foreach (Int4 d in vals) val = d > val ? d : val;
return val;
}
public static Int4 Min(params Int4[] vals)
{
if (vals.Length < 1) return Zero;
Int4 val = vals[0];
foreach (Int4 d in vals) val = d < val ? d : val;
return val;
}
public static Int4 Multiply(params Int4[] vals)
{
if (vals.Length < 1) return Zero;
Int4 val = One;
foreach (Int4 d in vals) val *= d;
return val;
}
public static Int4 Subtract(Int4 num, params Int4[] vals)
{
foreach (Int4 d in vals) num -= d;
return num;
}
public static Int4 Sum(params Int4[] vals)
{
Int4 val = Zero;
foreach (Int4 d in vals) val += d;
return val;
}
public int CompareTo(Int4 other) => Magnitude.CompareTo(other.Magnitude);
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null || obj.GetType() != typeof(Int4)) return false;
return Equals((Int4)obj);
}
public bool Equals(Int4 other) => x == other.x && y == other.y && z == other.z && w == other.w;
public override int GetHashCode() => x.GetHashCode() ^ y.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider) + " Z: " + z.ToString(provider)
+ " W: " + w.ToString(provider);
public string ToString(IFormatProvider provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider) + " Z: " + z.ToString(provider)
+ " W: " + w.ToString(provider);
public object Clone() => new Int4(x, y, z, w);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<int> GetEnumerator()
{
yield return x;
yield return y;
yield return z;
yield return w;
}
public int[] ToArray() => new[] { x, y, z, w };
public List<int> ToList() => new() { x, y, z, w };
public static Int4 operator +(Int4 a, Int4 b) => new(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
public static Int4 operator -(Int4 d) => new(-d.x, -d.y, -d.z, -d.w);
public static Int4 operator -(Int4 a, Int4 b) => new(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
public static Int4 operator *(Int4 a, Int4 b) => new(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w);
public static Int4 operator *(Int4 a, int b) => new(a.x * b, a.y * b, a.z * b, a.w * b);
public static Int4 operator /(Int4 a, Int4 b) => new(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w);
public static Int4 operator /(Int4 a, int b) => new(a.x / b, a.y / b, a.z / b, a.w / b);
public static Int4 operator &(Int4 a, Int4 b) => new(a.x & b.x, a.y & b.y, a.z & b.z, a.w & b.w);
public static Int4 operator |(Int4 a, Int4 b) => new(a.x | b.x, a.y | b.y, a.z | b.z, a.w | b.w);
public static Int4 operator ^(Int4 a, Int4 b) => new(a.x ^ b.x, a.y ^ b.y, a.z ^ b.z, a.w ^ b.w);
public static bool operator ==(Int4 a, Int4 b) => a.Equals(b);
public static bool operator !=(Int4 a, Int4 b) => !a.Equals(b);
public static bool operator >(Int4 a, Int4 b) => a.CompareTo(b) > 0;
public static bool operator <(Int4 a, Int4 b) => a.CompareTo(b) < 0;
public static bool operator >=(Int4 a, Int4 b) => a == b || a > b;
public static bool operator <=(Int4 a, Int4 b) => a == b || a < b;
public static explicit operator Int4(Double2 val) => new((int)val.x, (int)val.y, 0, 0);
public static explicit operator Int4(Double3 val) => new((int)val.x, (int)val.y, (int)val.z, 0);
public static explicit operator Int4(Double4 val) => new((int)val.x, (int)val.y, (int)val.z, (int)val.w);
public static implicit operator Int4(Int2 val) => new(val.x, val.y, 0, 0);
public static implicit operator Int4(Int3 val) => new(val.x, val.y, val.z, 0);
public static explicit operator Int4(Vert val) => new((int)val.position.x, (int)val.position.y,
(int)val.position.z, 0);
public static implicit operator Int4(Fill<int> fill) => new(fill);
} }
public Int4(Fill<int> fill) : this(fill(0), fill(1), fill(2), fill(3)) { }
public int this[int index]
{
get => index switch
{
0 => x,
1 => y,
2 => z,
3 => w,
_ => throw new IndexOutOfRangeException(nameof(index)),
};
set
{
switch (index)
{
case 0:
x = value;
break;
case 1:
y = value;
break;
case 2:
z = value;
break;
case 3:
w = value;
break;
default: throw new IndexOutOfRangeException(nameof(index));
}
}
}
public static Int4 Absolute(Int4 val) =>
new(Mathf.Absolute(val.x), Mathf.Absolute(val.y), Mathf.Absolute(val.z), Mathf.Absolute(val.w));
public static Int4 Average(params Int4[] vals) => Sum(vals) / vals.Length;
public static Int4 Clamp(Int4 val, Int4 min, Int4 max) =>
new(Mathf.Clamp(val.x, min.x, max.x),
Mathf.Clamp(val.y, min.y, max.y),
Mathf.Clamp(val.z, min.z, max.z),
Mathf.Clamp(val.w, min.w, max.w));
public static Int4 ClampMagnitude(Int4 val, int minMag, int maxMag)
{
if (maxMag < minMag) throw new ArgumentOutOfRangeException(nameof(maxMag),
nameof(maxMag) + " must be greater than or equal to " + nameof(minMag));
double mag = val.Magnitude;
if (mag >= minMag && mag <= maxMag) return val;
val = val.Normalized;
if (mag < minMag) val *= minMag;
else if (mag > maxMag) val *= maxMag;
return val;
}
public static Int4 Divide(Int4 num, params Int4[] vals)
{
foreach (Int4 d in vals) num /= d;
return num;
}
public static int Dot(Int4 a, Int4 b) => a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
public static int Dot(params Int4[] vals)
{
if (vals.Length < 1) return 0;
int x = 1, y = 1, z = 1, w = 1;
foreach (Int4 d in vals)
{
x *= d.x;
y *= d.y;
z *= d.z;
w *= d.w;
}
return x + y + z;
}
public static Int4 Lerp(Int4 a, Int4 b, double t, bool clamp = true) =>
new(Mathf.Lerp(a.x, b.x, t, clamp), Mathf.Lerp(a.y, b.y, t, clamp), Mathf.Lerp(a.z, b.z, t, clamp),
Mathf.Lerp(a.w, b.w, t, clamp));
public static Int4 Median(params Int4[] vals)
{
int index = Mathf.Average(0, vals.Length - 1);
Int4 valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)];
return Average(valA, valB);
}
public static Int4 Max(params Int4[] vals)
{
if (vals.Length < 1) return Zero;
Int4 val = vals[0];
foreach (Int4 d in vals) val = d > val ? d : val;
return val;
}
public static Int4 Min(params Int4[] vals)
{
if (vals.Length < 1) return Zero;
Int4 val = vals[0];
foreach (Int4 d in vals) val = d < val ? d : val;
return val;
}
public static Int4 Multiply(params Int4[] vals)
{
if (vals.Length < 1) return Zero;
Int4 val = One;
foreach (Int4 d in vals) val *= d;
return val;
}
public static Int4 Subtract(Int4 num, params Int4[] vals)
{
foreach (Int4 d in vals) num -= d;
return num;
}
public static Int4 Sum(params Int4[] vals)
{
Int4 val = Zero;
foreach (Int4 d in vals) val += d;
return val;
}
public int CompareTo(Int4 other) => Magnitude.CompareTo(other.Magnitude);
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null || obj.GetType() != typeof(Int4)) return false;
return Equals((Int4)obj);
}
public bool Equals(Int4 other) => x == other.x && y == other.y && z == other.z && w == other.w;
public override int GetHashCode() => x.GetHashCode() ^ y.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider) + " Z: " + z.ToString(provider)
+ " W: " + w.ToString(provider);
public string ToString(IFormatProvider provider) =>
"X: " + x.ToString(provider) + " Y: " + y.ToString(provider) + " Z: " + z.ToString(provider)
+ " W: " + w.ToString(provider);
public object Clone() => new Int4(x, y, z, w);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<int> GetEnumerator()
{
yield return x;
yield return y;
yield return z;
yield return w;
}
public int[] ToArray() => new[] { x, y, z, w };
public List<int> ToList() => new() { x, y, z, w };
public static Int4 operator +(Int4 a, Int4 b) => new(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
public static Int4 operator -(Int4 d) => new(-d.x, -d.y, -d.z, -d.w);
public static Int4 operator -(Int4 a, Int4 b) => new(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
public static Int4 operator *(Int4 a, Int4 b) => new(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w);
public static Int4 operator *(Int4 a, int b) => new(a.x * b, a.y * b, a.z * b, a.w * b);
public static Int4 operator /(Int4 a, Int4 b) => new(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w);
public static Int4 operator /(Int4 a, int b) => new(a.x / b, a.y / b, a.z / b, a.w / b);
public static Int4 operator &(Int4 a, Int4 b) => new(a.x & b.x, a.y & b.y, a.z & b.z, a.w & b.w);
public static Int4 operator |(Int4 a, Int4 b) => new(a.x | b.x, a.y | b.y, a.z | b.z, a.w | b.w);
public static Int4 operator ^(Int4 a, Int4 b) => new(a.x ^ b.x, a.y ^ b.y, a.z ^ b.z, a.w ^ b.w);
public static bool operator ==(Int4 a, Int4 b) => a.Equals(b);
public static bool operator !=(Int4 a, Int4 b) => !a.Equals(b);
public static bool operator >(Int4 a, Int4 b) => a.CompareTo(b) > 0;
public static bool operator <(Int4 a, Int4 b) => a.CompareTo(b) < 0;
public static bool operator >=(Int4 a, Int4 b) => a == b || a > b;
public static bool operator <=(Int4 a, Int4 b) => a == b || a < b;
public static explicit operator Int4(Double2 val) => new((int)val.x, (int)val.y, 0, 0);
public static explicit operator Int4(Double3 val) => new((int)val.x, (int)val.y, (int)val.z, 0);
public static explicit operator Int4(Double4 val) => new((int)val.x, (int)val.y, (int)val.z, (int)val.w);
public static implicit operator Int4(Int2 val) => new(val.x, val.y, 0, 0);
public static implicit operator Int4(Int3 val) => new(val.x, val.y, val.z, 0);
public static explicit operator Int4(Vert val) => new((int)val.position.x, (int)val.position.y,
(int)val.position.z, 0);
public static implicit operator Int4(Fill<int> fill) => new(fill);
} }

View File

@ -1,253 +1,252 @@
namespace Nerd_STF.Mathematics namespace Nerd_STF.Mathematics;
public static class Mathf
{ {
public static class Mathf public const double RadToDeg = 0.0174532925199; // Pi / 180
public const double E = 2.71828182846;
public const double GoldenRatio = 1.61803398875; // (1 + Sqrt(5)) / 2
public const double HalfPi = 1.57079632679; // Pi / 2
public const double Pi = 3.14159265359;
public const double DegToRad = 57.2957795131; // 180 / Pi
public const double Tau = 6.28318530718; // 2 * Pi
public static double Absolute(double val) => val < 0 ? -val : val;
public static int Absolute(int val) => val < 0 ? -val : val;
public static double ArcCos(double value) => -ArcSin(value) + HalfPi;
public static double ArcCot(double value) => ArcCos(value / Sqrt(1 + value * value));
public static double ArcCsc(double value) => ArcSin(1 / value);
public static double ArcSec(double value) => ArcCos(1 / value);
// Maybe one day I'll have a polynomial for this, but the RMSE for an order 10 polynomial is only 0.00876.
public static double ArcSin(double value) => Math.Asin(value);
public static double ArcTan(double value) => ArcSin(value / Sqrt(1 + value * value));
public static double Average(Equation equ, double min, double max, double step = Calculus.DefaultStep)
{ {
public const double RadToDeg = 0.0174532925199; // Pi / 180 List<double> vals = new();
public const double E = 2.71828182846; for (double x = min; x <= max; x += step) vals.Add(equ(x));
public const double GoldenRatio = 1.61803398875; // (1 + Sqrt(5)) / 2 return Average(vals.ToArray());
public const double HalfPi = 1.57079632679; // Pi / 2
public const double Pi = 3.14159265359;
public const double DegToRad = 57.2957795131; // 180 / Pi
public const double Tau = 6.28318530718; // 2 * Pi
public static double Absolute(double val) => val < 0 ? -val : val;
public static int Absolute(int val) => val < 0 ? -val : val;
public static double ArcCos(double value) => -ArcSin(value) + HalfPi;
public static double ArcCot(double value) => ArcCos(value / Sqrt(1 + value * value));
public static double ArcCsc(double value) => ArcSin(1 / value);
public static double ArcSec(double value) => ArcCos(1 / value);
// Maybe one day I'll have a polynomial for this, but the RMSE for an order 10 polynomial is only 0.00876.
public static double ArcSin(double value) => Math.Asin(value);
public static double ArcTan(double value) => ArcSin(value / Sqrt(1 + value * value));
public static double Average(Equation equ, double min, double max, double step = Calculus.DefaultStep)
{
List<double> vals = new();
for (double x = min; x <= max; x += step) vals.Add(equ(x));
return Average(vals.ToArray());
}
public static double Average(params double[] vals) => Sum(vals) / vals.Length;
public static int Average(params int[] vals) => Sum(vals) / vals.Length;
public static int Ceiling(double val) => (int)(val + (1 - (val % 1)));
public static double Clamp(double val, double min, double max)
{
if (max < min) throw new ArgumentOutOfRangeException(nameof(max),
nameof(max) + " must be greater than or equal to " + nameof(min));
val = val < min ? min : val;
val = val > max ? max : val;
return val;
}
public static int Clamp(int val, int min, int max)
{
if (max < min) throw new ArgumentOutOfRangeException(nameof(max),
nameof(max) + " must be greater than or equal to " + nameof(min));
val = val < min ? min : val;
val = val > max ? max : val;
return val;
}
public static double Cos(double radians) => Sin(radians + HalfPi);
public static double Cot(double radians) => Cos(radians) / Sin(radians);
public static double Csc(double radians) => 1 / Sin(radians);
public static double Divide(double val, params double[] dividends)
{
foreach (double d in dividends) val /= d;
return val;
}
public static int Divide(int val, params int[] dividends)
{
foreach (int i in dividends) val /= i;
return val;
}
public static int Factorial(int amount)
{
if (amount < 0) return 0;
int val = 1;
for (int i = 2; i <= amount; i++) val *= i;
return val;
}
public static int Floor(double val) => (int)(val - (val % 1));
public static Dictionary<double, double> GetValues(Equation equ, double min, double max,
double step = Calculus.DefaultStep)
{
Dictionary<double, double> vals = new();
for (double x = min; x <= max; x += step) vals.Add(x, equ(x));
return vals;
}
public static double Lerp(double a, double b, double t, bool clamp = true)
{
double v = a + t * (b - a);
if (clamp) v = Clamp(v, a, b);
return v;
}
public static int Lerp(int a, int b, double value, bool clamp = true) => Floor(Lerp(a, b, value, clamp));
public static Equation MakeEquation(Dictionary<double, double> vals) => (x) =>
{
double min = -1, max = -1;
foreach (KeyValuePair<double, double> val in vals)
{
if (val.Key <= x) min = val.Key;
if (val.Key >= x) max = val.Key;
if (min != -1 && max != -1) break;
}
double per = x % (max - min);
return Lerp(min, max, per);
};
public static double Max(Equation equ, double min, double max, double step = Calculus.DefaultStep)
{
double Y = equ(min);
for (double x = min; x <= max; x += step)
{
double val = equ(x);
Y = val > Y ? val : Y;
}
return Y;
}
public static double Max(params double[] vals)
{
if (vals.Length < 1) return 0;
double val = vals[0];
foreach (double d in vals) val = d > val ? d : val;
return val;
}
public static int Max(params int[] vals)
{
if (vals.Length < 1) return 0;
int val = vals[0];
foreach (int i in vals) val = i > val ? i : val;
return val;
}
public static double Median(params double[] vals)
{
double index = Average(0, vals.Length - 1);
double valA = vals[Floor(index)], valB = vals[Ceiling(index)];
return Average(valA, valB);
}
public static int Median(params int[] vals) => vals[Floor(Average(0, vals.Length - 1))];
public static double Min(Equation equ, double min, double max, double step = Calculus.DefaultStep)
{
double Y = equ(min);
for (double x = min; x <= max; x += step)
{
double val = equ(x);
Y = val < Y ? val : Y;
}
return Y;
}
public static double Min(params double[] vals)
{
if (vals.Length < 1) return 0;
double val = vals[0];
foreach (double d in vals) val = d < val ? d : val;
return val;
}
public static int Min(params int[] vals)
{
if (vals.Length < 1) return 0;
int val = vals[0];
foreach (int i in vals) val = i < val ? i : val;
return val;
}
public static double Multiply(params double[] vals)
{
if (vals.Length < 1) return 0;
double val = 1;
foreach (double d in vals) val *= d;
return val;
}
public static int Multiply(params int[] vals)
{
if (vals.Length < 1) return 0;
int val = 1;
foreach (int i in vals) val *= i;
return val;
}
public static double Power(double num, double pow) => Math.Pow(num, pow);
public static int Power(int num, int pow)
{
if (pow < 0) return 0;
int val = 1;
for (int i = 0; i < Absolute(pow); i++) val *= num;
return val;
}
public static double Root(double value, double index) => Math.Exp(index * Math.Log(value));
public static double Round(double num) => num % 1 >= 0.5 ? Ceiling(num) : Floor(num);
public static double Round(double num, double nearest) => nearest * Round(num / nearest);
public static int RoundInt(double num) => (int)Round(num);
public static double Sec(double radians) => 1 / Cos(radians);
public static double Sin(double radians)
{
// Really close polynomial to sin(x) (when modded by 2pi). RMSE of 0.000003833
const double a = 0.000013028,
b = 0.999677,
c = 0.00174164,
d = -0.170587,
e = 0.0046494,
f = 0.00508955,
g = 0.00140205,
h = -0.000577413,
i = 0.0000613134,
j = -0.00000216852;
double x = radians % Tau;
return
a + (b * x) + (c * x * x) + (d * x * x * x) + (e * x * x * x * x) + (f * x * x * x * x * x)
+ (g * x * x * x * x * x * x) + (h * x * x * x * x * x * x * x) + (i * x * x * x * x * x * x * x * x)
+ (j * x * x * x * x * x * x * x * x * x);
}
public static double Sqrt(double value) => Root(value, 2);
public static double Subtract(double num, params double[] vals)
{
foreach (double d in vals) num -= d;
return num;
}
public static int Subtract(int num, params int[] vals)
{
foreach (int i in vals) num -= i;
return num;
}
public static double Sum(params double[] vals)
{
double val = 0;
foreach (double d in vals) val += d;
return val;
}
public static int Sum(params int[] vals)
{
int val = 0;
foreach (int i in vals) val += i;
return val;
}
public static double Tan(double radians) => Sin(radians) / Cos(radians);
} }
public static double Average(params double[] vals) => Sum(vals) / vals.Length;
public static int Average(params int[] vals) => Sum(vals) / vals.Length;
public static int Ceiling(double val) => (int)(val + (1 - (val % 1)));
public static double Clamp(double val, double min, double max)
{
if (max < min) throw new ArgumentOutOfRangeException(nameof(max),
nameof(max) + " must be greater than or equal to " + nameof(min));
val = val < min ? min : val;
val = val > max ? max : val;
return val;
}
public static int Clamp(int val, int min, int max)
{
if (max < min) throw new ArgumentOutOfRangeException(nameof(max),
nameof(max) + " must be greater than or equal to " + nameof(min));
val = val < min ? min : val;
val = val > max ? max : val;
return val;
}
public static double Cos(double radians) => Sin(radians + HalfPi);
public static double Cot(double radians) => Cos(radians) / Sin(radians);
public static double Csc(double radians) => 1 / Sin(radians);
public static double Divide(double val, params double[] dividends)
{
foreach (double d in dividends) val /= d;
return val;
}
public static int Divide(int val, params int[] dividends)
{
foreach (int i in dividends) val /= i;
return val;
}
public static int Factorial(int amount)
{
if (amount < 0) return 0;
int val = 1;
for (int i = 2; i <= amount; i++) val *= i;
return val;
}
public static int Floor(double val) => (int)(val - (val % 1));
public static Dictionary<double, double> GetValues(Equation equ, double min, double max,
double step = Calculus.DefaultStep)
{
Dictionary<double, double> vals = new();
for (double x = min; x <= max; x += step) vals.Add(x, equ(x));
return vals;
}
public static double Lerp(double a, double b, double t, bool clamp = true)
{
double v = a + t * (b - a);
if (clamp) v = Clamp(v, a, b);
return v;
}
public static int Lerp(int a, int b, double value, bool clamp = true) => Floor(Lerp(a, b, value, clamp));
public static Equation MakeEquation(Dictionary<double, double> vals) => (x) =>
{
double min = -1, max = -1;
foreach (KeyValuePair<double, double> val in vals)
{
if (val.Key <= x) min = val.Key;
if (val.Key >= x) max = val.Key;
if (min != -1 && max != -1) break;
}
double per = x % (max - min);
return Lerp(min, max, per);
};
public static double Max(Equation equ, double min, double max, double step = Calculus.DefaultStep)
{
double Y = equ(min);
for (double x = min; x <= max; x += step)
{
double val = equ(x);
Y = val > Y ? val : Y;
}
return Y;
}
public static double Max(params double[] vals)
{
if (vals.Length < 1) return 0;
double val = vals[0];
foreach (double d in vals) val = d > val ? d : val;
return val;
}
public static int Max(params int[] vals)
{
if (vals.Length < 1) return 0;
int val = vals[0];
foreach (int i in vals) val = i > val ? i : val;
return val;
}
public static double Median(params double[] vals)
{
double index = Average(0, vals.Length - 1);
double valA = vals[Floor(index)], valB = vals[Ceiling(index)];
return Average(valA, valB);
}
public static int Median(params int[] vals) => vals[Floor(Average(0, vals.Length - 1))];
public static double Min(Equation equ, double min, double max, double step = Calculus.DefaultStep)
{
double Y = equ(min);
for (double x = min; x <= max; x += step)
{
double val = equ(x);
Y = val < Y ? val : Y;
}
return Y;
}
public static double Min(params double[] vals)
{
if (vals.Length < 1) return 0;
double val = vals[0];
foreach (double d in vals) val = d < val ? d : val;
return val;
}
public static int Min(params int[] vals)
{
if (vals.Length < 1) return 0;
int val = vals[0];
foreach (int i in vals) val = i < val ? i : val;
return val;
}
public static double Multiply(params double[] vals)
{
if (vals.Length < 1) return 0;
double val = 1;
foreach (double d in vals) val *= d;
return val;
}
public static int Multiply(params int[] vals)
{
if (vals.Length < 1) return 0;
int val = 1;
foreach (int i in vals) val *= i;
return val;
}
public static double Power(double num, double pow) => Math.Pow(num, pow);
public static int Power(int num, int pow)
{
if (pow < 0) return 0;
int val = 1;
for (int i = 0; i < Absolute(pow); i++) val *= num;
return val;
}
public static double Root(double value, double index) => Math.Exp(index * Math.Log(value));
public static double Round(double num) => num % 1 >= 0.5 ? Ceiling(num) : Floor(num);
public static double Round(double num, double nearest) => nearest * Round(num / nearest);
public static int RoundInt(double num) => (int)Round(num);
public static double Sec(double radians) => 1 / Cos(radians);
public static double Sin(double radians)
{
// Really close polynomial to sin(x) (when modded by 2pi). RMSE of 0.000003833
const double a = 0.000013028,
b = 0.999677,
c = 0.00174164,
d = -0.170587,
e = 0.0046494,
f = 0.00508955,
g = 0.00140205,
h = -0.000577413,
i = 0.0000613134,
j = -0.00000216852;
double x = radians % Tau;
return
a + (b * x) + (c * x * x) + (d * x * x * x) + (e * x * x * x * x) + (f * x * x * x * x * x)
+ (g * x * x * x * x * x * x) + (h * x * x * x * x * x * x * x) + (i * x * x * x * x * x * x * x * x)
+ (j * x * x * x * x * x * x * x * x * x);
}
public static double Sqrt(double value) => Root(value, 2);
public static double Subtract(double num, params double[] vals)
{
foreach (double d in vals) num -= d;
return num;
}
public static int Subtract(int num, params int[] vals)
{
foreach (int i in vals) num -= i;
return num;
}
public static double Sum(params double[] vals)
{
double val = 0;
foreach (double d in vals) val += d;
return val;
}
public static int Sum(params int[] vals)
{
int val = 0;
foreach (int i in vals) val += i;
return val;
}
public static double Tan(double radians) => Sin(radians) / Cos(radians);
} }

View File

@ -1,6 +1,7 @@
global using System; global using System;
global using System.Collections; global using System.Collections;
global using System.Collections.Generic; global using System.Collections.Generic;
global using System.Diagnostics.CodeAnalysis;
global using System.IO; global using System.IO;
global using System.Linq; global using System.Linq;
global using System.Net.Http; global using System.Net.Http;