From f685517d9e05c37351b8f20938a77e7fe0f6f261 Mon Sep 17 00:00:00 2001 From: That_One_Nerd Date: Mon, 21 Aug 2023 10:33:43 -0400 Subject: [PATCH] Wrote one of the final drafts for the Line class. --- Changelog.md | 3 +- Nerd_STF/Mathematics/Abstract/IWithinRange.cs | 12 + Nerd_STF/Mathematics/Geometry/Line.cs | 244 ++++++++++++++++++ Nerd_STF/Mathematics/Geometry/Polygon.cs | 12 + Nerd_STF/Mathematics/Geometry/Triangle.cs | 12 + Nerd_STF/Nerd_STF.csproj | 4 - 6 files changed, 282 insertions(+), 5 deletions(-) create mode 100644 Nerd_STF/Mathematics/Abstract/IWithinRange.cs create mode 100644 Nerd_STF/Mathematics/Geometry/Line.cs create mode 100644 Nerd_STF/Mathematics/Geometry/Polygon.cs create mode 100644 Nerd_STF/Mathematics/Geometry/Triangle.cs diff --git a/Changelog.md b/Changelog.md index ee2fd92..8ba4209 100644 --- a/Changelog.md +++ b/Changelog.md @@ -18,12 +18,13 @@ Here's the full changelog: * Geometry - Box2d (REMEMBER: name change) - Box3d (REMEMBER: name change) - - Line - Polygon - Quadrilateral - Sphere - Triangle - Vert + = Rewrote all of `Line` + TODO: Compare me to the original line and note changes. * NumberSystems * Complex = Replaced all references to `Vert` with references to `Float3` diff --git a/Nerd_STF/Mathematics/Abstract/IWithinRange.cs b/Nerd_STF/Mathematics/Abstract/IWithinRange.cs new file mode 100644 index 0000000..cb9b296 --- /dev/null +++ b/Nerd_STF/Mathematics/Abstract/IWithinRange.cs @@ -0,0 +1,12 @@ +using System.Numerics; + +namespace Nerd_STF.Mathematics.Abstract; + +public interface IWithinRange +{ + public bool WithinRange(TSub obj, TNumber range) where TNumber : INumber; +} +public interface IWithinRange where TNumber : INumber +{ + public bool WithinRange(TSub obj, TNumber range); +} diff --git a/Nerd_STF/Mathematics/Geometry/Line.cs b/Nerd_STF/Mathematics/Geometry/Line.cs new file mode 100644 index 0000000..ab21320 --- /dev/null +++ b/Nerd_STF/Mathematics/Geometry/Line.cs @@ -0,0 +1,244 @@ +namespace Nerd_STF.Mathematics.Geometry; + +public class Line : IAverage, IClosestTo, IContains, IEquatable, + IFloatArray, IFromTuple, IGroup, + ILerp, IMedian, IPresets3d, + ISplittable, ISubdivide, + IWithinRange +{ + public static Line Back => (Float3.Zero, Float3.Back); + public static Line Down => (Float3.Zero, Float3.Down); + public static Line Forward => (Float3.Zero, Float3.Forward); + public static Line Left => (Float3.Zero, Float3.Left); + public static Line Right => (Float3.Zero, Float3.Right); + public static Line Up => (Float3.Zero, Float3.Up); + + public static Line One => (Float3.Zero, Float3.One); + public static Line Zero => (Float3.Zero, Float3.Zero); + + public float Length => (b - a).Magnitude; + public Float3 Midpoint => (a + b) / 2; + public float Slope => (b.y - a.y) / (b.x - a.x); + + public Float3 a, b; + + public Line() : this(Float3.Zero, Float3.Zero) { } + public Line(Float3 a, Float3 b) + { + this.a = a; + this.b = b; + } + public Line(float x1, float y1, float x2, float y2) + { + a = (x1, y1, 0); + b = (x2, y2, 0); + } + public Line(float x1, float y1, float z1, float x2, float y2, float z2) + { + a = (x1, y1, z1); + b = (x2, y2, z2); + } + public Line(Fill fill) : this(fill(0), fill(1)) { } + public Line(Fill fill) : this(fill(0), fill(1)) { } + public Line(Fill fill) : this(fill(0), fill(1), fill(2), + fill(3), fill(4), fill(5)) { } + public Line(Fill fill) : this(fill(0), fill(1), fill(2), + fill(3), fill(4), fill(5)) { } + + public Float3 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 Float3 this[Index index] + { + get => this[index.IsFromEnd ? 2 - index.Value : index.Value]; + set => this[index.IsFromEnd ? 2 - index.Value : index.Value] = value; + } + public Float3[] this[Range range] + { + get + { + int start = range.Start.IsFromEnd ? 2 - range.Start.Value : range.Start.Value; + int end = range.End.IsFromEnd ? 2 - range.End.Value : range.End.Value; + List res = new(); + for (int i = start; i < end; i++) res.Add(this[i]); + return res.ToArray(); + } + set + { + int start = range.Start.IsFromEnd ? 2 - range.Start.Value : range.Start.Value; + int end = range.End.IsFromEnd ? 2 - range.End.Value : range.End.Value; + for (int i = start; i < end; i++) this[i] = value[i]; + } + } + + public static Line Average(params Line[] lines) + { + (Float3[] As, Float3[] Bs) = SplitArray(lines); + return (Float3.Average(As), Float3.Average(Bs)); + } + public static Line Lerp(Line a, Line b, float t, bool clamp = true) => + (Float3.Lerp(a.a, b.a, t, clamp), Float3.Lerp(a.b, b.b, t, clamp)); + public static Line Median(params Line[] lines) + { + (Float3[] As, Float3[] Bs) = SplitArray(lines); + return (Float3.Median(As), Float3.Median(Bs)); + } + public static (Float3[] As, Float3[] Bs) SplitArray(params Line[] lines) + { + Float3[] As = new Float3[lines.Length], + Bs = new Float3[lines.Length]; + + for (int i = 0; i < lines.Length; i++) + { + As[i] = lines[i].a; + Bs[i] = lines[i].b; + } + + return (As, Bs); + } + + public static float[] ToFloatArrayAll(params Line[] vals) + { + float[] result = new float[6 * vals.Length]; + for (int i = 0; i < vals.Length; i++) + { + int p = i * 6; + result[p + 0] = vals[i].a.x; + result[p + 1] = vals[i].a.y; + result[p + 2] = vals[i].a.z; + result[p + 3] = vals[i].b.x; + result[p + 4] = vals[i].b.y; + result[p + 5] = vals[i].b.z; + } + return result; + } + + public Float3[] ToArray() => new[] { a, b }; + public Fill ToFill() + { + Line @this = this; + return i => @this[i]; + } + public List ToList() => new() { a, b }; + + public float[] ToFloatArray() => new[] { a.x, a.y, a.z, b.x, b.y, b.z }; + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public IEnumerator GetEnumerator() + { + yield return a; + yield return b; + } + + public bool Equals(Line? other) => other is not null && a == other.a && b == other.b; + public override bool Equals(object? obj) + { + if (obj is null) return false; + else if (obj is Line line) return Equals(line); + else return false; + } + public override int GetHashCode() => base.GetHashCode(); + + public override string ToString() + { + StringBuilder builder = new(); + builder.Append(nameof(Line)); + builder.Append(" { "); + builder.Append(a); + builder.Append(", "); + builder.Append(b); + builder.Append(" }"); + return builder.ToString(); + } + + public Float3 ClosestTo(Float3 item) => ClosestTo(item, Calculus.DefaultStep); + public Float3 ClosestTo(Float3 item, float step) + { + // Probably could optimize this with some weird formula but whatever. + // This isn't the optimization update. + + (Float3 point, float dist) min = (a, float.MaxValue); + for (float f = 0; f < 1; f += step) + { + Float3 check = Float3.Lerp(a, b, f); + float dist = (check - item).Magnitude; + + if (dist < min.dist) min = (check, dist); + } + return min.point; + } + + public bool Contains(Float3 point) + { + float left = (point.y - a.y) / (b.y - a.y), + right = (point.x - a.x) / (b.x - a.x); + + return left == right && point.x >= float.Min(a.x, b.x) + && point.x <= float.Max(a.x, b.x); + } + + public Line[] Subdivide() + { + Float3 midPoint = Float3.Lerp(a, b, 0.5f); + return new Line[] { (a, midPoint), (midPoint, b) }; + } + public Line[] Subdivide(int iterations) + { + Line[] result = new Line[iterations + 4]; + float step = 1f / (iterations + 1); + + int i = 0; + float prev = 0; + for (float f = step; f <= 1; f += step) + { + result[i] = (Float3.Lerp(a, b, prev), Float3.Lerp(a, b, f)); + prev = f; + i++; + } + + return result; + } + + public bool WithinRange(Float3 point, float range) => + WithinRange(point, range, Calculus.DefaultStep); + public bool WithinRange(Float3 point, float range, float step) + { + // I could probably replace this with a more optimized solution (such as a + // modified version of `Contains(Float3)`), but hey, this isn't the optimization + // update, is it? + + for (float f = 0; f <= 1; f += step) + { + Float3 check = Float3.Lerp(a, b, f); + + // I could make a new line but that seems wasteful. + float dist = (check - point).Magnitude; + if (dist <= range) return true; + } + + return false; + } + + public static implicit operator Line((Float3 a, Float3 b) tuple) => new(tuple.a, tuple.b); +} diff --git a/Nerd_STF/Mathematics/Geometry/Polygon.cs b/Nerd_STF/Mathematics/Geometry/Polygon.cs new file mode 100644 index 0000000..8c564dc --- /dev/null +++ b/Nerd_STF/Mathematics/Geometry/Polygon.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Nerd_STF.Mathematics.Geometry +{ + public class Polygon + { + } +} diff --git a/Nerd_STF/Mathematics/Geometry/Triangle.cs b/Nerd_STF/Mathematics/Geometry/Triangle.cs new file mode 100644 index 0000000..3cde921 --- /dev/null +++ b/Nerd_STF/Mathematics/Geometry/Triangle.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Nerd_STF.Mathematics.Geometry +{ + public class Triangle + { + } +} diff --git a/Nerd_STF/Nerd_STF.csproj b/Nerd_STF/Nerd_STF.csproj index c805cf8..5bb893b 100644 --- a/Nerd_STF/Nerd_STF.csproj +++ b/Nerd_STF/Nerd_STF.csproj @@ -66,8 +66,4 @@ Anyway, that's everything in this update. Again, pretty small, but meaningful no - - - -