Bir noktanın daire içinde olup olmadığını test etmek için denklem


309

Merkez (center_x, center_y)ve yarıçaplı bir daireniz radiusvarsa, koordinatlarla belirli bir noktanın (x, y)dairenin içinde olup olmadığını nasıl test edersiniz ?


20
Bu soru gerçekten dil agnostik, ben java aynı formülü kullanıyorum, Yani yeniden etiketleme.
Gautam

Görünüşe göre sadece pozitif koordinatlar varsayıyorsunuz. Aşağıdaki çözümler imzalı koordinatlarla çalışmaz.
cjbarth

Çoğu çözüm aşağıda do pozitif ve negatif koordinatlarla işi. Sadece bu sorunun gelecekteki izleyicileri için bu tidbit düzeltildi.
William Morrison

Bu soruyu konu dışı olarak kapatmak için oy kullanıyorum çünkü bu programlama yerine ortaokul matematik ile ilgili.
n. 'zamirler' m.

Yanıtlar:


481

Genel olarak xve ytatmin etmek gerekir (x - center_x)^2 + (y - center_y)^2 < radius^2.

Lütfen not bununla yukarıdaki denklemi tatmin noktaları <yerini ==noktaları olarak kabul edilir üzerindeki daire ve yukarıda denklemi tatmin noktaları <yerine >kabul edilir dışında çember.


6
Daha az matematik düşünen bazı kişilerin yarıçapa kıyasla mesafeyi ölçmek için kullanılan karekök işlemini görmelerine yardımcı olabilir. Bunun optimal olmadığını anlıyorum, ancak cevabınız koddan çok bir denklem gibi biçimlendirildiğinden, belki daha mantıklı mı? Sadece bir öneri.
William Morrison

30
Bu sadece basit bir cümle ve hemen kullanılabilir bir denklemle sağlanan en anlaşılır açıklamadır. Aferin.
thgc

Bu, bu kaynağı daha çabuk bulacağım büyük bir dilek. X değeri nereden geliyor?
Devin Tripp

2
@DevinTripp 'x', test edilen noktanın x koordinatıdır.
Chris

5
Bu açık olabilir, ancak <=dairenin içinde veya kenarında noktalar bulacağı belirtilmelidir .
Tyler

131

Matematiksel olarak, Pisagor muhtemelen daha önce de belirttiğimiz gibi basit bir yöntemdir.

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

Hesaplamalı olarak, daha hızlı yollar var. Tanımlamak:

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

Bir noktanın bu dairenin dışında olma olasılığı daha yüksekse, etrafının çizildiği bir kare hayal edin ki kenarları bu daireye teğet olacak:

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

Şimdi, bu çemberin içine çizilen kare bir elmas hayal edin, böylece köşeleri bu daireye değecektir:

if dx + dy <= R then 
    return true.

Şimdi alanımızın çoğunu kapladık ve bu dairenin sadece küçük bir alanı test edilecek kare ve elmas arasında kalıyor. Burada yukarıdaki gibi Pisagor'a dönüyoruz.

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

Bir noktanın bu dairenin içinde olma olasılığı daha yüksekse , ilk 3 adımın tersini uygulayın:

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.

Alternatif yöntemler, bu dairenin içinde bir elmas yerine bir kare hayal eder, ancak bu, hesaplama avantajı olmayan biraz daha fazla test ve hesaplama gerektirir (iç kare ve elmaslar aynı alanlara sahiptir):

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

Güncelleme:

Performansla ilgilenenler için bu yöntemi c'de uyguladım ve -O3 ile derledim.

Tarafından icra süreleri elde ettim time ./a.out

Zamanlama yükünü belirlemek için bu yöntemi, normal bir yöntemi ve bir kukla yöntemi uyguladım.

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

Yani, bu yöntem bu uygulamada daha verimli görünüyor.

// 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
Bu cevap mükemmel. Önerdiğiniz bazı optimizasyonları hiç fark etmemiştim. Aferin.
William Morrison

2
Bu optimizasyonları profilleme yapıp yapmadığınızı merak ediyorum? Bağırsak hissim, birden fazla koşulun bazı matematik ve bir koşuldan daha yavaş olacağı, ancak yanlış olabilirim.
yoyo

3
@yoyo, profilleme yapmadım - bu soru herhangi bir programlama dili için bir yöntem hakkında. Birisi bunun uygulamalarındaki performansı artırabileceğini düşünüyorsa, önerdiğiniz gibi normal senaryolarda daha hızlı olduğunu göstermelidir.
philcolbourn

2
Fonksiyonda inCircleNgereksiz ABS kullanıyorsunuz. Muhtemelen ABS farkı olmadan inCircleve inCircleNdaha küçük olacaktır.
tzaloga

1
ABS'yi kaldırmak inCircleN performansını artırır ancak yeterli değildir. Bununla birlikte, yöntemim R = 1'den beri daire dışında daha muhtemel noktalara doğru eğilimli. Rasgele yarıçap ile [0..499], yaklaşık% 25 puan dairenin içindeydi ve inCircleN daha hızlı.
philcolbourn

74

Noktanız ile merkez arasındaki mesafeyi ölçmek ve yarıçaptan daha düşük olup olmadığını görmek için Pisagor'u kullanabilirsiniz:

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

