129 lines
3.6 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|