web-dev-qa-db-fra.com

Trouver si un point se trouve ou non dans un rectangle

Je veux savoir si un point se trouve dans un rectangle ou non. Le rectangle peut être orienté de n'importe quelle manière et ne doit pas nécessairement être aligné sur un axe.

Une méthode à laquelle je pouvais penser était de faire pivoter le rectangle et les coordonnées du point pour aligner l'axe du rectangle, puis de simplement tester les coordonnées du point, qu'elles se trouvent ou non dans celui du rectangle.

La méthode ci-dessus nécessite une rotation et donc des opérations en virgule flottante. Existe-t-il un autre moyen efficace de le faire?

72
avd

Comment le rectangle est-il représenté? Trois points? Quatre points? Point, côtés et angle? Deux points et un côté? Autre chose? Sans le savoir, toute tentative de réponse à votre question n'aura qu'une valeur purement académique.

Dans tous les cas, pour tout convexe polygone (rectangle inclus), le test est très simple: vérifiez chaque bord du polygone, en supposant que chaque bord est orienté dans le sens inverse des aiguilles d'une montre, et vérifiez si le point se trouve à gauche du bord (dans le demi-plan gauche). Si toutes les arêtes réussissent le test, le point est à l'intérieur. Si au moins un échoue - le point est à l'extérieur.

Afin de tester si le point (xp, yp) se trouve à gauche de l’Edge (x1, y1) - (x2, y2), il vous suffit de calculer

D = (x2 - x1) * (yp - y1) - (xp - x1) * (y2 - y1)

Si D > 0, le point est à gauche. Si D < 0, le point est à droite. Si D = 0, le point est sur la ligne.


La version précédente de cette réponse décrivait une version apparemment différente du test du côté gauche (voir ci-dessous). Mais on peut facilement montrer qu’il calcule la même valeur.

... afin de vérifier si le point (xp, yp) se trouve à gauche de l’Edge (x1, y1) - (x2, y2), vous devez créer l'équation de la ligne pour la ligne contenant le bord. L'équation est la suivante

A * x + B * y + C = 0

A = -(y2 - y1)
B = x2 - x1
C = -(A * x1 + B * y1)

Il ne vous reste plus qu'à calculer

D = A * xp + B * yp + C

Si D > 0, le point est à gauche. Si D < 0, le point est à droite. Si D = 0, le point est sur la ligne.

Cependant, ce test fonctionne à nouveau pour tout polygone convexe, ce qui signifie qu'il est peut-être trop générique pour un rectangle. Un rectangle peut permettre un test plus simple ... Par exemple, dans un rectangle (ou dans tout autre parallélogramme), les valeurs de A et B ont la même ampleur mais des signes différents pour une opposition (c'est-à-dire parallèles), qui peuvent être exploités pour simplifier le test.

76
AnT

En supposant que le rectangle soit représenté par trois points A, B, C, avec AB et BC perpendiculaires, il vous suffit de vérifier les projections du point de requête M sur AB et BC:

0 <= dot(AB,AM) <= dot(AB,AB) &&
0 <= dot(BC,BM) <= dot(BC,BC)

AB est le vecteur AB, avec les coordonnées (Bx-Ax, By-Ay) et dot(U,V) est le produit scalaire des vecteurs U et V: Ux*Vx+Uy*Vy.

Mise à jour. Prenons un exemple pour illustrer ceci: A (5,0) B (0,2) C (1,5) et D (6,3). A partir des coordonnées du point, on obtient AB = (- 5,2), BC = (1,3), point (AB, AB) = 29, point (BC, BC) = 10.

Pour le point d'interrogation M (4,2), nous avons AM = (- 1,2), BM = (4,0), point (AB, AM) = 9, point (BC, BM) = 4. M est à l'intérieur du rectangle.

Pour le point d'interrogation P (6,1), nous avons AP = (1,1), BP = (6, -1), point (AB, AP) = - 3, point (BC, BP) = 3. P n'est pas à l'intérieur du rectangle, car sa projection sur le côté AB n'est pas à l'intérieur du segment AB.

39
Eric Bainville

J'ai emprunté à la réponse d'Eric Bainville:

0 <= dot(AB,AM) <= dot(AB,AB) && 0 <= dot(BC,BM) <= dot(BC,BC)

Ce qui en javascript ressemble à ceci:

function pointInRectangle(m, r) {
    var AB = vector(r.A, r.B);
    var AM = vector(r.A, m);
    var BC = vector(r.B, r.C);
    var BM = vector(r.B, m);
    var dotABAM = dot(AB, AM);
    var dotABAB = dot(AB, AB);
    var dotBCBM = dot(BC, BM);
    var dotBCBC = dot(BC, BC);
    return 0 <= dotABAM && dotABAM <= dotABAB && 0 <= dotBCBM && dotBCBM <= dotBCBC;
}

