Version 2.1.0

This commit is contained in:
That-One-Nerd 2022-04-17 13:43:38 -04:00
parent 1a769b4404
commit 4be41701cd
30 changed files with 1894 additions and 139 deletions

82
Changelog.md Normal file
View File

@ -0,0 +1,82 @@
# Nerd_STF v2.1.0
```
* Nerd_STF
+ Exceptions
+ Nerd_STFException
+ DifferingVertCountException
+ DisconnectedLinesException
+ Miscellaneous
+ `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,16 +0,0 @@
# Nerd_STF v2.0.1
* Nerd_STF
* Mathematics
* Geometry
* Line
+ ToDoubleArray()
+ ToDoubleList()
* Triangle
+ ToDoubleArray()
+ ToDoubleList()
* Vert
+ : IGroup\<Vert>
+ GetEnumerator()
+ ToArray()
+ ToList()

View File

@ -0,0 +1,30 @@
using System.Runtime.Serialization;
namespace Nerd_STF.Exceptions
{
[Serializable]
public class DifferingVertCountException : Nerd_STFException
{
public string? ParamName;
public Polygon[]? Polygons;
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) { }
}
}

View File

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

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
namespace Nerd_STF.Exceptions
{
[Serializable]
public class Nerd_STFException : Exception
{
public Nerd_STFException() { }
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) { }
}
}

13
Nerd_STF/IClosest.cs Normal file
View File

@ -0,0 +1,13 @@
using System;
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 T ClosestTo(T item);
}
}

13
Nerd_STF/IContainer.cs Normal file
View File

@ -0,0 +1,13 @@
using System;
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 bool Contains(T item);
}
}

13
Nerd_STF/IEncapsulator.cs Normal file
View File

@ -0,0 +1,13 @@
using System;
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 T Encapsulate(TE val);
}
}

View File

@ -58,7 +58,7 @@ namespace Nerd_STF
LogSeverity.Warning => ConsoleColor.DarkYellow,
LogSeverity.Error => ConsoleColor.Red,
LogSeverity.Fatal => ConsoleColor.DarkRed,
_ => throw new Exception("Unknown log severity " + msg.Severity),
_ => throw new ArgumentException("Unknown log severity " + msg.Severity, nameof(msg)),
};
ConsoleColor originalCol = Console.ForegroundColor;

View File

@ -0,0 +1,124 @@
using System;
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 static Angle Full => new(360);
public static Angle Half => new(180);
public static Angle One => new(1);
public static Angle Quarter => new(90);
public static Angle Zero => new(0);
public double Degrees
{
get => p_deg;
set => p_deg = value;
}
public double Gradians
{
get => p_deg * 1.11111111111; // Reciprocal of 9/10 as a constant (10/9)
set => p_deg = value * 0.9;
}
public double Radians
{
get => p_deg * Mathf.DegToRad;
set => p_deg = value * Mathf.RadToDeg;
}
public Angle Bounded => new(p_deg % 360);
private double p_deg;
public Angle(double value, Type valueType = Type.Degrees)
{
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 => 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 override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null || obj.GetType() != typeof(Angle)) return false;
return Equals((Angle)obj);
}
public bool Equals(Angle other) => p_deg == other.p_deg;
public override int GetHashCode() => Degrees.GetHashCode() ^ Gradians.GetHashCode() ^ Radians.GetHashCode();
public override string ToString() => ToString((string?)null);
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 object Clone() => new Angle(p_deg);
public static Angle operator +(Angle a, Angle b) => new(a.p_deg + b.p_deg);
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);
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

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

View File

@ -134,11 +134,7 @@ namespace Nerd_STF.Mathematics
return val;
}
public int CompareTo(Double2 other)
{
double magA = Magnitude, magB = other.Magnitude;
return magA == magB ? 0 : magA > magB ? 1 : -1;
}
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;

View File

@ -154,11 +154,7 @@ namespace Nerd_STF.Mathematics
return val;
}
public int CompareTo(Double3 other)
{
double magA = Magnitude, magB = other.Magnitude;
return magA == magB ? 0 : magA > magB ? 1 : -1;
}
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;

View File

@ -167,11 +167,7 @@ namespace Nerd_STF.Mathematics
return val;
}
public int CompareTo(Double4 other)
{
double magA = Magnitude, magB = other.Magnitude;
return magA == magB ? 0 : magA > magB ? 1 : -1;
}
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;

