用于测试点是否在圆内的方程式


309

如果您有一个具有中心(center_x, center_y)和半径的圆radius,如何测试给定的坐标点(x, y)是否在圆内?


20
这个问题真的与语言无关,我在Java中使用了相同的公式,因此需要重新标记。
Gautam 2012年

看来您仅假设正坐标。以下解决方案不适用于带符号的坐标。
cjbarth 2015年

下面的大多数解决方案可以使用正负坐标。只需更正此花絮,供以后观看此问题的读者使用。
威廉·莫里森,

我投票结束这个题为离题,因为这是关于中学数学而不是编程。
n。代词

Answers:


481

在一般情况下,xy必须满足(x - center_x)^2 + (y - center_y)^2 < radius^2

请注意,满足上述方程的点<换成了==被认为是点的圆圈,并满足上述方程的点<换成了>被认为是外面的圆圈。


6
这可能会帮助一些不太懂数学的人了解平方根运算,该运算用于测量距离相对于半径的距离。我意识到这不是最佳选择,但是由于您的答案的格式更像是公式而不是代码,也许更有意义?只是一个建议。
威廉·莫里森

30
这是一个简单的句子和立即可用的方程式中提供的最可理解的解释。做得好。
thgc

非常希望我能更快地找到此资源。x值从哪里来?
Devin Tripp

2
@DevinTripp'x'是被测点的x坐标。
克里斯

5
这可能是显而易见的,但应该指出,<=它将在圆内或圆上找到点。
泰勒(Tyler)

131

数学上,毕达哥拉斯可能是许多人已经提到的简单方法。

(x-center_x)^2 + (y - center_y)^2 < radius^2

计算上,有更快的方法。限定:

dx = abs(x-center_x)
dy = abs(y-center_y)
R = radius

如果某个点更可能该圆之外,则可以想象一个围绕它绘制的正方形,使得其边与该圆相切:

if dx>R then 
    return false.
if dy>R then 
    return false.

现在想象一下在此圆内绘制的正方形菱形,使其顶点触及该圆:

if dx + dy <= R then 
    return true.

现在,我们已经覆盖了我们的大部分空间,并且这个圆的一小部分仅保留在我们要测试的正方形和钻石之间。在这里,我们如上所述恢复到毕达哥拉斯。

if dx^2 + dy^2 <= R^2 then 
    return true
else 
    return false.

如果某个点更可能该圆内,则颠倒前3步的顺序:

if dx + dy <= R then 
    return true.
if dx > R then 
    return false.
if dy > R 
    then return false.
if dx^2 + dy^2 <= R^2 then 
    return true
else
    return false.

另一种方法是想象在该圆内的正方形而不是菱形,但这需要进行更多的测试和计算,而没有计算优势(内部正方形和菱形具有相同的面积):

k = R/sqrt(2)
if dx <= k and dy <= k then 
    return true.

更新:

对于那些对性能感兴趣的人,我在c中实现了此方法,并使用-O3进行了编译。

我获得了执行时间 time ./a.out

我实现了此方法,常规方法和虚拟方法来确定计时开销。

Normal: 21.3s This: 19.1s Overhead: 16.5s

因此,似乎此方法在此实现中效率更高。

// compile gcc -O3 <filename>.c
// run: time ./a.out

#include <stdio.h>
#include <stdlib.h>

#define TRUE  (0==0)
#define FALSE (0==1)

#define ABS(x) (((x)<0)?(0-(x)):(x))

int xo, yo, R;

int inline inCircle( int x, int y ){  // 19.1, 19.1, 19.1
  int dx = ABS(x-xo);
  if (    dx >  R ) return FALSE;
  int dy = ABS(y-yo);
  if (    dy >  R ) return FALSE;
  if ( dx+dy <= R ) return TRUE;
  return ( dx*dx + dy*dy <= R*R );
}

int inline inCircleN( int x, int y ){  // 21.3, 21.1, 21.5
  int dx = ABS(x-xo);
  int dy = ABS(y-yo);
  return ( dx*dx + dy*dy <= R*R );
}

int inline dummy( int x, int y ){  // 16.6, 16.5, 16.4
  int dx = ABS(x-xo);
  int dy = ABS(y-yo);
  return FALSE;
}

#define N 1000000000

int main(){
  int x, y;
  xo = rand()%1000; yo = rand()%1000; R = 1;
  int n = 0;
  int c;
  for (c=0; c<N; c++){
    x = rand()%1000; y = rand()%1000;
//    if ( inCircle(x,y)  ){
    if ( inCircleN(x,y) ){
//    if ( dummy(x,y) ){
      n++;
    }
  }
  printf( "%d of %d inside circle\n", n, N);
}

5
这个答案很好。我从未意识到您建议的某些优化。做得好。
威廉·莫里森