function vector(p1, p2) {
    return {
            x: (p2.x - p1.x),
            y: (p2.y - p1.y)
    };
}

function dot(u, v) {
    return u.x * v.x + u.y * v.y; 
}

par exemple:

var r = {
    A: {x: 50, y: 0},
    B: {x: 0, y: 20},
    C: {x: 10, y: 50},
    D: {x: 60, y: 30}
};

var m = {x: 40, y: 20};

puis:

pointInRectangle(m, r); // returns true.

Voici un code pour dessiner la sortie sous forme de test visuel :) http://codepen.io/mattburns/pen/jrrprN

enter image description here

17
matt burns
# Pseudo code
# Corners in ax,ay,bx,by,dx,dy
# Point in x, y

bax = bx - ax
bay = by - ay
dax = dx - ax
day = dy - ay

if ((x - ax) * bax + (y - ay) * bay < 0.0) return false
if ((x - bx) * bax + (y - by) * bay > 0.0) return false
if ((x - ax) * dax + (y - ay) * day < 0.0) return false
if ((x - dx) * dax + (y - dy) * day > 0.0) return false

return true
15
Jonas Elfström

Je réalise que c'est un vieux fil, mais pour ceux qui sont intéressés à regarder cela d'un point de vue purement mathématique, il y a un excellent fil sur l'échange de pile mathématique, ici:

https://math.stackexchange.com/questions/190111/how-to-check-if-a-point-is-inside-a-rectangle

Edit: Inspiré par ce fil de discussion, j'ai mis au point une méthode vectorielle simple pour déterminer rapidement où se situe votre idée.

Supposons que vous avez un rectangle avec des points à p1 = (x1, y1), p2 = (x2, y2), p3 = (x3, y3) et p4 = (x4, y4), dans le sens des aiguilles d'une montre. Si un point p = (x, y) se trouve à l'intérieur du rectangle, le produit scalaire (p - p1). (P2 - p1) sera compris entre 0 et | p2 - p1 | ^ 2 et (p - p1). (p4 - p1) sera compris entre 0 et | p4 - p1 | ^ 2. Cela équivaut à prendre la projection du vecteur p - p1 le long de la longueur et de la largeur du rectangle, avec p1 comme origine.

Cela peut sembler plus logique si je montre un code équivalent:

p21 = (x2 - x1, y2 - y1)
p41 = (x4 - x1, y4 - y1)

p21magnitude_squared = p21[0]^2 + p21[1]^2
p41magnitude_squared = p41[0]^2 + p41[1]^2

for x, y in list_of_points_to_test:

    p = (x - x1, y - y1)

    if 0 <= p[0] * p21[0] + p[1] * p21[1] <= p21magnitude_squared:
        if 0 <= p[0] * p41[0] + p[1] * p41[1]) <= p41magnitude_squared:
            return "Inside"
        else:
            return "Outside"
    else:
        return "Outside"

Et c'est tout. Cela fonctionnera également pour les parallélogrammes.

12
Matt Thompson

Si vous ne pouvez pas résoudre le problème du rectangle, essayez de diviser le problème en problèmes plus faciles. Divisez le rectangle en 2 triangles et cochez si le point est à l'intérieur de l'un d'eux, comme ils l'expliquent dans ici

Essentiellement, vous parcourez les bords toutes les deux paires de lignes à partir d'un point. Ensuite, utilisez le produit croisé pour vérifier si le point se trouve entre les deux lignes à l'aide du produit croisé. S'il est vérifié pour les 3 points, alors le point est à l'intérieur du triangle. La bonne chose à propos de cette méthode est qu’elle ne crée aucune erreur en virgule flottante, ce qui peut arriver si vous vérifiez les angles.

6
Cristian T
bool pointInRectangle(Point A, Point B, Point C, Point D, Point m ) {
    Point AB = vect2d(A, B);  float C1 = -1 * (AB.y*A.x + AB.x*A.y); float  D1 = (AB.y*m.x + AB.x*m.y) + C1;
    Point AD = vect2d(A, D);  float C2 = -1 * (AD.y*A.x + AD.x*A.y); float D2 = (AD.y*m.x + AD.x*m.y) + C2;
    Point BC = vect2d(B, C);  float C3 = -1 * (BC.y*B.x + BC.x*B.y); float D3 = (BC.y*m.x + BC.x*m.y) + C3;
    Point CD = vect2d(C, D);  float C4 = -1 * (CD.y*C.x + CD.x*C.y); float D4 = (CD.y*m.x + CD.x*m.y) + C4;
    return     0 >= D1 && 0 >= D4 && 0 <= D2 && 0 >= D3;}





Point vect2d(Point p1, Point p2) {
    Point temp;
    temp.x = (p2.x - p1.x);
    temp.y = -1 * (p2.y - p1.y);
    return temp;}

Points inside polygon

