Como calcular a distância entre um ponto e um retângulo alinhado ao eixo?


29

Eu tenho um retângulo 2D com posição x, y, altura e largura e um ponto posicionado aleatoriamente nas proximidades.

Existe uma maneira de verificar se esse ponto pode colidir com o retângulo se estiver mais próximo do que uma certa distância? Imagine um raio invisível fora desse ponto colidindo com o referido retângulo. Eu tenho problemas com isso simplesmente porque não é um quadrado!

Respostas:


26

Se (x,y)é o centro do retângulo, a distância ao quadrado de um ponto (px,py)até a borda do retângulo pode ser calculada da seguinte maneira:

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

Se essa distância ao quadrado é zero, significa que o ponto toca ou está dentro do retângulo.


6
Para qualquer outra pessoa que está perguntando, (x, y) é o centro do retângulo, não o canto
Greg Rozmarynowycz

2
Desculpe pelo comentário antigo, mas essa equação supõe que o retângulo esteja alinhado ao eixo?
BitNinja 25/10/14

1
@ BitNinja sim, é isso que a pergunta assume. Se não estiver alinhado ao eixo, o algoritmo mais rápido / mais simples dependerá de como as informações do retângulo são armazenadas.
23414 # 13:

digamos, o ponto é (4: 4), o retângulo está em (5: 5) com largura / altura (5: 5). Seu código diria que os toques de ponto ou está dentro do retângulo, mas é obviamente fora
LRN

@LRN, um retângulo centrado em (5: 5) com largura / altura (5: 5) se estende de (2,5: 2,5) a (7,5: 7,5). O ponto (4: 4) está dentro desse retângulo.
Sam Hocevar

11

Presumo que seu retângulo esteja alinhado ao eixo.

Você apenas precisa "prender" o ponto no retângulo e depois calcular a distância do ponto preso.

Ponto = (px, py), Retângulo = (rx, ry, rwidth, rheight) // (canto superior esquerdo, dimensões)

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

Você deve usar colisões círculo-retângulo para isso. Há uma pergunta semelhante no Stack Overflow.

O centro do seu círculo seria o ponto em questão e o raio seria a distância que você deseja verificar.


3

Se você está tentando descobrir a distância de um ponto até a aresta de um retângulo, trabalhar com cada uma das nove regiões criadas pelo retângulo pode ser a maneira mais rápida:

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

[Resposta modificada com base nos comentários]

Se você quiser ver se o ponto está dentro, por exemplo, 10 unidades, se o retângulo cinza na imagem abaixo, verifique se o ponto está em qualquer um dos

  1. retângulo vermelho
  2. Retângulo azul
  3. qualquer um dos círculos verdes (raio 10)

insira a descrição da imagem aqui

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

Essa abordagem é um pouco deselegante. Um método semelhante que evita a necessidade de testar todos os 4 cantos usando simetria de retângulo está documentado aqui no stackoverflow


Na direção diagonal, isso dará um falso positivo aos pontos que são, por exemplo. 11 unidades de distância.
Eric B

A imagem atualizada está flagrantemente errada, na verdade, ilustra o caso de erro e faz com que pareça correto. Esse ponto verde pode facilmente estar a mais de 10 unidades e estar dentro desse retângulo externo.
Eric B

Hey @EricB, corrigi o erro que você apontou, que tal desfazer seu voto negativo?
26712 Ken

Sua resposta não dará mais resultados estritamente incorretos, por isso removi o voto negativo, mas também não é o melhor caminho. Por que não testar apenas se o centro está dentro do retângulo e se os quatro segmentos de linha cruzam o círculo? A construção desses novos retângulos e círculos simplesmente não é necessária. Sua resposta também não fornece a distância real do ponto ao retângulo.
Eric B

Esta resposta é honestamente terrível. 12 adições, 4 construções de objetos, 12 testes, 4 raízes quadradas para uma tarefa que realmente requer 3 linhas de código?
28612 Sam sami hocevar

-2

Você pode usar algo como isto: insira a descrição da imagem aqui


Este método parece desnecessariamente complicado. Encontrar x1 e y1 não é necessário para resolver esse problema.
Eric B

De fato, isso nem sequer satisfaz a exigência de encontrar uma colisão a uma determinada distância. É apenas uma maneira ruim de detectar se o ponto está dentro do retângulo.
Eric B

Uma medida de distância já está implicitamente presente. if (d2 <10 * 10) {/ * dentro de 10 unidades de medida * /} #
AlexanderBrevig
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.