2020-07-23 11:00:46 +08:00
|
|
|
|
module es {
|
2020-06-10 08:57:17 +08:00
|
|
|
|
/**
|
2020-12-07 11:48:42 +08:00
|
|
|
|
* 简单的剪耳三角测量器,最终的三角形将出现在triangleIndices列表中。
|
2020-06-10 08:57:17 +08:00
|
|
|
|
*/
|
2020-07-23 11:00:46 +08:00
|
|
|
|
export class Triangulator {
|
|
|
|
|
|
/**
|
2020-12-07 11:48:42 +08:00
|
|
|
|
* 上次三角函数调用中使用的点列表的三角列表条目索引
|
2020-07-23 11:00:46 +08:00
|
|
|
|
*/
|
|
|
|
|
|
public triangleIndices: number[] = [];
|
|
|
|
|
|
|
|
|
|
|
|
private _triPrev: number[] = new Array<number>(12);
|
|
|
|
|
|
private _triNext: number[] = new Array<number>(12);
|
|
|
|
|
|
|
2020-07-28 16:25:20 +08:00
|
|
|
|
public static testPointTriangle(point: Vector2, a: Vector2, b: Vector2, c: Vector2): boolean {
|
2020-12-07 11:48:42 +08:00
|
|
|
|
// 如果点在AB的右边,那么外边的三角形是
|
2020-07-28 16:25:20 +08:00
|
|
|
|
if (Vector2Ext.cross(Vector2.subtract(point, a), Vector2.subtract(b, a)) < 0)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
2020-12-07 11:48:42 +08:00
|
|
|
|
// 如果点在BC的右边,则在三角形的外侧
|
2020-07-28 16:25:20 +08:00
|
|
|
|
if (Vector2Ext.cross(Vector2.subtract(point, b), Vector2.subtract(c, b)) < 0)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
2020-12-07 11:48:42 +08:00
|
|
|
|
// 如果点在ca的右边,则在三角形的外面
|
2020-07-28 16:25:20 +08:00
|
|
|
|
if (Vector2Ext.cross(Vector2.subtract(point, c), Vector2.subtract(a, c)) < 0)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
2020-12-07 11:48:42 +08:00
|
|
|
|
// 点在三角形上
|
2020-07-28 16:25:20 +08:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-23 11:00:46 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 计算一个三角形列表,该列表完全覆盖给定点集所包含的区域。如果点不是CCW,则将arePointsCCW参数传递为false
|
|
|
|
|
|
* @param points 定义封闭路径的点列表
|
|
|
|
|
|
* @param arePointsCCW
|
|
|
|
|
|
*/
|
2020-07-28 16:25:20 +08:00
|
|
|
|
public triangulate(points: Vector2[], arePointsCCW: boolean = true) {
|
2020-07-23 11:00:46 +08:00
|
|
|
|
let count = points.length;
|
|
|
|
|
|
|
|
|
|
|
|
// 设置前一个链接和下一个链接
|
|
|
|
|
|
this.initialize(count);
|
|
|
|
|
|
|
|
|
|
|
|
// 非三角的多边形断路器
|
|
|
|
|
|
let iterations = 0;
|
|
|
|
|
|
|
|
|
|
|
|
// 从0开始
|
|
|
|
|
|
let index = 0;
|
|
|
|
|
|
|
|
|
|
|
|
// 继续移除所有的三角形,直到只剩下一个三角形
|
2020-07-28 16:25:20 +08:00
|
|
|
|
while (count > 3 && iterations < 500) {
|
|
|
|
|
|
iterations++;
|
2020-12-07 11:48:42 +08:00
|
|
|
|
|
2020-07-23 11:00:46 +08:00
|
|
|
|
let isEar = true;
|
2020-12-07 11:48:42 +08:00
|
|
|
|
|
2020-07-23 11:00:46 +08:00
|
|
|
|
let a = points[this._triPrev[index]];
|
|
|
|
|
|
let b = points[index];
|
|
|
|
|
|
let c = points[this._triNext[index]];
|
|
|
|
|
|
|
2020-07-28 16:25:20 +08:00
|
|
|
|
if (Vector2Ext.isTriangleCCW(a, b, c)) {
|
2020-07-23 11:00:46 +08:00
|
|
|
|
let k = this._triNext[this._triNext[index]];
|
|
|
|
|
|
do {
|
2020-07-28 16:25:20 +08:00
|
|
|
|
if (Triangulator.testPointTriangle(points[k], a, b, c)) {
|
2020-07-23 11:00:46 +08:00
|
|
|
|
isEar = false;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
k = this._triNext[k];
|
|
|
|
|
|
} while (k != this._triPrev[index]);
|
2020-07-28 16:25:20 +08:00
|
|
|
|
} else {
|
2020-07-23 11:00:46 +08:00
|
|
|
|
isEar = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-28 16:25:20 +08:00
|
|
|
|
if (isEar) {
|
2020-07-23 11:00:46 +08:00
|
|
|
|
this.triangleIndices.push(this._triPrev[index]);
|
|
|
|
|
|
this.triangleIndices.push(index);
|
|
|
|
|
|
this.triangleIndices.push(this._triNext[index]);
|
|
|
|
|
|
|
|
|
|
|
|
// 删除vert通过重定向相邻vert的上一个和下一个链接,从而减少vertext计数
|
|
|
|
|
|
this._triNext[this._triPrev[index]] = this._triNext[index];
|
|
|
|
|
|
this._triPrev[this._triNext[index]] = this._triPrev[index];
|
2020-07-28 16:25:20 +08:00
|
|
|
|
count--;
|
2020-07-23 11:00:46 +08:00
|
|
|
|
|
|
|
|
|
|
// 接下来访问前一个vert
|
|
|
|
|
|
index = this._triPrev[index];
|
2020-07-28 16:25:20 +08:00
|
|
|
|
} else {
|
2020-07-23 11:00:46 +08:00
|
|
|
|
index = this._triNext[index];
|
|
|
|
|
|
}
|
2020-06-10 08:57:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-23 11:00:46 +08:00
|
|
|
|
this.triangleIndices.push(this._triPrev[index]);
|
|
|
|
|
|
this.triangleIndices.push(index);
|
|
|
|
|
|
this.triangleIndices.push(this._triNext[index]);
|
2020-06-10 08:57:17 +08:00
|
|
|
|
|
2020-07-23 11:00:46 +08:00
|
|
|
|
if (!arePointsCCW)
|
|
|
|
|
|
this.triangleIndices.reverse();
|
2020-06-10 08:57:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-28 16:25:20 +08:00
|
|
|
|
private initialize(count: number) {
|
2020-07-23 11:00:46 +08:00
|
|
|
|
this.triangleIndices.length = 0;
|
2020-06-10 08:57:17 +08:00
|
|
|
|
|
2020-07-28 16:25:20 +08:00
|
|
|
|
if (this._triNext.length < count) {
|
2020-07-23 11:00:46 +08:00
|
|
|
|
this._triNext.reverse();
|
2020-12-07 11:48:42 +08:00
|
|
|
|
this._triNext.length = Math.max(this._triNext.length * 2, count);
|
2020-07-23 11:00:46 +08:00
|
|
|
|
}
|
2020-07-28 16:25:20 +08:00
|
|
|
|
if (this._triPrev.length < count) {
|
2020-07-23 11:00:46 +08:00
|
|
|
|
this._triPrev.reverse();
|
2020-12-07 11:48:42 +08:00
|
|
|
|
this._triPrev.length = Math.max(this._triPrev.length * 2, count);
|
2020-07-23 11:00:46 +08:00
|
|
|
|
}
|
2020-06-10 08:57:17 +08:00
|
|
|
|
|
2020-07-28 16:25:20 +08:00
|
|
|
|
for (let i = 0; i < count; i++) {
|
2020-07-23 11:00:46 +08:00
|
|
|
|
this._triPrev[i] = i - 1;
|
|
|
|
|
|
this._triNext[i] = i + 1;
|
|
|
|
|
|
}
|
2020-06-10 08:57:17 +08:00
|
|
|
|
|
2020-07-23 11:00:46 +08:00
|
|
|
|
this._triPrev[0] = count - 1;
|
|
|
|
|
|
this._triNext[count - 1] = 0;
|
2020-06-10 08:57:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-07-23 11:00:46 +08:00
|
|
|
|
}
|