Para encontrar a distância entre um ponto em uma reta, a ideia é a seguinte.
Suponha uma reta definida pelos pontos a e b.
Suponha que o ponto que queremos calcular a distância para essa reta é p.
A estratégia apresentada aqui não é a mais simples, mas tem a vantagem de além de achar a distância, encontrar também o ponto na reta que é mais próximo de p.
A ideia geral é a seguinte:
Eu sei que parece complicado, mas veja o detalhamento a seguir para entender em detalhes cada etapa.
Veja o esquema da figura a seguir. Iremos encontrar a distância entre o ponto p e a reta r formada pelos pontos a e b. Além disso, iremos também encontrar exatamente qual o ponto c que está presente na reta r que é o mais próximo de p.
Primeiro, construímos os vetores ap e ab. Agora iremos fazer o produto escalar entre esses dois vetores. O produto escalar nos dá o quanto que um vetor está apontando na direção do outro e multiplica suas magnitudes. Isso pode ser aplicado por exemplo para o calculo de forças resultantes em problemas de engenharias e física (veja aqui alguns exemplos).
Como o produto escalar está sempre na direção do vetor, então ele nos dá apenas uma grandeza escalar, ou seja, um número. Na figura abaixo, representamos visualmente representamos esse produto escalar na linha tracejada ( ap * ab ). Perceba que nesse exemplo, o produto escalar teria uma magnitude que o colocaria distante dos vetores ab e ac, afinal, ele será calculado como a multiplicação de suas magnitudes.
Bem, para calcular o produto escalar, se conhecemos o ângulo theta do vértice do ponto "a", é dada por: ||ap|| ||ab|| cos(theta) (veja aqui como essa fórmula faz sentido). Para simplificar aqui, vamos dizer que:
projeção ap em ab = ||ap|| cos(theta)
Logo, o produto escalar será: ||ab|| (projeção ap em ab).
É importante perceber então que o produto escalar é obtido multiplicando o ||ab|| pela projeção.
Porém, uma outra forma mais simples de obter o produto escalar sem precisar do ângulo é utilizar as componentes do vetor (veja aqui como se chega nessa fórmula):
ap * ab = ap.x * ab.x + ap.y * ab.y
Ótimo, agora já sabemos calcular o produto escalar de forma bem fácil. Porém, o que queremos é encontrar o ponto c. Para isso, precisamos encontrar a projeção de ap na reta r que passa por a e b.
Aqui está uma sacada. Se olharmos para a primeira fórmula do produto escalar, veremos que multiplicamos a projeção ap em ab por ||ab||. Ou seja, se já temos o valor do produto escalar, sabemos o valor de ||ab||, então, para achar a projeção, basta dividir o produto escalar por ||ab||:
projeção de ap em ab = (ap * ab) / ||ab||
Pronto, agora já encontramos a projeção.
Para achar o ponto c, uma ideia é criar um vetor com a magnitude da projeção e a direção de ab. Depois, usamos esse vetor para deslocar o ponto a e obter o ponto c.
Para isso, se dividirmos a magnitude da projeção pela magnitude do vetor ab, iremos saber o quão maior ou menor a projeção é em relação a ab:
= ((ap * ab) / || ab|| ) / ||ab||
= ((ap * ab) / || ab||^2 )
com esse valor, basta aplicarmos uma transformação de escala no vetor ab, para fazer com que ele tenha exatamente a magnitude até o ponto c.
Depois, aplicamos uma operação de deslocamento de ponto para deslocar e obter c.
Por fim, para encontrar a distância entre p e c, basta aplicar a distância entre dois pontos.
O código abaixo, depois da figura, contém a implementação dessas ideias.
vec toVec(point a, point b)
{
return vec(b.x - a.x, b.y -a.y);
}
vec scale(vec v, double factor)
{
return vec( v.x * factor, v.y * factor );
}
point translate(point p, vec v)
{
return point(p.x + v.x, p.y + v.y);
}
double dist(point p1, point p2)
{
return hypot(p1.x - p2.x, p1.y- p2.y);
}
/*
https://sites.google.com/site/ldsicufal/disciplinas/programacao-avancada/distancia-entre-ponto-e-reta
Representa o quanto que de a está apontando na mesma direção de b.
*/
double dot(vec a, vec b)
{
/*
Uma outra forma de fazer isso seria: |a||b| cos(theta). São equivalentes.
A forma simplificada abaixo é só uma derivação da forma acima
*/
return a.x*b.x + a.y*b.y;
}
/*
Retorna o quadrado da norma de um vetor.
Perceba que usamos a norma ao quadrado, pq no local onde usamos
essa norma, (função distToLine), a gente precisa dividir pela norma
ao quadrado. Como a norma é a raiz quadrada, para evitar calcular a raiz
e depois elevar ao quadrado, evitamos essas duas operações.
A norma representa a magnitude do vetor.
*/
double norm_sq(vec a)
{
return a.x * a.x + a.y * a.y;
}
/*
Retorna a distância do ponto p para as retas formadas pelos pontos a e b.
Além disso, também retorna qual o ponto na reta mais próximo de p.
*/
double distToLine(point p, point a, point b, point &c)
{
/* Cria 2 vetores:
ap e ab
*/
vec ap = toVec(a,p);
vec ab = toVec(a,b);
double u = dot(ap, ab) / norm_sq(ab);
/*
Agora, com a proporção, basta deslocar o ponto da origem : 'a'
com um vetor do mesmo sentido de ab e com magnitude calculada
pela proporção
*/
c = translate(a, scale(ab, u) );
/* Achamos o ponto, agora é só calcular a distância */
return dist (p, c);
}