- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有一个对象数组。每个对象代表一个点,具有一个 ID 和一个具有 x y 坐标的数组。 ,例如:
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
}
用户用鼠标绘制多边形,其工作原理如下: http://bl.ocks.org/bycoffe/5575904
每次鼠标移动(获取新坐标),我们都要将当前鼠标位置添加到多边形中,然后我们要循环遍历所有点并调用 pointInPolygon
函数点在每次迭代。我已经限制了事件以提高性能:
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)
当点数不是那么高 (<200) 时,这很好用,但在我当前的项目中,我们有超过 4000 个点。遍历所有这些点并每 100 毫秒为每个点调用 pointInPolygon
函数使得整个过程非常缓慢。
我正在寻找一种更快的方法来完成此任务。例如:也许,不是在鼠标绘制多边形时每 100 毫秒触发一次此函数,我们可以查找一些离鼠标位置最近的点并将其存储在 closestPoints
数组中。然后,当鼠标 x/y 高于/低于某个值时,它只会循环遍历 closestPoints
中的点和多边形中已经存在的点。但我不知道这些 closestPoints
是什么,或者整个方法是否有意义。但我确实觉得解决方案是减少我们每次必须循环的点数。
需要说明的是,我项目中的 4000 多个点是固定的——它们不是动态生成的,但始终具有完全相同的坐标。事实上,这些点代表多边形的质心,代表 map 上城市的边界。因此,例如,可以提前为每个点计算 closestPoints
(在这种情况下,我们将针对这些点进行计算,而不是像上一段中那样计算鼠标位置)。
有计算几何专家可以帮助我解决这个问题吗?
最佳答案
如果我没理解错的话,从鼠标记录的新点将使多边形变大一个点。因此,如果在某个时刻多边形由 n 个点 (0,1,...,n-1) 和一个新点 p 定义em> 被记录,然后多边形变为 (0,1,...,n-1,p)。
所以这意味着从多边形中删除了一条边,而是添加了两条边。
例如,假设我们在多边形上有 9 个点,编号为 0 到 8,其中点 8 是添加到它的最后一个点:
灰线是闭合多边形的边。
现在鼠标移动到点 9,它被添加到多边形中:
从多边形中移除灰色边,并添加两条绿色边。现在遵守以下规则:
与更改前的位置相比,由灰色和两条绿色边形成的三 Angular 形中的点在多边形中换入/换出。所有其他点保留其之前的进/出状态。
所以,如果你想在内存中保留每个点的状态,那么你只需要检查每个点是否在上面提到的三 Angular 形内,如果是,你需要切换那个点的状态。
由于测试是否包含在三 Angular 形中比测试可能复杂的多边形花费的时间更少,因此这将导致更有效的算法。
可以进一步提高效率,如果取 Angular 点在(x0, y0),(x< sub>1, y0),(x1, y1),(x0>, y1)。然后您已经可以跳过具有超出范围的 x 或 y 坐标的点:
蓝色框外的任何点都不会改变状态:如果在添加最后一个点 9 之前它在多边形内部,那么现在它仍然是。仅对于方框内的点,您需要执行 pointInPolygon
测试,但仅针对三 Angular 形,而不是整个多边形。如果该测试返回 true
,则必须切换测试点的状态。
为了进一步加快该过程,您可以使用网格将平面划分为方形框,其中每个点属于一个框,但一个框通常会有很多点。为了确定哪些点在三 Angular 形中,您可以首先确定哪些框与三 Angular 形重叠。
为此,您不必测试每个框,但可以从三 Angular 形边缘上的坐标导出框。
然后只需要单独测试剩余框中的点。您可以调整盒子大小,看看它如何影响性能。
这是一个工作示例,实现了这些想法。有 10000 点,但我的 PC 上没有滞后:
canvas.width = document.body.clientWidth;
const min = [0, 0],
max = [canvas.width, canvas.height],
points = Array.from(Array(10000), i => {
let x = Math.floor(Math.random() * (max[0]-min[0]) + min[0]);
let y = Math.floor(Math.random() * (max[1]-min[1]) + min[1]);
return [x, y];
}),
polygon = [],
boxSize = Math.ceil((max[0] - min[0]) / 50),
boxes = (function (xBoxes, yBoxes) {
return Array.from(Array(yBoxes), _ =>
Array.from(Array(xBoxes), _ => []));
})(toBox(0, max[0])+1, toBox(1, max[1])+1),
insidePoints = new Set,
ctx = canvas.getContext('2d');
function drawPoint(p) {
ctx.fillRect(p[0], p[1], 1, 1);
}
function drawPolygon(pol) {
ctx.beginPath();
ctx.moveTo(pol[0][0], pol[0][1]);
for (const p of pol) {
ctx.lineTo(p[0], p[1]);
}
ctx.stroke();
}
function segmentMap(a, b, dim, coord) {
// Find the coordinate where ab is intersected by a coaxial line at
// the given coord.
// First some boundary conditions:
const dim2 = 1 - dim;
if (a[dim] === coord) {
if (b[dim] === coord) return [a[dim2], b[dim2]];
return [a[dim2]];
}
if (b[dim] === coord) return [b[dim2]];
// See if there is no intersection:
if ((coord > a[dim]) === (coord > b[dim])) return [];
// There is an intersection point:
const res = (coord - a[dim]) * (b[dim2] - a[dim2]) / (b[dim] - a[dim]) + a[dim2];
return [res];
}
function isLeft(a, b, c) {
// Return true if c lies at the left of ab:
return (b[0] - a[0])*(c[1] - a[1]) - (b[1] - a[1])*(c[0] - a[0]) > 0;
}
function inTriangle(a, b, c, p) {
// First do a bounding box check:
if (p[0] < Math.min(a[0], b[0], c[0]) ||
p[0] > Math.max(a[0], b[0], c[0]) ||
p[1] < Math.min(a[1], b[1], c[1]) ||
p[1] > Math.max(a[1], b[1], c[1])) return false;
// Then check that the point is on the same side of each of the
// three edges:
const x = isLeft(a, b, p),
y = isLeft(b, c, p),
z = isLeft(c, a, p);
return x ? y && z : !y && !z;
}
function toBox(dim, coord) {
return Math.floor((coord - min[dim]) / boxSize);
}
function toWorld(dim, box) {
return box * boxSize + min[dim];
}
function drawBox(boxX, boxY) {
let x = toWorld(0, boxX);
let y = toWorld(1, boxY);
drawPolygon([[x, y], [x + boxSize, y], [x + boxSize, y + boxSize], [x, y + boxSize], [x, y]]);
}
function triangleTest(a, b, c, points, insidePoints) {
const markedBoxes = new Set(), // collection of boxes that overlap with triangle
box = [];
for (let dim = 0; dim < 2; dim++) {
const dim2 = 1-dim,
// Order triangle points by coordinate
[d, e, f] = [a, b, c].sort( (p, q) => p[dim] - q[dim] ),
lastBox = toBox(dim, f[dim]);
for (box[dim] = toBox(dim, d[dim]); box[dim] <= lastBox; box[dim]++) {
// Calculate intersections of the triangle edges with the row/column of boxes
const coord = toWorld(dim, box[dim]),
intersections =
[...new Set([...segmentMap(a, b, dim, coord),
...segmentMap(b, c, dim, coord),
...segmentMap(a, c, dim, coord)])];
if (!intersections.length) continue;
intersections.sort( (a,b) => a - b );
const lastBox2 = toBox(dim2, intersections.slice(-1)[0]);
// Mark all boxes between the two intersection points
for (box[dim2] = toBox(dim2, intersections[0]); box[dim2] <= lastBox2; box[dim2]++) {
markedBoxes.add(boxes[box[1]][box[0]]);
if (box[dim]) {
markedBoxes.add(boxes[box[1]-dim][box[0]-(dim2)]);
}
}
}
}
// Perform the triangle test for each individual point in the marked boxes
for (const box of markedBoxes) {
for (const p of box) {
if (inTriangle(a, b, c, p)) {
// Toggle in/out state of this point
if (insidePoints.delete(p)) {
ctx.fillStyle = '#000000';
} else {
ctx.fillStyle = '#e0e0e0';
insidePoints.add(p);
}
drawPoint(p);
}
}
}
}
// Draw points
points.forEach(drawPoint);
// Distribute points into boxes
for (const p of points) {
let hor = Math.floor((p[0] - min[0]) / boxSize);
let ver = Math.floor((p[1] - min[1]) / boxSize);
boxes[ver][hor].push(p);
}
canvas.addEventListener('mousemove', (e) => {
if (e.buttons !== 1) return;
polygon.push([Math.max(e.offsetX,0), Math.max(e.offsetY,0)]);
ctx.strokeStyle = '#000000';
drawPolygon(polygon);
const len = polygon.length;
if (len > 2) {
triangleTest(polygon[0], polygon[len-2+len%2], polygon[len-1-len%2], points, insidePoints);
}
});
canvas.addEventListener('mousedown', (e) => {
// Start a new polygon
polygon.length = 0;
});
Drag mouse to draw a shape:
<canvas id="canvas"></canvas>
关于Javascript:多边形中的点性能改进,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46634887/
我已经实现了 Bentley-Ottmann-algorithm检测多边形-多边形交叉点。这通常非常有效:由于多边形不自相交,因此两个多边形的线段并集中的任何线段相交表明两个多边形相交。 但是如果我看
我在 Silverlight 中有一个多边形(棋盘游戏的十六进制),例如; public class GridHex : IGridShape { ..... public IList
我创建了一个简单的 DirectX 应用程序来渲染一个顶点场。顶点呈现如下(如果从顶部查看): |\|\|\|\| |\|\|\|\| 每个三角形都是这样渲染的: 1 |\ 2 3 这应该意味着多边形
我的代码的某些部分here : var stage = new Kinetic.Stage({ container: "canvas", width: 300,
我正在尝试从 map 数据构造导航网格物体。步骤之一涉及将二进制图像(其中0表示占用空间,1表示自由空间)转换成平面直线图。 我正在尝试找到一种可靠的方法。我目前的想法是使用Canny边缘检测器,然后
我的任务是编写 MATLAB 代码来生成一个由 4 部分组成的 Logo ,如屏幕截图所示。左上角应为黑色,右下角应为白色。另一个程序应随机选择两种颜色。 我采取了以下方法: clear all cl
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
如何向 google.maps.Rectangle 和 google.maps.Polygon 添加标题? title 属性在 RectangleOptions 中不可用.我试过了,但没用(对于 go
我有一个表,用于将表上的段存储为多边形。然后我想获取所有被另一个多边形接触的线段,例如正方形或圆形。图片上:http://img.acianetmedia.com/GJ3 我将灰色小框表示为段和 bi
我正在我的网站上使用 CSS 来制作形状。它在 chrome 中运行良好,但在 mozilla、internet explorer 中打开时,它无法运行。 有人知道怎么解决吗? 这是 fiddle h
我使用 DrawingManager 在 Google map 上绘制圆形和多边形。我尝试使用下面的代码删除圆形/多边形。 selectedShape.setMap(null); 这里selected
我看到了很多如何检测碰撞的教程,但没有看到如何解决它。我正在制作一个自上而下的游戏,玩家具有圆形碰撞形状,而墙壁是各种多边形。 我正在使用 slick2d。我应该做的是,如果玩家与角落碰撞,我会按法线
我对 tkinter 比较陌生,我正在制作一个只使用正方形的游戏。我正在抄写的书只显示三角形。这是代码: # The tkinter launcher (Already complete) from
我在 OpenCV/Python 中工作,我遇到了这个问题。我已经使用 cv2.minAreaRect() 来获取围绕一组点的边界框。是否有任何其他函数/通用算法可以给我内接多边形(点集)的最大矩形?
如果给定一组线段 S ,我们能否设计一种算法来测试集合 S 中的线段是否可以形成多边形,我对它们是否相交多边形不感兴趣,我只想知道我可以测试什么标准, 任何建议 最佳答案 构建一个图形数据结构,其中节
如何从多个地理位置(经度、纬度值)创建多边形地理围栏。此外,如何在 Android 上跟踪用户进入此地理围栏区域或从该区域退出。 最佳答案 地理围栏只是一组形成多边形的纬度/经度点。获得纬度/经度点列
我想要一个 complex image like this在我的申请中。我想让用户点击复杂的多边形(在这种情况下是有边界的国家/地区)并突出显示他们点击的多边形。我有我需要用于该状态的图像。 我怎样才
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,
我想在 python tkinter 中移动对象,特别是多边形。问题出在 is_click 函数中。我似乎无法弄清楚如何确定我是否单击了该对象。代码还没有 100% 完成,移动仍然需要完成,但我现在需
我有一个大多边形,我想找到与该多边形相交的要素,但由于多边形太大,我遇到超时异常。 我试图研究 JTS 方法,但不知道如何使用它。 final List coordinates = List.of(n
我是一名优秀的程序员,十分优秀!