Files
BracerLib/Assets/Scripts/Utility/Triangulator.cs
T
2026-06-02 18:57:47 -04:00

129 lines
3.6 KiB
C#

using System;
using System.Collections.Generic;
using UnityEngine;
namespace BracerLib.Utility
{
/// <summary>
/// Sourced from https://mmems.gitbook.io/calepin/Algorithms/Polygon%20triangulation%20-%20tessellation/Triangulator%20-%20Unity
/// </summary>
public static class Triangulator
{
public static int[] Triangulate(Vector2[] points)
{
var n = points.Length;
if (n < 3)
return Array.Empty<int>();
var indices = new List<int>();
var verts = new int[n];
if (Area(points) > 0f)
{
for (var i = 0; i < n; i++)
verts[i] = i;
}
else
{
for (var i = 0; i < n; i++)
verts[i] = n - 1 - i;
}
var nv = n;
var count = 2 * nv;
for (int _ = 0, j = nv - 1; nv > 2;)
{
if (count-- <= 0)
return indices.ToArray();
var u = j;
if (nv <= u)
u = 0;
j = u + 1;
if (nv <= j)
j = 0;
var w = j + 1;
if (nv <= w)
w = 0;
if (!Snip(u, j, w, nv, points, verts))
continue;
indices.Add(verts[u]);
indices.Add(verts[j]);
indices.Add(verts[w]);
_++;
for (int s = j, t = j + 1; t < nv; s++, t++)
verts[s] = verts[t];
nv--;
count = 2 * nv;
}
indices.Reverse();
return indices.ToArray();
}
public static bool Snip(int u, int v, int w, int n, Vector2[] points, int[] verts)
{
var a = points[verts[u]];
var b = points[verts[v]];
var c = points[verts[w]];
if (Mathf.Epsilon > (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x))
return false;
for (var i = 0; i < n; i++)
{
if (i == u || i == v || i == w)
continue;
if (InsideTriangle(a, b, c, points[verts[i]]))
return false;
}
return true;
}
/// <summary>
/// Check if a point is inside of a triangle of 3 given points.
/// Used from https://blackpawn.com/texts/pointinpoly/
/// </summary>
public static bool InsideTriangle(Vector2 a, Vector2 b, Vector2 c, Vector2 p)
{
var v0 = c - a;
var v1 = b - a;
var v2 = p - a;
var dots = new[]
{
Vector2.Dot(v0, v0),
Vector2.Dot(v0, v1),
Vector2.Dot(v0, v2),
Vector2.Dot(v1, v1),
Vector2.Dot(v1, v2)
};
var invDenom = 1 / (dots[0] * dots[3] - dots[1] * dots[1]);
var u = (dots[3] * dots[2] - dots[1] * dots[4]) * invDenom;
var v = (dots[0] * dots[4] - dots[1] * dots[2]) * invDenom;
return u >= 0f && v >= 0f && u + v < 1;
}
public static float Area(Vector2[] points)
{
var n = points.Length;
var area = 0f;
for (int i = n - 1, j = 0; j < n; i = j++)
{
var iVal = points[i];
var jVal = points[j];
area += iVal.x * jVal.y - jVal.x * iVal.y;
}
return area * 0.5f;
}
}
}