This repository has been archived on 2026-07-07. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
UIGraphRenderer/Runtime/Core/TriangulateHelp.cs
T
2026-04-30 08:57:45 +08:00

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);
}
}
}