Lots more stuff. Finished Box2d.

This commit is contained in:
That_One_Nerd 2023-10-26 15:08:46 -04:00
parent 6f42afa15e
commit 7484195f97
13 changed files with 511 additions and 29 deletions

View 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
}
}

View 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>;
}

View File

@ -1,7 +0,0 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IContainsPartial<T> : IContains<T>
where T : IEquatable<T>
{
public bool ContainsPartially(Line other);
}

View File

@ -0,0 +1,6 @@
namespace Nerd_STF.Mathematics.Abstract;
public interface IIntersect<T> where T : IEquatable<T>
{
public bool Intersects(T other);
}

View File

@ -1,6 +1,6 @@
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>,
ILerp<T, float>, IMedian<T>, ITriangulate
where T : IPolygon<T>

View File

@ -2,6 +2,6 @@
public interface ISubdivide<T>
{
public T Subdivide();
public T Subdivide(int iterations);
public T[] Subdivide();
public T[] Subdivide(int iterations);
}

View File

@ -101,7 +101,7 @@ public record struct Vector2d : IAbsolute<Vector2d>, IAverage<Vector2d>,
public override int GetHashCode() => base.GetHashCode();
public override string ToString() => ToString(Angle.Type.Degrees);
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;

View File

@ -230,6 +230,14 @@ public record struct Float3 : IAbsolute<Float3>, IAverage<Float3>,
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)
{
builder.Append("x = ");

View 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
}
}

View File

@ -2,9 +2,9 @@
public class Line : IAverage<Line>, IClosestTo<Float3>, IContains<Float3>, IEquatable<Line>,
IFloatArray<Line>, IFromTuple<Line, (Float3 a, Float3 b)>, IGroup<Float3>,
IIndexAll<Float3>, IIndexRangeAll<Float3>, ILerp<Line, float>, IMedian<Line>,
IPresets3d<Line>, ISplittable<Line, (Float3[] As, Float3[] Bs)>, ISubdivide<Line[]>,
IWithinRange<Float3, float>
IIndexAll<Float3>, IIndexRangeAll<Float3>, ILerp<Line, float>, IIntersect<Line>,
IMedian<Line>, IPresets3d<Line>, ISplittable<Line, (Float3[] As, Float3[] Bs)>,
ISubdivide<Line>, IWithinRange<Float3, float>
{
public static Line Back => (Float3.Zero, Float3.Back);
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;
}
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 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);
}
public bool Intersects(Line line) => GeometryHelper.LineIntersects(this, line);
public Line[] Subdivide()
{
Float3 midPoint = Float3.Lerp(a, b, 0.5f);

View File

@ -1,9 +1,9 @@
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>,
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
{
@ -156,7 +156,7 @@ public class Quadrilateral : IClosestTo<Float3>, IContains<Float3>, IContainsPar
float x3, float y3, float z3, float x4, float y4, float z4)
{
a = (x1, y1, z1);
b = (x2, y2, z3);
b = (x2, y2, z2);
c = (x3, y3, z3);
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;
return true;
}
public bool Contains(Triangle tri) => Contains(tri.a) && Contains(tri.b) && Contains(tri.c);
public bool Contains(Quadrilateral quad) => Contains(quad.a) && Contains(quad.b) &&
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 ContainsPartially(Line line) => ContainsPartially(line, 0.05f);
public bool ContainsPartially(Line line, float tolerance, float step = Calculus.DefaultStep)
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)
if (Contains(Float3.Lerp(line.a, line.b, t), tolerance))
return true;
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 Line[] GetOutlines() => new[] { AB, BC, CD, DA };

View File

@ -1,9 +1,9 @@
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>,
ISplittable<Triangle, (Float3[] As, Float3[] Bs, Float3[] Cs)>,
ISubdivide<Triangle[]>, IWithinRange<Float3, float>
ISubdivide<Triangle>, IWithinRange<Float3, float>
{
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;
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 ContainsPartially(Line line, float tolerance, float step = Calculus.DefaultStep)
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)
if (Contains(Float3.Lerp(line.a, line.b, t), tolerance))
return true;
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 Line[] GetOutlines() => new[] { AB, BC, CA };

View File

@ -448,7 +448,7 @@ public static class Mathf
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)
{
if (equ(initialA) == 0) return initialA;
@ -484,10 +484,10 @@ public static class Mathf
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) =>
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)
{
if (equ(initial) == 0) return initial;
@ -511,7 +511,7 @@ public static class Mathf
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
public static float StandardDeviation(params float[] vals) => Sqrt(Variance(vals));