有效地找到圆形扇区内的点 [英] Efficiently find points inside a circle sector

查看:26
本文介绍了有效地找到圆形扇区内的点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一组随机分布的二维点.我需要对这些点的一小部分执行时间密集型操作,但我需要首先弄清楚我需要在哪些点上执行这种时间密集型操作.要确定我需要哪些点,它们必须通过一系列几何标准.

最基本的标准是它们在特定点的一定距离内.第二个最基本的标准是它们是否包含在从该特定点向外延伸的圆形扇区(二维圆锥)内.(定期调用此操作,每次使用不同的特定点但相同的 2d 点集.)

我最初的想法是创建一个包含 2d 点的网格,然后沿着圆锥体进行迭代,抓取与之相交的网格正方形.根据网格的大小,它将过滤掉绝大多数不需要的二维点.不幸的是,我正在运行的嵌入式系统内存严重受限,因此大型(按照我们的标准,而不是其他任何人)二维数组将过于占用内存.

我一直在尝试使用 KD 树来加快计算速度,但我无法找到与圆形扇区和 kd 树相关的算法.

是否有一种有效的算法可以找到圆形扇区内的二维点?

请注意,我们的特定系统在浮点数学和三角学方面都很慢,因此涉及较少的解决方案是需要大量解决方案的优秀解决方案.

解决方案

只需要整数运算和加减乘的基本运算就可以判断一个点是否在扇区内.

要使点位于.法线向量与原始向量成 90 度角.这是.

示例源代码