EDIT (Paul'e şapka ucu)

Pratikte, kareleme genellikle kare kökünü almaktan çok daha ucuzdur ve sadece bir siparişle ilgilendiğimizden, elbette kare kökünü almayı bırakabiliriz:

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

Ayrıca, Jason <=bunun değiştirilmesi gerektiğini belirtti <ve kullanıma bağlı olarak bu aslında mantıklı olabilirkatı matematiksel anlamda doğru olmadığına inansam bile. Ben düzeltilmiş duruyorum.


1
Dairenin içinde olan noktayı test etmek için dist <= radius değerini dist <radius ile değiştirin.
Jason

16
sqrt pahalıdır. Mümkünse kaçının - x ^ 2 + y ^ y ile r ^ 2'yi karşılaştırın.
Paul Tomblin

Jason: Bizim tanımları katılmıyorum olabilir ama benim için bir nokta üzerinde çemberin çevresinin aynı zamanda en basa basa olduğu içinde daire ve ben oldukça emin mayın biçimsel matematiksel tanımı ile anlaşma olduğunu duyuyorum.
Konrad Rudolph

3
Bir dairenin iç kısmının biçimsel matematiksel tanımı, yazımda verdiğim şekildir. Wikipedia'dan: Genel olarak, bir şeyin iç kısmı, dış çevresindeki herhangi bir duvar veya sınır hariç, içindeki boşluk veya bölüm anlamına gelir. en.wikipedia.org/wiki/Interior_(topology)
jason

1
Pascal, delphi ve FPC'de, hem güç hem de sqrt pahalıdır ve güç operatörü EG: **veya yoktur ^. Sadece x ^ 2 veya x ^ 3 "elle" bunu yapmak için ise gerektiğinde en hızlı şekilde yapmak: 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;
}

Bu daha verimli ve okunabilir. Pahalı karekök işleminden kaçınır. Ayrıca noktanın dairenin sınırlayıcı dikdörtgeni içinde olup olmadığını belirlemek için bir kontrol ekledim.

Dikdörtgen kontrolü, çok sayıda nokta veya çok sayıda daire haricinde gereksizdir. Noktaların çoğu dairelerin içindeyse, sınırlayıcı dikdörtgen kontrolü aslında işleri yavaşlatır!

Her zaman olduğu gibi, kullanım durumunuzu dikkate aldığınızdan emin olun.


12

Mesafeyi Hesapla

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

Bu C # ... python kullanmak için dönüştürmek ...


11
D karesini yarıçap karesiyle karşılaştırarak iki pahalı Sqrt çağrısını önleyebilirsiniz.
Paul Tomblin

10

Dairenin merkezinden noktaya olan mesafenin yarıçaptan daha küçük olup olmadığını kontrol etmelisiniz, yani

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

5

Yukarıda belirtildiği gibi Öklid mesafesini kullanın.

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

Dairenin merkezi ile verilen noktalar arasındaki mesafeyi bulun. Aralarındaki mesafe yarıçaptan daha azsa, nokta dairenin içindedir. aralarındaki mesafe dairenin yarıçapına eşitse, nokta dairenin çevresindedir. mesafe yarıçaptan büyükse nokta dairenin dışındadır.

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

Aşağıdaki denklem, bir nokta belirli bir dairenin içinde bir test ise bir ifade xP ve yP noktasının koordinatları, xC ve yC dairenin merkezinin koordinatları ve R verilen dairenin yarıçapıdır.

resim açıklamasını buraya girin

Yukarıdaki ifade doğruysa, nokta dairenin içindedir.

Aşağıda C # ile ilgili örnek bir uygulama yer almaktadır:

    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

Bu, Jason Punyon tarafından belirtilenle aynı çözümdür , ancak sözde kod örneği ve daha fazla ayrıntı içerir. Bunu yazdıktan sonra cevabını gördüm, ama benimkini kaldırmak istemedim.

Bence en kolay anlaşılır yol önce dairenin merkezi ile nokta arasındaki mesafeyi hesaplamaktır. Bu formülü kullanırdım:

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

Ardından, bu formülün sonucunu, uzaklık ( d) ile radius. Distance ( d), yarıçaptan ( r) küçük veya ona eşitse , nokta dairenin içindedir ( dve reşitse dairenin kenarında ).

İşte herhangi bir programlama diline kolayca dönüştürülebilen bir sözde kod örneği:

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

Burada circle_xve circle_ydairenin merkez koordinatları olan, rdairenin yarıçapıdır, ve xve ynoktasının koordinatları olan.


2

Tam bir kes ve yapıştır (optimize edilmemiş) çözümü olarak C #'daki cevabım:

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));
}

Kullanımı:

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

1

Daha önce belirtildiği gibi, noktanın daire içinde olup olmadığını göstermek için aşağıdakileri kullanabiliriz

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

Grafiksel olarak temsil etmek için şunları kullanabiliriz:

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

Benim gibi yeni başlayanlar için aşağıdaki kodu kullandım :).

kamu sınıfı 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

Bir 3D noktasının Birim Kürede olup olmadığını kontrol etmek istiyorsanız, 3D dünyasına geçmek, sonunda benzer bir şey yaparsınız. 2D'de çalışmak için gereken tek şey 2D vektör işlemlerini kullanmaktır.

    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

En iyi oyu alan yanıttan birkaç yıl geçtiğini biliyorum, ancak hesaplama süresini 4 oranında azaltmayı başardım.

Dairenin yalnızca 1 / 4'ünden pikselleri hesaplamanız ve ardından 4 ile çarpmanız gerekir.

Bu benim ulaştığım çözüm:

#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
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.