├── .gitignore ├── D2.meta ├── D2 ├── AABB.cs ├── AABB.cs.meta ├── Circle.cs ├── Circle.cs.meta ├── Convex.cs ├── Convex.cs.meta ├── Line.cs ├── Line.cs.meta ├── Segment.cs ├── Segment.cs.meta ├── Triangle.cs └── Triangle.cs.meta ├── D3.meta ├── D3 ├── Convex.cs ├── Convex.cs.meta ├── Line.cs ├── Line.cs.meta ├── Segment.cs ├── Segment.cs.meta ├── Sphere.cs ├── Sphere.cs.meta ├── Tetrahedra.cs ├── Tetrahedra.cs.meta ├── Triangle.cs ├── Triangle.cs.meta ├── Util.cs └── Util.cs.meta ├── LICENSE.md ├── LICENSE.md.meta ├── README.md ├── README.md.meta ├── Tests.meta ├── Tests ├── Test2D.cs ├── Test2D.cs.meta ├── Test3D.cs ├── Test3D.cs.meta ├── Tests.asmdef └── Tests.asmdef.meta ├── kmty.geom.asmdef └── kmty.geom.asmdef.meta /.gitignore: -------------------------------------------------------------------------------- 1 | # This .gitignore file should be placed at the root of your Unity project directory 2 | # 3 | # Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore 4 | # 5 | /[Ll]ibrary/ 6 | /[Tt]emp/ 7 | /[Oo]bj/ 8 | /[Bb]uild/ 9 | /[Bb]uilds/ 10 | /[Ll]ogs/ 11 | /[Mm]emoryCaptures/ 12 | /[Cc]apture*/ 13 | 14 | # Never ignore Asset meta data 15 | !/[Aa]ssets/**/*.meta 16 | 17 | # Uncomment this line if you wish to ignore the asset store tools plugin 18 | # /[Aa]ssets/AssetStoreTools* 19 | 20 | # Autogenerated Jetbrains Rider plugin 21 | [Aa]ssets/Plugins/Editor/JetBrains* 22 | 23 | # Visual Studio cache directory 24 | .vs/ 25 | .vsconfig 26 | 27 | # Gradle cache directory 28 | .gradle/ 29 | 30 | # Autogenerated VS/MD/Consulo solution and project files 31 | ExportedObj/ 32 | .consulo/ 33 | *.csproj 34 | *.unityproj 35 | *.sln 36 | *.suo 37 | *.tmp 38 | *.user 39 | *.userprefs 40 | *.pidb 41 | *.booproj 42 | *.svd 43 | *.pdb 44 | *.mdb 45 | *.opendb 46 | *.VC.db 47 | 48 | # Unity3D generated meta files 49 | *.pidb.meta 50 | *.pdb.meta 51 | *.mdb.meta 52 | 53 | # Unity3D generated file on crash reports 54 | sysinfo.txt 55 | 56 | # Builds 57 | *.apk 58 | *.unitypackage 59 | 60 | # Crashlytics generated file 61 | crashlytics-build.properties 62 | -------------------------------------------------------------------------------- /D2.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 94398a0ca23a1c648a2fa28845ee14dd 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /D2/AABB.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using Unity.Mathematics; 3 | using static Unity.Mathematics.math; 4 | 5 | namespace kmty.geom.d2 { 6 | using f2 = float2; 7 | using f3 = float3; 8 | 9 | public struct AABB { 10 | public f2 size => new f2(max.x - min.x, max.y - min.y); 11 | public f2 min { get; } 12 | public f2 max { get; } 13 | public f2 center { get; } 14 | 15 | public AABB(f2 _min, f2 _max) { 16 | this.min = new f2(min(_min.x, _max.x), min(_min.y, _max.y)); 17 | this.max = new f2(max(_min.x, _max.x), max(_min.y, _max.y)); 18 | this.center = (this.min + this.max) * 0.5f; 19 | } 20 | 21 | public AABB(f3 min, f3 max) : this(new f2(min.x, min.y), new f2(max.x, max.y)) { } 22 | 23 | public bool Contains(f2 p, float offset = 0f) { 24 | return 25 | min.x + offset <= p.x && p.x <= max.x - offset && 26 | min.y + offset <= p.y && p.y <= max.y - offset; 27 | } 28 | 29 | public bool ContainsX(float x, float offset = 0f) => min.x + offset <= x && x <= max.x - offset; 30 | public bool ContainsY(float y, float offset = 0f) => min.y + offset <= y && y <= max.y - offset; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /D2/AABB.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 30c6df7d42cb0294fa39293c66c0fb00 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /D2/Circle.cs: -------------------------------------------------------------------------------- 1 | using Unity.Mathematics; 2 | using static Unity.Mathematics.math; 3 | 4 | namespace kmty.geom.d2 { 5 | using f2 = float2; 6 | 7 | public struct Circle : System.IEquatable { 8 | public f2 center; 9 | public float radius; 10 | 11 | public Circle(f2 c, float r) { 12 | center = c; 13 | radius = r; 14 | } 15 | 16 | public bool Contains(f2 p) => lengthsq(p - center) < radius * radius; 17 | 18 | #region IEquatable 19 | public override bool Equals(object obj) { return obj is Circle circle && Equals(circle); } 20 | public bool Equals(Circle other) { return center.Equals(other.center) && radius == other.radius; } 21 | public static bool operator ==(Circle left, Circle right) { return left.Equals(right); } 22 | public static bool operator !=(Circle left, Circle right) { return !(left == right); } 23 | public override int GetHashCode() { 24 | int hashCode = 1472534999; 25 | hashCode = hashCode * -1521134295 + center.GetHashCode(); 26 | hashCode = hashCode * -1521134295 + radius.GetHashCode(); 27 | return hashCode; 28 | } 29 | #endregion 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /D2/Circle.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e83709371eb317e48853ad41ab9a32eb 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /D2/Convex.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using UnityEngine; 5 | using Unity.Mathematics; 6 | using static Unity.Mathematics.math; 7 | 8 | namespace kmty.geom.d2 { 9 | using f2 = float2; 10 | using f3 = float3; 11 | using SG = Segment; 12 | 13 | public class Convex { 14 | private IEnumerable outsides; 15 | public f2[] points { get; protected set; } 16 | public SG[] segments { get; protected set; } 17 | public AABB aabb { get; protected set; } 18 | 19 | public Convex(IEnumerable originals) { 20 | var xsrt = originals.OrderBy(p => p.x); 21 | var xmin = xsrt.First(); 22 | var xmax = xsrt.Last(); 23 | var dsrt = originals.OrderBy(p => DistFactor(new SG(xmin, xmax), p)); 24 | var dmin = dsrt.First(); 25 | var dmax = dsrt.Last(); 26 | this.outsides = originals; 27 | this.points = new f2[] { xmin, dmin, xmax, dmax }; 28 | Reset(); 29 | } 30 | 31 | void Reset() { 32 | this.segments = new SG[points.Length]; 33 | var l = points.Length; 34 | var xmin = float.MaxValue; 35 | var ymin = float.MaxValue; 36 | var xmax = float.MinValue; 37 | var ymax = float.MinValue; 38 | for (int i = 0; i < l; i++) { 39 | segments[i] = new SG(points[i], points[(i + 1) % l]); 40 | xmin = min(xmin, points[i].x); 41 | ymin = min(ymin, points[i].y); 42 | xmax = max(xmax, points[i].x); 43 | ymax = max(ymax, points[i].y); 44 | } 45 | this.aabb = new AABB(new f2(xmin, ymin), new f2(xmax, ymax)); 46 | } 47 | 48 | float DistFactor(SG s, f2 p) => cross(new f3(s.b - s.a, 0), new f3(p - s.a, 0)).z; 49 | 50 | bool Contains(f2 p) { 51 | foreach (var s in segments) { 52 | if (cross(new f3(s.b - s.a, 0), new f3(p - s.a, 0)).z < 0) return false; 53 | } 54 | return true; 55 | } 56 | 57 | public void ExpandLoop(int maxitr = int.MaxValue) { 58 | int itr = 0; 59 | while (itr < maxitr) { itr++; if (!Expand()) break; } 60 | } 61 | 62 | public bool Expand() { 63 | outsides = outsides.Where(p => !Contains(p)); 64 | if (outsides.Count() == 0) return false; 65 | var l = new List(); 66 | foreach (var s in segments) { 67 | var sort = outsides.OrderBy(p => DistFactor(s, p)); 68 | l.Add(s.a); 69 | if (sort.Count() > 0) { 70 | var f = sort.First(); 71 | if (DistFactor(s, f) < 0) l.Add(f); 72 | } 73 | } 74 | points = l.ToArray(); 75 | Reset(); 76 | return true; 77 | } 78 | 79 | public void Draw() { 80 | var l = points.Length; 81 | GL.Begin(GL.LINE_STRIP); 82 | for (int i = 0; i <= l; i++) 83 | GL.Vertex(new f3(points[i % l], 0)); 84 | GL.End(); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /D2/Convex.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3826bc01acc34c94cb8bbf0040ea00df 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /D2/Line.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Unity.Mathematics; 3 | using static Unity.Mathematics.math; 4 | 5 | namespace kmty.geom.d2 { 6 | 7 | public struct Line : IEquatable { 8 | public float2 pos; 9 | public float2 vec; 10 | 11 | public Line(float2 p, float2 v) { 12 | this.vec = normalize(v); 13 | this.pos = p; 14 | } 15 | 16 | public bool Equals(Line other) { 17 | // fixed later 18 | return pos.Equals(other.pos) && 19 | vec.Equals(other.vec); 20 | } 21 | 22 | public override int GetHashCode() { 23 | int h = -1370888234; 24 | h = h * -1521134295 + pos.GetHashCode(); 25 | h = h * -1521134295 + vec.GetHashCode(); 26 | return h; 27 | } 28 | 29 | public override bool Equals(object obj) { return obj is Line line && Equals(line); } 30 | public static bool operator ==(Line left, Line right) { return left.Equals(right); } 31 | public static bool operator !=(Line left, Line right) { return !(left == right); } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /D2/Line.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5b7e96965ef26834d9a43364e05d0a2b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /D2/Segment.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using Unity.Mathematics; 3 | using static Unity.Mathematics.math; 4 | 5 | namespace kmty.geom.d2 { 6 | using f2 = float2; 7 | using f3 = float3; 8 | using SG = Segment; 9 | 10 | /// 11 | /// non oriented 1-simplex. 12 | /// 13 | public struct Segment : System.IEquatable { 14 | public f2 a { get; } 15 | public f2 b { get; } 16 | public Line line { get; } 17 | 18 | public Segment(f2 a, f2 b) { 19 | this.a = a; 20 | this.b = b; 21 | this.line = new Line(a, b - a); 22 | } 23 | 24 | public bool Intersects(Line l) { 25 | var t1 = cross(new f3(l.vec, 0), new f3(a - l.pos, 0)).z; 26 | var t2 = cross(new f3(l.vec, 0), new f3(b - l.pos, 0)).z; 27 | return t1 * t2 <= 0; 28 | } 29 | 30 | public bool Intersects(SG s) => Intersects(s.line) && s.Intersects(line); 31 | 32 | public bool Intersects(Bounds b) { 33 | if (Intersects(new SG(new f2(b.min.x, b.max.y), new f2(b.min.x, b.min.y)))) return true; 34 | if (Intersects(new SG(new f2(b.min.x, b.max.y), new f2(b.max.x, b.max.y)))) return true; 35 | if (Intersects(new SG(new f2(b.max.x, b.max.y), new f2(b.max.x, b.min.y)))) return true; 36 | if (Intersects(new SG(new f2(b.min.x, b.min.y), new f2(b.max.x, b.min.y)))) return true; 37 | return false; 38 | } 39 | 40 | public f2 GetClosestPoint(f2 p) { 41 | float a1 = b.y - a.y, 42 | b1 = a.x - b.x, 43 | c1 = a1 * a.x + b1 * a.y, 44 | c2 = -b1 * p.x + a1 * p.y, 45 | dt = a1 * a1 - -b1 * b1; 46 | if (Mathf.Approximately(dt, 0f)) return p; 47 | return float2(a1 * c1 - b1 * c2, a1 * c2 - -b1 * c1) / dt; 48 | } 49 | 50 | public bool Contains(f2 p) { 51 | var d0 = b - GetClosestPoint(p); 52 | var d1 = b - a; 53 | return (dot(d0, d1) >= 0f) && (lengthsq(d0) <= lengthsq(d1)); 54 | } 55 | 56 | public float GetDistance(f2 p) => abs(cross(new f3(p - a, 0), new f3(normalize(b - a), 0)).z); 57 | 58 | public Vector2 GetPoint(float offset) => a + normalize(b - a) * offset; 59 | 60 | #region IEqatable 61 | public override bool Equals(object obj) { return obj is SG segment && Equals(segment); } 62 | public bool Equals(SG other) { 63 | var f1 = a.Equals(other.a) && b.Equals(other.b); 64 | var f2 = a.Equals(other.b) && b.Equals(other.a); 65 | return f1 || f2; 66 | } 67 | public static bool operator ==(SG left, SG right) { return left.Equals(right); } 68 | public static bool operator !=(SG left, SG right) { return !(left == right); } 69 | public override int GetHashCode() { 70 | int hashCode = 2118541809; 71 | hashCode = hashCode * -1521134295 + a.GetHashCode(); 72 | hashCode = hashCode * -1521134295 + b.GetHashCode(); 73 | return hashCode; 74 | } 75 | #endregion 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /D2/Segment.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dc58f7e988bc0e44da438379a9b5db29 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /D2/Triangle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Unity.Mathematics; 3 | using static Unity.Mathematics.math; 4 | 5 | namespace kmty.geom.d2 { 6 | using f2 = float2; 7 | using f3 = float3; 8 | using SG = Segment; 9 | 10 | /// 11 | /// Borders of oriented 2-simplex in R^2 (counterclockwise oriented). 12 | /// 13 | public struct Triangle : IEquatable { 14 | public f2 a { get; } 15 | public f2 b { get; } 16 | public f2 c { get; } 17 | SG sa; 18 | SG sb; 19 | SG sc; 20 | float2x2 mtx; // 三角形空間のe1, e2についての表現行列 21 | float2x2 inv; // e1, e2空間の三角形の2辺についての表現行列 22 | 23 | public Triangle(SG e, f2 c) : this(e.a, e.b, c) { } 24 | public Triangle(f2 a, f2 b, f2 c) { 25 | if (Equals(a, b) || Equals(b, c) || Equals(c, a)) throw new Exception(); 26 | bool f = cross(float3(b - a, 0), float3(c - b, 0)).z > 0; 27 | this.a = a; 28 | this.b = f ? b : c; 29 | this.c = f ? c : b; 30 | this.sa = new SG(this.b, this.c); 31 | this.sb = new SG(this.c, this.a); 32 | this.sc = new SG(this.a, this.b); 33 | this.mtx = new float2x2(b - a, c - a); 34 | this.inv = math.inverse(mtx); 35 | } 36 | 37 | public bool Contains(SG e) => (sa == e || sb == e || sc == e); 38 | 39 | public f2 EuclidCord2TriangleCord(f2 p) => math.mul(inv, p - this.a); 40 | public f2 TriangleCord2EuclidCord(f2 p) => math.mul(mtx, p) + this.a; 41 | 42 | public f2 RemainingPoint(SG e) { 43 | if (e.Equals(sa)) return a; 44 | if (e.Equals(sb)) return b; 45 | if (e.Equals(sc)) return c; 46 | throw new Exception(); 47 | } 48 | 49 | public bool Includes(f2 p, bool close) { 50 | var v = EuclidCord2TriangleCord(p); 51 | var f1 = v.x == 0 || v.y == 0 || v.x + v.y == 1; 52 | var f2 = v.x > 0 && v.y > 0 && v.x + v.y < 1; 53 | return close ? f1 || f2 : f2; 54 | } 55 | 56 | public bool OnEdge(f2 p) { 57 | var v = EuclidCord2TriangleCord(p); 58 | return v.x == 0 || v.y == 0 || v.x + v.y == 1; 59 | } 60 | 61 | public Circle GetCircumscribledCircle() { 62 | float xa2 = a.x * a.x, ya2 = a.y * a.y; 63 | float xb2 = b.x * b.x, yb2 = b.y * b.y; 64 | float xc2 = c.x * c.x, yc2 = c.y * c.y; 65 | float k = 2 * ((b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x)); 66 | var ctr = new f2( 67 | ((c.y - a.y) * (xb2 - xa2 + yb2 - ya2) + (a.y - b.y) * (xc2 - xa2 + yc2 - ya2)) / k, 68 | ((a.x - c.x) * (xb2 - xa2 + yb2 - ya2) + (b.x - a.x) * (xc2 - xa2 + yc2 - ya2)) / k 69 | ); 70 | return new Circle(ctr, distance(a, ctr)); 71 | } 72 | 73 | #region IEquitable 74 | public override bool Equals(object obj) { return obj is Triangle triangle && Equals(triangle); } 75 | public static bool operator ==(Triangle left, Triangle right) { return left.Equals(right); } 76 | public static bool operator !=(Triangle left, Triangle right) { return !(left == right); } 77 | 78 | public bool Equals(Triangle other) { 79 | var f1 = a.Equals(other.a) && b.Equals(other.b) && c.Equals(other.c); 80 | var f2 = a.Equals(other.b) && b.Equals(other.c) && c.Equals(other.a); 81 | var f3 = a.Equals(other.c) && b.Equals(other.a) && c.Equals(other.b); 82 | return f1 || f2 || f3; 83 | } 84 | 85 | public override int GetHashCode() { 86 | int h = 1474027755; 87 | h = h * -1521134295 + a.GetHashCode(); 88 | h = h * -1521134295 + b.GetHashCode(); 89 | h = h * -1521134295 + c.GetHashCode(); 90 | return h; 91 | } 92 | #endregion 93 | 94 | #region Alternate Primitive Algolithm 95 | public bool Includes4Test(f2 p, bool close) { 96 | CrossForEachEdge4Test(p, out float c1, out float c2, out float c3); 97 | bool f1 = (c1 > 0 && c2 > 0 && c3 > 0) || (c1 < 0 && c2 < 0 && c3 < 0); 98 | bool f2 = c1 * c2 * c3 == 0; 99 | return close ? f1 || f2 : f1; 100 | } 101 | 102 | public bool OnEdge4Test(f2 p) { 103 | CrossForEachEdge4Test(p, out float c1, out float c2, out float c3); 104 | return c1 * c2 * c3 == 0; 105 | } 106 | 107 | void CrossForEachEdge4Test(f2 p, out float ca, out float cb, out float cc) { 108 | ca = cross(new f3(b - a, 0), new f3(p - b, 0)).z; 109 | cb = cross(new f3(c - b, 0), new f3(p - c, 0)).z; 110 | cc = cross(new f3(a - c, 0), new f3(p - a, 0)).z; 111 | } 112 | #endregion 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /D2/Triangle.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2ef652aa2d055de4788f262f419d2a5b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /D3.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 724c713dd3869c14890231b4aaf88353 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /D3/Convex.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Unity.Mathematics; 4 | using static Unity.Mathematics.math; 5 | 6 | namespace kmty.geom.d3 { 7 | using f3 = float3; 8 | using d3 = double3; 9 | using SG = Segment; 10 | using TN = TriangleNode; 11 | 12 | public class Convex { 13 | private IEnumerable outsides; 14 | public List nodes { get; protected set; } 15 | public d3 centroid { get; protected set; } 16 | public List taggeds { get; protected set; } // node that will be removed 17 | public List<(TN n, SG s)> contour { get; protected set; } // node that will be connected 18 | double h = 1e-10d; 19 | 20 | public Convex(IEnumerable originals) { 21 | var xs = originals.OrderBy(p => p.x); 22 | var p0 = xs.First(); 23 | var p1 = xs.Last(); 24 | var p2 = originals.OrderByDescending(p => lengthsq(cross(p - p0, p1 - p0))).First(); 25 | var p3 = originals.OrderByDescending(f => new TN(p0, p1, p2).DistFactor(f)).First(); 26 | this.centroid = (p0 + p1 + p2 + p3) * 0.25f; 27 | 28 | var n0 = new TN(p0, p1, p2); n0.SetNormal(centroid); 29 | var n1 = new TN(p1, p2, p3); n1.SetNormal(centroid); 30 | var n2 = new TN(p2, p3, p0); n2.SetNormal(centroid); 31 | var n3 = new TN(p3, p0, p1); n3.SetNormal(centroid); 32 | 33 | n0.neighbors = new List() { n1, n2, n3 }; 34 | n1.neighbors = new List() { n2, n3, n0 }; 35 | n2.neighbors = new List() { n3, n0, n1 }; 36 | n3.neighbors = new List() { n0, n1, n2 }; 37 | 38 | this.nodes = new List() { n0, n1, n2, n3 }; 39 | this.outsides = originals; 40 | this.taggeds = new List(); 41 | this.contour = new List<(TN n, SG s)>(); 42 | } 43 | 44 | public void ExpandLoop(int maxitr = int.MaxValue) { 45 | int itr = 0; 46 | while (itr < maxitr) { itr++; if (!Expand()) break; } 47 | } 48 | 49 | public bool Expand() { 50 | taggeds.Clear(); 51 | contour.Clear(); 52 | outsides = outsides.Where(p => !Contains(p)); 53 | if (outsides.Count() == 0) return false; 54 | 55 | if (FindApex(out d3 apex, out TN root)) { 56 | // tag fase 57 | taggeds.Add(root); 58 | foreach (var nei in root.neighbors) NeighborSearch(nei, root, apex); 59 | 60 | // rmv fase 61 | foreach(var n in taggeds) { 62 | foreach (var nei in n.neighbors) { nei.neighbors.Remove(n); } 63 | nodes.Remove(n); 64 | RollbackCentroid(n); 65 | } 66 | 67 | // add fase 68 | var cone = new (TN n, d3 a, d3 b)[contour.Count]; 69 | for (var i = 0; i < contour.Count; i++) { 70 | var c = contour[i]; 71 | var s = c.s; 72 | var pair = c.n; 73 | var curr = new TN(apex, s.a, s.b); 74 | curr.neighbors.Add(pair); 75 | pair.neighbors.Add(curr); 76 | 77 | foreach (var l in cone) { 78 | if (l.n == null) continue; 79 | if (s.a.Equals(l.a) || s.a.Equals(l.b) || s.b.Equals(l.a) || s.b.Equals(l.b)) { 80 | if (!l.n.neighbors.Contains(curr) && !curr.neighbors.Contains(l.n)) { 81 | l.n.neighbors.Add(curr); 82 | curr.neighbors.Add(l.n); 83 | } else throw new System.Exception(); 84 | } 85 | } 86 | UnityEngine.Assertions.Assert.IsTrue(curr != null); 87 | cone[i] = (curr, s.a, s.b); 88 | UpdateCentroid(curr); 89 | } 90 | 91 | nodes.AddRange(cone.Select(l => l.n)); 92 | foreach (var n in nodes) n.SetNormal(centroid); 93 | return true; 94 | } 95 | return false; 96 | } 97 | 98 | bool FindApex(out d3 apex, out TN root) { 99 | foreach (var n in this.nodes) { 100 | var s = outsides.Where(p => dot(p - n.center, n.normal) > 0).OrderByDescending(p => n.DistFactor(p)); 101 | if (s.Count() > 0) { apex = s.First(); root = n; return true; } 102 | } 103 | apex = default; 104 | root = default; 105 | return false; 106 | } 107 | 108 | void NeighborSearch(TN curr, TN pair, d3 p) { 109 | if (dot(p - curr.center, curr.normal) > h) { 110 | taggeds.Add(curr); 111 | foreach (var n in curr.neighbors) { 112 | if (!taggeds.Contains(n)) NeighborSearch(n, curr, p); 113 | } 114 | } else { 115 | var t = (curr, curr.Common(pair)); 116 | if (!contour.Contains(t)) contour.Add(t); 117 | } 118 | } 119 | 120 | public bool Contains(d3 p) { 121 | foreach (var n in nodes) { 122 | if (dot(p - n.center, n.normal) > h) return false; 123 | } 124 | return true; 125 | } 126 | 127 | void UpdateCentroid(TN n) { 128 | var c = nodes.Count + 1; 129 | var f = 1f / c; 130 | centroid *= (c - 1); 131 | centroid = f * (centroid + n.center); 132 | } 133 | 134 | void RollbackCentroid(TN n) { 135 | var c = nodes.Count + 1; 136 | var f = 1f / (c - 1); 137 | centroid *= c; 138 | centroid = f * (centroid - n.center); 139 | } 140 | 141 | public void Draw() { 142 | for (int i = 0; i < nodes.Count; i++) { 143 | var n = nodes[i]; 144 | UnityEngine.GL.Begin(UnityEngine.GL.LINE_STRIP); 145 | UnityEngine.GL.Vertex((f3)n.t.a); 146 | UnityEngine.GL.Vertex((f3)n.t.b); 147 | UnityEngine.GL.Vertex((f3)n.t.c); 148 | UnityEngine.GL.Vertex((f3)n.t.a); 149 | UnityEngine.GL.End(); 150 | } 151 | } 152 | } 153 | 154 | public class TriangleNode { 155 | public Triangle t { get; } 156 | public d3 center { get; } 157 | public d3 normal { get; private set; } 158 | public List neighbors { get; set; } 159 | 160 | public TriangleNode(d3 p1, d3 p2, d3 p3) { 161 | this.t = new Triangle(p1, p2, p3); 162 | this.center = t.GetGravityCenter(); 163 | this.neighbors = new List(); 164 | } 165 | 166 | public void SetNormal(d3 centroid) { 167 | var v = cross(t.b - t.a, t.c - t.a); 168 | var s = dot(v, centroid - t.a) > 0 ? -1 : 1; 169 | this.normal = normalize(v) * s; 170 | } 171 | 172 | public double DistFactor(d3 p) => lengthsq(cross(p - t.a, dot(p - t.b, p - t.c))); 173 | 174 | public SG Common(TN pair) { 175 | if (this.t.HasVert(pair.t.a) && this.t.HasVert(pair.t.b)) return new SG(pair.t.a, pair.t.b); 176 | if (this.t.HasVert(pair.t.b) && this.t.HasVert(pair.t.c)) return new SG(pair.t.b, pair.t.c); 177 | if (this.t.HasVert(pair.t.c) && this.t.HasVert(pair.t.a)) return new SG(pair.t.c, pair.t.a); 178 | throw new System.Exception(); 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /D3/Convex.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bf5ba32dc4cd4a04d82abb8c14eeff9d 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /D3/Line.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using Unity.Mathematics; 3 | using static Unity.Mathematics.math; 4 | using static kmty.geom.d3.Utils; 5 | 6 | namespace kmty.geom.d3 { 7 | public struct Line { 8 | public double3 pos; 9 | public double3 vec; 10 | 11 | public Line(Vector3 a, Vector3 b): this(V3D3(a), V3D3(b)) { } 12 | public Line(double3 p, double3 v) { 13 | if (Equals(v, (double3)0)) Debug.LogWarning("not creating a line"); 14 | pos = p; 15 | vec = normalize(v); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /D3/Line.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8cab8716e5346b8449819e620ed37631 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /D3/Segment.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using Unity.Mathematics; 3 | using static Unity.Mathematics.math; 4 | using static kmty.geom.d3.Utils; 5 | 6 | namespace kmty.geom.d3 { 7 | public struct Segment { 8 | public double3 a; 9 | public double3 b; 10 | public double3x2 points => double3x2(a, b); 11 | 12 | public Segment(Vector3 a, Vector3 b): this(V3D3(a), V3D3(b)) { } 13 | public Segment(double3 a, double3 b) { 14 | if (Equals(a, b)) Debug.LogWarning("not creating an edge"); 15 | this.a = a; 16 | this.b = b; 17 | } 18 | 19 | public bool Contains(double3 s){ 20 | return math.all(a == s) || math.all(b == s); 21 | } 22 | 23 | public bool EqualsIgnoreDirection(Segment pair){ 24 | return this.Equals(pair) || this.Equals(new Segment(pair.b, pair.a)); 25 | } 26 | 27 | /* 28 | #region IEquatable 29 | public override bool Equals(object obj) { return obj is Segment pair && Equals(pair); } 30 | public static bool operator ==(Segment left, Segment right) { return left.Equals(right); } 31 | public static bool operator !=(Segment left, Segment right) { return !(left == right); } 32 | 33 | public bool Equals(Segment s) { 34 | if(Equals(s.a, a) && Equals(s.b, b)) return true; 35 | return false; 36 | } 37 | 38 | public override int GetHashCode() { 39 | int hashCode = 1474027755; 40 | hashCode = hashCode * -1521134295 + a.GetHashCode(); 41 | hashCode = hashCode * -1521134295 + b.GetHashCode(); 42 | return hashCode; 43 | } 44 | #endregion 45 | */ 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /D3/Segment.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 582a7a0f7f92eb64e9138bb1b73acdc9 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /D3/Sphere.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using Unity.Mathematics; 3 | using static Unity.Mathematics.math; 4 | 5 | namespace kmty.geom.d3 { 6 | public struct Sphere { 7 | public double3 center; 8 | public double radius; 9 | 10 | public Sphere(double3 c, double r) { 11 | center = c; 12 | radius = r; 13 | } 14 | 15 | public bool Contains(double3 p, bool inclusive = true) { 16 | if (inclusive) return lengthsq(p - center) <= radius * radius; 17 | else return lengthsq(p - center) < radius * radius; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /D3/Sphere.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7ed7cda747df9374e9c9089fe7d9d9ef 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /D3/Tetrahedra.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | using Unity.Mathematics; 4 | using static Unity.Mathematics.math; 5 | using static kmty.geom.d3.Utils; 6 | 7 | namespace kmty.geom.d3 { 8 | using TR = Triangle; 9 | using V3 = Vector3; 10 | using f3 = float3; 11 | using d3 = double3; 12 | using d34 = double3x4; 13 | using d44 = double4x4; 14 | 15 | public struct Tetrahedra { 16 | public d34 vrts { get; } 17 | public d3 a => vrts[0]; 18 | public d3 b => vrts[1]; 19 | public d3 c => vrts[2]; 20 | public d3 d => vrts[3]; 21 | 22 | public TR ta { get; } 23 | public TR tb { get; } 24 | public TR tc { get; } 25 | public TR td { get; } 26 | 27 | public Tetrahedra(V3 a, V3 b, V3 c, V3 d) : this(V3D3(a), V3D3(b), V3D3(c), V3D3(d)) { } 28 | public Tetrahedra(d3 a, d3 b, d3 c, d3 d) { 29 | this.vrts = new d34(a, b, c, d); 30 | this.ta = new TR(a, b, c); 31 | this.tb = new TR(b, c, d); 32 | this.tc = new TR(c, d, a); 33 | this.td = new TR(d, a, b); 34 | 35 | if (Equals(a, b) || Equals(a, c) || Equals(a, d) || 36 | Equals(b, c) || Equals(b, d) || Equals(c, d)) throw new Exception(); 37 | } 38 | 39 | public bool HasFace(TR t) { 40 | if (t == ta || t == tb || t == tc || t == td) return true; 41 | return false; 42 | } 43 | 44 | public bool HasPoint(d3 p) { 45 | if (p.Equals(a)) return true; 46 | if (p.Equals(b)) return true; 47 | if (p.Equals(c)) return true; 48 | if (p.Equals(d)) return true; 49 | return false; 50 | } 51 | 52 | public bool Contains(d3 p, bool includeOnFacet) { 53 | var f1 = ta.IsSameSide(d, p, includeOnFacet); 54 | var f2 = tb.IsSameSide(a, p, includeOnFacet); 55 | var f3 = tc.IsSameSide(b, p, includeOnFacet); 56 | var f4 = td.IsSameSide(c, p, includeOnFacet); 57 | return f1 && f2 && f3 && f4; 58 | } 59 | 60 | public d3 RemainingPoint(TR t) { 61 | if (t == ta) return d; 62 | if (t == tb) return a; 63 | if (t == tc) return b; 64 | if (t == td) return c; 65 | throw new Exception(); 66 | } 67 | 68 | /// 69 | /// http://mathworld.wolfram.com/Circumsphere.html 70 | /// 71 | public Sphere GetCircumscribedSphere() { 72 | var a2 = a * a; var a2ex = a2.x + a2.y + a2.z; 73 | var b2 = b * b; var b2ex = b2.x + b2.y + b2.z; 74 | var c2 = c * c; var c2ex = c2.x + c2.y + c2.z; 75 | var d2 = d * d; var d2ex = d2.x + d2.y + d2.z; 76 | var detA = determinant(new d44( 77 | a.x, a.y, a.z, 1, 78 | b.x, b.y, b.z, 1, 79 | c.x, c.y, c.z, 1, 80 | d.x, d.y, d.z, 1)); 81 | var detX = determinant(new d44( 82 | a2ex, a.y, a.z, 1, 83 | b2ex, b.y, b.z, 1, 84 | c2ex, c.y, c.z, 1, 85 | d2ex, d.y, d.z, 1)); 86 | var detY = -determinant(new d44( 87 | a2ex, a.x, a.z, 1, 88 | b2ex, b.x, b.z, 1, 89 | c2ex, c.x, c.z, 1, 90 | d2ex, d.x, d.z, 1)); 91 | var detZ = determinant(new d44( 92 | a2ex, a.x, a.y, 1, 93 | b2ex, b.x, b.y, 1, 94 | c2ex, c.x, c.y, 1, 95 | d2ex, d.x, d.y, 1)); 96 | var ctr = new d3(detX, detY, detZ) / (2 * detA); 97 | return new Sphere(ctr, distance(ctr, a)); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /D3/Tetrahedra.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: de25f12c275a27f439d835a97eebad53 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /D3/Triangle.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using Unity.Mathematics; 3 | using static Unity.Mathematics.math; 4 | using static kmty.geom.d3.Utils; 5 | 6 | namespace kmty.geom.d3 { 7 | using V3 = Vector3; 8 | using f3 = float3; 9 | using d3 = double3; 10 | using SG = Segment; 11 | 12 | /// 13 | /// Borders of non oriented 2-simplex in R^3. 14 | /// 15 | public struct Triangle : System.IEquatable { 16 | public d3 a { get; } 17 | public d3 b { get; } 18 | public d3 c { get; } 19 | public d3 n { get; } 20 | double3x3 mtx; //三角形空間のe1, e2についての表現行列 21 | double3x3 inv; //e1, e2空間の三角形の2辺についての表現行列 22 | 23 | public Triangle(SG s, d3 p) : this(s.a, s.b, p) { } 24 | public Triangle(V3 p1, V3 p2, V3 p3) : this(V3D3(p1), V3D3(p2), V3D3(p3)) { } 25 | public Triangle(d3 p1, d3 p2, d3 p3) { 26 | if (Equals(p1, p2) || Equals(p2, p3) || Equals(p3, p1)) throw new System.Exception(); 27 | this.a = p1; 28 | this.b = p2; 29 | this.c = p3; 30 | this.n = normalize(cross(b - a, c - a)); 31 | this.mtx = new double3x3(b - a, c - a, cross(b - a, c - a)); 32 | this.inv = math.inverse(mtx); 33 | } 34 | 35 | public d3 EuclidCord2TriangleCord(d3 p) => math.mul(inv, p - this.a); 36 | public d3 TriangleCord2EuclidCord(d3 p) => math.mul(mtx, p) + this.a; 37 | 38 | public bool HasVert(d3 p) => p.Equals(a) || p.Equals(b) || p.Equals(c); 39 | 40 | public SG Remaining(d3 p) { 41 | if (p.Equals(a)) return new SG(b, c); 42 | if (p.Equals(b)) return new SG(c, a); 43 | if (p.Equals(c)) return new SG(a, b); 44 | throw new System.Exception(); 45 | } 46 | 47 | public bool IsSameSide(d3 p1, d3 p2, bool includeOnPlane) { 48 | double d = dot(n, p1 - a) * dot(n, p2 - a); 49 | return includeOnPlane ? d >= 0 : d > 0; 50 | } 51 | 52 | bool CramersRule(d3 ogn, d3 ray, out d3 det, out d3 pos) { 53 | var e1 = b - a; 54 | var e2 = c - a; 55 | var denominator = determinant(double3x3(e1, e2, -ray)); 56 | if (denominator == 0) { 57 | Debug.LogWarning("parallele"); 58 | det = default; 59 | pos = default; 60 | return false; 61 | } 62 | var d = ogn - a; 63 | var u = determinant(double3x3(d, e2, -ray)) / denominator; 64 | var v = determinant(double3x3(e1, d, -ray)) / denominator; 65 | var t = determinant(double3x3(e1, e2, d)) / denominator; 66 | pos = ogn + ray * t; 67 | det = new d3(u, v, t); 68 | return true; 69 | } 70 | 71 | public bool Intersects(Line l, out d3 p, out bool isOnEdge) { 72 | if (!CramersRule(l.pos, l.vec, out d3 d, out p)) { 73 | isOnEdge = default; 74 | return false; 75 | } 76 | isOnEdge = d.x == 0 || d.x == 1 || d.y == 0 || d.y == 1 || d.x + d.y == 1; 77 | return d.x >= 0 && d.x <= 1 && d.y >= 0 && d.y <= 1 && d.x + d.y <= 1; 78 | } 79 | 80 | public bool Intersects(SG e, out d3 p, out bool isOnEdge) { 81 | if (!CramersRule(e.a, normalize(e.b - e.a), out d3 d, out p)) { 82 | isOnEdge = default; 83 | return false; 84 | } 85 | bool f1 = d.x >= 0 && d.x <= 1 && d.y >= 0 && d.y <= 1 && d.x + d.y <= 1; 86 | bool f2 = d.z >= 0 && d.z <= length(e.b - e.a); 87 | isOnEdge = d.x == 0 || d.x == 1 || d.y == 0 || d.y == 1 || d.x + d.y == 1; 88 | return f1 && f2; 89 | } 90 | 91 | /// 92 | /// Close Enoughly identical to Intersection algolithm of Cramer's Rule one, 93 | /// but some how causes an error (in BistellarFllip of 3d). 94 | /// 95 | public bool IntersectsUsingMtx(SG e, out d3 point, out bool onedge) { 96 | var va = EuclidCord2TriangleCord(e.a); 97 | var vb = EuclidCord2TriangleCord(e.b); 98 | point = default; 99 | onedge = default; 100 | 101 | if (va.z * vb.z > 0) return false; 102 | 103 | var r = abs(va.z) / (abs(va.z) + abs(vb.z)); 104 | var p = va * (1 - r) + vb * r; 105 | 106 | if (p.x >= 0 && p.y >= 0 && p.x + p.y <= 1) { 107 | point = TriangleCord2EuclidCord(p); 108 | onedge = p.x == 0 && p.y == 0 && p.x + p.y == 1; 109 | return true; 110 | } 111 | return false; 112 | } 113 | 114 | /// 115 | /// ref: https://cvtech.cc/pointdist/ 116 | /// 117 | public d3 Distance(d3 p) { 118 | var v = length(cross(p - a, dot(p - b, p - c))) / 6d; 119 | var s = length(cross(b - a, c - a)) / 2d; 120 | return 3 * v / s; 121 | } 122 | 123 | public d3 GetGravityCenter() => (a + b + c) / 3d; 124 | 125 | /// 126 | /// TODO: circumscribed center has to be deterniministically called. (two 127 | /// vertical line has to cross each other so that they are on same triangle) 128 | /// 129 | public d3 GetCircumCenter() { 130 | var pab = lerp(a, b, 0.5); 131 | var pbc = lerp(b, c, 0.5); 132 | var vab = b - a; 133 | var vbc = c - b; 134 | var axis = normalize(cross(vab, vbc)); 135 | var d1 = normalize(cross(vab, axis)); 136 | var d2 = normalize(cross(vbc, axis)); 137 | var precision = 1e-15d; 138 | return GetIntersectionPoint(new Line(pab, d1), new Line(pbc, d2), precision); 139 | } 140 | 141 | public static (int num, d3 p1, d3 p2) Intersects(Triangle t1, Triangle t2) { 142 | var f1 = t1.Intersects(new SG(t2.a, t2.b), out d3 p1, out bool o1); 143 | var f2 = t1.Intersects(new SG(t2.b, t2.c), out d3 p2, out bool o2); 144 | var f3 = t1.Intersects(new SG(t2.c, t2.a), out d3 p3, out bool o3); 145 | var f4 = t2.Intersects(new SG(t1.a, t1.b), out d3 p4, out bool o4); 146 | var f5 = t2.Intersects(new SG(t1.b, t1.c), out d3 p5, out bool o5); 147 | var f6 = t2.Intersects(new SG(t1.c, t1.a), out d3 p6, out bool o6); 148 | 149 | // intersect one edge (for each other) 150 | if(f1 && !f2 && !f3) return (num: 1, p1: p1, p2: default); 151 | if(f2 && !f3 && !f1) return (num: 1, p1: p2, p2: default); 152 | if(f3 && !f1 && !f2) return (num: 1, p1: p3, p2: default); 153 | 154 | // t1 contains t2, intersect one edge, one vert 155 | if (f1 && f2 && f3) return (num: 2, p1: p1, p2: all(p1 == p2) ? p3 : p2); 156 | 157 | // t1 contains t2, intersect two edge 158 | if (f1 && f2 && !f3) return (num: 2, p1: p1, p2: p2); 159 | if (f2 && f3 && !f1) return (num: 2, p1: p2, p2: p3); 160 | if (f3 && f1 && !f2) return (num: 2, p1: p3, p2: p1); 161 | 162 | // t2 contains t1, intersect one edge, one vert 163 | if (f4 && f5 && f6) return (num: 2, p1: p4, p2: all(p4 == p5) ? p6 : p5); 164 | 165 | // t2 contains t1, intersect two edge 166 | if (f4 && f5 && !f6) return (num: 2, p1: p4, p2: p5); 167 | if (f5 && f6 && !f4) return (num: 2, p1: p5, p2: p6); 168 | if (f6 && f4 && !f5) return (num: 2, p1: p6, p2: p4); 169 | 170 | return (num: 0, p1: default, p2: default); 171 | } 172 | 173 | #region IEquatable 174 | public override bool Equals(object obj) { return obj is Triangle triangle && Equals(triangle); } 175 | public static bool operator ==(Triangle left, Triangle right) { return left.Equals(right); } 176 | public static bool operator !=(Triangle left, Triangle right) { return !(left == right); } 177 | 178 | public bool Equals(Triangle t) { 179 | if (Equals(t.a, a) && Equals(t.b, b) && Equals(t.c, c)) return true; 180 | else if(Equals(t.b, a) && Equals(t.c, b) && Equals(t.a, c)) return true; 181 | else if(Equals(t.c, a) && Equals(t.a, b) && Equals(t.b, c)) return true; 182 | else if(Equals(t.a, a) && Equals(t.c, b) && Equals(t.b, c)) return true; 183 | else if(Equals(t.b, a) && Equals(t.a, b) && Equals(t.c, c)) return true; 184 | else if(Equals(t.c, a) && Equals(t.b, b) && Equals(t.a, c)) return true; 185 | return false; 186 | } 187 | 188 | public override int GetHashCode() { 189 | int hashCode = 1474027755; 190 | hashCode = hashCode * -1521134295 + a.GetHashCode(); 191 | hashCode = hashCode * -1521134295 + b.GetHashCode(); 192 | hashCode = hashCode * -1521134295 + c.GetHashCode(); 193 | return hashCode; 194 | } 195 | #endregion 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /D3/Triangle.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4ee815c7764d0834d9acc9f5c03314b1 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /D3/Util.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Unity.Mathematics; 3 | using UnityEngine; 4 | using static Unity.Mathematics.math; 5 | 6 | namespace kmty.geom.d3 { 7 | using V3 = Vector3; 8 | using f3 = float3; 9 | using d3 = double3; 10 | using SG = Segment; 11 | public static class Utils { 12 | 13 | public static d3 V3D3(V3 a) => (d3)(f3)a; 14 | 15 | public static d3 GetIntersectionPoint(Line a, Line b, double threshold) { 16 | var alpha = dot(a.vec, b.vec); 17 | var r = a.pos - b.pos; 18 | var rho = dot(r, a.vec - alpha * b.vec) / (alpha * alpha - 1d); 19 | var tau = dot(r, alpha * a.vec - b.vec) / (alpha * alpha - 1d); 20 | var posA = mad(rho, a.vec, a.pos); 21 | var posB = mad(tau, b.vec, b.pos); 22 | if (lengthsq(posA - posB) < threshold) return posA; 23 | throw new ArgumentException(); 24 | } 25 | 26 | public static bool IsIntersecting(SG e1, SG e2, double threshold) { 27 | IsIntersecting(e1, e2, out bool f, out d3 p, threshold); 28 | return f; 29 | } 30 | 31 | public static d3 GetIntersectionPoint(SG e1, SG e2, double threshold) { 32 | IsIntersecting(e1, e2, out bool f, out d3 p, threshold); 33 | return p; 34 | } 35 | 36 | static void IsIntersecting(SG e1, SG e2, out bool flag, out d3 pos, double threshold) { 37 | var v1 = e1.b - e1.a; 38 | var v2 = e2.b - e2.a; 39 | var n1 = normalize(v1); 40 | var n2 = normalize(v2); 41 | 42 | var alpha = dot(n1, n2); 43 | var r = e1.a - e2.a; 44 | var rho = dot(r, n1 - alpha * n2) / (alpha * alpha - 1d); 45 | var tau = dot(r, alpha * n1 - n2) / (alpha * alpha - 1d); 46 | var pos1 = e1.a + rho * n1; 47 | var pos2 = e2.a + tau * n2; 48 | var f1 = lengthsq(pos1 - pos2) < threshold; 49 | 50 | var _rho = rho / length(v1); 51 | var _tau = tau / length(v2); 52 | var f2 = _rho >= 0d && _rho <= 1d && _tau >= 0d && _tau <= 1d; 53 | flag = f1 && f2; 54 | pos = pos2; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /D3/Util.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3713bcc334185624ba739d0cfa0f542f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 Saki Komikado 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /LICENSE.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c81ca3f0cfbb24b86b61bec8bf80a485 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # unity-simplex-geometry 2 | 3 | Simplex geometry utility library on 2D/3D, which includes 4 | 5 | - Segment(2D/3D) 6 | - Triangle(2D/3D) 7 | - Tetrahedra(3D) 8 | - Convexhull(2D/3D) 9 | - Other utils like Circles... 10 | 11 | This library is based on Unity.Mathematics SIMD library, be sure to install them beforehand (ver 1.1.0~). 12 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7584142ed8d8fa84790aaccb4c542927 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Tests.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b273e0a69a0dffd4ea307ed37fc58c94 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Tests/Test2D.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using NUnit.Framework; 4 | using System.Linq; 5 | using UnityEngine; 6 | using UnityEngine.TestTools; 7 | using Unity.Mathematics; 8 | using System.Runtime.InteropServices; 9 | using static Unity.Mathematics.math; 10 | 11 | namespace kmty.geom.d2.test { 12 | using f2 = float2; 13 | using SG = Segment; 14 | using TR = Triangle; 15 | using UR = UnityEngine.Random; 16 | 17 | public class Test2D { 18 | [Test] 19 | public void EquatableTest() { 20 | var ps = Enumerable.Repeat(0, 10).Select(_ => new f2(UR.value, UR.value)).ToArray(); 21 | var a = new f2(0, 0); 22 | var b = new f2(1, 0); 23 | var c = new f2(0, 1); 24 | var d = new f2(1, 1); 25 | var e = new f2(2, 0); 26 | var f = new f2(0, 2); 27 | var g = new f2(2, 2); 28 | 29 | Debug.Log("f2: " + Marshal.SizeOf(a)); 30 | Debug.Log("sg: " + Marshal.SizeOf(new SG(a, b))); 31 | Debug.Log("tr: " + Marshal.SizeOf(new TR(a, b, c))); 32 | 33 | // primitive 34 | Assert.IsFalse(a.Equals(b)); 35 | Assert.IsFalse(a.Equals(c)); 36 | Assert.IsTrue (a.Equals(a)); 37 | 38 | // circle 39 | var c1 = new Circle(d, 1); 40 | var c2 = new Circle(d, 1); 41 | var c3 = new Circle(d, 2); 42 | Assert.IsTrue(c1 == c2); 43 | Assert.IsTrue(c1 != c3); 44 | 45 | // segment 46 | var s1 = new SG(a, b); 47 | var s2 = new SG(b, a); 48 | var s3 = new SG(c, a); 49 | Assert.IsTrue(s1 == s2); 50 | Assert.IsTrue(s1 != s3); 51 | 52 | // triangle 53 | var t1 = new TR(a, b, c); 54 | var t2 = new TR(a, c, b); 55 | var t3 = new TR(b, c, a); 56 | Assert.IsTrue(t1 == t2); 57 | Assert.IsTrue(t1 == t3); 58 | } 59 | 60 | [Test] 61 | public void TriangleTest() { 62 | var t1 = new TR(new f2(0, 0), new f2(1, 0), new f2(0, 1)); 63 | var s1 = new SG(new f2(0, 0), new f2(0, 1)); 64 | var s2 = new SG(new f2(0, 1), new f2(1, 0)); 65 | var s3 = new SG(new f2(1, 0), new f2(0, 0)); 66 | 67 | Assert.IsTrue(t1.Contains(s1)); 68 | Assert.IsTrue(t1.Contains(s2)); 69 | Assert.IsTrue(t1.Contains(s3)); 70 | Assert.IsTrue(t1.RemainingPoint(s3).Equals(t1.c)); 71 | Assert.IsTrue(t1.RemainingPoint(s2).Equals(t1.a)); 72 | Assert.IsTrue(t1.RemainingPoint(s1).Equals(t1.b)); 73 | } 74 | 75 | [Test] 76 | public void TriangleTransCoordTest() { 77 | var t = new TR(new f2(1, 1), new f2(5, 2), new f2(2, 4)); 78 | var v1 = t.EuclidCord2TriangleCord(new f2(2, 2)); 79 | Assert.IsTrue(math.all(v1 == new f2(2, 3) / 11)); 80 | var v2 = t.TriangleCord2EuclidCord(v1); 81 | Assert.IsTrue(math.all(v2 == new f2(2, 2))); 82 | 83 | for (var i = 0; i < 100; i++) { 84 | var t1 = new TR(UR.insideUnitCircle, UR.insideUnitCircle, UR.insideUnitCircle); 85 | var p1 = UR.insideUnitCircle; 86 | var f1 = t1.Includes(p1, true); 87 | var f2 = t1.Includes4Test(p1, true); 88 | Assert.IsTrue(!(f1 ^ f2)); 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Tests/Test2D.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3b30f2db813d49043b6ddb138d3ff030 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Tests/Test3D.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using NUnit.Framework; 5 | using Unity.Mathematics; 6 | using UnityEngine; 7 | using UnityEngine.TestTools; 8 | 9 | namespace kmty.geom.d3.test { 10 | using f3 = float3; 11 | using UR = UnityEngine.Random; 12 | 13 | public class Test3D { 14 | 15 | [Test] 16 | public void ConvexTestStatic() { 17 | List sample1 = new List { 18 | new f3(0, 0, 0), 19 | new f3(1, 0, 0), 20 | new f3(0, 1, 0), 21 | new f3(0, 0, 1), 22 | }; 23 | var c = new Convex(sample1); 24 | Assert.IsTrue(c.Contains(new f3(0.1f, 0.1f, 0.1f))); 25 | Assert.IsTrue(c.Contains(new f3(0, 0, 0))); 26 | Assert.IsTrue(c.Contains(new f3(1, 0, 0))); 27 | Assert.IsTrue(c.Contains(new f3(0, 1, 0))); 28 | Assert.IsTrue(c.Contains(new f3(0, 0, 1))); 29 | Assert.IsFalse(c.Contains(new f3(2.0f, 0.1f, 0.1f))); 30 | } 31 | 32 | 33 | [Test] 34 | public void ConvexTestRandom() { 35 | var points = Enumerable.Repeat(0, 200).Select(_ => (f3)UR.insideUnitSphere).ToList(); 36 | var convex = new Convex(points); 37 | convex.ExpandLoop(); 38 | 39 | int itr = 0; 40 | while (itr < int.MaxValue) { 41 | itr++; 42 | var f = convex.Expand(); 43 | if (!f) break; 44 | } 45 | 46 | foreach (var n in convex.nodes) { 47 | Assert.IsTrue(n.neighbors.Count == 3); 48 | } 49 | 50 | foreach (var p in points) { 51 | Assert.IsTrue(convex.Contains(p)); 52 | } 53 | } 54 | 55 | [Test] 56 | public void TriangleSegmentIntersectionTest() { 57 | for (var i = 0; i < 1000; i++) { 58 | var t = new Triangle(UR.insideUnitSphere, UR.insideUnitSphere, UR.insideUnitSphere); 59 | var s = new Segment(UR.insideUnitSphere, UR.insideUnitSphere); 60 | var f1 = t.Intersects(s, out double3 p1, out bool e1); 61 | var f2 = t.IntersectsUsingMtx(s, out double3 p2, out bool e2); 62 | Assert.IsTrue(f1 == f2); 63 | Assert.IsTrue(e1 == e2); 64 | //Assert.IsTrue(math.all(p1 == p2)); 65 | } 66 | } 67 | 68 | [Test] 69 | public void TriangleTriangleIntersectionTest() { 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Tests/Test3D.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 63f4d077e29a4174db8dddaa57bc3448 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Tests/Tests.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Tests", 3 | "rootNamespace": "", 4 | "references": [ 5 | "UnityEngine.TestRunner", 6 | "UnityEditor.TestRunner", 7 | "Unity.Mathematics", 8 | "kmty.geom" 9 | ], 10 | "includePlatforms": [ 11 | "Editor" 12 | ], 13 | "excludePlatforms": [], 14 | "allowUnsafeCode": false, 15 | "overrideReferences": true, 16 | "precompiledReferences": [ 17 | "nunit.framework.dll" 18 | ], 19 | "autoReferenced": false, 20 | "defineConstraints": [ 21 | "UNITY_INCLUDE_TESTS" 22 | ], 23 | "versionDefines": [], 24 | "noEngineReferences": false 25 | } -------------------------------------------------------------------------------- /Tests/Tests.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: de0ba4b7e9d521a48b78e3ffc45e1f9c 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /kmty.geom.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kmty.geom", 3 | "rootNamespace": "", 4 | "references": [ 5 | "GUID:d8b63aba1907145bea998dd612889d6b" 6 | ], 7 | "includePlatforms": [], 8 | "excludePlatforms": [], 9 | "allowUnsafeCode": true, 10 | "overrideReferences": false, 11 | "precompiledReferences": [], 12 | "autoReferenced": true, 13 | "defineConstraints": [], 14 | "versionDefines": [], 15 | "noEngineReferences": false 16 | } -------------------------------------------------------------------------------- /kmty.geom.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 828f147e4ee5beb4c9173b99c456b0ca 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------