當(dāng)一個(gè)國(guó)家由多邊形輪廓組合而成時(shí),我們?nèi)绾螌⑺D(zhuǎn)換成三角面模型呢?
國(guó)家球面Mesh生成思路:
- 多邊形輪廓內(nèi)生成一系列等間距點(diǎn)陣。
- 對(duì)點(diǎn)集進(jìn)行三角剖分,生成國(guó)家平面Mesh。
- 國(guó)家平面Mesh轉(zhuǎn)球面Mesh:國(guó)家平面Mesh三角形頂點(diǎn)經(jīng)緯度坐標(biāo)轉(zhuǎn)球面坐標(biāo)即可。
所使用的到的工具庫(kù):
- delaunator庫(kù)(三角剖分)
github地址:https://github.com/mapbox/delaunator
安裝方式:npm install delaunator -S - 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)行拾取操作。