From 7484195f97d90ac11feb63cd5d39e9012ad9c40f Mon Sep 17 00:00:00 2001 From: That_One_Nerd Date: Thu, 26 Oct 2023 15:08:46 -0400 Subject: [PATCH] Lots more stuff. Finished Box2d. --- Nerd_STF/Helpers/GeometryHelper.cs | 100 +++++++ .../Mathematics/Abstract/IContainsGeometry.cs | 18 ++ .../Mathematics/Abstract/IContainsPartial.cs | 7 - Nerd_STF/Mathematics/Abstract/IIntersect.cs | 6 + Nerd_STF/Mathematics/Abstract/IPolygon.cs | 4 +- Nerd_STF/Mathematics/Abstract/ISubdivide.cs | 4 +- Nerd_STF/Mathematics/Algebra/Vector2d.cs | 2 +- Nerd_STF/Mathematics/Float3.cs | 8 + Nerd_STF/Mathematics/Geometry/Box2d.cs | 264 ++++++++++++++++++ Nerd_STF/Mathematics/Geometry/Line.cs | 11 +- .../Mathematics/Geometry/Quadrilateral.cs | 60 +++- Nerd_STF/Mathematics/Geometry/Triangle.cs | 48 +++- Nerd_STF/Mathematics/Mathf.cs | 8 +- 13 files changed, 511 insertions(+), 29 deletions(-) create mode 100644 Nerd_STF/Helpers/GeometryHelper.cs create mode 100644 Nerd_STF/Mathematics/Abstract/IContainsGeometry.cs delete mode 100644 Nerd_STF/Mathematics/Abstract/IContainsPartial.cs create mode 100644 Nerd_STF/Mathematics/Abstract/IIntersect.cs create mode 100644 Nerd_STF/Mathematics/Geometry/Box2d.cs diff --git a/Nerd_STF/Helpers/GeometryHelper.cs b/Nerd_STF/Helpers/GeometryHelper.cs new file mode 100644 index 0000000..53d383f --- /dev/null +++ b/Nerd_STF/Helpers/GeometryHelper.cs @@ -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 + } +} diff --git a/Nerd_STF/Mathematics/Abstract/IContainsGeometry.cs b/Nerd_STF/Mathematics/Abstract/IContainsGeometry.cs new file mode 100644 index 0000000..64ea406 --- /dev/null +++ b/Nerd_STF/Mathematics/Abstract/IContainsGeometry.cs @@ -0,0 +1,18 @@ +namespace Nerd_STF.Mathematics.Abstract; + +public interface IContainsGeometry : IContains, IContains, IContains, + IContains, IIntersect, IIntersect, IIntersect + where T : IContainsGeometry, IEquatable +{ + public bool Contains(T obj); + public bool Intersects(T obj); + + public bool Contains(IEnumerable points); + public bool Contains(Fill points, int count); + public bool Intersects(IEnumerable lines); + public bool Intersects(Fill lines, int count); + + // later: + //public bool Contains(TOther obj) where TOther : IPolygon; + //public bool Intersects(TOther obj) where TOther : IPolygon; +} diff --git a/Nerd_STF/Mathematics/Abstract/IContainsPartial.cs b/Nerd_STF/Mathematics/Abstract/IContainsPartial.cs deleted file mode 100644 index 42bacbb..0000000 --- a/Nerd_STF/Mathematics/Abstract/IContainsPartial.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Nerd_STF.Mathematics.Abstract; - -public interface IContainsPartial : IContains - where T : IEquatable -{ - public bool ContainsPartially(Line other); -} diff --git a/Nerd_STF/Mathematics/Abstract/IIntersect.cs b/Nerd_STF/Mathematics/Abstract/IIntersect.cs new file mode 100644 index 0000000..01fc19d --- /dev/null +++ b/Nerd_STF/Mathematics/Abstract/IIntersect.cs @@ -0,0 +1,6 @@ +namespace Nerd_STF.Mathematics.Abstract; + +public interface IIntersect where T : IEquatable +{ + public bool Intersects(T other); +} diff --git a/Nerd_STF/Mathematics/Abstract/IPolygon.cs b/Nerd_STF/Mathematics/Abstract/IPolygon.cs index 038f0b0..c39b924 100644 --- a/Nerd_STF/Mathematics/Abstract/IPolygon.cs +++ b/Nerd_STF/Mathematics/Abstract/IPolygon.cs @@ -1,6 +1,6 @@ namespace Nerd_STF.Mathematics.Abstract; -public interface IPolygon : IAverage, IEquatable, +public interface IPolygon : IAverage, IContainsGeometry, IEquatable, IFloatArray, IGroup, IIndexAll, IIndexRangeAll, ILerp, IMedian, ITriangulate where T : IPolygon @@ -11,7 +11,7 @@ public interface IPolygon : IAverage, IEquatable, public Float3[] GetAllVerts(); 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, float scale); diff --git a/Nerd_STF/Mathematics/Abstract/ISubdivide.cs b/Nerd_STF/Mathematics/Abstract/ISubdivide.cs index 9738818..226f41d 100644 --- a/Nerd_STF/Mathematics/Abstract/ISubdivide.cs +++ b/Nerd_STF/Mathematics/Abstract/ISubdivide.cs @@ -2,6 +2,6 @@ public interface ISubdivide { - public T Subdivide(); - public T Subdivide(int iterations); + public T[] Subdivide(); + public T[] Subdivide(int iterations); } diff --git a/Nerd_STF/Mathematics/Algebra/Vector2d.cs b/Nerd_STF/Mathematics/Algebra/Vector2d.cs index 540a4ee..8e29cf4 100644 --- a/Nerd_STF/Mathematics/Algebra/Vector2d.cs +++ b/Nerd_STF/Mathematics/Algebra/Vector2d.cs @@ -101,7 +101,7 @@ public record struct Vector2d : IAbsolute, IAverage, 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; diff --git a/Nerd_STF/Mathematics/Float3.cs b/Nerd_STF/Mathematics/Float3.cs index ee1ce2d..0554e93 100644 --- a/Nerd_STF/Mathematics/Float3.cs +++ b/Nerd_STF/Mathematics/Float3.cs @@ -230,6 +230,14 @@ public record struct Float3 : IAbsolute, IAverage, 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 = "); diff --git a/Nerd_STF/Mathematics/Geometry/Box2d.cs b/Nerd_STF/Mathematics/Geometry/Box2d.cs new file mode 100644 index 0000000..546a31a --- /dev/null +++ b/Nerd_STF/Mathematics/Geometry/Box2d.cs @@ -0,0 +1,264 @@ +namespace Nerd_STF.Mathematics.Geometry; + +public class Box2d : IAverage, IClosestTo, IContains, IContains, + IContains, IContains, IIntersect, IIntersect, + IIntersect, IEquatable, ILerp, IMedian, + ISubdivide, ITriangulate, IWithinRange +{ + 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 fill) : this(fill(0), fill(1)) { } + public Box2d(Fill fill) : this(fill(0), fill(1)) { } + public Box2d(Fill fill) : this(fill(0), fill(1), fill(2), fill(3)) { } + public Box2d(Fill 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 points) + { + foreach (Float3 p in points) if (!Contains(p)) return false; + return true; + } + public bool Contains(IEnumerable points) + { + foreach (Float3 p in points) if (!Contains(p)) return false; + return true; + } + public bool Contains(Fill points, int count) + { + for (int i = 0; i < count; i++) if (!Contains(points(i))) return false; + return true; + } + public bool Contains(Fill 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 lines) + { + foreach (Line l in lines) if (Intersects(l)) return true; + return false; + } + public bool Intersects(Fill 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 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 + } +} diff --git a/Nerd_STF/Mathematics/Geometry/Line.cs b/Nerd_STF/Mathematics/Geometry/Line.cs index 7125e24..9836bda 100644 --- a/Nerd_STF/Mathematics/Geometry/Line.cs +++ b/Nerd_STF/Mathematics/Geometry/Line.cs @@ -2,9 +2,9 @@ public class Line : IAverage, IClosestTo, IContains, IEquatable, IFloatArray, IFromTuple, IGroup, - IIndexAll, IIndexRangeAll, ILerp, IMedian, - IPresets3d, ISplittable, ISubdivide, - IWithinRange + IIndexAll, IIndexRangeAll, ILerp, IIntersect, + IMedian, IPresets3d, ISplittable, + ISubdivide, IWithinRange { public static Line Back => (Float3.Zero, Float3.Back); public static Line Down => (Float3.Zero, Float3.Down); @@ -152,6 +152,9 @@ public class Line : IAverage, IClosestTo, IContains, 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, IClosestTo, IContains, 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); diff --git a/Nerd_STF/Mathematics/Geometry/Quadrilateral.cs b/Nerd_STF/Mathematics/Geometry/Quadrilateral.cs index f1c07c7..10fea42 100644 --- a/Nerd_STF/Mathematics/Geometry/Quadrilateral.cs +++ b/Nerd_STF/Mathematics/Geometry/Quadrilateral.cs @@ -1,9 +1,9 @@ namespace Nerd_STF.Mathematics.Geometry; -public class Quadrilateral : IClosestTo, IContains, IContainsPartial, +public class Quadrilateral : IClosestTo, IFromTuple, IPolygon, ISplittable, - ISubdivide, ITriangulate, IWithinRange + ISubdivide, ITriangulate, IWithinRange { public float Area { @@ -156,7 +156,7 @@ public class Quadrilateral : IClosestTo, IContains, 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, IContains, IContainsPar if (!Contains(Float3.Lerp(line.a, line.b, t), tolerance)) 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 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 points) + { + foreach (Float3 point in points) if (!Contains(point)) return false; + return true; + } + public bool Contains(Fill 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) 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 lines) + { + foreach (Line l in lines) if (Contains(l)) return true; + return false; + } + public bool Intersects(Fill 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 }; diff --git a/Nerd_STF/Mathematics/Geometry/Triangle.cs b/Nerd_STF/Mathematics/Geometry/Triangle.cs index 0f0efd8..2c44db6 100644 --- a/Nerd_STF/Mathematics/Geometry/Triangle.cs +++ b/Nerd_STF/Mathematics/Geometry/Triangle.cs @@ -1,9 +1,9 @@ namespace Nerd_STF.Mathematics.Geometry; -public class Triangle : IClosestTo, IContains, IContainsPartial, +public class Triangle : IClosestTo, IFromTuple, IPolygon, ISplittable, - ISubdivide, IWithinRange + ISubdivide, IWithinRange { public float Area { @@ -224,15 +224,55 @@ public class Triangle : IClosestTo, IContains, 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 points) + { + foreach (Float3 point in points) if (!Contains(point)) return false; + return true; + } + public bool Contains(Fill 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 lines) + { + foreach (Line l in lines) if (Contains(l)) return true; + return false; + } + public bool Intersects(Fill 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 }; diff --git a/Nerd_STF/Mathematics/Mathf.cs b/Nerd_STF/Mathematics/Mathf.cs index 132f1df..3105c8e 100644 --- a/Nerd_STF/Mathematics/Mathf.cs +++ b/Nerd_STF/Mathematics/Mathf.cs @@ -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));