Come calcolo la distanza tra un punto e un rettangolo allineato sull'asse?


29

Ho un rettangolo 2D con posizione x, y, altezza e larghezza e un punto posizionato casualmente nelle vicinanze.

C'è un modo per verificare se questo punto potrebbe scontrarsi con il rettangolo se è più vicino di una certa distanza? Immagina un raggio invisibile al di fuori di quel punto in collisione con detto rettangolo. Ho problemi con questo semplicemente perché non è un quadrato!

Risposte:


26

Se (x,y)è il centro del rettangolo, la distanza quadrata da un punto (px,py)al bordo del rettangolo può essere calcolata in questo modo:

dx = max(abs(px - x) - width / 2, 0);
dy = max(abs(py - y) - height / 2, 0);
return dx * dx + dy * dy;

Se la distanza al quadrato è zero, significa che il punto tocca o è all'interno del rettangolo.


6
Per chiunque si stia chiedendo, (x, y) è il centro del rettangolo, non l'angolo
Greg Rozmarynowycz,

2
Scusate il vecchio commento, ma questa equazione presuppone che il rettangolo sia allineato all'asse?
BitNinja,

1
@BitNinja sì, questo è ciò che la domanda presuppone. Se non è allineato all'asse, l'algoritmo più veloce / più semplice dipenderà dalla modalità di memorizzazione delle informazioni sul rettangolo.
sam hocevar,

diciamo che il punto è (4: 4), il rettangolo è a (5: 5) con larghezza / altezza (5: 5). Il tuo codice affermerebbe che il punto tocca o è all'interno del rettangolo, ma è ovviamente al di fuori
LRN

@LRN un rettangolo centrato su (5: 5) con larghezza / altezza (5: 5) si estende da (2,5: 2,5) a (7,5: 7,5). Il punto (4: 4) è all'interno di quel rettangolo.
Sam Hocevar,

11

Presumo che il tuo rettangolo sia allineato all'asse.

Devi solo "bloccare" il punto nel rettangolo e quindi calcolare la distanza dal punto bloccato.

Point = (px, py), Rectangle = (rx, ry, rwidth, rheight) // (angolo in alto a sinistra, dimensioni)

function pointRectDist (px, py, rx, ry, rwidth, rheight)
{
    var cx = Math.max(Math.min(px, rx+rwidth ), rx);
    var cy = Math.max(Math.min(py, ry+rheight), ry);
    return Math.sqrt( (px-cx)*(px-cx) + (py-cy)*(py-cy) );
}

3

Per questo è necessario utilizzare le collisioni cerchio-rettangolo. C'è una domanda simile su Stack Overflow.

Il centro della tua cerchia sarebbe il punto in questione e il raggio sarebbe la distanza che desideri controllare.


3

Se stai cercando di capire la distanza da un punto al bordo di un rettangolo, lavorare con ognuna delle nove regioni create dal rettangolo potrebbe essere il modo più veloce:

function pointRectangleDistance(x, y, x1, y1, x2, y2) {
    var dx, dy;
    if (x < x1) {
        dx = x1 - x;
        if (y < y1) {
            dy = y1 - y;
            return Math.sqrt(dx * dx + dy * dy);
        }
        else if (y > y2) {
            dy = y - y2;
            return Math.sqrt(dx * dx + dy * dy);
        }
        else {
            return dx;
        }
    }
    else if (x > x2) {
        dx = x - x2;
        if (y < y1) {
            dy = y1 - y;
            return Math.sqrt(dx * dx + dy * dy);
        }
        else if (y > y2) {
            dy = y - y2;
            return Math.sqrt(dx * dx + dy * dy);
        }
        else {
            return dx;
        }
    }
    else {
        if (y < y1) {
            return y1 - y;
        }
        else if (y > y2) {
            return y - y2;
        }
        else {
            return 0.0; // inside the rectangle or on the edge
        }
    }
}

2

[Risposta modificata in base ai commenti]

Se vuoi vedere se il punto è dentro diciamo 10 unità se il rettangolo grigio nell'immagine qui sotto, controlla se il punto è in uno qualsiasi di

  1. rettangolo rosso
  2. Rettangolo blu
  3. uno qualsiasi dei cerchi verdi (raggio 10)

inserisci qui la descrizione dell'immagine

inside=false;

bluerect.x=oldrect.x-10;
bluerect.y=oldrect.y;
bluerect.width=oldrect.width;
bluerect.height=oldrect.height+20;

if(  point.x >=bluerect && point.x <=redrect.x+bluerect.width &&
     point.y >=bluerect && point.y <=redrect.y+bluerect.height){
         //now point is side the blue rectangle
         inside=true;
}

redrect.x=oldrect.x;
redrect.y=oldrect.y-10;
redrect.width=oldrect.width+20;
redrect.height=oldrect.height;

if(  point.x >=redrect&& point.x <=redrect.x+redrect.width &&
     point.y >=redrect&& point.y <=redrect.y+redrect.height){
         //now point is side the redrectangle
         inside=true;
}


d1= distance(point, new point(oldrect.x, oldrect.y)) //calculate distance between point and (oldrect.x, oldrect.y)
d2= distance(point, new point(oldrect.x+10, oldrect.y))
d3= distance(point, new point(oldrect.x, oldrect.y+10))
d4= distance(point, new point(oldrect.x+10, oldrect.y+10))
if (d1 < 10 || d2 <10 || d3 < 10 || d4 <10){
    inside=true;
}

//inside is now true if the point is within 10 units of rectangle

Questo approccio è un po 'inelegante. Un metodo simile che evita di dover testare tutti e 4 gli angoli utilizzando la simmetria del rettangolo è documentato qui su StackOverflow


Nella direzione diagonale questo darà un falso positivo ai punti che sono ad es. 11 unità di distanza.
Eric B,

L'immagine aggiornata è palesemente sbagliata, in realtà illustra effettivamente il caso di errore e lo fa apparire corretto. Quel punto verde potrebbe facilmente essere a più di 10 unità di distanza ed essere all'interno di quel rettangolo esterno.
Eric B,

Ehi @EricB, ho corretto l'errore che hai sottolineato, che ne dici di annullare il tuo downvote?
Ken,

La tua risposta non darà più risultati strettamente errati, quindi ho rimosso il downvote, ma non è affatto il modo migliore. Perché non provare solo per vedere se il centro si trova all'interno del rettangolo e se i quattro segmenti di linea si intersecano con il cerchio? La costruzione di questi nuovi rettangoli e cerchi non è necessaria. Inoltre, la tua risposta non fornisce la distanza effettiva dal punto al rettangolo.
Eric B,

Questa risposta è davvero terribile. 12 aggiunte, 4 costruzioni di oggetti, 12 test, 4 radici quadrate per un'attività che richiede effettivamente 3 righe di codice?
Sam Hocevar,

-2

Puoi usare qualcosa del genere: inserisci qui la descrizione dell'immagine


Questo metodo sembra inutilmente complicato. Trovare x1 e y1 non è necessario per risolvere questo problema.
Eric B,

In realtà, questo non soddisfa nemmeno il requisito di trovare una collisione entro una determinata distanza. È solo un brutto modo di rilevare se il punto si trova all'interno del rettangolo.
Eric B,

Una misura della distanza è già implicitamente lì. if (d2 <10 * 10) {/ * entro 10 unità di misura * /}
AlexanderBrevig
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.