Three.js將多邊形線條(Line)轉(zhuǎn)換成模型(Mesh)

當(dāng)一個(gè)國(guó)家由多邊形輪廓組合而成時(shí),我們?nèi)绾螌⑺D(zhuǎn)換成三角面模型呢?

國(guó)家球面Mesh生成思路:
  1. 多邊形輪廓內(nèi)生成一系列等間距點(diǎn)陣。
  2. 對(duì)點(diǎn)集進(jìn)行三角剖分,生成國(guó)家平面Mesh。
  3. 國(guó)家平面Mesh轉(zhuǎn)球面Mesh:國(guó)家平面Mesh三角形頂點(diǎn)經(jīng)緯度坐標(biāo)轉(zhuǎn)球面坐標(biāo)即可。
所使用的到的工具庫(kù):
  1. delaunator庫(kù)(三角剖分)
    github地址:https://github.com/mapbox/delaunator
    安裝方式:npm install delaunator -S
  2. point-in-polygon庫(kù)(判斷點(diǎn)是否在多邊形內(nèi))
    github地址:https://github.com/substack/point-in-polygon
    安裝方式:npm install point-in-polygon -S
1、多邊形輪廓示例圖
2、多邊形輪廓內(nèi)生成一系列等間距點(diǎn)陣。
2.1、生成等距點(diǎn)陣的實(shí)例代碼
//根據(jù)經(jīng)緯度生成點(diǎn)陣
const  pointInPolygon  = require('point-in-polygon'); //判斷點(diǎn)是否在多邊形內(nèi)
//polygon是多邊形輪廓的數(shù)據(jù)
function girlPoint(polygon) {
    var lonArr = [];  //polygon所有的經(jīng)度坐標(biāo)
    var latArr = []; //polygon所有的維度坐標(biāo)
    polygon.forEach(elem => {
        (<any>lonArr).push(elem[0]);
        (<any>latArr).push(elem[1]);
    });
    const [lonMin, logMax] = minMax(lonArr);
    const [latMin, latMax] = minMax(latArr);

    // 經(jīng)緯度極小值和極大值構(gòu)成一個(gè)矩形范圍,可以包裹多邊形polygon,在矩形范圍內(nèi)生成等間距頂點(diǎn)
    const interval = 1; //polygon輪廓內(nèi)填充頂點(diǎn)的經(jīng)緯度間隔距離,選擇一個(gè)合適的值,太小,計(jì)算量大,太大,國(guó)家球面不夠光滑
    const row = Math.ceil((logMax - lonMin) / interval);
    const col = Math.ceil((latMax - latMin) / interval);
    var rectPointsArr = [];//polygon對(duì)應(yīng)的矩形輪廓內(nèi)生成均勻間隔的矩形網(wǎng)格數(shù)據(jù)rectPointsArr
    for (var i = 0; i < row + 1; i++) {
        for (var j = 0; j < col + 1; j++) {
            //兩層for循環(huán)在矩形范圍內(nèi)批量生成等間距的網(wǎng)格頂點(diǎn)數(shù)據(jù)
            (<any>rectPointsArr).push([lonMin + i * interval, latMin + j * interval]);
        }
    }
    const pointArr = [];
    rectPointsArr.forEach(elem => {
        // 判斷點(diǎn)是否在多邊形內(nèi)
        if (pointInPolygon(elem, polygon)) {
            (<any>pointArr).push(elem);
        }
    });

    return [...polygon, ...pointArr]; //返回多邊形邊界和內(nèi)部的點(diǎn)
}
// 經(jīng)緯度坐標(biāo)排序
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、對(duì)點(diǎn)集進(jìn)行三角剖分,生成國(guó)家平面Mesh。

對(duì)點(diǎn)擊進(jìn)行三角剖分后我們發(fā)現(xiàn)多邊形輪廓外面也進(jìn)行了三角剖分,顯然這不是我們想要的。
接下來(lái)我們需要使用point-in-polygon庫(kù)(判斷點(diǎn)是否在多邊形內(nèi))來(lái)去除輪廓外的三角面。
1、求出外面三角面的重心
2、使用point-in-polygon判斷重心是否在輪廓內(nèi)。
3、重置頂點(diǎn)索引

