three.js:点到多边形的最短距离计算
程序员文章站
2022-07-14 19:41:42
...
1. 多边形的顶点存储在pointsAll数组中,允许场景中存在多个多边形,在数组中通过[1.111, 1.111, 1.111]作为不同多边形的分割点。如图,有两个多边形,顶点数据为:
(7) [Vector3, Vector3, Vector3, Vector3, Vector3, Vector3, Vector3]
0: Vector3 {x: -51.000000000000014, y: 0, z: 8.49049636423993}
1: Vector3 {x: 69.00000000000001, y: 0, z: 21.490496364233458}
2: Vector3 {x: 79.99999999999999, y: 0, z: 153.49049636416737}
3: Vector3 {x: -45.99999999999997, y: 0, z: 191.49049636414838}
4: Vector3 {x: -94.99999999999999, y: 0, z: 138.4904963641749}
5: Vector3 {x: -87.00000000000001, y: 0, z: 55.49049636421642}
6: Vector3 {x: 1.111, y: 1.111, z: 1.111}
7: Vector3 {x: -209.99999999999997, y: 0, z: -209.509503635651}
8: Vector3 {x: -114.99999999999999, y: 0, z: -208.50950363565144}
9: Vector3 {x: -69.00000000000001, y: 0, z: -89.50950363571106}
10: Vector3 {x: -145, y: 0, z: -21.509503635745077}
11: Vector3 {x: -208, y: 0, z: -60.50950363572556}
12: Vector3 {x: -250.99999999999997, y: 0, z: -130.5095036356905}
13: Vector3 {x: 1.111, y: 1.111, z: 1.111}
length: 14
__proto__: Array(0)
2. 鼠标点击页面时,调用函数inFenceDetect和closeFenceDetect
inFenceDetect(pointsAll, cirFenceAll, clickPos)
- 功能:检测鼠标点击的点是否落在多边形电子围栏内
- 参数:pointsAll是所有多边形的顶点集合,如步骤1所示;cirFenceAll是圆形电子围栏的位置和半径,此处忽略,以[0, 0, 0]代替;clickPos是鼠标的点击位置转换到three.js世界坐标后的值
- 返回值:true或者false,true代表鼠标点击位置处于多边形电子围栏内,反之则在围栏外
closeFenceDetect(pointsAll, cirFenceAll, clickPos, 10)
- 功能:检测鼠标点击位置是否在多边形电子围栏附近
- 参数:pointsAll是所有多边形的顶点集合,如步骤1所示;cirFenceAll是圆形电子围栏的位置和半径,此处忽略,以[0, 0, 0]代替;clickPos是鼠标的点击位置转换到three.js世界坐标后的值;参数值10是阈值,代表鼠标点击位置与多边形的最小距离小于10个像素时,判定为接近电子围栏,该值可以随意设定
- 返回值:字符串IN / OUT / CLOSE,IN表示点击位置在多边形电子围栏内,OUT表示点击点在多边形电子围栏外面且不接近;CLOSE表示点击位置距离多边形电子围栏10个像素以内时,判定为接近电子围栏
container.onclick = function(){
var res, str, dis;
var clickPos = dom2world(event.clientX,event.clientY);
res = inFenceDetect(pointsAll, cirFenceAll, clickPos);
dis = closeFenceDetect(pointsAll, cirFenceAll, clickPos, 10);
if (res) {str='IN';} else {str='OUT';}
console.log('在多边形内,结果:'+str)
console.log('靠近多边形,结果:'+dis)
// console.log( pointsAll )
}
3. 函数体
inFenceDetect(pointsAll, cirFenceAll, clickPos)
function inFenceDetect(PointsAll, cirFenceAll, p){
/*
1. 从数据库或者本地获取一共有M个圆形,N个多边形
2. 获取每个圆形的圆心和半径,获取每个多边形的顶点
3. 测试当前点是否在圆形中
4. 测试当前点是否在多边形中
有PointsAll中包含多个多边形,以1.111分割
PointsSeg表示某个具体的多边形顶点集合
segFlag表示某个多边形第一个起始顶点在pointsAll中的索引值
res为总的返回结果,true或者false
segRes为某个多边形返回的判断结果,true或者false
segRes之间是或关系,只要有一个segRes返回真,就说明多边形电子围栏被触发了
*/
var count = PointsAll.length, PointsSeg=[], segFlag=0, res=0, segRes;
for (var i = 0; i < PointsAll.length; i++) {
if (PointsAll[i].x==1.111 && PointsAll[i].y==1.111 && PointsAll[i].z == 1.111) {
for (var j = segFlag; j < i; j++) {
PointsSeg.push( PointsAll[j] );
}
segRes = isInsidePoly(PointsSeg,p);
while(PointsSeg.length>0){ PointsSeg.pop(); }
segFlag=i+1;
res = res|segRes;
i = i+1;
}
}
for (var i = 0; i < cirFenceAll.length; i++) {
var resCir = isInsideCircle([cirFenceAll[i][0],cirFenceAll[i][1]], cirFenceAll[i][2], p);
res = res|resCir;
}
return res;
// isInsidePoly();
}
function isInsidePoly(Points,p){
//判断一个点是否在Points组成的多边形内
//1. 获取多边形的顶点数目
//2. 如果多边形顶点小于3,直接返回0。否则继续往下判断
//3. 以射线法探测顶点,判断顶点是否在多边形内。
// 判断规则,从p点引射线,如果与多边形相交为奇数个点,则在多边形内,否则在多边形外。
var count = Points.length;
if(count < 3){ return false; }
var result = false;
for(var i = 0, j = count - 1; i < count; i++)
{
var p1 = Points[i];
var p2 = Points[j];
if(p1.x < p.x && p2.x >= p.x || p2.x < p.x && p1.x >= p.x)
{
if(p1.z + (p.x-p1.x) / (p2.x-p1.x) * (p2.z-p1.z) < p.z) { result = !result; }
}
j = i;
}
return result;
}
function isInsideCircle(pos, radius, p){
//判断点p到圆形电子围栏的距离,距离小于半径radius,证明越过了电子围栏,违规,返回true
console.log(pos)
console.log(radius)
var dis = Math.pow(p.x-pos[0],2) + Math.pow(p.z-pos[1],2);
var dis = Math.sqrt(dis);
if(dis<radius)
return true;
else
return false;
}
closeFenceDetect(pointsAll, cirFenceAll, clickPos, 10)
function closeFenceDetect(PointsAll, cirFenceAll, p, threshold){
//1. 检测当前点是否接近多边形电子围栏
//2. 距离电子围栏近
//返回值 在电子围栏中IN 靠近CLOSE 不靠近OUT
var count = PointsAll.length, PointsSeg=[], segFlag=0, res=0, segRes=0;
res = inFenceDetect(PointsAll, cirFenceAll, p);
if (res==true) { return 'IN'; }
for (var i = 0; i < PointsAll.length; i++) {
if (PointsAll[i].x==1.111 && PointsAll[i].y==1.111 && PointsAll[i].z == 1.111) {
for (var j = segFlag; j < i; j++) {
PointsSeg.push( PointsAll[j] );
}
segRes = isClosePoly(PointsSeg,p);
console.log('segRes: '+segRes)
while(PointsSeg.length>0){ PointsSeg.pop(); }
segFlag=i+1;
i = i+1;
if( segRes<threshold ){ return 'CLOSE'; }
}
}
return 'OUT';
}
function isClosePoly(points,p){
/*
points: 多边形的所有顶点 p:多边形外一点
点p可以和相邻的两个顶点组成一个三角形;
如果三角形三个角都为锐角,则p到该边最小值为垂线;
如果点p对应的角为钝角,则p到该边最小值为垂线;
如果点p为锐角,其余两个角中任意一个为钝角,则p到该边最小值为p到两个顶点的距离的最小值
*/
var a,b,c; //三条边
var A,B,C; //三个角
var dis=0, dism=0, dis_temp; //点到线段的距离;最小距离;记录第一个数值
for (var i = 0; i < points.length; i++) {
if ( i<points.length-1 ) {
a = Math.sqrt( Math.pow( p.x-points[i+1].x,2 ) + Math.pow( p.z-points[i+1].z,2 ) );
b = Math.sqrt( Math.pow( p.x-points[i].x,2 ) + Math.pow( p.z-points[i].z,2 ) );
c = Math.sqrt( Math.pow( points[i].x-points[i+1].x,2 ) + Math.pow( points[i].z-points[i+1].z,2 ) );
A = Math.acos( (b*b+c*c-a*a)/(2*b*c) ) * 180/3.14159;
B = Math.acos( (a*a+c*c-b*b)/(2*a*c) ) * 180/3.14159;
C = Math.acos( (a*a+b*b-c*c)/(2*a*b) ) * 180/3.14159;
if( C>90 ){
dis = Math.sqrt( b*b - Math.pow( (b*b+c*c-a*a)/(2*c),2) );
}else if( A<90 && B<90 ){
dis = Math.sqrt( b*b - Math.pow( (b*b+c*c-a*a)/(2*c),2) );
}else if( A>90 || B>90 ){
a<b?dis=a:dis=b;
}
}else{
a = Math.sqrt( Math.pow( p.x-points[0].x,2 ) + Math.pow( p.z-points[0].z,2 ) );
b = Math.sqrt( Math.pow( p.x-points[i].x,2 ) + Math.pow( p.z-points[i].z,2 ) );
c = Math.sqrt( Math.pow( points[i].x-points[0].x,2 ) + Math.pow( points[i].z-points[0].z,2 ) );
A = Math.acos( (b*b+c*c-a*a)/(2*b*c) ) * 180/3.14159;
B = Math.acos( (a*a+c*c-b*b)/(2*a*c) ) * 180/3.14159;
C = Math.acos( (a*a+b*b-c*c)/(2*a*b) ) * 180/3.14159;
if( C>90 ){
dis = Math.sqrt( b*b - Math.pow( (b*b+c*c-a*a)/(2*c),2) );
}else if( A<90 && B<90 ){
dis = Math.sqrt( b*b - Math.pow( (b*b+c*c-a*a)/(2*c),2) );
}else if( A>90 || B>90 ){
a<b?dis=a:dis=b;
}
}
i==0?dism=dis:dism=dism;
dis<dism?dism=dis:dism=dism;
console.log('角 A='+A+',B='+B+',C='+C)
console.log('边 a='+a+',b='+b+',c='+c)
console.log('dis = '+dis)
console.log('dism = '+dism)
}
return dism;
}
4. 如何计算点到多边形的最短距离
假设多边形有5条边,多边形外一点为C,那么最短距离无疑是点C到多边形5条边的距离的最小值。注意,注意,注意,这5条边是线段,而不是直线。以其中一条边AB为例:当∠A和∠B为锐角时,点C到线段AB的最短距离为垂直距离;当∠A和∠B中有一个为锐角时,点C到线段AB的最短距离为min{AC, BC}。for循环重复5遍上述步骤,就可以求出点C到五边形的最短距离了。