<!DOCTYPE html><html><头><script src="http://code.jquery.com/jquery-1.8.2.min.js"></script><风格>.帆布 {位置:绝对;背景:#f4f4f4;边框:8px 实心#f4f4f4;宽度:400px;高度:400px;}.dot {位置:绝对;字体:16px 宋体;}.out { 颜色:#ddd;}.in { 颜色:#00dd44;}</风格><脚本>函数isInsideSector(点,中心,扇区开始,扇区结束,半径平方){变量 relPoint = {x:point.x - center.x,y:point.y - center.y};return !areClockwise(sectorStart, relPoint) &&areClockwise(sectorEnd, relPoint) &&isWithinRadius(relPoint, radiusSquared);}功能是顺时针(v1,v2){返回 -v1.x*v2.y + v1.y*v2.x >0;}函数 isWithinRadius(v, radiusSquared) {返回 v.x*v.x + v.y*v.y <= radiusSquared;}$(函数(){var $canvas = $("#canvas");var canvasSize = 400;变量计数 = 4000;//定义扇区var center = { x: canvasSize/2, y: canvasSize/2 };var 扇区开始 = { x: 4, y: 1 };var 扇区结束 = { x: 1, y: 4 };var radiusSquared = canvasSize * canvasSize/4;//创建、绘制和测试一些随机点for (var i = 0; i < count; ++i) {//生成一个随机点变量点 = {x: Math.random() * canvasSize,y: Math.random() * canvasSize};//测试点是否在扇区内var isInside = isInsideSector(点,中心,sectorStart,sectorEnd,radiusSquared);//绘制点var $point = $("<div class='dot'></div>").css({左:point.x - 3,顶部:canvasSize - point.y - 8 }).html("&#8226;").addClass(isInside ? "in" : "out").appendTo($canvas);}});</脚本></头><身体><div id="canvas" class="canvas"></div></身体></html>

注意事项、注意事项和限制

  1. 您必须根据向量指定扇区的边界.例如,上面的屏幕截图显示了在 (4,1) 和 (1,4) 的向量之间拉伸的扇区.

  2. 如果您的部门是用其他术语指定的,例如角度,您必须先将其转换为矢量,例如使用 tan() 函数.幸运的是,您只需执行一次.

  3. 这里的逻辑适用于内角小于 180 度的扇区.如果您的扇区可以跨越更大的角度,则必须对其进行修改.

  4. 此外,代码假定您知道扇区的哪个边界向量是开始",哪个是结束".如果没有,您可以在它们上运行 areClockwise() 以找出答案.

  5. 请注意,虽然所有这些都可以通过整数运算来完成,但半径和顺时针测试都使用更大范围的数字,因为 x 和 y 平方并将它们相乘.确保使用足够位的整数来保存结果.

I have a set of 2d points distributed randomly. I need to perform a time intensive operation on a small subset of these points but I need to first figure out what points I need to perform this time intensive operation on. To determine what points I need they must pass a series of geometric criteria.

The most basic criteria is are they within a certain distance of a specific point. The second most basic criteria is whether they are contained within a circle sector (a 2-D cone) extending out from that specific point. (Edit: This operation is called regularly with a different specific point each time but the same set of 2d points.)

My initial thought was to create a grid containing the 2d points, then iterate along the cone grabbing grid squares that it intersects. Depending on the size of the grid it would filter out the vast majority of unneeded 2d points. Unfortunately the embedded system I'm running on is severely memory constrained so a large (by our standards not anyone elses) 2d array would be too memory intensive.

I have been trying to investigate using KD trees to speed up the calculation but I haven't been able to find an algorithm relating circle sectors and kd-trees.

Is there an efficient algorithm for finding what 2d points lie within a circle sector?

Just a note our particular system is slow at both floating point math and trigonometry so a solution that involves less of those is superior one that requires a lot of it.

解决方案

It's possible to check if a point is inside a sector with only integer arithmetic and the basic operations of addition, subtraction and multiplication.

For a point to be inside a circular sector, it has to meet the following tests:

  1. It has to be positioned counter-clockwise from the start "arm" of the sector.
  2. It has to be positioned clockwise from the end arm of the sector.
  3. It has to be closer to the center of the circle than the sector's radius.

Clockwise tests

To test if a vector v2 is clockwise to a another vector v1, do the following:

  1. Find the counter-clockwise normal vector of v1. The normal vector is at a 90 degrees angle to the original vector. This is straightforward to do: if v1=(x1,y1), then the counter-clockwise normal is n1=(-y1,x1).

  2. Find the size of the projection of v2 on the normal. This can be done by calculating the dot product of v2 and the normal.

    projection = v2.x*n1.x + v2.y*n1.y

  3. If the projection is a positive number, then the v2 is positioned counter-clockwise to v1. Otherwise, v2 is clockwise to v1.

Here's a counter-clockwise example:

And a clockwise example:

The steps can be combined:

function areClockwise(v1, v2) {
  return -v1.x*v2.y + v1.y*v2.x > 0;
}

Radius test

The radius test is straightforward. Just check if the distance of the point from the center of the circle is less than the desired radius. To avoid computing square roots, we can compare the square of the distance with the square of the radius instead.

function isWithinRadius(v, radiusSquared) {
  return v.x*v.x + v.y*v.y <= radiusSquared;
}

Putting it together

The complete sector test looks something like:

function isInsideSector(point, center, sectorStart, sectorEnd, radiusSquared) {
  var relPoint = {
    x: point.x - center.x,
    y: point.y - center.y
  };

  return !areClockwise(sectorStart, relPoint) &&
         areClockwise(sectorEnd, relPoint) &&
         isWithinRadius(relPoint, radiusSquared);
}

The following sample page demonstrates this over several thousand points. You can experiment with the code at: http://jsbin.com/oriyes/8/edit.

Sample source code

<!DOCTYPE html>
<html>
  <head>
    <script src="http://code.jquery.com/jquery-1.8.2.min.js"></script>
    <style>
      .canvas {
        position: absolute;
        background: #f4f4f4;
        border: 8px solid #f4f4f4;
        width: 400px;
        height: 400px;
      }

      .dot {
        position: absolute;
        font: 16px Arial;
      }
      .out { color: #ddd; }
      .in { color: #00dd44; }
    </style>
    <script>
      function isInsideSector(point, center, sectorStart, sectorEnd, radiusSquared) {
        var relPoint = {
          x: point.x - center.x,
          y: point.y - center.y
        };

        return !areClockwise(sectorStart, relPoint) &&
               areClockwise(sectorEnd, relPoint) &&
               isWithinRadius(relPoint, radiusSquared);
      }

      function areClockwise(v1, v2) {
        return -v1.x*v2.y + v1.y*v2.x > 0;
      }

      function isWithinRadius(v, radiusSquared) {
        return v.x*v.x + v.y*v.y <= radiusSquared;
      }

      $(function() {
        var $canvas = $("#canvas");
        var canvasSize = 400;
        var count = 4000;

        // define the sector
        var center = { x: canvasSize / 2, y: canvasSize / 2 };
        var sectorStart = { x: 4, y: 1 };
        var sectorEnd = { x: 1, y: 4 };
        var radiusSquared = canvasSize * canvasSize / 4;

        // create, draw and test a number of random points
        for (var i = 0; i < count; ++i) {

          // generate a random point
          var point = {
            x: Math.random() * canvasSize,
            y: Math.random() * canvasSize
          };

          // test if the point is inside the sector
          var isInside = isInsideSector(point, center, sectorStart, sectorEnd, radiusSquared);

          // draw the point
          var $point = $("<div class='dot'></div>")
              .css({
                left: point.x - 3,
                top:  canvasSize - point.y - 8 })
              .html("&#8226;")
              .addClass(isInside ? "in" : "out")
              .appendTo($canvas);
        }
      });
    </script>
  </head>
  <body>
    <div id="canvas" class="canvas"></div>
  </body>
</html>

Notes, caveats and limitations

  1. You have to specify the boundaries of the sector in terms of vectors. The screenshot above, for example, shows a sector stretching between the vectors of (4,1) and (1,4).

  2. If your sector is specified in other terms, e.g. angles, you will have to convert that to vectors first, e.g. using the tan() function. Fortunately, you only have to do this once.

  3. The logic here works for sectors with an inner angle of less than 180 degrees. If your sectors can span a larger angle, you'll have to modify it.

  4. Additionally, the code assumes that you know which of the bounding vectors of the sector is the "start" and which is the "end". If you don't, you can run the areClockwise() on them to find out.

  5. Note that while all this can be done with integer arithmetic, both the radius and clockwise tests use a larger range of numbers, due to squaring x's and y's and multiplying them together. Make sure to use integers of sufficient bits to hold the results.

这篇关于有效地找到圆形扇区内的点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