View File

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

View File

@ -0,0 +1,130 @@
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 Box2D : ICloneable, IContainer<Vert>, IEquatable<Box2D>
{
public static Box2D Unit => new(Vert.Zero, Double2.One);
public Vert MaxVert
{
get => center + (size / 2);
set
{
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);
}
}

View File

@ -0,0 +1,131 @@
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 Box3D : ICloneable, IContainer<Vert>, IEquatable<Box3D>
{
public static Box3D Unit => new(Vert.Zero, Double3.One);
public Vert MaxVert
{
get => center + (Vert)(size / 2);
set
{
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);
}
}

View File

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

View File

@ -8,6 +8,13 @@ namespace Nerd_STF.Mathematics.Geometry
{
public interface ITriangulatable
{
public static Triangle[] TriangulateAll(params ITriangulatable[] triangulatables)
{
List<Triangle> res = new();
foreach (ITriangulatable triangulatable in triangulatables) res.AddRange(triangulatable.Triangulate());
return res.ToArray();
}
public Triangle[] Triangulate();
}
}

View File

@ -8,7 +8,8 @@ using System.Threading.Tasks;
namespace Nerd_STF.Mathematics.Geometry
{
public struct Line : ICloneable, IEquatable<Line>, IGroup<Vert>
public struct Line : ICloneable, IClosest<Vert>, IComparable<Line>, IContainer<Vert>, IEquatable<Line>,
IGroup<Vert>, ISubdividable<Line[]>
{
public static Line Back => new(Vert.Zero, Vert.Back);
public static Line Down => new(Vert.Zero, Vert.Down);
@ -20,14 +21,14 @@ namespace Nerd_STF.Mathematics.Geometry
public static Line One => new(Vert.Zero, Vert.One);
public static Line Zero => new(Vert.Zero, Vert.Zero);
public double Length => (end - start).Magnitude;
public double Length => (b - a).Magnitude;
public Vert start, end;
public Vert a, b;
public Line(Vert start, Vert end)
public Line(Vert a, Vert b)
{
this.start = start;
this.end = end;
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)
@ -42,8 +43,8 @@ namespace Nerd_STF.Mathematics.Geometry
{
get => index switch
{
0 => start,
1 => end,
0 => a,
1 => b,
_ => throw new IndexOutOfRangeException(nameof(index)),
};
set
@ -51,11 +52,11 @@ namespace Nerd_STF.Mathematics.Geometry
switch (index)
{
case 0:
start = value;
a = value;
break;
case 1:
end = value;
b = value;
break;
default: throw new IndexOutOfRangeException(nameof(index));
@ -63,18 +64,18 @@ namespace Nerd_STF.Mathematics.Geometry
}
}
public static Line Absolute(Line val) => new(Vert.Absolute(val.start), Vert.Absolute(val.end));
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.start), Vert.Ceiling(val.end));
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.start, min.start, max.start), Vert.Clamp(val.end, min.end, max.end));
public static Line Floor(Line val) => new(Vert.Floor(val.start), Vert.Floor(val.end));
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.start, b.start, t, clamp), Vert.Lerp(a.end, b.end, t, clamp));
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);
@ -96,8 +97,8 @@ namespace Nerd_STF.Mathematics.Geometry
Vert[] starts = new Vert[lines.Length], ends = new Vert[lines.Length];
for (int i = 0; i < lines.Length; i++)
{
starts[i] = lines[i].start;
ends[i] = lines[i].end;
starts[i] = lines[i].a;
ends[i] = lines[i].b;
}
return (starts, ends);
}
@ -107,43 +108,90 @@ namespace Nerd_STF.Mathematics.Geometry
if (obj == null || obj.GetType() != typeof(Line)) return false;
return Equals((Line)obj);
}
public bool Equals(Line other) => start == other.start && end == other.end;
public override int GetHashCode() => start.GetHashCode() ^ end.GetHashCode();
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: " + start.ToString(provider) + " B: " + end.ToString(provider);
"A: " + a.ToString(provider) + " B: " + b.ToString(provider);
public string ToString(IFormatProvider provider) =>
"A: " + start.ToString(provider) + " B: " + end.ToString(provider);
"A: " + a.ToString(provider) + " B: " + b.ToString(provider);
public object Clone() => new Line(start, end);
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 start;
yield return end;
yield return a;
yield return b;
}
public Vert[] ToArray() => new Vert[] { start, end };
public List<Vert> ToList() => new() { start, end };
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 double[] ToDoubleArray() => new double[] { start.position.x, start.position.y, start.position.z,
end.position.x, end.position.y, end.position.z };
public List<double> ToDoubleList() => new() { start.position.x, start.position.y, start.position.z,
end.position.x, end.position.y, end.position.z };
public Vert[] ToArray() => new Vert[] { a, b };
public List<Vert> ToList() => new() { a, b };
public static Line operator +(Line a, Line b) => new(a.start + b.start, a.end + b.end);
public static Line operator +(Line a, Vert b) => new(a.start + b, a.end + b);
public static Line operator -(Line a, Line b) => new(a.start - b.start, a.end - b.end);
public static Line operator -(Line a, Vert b) => new(a.start - b, a.end - b);
public static Line operator *(Line a, Line b) => new(a.start * b.start, a.end * b.end);
public static Line operator *(Line a, Vert b) => new(a.start * b, a.end * b);
public static Line operator *(Line a, double b) => new(a.start * b, a.end * b);
public static Line operator /(Line a, Line b) => new(a.start / b.start, a.end / b.end);
public static Line operator /(Line a, Vert b) => new(a.start / b, a.end / b);
public static Line operator /(Line a, double b) => new(a.start / b, a.end / 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);

