Archived
221 lines
7.2 KiB
C#
221 lines
7.2 KiB
C#
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
|
|
namespace XericLibrary.Runtime.UIGraph
|
|
{
|
|
public static class TriangulateHelp
|
|
{
|
|
|
|
/// <summary>
|
|
/// 添加填充多边形
|
|
/// </summary>
|
|
public static void AddFilledPolygon(VertexHelper vh, List<Vector2> vertices, List<int> triangles, Color color)
|
|
{
|
|
int vertexStartIndex = vh.currentVertCount;
|
|
|
|
// 添加顶点
|
|
foreach (var vertex in vertices)
|
|
{
|
|
vh.AddVert(vertex, color, Vector2.zero);
|
|
}
|
|
|
|
// 添加三角形索引
|
|
for (int i = 0; i < triangles.Count; i += 3)
|
|
{
|
|
vh.AddTriangle(
|
|
vertexStartIndex + triangles[i],
|
|
vertexStartIndex + triangles[i + 1],
|
|
vertexStartIndex + triangles[i + 2]
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 添加描边
|
|
/// </summary>
|
|
public static void AddStroke(VertexHelper vh, List<Vector2> vertices, float width, Color color)
|
|
{
|
|
if (vertices.Count < 2)
|
|
return;
|
|
|
|
float halfWidth = width * 0.5f;
|
|
|
|
// 为每条边生成两个三角形(形成矩形)
|
|
for (int i = 0; i < vertices.Count; i++)
|
|
{
|
|
int next = (i + 1) % vertices.Count;
|
|
|
|
Vector2 p1 = vertices[i];
|
|
Vector2 p2 = vertices[next];
|
|
|
|
// 计算边的法线
|
|
Vector2 edge = p2 - p1;
|
|
Vector2 normal = new Vector2(-edge.y, edge.x).normalized;
|
|
|
|
// 生成四个顶点(矩形)
|
|
Vector2 v1 = p1 + normal * halfWidth;
|
|
Vector2 v2 = p1 - normal * halfWidth;
|
|
Vector2 v3 = p2 - normal * halfWidth;
|
|
Vector2 v4 = p2 + normal * halfWidth;
|
|
|
|
int startIndex = vh.currentVertCount;
|
|
|
|
vh.AddVert(v1, color, Vector2.zero);
|
|
vh.AddVert(v2, color, Vector2.zero);
|
|
vh.AddVert(v3, color, Vector2.zero);
|
|
vh.AddVert(v4, color, Vector2.zero);
|
|
|
|
// 两个三角形
|
|
vh.AddTriangle(startIndex, startIndex + 1, startIndex + 2);
|
|
vh.AddTriangle(startIndex, startIndex + 2, startIndex + 3);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// 耳切法三角剖分
|
|
/// </summary>
|
|
public static List<int> TriangulatePolygon(List<Vector2> polygonVertices)
|
|
{
|
|
List<int> triangles = new List<int>();
|
|
|
|
if (polygonVertices == null || polygonVertices.Count < 3)
|
|
return triangles;
|
|
|
|
// 复制顶点列表以便修改
|
|
List<Vector2> verticesCopy = new List<Vector2>(polygonVertices);
|
|
List<int> indices = Enumerable.Range(0, verticesCopy.Count).ToList();
|
|
|
|
// 确保顶点顺序是逆时针(耳切法要求)
|
|
if (!IsCounterClockwise(verticesCopy))
|
|
{
|
|
verticesCopy.Reverse();
|
|
indices.Reverse();
|
|
}
|
|
|
|
int n = verticesCopy.Count;
|
|
|
|
// 当顶点数大于3时继续切耳朵
|
|
while (n > 3)
|
|
{
|
|
bool foundEar = false;
|
|
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
int prev = (i - 1 + n) % n;
|
|
int curr = i;
|
|
int next = (i + 1) % n;
|
|
|
|
// 检查是否是耳朵
|
|
if (IsEar(verticesCopy, prev, curr, next, n))
|
|
{
|
|
// 添加三角形
|
|
triangles.Add(indices[prev]);
|
|
triangles.Add(indices[curr]);
|
|
triangles.Add(indices[next]);
|
|
|
|
// 移除耳朵顶点
|
|
verticesCopy.RemoveAt(curr);
|
|
indices.RemoveAt(curr);
|
|
n--;
|
|
foundEar = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 如果没有找到耳朵,可能是复杂多边形,退出
|
|
if (!foundEar)
|
|
{
|
|
Debug.LogWarning("无法完成三角剖分,可能是自相交多边形");
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 添加最后一个三角形
|
|
if (n == 3)
|
|
{
|
|
triangles.Add(indices[0]);
|
|
triangles.Add(indices[1]);
|
|
triangles.Add(indices[2]);
|
|
}
|
|
|
|
return triangles;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 检查顶点顺序是否为逆时针
|
|
/// </summary>
|
|
public static bool IsCounterClockwise(List<Vector2> vertices)
|
|
{
|
|
float sum = 0;
|
|
int n = vertices.Count;
|
|
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
Vector2 v1 = vertices[i];
|
|
Vector2 v2 = vertices[(i + 1) % n];
|
|
sum += (v2.x - v1.x) * (v2.y + v1.y);
|
|
}
|
|
|
|
return sum < 0;
|
|
}
|
|
/// <summary>
|
|
/// 检查三个顶点是否构成耳朵
|
|
/// </summary>
|
|
public static bool IsEar(List<Vector2> vertices, int prev, int curr, int next, int n)
|
|
{
|
|
Vector2 a = vertices[prev];
|
|
Vector2 b = vertices[curr];
|
|
Vector2 c = vertices[next];
|
|
|
|
// 检查是否是凸角
|
|
if (!IsConvex(a, b, c))
|
|
return false;
|
|
|
|
// 检查三角形内部是否包含其他顶点
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
if (i == prev || i == curr || i == next)
|
|
continue;
|
|
|
|
if (PointInTriangle(vertices[i], a, b, c))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 检查点是否在三角形内部
|
|
/// </summary>
|
|
public static bool PointInTriangle(Vector2 p, Vector2 a, Vector2 b, Vector2 c)
|
|
{
|
|
float area = Mathf.Abs(CrossProduct(a, b, c));
|
|
float area1 = Mathf.Abs(CrossProduct(p, a, b));
|
|
float area2 = Mathf.Abs(CrossProduct(p, b, c));
|
|
float area3 = Mathf.Abs(CrossProduct(p, c, a));
|
|
|
|
return Mathf.Abs(area - (area1 + area2 + area3)) < 0.001f;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 检查三点是否构成凸角(逆时针)
|
|
/// </summary>
|
|
public static bool IsConvex(Vector2 a, Vector2 b, Vector2 c)
|
|
{
|
|
return CrossProduct(a, b, c) > 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 计算叉积
|
|
/// </summary>
|
|
public static float CrossProduct(Vector2 a, Vector2 b, Vector2 c)
|
|
{
|
|
return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
|
|
}
|
|
}
|
|
} |