Javascript:多边形中的点性能改进

Javascript:多边形中的点性能改进,javascript,polygon,computational-geometry,point,point-in-polygon,Javascript,Polygon,Computational Geometry,Point,Point In Polygon,我有一个对象数组。每个对象表示一个点,该点有一个ID和一个具有x y坐标的数组,e、 g: let points = [{id: 1, coords: [1,2]}, {id: 2, coords: [2,3]}] 我还有一个数组,包含x和y坐标。此数组表示多边形,例如: let polygon = [[0,0], [0,3], [1,4], [0,2]] 多边形是闭合的,因此阵列的最后一个点链接到第一个点 我使用以下算法检查点是否位于多边形内: pointInPolygon = funct

我有一个对象数组。每个对象表示一个点,该点有一个ID和一个具有x y坐标的数组,e、 g:

let points = [{id: 1, coords: [1,2]}, {id: 2, coords: [2,3]}]
我还有一个数组,包含x和y坐标。此数组表示多边形,例如:

let polygon = [[0,0], [0,3], [1,4], [0,2]]
多边形是闭合的,因此阵列的最后一个点链接到第一个点

我使用以下算法检查点是否位于多边形内:

pointInPolygon = function (point, polygon) {
  // from https://github.com/substack/point-in-polygon
  let x = point.coords[0]
  let y = point.coords[1]
  let inside = false

  for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
    let xi = polygon[i][0]
    let yi = polygon[i][1]
    let xj = polygon[j][0]
    let yj = polygon[j][1]
    let intersect = ((yi > y) !== (yj > y)) &&
                    (x < (xj - xi) * (y - yi) / (yj - yi) + xi)
    if (intersect) inside = !inside
  }

  return inside
}

当点数不是很高时,这可以正常工作(如果我理解正确,从鼠标记录的新点将使多边形变大一点。因此,如果在某个时刻多边形由n个点(0,1,…,n-1)定义,并且记录了一个新点p,则多边形变为(0,1,…,n-1,p)

这意味着从多边形中删除一条边,并向其中添加两条边

例如,假设多边形上有9个点,编号为0到8,其中点8是添加到该多边形的最后一个点:

灰线是闭合多边形的边

现在,鼠标移动到添加到多边形的点9:

灰色边将从多边形中删除,两条绿色边将添加到多边形中。现在请遵守以下规则:

由灰色和两条绿色边组成的三角形中的点在与更改前的位置相比时交换多边形的输入/输出。所有其他点保留其以前的输入/输出状态

因此,如果要在内存中保留每个点的状态,则只需检查每个点是否在上述三角形内,如果是,则需要切换该点的状态

由于三角形中包含的测试比潜在复杂多边形中包含的测试花费的时间要少,因此这将导致更高效的算法

您可以进一步提高效率,如果您采用三角形的边界矩形,其角位于(x0,y0)、(x1,y0)、(x1,y1)、(x0,y1)。那么您可以跳过x或y坐标超出范围的点:

蓝色框外的任何点都不会改变状态:如果在添加最后一个点9之前它在多边形内,现在仍然是。仅对框内的点需要进行
pointInPolygon
测试,但只对三角形进行测试,而不是对整个多边形进行测试。如果该测试返回
true
,则测试poi的状态必须切换nt

将点分组到方形框中 为了进一步加快该过程,您可以将带有栅格的平面划分为方形框,其中每个点属于一个框,但一个框通常会有多个点。要确定三角形中的哪些点,您可以首先确定哪些框与三角形重叠

为此,您不必测试每个长方体,但可以从三角形边上的坐标导出长方体

然后,只需单独测试其余框中的点。您可以使用框大小,看看它如何影响性能

下面是一个实施这些想法的工作示例。有10000分,但我的电脑没有落后:

canvas.width=document.body.clientWidth;
常数min=[0,0],
max=[canvas.width,canvas.height],
点=数组。从(数组(10000),i=>{
设x=Math.floor(Math.random()*(max[0]-min[0])+min[0]);
设y=Math.floor(Math.random()*(max[1]-min[1])+min[1]);
返回[x,y];
}),
多边形=[],
boxSize=Math.ceil((最大值[0]-最小值[0])/50),
框=(函数(Xbox、Ybox){
返回Array.from(数组(yBoxes))\ux=>
从(数组(Xbox),=>[]);
})(toBox(0,最大[0])+1,toBox(1,最大[1])+1),
insidePoints=新集合,
ctx=canvas.getContext('2d');
功能提取点(p){
ctx.fillRect(p[0],p[1],1,1);
}
函数绘制多边形(pol){
ctx.beginPath();
ctx.moveTo(pol[0][0],pol[0][1]);;
对于(pol的常数p){
ctx.lineTo(p[0],p[1]);
}
ctx.stroke();
}
功能分段图(a、b、dim、坐标){
//找到ab与同轴线相交的坐标
//给定坐标。
//首先是一些边界条件:
常数dim2=1-dim;
if(a[dim]==坐标){
如果(b[dim]==coord)返回[a[dim2],b[dim2]];
返回[a[dim2]];
}
如果(b[dim]==coord)返回[b[dim2];
//查看是否没有交叉点:
如果((坐标>a[dim])==(坐标>b[dim])返回[];
//有一个交点:
常数res=(坐标-a[dim])*(b[dim2]-a[dim2])/(b[dim]-a[dim])+a[dim2];
返回[res];
}
函数isLeft(a、b、c){
//如果c位于ab的左侧,则返回true:
返回(b[0]-a[0])*(c[1]-a[1])-(b[1]-a[1])*(c[0]-a[0])>0;
}
三角函数(a,b,c,p){
//首先,选中边界框:
if(p[0]Math.max(a[0],b[0],c[0])||
p[1]Math.max(a[1],b[1],c[1])返回false;
//然后检查该点是否位于每一条曲线的同一侧
//三条边:
常数x=isLeft(a,b,p),
y=isLeft(b,c,p),
z=isLeft(c,a,p);
返回x?y&&z:!y&&z;
}
toBox功能(dim、coord){
返回数学地板((坐标-最小值[尺寸])/boxSize);
}
功能到世界(尺寸,方框){
返回框*boxSize+min[dim];
}
功能抽屉(boxX,boxY){
设x=toWorld(0,boxX);
设y=toWorld(1,长方体);
drawPolygon([[x,y],[x+boxSize,y],[x+boxSize,y+boxSize],[x,y+boxSize],[x,y]];
}
函数三角测试(a、b、c、点、内部点){
const markedBoxes=new Set(),//与三角形重叠的框的集合
框=[];
对于(设dim=0;dim<2;dim++){
常数dim2=1-dim,
//按坐标对三角形点进行排序
[d,e,f]=[a,b,c]。排序((
handleCurrentMouseLocation = throttle(function (mouseLocation, points, polygon) {
    let pointIDsInPolygon = []
    polygon.push(mouseLocation)

    for (let point in points) {
        if (pointInPolygon(point, polygon) {
            pointIDsInPolygon.push(point.id)
        }
    }
    return pointIDsInPolygon
}, 100)