Je viens d'implémenter Answer de AnT en utilisant c ++. J'ai utilisé ce code pour vérifier si la coordination du pixel (X, Y) se situe ou non dans la forme.

5
Alghyaline

Si un point est à l'intérieur d'un rectangle. Dans un avion. Pour les coordonnées du mathématicien ou de la géodésie (GPS)

  • Soit le rectangle défini par les sommets A, B, C, D. Le point est P. Les coordonnées sont rectangulaires: x, y.
  • Permet de prolonger les côtés du rectangle. Nous avons donc 4 lignes droites lUN B, lAvant JC, lCD, lDA, ou, pour abréger, l1, l2, l3, l4.
  • Faites une équation pour chaque lje. Le genre d'équation de:

    fje(P) = 0.

P est un point. Pour les points appartenant à lje, l'équation est vraie.

  • Nous avons besoin des fonctions situées à gauche des équations. Ils sont f1, F2, F3, F4.
  • Notez que pour chaque point d'un côté de lje la fonction fje est supérieur à 0, pour les points de l'autre côté fje est inférieur à 0.
  • Donc, si nous vérifions si P est en rectangle, il suffit que p soit sur les côtés corrects des quatre lignes. Nous devons donc vérifier quatre fonctions pour leurs signes.
  • Mais de quel côté de la droite correspond le rectangle? C'est le côté où se trouvent les sommets du rectangle qui n'appartiennent pas à la ligne. Pour vérifier, nous pouvons choisir n'importe qui de deux sommets n'appartenant pas.
  • Donc, nous devons vérifier ceci:

    fUN B(P) fUN B(C)> = 0

    fAvant JC(P) fAvant JC(D)> = 0

    fCD(P) fCD(A)> = 0

    fDA(P) fDA(B)> = 0

Les inégalités ne sont pas strictes, car si un point se trouve sur la bordure, il appartient également au rectangle. Si vous n'avez pas besoin de points à la frontière, vous pouvez modifier les inéquations pour les plus strictes. Mais lorsque vous travaillez dans des opérations en virgule flottante, le choix n'a pas d'importance.

  • Pour un point situé dans le rectangle, les quatre équations sont vraies. Notez que cela fonctionne également pour chaque polygone convexe, seul le nombre de lignes/équations sera différent.
  • Il ne reste plus qu'à obtenir une équation pour une ligne passant par deux points. C'est une équation linéaire bien connue. Ecrivons-le pour une ligne AB et le point P:

    fUN B(P) (xUNE-XB) (yP-yB) yUNE-yB) (XP-XB)

La vérification pourrait être simplifiée - allons le long du rectangle --- (sens horaire - A, B, C, D, A. Tous les côtés corrects seront alors à droite des lignes. Nous n’avons donc pas besoin de comparer avec le côté où se trouve un autre sommet. Et nous avons besoin de vérifier un ensemble d'inéquations plus courtes:

fUN B(P)> = 0

fAvant JC(P)> = 0

fCD(P)> = 0

fDA(P)> = 0

Mais ceci est correct pour l'ensemble de coordonnées normal mathématicien (à partir des mathématiques scolaires), où X est à droite et Y au sommet. Et pour les coordonnées géodésie, comme dans le GPS, où X est en haut et Y à droite, nous devons tourner les inéquations:

fUN B(P) <= 0

fAvant JC(P) <= 0

fCD(P) <= 0

fDA(P) <= 0

Si vous n'êtes pas sûr de la direction des axes, soyez prudent avec cette vérification simplifiée - recherchez un point avec le placement connu, si vous avez choisi les bonnes inéquations.

4
Gangnus

Le moyen le plus simple auquel je pensais était de simplement projeter le point sur l’axe du rectangle. Laissez-moi expliquer:

Si vous pouvez obtenir le vecteur du centre du rectangle vers le bord supérieur ou inférieur et le bord gauche ou droit. Et vous avez également un vecteur du centre du rectangle à votre point, vous pouvez projeter ce point sur vos vecteurs largeur et hauteur.

P = vecteur de points, H = vecteur de hauteur, W = vecteur de largeur

Obtenez le vecteur unitaire W ', H' en divisant les vecteurs par leur magnitude

proj_P, H = P - (P.H ') H' proj_P, W = P - (P.W ') W'

Si je ne me trompe pas, ce que je ne pense pas être ... (corrigez-moi si je me trompe), mais si la magnitude de la projection de votre point sur le vecteur de hauteur est inférieure à la magnitude du vecteur de hauteur (qui est la moitié de la hauteur du rectangle) et la magnitude de la projection de votre point sur le vecteur largeur est alors, vous avez un point à l’intérieur de votre rectangle.

Si vous avez un système de coordonnées universel, vous devrez peut-être déterminer les vecteurs hauteur/largeur/points à l'aide de la soustraction vectorielle. Les projections vectorielles sont incroyables! souviens-toi de ça.

0
Matthew