View File

@ -0,0 +1,519 @@
using Nerd_STF.Exceptions;
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 Line[] Lines
{
get => p_lines;
set
{
p_lines = value;
p_verts = GenerateVerts(value);
}
}
public Vert[] Verts
{
get => p_verts;
set
{
p_verts = value;
p_lines = GenerateLines(value);
}
}
private Line[] p_lines;
private Vert[] p_verts;
[Obsolete("This method uses the Polygon.Triangulate() function, which has issues. It will be fixed in a " +
"future update.")]
public double Area
{
get
{
double val = 0;
foreach (Triangle t in Triangulate()) val += t.Area;
return val;
}
}
public double Perimeter
{
get
{
double val = 0;
foreach (Line l in Lines) val += l.Length;
return val;
}
}
public Polygon()
{
p_lines = Array.Empty<Line>();
p_verts = Array.Empty<Vert>();
}
public Polygon(Fill<Vert?> fill)
{
List<Vert> verts = new();
int i = 0;
while (true)
{
Vert? v = fill(i);
if (!v.HasValue) break;
verts.Add(v.Value);
}
this = new(verts.ToArray());
}
public Polygon(Fill<Double3?> fill)
{
List<Vert> verts = new();
int i = 0;
while (true)
{
Vert? v = fill(i);
if (!v.HasValue) break;
verts.Add(v.Value);
}
this = new(verts.ToArray());
}
public Polygon(Fill<Line?> fill)
{
List<Line> lines = new();
int i = 0;
while (true)
{
Line? v = fill(i);
if (!v.HasValue) break;
lines.Add(v.Value);
}
this = new(lines.ToArray());
}
public Polygon(Fill<Vert> fill, int length)
{
List<Vert> verts = new();
for (int i = 0; i < length; i++) verts.Add(fill(i));
this = new(verts.ToArray());
}
public Polygon(Fill<Double3> fill, int length)
{
List<Vert> verts = new();
for (int i = 0; i < length; i++) verts.Add(fill(i));
this = new(verts.ToArray());
}
public Polygon(Fill<Line> fill, int length)
{
List<Line> lines = new();
for (int i = 0; i < length; i++) lines.Add(fill(i));
this = new(lines.ToArray());
}
public Polygon(params Double3[] verts)
{
p_verts = new Vert[verts.Length];
for (int i = 0; i < verts.Length; i++) p_verts[i] = verts[i];
p_lines = GenerateLines(p_verts);
}
public Polygon(params Vert[] verts)
{
p_verts = verts;
p_lines = GenerateLines(verts);
}
public Polygon(params Line[] lines)
{
p_lines = lines;
p_verts = GenerateVerts(lines);
}
public Vert this[int index]
{
get => Verts[index];
set => Verts[index] = value;
}
public static Polygon CreateCircle(int vertCount)
{
List<Vert> parts = new();
for (int i = 0; i < vertCount; i++)
{
double val = Mathf.Tau * i / vertCount;
parts.Add(new(Mathf.Cos(val), Mathf.Sin(val)));
}
return new(parts.ToArray());
}
public static Polygon Absolute(Polygon val)
{
Vert[] v = val.Verts;
for (int i = 0; i < v.Length; i++) v[i] = Vert.Absolute(v[i]);
return new(v);
}
public static Polygon Average(params Polygon[] vals)
{
if (!CheckVerts(vals)) throw new DifferingVertCountException(nameof(vals), vals);
if (vals.Length < 1) return default;
Line[][] lines = new Line[vals.Length][];
for (int i = 0; i < vals.Length; i++) lines[i] = vals[i].Lines;
Line[] res = new Line[vals[0].Lines.Length];
for (int i = 0; i < res.Length; i++)
{
Line[] row = new Line[vals.Length];
for (int j = 0; j < vals[0].Lines.Length; j++) row[j] = vals[j].Lines[i];
res[i] = Line.Average(row);
}
return new(res);
}
public static Polygon Ceiling(Polygon val)
{
Vert[] v = val.Verts;
for (int i = 0; i < v.Length; i++) v[i] = Vert.Ceiling(v[i]);
return new(v);
}
public static Polygon Clamp(Polygon val, Polygon min, Polygon max)
{
if (!CheckVerts(val, min, max)) throw new DifferingVertCountException(val, min, max);
Line[][] lines = new Line[3][] { val.Lines, min.Lines, max.Lines };
Line[] res = new Line[val.Lines.Length];
for (int i = 0; i < res.Length; i++) res[i] = Line.Clamp(lines[0][i], lines[1][i], lines[2][i]);
return new(res);
}
public static Polygon Floor(Polygon val)
{
Vert[] v = val.Verts;
for (int i = 0; i < v.Length; i++) v[i] = Vert.Floor(v[i]);
return new(v);
}
public static Polygon Lerp(Polygon a, Polygon b, double t, bool clamp = true)
{
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] = Line.Lerp(lines[0][i], lines[1][i], t, clamp);
return new(res);
}
public static Polygon Max(params Polygon[] vals)
{
if (!CheckVerts(vals)) throw new DifferingVertCountException(nameof(vals), vals);
if (vals.Length < 1) return default;
Line[][] lines = new Line[vals.Length][];
for (int i = 0; i < vals.Length; i++) lines[i] = vals[i].Lines;
Line[] res = new Line[vals[0].Lines.Length];
for (int i = 0; i < res.Length; i++)
{
Line[] row = new Line[vals.Length];
for (int j = 0; j < vals[0].Lines.Length; j++) row[j] = vals[j].Lines[i];
res[i] = Line.Max(row);
}
return new(res);
}
public static Polygon Median(params Polygon[] vals)
{
if (!CheckVerts(vals)) throw new DifferingVertCountException(nameof(vals), vals);
if (vals.Length < 1) return default;
Line[][] lines = new Line[vals.Length][];
for (int i = 0; i < vals.Length; i++) lines[i] = vals[i].Lines;
Line[] res = new Line[vals[0].Lines.Length];
for (int i = 0; i < res.Length; i++)
{
Line[] row = new Line[vals.Length];
for (int j = 0; j < vals[0].Lines.Length; j++) row[j] = vals[j].Lines[i];
res[i] = Line.Median(row);
}
return new(res);
}
public static Polygon Min(params Polygon[] vals)
{
if (!CheckVerts(vals)) throw new DifferingVertCountException(nameof(vals), vals);
if (vals.Length < 1) return default;
Line[][] lines = new Line[vals.Length][];
for (int i = 0; i < vals.Length; i++) lines[i] = vals[i].Lines;
Line[] res = new Line[vals[0].Lines.Length];
for (int i = 0; i < res.Length; i++)
{
Line[] row = new Line[vals.Length];
for (int j = 0; j < vals[0].Lines.Length; j++) row[j] = vals[j].Lines[i];
res[i] = Line.Min(row);
}
return new(res);
}
public static double[] ToDoubleArrayAll(params Polygon[] polys) => ToDoubleListAll(polys).ToArray();
public static List<double> ToDoubleListAll(params Polygon[] polys)
{
List<double> vals = new();
foreach (Polygon poly in polys) vals.AddRange(poly.ToDoubleArray());
return vals;
}
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null || obj.GetType() != typeof(Polygon)) return false;
return Equals((Polygon)obj);
}
public bool Equals(Polygon other)
{
if (!CheckVerts(this, other)) return false;
return Lines == other.Lines;
}
public override int GetHashCode() => Lines.GetHashCode();
public override string ToString() => ToString((string?)null);
public string ToString(string? provider)
{
string s = "";
for (int i = 0; i < Lines.Length; i++) s += "L" + i + ": " + Lines[i].ToString(provider) + " ";
return s;
}
public string ToString(IFormatProvider provider)
{
string s = "";
for (int i = 0; i < Lines.Length; i++) s += "L" + i + ": " + Lines[i].ToString(provider) + " ";
return s;
}
public object Clone() => new Polygon(Lines);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<Vert> GetEnumerator() { foreach (Vert v in Verts) yield return v; }
public Vert[] ToArray() => Verts;
public List<Vert> ToList() => new(Verts);
public double[] ToDoubleArray()
{
double[] vals = new double[Verts.Length * 3];
for (int i = 0; i < Verts.Length; i++)
{
int pos = i * 3;
vals[pos + 0] = Verts[i].position.x;
vals[pos + 1] = Verts[i].position.y;
vals[pos + 2] = Verts[i].position.z;
}
return vals;
}
public List<double> ToDoubleList() => new(ToDoubleArray());
public Polygon Subdivide()
{
Polygon poly = new();
List<Line> lines = new();
for (int i = 0; i < Lines.Length; i++) lines.AddRange(Lines[i].Subdivide());
return poly;
}
public Polygon Subdivide(int iterations)
{
if (iterations < 1) return new();
Polygon poly = this;
for (int i = 0; i < iterations; i++) poly = poly.Subdivide();
return poly;
}
public Polygon SubdivideCatmullClark(int segments)
{
// Thanks Saalty for making this accidentally.
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)
{
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);
}
}
return new(newVerts.ToArray());
}
[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.")]
public Triangle[] Triangulate()
{
// This may cause issues. FIXME
// Tbh, not even sure if this works. This was a bit confusing.
if (Verts.Length == 3) return new Triangle[] { new(Verts[0], Verts[1], Verts[2]) };
(int posA, int posB, Line line)? closest = null;
for (int i = 0; i < Verts.Length; i++)
{
for (int j = 0; j < Verts.Length; j++)
{
if (i == j) continue;
Line l = new(Verts[i], Verts[j]);
if (Lines.Contains(l)) continue;
if (!closest.HasValue || closest.Value.line.Length > l.Length) closest = (i, j, l);
}
}
if (closest == null) throw new("Unknown error triangulating the polygon.");
if (closest.Value.posB > closest.Value.posA)
closest = (closest.Value.posB, closest.Value.posA, closest.Value.line);
List<Line> partA = new(Lines[closest.Value.posA..(closest.Value.posB - 1)]);
partA.Add(closest.Value.line);
Polygon pA = new(partA.ToArray());
List<Line> partB = new(Lines[0..(closest.Value.posA - 1)]);
partB.AddRange(Lines[closest.Value.posB..(Lines.Length - 1)]);
partB.Add(closest.Value.line);
Polygon pB = new(partB.ToArray());
List<Triangle> tris = new(pA.Triangulate());
tris.AddRange(pB.Triangulate());
return tris.ToArray();
}
private static bool CheckVerts(params Polygon[] polys)
{
int len = -1;
foreach (Polygon poly in polys)
{
if (len == -1)
{
len = poly.Verts.Length;
continue;
}
if (poly.Verts.Length != len) return false;
}
return true;
}
private static Line[] GenerateLines(Vert[] verts)
{
Line[] lines = new Line[verts.Length];
for (int i = 0; i < lines.Length; i++)
lines[i] = new(verts[i], verts[i == lines.Length - 1 ? 0 : i + 1]);
return lines;
}
private static Vert[] GenerateVerts(Line[] lines)
{
Vert[] verts = new Vert[lines.Length];
for (int i = 0; i < verts.Length; i++)
{
verts[i] = lines[i].a;
if (lines[i].b != lines[i == verts.Length - 1 ? 0 : i + 1].a)
throw new DisconnectedLinesException(nameof(lines), lines);
}
return verts;
}
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 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

@ -0,0 +1,342 @@
using Nerd_STF.Exceptions;
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 Vert A
{
get => p_a;
set
{
p_a = value;
p_ab.a = value;
p_da.b = value;
}
}
public Vert B
{
get => p_b;
set
{
p_b = value;
p_ab.b = value;
p_bc.a = value;
}
}
public Vert C
{
get => p_c;
set
{
p_c = value;
p_bc.b = value;
p_cd.a = value;
}
}
public Vert D
{
get => p_d;
set
{
p_d = value;
p_cd.b = value;
p_da.a = value;
}
}
public Line AB
{
get => p_ab;
set
{
p_ab = value;
p_a = value.a;
p_b = value.b;
p_bc.a = value.b;
p_da.b = value.a;
}
}
public Line BC
{
get => p_bc;
set
{
p_bc = value;
p_b = value.a;
p_c = value.b;
p_cd.a = value.b;
p_ab.b = value.a;
}
}
public Line CD
{
get => p_cd;
set
{
p_cd = value;
p_c = value.a;
p_d = value.b;
p_da.a = value.b;
p_bc.b = value.a;
}
}
public Line DA
{
get => p_da;
set
{
p_da = value;
p_d = value.a;
p_a = value.b;
p_ab.a = value.b;
p_cd.b = value.a;
}
}
private Vert p_a, p_b, p_c, p_d;
private Line p_ab, p_bc, p_cd, p_da;
public double Area
{
get
{
double val = 0;
foreach (Triangle t in Triangulate()) val += t.Area;
return val;
}
}
public double Perimeter => AB.Length + BC.Length + CD.Length + DA.Length;
public Quadrilateral(Vert a, Vert b, Vert c, Vert d)
{
p_a = a;
p_b = b;
p_c = c;
p_d = d;
p_ab = new(a, b);
p_bc = new(b, c);
p_cd = new(c, d);
p_da = new(d, a);
}
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)
throw new DisconnectedLinesException(ab, bc, cd, da);
p_a = ab.a;
p_b = bc.a;
p_c = cd.a;
p_d = da.a;
p_ab = ab;
p_bc = bc;
p_cd = cd;
p_da = da;
}
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)) { }
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)
: 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<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<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),
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),
fill(7), fill(8), fill(9), fill(10), fill(11)) { }
public Vert this[int index]
{
get => index switch
{
0 => A,
1 => B,
2 => C,
3 => D,
_ => throw new IndexOutOfRangeException(nameof(index)),
};
set
{
switch (index)
{
case 0:
A = value;
break;
case 1:
B = value;
break;
case 2:
C = value;
break;
case 3:
D = value;
break;
default: throw new IndexOutOfRangeException(nameof(index));
}
}
}
public static Quadrilateral Absolute(Quadrilateral val) =>
new(Vert.Absolute(val.A), Vert.Absolute(val.B), Vert.Absolute(val.C), Vert.Absolute(val.D));
public static Quadrilateral Average(params Quadrilateral[] 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));
}
public static Quadrilateral Ceiling(Quadrilateral val) =>
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) =>
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));
public static Quadrilateral Floor(Quadrilateral val) =>
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) =>
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));
public static Quadrilateral Max(params Quadrilateral[] 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));
}
public static Quadrilateral Median(params Quadrilateral[] 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));
}
public static Quadrilateral Min(params Quadrilateral[] 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));
}
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],
c = new Vert[quads.Length], d = new Vert[quads.Length];
for (int i = 0; i < quads.Length; i++)
{
a[i] = quads[i].A;
b[i] = quads[i].B;
c[i] = quads[i].C;
d[i] = quads[i].D;
}
return (a, b, c, d);
}
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],
cd = new Line[quads.Length], da = new Line[quads.Length];
for (int i = 0; i < quads.Length; i++)
{
ab[i] = quads[i].AB;
bc[i] = quads[i].BC;
cd[i] = quads[i].CD;
da[i] = quads[i].DA;
}
return (ab, bc, cd, da);
}
public static double[] ToDoubleArrayAll(params Quadrilateral[] quads)
{
double[] vals = new double[quads.Length * 12];
for (int i = 0; i < quads.Length; i++)
{
int pos = i * 12;
vals[pos + 0] = quads[i].A.position.x;
vals[pos + 1] = quads[i].A.position.y;
vals[pos + 2] = quads[i].A.position.z;
vals[pos + 3] = quads[i].B.position.x;
vals[pos + 4] = quads[i].B.position.y;
vals[pos + 5] = quads[i].B.position.z;
vals[pos + 6] = quads[i].C.position.x;
vals[pos + 7] = quads[i].C.position.y;
vals[pos + 8] = quads[i].C.position.z;
vals[pos + 9] = quads[i].D.position.x;
vals[pos + 10] = quads[i].D.position.y;
vals[pos + 11] = quads[i].D.position.z;
}
return vals;
}
public static List<double> ToDoubleListAll(params Quadrilateral[] quads) => new(ToDoubleArrayAll(quads));
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null || obj.GetType() != typeof(Quadrilateral)) return false;
return Equals((Quadrilateral)obj);
}
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 string ToString() => ToString((string?)null);
public string ToString(string? provider) => "A: " + A.ToString(provider) + " B: " + B.ToString(provider)
+ " C: " + C.ToString(provider) + " D: " + D.ToString(provider);
public string ToString(IFormatProvider provider) => "A: " + A.ToString(provider) + " B: "
+ B.ToString(provider) + " C: " + C.ToString(provider) + " D: " + D.ToString(provider);
public object Clone() => new Quadrilateral(A, B, C, D);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<Vert> GetEnumerator()
{
yield return A;
yield return B;
yield return C;
yield return D;
}
public Vert[] ToArray() => new Vert[] { 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,
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,
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 ?
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,
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 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,
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, Quadrilateral b) => new(a.A * b.A, a.B * b.B,
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, 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,
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, 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 implicit operator Quadrilateral(Fill<Vert> 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<Line> 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 explicit operator Quadrilateral(Polygon poly) => new(poly.Lines[0], poly.Lines[1],
poly.Lines[2], poly.Lines[3]);
}
}

