Three.js实现三角剖分学习笔记Day3

理论学习差不多之后,需要实现。主要参考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]) 
    }
    
}

最终实现效果

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值