理论学习差不多之后,需要实现。主要参考http://t.csdnimg.cn/tHjOm一文的代码。
首先第一步先实现Three.js环境搭建
function init() {
scene=new THREE.Scene()
camera=new THREE.PerspectiveCamera(40,innerWidth/innerHeight,0.1,1000)
camera.position.set(100,100,0)
scene.add(camera)
scene.background = new THREE.Color(0x000000);
renderer=new THREE.WebGLRenderer()
renderer.setSize(innerWidth,innerHeight)
document.body.appendChild(renderer.domElement)
renderer.shadowMap.enabled=true
controls=new OrbitControls(camera,renderer.domElement)
const Ax=new THREE.AxesHelper(100)
scene.add(Ax)
render()
}
调用init()即可
接下来需要随机生成100个散点
即可生成100个随机点。
function initMesh(){
const geometry=new THREE.BoxGeometry(0.5,0.5,0.5)
const material=new THREE.MeshBasicMaterial({
color:new THREE.Color(0xffffff),
side:THREE.DoubleSide
})
for(var i=0;i<100;i++){
const mesh=new THREE.Mesh(geometry,material)
mesh.position.set(Math.random()*50-25,0,Math.random()*100-50)
verctories.push([mesh.position.x,mesh.position.y,mesh.position.z])
scene.add(mesh)
}
console.log(verctories)
}
一切准备工作就绪,接下来就是对散点三角剖分的实现
(1)获取这100个点的坐标
let vertices=[]
vertices.push([mesh.position.x,mesh.position.y,mesh.position.z])
(2)构建一个超级三角形包含所有的散点
function initSuperTri(){
// 初始得到xmax,xmin,ymax,ymin
// Number.NEGATIVE_INFINITY算是最小值,负无穷小
// POSITIVE_INFINITY算是最大值,可以想象成正无穷大
let xmax=Number.NEGATIVE_INFINITY
let xmin=Number.POSITIVE_INFINITY
let zmax=Number.NEGATIVE_INFINITY
let zmin=Number.POSITIVE_INFINITY
let dx,dz,dmax,xmid,zmid
// console.log(verctories[0])
// 获取点集中的X,Y最大值与最小值
for(let i=0;i<vertices.length;i++){
if(xmax<vertices[i][0]) xmax=vertices[i][0]
if(zmax<vertices[i][2]) zmax=vertices[i][2]
if(xmin>vertices[i][0]) xmin=vertices[i][0]
if(zmin>vertices[i][2]) zmin=vertices[i][2]
}
dx=xmax-xmin
dz=zmax-zmin
// console.log(dx,dz)
dmax=Math.max(dx,dz)
xmid=xmin+dx*0.5
zmid=zmin+dz*0.5
return [[xmid - 20 * dmax,0, zmid - dmax],[xmid,0,zmid + 20 * dmax],[xmid+20*dmax,0,zmid-dmax]]
}
(3)三点做外接圆方法
function initCircle(vertices, i, j, k){
// 获取三角形的三个顶点
let x1 = vertices[i][0],
z1 = vertices[i][2],
x2 = vertices[j][0],
z2 = vertices[j][2],
x3 = vertices[k][0],
z3 = vertices[k][2],
fabsz1z2 = Math.abs(z1 - z2),
fabsz2z3 = Math.abs(z2 - z3),
xc, zc, m1, m2, mx1, mx2, mz1, mz2, dx, dz;
// 这一步是为了判断三点是否在同一条线上
if(fabsz1z2 < EPSILON && fabsz2z3 < EPSILON)
throw new Error("Eek! Coincident points!");
// 当其中两个顶点在同一y坐标,也就是两点的连线就是外接圆的直径
if(fabsz1z2 < EPSILON) {
m2 = -((x3 - x2) / (z3 - z2));
mx2 = (x2 + x3) / 2.0;
mz2 = (z2 + z3) / 2.0;
xc = (x2 + x1) / 2.0;
zc = m2 * (xc - mx2) + mz2;
}
else if(fabsz2z3 < EPSILON) {
m1 = -((x2 - x1) / (z2 - z1));
mx1 = (x1 + x2) / 2.0;
mz1 = (z1 + z2) / 2.0;
xc = (x3 + x2) / 2.0;
zc = m1 * (xc - mx1) + mz1;
}
else {
m1 = -((x2 - x1) / (z2 - z1));
m2 = -((x3 - x2) / (z3 - z2));
// 计算两点之间X的中点值
mx1 = (x1 + x2) / 2.0;
mx2 = (x2 + x3) / 2.0;
mz1 = (z1 + z2) / 2.0;
mz2 = (z2 + z3) / 2.0;
// 外接圆的中心点坐标
xc = (m1 * mx1 - m2 * mx2 + mz2 - mz1) / (m1 - m2);
zc = (fabsz1z2 > fabsz2z3) ?
m1 * (xc - mx1) + mz1 :
m2 * (xc - mx2) + mz2;
}
// 计算半径
dx = x2 - xc;
dz = z2 - zc;
return {i: i, j: j, k: k, x: xc, z: zc, r: dx * dx + dz * dz};
}
(4)重复点的删除
function dedup(edges) {
var i, j, a, b, m, n;
for(j = edges.length; j; ) {
b = edges[--j];
a = edges[--j];
for(i = j; i; ) {
n = edges[--i];
m = edges[--i];
if((a === m && b === n) || (a === n && b === m)) {
edges.splice(j, 2);
edges.splice(i, 2);
break;
}
}
}
}
万事俱备只欠东风,接下来就是开始调用上面的代码,真正实现三角剖分。
function triangulate(vertices, key) {
var n = vertices.length
if(n < 3)
return [];
indices = new Array(n);
for(let i=0;i<n;i++ ){
indices[i] = i;
}
// 对点集中的点进行排序
indices.sort(function(i, j) {
var diff = vertices[j][0] - vertices[i][0];
return diff !== 0 ? diff : i - j;
});
// 生成超级三角形
st=initSuperTri(vertices)
vertices.push(st[0], st[1], st[2]);
open= [initCircle(vertices, n + 0, n + 1, n + 2)];
let num=open.length
closed = [];
edges = [];
let temp=[]
// 接下来需要将点放入到网格中,就是逐点加入整个过程
for(let i = indices.length-1; i>-1;i--) {
temp.length=0
c = indices[i];
// 检查当前点是否在外接圆内
edges.length=0
for(let j =0;j<num;j++) {
// 在计算当下这个点与三角形外接圆中心的距离
// 距离与半径相比较
dx = vertices[c][0] - open[j].x;
if(dx > 0.0 && dx * dx > open[j].r) {
closed.push(open[j]);
temp.push(j)
continue;
}
// 如果计算点在外接圆右侧跳出当前循环
// 否则进一步判断点是否在外接圆外侧
dz = vertices[c][2] - open[j].z;
if(dx * dx + dz * dz - open[j].r >0)
{
continue;
}
// 如果上述两种情况都不满足
// 那么这个三角形需要拆
// 并把三角形的三个边放入边列表
// 将三角形从暂时的三角网格列表中删除
else
{
edges.push(
open[j].i, open[j].j,
open[j].j, open[j].k,
open[j].k, open[j].i
);
temp.push(j)
}
}
dedup(edges);
let tempnum=0
let templist=[]
if(temp.length>0){
for(let d=0;d<open.length;d++){
if(d==temp[tempnum]){
num=num-1
tempnum=tempnum+1
}
else{
templist.push(open[d])
}
}
open.length=0
for(let e=0;e<templist.length;e++){
open.push(templist[e])
}
}
for(let j =0; j<edges.length;j++ ) {
b = edges[j];
a = edges[++j];
open.push(initCircle(vertices, a, b, c));
num++
}
}
for(let i=0;i<open.length;i++){
closed.push(open[i])
}
let count=closed.length
for(let l=0;l<count;l++){
if(closed[l].i<n & closed[l].j<n &closed[l].k<n){
intTri.push(closed[l])
}
// console.log('?')
}
for(let l=0;l<intTri.length;l++){
triline.push(vertices[intTri[l].i][0])
triline.push(vertices[intTri[l].i][1])
triline.push(vertices[intTri[l].i][2])
triline.push(vertices[intTri[l].j][0])
triline.push(vertices[intTri[l].j][1])
triline.push(vertices[intTri[l].j][2])
triline.push(vertices[intTri[l].k][0])
triline.push(vertices[intTri[l].k][1])
triline.push(vertices[intTri[l].k][2])
}
}
最终实现效果