View File

@ -0,0 +1,126 @@
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 Sphere : ICloneable, IClosest<Vert>, IComparable<Sphere>, IComparable<double>, IContainer<Vert>,
IEquatable<Sphere>, IEquatable<double>
{
public static Sphere Unit => new(Vert.Zero, 1);
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;
}
}

View File

@ -1,4 +1,5 @@
using System.Collections;
using Nerd_STF.Exceptions;
using System.Collections;
using System.Diagnostics.CodeAnalysis;
namespace Nerd_STF.Mathematics.Geometry
@ -11,8 +12,8 @@ namespace Nerd_STF.Mathematics.Geometry
set
{
p_a = value;
p_l1 = new(value, p_b);
p_l3 = new(p_c, value);
p_ab.a = value;
p_ca.b = value;
}
}
public Vert B
@ -21,8 +22,8 @@ namespace Nerd_STF.Mathematics.Geometry
set
{
p_b = value;
p_l1 = new(p_a, value);
p_l2 = new(value, p_c);
p_ab.b = value;
p_bc.a = value;
}
}
public Vert C
@ -31,67 +32,74 @@ namespace Nerd_STF.Mathematics.Geometry
set
{
p_c = value;
p_l2 = new(p_b, value);
p_l3 = new(value, p_a);
p_bc.b = value;
p_ca.a = value;
}
}
public Line L1
public Line AB
{
get => p_l1;
get => p_ab;
set
{
p_a = value.start;
p_b = value.end;
p_l2 = new(value.end, p_c);
p_l3 = new(p_c, value.start);
p_ab = value;
p_a = value.a;
p_b = value.b;
p_bc.a = value.b;
p_ca.b = value.a;
}
}
public Line L2
public Line BC
{
get => p_l2;
get => p_bc;
set
{
p_b = value.start;
p_c = value.end;
p_l1 = new(p_a, value.start);
p_l3 = new(value.end, p_a);
p_bc = value;
p_b = value.a;
p_c = value.b;
p_ca.a = value.b;
p_ab.b = value.a;
}
}
public Line L3
public Line CA
{
get => p_l3;
get => p_ca;
set
{
p_a = value.end;
p_c = value.start;
p_l1 = new(value.end, p_b);
p_l2 = new(p_b, value.start);
p_ca = value;
p_a = value.b;
p_c = value.a;
p_ab.a = value.b;
p_bc.b = value.a;
}
}
private Vert p_a, p_b, p_c;
private Line p_l1, p_l2, p_l3;
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) -
((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 Triangle(Vert a, Vert b, Vert c)
{
p_a = a;
p_b = b;
p_c = c;
p_l1 = new(a, b);
p_l2 = new(b, c);
p_l3 = new(c, a);
p_ab = new(a, b);
p_bc = new(b, c);
p_ca = new(c, a);
}
public Triangle(Line l1, Line l2, Line l3)
public Triangle(Line ab, Line bc, Line ca)
{
if (l1.start != l3.end && l1.end != l2.start && l2.end != l3.start)
throw new ArgumentException("Lines are not connected.");
if (ab.a != ca.b || ab.b != bc.a || bc.b != ca.a)
throw new DisconnectedLinesException(ab, bc, ca);
p_a = l1.start;
p_b = l2.start;
p_c = l3.start;
p_l1 = l1;
p_l2 = l2;
p_l3 = l3;
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)) { }
@ -151,18 +159,18 @@ namespace Nerd_STF.Mathematics.Geometry
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 = false) =>
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 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 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);
@ -181,19 +189,39 @@ namespace Nerd_STF.Mathematics.Geometry
return (a, b, c);
}
public static (Line[] L1s, Line[] L2s, Line[] L3s) SplitLineArray(params Triangle[] tris)
public static (Line[] ABs, Line[] BCs, Line[] CAs) SplitLineArray(params Triangle[] tris)
{
Line[] l1 = new Line[tris.Length], l2 = new Line[tris.Length], l3 = new Line[tris.Length];
Line[] ab = new Line[tris.Length], bc = new Line[tris.Length], ca = new Line[tris.Length];
for (int i = 0; i < tris.Length; i++)
{
l1[i] = tris[i].L1;
l2[i] = tris[i].L2;
l3[i] = tris[i].L3;
ab[i] = tris[i].AB;
bc[i] = tris[i].BC;
ca[i] = tris[i].CA;
}
return (l1, l2, l3);
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++)
{
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)
{
if (obj == null || obj.GetType() != typeof(Triangle)) return false;
@ -229,6 +257,7 @@ namespace Nerd_STF.Mathematics.Geometry
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);
@ -246,5 +275,6 @@ namespace Nerd_STF.Mathematics.Geometry
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

