Three.js将多边形线条(Line)转换成模型(Mesh)

当一个国家由多边形轮廓组合而成时,我们如何将它转换成三角面模型呢?

国家球面Mesh生成思路:
  1. 多边形轮廓内生成一系列等间距点阵。
  2. 对点集进行三角剖分,生成国家平面Mesh。
  3. 国家平面Mesh转球面Mesh:国家平面Mesh三角形顶点经纬度坐标转球面坐标即可。
所使用的到的工具库:
  1. delaunator库(三角剖分)
    github地址:https://github.com/mapbox/delaunator
    安装方式:npm install delaunator -S
  2. point-in-polygon库(判断点是否在多边形内)
    github地址:https://github.com/substack/point-in-polygon
    安装方式:npm install point-in-polygon -S
1、多边形轮廓示例图
2、多边形轮廓内生成一系列等间距点阵。
2.1、生成等距点阵的实例代码
//根据经纬度生成点阵
const  pointInPolygon  = require('point-in-polygon'); //判断点是否在多边形内
//polygon是多边形轮廓的数据
function girlPoint(polygon) {
    var lonArr = [];  //polygon所有的经度坐标
    var latArr = []; //polygon所有的维度坐标
    polygon.forEach(elem => {
        (lonArr).push(elem[0]);
        (latArr).push(elem[1]);
    });
    const [lonMin, logMax] = minMax(lonArr);
    const [latMin, latMax] = minMax(latArr);

    // 经纬度极小值和极大值构成一个矩形范围,可以包裹多边形polygon,在矩形范围内生成等间距顶点
    const interval = 1; //polygon轮廓内填充顶点的经纬度间隔距离,选择一个合适的值,太小,计算量大,太大,国家球面不够光滑
    const row = Math.ceil((logMax - lonMin) / interval);
    const col = Math.ceil((latMax - latMin) / interval);
    var rectPointsArr = [];//polygon对应的矩形轮廓内生成均匀间隔的矩形网格数据rectPointsArr
    for (var i = 0; i < row + 1; i++) {
        for (var j = 0; j < col + 1; j++) {
            //两层for循环在矩形范围内批量生成等间距的网格顶点数据
            (rectPointsArr).push([lonMin + i * interval, latMin + j * interval]);
        }
    }
    const pointArr = [];
    rectPointsArr.forEach(elem => {
        // 判断点是否在多边形内
        if (pointInPolygon(elem, polygon)) {
            (pointArr).push(elem);
        }
    });

    return [...polygon, ...pointArr]; //返回多边形边界和内部的点
}
// 经纬度坐标排序
function minMax(arr) {
    arr.sort(compareNum);
    return [Math.floor(arr[0]), Math.ceil(arr[arr.length - 1])];
}

function compareNum(a, b) {
    if (a < b) {
        return -1;
    } else if (a > b) {
        return 1;
    } else {
        return 0;
    }
}

export { girlPoint };

3、对点集进行三角剖分,生成国家平面Mesh。

对点击进行三角剖分后我们发现多边形轮廓外面也进行了三角剖分,显然这不是我们想要的。
接下来我们需要使用point-in-polygon库(判断点是否在多边形内)来去除轮廓外的三角面。
1、求出外面三角面的重心
2、使用point-in-polygon判断重心是否在轮廓内。
3、重置顶点索引

3.1、三角剖分源码
// 三角剖分
import Delaunator from 'delaunator'; //三角剖分
const pointInPolygon = require('point-in-polygon'); //判断点是否在多边形内
//第一个参数标识多边形轮廓上的点以及内部的等边距的点集
//第二个参数标识多边形轮廓上的点
function delaunay(polygonPointsArr, polygonData) {
    // 三角剖分
    const indexArr = Delaunator.from(polygonPointsArr).triangles; //.from(pointsArr).triangles:平面上一系列点集三角剖分,并获取三角形索引值
    /**三角剖分获得的三角形索引indexArr需要进行二次处理,删除多边形polygon轮廓外面的三角形对应索引 */
    var usefulIndexArr = [];//二次处理后三角形索引,也就是保留多边形polygon内部三角形对应的索引
    for (let i = 0; i < indexArr.length; i += 3) {
        const point1 = polygonPointsArr[indexArr[i]];
        const point2 = polygonPointsArr[indexArr[i + 1]];
        const point3 = polygonPointsArr[indexArr[i + 2]];
        // 三角形重心计算
        const triangleCenter = [(point1[0] + point2[0] + point3[0]) / 3, (point1[1] + point2[1] + point3[1]) / 3];
        if (pointInPolygon(triangleCenter, polygonData)) {//判断三角形的重心是在多边形polygon内
            // 保留复合条件三角形对应的索引:indexArr[i], indexArr[i+1],indexArr[i+2]
            (usefulIndexArr).push(indexArr[i], indexArr[i + 1], indexArr[i + 2]);//这种情况需要设置three.js材质背面可见THREE.BackSide才能看到球面国家Mesh
        }
    }
    return usefulIndexArr;
}

export { delaunay };
3.2、 去除轮廓外的三角面后标志着该多边形转换成为一个Mesh
3.3、 显示三角面的源码
const polygonPointsArr = girlPoint(polygonData); //多边形边界的点以及内部的点
const usefulIndexArr = delaunay(polygonPointsArr, polygonData); //三角剖分
const posArr = []; //顶点坐标
polygonPointsArr.forEach(elem => {
    (posArr).push(elem[0], elem[1], 0);
});

const geometry = new BufferGeometry();
geometry.index = new BufferAttribute(new Uint16Array(usefulIndexArr), 1); //设置几何体的索引
geometry.attributes.position = new BufferAttribute(new Float32Array(posArr), 3); //设置几何体的顶点坐标
var material = new MeshBasicMaterial({
    color: 0x004444,
    // side: DoubleSide, //背面可见,默认正面可见   THREE.DoubleSide:双面可见
});
// geometry.computeVertexNormals();//如果使用受光照影响材质,需要计算生成法线
var mesh = new Mesh(geometry, material);
mesh.position.z = -0.01;
const tGroup = new Group();
tGroup.add(mesh);
const mesh2 = mesh.clone();
mesh2.material = new MeshBasicMaterial({
    wireframe: true,
    color: 0x009999,
});
mesh2.position.z = -0.02;
tGroup.add(mesh2);

将多边形轮廓转换成mesh后可以使用射线进行拾取操作。

你可能感兴趣的:(Three.js将多边形线条(Line)转换成模型(Mesh))