
最近在做一些自定义顶点的需求,三角形组装的算法麻烦还不尽人意,干脆找了个三方库。这个效果有点像我在spine里面添加顶点时候自动组装的三角形效果了,挺不错的。
一、CDT2D 三角剖分库
1.1 功能简介
CDT2D (Constrained Delaunay Triangulation in 2D) 是一个高性能的二维三角剖分算法库。
1.2 算法原理
- Delaunay三角剖分:保证所有三角形的外接圆内不包含其他顶点,生成质量最优的三角网格
- 无边界模式:本项目使用无约束边界的剖分方式,适合凸多边形和简单凹多边形
1.3 安装与使用
安装依赖
npm install rollup cdt2d terser
const fs = require('fs');
const path = require('path');
const { rollup } = require('rollup');
const resolve = require('@rollup/plugin-node-resolve');
const commonjs = require('@rollup/plugin-commonjs');
const { minify } = require('terser');
const outputDir = path.join(__dirname, 'assets/libs');
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
const dtsContent = `declare function cdt2d(...args: any[]): any;
export = cdt2d;
`;
async function build() {
console.log('Building cdt2d...\n');
try {
console.log('Step 1/3: Rollup bundling...');
const bundle = await rollup({
input: 'node_modules/cdt2d/cdt2d.js',
plugins: [
resolve({ preferBuiltins: false }),
commonjs()
]
});
const { output } = await bundle.generate({
format: 'umd',
name: 'cdt2d',
exports: 'default'
});
const bundledCode = output[0].code;
const jsPath = path.join(outputDir, 'cdt2d.js');
fs.writeFileSync(jsPath, bundledCode);
console.log(`✓ cdt2d.js (${(bundledCode.length / 1024).toFixed(1)} KB)`);
console.log('\nStep 2/3: Terser minifying...');
const minified = await minify(bundledCode, {
compress: {
dead_code: true,
drop_debugger: true,
conditionals: true,
evaluate: true,
booleans: true,
loops: true,
unused: true,
hoist_funs: true,
keep_fargs: false,
hoist_vars: false,
if_return: true,
join_vars: true,
side_effects: true,
warnings: false
},
mangle: {
toplevel: false,
reserved: ['cdt2d']
},
format: {
comments: false,
beautify: false
}
});
if (minified.error) {
throw minified.error;
}
const minPath = path.join(outputDir, 'cdt2d.min.js');
fs.writeFileSync(minPath, minified.code);
const minSize = minified.code.length / 1024;
const originalSize = bundledCode.length / 1024;
const ratio = ((1 - minSize / originalSize) * 100).toFixed(1);
console.log(`✓ cdt2d.min.js (${minSize.toFixed(1)} KB, ${ratio}% compressed)`);
console.log('\nStep 3/3: Generating d.ts...');
const dtsPath = path.join(outputDir, 'cdt2d.d.ts');
fs.writeFileSync(dtsPath, dtsContent);
console.log(`✓ cdt2d.d.ts`);
console.log('\n' + '='.repeat(40));
console.log('Build completed!');
console.log('='.repeat(40));
console.log(`\nOutput: ${path.relative(__dirname, outputDir)}/`);
} catch (error) {
console.error('\nBuild failed:', error.message);
process.exit(1);
}
}
build();
创建一个js脚本,执行js脚本后会在assets/libs/构建js代码文件和d.ts声明文件
测试使用
import cdt2d = require("./libs/cdt2d");
const { ccclass, property } = cc._decorator;
@ccclass
export default class CDT2DExample extends cc.Component {
onLoad() {
this.testCDT2D();
}
testCDT2D() {
console.log('=== CDT2D 测试开始 ===');
// 示例1: 简单的正方形
const points1 = [
[0, 0],
[100, 0],
[100, 100],
[0, 100]
];
const edges1 = [
[0, 1],
[1, 2],
[2, 3],
[3, 0]
];
const triangles1 = cdt2d(points1, edges1);
console.log('正方形三角剖分结果:', triangles1);
// 示例2: 带孔的多边形
const points2 = [
// 外部正方形
[0, 0],
[200, 0],
[200, 200],
[0, 200],
// 内部正方形 (孔)
[50, 50],
[150, 50],
[150, 150],
[50, 150]
];
const edges2 = [
// 外部边界
[0, 1], [1, 2], [2, 3], [3, 0],
// 内部孔边界
[4, 5], [5, 6], [6, 7], [7, 4]
];
const triangles2 = cdt2d(points2, edges2, {
interior: true, // 只要内部三角形
exterior: false
});
console.log('带孔多边形三角剖分结果:', triangles2);
// 示例3: 绘制三角形网格
this.drawTriangles(points2, triangles2);
console.log('=== CDT2D 测试完成 ===');
}
/**
* 在节点上绘制三角形网格
*/
drawTriangles(points: number[][], triangles: number[][]) {
// 创建新的子节点用于绘制
const graphicsNode = new cc.Node('TriangleGraphics');
graphicsNode.parent = this.node;
graphicsNode.setPosition(0, 0);
const graphics = graphicsNode.addComponent(cc.Graphics);
// 设置绘制样式
graphics.lineWidth = 2;
graphics.strokeColor = cc.Color.GREEN;
graphics.fillColor = new cc.Color(0, 255, 0, 50);
// 绘制每个三角形
for (const tri of triangles) {
const p0 = points[tri[0]];
const p1 = points[tri[1]];
const p2 = points[tri[2]];
// 填充三角形
graphics.moveTo(p0[0] - 100, p0[1] - 100);
graphics.lineTo(p1[0] - 100, p1[1] - 100);
graphics.lineTo(p2[0] - 100, p2[1] - 100);
graphics.close();
graphics.fill();
graphics.stroke();
}
// 绘制顶点
graphics.fillColor = cc.Color.RED;
for (const point of points) {
graphics.circle(point[0] - 100, point[1] - 100, 3);
graphics.fill();
}
}
}
贴个公众号

欢迎关注,后续准备把自定义顶点的功能写一篇发一发