2
我很想知道您是否介绍了这些优化?我的直觉是,多个条件比一个数学和一个条件要慢,但我可能是错的。
yoyo 2014年

3
@yoyo,我没有执行任何配置文件-此问题与任何编程语言的方法有关。如果有人认为这可以提高其应用程序的性能,则应按照您的建议,证明它在正常情况下更快。
philcolbourn 2014年

2
在功能上,inCircleN您正在使用不必要的ABS。大概没有ABS之间的差异inCircle,并inCircleN会更小。
tzaloga

1
删除ABS确实可以提高inCircleN性能,但还不够。但是,由于R = 1,我的方法偏向于更可能在圆外的点。使用随机半径[0..499],大约25%的点在圆内,而inCircleN更快。
philcolbourn

74

您可以使用毕达哥拉斯(Pythagoras)测量点到中心的距离,并查看其是否小于半径:

def in_circle(center_x, center_y, radius, x, y):
    dist = math.sqrt((center_x - x) ** 2 + (center_y - y) ** 2)
    return dist <= radius

编辑(给保罗的提示)

在实践中,平方通常比起平方根便宜得多,并且由于我们仅对订购感兴趣,因此我们当然可以放弃以平方根:

def in_circle(center_x, center_y, radius, x, y):
    square_dist = (center_x - x) ** 2 + (center_y - y) ** 2
    return square_dist <= radius ** 2

此外,Jason指出,<=<根据实际情况将其替换为,这取决于实际用法即使我相信严格的数学意义上的说法也不对我站得住了。


1
将dist <= radius替换为dist <radius以测试圆内的点。
杰森2009年

16
sqrt很贵。尽可能避免-将x ^ 2 + y ^ y与r ^ 2比较。
Paul Tomblin,2009年

杰森:我们的定义可能不同意,但对我来说,一个点是圆的周长是最着重也是圆和我敢肯定,我的是与正式的,数学定义的协议。
康拉德·鲁道夫

3
圆内部的正式数学定义是我在帖子中给出的。摘自Wikipedia:通常,事物的内部是指其内部的空间或部分,但不包括外部的任何墙壁或边界。en.wikipedia.org/wiki/Interior_(topology)–
杰森(

1
在pascal,delphi和FPC中,功率和sqrt都很昂贵,并且没有功率运算符EG:**^。当您只需要x ^ 2或x ^ 3时,最快的方法是“手动”执行:x*x
JHolta

37
boolean isInRectangle(double centerX, double centerY, double radius, 
    double x, double y)
{
        return x >= centerX - radius && x <= centerX + radius && 
            y >= centerY - radius && y <= centerY + radius;
}    

//test if coordinate (x, y) is within a radius from coordinate (center_x, center_y)
public boolean isPointInCircle(double centerX, double centerY, 
    double radius, double x, double y)
{
    if(isInRectangle(centerX, centerY, radius, x, y))
    {
        double dx = centerX - x;
        double dy = centerY - y;
        dx *= dx;
        dy *= dy;
        double distanceSquared = dx + dy;
        double radiusSquared = radius * radius;
        return distanceSquared <= radiusSquared;
    }
    return false;
}

这是更有效和可读的。它避免了昂贵的平方根运算。我还添加了一个检查以确定该点是否在圆的边界矩形内。

矩形检查是不必要的,除非有很多点或很多圆。如果大多数点在圆内,则边界矩形检查实际上会使速度变慢!

与往常一样,请务必考虑您的用例。


12

计算距离

D = Math.Sqrt(Math.Pow(center_x - x, 2) + Math.Pow(center_y - y, 2))
return D <= radius

在C#中...转换为在python中使用...


11
通过比较D平方和半径平方,可以避免两次昂贵的Sqrt调用。
Paul Tomblin,2009年

10

您应该检查从圆心到点的距离是否小于半径,即

if (x-center_x)**2 + (y-center_y)**2 <= radius**2:
    # inside circle

5

如上所述-使用欧几里得距离。

from math import hypot

def in_radius(c_x, c_y, r, x, y):
    return math.hypot(c_x-x, c_y-y) <= r

4

找到圆心和给定点之间的距离。如果它们之间的距离小于半径,则该点位于圆内。如果它们之间的距离等于圆的半径,则该点位于圆的圆周上。如果距离大于半径,则该点在圆之外。

int d = r^2 - (center_x-x)^2 + (center_y-y)^2;

if(d>0)
  print("inside");
else if(d==0)
  print("on the circumference");
else
  print("outside");

4

下面的方程式是一个测试点的表达式,其中xPyP是该点的坐标,xCyC是该圆的中心,R是该给定圆的半径。

在此处输入图片说明

如果上述表达式为真,则该点在圆内。

以下是C#中的示例实现:

    public static bool IsWithinCircle(PointF pC, Point pP, Single fRadius){
        return Distance(pC, pP) <= fRadius;
    }

    public static Single Distance(PointF p1, PointF p2){
        Single dX = p1.X - p2.X;
        Single dY = p1.Y - p2.Y;
        Single multi = dX * dX + dY * dY;
        Single dist = (Single)Math.Round((Single)Math.Sqrt(multi), 3);

        return (Single)dist;
    }

2

这与Jason Punyon提到的解决方案相同,但其中包含一个伪代码示例和更多详细信息。写完这篇文章后,我看到了他的回答,但我不想删除我的。

我认为最容易理解的方法是首先计算圆心与点之间的距离。我将使用以下公式:

d = sqrt((circle_x - x)^2 + (circle_y - y)^2)

然后,只需将该公式的结果(距离(d))与进行比较radius。如果距离(d)小于或等于半径(r),则该点在圆内(如果dr相等,则在圆的边缘)。

这是一个伪代码示例,可以轻松将其转换为任何编程语言:

function is_in_circle(circle_x, circle_y, r, x, y)
{
    d = sqrt((circle_x - x)^2 + (circle_y - y)^2);
    return d <= r;
}

其中circle_xcircle_y是圆的中心坐标,是圆r的半径,x并且y是点的坐标。


2

我在C#中的答案是完整的剪切和粘贴(未优化)解决方案:

public static bool PointIsWithinCircle(double circleRadius, double circleCenterPointX, double circleCenterPointY, double pointToCheckX, double pointToCheckY)
{
    return (Math.Pow(pointToCheckX - circleCenterPointX, 2) + Math.Pow(pointToCheckY - circleCenterPointY, 2)) < (Math.Pow(circleRadius, 2));
}

用法:

if (!PointIsWithinCircle(3, 3, 3, .5, .5)) { }

1

如前所述,要显示该点是否在圆中,可以使用以下命令

if ((x-center_x)^2 + (y - center_y)^2 < radius^2) {
    in.circle <- "True"
} else {
    in.circle <- "False"
}

为了用图形表示它,我们可以使用:

plot(x, y, asp = 1, xlim = c(-1, 1), ylim = c(-1, 1), col = ifelse((x-center_x)^2 + (y - center_y)^2 < radius^2,'green','red'))
draw.circle(0, 0, 1, nv = 1000, border = NULL, col = NA, lty = 1, lwd = 1)

0

我将以下代码用于像我这样的初学者:)。