3.1、三角剖分源碼
// 三角剖分
import Delaunator from 'delaunator'; //三角剖分
const pointInPolygon = require('point-in-polygon'); //判斷點(diǎn)是否在多邊形內(nèi)
//第一個(gè)參數(shù)標(biāo)識(shí)多邊形輪廓上的點(diǎn)以及內(nèi)部的等邊距的點(diǎn)集
//第二個(gè)參數(shù)標(biāo)識(shí)多邊形輪廓上的點(diǎn)
function delaunay(polygonPointsArr, polygonData) {
    // 三角剖分
    const indexArr = Delaunator.from(polygonPointsArr).triangles; //.from(pointsArr).triangles:平面上一系列點(diǎn)集三角剖分,并獲取三角形索引值
    /**三角剖分獲得的三角形索引indexArr需要進(jìn)行二次處理,刪除多邊形polygon輪廓外面的三角形對(duì)應(yīng)索引 */
    var usefulIndexArr = [];//二次處理后三角形索引,也就是保留多邊形polygon內(nèi)部三角形對(duì)應(yīng)的索引
    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]];
        // 三角形重心計(jì)算
        const triangleCenter = [(point1[0] + point2[0] + point3[0]) / 3, (point1[1] + point2[1] + point3[1]) / 3];
        if (pointInPolygon(triangleCenter, polygonData)) {//判斷三角形的重心是在多邊形polygon內(nèi)
            // 保留復(fù)合條件三角形對(duì)應(yīng)的索引:indexArr[i], indexArr[i+1],indexArr[i+2]
            (<any>usefulIndexArr).push(indexArr[i], indexArr[i + 1], indexArr[i + 2]);//這種情況需要設(shè)置three.js材質(zhì)背面可見(jiàn)THREE.BackSide才能看到球面國(guó)家Mesh
        }
    }
    return usefulIndexArr;
}

export { delaunay };
3.2、 去除輪廓外的三角面后標(biāo)志著該多邊形轉(zhuǎn)換成為一個(gè)Mesh
3.3、 顯示三角面的源碼
const polygonPointsArr = girlPoint(polygonData); //多邊形邊界的點(diǎn)以及內(nèi)部的點(diǎn)
const usefulIndexArr = delaunay(polygonPointsArr, polygonData); //三角剖分
const posArr = []; //頂點(diǎn)坐標(biāo)
polygonPointsArr.forEach(elem => {
    (<any>posArr).push(elem[0], elem[1], 0);
});

const geometry = new BufferGeometry();
geometry.index = new BufferAttribute(new Uint16Array(usefulIndexArr), 1); //設(shè)置幾何體的索引
geometry.attributes.position = new BufferAttribute(new Float32Array(posArr), 3); //設(shè)置幾何體的頂點(diǎn)坐標(biāo)
var material = new MeshBasicMaterial({
    color: 0x004444,
    // side: DoubleSide, //背面可見(jiàn),默認(rèn)正面可見(jiàn)   THREE.DoubleSide:雙面可見(jiàn)
});
// geometry.computeVertexNormals();//如果使用受光照影響材質(zhì),需要計(jì)算生成法線
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);

將多邊形輪廓轉(zhuǎn)換成mesh后可以使用射線進(jìn)行拾取操作。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 多邊形三角剖分 (Triangulation) 三角剖分有兩種,一種是對(duì)多邊形的三角剖分,一種是對(duì)平面點(diǎn)集的...
    dyume閱讀 11,062評(píng)論 0 3
  • 背景介紹與問(wèn)題分析 在之前的 《如何判斷一個(gè)多邊形是否合法》 一文中有提到,用無(wú)人機(jī)規(guī)劃飛行路線前,往往需要框選...
    hanson21閱讀 19,237評(píng)論 2 7
  • 前言 最近跟團(tuán)隊(duì)想要開(kāi)發(fā)一個(gè)開(kāi)放世界的游戲,這是很有趣的游戲概念,然而參考了《塞爾達(dá)傳說(shuō) 荒野之息》的設(shè)定后發(fā)現(xiàn),...
    heibe閱讀 7,655評(píng)論 1 16
  • File open:打開(kāi) save:保存 Global Shift settings:設(shè)置最大絕對(duì)坐標(biāo),最大實(shí)體對(duì)...
    huihut閱讀 10,065評(píng)論 0 2
  • 多邊形是可以用來(lái)在 Autodesk? Maya? 中創(chuàng)建三維模型的一種幾何體類(lèi)型。Maya 為在 3D 中建模提...
    ZeroBY閱讀 1,699評(píng)論 0 1

友情鏈接更多精彩內(nèi)容