using UnityEngine; namespace SphereTriangle { /// /// 三角形との交差が 2 => 交点 と 交点 /// 三角形との交差が 1 => 交点 と 三角形内の線分端点 /// 三角形との交差が 0 => 線分の端点が両方とも三角形内の場合。端点 と 端点 /// public struct TriangleSegmentIntersection { public float t0; public float t1; public TriangleSegmentIntersection(float _t0, float _t1) { if (_t0 <= _t1) { t0 = _t0; t1 = _t1; } else { t0 = _t1; t1 = _t0; } } } public struct Triangle { public Plane Plane; public Vector3[] Points; public Vector3 a => Points[0]; public Vector3 b => Points[1]; public Vector3 c => Points[2]; public Triangle(Vector3 a, Vector3 b, Vector3 c) { Plane = new Plane(a, b, c); Points = new Vector3[] { a, b, c }; } /// /// p がすべての辺の同じ側(内積的な意味で)にある => 三角形の内側にある /// /// /// public bool IsSameSide(Vector3 p) { var da = Vector3.Dot(Vector3.Cross(p - a, b - a), Plane.normal); var db = Vector3.Dot(Vector3.Cross(p - b, c - b), Plane.normal); var dc = Vector3.Dot(Vector3.Cross(p - c, a - c), Plane.normal); if (da > 0) { if (db > 0) { if (dc > 0) { return true; } } } else if (da < 0) { if (db < 0) { if (dc < 0) { return true; } } } return false; } // /// // /// p から lerp の係数を計算 // /// // /// // /// // /// // /// // static float getT(in Vector3 p0, in Vector3 p1, in Vector3 p) // { // return (p - p0).magnitude / (p1 - p0).magnitude; // } // /// // /// 三角形と線分の交差を判定する。 // /// // /// 線分始点(t=0)。三角形 abc と同一平面を期待 // /// 線分終点(t=1)。三角形 abc と同一平面を期待 // /// // public bool TryIntersectSegment(in Vector3 p0, in Vector3 p1, out TriangleSegmentIntersection intersection) // { // // [ab bc ca] // // [ab bc] [bc ca] [ca ab] // // [ab] [bc] [ca] // // [] // if (TryIntersectSegments(p0, p1, a, b, out var ab)) // { // if (TryIntersectSegments(p0, p1, b, c, out var bc)) // { // // [ab bc] or [ab bc ca] // intersection = new TriangleSegmentIntersection(getT(p0, p1, ab), getT(p0, p1, bc)); // return true; // } // else // { // if (TryIntersectSegments(p0, p1, c, a, out var ca)) // { // // [ab ca] // intersection = new TriangleSegmentIntersection(getT(p0, p1, ab), getT(p0, p1, ca)); // return true; // } // else // { // // [ab] // if (IsSameSide(p0)) // { // intersection = new TriangleSegmentIntersection(0, getT(p0, p1, ab)); // return true; // } // else // { // intersection = new TriangleSegmentIntersection(getT(p0, p1, ab), 1.0f); // return true; // } // } // } // } // else // { // if (TryIntersectSegments(p0, p1, b, c, out var bc)) // { // if (TryIntersectSegments(p0, p1, c, a, out var ca)) // { // // [bc ca] // intersection = new TriangleSegmentIntersection(getT(p0, p1, bc), getT(p0, p1, ca)); // return true; // } // else // { // // [bc] // if (IsSameSide(p0)) // { // intersection = new TriangleSegmentIntersection(0, getT(p0, p1, bc)); // return true; // } // else // { // intersection = new TriangleSegmentIntersection(getT(p0, p1, bc), 1.0f); // return true; // } // } // } // else // { // if (TryIntersectSegments(p0, p1, c, a, out var ca)) // { // // [ca] // if (IsSameSide(p0)) // { // intersection = new TriangleSegmentIntersection(0, getT(p0, p1, ca)); // return true; // } // else // { // intersection = new TriangleSegmentIntersection(getT(p0, p1, ca), 1.0f); // return true; // } // } // else // { // // [] // if (IsSameSide(p0) && IsSameSide(p1)) // { // intersection = new TriangleSegmentIntersection(0, 1); // return true; // } // else // { // intersection = default; // return false; // } // } // } // } // } // /// // /// 線分と線分の交差を判定する // /// a-b d // /// / // /// c // /// https://qiita.com/zu_rin/items/09876d2c7ec12974bc0f // /// // /// // /// // /// // /// // /// // static bool TryIntersectSegments(in Vector3 a, in Vector3 b, in Vector3 c, in Vector3 d, out Vector3 p) // { // var deno = Vector3.Cross(b - a, d - c).magnitude; // if (deno < 1e-5) // { // // 線分が平行 // p = default; // return false; // } // var s = Vector3.Cross(c - a, d - c).magnitude / deno; // var t = Vector3.Cross(b - a, a - c).magnitude / deno; // if (s < 0.0 || 1.0 < s || t < 0.0 || 1.0 < t) // { // // 線分が交差していない // p = default; // return false; // } // p = new Vector3( // a.x + s * (b - a).x, // a.y + s * (b - a).y, // a.z + s * (b - a).z // ); // return true; // } // public (Vector3, float) ProjectAB(in Vector3 p) // { // var ab = (b - a).normalized; // var d = Vector3.Dot(ab, (p - a)); // var x = a + ab * d; // return (x, d / (b - a).magnitude); // } // public (Vector3, float) ProjectBC(in Vector3 p) // { // var bc = (c - b).normalized; // var d = Vector3.Dot(bc, (p - b)); // var x = b + bc * d; // return (x, d / (c - b).magnitude); // } // public (Vector3, float) ProjectCA(in Vector3 p) // { // var ca = (a - c).normalized; // var d = Vector3.Dot(ca, (p - c)); // var x = c + ca * d; // return (x, d / (a - c).magnitude); // } // public TriangleProjection Project(in Vector3 p) // { // var (ab, ta) = ProjectAB(p); // var (bc, tb) = ProjectBC(p); // var (ca, tc) = ProjectCA(p); // return new TriangleProjection // { // ab = ab, // ta = ta, // bc = bc, // tb = tb, // ca = ca, // tc = tc, // }; // } public void DrawGizmos() { #if UNITY_2022_3_OR_NEWER Gizmos.DrawLineStrip(Points, true); #else for (int i = 0; i < Points.Length - 1; ++i) { Gizmos.DrawLine(Points[i], Points[i + 1]); } Gizmos.DrawLine(Points[Points.Length - 1], Points[0]); #endif } } }