@ -129,11 +129,7 @@ namespace Nerd_STF.Mathematics
return val;
}
public int CompareTo(Int2 other)
{
double magA = Magnitude, magB = other.Magnitude;
return magA == magB ? 0 : magA > magB ? 1 : -1;
}
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;
@ -166,6 +162,9 @@ namespace Nerd_STF.Mathematics
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;

View File

@ -149,11 +149,7 @@ namespace Nerd_STF.Mathematics
return val;
}
public int CompareTo(Int3 other)
{
double magA = Magnitude, magB = other.Magnitude;
return magA == magB ? 0 : magA > magB ? 1 : -1;
}
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;
@ -187,6 +183,9 @@ namespace Nerd_STF.Mathematics
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;

View File

@ -162,11 +162,7 @@ namespace Nerd_STF.Mathematics
return val;
}
public int CompareTo(Int4 other)
{
double magA = Magnitude, magB = other.Magnitude;
return magA == magB ? 0 : magA > magB ? 1 : -1;
}
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;
@ -203,6 +199,9 @@ namespace Nerd_STF.Mathematics
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;

View File

@ -2,12 +2,12 @@
{
public static class Mathf
{
public const double DegToRad = 0.0174532925199; // Pi / 180
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 RadToDeg = 57.2957795131; // 180 / Pi
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;
@ -26,6 +26,12 @@
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;
@ -75,6 +81,14 @@
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);
@ -83,6 +97,30 @@
}
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;
@ -106,6 +144,16 @@
}
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;

View File

@ -0,0 +1,12 @@
global using System;
global using System.Collections;
global using System.Collections.Generic;
global using System.IO;
global using System.Linq;
global using System.Net.Http;
global using System.Threading;
global using System.Threading.Tasks;
global using Nerd_STF;
global using Nerd_STF.Exceptions;
global using Nerd_STF.Mathematics;
global using Nerd_STF.Mathematics.Geometry;