using System.Collections.Generic;
using UnityEngine;
namespace FairyGUI
{
///
///
///
public class PolygonMesh : IMeshFactory, IHitTest
{
///
/// points must be in clockwise order, and must start from bottom-left if stretchUV is set.
///
public readonly List points;
///
/// if you dont want to provide uv, leave it empty.
///
public readonly List texcoords;
///
///
///
public float lineWidth;
///
///
///
public Color32 lineColor;
///
///
///
public Color32? fillColor;
///
///
///
public Color32[] colors;
///
///
///
public bool usePercentPositions;
static List sRestIndices = new List();
public PolygonMesh()
{
points = new List();
texcoords = new List();
}
///
///
///
///
public void Add(Vector2 point)
{
points.Add(point);
}
///
///
///
///
///
public void Add(Vector2 point, Vector2 texcoord)
{
points.Add(point);
texcoords.Add(texcoord);
}
public void OnPopulateMesh(VertexBuffer vb)
{
int numVertices = points.Count;
if (numVertices < 3)
return;
int restIndexPos, numRestIndices;
Color32 color = fillColor != null ? (Color32)fillColor : vb.vertexColor;
float w = vb.contentRect.width;
float h = vb.contentRect.height;
bool useTexcoords = texcoords.Count >= numVertices;
bool fullUV = true;
for (int i = 0; i < numVertices; i++)
{
Vector3 vec = new Vector3(points[i].x, points[i].y, 0);
if (usePercentPositions)
{
vec.x *= w;
vec.y *= h;
}
if (useTexcoords)
{
Vector2 uv = texcoords[i];
if (uv.x != 0 && uv.x != 1 || uv.y != 0 && uv.y != 1)
fullUV = false;
uv.x = Mathf.Lerp(vb.uvRect.x, vb.uvRect.xMax, uv.x);
uv.y = Mathf.Lerp(vb.uvRect.y, vb.uvRect.yMax, uv.y);
vb.AddVert(vec, color, uv);
}
else
vb.AddVert(vec, color);
}
if (useTexcoords && fullUV && numVertices == 4)
vb._isArbitraryQuad = true;
// Algorithm "Ear clipping method" described here:
// -> https://en.wikipedia.org/wiki/Polygon_triangulation
//
// Implementation inspired by:
// -> http://polyk.ivank.net
// -> Starling
sRestIndices.Clear();
for (int i = 0; i < numVertices; ++i)
sRestIndices.Add(i);
restIndexPos = 0;
numRestIndices = numVertices;
Vector2 a, b, c, p;
int otherIndex;
bool earFound;
int i0, i1, i2;
while (numRestIndices > 3)
{
earFound = false;
i0 = sRestIndices[restIndexPos % numRestIndices];
i1 = sRestIndices[(restIndexPos + 1) % numRestIndices];
i2 = sRestIndices[(restIndexPos + 2) % numRestIndices];
a = points[i0];
b = points[i1];
c = points[i2];
if ((a.y - b.y) * (c.x - b.x) + (b.x - a.x) * (c.y - b.y) >= 0)
{
earFound = true;
for (int i = 3; i < numRestIndices; ++i)
{
otherIndex = sRestIndices[(restIndexPos + i) % numRestIndices];
p = points[otherIndex];
if (IsPointInTriangle(ref p, ref a, ref b, ref c))
{
earFound = false;
break;
}
}
}
if (earFound)
{
vb.AddTriangle(i0, i1, i2);
sRestIndices.RemoveAt((restIndexPos + 1) % numRestIndices);
numRestIndices--;
restIndexPos = 0;
}
else
{
restIndexPos++;
if (restIndexPos == numRestIndices) break; // no more ears
}
}
vb.AddTriangle(sRestIndices[0], sRestIndices[1], sRestIndices[2]);
if (colors != null)
vb.RepeatColors(colors, 0, vb.currentVertCount);
if (lineWidth > 0)
DrawOutline(vb);
}
void DrawOutline(VertexBuffer vb)
{
int numVertices = points.Count;
int start = vb.currentVertCount - numVertices;
int k = vb.currentVertCount;
for (int i = 0; i < numVertices; i++)
{
Vector3 p0 = vb.vertices[start + i];
p0.y = -p0.y;
Vector3 p1;
if (i < numVertices - 1)
p1 = vb.vertices[start + i + 1];
else
p1 = vb.vertices[start];
p1.y = -p1.y;
Vector3 lineVector = p1 - p0;
Vector3 widthVector = Vector3.Cross(lineVector, new Vector3(0, 0, 1));
widthVector.Normalize();
vb.AddVert(p0 - widthVector * lineWidth * 0.5f, lineColor);
vb.AddVert(p0 + widthVector * lineWidth * 0.5f, lineColor);
vb.AddVert(p1 - widthVector * lineWidth * 0.5f, lineColor);
vb.AddVert(p1 + widthVector * lineWidth * 0.5f, lineColor);
k += 4;
vb.AddTriangle(k - 4, k - 3, k - 1);
vb.AddTriangle(k - 4, k - 1, k - 2);
//joint
if (i != 0)
{
vb.AddTriangle(k - 6, k - 5, k - 3);
vb.AddTriangle(k - 6, k - 3, k - 4);
}
if (i == numVertices - 1)
{
start += numVertices;
vb.AddTriangle(k - 2, k - 1, start + 1);
vb.AddTriangle(k - 2, start + 1, start);
}
}
}
bool IsPointInTriangle(ref Vector2 p, ref Vector2 a, ref Vector2 b, ref Vector2 c)
{
// From Starling
// This algorithm is described well in this article:
// http://www.blackpawn.com/texts/pointinpoly/default.html
float v0x = c.x - a.x;
float v0y = c.y - a.y;
float v1x = b.x - a.x;
float v1y = b.y - a.y;
float v2x = p.x - a.x;
float v2y = p.y - a.y;
float dot00 = v0x * v0x + v0y * v0y;
float dot01 = v0x * v1x + v0y * v1y;
float dot02 = v0x * v2x + v0y * v2y;
float dot11 = v1x * v1x + v1y * v1y;
float dot12 = v1x * v2x + v1y * v2y;
float invDen = 1.0f / (dot00 * dot11 - dot01 * dot01);
float u = (dot11 * dot02 - dot01 * dot12) * invDen;
float v = (dot00 * dot12 - dot01 * dot02) * invDen;
return (u >= 0) && (v >= 0) && (u + v < 1);
}
public bool HitTest(Rect contentRect, Vector2 point)
{
if (!contentRect.Contains(point))
return false;
// Algorithm & implementation thankfully taken from:
// -> http://alienryderflex.com/polygon/
// inspired by Starling
int len = points.Count;
int i;
int j = len - 1;
bool oddNodes = false;
float w = contentRect.width;
float h = contentRect.height;
for (i = 0; i < len; ++i)
{
float ix = points[i].x;
float iy = points[i].y;
float jx = points[j].x;
float jy = points[j].y;
if (usePercentPositions)
{
ix *= w;
iy *= h;
ix *= w;
iy *= h;
}
if ((iy < point.y && jy >= point.y || jy < point.y && iy >= point.y) && (ix <= point.x || jx <= point.x))
{
if (ix + (point.y - iy) / (jy - iy) * (jx - ix) < point.x)
oddNodes = !oddNodes;
}
j = i;
}
return oddNodes;
}
}
}