实现凹形的三角剖分算法
Implementing triangulation algorithm for concave shapes
我在用 c 实现三角剖分算法时遇到了一些问题。有些点选错了,我也不知道哪里会出错,但是对于我这个没有经验的人来说,看起来还不错。
编辑:我正在使用 msvc c++ 编译器为 v3 结构重载一些运算符。
这是我的主要代码:
struct shape
{
u32 VerticesCount;
v3* Vertices;
u32 TrianglesCount;
int* Triangles;
};
static void PolygonTriangulate(shape* Shape)
{
bool Result = false;
int ListSize = Shape->VerticesCount;
int* IndexList = (int*)malloc(sizeof(int) * ListSize);
for(int i = 0; i < ListSize; ++i)
{
IndexList[i] = i;
}
int TotalTriangleCount = Shape->VerticesCount - 2;
int TotalTriangleCountIdx = TotalTriangleCount*3;
int* TrianglesResult = (int*)malloc(sizeof(int)*TotalTriangleCountIdx);
int TriangleIdx = 0;
while(ListSize > 3)
{
for(int PolyIdx = 0; PolyIdx < ListSize; ++PolyIdx)
{
int cur = IndexList[PolyIdx];
int prev = GetListElement(IndexList, ListSize, PolyIdx - 1);
int next = GetListElement(IndexList, ListSize, PolyIdx + 1);
v3 A = Shape->Vertices[cur];
v3 B = Shape->Vertices[prev];
v3 C = Shape->Vertices[next];
v3 AB = B - A;
v3 AC = C - A;
if(Cross(AB, AC).Z < 0.0f)
{
continue;
}
bool IsEar = true;
for(int i = 0; i < Shape->VerticesCount; ++i)
{
if((cur == i) || (prev == i) || (next == i))
{
continue;
}
v3 Point = Shape->Vertices[i];
if(IsPointInTriangle(Point, B, A, C))
{
IsEar = false;
break;
}
}
if(IsEar)
{
TrianglesResult[TriangleIdx++] = prev;
TrianglesResult[TriangleIdx++] = cur;
TrianglesResult[TriangleIdx++] = next;
RemoveElementFromList(IndexList, &ListSize, PolyIdx);
break;
}
}
}
TrianglesResult[TriangleIdx++] = IndexList[0];
TrianglesResult[TriangleIdx++] = IndexList[1];
TrianglesResult[TriangleIdx++] = IndexList[2];
Shape->TrianglesCount = TriangleIdx;
Shape->Triangles = TrianglesResult;
}
助手函数:
static bool IsPointInTriangle(v3 Point, v3 A, v3 B, v3 C)
{
v3 AB = B - A;
v3 BC = C - B;
v3 CA = A - C;
v3 AP = Point - A;
v3 BP = Point - B;
v3 CP = Point - C;
float Cross1 = Cross(AB, AP).Z;
float Cross2 = Cross(BC, BP).Z;
float Cross3 = Cross(CA, CP).Z;
if((Cross1 <= 0.0f) && (Cross2 <= 0.0f) && (Cross3 <= 0.0f))
{
return true;
}
return false;
}
int GetListElement(int* IndexList, int ListSize, int PolyIdx)
{
int Result;
if(PolyIdx < 0)
{
Result = IndexList[PolyIdx % ListSize + ListSize];
}
else if(PolyIdx >= ListSize)
{
Result = IndexList[PolyIdx % ListSize];
}
else
{
Result = IndexList[PolyIdx];
}
return Result;
}
void RemoveElementFromList(int* IndexList, int* ListSize, int PolyIdx)
{
for(int i = 0; i < *ListSize; ++i)
{
if(IndexList[i] >= PolyIdx)
{
IndexList[i] = IndexList[i + 1];
}
}
*ListSize -= 1;
}
以及我渲染形状的地方,但我认为不会有问题:
for(u32 CoordIdx = 0; CoordIdx < Shape->TrianglesCount; CoordIdx += 3)
{
triangle_t Triangle = {};
Triangle.points[0] = 20*Shape->Vertices[Shape->Triangles[CoordIdx ]].XY + Center;
Triangle.points[1] = 20*Shape->Vertices[Shape->Triangles[CoordIdx+1]].XY + Center;
Triangle.points[2] = 20*Shape->Vertices[Shape->Triangles[CoordIdx+2]].XY + Center;
DrawTriangle(Triangle, CreateColor(V3(1.0f, 1.0f, 0.0f)));
}
我的结果:
我的结果应该是这样的,但是没有内部三角形:
预期结果:
上图请注意:我使用了不同的渲染系统,这就是为什么它看起来是倒置的,这应该不会影响结果。谢谢。
好的,正如 Matt Timmermans 提到的,我只是在 RemoveElementFromList
函数中遇到了简单的错字。
而不是 if (IndexList[i] >= PolyIdx)
应该是 if (i >= PolyIdx)
:
void RemoveElementFromList(int* IndexList, int* ListSize, int PolyIdx)
{
for(int i = 0; i < *ListSize; ++i)
{
if(i >= PolyIdx)
{
IndexList[i] = IndexList[i + 1];
}
}
*ListSize -= 1;
}
就这么简单。
我在用 c 实现三角剖分算法时遇到了一些问题。有些点选错了,我也不知道哪里会出错,但是对于我这个没有经验的人来说,看起来还不错。
编辑:我正在使用 msvc c++ 编译器为 v3 结构重载一些运算符。
这是我的主要代码:
struct shape
{
u32 VerticesCount;
v3* Vertices;
u32 TrianglesCount;
int* Triangles;
};
static void PolygonTriangulate(shape* Shape)
{
bool Result = false;
int ListSize = Shape->VerticesCount;
int* IndexList = (int*)malloc(sizeof(int) * ListSize);
for(int i = 0; i < ListSize; ++i)
{
IndexList[i] = i;
}
int TotalTriangleCount = Shape->VerticesCount - 2;
int TotalTriangleCountIdx = TotalTriangleCount*3;
int* TrianglesResult = (int*)malloc(sizeof(int)*TotalTriangleCountIdx);
int TriangleIdx = 0;
while(ListSize > 3)
{
for(int PolyIdx = 0; PolyIdx < ListSize; ++PolyIdx)
{
int cur = IndexList[PolyIdx];
int prev = GetListElement(IndexList, ListSize, PolyIdx - 1);
int next = GetListElement(IndexList, ListSize, PolyIdx + 1);
v3 A = Shape->Vertices[cur];
v3 B = Shape->Vertices[prev];
v3 C = Shape->Vertices[next];
v3 AB = B - A;
v3 AC = C - A;
if(Cross(AB, AC).Z < 0.0f)
{
continue;
}
bool IsEar = true;
for(int i = 0; i < Shape->VerticesCount; ++i)
{
if((cur == i) || (prev == i) || (next == i))
{
continue;
}
v3 Point = Shape->Vertices[i];
if(IsPointInTriangle(Point, B, A, C))
{
IsEar = false;
break;
}
}
if(IsEar)
{
TrianglesResult[TriangleIdx++] = prev;
TrianglesResult[TriangleIdx++] = cur;
TrianglesResult[TriangleIdx++] = next;
RemoveElementFromList(IndexList, &ListSize, PolyIdx);
break;
}
}
}
TrianglesResult[TriangleIdx++] = IndexList[0];
TrianglesResult[TriangleIdx++] = IndexList[1];
TrianglesResult[TriangleIdx++] = IndexList[2];
Shape->TrianglesCount = TriangleIdx;
Shape->Triangles = TrianglesResult;
}
助手函数:
static bool IsPointInTriangle(v3 Point, v3 A, v3 B, v3 C)
{
v3 AB = B - A;
v3 BC = C - B;
v3 CA = A - C;
v3 AP = Point - A;
v3 BP = Point - B;
v3 CP = Point - C;
float Cross1 = Cross(AB, AP).Z;
float Cross2 = Cross(BC, BP).Z;
float Cross3 = Cross(CA, CP).Z;
if((Cross1 <= 0.0f) && (Cross2 <= 0.0f) && (Cross3 <= 0.0f))
{
return true;
}
return false;
}
int GetListElement(int* IndexList, int ListSize, int PolyIdx)
{
int Result;
if(PolyIdx < 0)
{
Result = IndexList[PolyIdx % ListSize + ListSize];
}
else if(PolyIdx >= ListSize)
{
Result = IndexList[PolyIdx % ListSize];
}
else
{
Result = IndexList[PolyIdx];
}
return Result;
}
void RemoveElementFromList(int* IndexList, int* ListSize, int PolyIdx)
{
for(int i = 0; i < *ListSize; ++i)
{
if(IndexList[i] >= PolyIdx)
{
IndexList[i] = IndexList[i + 1];
}
}
*ListSize -= 1;
}
以及我渲染形状的地方,但我认为不会有问题:
for(u32 CoordIdx = 0; CoordIdx < Shape->TrianglesCount; CoordIdx += 3)
{
triangle_t Triangle = {};
Triangle.points[0] = 20*Shape->Vertices[Shape->Triangles[CoordIdx ]].XY + Center;
Triangle.points[1] = 20*Shape->Vertices[Shape->Triangles[CoordIdx+1]].XY + Center;
Triangle.points[2] = 20*Shape->Vertices[Shape->Triangles[CoordIdx+2]].XY + Center;
DrawTriangle(Triangle, CreateColor(V3(1.0f, 1.0f, 0.0f)));
}
我的结果:
我的结果应该是这样的,但是没有内部三角形:
预期结果:
上图请注意:我使用了不同的渲染系统,这就是为什么它看起来是倒置的,这应该不会影响结果。谢谢。
好的,正如 Matt Timmermans 提到的,我只是在 RemoveElementFromList
函数中遇到了简单的错字。
而不是 if (IndexList[i] >= PolyIdx)
应该是 if (i >= PolyIdx)
:
void RemoveElementFromList(int* IndexList, int* ListSize, int PolyIdx)
{
for(int i = 0; i < *ListSize; ++i)
{
if(i >= PolyIdx)
{
IndexList[i] = IndexList[i + 1];
}
}
*ListSize -= 1;
}
就这么简单。