公开课Incirkel {

public static void main(String[] args) {
    int x; 
    int y; 
    int middelx; 
    int middely; 
    int straal; {

// Adjust the coordinates of x and y 
x = -1;
y = -2;

// Adjust the coordinates of the circle
middelx = 9; 
middely = 9;
straal =  10;

{
    //When x,y is within the circle the message below will be printed
    if ((((middelx - x) * (middelx - x)) 
                    + ((middely - y) * (middely - y))) 
                    < (straal * straal)) {
                        System.out.println("coordinaten x,y vallen binnen cirkel");
    //When x,y is NOT within the circle the error message below will be printed
    } else {
        System.err.println("x,y coordinaten vallen helaas buiten de cirkel");
    } 
}



    }
}}

0

如果要检查3D点是否在单位球体中,则进入3D世界,您最终会做类似的事情。在2D模式下工作所需要做的就是使用2D向量操作。

    public static bool Intersects(Vector3 point, Vector3 center, float radius)
    {
        Vector3 displacementToCenter = point - center;

        float radiusSqr = radius * radius;

        bool intersects = displacementToCenter.magnitude < radiusSqr;

        return intersects;
    }

0

我知道距离最佳投票答案还有数年的时间,但我设法将计算时间减少了4。

您只需要从圆的1/4计算像素,然后乘以4。

这是我已经达到的解决方案:

#include <stdio.h>
#include <stdlib.h>
#include <time.h> 

int x, y, r;
int mx, c, t;
int dx, dy;
int p;

int main() {
    for (r = 1; r < 128; r++){

        clock_t t; 
        t = clock();

        p = calculatePixels(r);

        t = clock() - t; 
        double time_taken = ((double)t)/CLOCKS_PER_SEC; // in seconds 

        printf( "%d of pixels inside circle with radius %d, took %f seconds to execute \n", p, r, time_taken);
    }
}

int calculatePixels(int r){
    mx = 2 * r;
    c = (mx+1)*(mx+1);
    t = r * r;
    int a = 0;
    for (x = 0; x < r; x++){
      for (y = 0; y < r; y++){
          dx = x-r;
          dy = y-r;
          if ((dx*dx + dy*dy) > t)
              a++;
          else 
              y = r;
      }
    }
    return (c - (a * 4));
}


0

的PHP

if ((($x - $center_x) ** 2 + ($y - $center_y) ** 2) <=  $radius **2) {
    return true; // Inside
} else {
    return false; // Outside
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.