Lots more stuff. Finished Box2d.
This commit is contained in:
parent
6f42afa15e
commit
7484195f97
100
Nerd_STF/Helpers/GeometryHelper.cs
Normal file
100
Nerd_STF/Helpers/GeometryHelper.cs
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
using Nerd_STF.Mathematics.Abstract;
|
||||||
|
|
||||||
|
namespace Nerd_STF.Helpers;
|
||||||
|
|
||||||
|
internal static class GeometryHelper
|
||||||
|
{
|
||||||
|
public static Float2 Box2dAlongRay(Box2d box, Float2 p)
|
||||||
|
{
|
||||||
|
// This is an approximate system. It's good enough for most purposes
|
||||||
|
// but maybe not all of them. Basically it just makes a ray through the point
|
||||||
|
// and sees where it intersects the box.
|
||||||
|
|
||||||
|
if (box.Contains(p)) return p;
|
||||||
|
|
||||||
|
Float2 c = box.center, m1 = box.Max, m2 = box.Min;
|
||||||
|
|
||||||
|
// Calculates the coordinates of the ray along the points `p` and `c`
|
||||||
|
// for both a reference `x` and a reference `y`.
|
||||||
|
float rayRefX(float x) => c.y - (p.y - c.y) * (c.x - x) / (p.x - c.x);
|
||||||
|
float rayRefY(float y) => c.x - (p.x - c.x) * (c.y - y) / (p.y - c.y);
|
||||||
|
|
||||||
|
// Calculates all possible intersections for the rectangle at once, then
|
||||||
|
// eliminates invalid options.
|
||||||
|
float xSol1 = (m1.y - c.y) * (p.x - c.x) / (p.y - c.y) + c.x,
|
||||||
|
xSol2 = (m2.y - c.y) * (p.x - c.x) / (p.y - c.y) + c.x,
|
||||||
|
ySol1 = (m1.x - c.x) * (p.y - c.y) / (p.x - c.x) + c.y,
|
||||||
|
ySol2 = (m2.x - c.x) * (p.y - c.y) / (p.x - c.x) + c.y;
|
||||||
|
|
||||||
|
const float tolerance = 0.0005f;
|
||||||
|
|
||||||
|
Float2 option1, option2;
|
||||||
|
float dist1, dist2;
|
||||||
|
|
||||||
|
if (float.IsNormal(xSol1) && m1.x - xSol1 >= -tolerance && xSol1 - m2.x >= -tolerance)
|
||||||
|
{
|
||||||
|
// The valid solutions are xSol1 and xSol2.
|
||||||
|
option1 = (xSol1, rayRefX(xSol1));
|
||||||
|
option2 = (xSol2, rayRefX(xSol2));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// The valid solutions are ySol1 and ySol2.
|
||||||
|
option1 = (rayRefY(ySol1), ySol1);
|
||||||
|
option2 = (rayRefY(ySol2), ySol2);
|
||||||
|
}
|
||||||
|
|
||||||
|
dist1 = (p - option1).Magnitude;
|
||||||
|
dist2 = (p - option2).Magnitude;
|
||||||
|
|
||||||
|
// Pick the closest option.
|
||||||
|
if (dist1 < dist2) return option1;
|
||||||
|
else return option2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool LineIntersects(Line a, Line b) =>
|
||||||
|
LineIntersects2d(a, b, CrossSection2d.XY) &&
|
||||||
|
LineIntersects2d(a, b, CrossSection2d.YZ) &&
|
||||||
|
LineIntersects2d(a, b, CrossSection2d.ZX);
|
||||||
|
|
||||||
|
public static bool LineIntersects2d(Line a, Line b, CrossSection2d plane)
|
||||||
|
{
|
||||||
|
Float2 p1 = a.a.GetCrossSection(plane), q1 = a.b.GetCrossSection(plane),
|
||||||
|
p2 = b.a.GetCrossSection(plane), q2 = b.b.GetCrossSection(plane);
|
||||||
|
|
||||||
|
OrientationType o1 = GetOrientation(p1, q1, p2),
|
||||||
|
o2 = GetOrientation(p1, q1, q2),
|
||||||
|
o3 = GetOrientation(p2, q2, p1),
|
||||||
|
o4 = GetOrientation(p2, q2, q1);
|
||||||
|
|
||||||
|
return (o1 != o2 && o3 != o4) ||
|
||||||
|
(o1 == OrientationType.Colinear && PointOnSegmentCo(p1, p2, q1)) ||
|
||||||
|
(o2 == OrientationType.Colinear && PointOnSegmentCo(p1, q2, q1)) ||
|
||||||
|
(o3 == OrientationType.Colinear && PointOnSegmentCo(p2, p1, q2)) ||
|
||||||
|
(o4 == OrientationType.Colinear && PointOnSegmentCo(p2, q1, q2));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool PointOnSegmentCo(Float2 a, Float2 r, Float2 b)
|
||||||
|
{
|
||||||
|
// Just checks the box. Points must be colinear.
|
||||||
|
return r.x <= Mathf.Max(a.x, b.x) && r.x >= Mathf.Min(a.x, b.x) &&
|
||||||
|
r.y <= Mathf.Max(a.y, b.y) && r.y >= Mathf.Min(a.y, b.y);
|
||||||
|
}
|
||||||
|
private static OrientationType GetOrientation(Float2 a, Float2 b, Float2 c)
|
||||||
|
{
|
||||||
|
// Rotation type between the three points.
|
||||||
|
float rot = (b.y - a.y) * (c.x - b.x) -
|
||||||
|
(b.x - a.x) * (c.y - b.y);
|
||||||
|
|
||||||
|
if (rot > 0) return OrientationType.Clockwise;
|
||||||
|
else if (rot < 0) return OrientationType.CounterClockwise;
|
||||||
|
else return OrientationType.Colinear;
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum OrientationType
|
||||||
|
{
|
||||||
|
Colinear,
|
||||||
|
Clockwise,
|
||||||
|
CounterClockwise
|
||||||
|
}
|
||||||
|
}
|
||||||
18
Nerd_STF/Mathematics/Abstract/IContainsGeometry.cs
Normal file
18
Nerd_STF/Mathematics/Abstract/IContainsGeometry.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
namespace Nerd_STF.Mathematics.Abstract;
|
||||||
|
|
||||||
|
public interface IContainsGeometry<T> : IContains<Float3>, IContains<Box2d>, IContains<Line>,
|
||||||
|
IContains<Triangle>, IIntersect<Box2d>, IIntersect<Line>, IIntersect<Triangle>
|
||||||
|
where T : IContainsGeometry<T>, IEquatable<T>
|
||||||
|
{
|
||||||
|
public bool Contains(T obj);
|
||||||
|
public bool Intersects(T obj);
|
||||||
|
|
||||||
|
public bool Contains(IEnumerable<Float3> points);
|
||||||
|
public bool Contains(Fill<Float3> points, int count);
|
||||||
|
public bool Intersects(IEnumerable<Line> lines);
|
||||||
|
public bool Intersects(Fill<Line> lines, int count);
|
||||||
|
|
||||||
|
// later:
|
||||||
|
//public bool Contains<TOther>(TOther obj) where TOther : IPolygon<TOther>;
|
||||||
|
//public bool Intersects<TOther>(TOther obj) where TOther : IPolygon<TOther>;
|
||||||
|
}
|
||||||
@ -1,7 +0,0 @@
|
|||||||
namespace Nerd_STF.Mathematics.Abstract;
|
|
||||||
|
|
||||||
public interface IContainsPartial<T> : IContains<T>
|
|
||||||
where T : IEquatable<T>
|
|
||||||
{
|
|
||||||
public bool ContainsPartially(Line other);
|
|
||||||
}
|
|
||||||
6
Nerd_STF/Mathematics/Abstract/IIntersect.cs
Normal file
6
Nerd_STF/Mathematics/Abstract/IIntersect.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
namespace Nerd_STF.Mathematics.Abstract;
|
||||||
|
|
||||||
|
public interface IIntersect<T> where T : IEquatable<T>
|
||||||
|
{
|
||||||
|
public bool Intersects(T other);
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
namespace Nerd_STF.Mathematics.Abstract;
|
namespace Nerd_STF.Mathematics.Abstract;
|
||||||
|
|
||||||
public interface IPolygon<T> : IAverage<T>, IEquatable<T>,
|
public interface IPolygon<T> : IAverage<T>, IContainsGeometry<T>, IEquatable<T>,
|
||||||
IFloatArray<T>, IGroup<Float3>, IIndexAll<Float3>, IIndexRangeAll<Float3>,
|
IFloatArray<T>, IGroup<Float3>, IIndexAll<Float3>, IIndexRangeAll<Float3>,
|
||||||
ILerp<T, float>, IMedian<T>, ITriangulate
|
ILerp<T, float>, IMedian<T>, ITriangulate
|
||||||
where T : IPolygon<T>
|
where T : IPolygon<T>
|
||||||
@ -11,7 +11,7 @@ public interface IPolygon<T> : IAverage<T>, IEquatable<T>,
|
|||||||
|
|
||||||
public Float3[] GetAllVerts();
|
public Float3[] GetAllVerts();
|
||||||
public Line[] GetOutlines();
|
public Line[] GetOutlines();
|
||||||
|
|
||||||
public static abstract T operator +(T poly, Float3 offset);
|
public static abstract T operator +(T poly, Float3 offset);
|
||||||
public static abstract T operator -(T poly, Float3 offset);
|
public static abstract T operator -(T poly, Float3 offset);
|
||||||
public static abstract T operator *(T poly, float scale);
|
public static abstract T operator *(T poly, float scale);
|
||||||
|
|||||||
@ -2,6 +2,6 @@
|
|||||||
|
|
||||||
public interface ISubdivide<T>
|
public interface ISubdivide<T>
|
||||||
{
|
{
|
||||||
public T Subdivide();
|
public T[] Subdivide();
|
||||||
public T Subdivide(int iterations);
|
public T[] Subdivide(int iterations);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -101,7 +101,7 @@ public record struct Vector2d : IAbsolute<Vector2d>, IAverage<Vector2d>,
|
|||||||
public override int GetHashCode() => base.GetHashCode();
|
public override int GetHashCode() => base.GetHashCode();
|
||||||
public override string ToString() => ToString(Angle.Type.Degrees);
|
public override string ToString() => ToString(Angle.Type.Degrees);
|
||||||
public string ToString(Angle.Type outputType) =>
|
public string ToString(Angle.Type outputType) =>
|
||||||
nameof(Vector2d) + " { Mag = " + magnitude + ", Rot = " + theta.ToString(outputType) + " }";
|
$"{magnitude:0.000} @ {theta.ToString(outputType)}";
|
||||||
|
|
||||||
public Float2 ToXYZ() => new Float2(Mathf.Cos(theta), Mathf.Sin(theta)) * magnitude;
|
public Float2 ToXYZ() => new Float2(Mathf.Cos(theta), Mathf.Sin(theta)) * magnitude;
|
||||||
|
|
||||||
|
|||||||
@ -230,6 +230,14 @@ public record struct Float3 : IAbsolute<Float3>, IAverage<Float3>,
|
|||||||
return new(yaw, pitch, mag);
|
return new(yaw, pitch, mag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Float2 GetCrossSection(CrossSection2d plane) => plane switch
|
||||||
|
{
|
||||||
|
CrossSection2d.XY => XY,
|
||||||
|
CrossSection2d.YZ => YZ,
|
||||||
|
CrossSection2d.ZX => XZ,
|
||||||
|
_ => throw new ArgumentException("Unknown cross section type.")
|
||||||
|
};
|
||||||
|
|
||||||
private bool PrintMembers(StringBuilder builder)
|
private bool PrintMembers(StringBuilder builder)
|
||||||
{
|
{
|
||||||
builder.Append("x = ");
|
builder.Append("x = ");
|
||||||
|
|||||||
264
Nerd_STF/Mathematics/Geometry/Box2d.cs
Normal file
264
Nerd_STF/Mathematics/Geometry/Box2d.cs
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
namespace Nerd_STF.Mathematics.Geometry;
|
||||||
|
|
||||||
|
public class Box2d : IAverage<Box2d>, IClosestTo<Float2>, IContains<Box2d>, IContains<Float2>,
|
||||||
|
IContains<Line>, IContains<Triangle>, IIntersect<Box2d>, IIntersect<Line>,
|
||||||
|
IIntersect<Triangle>, IEquatable<Box2d>, ILerp<Box2d, float>, IMedian<Box2d>,
|
||||||
|
ISubdivide<Box2d>, ITriangulate, IWithinRange<Float2, float>
|
||||||
|
{
|
||||||
|
public float Area => Size.x * Size.y;
|
||||||
|
public float Perimeter => 2 * Size.x + 2 * Size.y;
|
||||||
|
|
||||||
|
public float Height
|
||||||
|
{
|
||||||
|
get => extents.y * 2;
|
||||||
|
set => extents.y = value / 2;
|
||||||
|
}
|
||||||
|
public Float2 Max
|
||||||
|
{
|
||||||
|
get => center + extents;
|
||||||
|
set => extents = value - center;
|
||||||
|
}
|
||||||
|
public Float2 Min
|
||||||
|
{
|
||||||
|
get => center - extents;
|
||||||
|
set => extents = center - value;
|
||||||
|
}
|
||||||
|
public Float2 Size
|
||||||
|
{
|
||||||
|
get => extents * 2;
|
||||||
|
set => extents = value / 2;
|
||||||
|
}
|
||||||
|
public float Width
|
||||||
|
{
|
||||||
|
get => extents.x * 2;
|
||||||
|
set => extents.x = value / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Float2 center, extents;
|
||||||
|
|
||||||
|
public Box2d()
|
||||||
|
{
|
||||||
|
center = Float2.Zero;
|
||||||
|
extents = Float2.Zero;
|
||||||
|
}
|
||||||
|
public Box2d(Float2 center, Float2 extents)
|
||||||
|
{
|
||||||
|
this.center = center;
|
||||||
|
this.extents = extents;
|
||||||
|
}
|
||||||
|
public Box2d(Float2 center, float width, float height)
|
||||||
|
{
|
||||||
|
this.center = center;
|
||||||
|
extents = (width, height);
|
||||||
|
}
|
||||||
|
public Box2d(float centerX, float centerY, float width, float height)
|
||||||
|
{
|
||||||
|
center = (centerX, centerY);
|
||||||
|
extents = (width, height);
|
||||||
|
}
|
||||||
|
public Box2d(Fill<Float2> fill) : this(fill(0), fill(1)) { }
|
||||||
|
public Box2d(Fill<Int2> fill) : this(fill(0), fill(1)) { }
|
||||||
|
public Box2d(Fill<float> fill) : this(fill(0), fill(1), fill(2), fill(3)) { }
|
||||||
|
public Box2d(Fill<int> fill) : this(fill(0), fill(1), fill(2), fill(3)) { }
|
||||||
|
|
||||||
|
public static Box2d FromRange(Float2 min, Float2 max)
|
||||||
|
{
|
||||||
|
if (min.x > max.x) (max.x, min.x) = (min.x, max.x);
|
||||||
|
if (min.y > max.y) (max.y, min.y) = (min.y, max.y);
|
||||||
|
return new((max + min) / 2, (max - min) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Box2d Average(params Box2d[] vals)
|
||||||
|
{
|
||||||
|
(Float2[] centers, Float2[] extents) = SplitArray(vals);
|
||||||
|
return new(Float2.Average(centers), Float2.Average(extents));
|
||||||
|
}
|
||||||
|
public static Box2d Lerp(Box2d a, Box2d b, float t, bool clamp = true) =>
|
||||||
|
FromRange(Float2.Lerp(a.Min, b.Min, t, clamp), Float2.Lerp(a.Max, b.Max, t, clamp));
|
||||||
|
public static (Float2[] centers, Float2[] extents) SplitArray(params Box2d[] vals)
|
||||||
|
{
|
||||||
|
Float2[] centers = new Float2[vals.Length],
|
||||||
|
extents = new Float2[vals.Length];
|
||||||
|
for (int i = 0; i < vals.Length; i++)
|
||||||
|
{
|
||||||
|
centers[i] = vals[i].center;
|
||||||
|
extents[i] = vals[i].extents;
|
||||||
|
}
|
||||||
|
return (centers, extents);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Float2 AlongRay(Float2 p) => GeometryHelper.Box2dAlongRay(this, p);
|
||||||
|
public Float2 ClosestTo(Float2 p) => Float2.Clamp(p, Min, Max);
|
||||||
|
|
||||||
|
public bool Contains(Float2 point)
|
||||||
|
{
|
||||||
|
Float2 diff = Float2.Absolute(point - center);
|
||||||
|
return diff.x <= extents.x && diff.y <= extents.y;
|
||||||
|
}
|
||||||
|
public bool Contains(Float3 point) => Contains((Float2)point);
|
||||||
|
|
||||||
|
public bool Contains(Box2d box) => Contains(box.Min) && Contains(box.Max);
|
||||||
|
public bool Contains(Line line) => Contains(line.a) && Contains(line.b);
|
||||||
|
public bool Contains(Triangle tri) =>
|
||||||
|
Contains(tri.a) && Contains(tri.b) & Contains(tri.c);
|
||||||
|
public bool Contains(IEnumerable<Float2> points)
|
||||||
|
{
|
||||||
|
foreach (Float3 p in points) if (!Contains(p)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public bool Contains(IEnumerable<Float3> points)
|
||||||
|
{
|
||||||
|
foreach (Float3 p in points) if (!Contains(p)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public bool Contains(Fill<Float2> points, int count)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; i++) if (!Contains(points(i))) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public bool Contains(Fill<Float3> points, int count)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; i++) if (!Contains(points(i))) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Intersects(Box2d box)
|
||||||
|
{
|
||||||
|
if (Contains(box) || box.Contains(this)) return true;
|
||||||
|
|
||||||
|
// A bunch of brute force work but it's still decently fast.
|
||||||
|
(Line top, Line right, Line bottom, Line left) = box.GetOutlines();
|
||||||
|
return Intersects(top) || Intersects(right) || Intersects(bottom) || Intersects(left);
|
||||||
|
}
|
||||||
|
public bool Intersects(Line line)
|
||||||
|
{
|
||||||
|
if (Contains(line)) return true;
|
||||||
|
|
||||||
|
(Line top, Line right, Line bottom, Line left) = GetOutlines();
|
||||||
|
return GeometryHelper.LineIntersects2d(line, top, CrossSection2d.XY) ||
|
||||||
|
GeometryHelper.LineIntersects2d(line, right, CrossSection2d.XY) ||
|
||||||
|
GeometryHelper.LineIntersects2d(line, bottom, CrossSection2d.XY) ||
|
||||||
|
GeometryHelper.LineIntersects2d(line, left, CrossSection2d.XY);
|
||||||
|
}
|
||||||
|
public bool Intersects(Triangle tri)
|
||||||
|
{
|
||||||
|
if (Contains(tri) || tri.Contains(this)) return true;
|
||||||
|
return Intersects(tri.AB) || Intersects(tri.BC) || Intersects(tri.CA);
|
||||||
|
}
|
||||||
|
public bool Intersects(IEnumerable<Line> lines)
|
||||||
|
{
|
||||||
|
foreach (Line l in lines) if (Intersects(l)) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public bool Intersects(Fill<Line> lines, int count)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; i++) if (Intersects(lines(i))) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Float2 LerpAcrossOutline(float t, bool clamp = true)
|
||||||
|
{
|
||||||
|
if (!clamp) return LerpAcrossOutline(Mathf.AbsoluteMod(t, 1), true);
|
||||||
|
|
||||||
|
(Line top, Line right, Line bottom, Line left) = GetOutlines();
|
||||||
|
float weightTB = top.Length / Perimeter, weightLR = left.Length / Perimeter;
|
||||||
|
|
||||||
|
if (t < weightTB) return (Float2)top.LerpAcross(t / weightTB);
|
||||||
|
else if (t < 0.5f) return (Float2)right.LerpAcross((t - weightTB) / weightLR);
|
||||||
|
else if (t < 0.5f + weightTB) return (Float2)bottom.LerpAcross((t - 0.5f) / weightTB);
|
||||||
|
else return (Float2)left.LerpAcross((t - 0.5f - weightTB) / weightLR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
{
|
||||||
|
if (obj is null) return false;
|
||||||
|
else if (obj is Box2d box2d) return Equals(box2d);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public bool Equals(Box2d? other) => other is not null &&
|
||||||
|
center == other.center && extents == other.extents;
|
||||||
|
public override int GetHashCode() => base.GetHashCode();
|
||||||
|
public override string ToString() => $"{nameof(Box2d)} {{ " +
|
||||||
|
$"Min = {Min}, Max = {Max} }}";
|
||||||
|
|
||||||
|
// todo: lerp across the rectangle bounds.
|
||||||
|
// also todo: add contains and contains partially for the polygon interface.
|
||||||
|
// also also todo: add the encapsulate method
|
||||||
|
|
||||||
|
public (Float2 topLeft, Float2 topRight, Float2 bottomRight, Float2 bottomLeft) GetCorners()
|
||||||
|
{
|
||||||
|
float top = center.y + extents.y, bottom = center.y - extents.y,
|
||||||
|
left = center.x - extents.x, right = center.x + extents.x;
|
||||||
|
return ((top, left), (top, right), (bottom, right), (bottom, left));
|
||||||
|
}
|
||||||
|
public (Line top, Line right, Line bottom, Line left) GetOutlines()
|
||||||
|
{
|
||||||
|
(Float2 topLeft, Float2 topRight, Float2 bottomRight, Float2 bottomLeft) = GetCorners();
|
||||||
|
return ((topLeft, topRight), (topRight, bottomRight),
|
||||||
|
(bottomRight, bottomLeft), (bottomLeft, topLeft));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Box2d[] Subdivide()
|
||||||
|
{
|
||||||
|
Float2 halfExtents = extents / 2;
|
||||||
|
(Float2 topLeft, Float2 topRight, Float2 bottomRight, Float2 bottomLeft) = GetCorners();
|
||||||
|
return new Box2d[]
|
||||||
|
{
|
||||||
|
new(Float2.Lerp(center, topLeft, 0.5f), halfExtents),
|
||||||
|
new(Float2.Lerp(center, topRight, 0.5f), halfExtents),
|
||||||
|
new(Float2.Lerp(center, bottomRight, 0.5f), halfExtents),
|
||||||
|
new(Float2.Lerp(center, bottomLeft, 0.5f), halfExtents)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
public Box2d[] Subdivide(int iterations)
|
||||||
|
{
|
||||||
|
Box2d[] active = new[] { this };
|
||||||
|
for (int i = 0; i < iterations; i++)
|
||||||
|
{
|
||||||
|
List<Box2d> newBoxes = new();
|
||||||
|
foreach (Box2d box in active) newBoxes.AddRange(box.Subdivide());
|
||||||
|
active = newBoxes.ToArray();
|
||||||
|
}
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Triangle[] Triangulate()
|
||||||
|
{
|
||||||
|
(Float2 topLeft, Float2 topRight, Float2 bottomRight, Float2 bottomLeft) = GetCorners();
|
||||||
|
return new Triangle[]
|
||||||
|
{
|
||||||
|
(bottomLeft, topLeft, topRight),
|
||||||
|
(topRight, bottomRight, bottomLeft)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool WithinRange(Float2 point, float range)
|
||||||
|
{
|
||||||
|
// First, get the distance to each edge.
|
||||||
|
float top = center.y + extents.y, bottom = center.y - extents.y,
|
||||||
|
left = center.x - extents.x, right = center.x + extents.x;
|
||||||
|
(Float2 topLeft, Float2 topRight, Float2 bottomRight, Float2 bottomLeft) = GetCorners();
|
||||||
|
|
||||||
|
// Positive if inside the box, but that doesn't matter.
|
||||||
|
float toTop = Mathf.Absolute(top - point.y),
|
||||||
|
toBottom = Mathf.Absolute(point.y - bottom),
|
||||||
|
toLeft = Mathf.Absolute(left - point.x),
|
||||||
|
toRight = Mathf.Absolute(point.x - right);
|
||||||
|
|
||||||
|
// Then get the distance to each corner.
|
||||||
|
float toTL = (topLeft - point).Magnitude,
|
||||||
|
toTR = (topRight - point).Magnitude,
|
||||||
|
toBR = (bottomRight - point).Magnitude,
|
||||||
|
toBL = (bottomLeft - point).Magnitude;
|
||||||
|
|
||||||
|
// Get the minimum of them all and compare.
|
||||||
|
return Mathf.Min(toTop, toBottom, toRight, toLeft,
|
||||||
|
toTL, toTR, toBL, toBR) <= range;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ClosestToMethod
|
||||||
|
{
|
||||||
|
Iterative,
|
||||||
|
RayCalculations
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
public class Line : IAverage<Line>, IClosestTo<Float3>, IContains<Float3>, IEquatable<Line>,
|
public class Line : IAverage<Line>, IClosestTo<Float3>, IContains<Float3>, IEquatable<Line>,
|
||||||
IFloatArray<Line>, IFromTuple<Line, (Float3 a, Float3 b)>, IGroup<Float3>,
|
IFloatArray<Line>, IFromTuple<Line, (Float3 a, Float3 b)>, IGroup<Float3>,
|
||||||
IIndexAll<Float3>, IIndexRangeAll<Float3>, ILerp<Line, float>, IMedian<Line>,
|
IIndexAll<Float3>, IIndexRangeAll<Float3>, ILerp<Line, float>, IIntersect<Line>,
|
||||||
IPresets3d<Line>, ISplittable<Line, (Float3[] As, Float3[] Bs)>, ISubdivide<Line[]>,
|
IMedian<Line>, IPresets3d<Line>, ISplittable<Line, (Float3[] As, Float3[] Bs)>,
|
||||||
IWithinRange<Float3, float>
|
ISubdivide<Line>, IWithinRange<Float3, float>
|
||||||
{
|
{
|
||||||
public static Line Back => (Float3.Zero, Float3.Back);
|
public static Line Back => (Float3.Zero, Float3.Back);
|
||||||
public static Line Down => (Float3.Zero, Float3.Down);
|
public static Line Down => (Float3.Zero, Float3.Down);
|
||||||
@ -152,6 +152,9 @@ public class Line : IAverage<Line>, IClosestTo<Float3>, IContains<Float3>, IEqua
|
|||||||
yield return b;
|
yield return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Float3 LerpAcross(float t, bool clamp = true) =>
|
||||||
|
Float3.Lerp(a, b, t, clamp);
|
||||||
|
|
||||||
public bool Equals(Line? other) => other is not null && a == other.a && b == other.b;
|
public bool Equals(Line? other) => other is not null && a == other.a && b == other.b;
|
||||||
public override bool Equals(object? obj)
|
public override bool Equals(object? obj)
|
||||||
{
|
{
|
||||||
@ -199,6 +202,8 @@ public class Line : IAverage<Line>, IClosestTo<Float3>, IContains<Float3>, IEqua
|
|||||||
&& point.x <= float.Max(a.x, b.x);
|
&& point.x <= float.Max(a.x, b.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Intersects(Line line) => GeometryHelper.LineIntersects(this, line);
|
||||||
|
|
||||||
public Line[] Subdivide()
|
public Line[] Subdivide()
|
||||||
{
|
{
|
||||||
Float3 midPoint = Float3.Lerp(a, b, 0.5f);
|
Float3 midPoint = Float3.Lerp(a, b, 0.5f);
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
namespace Nerd_STF.Mathematics.Geometry;
|
namespace Nerd_STF.Mathematics.Geometry;
|
||||||
|
|
||||||
public class Quadrilateral : IClosestTo<Float3>, IContains<Float3>, IContainsPartial<Line>,
|
public class Quadrilateral : IClosestTo<Float3>,
|
||||||
IFromTuple<Quadrilateral, (Float3 a, Float3 b, Float3 c, Float3 d)>, IPolygon<Quadrilateral>,
|
IFromTuple<Quadrilateral, (Float3 a, Float3 b, Float3 c, Float3 d)>, IPolygon<Quadrilateral>,
|
||||||
ISplittable<Quadrilateral, (Float3[] As, Float3[] Bs, Float3[] Cs, Float3[] Ds)>,
|
ISplittable<Quadrilateral, (Float3[] As, Float3[] Bs, Float3[] Cs, Float3[] Ds)>,
|
||||||
ISubdivide<Quadrilateral[]>, ITriangulate, IWithinRange<Float3, float>
|
ISubdivide<Quadrilateral>, ITriangulate, IWithinRange<Float3, float>
|
||||||
{
|
{
|
||||||
public float Area
|
public float Area
|
||||||
{
|
{
|
||||||
@ -156,7 +156,7 @@ public class Quadrilateral : IClosestTo<Float3>, IContains<Float3>, IContainsPar
|
|||||||
float x3, float y3, float z3, float x4, float y4, float z4)
|
float x3, float y3, float z3, float x4, float y4, float z4)
|
||||||
{
|
{
|
||||||
a = (x1, y1, z1);
|
a = (x1, y1, z1);
|
||||||
b = (x2, y2, z3);
|
b = (x2, y2, z2);
|
||||||
c = (x3, y3, z3);
|
c = (x3, y3, z3);
|
||||||
d = (x4, y4, z4);
|
d = (x4, y4, z4);
|
||||||
}
|
}
|
||||||
@ -321,15 +321,63 @@ public class Quadrilateral : IClosestTo<Float3>, IContains<Float3>, IContainsPar
|
|||||||
if (!Contains(Float3.Lerp(line.a, line.b, t), tolerance)) return false;
|
if (!Contains(Float3.Lerp(line.a, line.b, t), tolerance)) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
public bool Contains(Triangle tri) => Contains(tri.a) && Contains(tri.b) && Contains(tri.c);
|
||||||
public bool ContainsPartially(Line line) => ContainsPartially(line, 0.05f);
|
public bool Contains(Quadrilateral quad) => Contains(quad.a) && Contains(quad.b) &&
|
||||||
public bool ContainsPartially(Line line, float tolerance, float step = Calculus.DefaultStep)
|
Contains(quad.c) && Contains(quad.d);
|
||||||
|
public bool Contains(Box2d box)
|
||||||
|
{
|
||||||
|
(Float2 topLeft, Float2 topRight, Float2 bottomRight, Float2 bottomLeft) = box.GetCorners();
|
||||||
|
return Contains(topLeft) || Contains(topRight) ||
|
||||||
|
Contains(bottomRight) || Contains(bottomLeft);
|
||||||
|
}
|
||||||
|
public bool Contains(IEnumerable<Float3> points)
|
||||||
|
{
|
||||||
|
foreach (Float3 point in points) if (!Contains(point)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public bool Contains(Fill<Float3> points, int count)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; i++) if (!Contains(points(i))) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Intersects(Line line) => Intersects(line, 0.05f);
|
||||||
|
public bool Intersects(Line line, float tolerance, float step = Calculus.DefaultStep)
|
||||||
{
|
{
|
||||||
for (float t = 0; t <= 1; t += step)
|
for (float t = 0; t <= 1; t += step)
|
||||||
if (Contains(Float3.Lerp(line.a, line.b, t), tolerance))
|
if (Contains(Float3.Lerp(line.a, line.b, t), tolerance))
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
public bool Intersects(Quadrilateral quad)
|
||||||
|
{
|
||||||
|
if (Contains(quad) || quad.Contains(this)) return true;
|
||||||
|
return Intersects(quad.AB) || Intersects(quad.BC) ||
|
||||||
|
Intersects(quad.CD) || Intersects(quad.DA);
|
||||||
|
}
|
||||||
|
public bool Intersects(Box2d box)
|
||||||
|
{
|
||||||
|
if (Contains(box) || box.Contains(this)) return true;
|
||||||
|
|
||||||
|
(Line top, Line right, Line bottom, Line left) = box.GetOutlines();
|
||||||
|
return Intersects(top) || Intersects(right) ||
|
||||||
|
Intersects(bottom) || Intersects(left);
|
||||||
|
}
|
||||||
|
public bool Intersects(Triangle tri)
|
||||||
|
{
|
||||||
|
if (Contains(tri) || tri.Contains(this)) return true;
|
||||||
|
return Intersects(tri.AB) || Intersects(tri.BC) || Intersects(tri.CA);
|
||||||
|
}
|
||||||
|
public bool Intersects(IEnumerable<Line> lines)
|
||||||
|
{
|
||||||
|
foreach (Line l in lines) if (Contains(l)) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public bool Intersects(Fill<Line> lines, int count)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; i++) if (Contains(lines(i))) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public Float3[] GetAllVerts() => new[] { a, b, c, d };
|
public Float3[] GetAllVerts() => new[] { a, b, c, d };
|
||||||
public Line[] GetOutlines() => new[] { AB, BC, CD, DA };
|
public Line[] GetOutlines() => new[] { AB, BC, CD, DA };
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
namespace Nerd_STF.Mathematics.Geometry;
|
namespace Nerd_STF.Mathematics.Geometry;
|
||||||
|
|
||||||
public class Triangle : IClosestTo<Float3>, IContains<Float3>, IContainsPartial<Line>,
|
public class Triangle : IClosestTo<Float3>,
|
||||||
IFromTuple<Triangle, (Float3 a, Float3 b, Float3 c)>, IPolygon<Triangle>,
|
IFromTuple<Triangle, (Float3 a, Float3 b, Float3 c)>, IPolygon<Triangle>,
|
||||||
ISplittable<Triangle, (Float3[] As, Float3[] Bs, Float3[] Cs)>,
|
ISplittable<Triangle, (Float3[] As, Float3[] Bs, Float3[] Cs)>,
|
||||||
ISubdivide<Triangle[]>, IWithinRange<Float3, float>
|
ISubdivide<Triangle>, IWithinRange<Float3, float>
|
||||||
{
|
{
|
||||||
public float Area
|
public float Area
|
||||||
{
|
{
|
||||||
@ -224,15 +224,55 @@ public class Triangle : IClosestTo<Float3>, IContains<Float3>, IContainsPartial<
|
|||||||
if (!Contains(Float3.Lerp(line.a, line.b, t), tolerance)) return false;
|
if (!Contains(Float3.Lerp(line.a, line.b, t), tolerance)) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
public bool Contains(Box2d box)
|
||||||
|
{
|
||||||
|
(Float2 topLeft, Float2 topRight, Float2 bottomRight, Float2 bottomLeft) = box.GetCorners();
|
||||||
|
return Contains(topLeft) || Contains(topRight) ||
|
||||||
|
Contains(bottomRight) || Contains(bottomLeft);
|
||||||
|
}
|
||||||
|
public bool Contains(Triangle tri) => Contains(tri.a) && Contains(tri.b) && Contains(tri.c);
|
||||||
|
public bool Contains(IEnumerable<Float3> points)
|
||||||
|
{
|
||||||
|
foreach (Float3 point in points) if (!Contains(point)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public bool Contains(Fill<Float3> points, int count)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; i++) if (!Contains(points(i))) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public bool ContainsPartially(Line line) => ContainsPartially(line, 0.05f);
|
public bool Intersects(Line line) => Intersects(line, 0.05f);
|
||||||
public bool ContainsPartially(Line line, float tolerance, float step = Calculus.DefaultStep)
|
public bool Intersects(Line line, float tolerance, float step = Calculus.DefaultStep)
|
||||||
{
|
{
|
||||||
for (float t = 0; t <= 1; t += step)
|
for (float t = 0; t <= 1; t += step)
|
||||||
if (Contains(Float3.Lerp(line.a, line.b, t), tolerance))
|
if (Contains(Float3.Lerp(line.a, line.b, t), tolerance))
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
public bool Intersects(Box2d box)
|
||||||
|
{
|
||||||
|
if (Contains(box) || box.Contains(this)) return true;
|
||||||
|
|
||||||
|
(Line top, Line right, Line bottom, Line left) = box.GetOutlines();
|
||||||
|
return Intersects(top) || Intersects(right) ||
|
||||||
|
Intersects(bottom) || Intersects(left);
|
||||||
|
}
|
||||||
|
public bool Intersects(Triangle tri)
|
||||||
|
{
|
||||||
|
if (Contains(tri) || tri.Contains(this)) return true;
|
||||||
|
return Intersects(tri.AB) || Intersects(tri.BC) || Intersects(tri.CA);
|
||||||
|
}
|
||||||
|
public bool Intersects(IEnumerable<Line> lines)
|
||||||
|
{
|
||||||
|
foreach (Line l in lines) if (Contains(l)) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public bool Intersects(Fill<Line> lines, int count)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; i++) if (Contains(lines(i))) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public Float3[] GetAllVerts() => new[] { a, b, c };
|
public Float3[] GetAllVerts() => new[] { a, b, c };
|
||||||
public Line[] GetOutlines() => new[] { AB, BC, CA };
|
public Line[] GetOutlines() => new[] { AB, BC, CA };
|
||||||
|
|||||||
@ -448,7 +448,7 @@ public static class Mathf
|
|||||||
else return CordicHelper.CalculateHyperTrig(value, 16).sinh;
|
else return CordicHelper.CalculateHyperTrig(value, 16).sinh;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float SolveBisection(Equation equ, float initialA, float initialB, float tolerance = 1e-5f,
|
public static float SolveBisection(Equation equ, float initialA, float initialB, float tolerance = 1e-3f,
|
||||||
int maxIterations = 1000)
|
int maxIterations = 1000)
|
||||||
{
|
{
|
||||||
if (equ(initialA) == 0) return initialA;
|
if (equ(initialA) == 0) return initialA;
|
||||||
@ -484,10 +484,10 @@ public static class Mathf
|
|||||||
|
|
||||||
return guessMid;
|
return guessMid;
|
||||||
}
|
}
|
||||||
public static float SolveEquation(Equation equ, float initial, float tolerance = 1e-5f,
|
public static float SolveEquation(Equation equ, float initial, float tolerance = 1e-3f,
|
||||||
float step = Calculus.DefaultStep, int maxIterations = 1000) =>
|
float step = Calculus.DefaultStep, int maxIterations = 1000) =>
|
||||||
SolveNewton(equ, initial, tolerance, step, maxIterations);
|
SolveNewton(equ, initial, tolerance, step, maxIterations);
|
||||||
public static float SolveNewton(Equation equ, float initial, float tolerance = 1e-5f,
|
public static float SolveNewton(Equation equ, float initial, float tolerance = 1e-3f,
|
||||||
float step = Calculus.DefaultStep, int maxIterations = 1000)
|
float step = Calculus.DefaultStep, int maxIterations = 1000)
|
||||||
{
|
{
|
||||||
if (equ(initial) == 0) return initial;
|
if (equ(initial) == 0) return initial;
|
||||||
@ -511,7 +511,7 @@ public static class Mathf
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float Sqrt(float value) => SolveNewton(x => x * x - value, 1);
|
public static float Sqrt(float value, float tolerance = 1e-3f) => SolveNewton(x => x * x - value, value / 2, tolerance);
|
||||||
|
|
||||||
// Known as stdev
|
// Known as stdev
|
||||||
public static float StandardDeviation(params float[] vals) => Sqrt(Variance(vals));
|
public static float StandardDeviation(params float[] vals) => Sqrt(Variance(vals));
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user