Distancia punto a segmento, segunda parte

Siguiendo con el cálculo de la distancia de un punto a un segmento ahora toca mostrar un ejemplo de código que calcule la distancia. En la entrada anterior explique la teoria que puede utilizarse para calcular la distancia, en un mundo perfecto habrías leido la explicación y la habrías entendido.

Como ejemplo de código he diseñado una clase en Objective-C, que entre otros métodos calculará la distancia entre el segmento y un punto. Comenzamos por la interfície y luego veremos los detalles del código.

Interface: segmento.h

@interface Segmento : NSObject {
    // Puntos que definen el segmento
    NSPoint A;
    NSPoint B;
}

- (NSPoint)A;
- (NSPoint)B;
- (void)establecerA:(NSPoint)puntoA;
- (void)establecerB:(NSPoint)puntoB;
- (float)modulo2:(NSPoint)puntoA a:(NSPoint)puntoB;
- (float)distancia:(NSPoint)puntoA a:(NSPoint)puntoB;
- (float)longitud;
- (float)distanciaPunto:(NSPoint)C;
@end

Lo más interesante es ver se define el segmento a partir de los dos puntos extremos A y B. Como es habitual están los típicos métodos para recuperar los valores de estos puntos y para cambiarlos. Los otros métodos son los que servirán para el cálculo de la distancia. En este caso haré una excepción a la regla que dice “las variables tienen que comenzar con minúsculas”. De esta forma el código se parecerá más a las fórmulas matemáticas de la teoria.

Para definir los puntos utilizo la estructura NSPoint que contiene la coordenada x y la coordenada y del punto. Esto hace que código sea algo más fácil de escribir y entener en algunos aspectos. A cambio puede ser más difícil de trasladar a otros lenguajes de programación.

Implementación segmento.m

//
//  segmento.m
//  Clase que define un segmento de recta
//
//  Permite obtener los puntos extremos del segmento, la longitud del segmento y
//  **la distancia desde un punto dado al segmento**. Para la definición de los puntos
//  utiliza la estructura NSPoint.
//
//  Para ver la teoria en https://luisrey.wordpress.com/distancia
//
//  Created by Luis Rey Cabrerizo on 02/07/08.
//  Copyright 2008 LRC y Cia. All rights reserved.
//

#import "segmento.h"

@implementation Segmento
// Inicialización en dos puntos cualesquiera
- (id) init {
    if ( self = [super init] ) {
	A = NSMakePoint(-1.0, 1.0);
        B = NSMakePoint(3.0, -3.0);
    }
    return self;
}

// Desaparación del objeto, simplemente utiliza la herencia de NSObject
- (void) dealloc {
	[super dealloc];
}

// Métodos para devolver los puntos extremos
-(NSPoint)A{
    return A;
}
-(NSPoint)B{
    return B;
    }

// Métodos para establecer extremos del segmento
-(void)establecerA:(NSPoint)puntoA{
    A = puntoA;
}
-(void)establecerB:(NSPoint)puntoB{
    B = puntoB;
}

// Cuadrado de la distancia
- (float)modulo2:(NSPoint)puntoA a:(NSPoint)puntoB {
    return (puntoB.x-puntoA.x)*(puntoB.x-puntoA.x)+
           (puntoB.y-puntoA.y)*(puntoB.y-puntoA.y);
}

// Distancia punto a punto en 2 dimensiones
- (float)distancia:(NSPoint)puntoA a:(NSPoint)puntoB {
    return sqrt([self modulo2:puntoA a:puntoB]);
}

// Longitud del vector
-(float)longitud{
    return sqrt([self modulo2:A a:B]);
}

// Distancia del segmento al punto C
-(float)distanciaPunto:(NSPoint)C{
    // Punto en el segmento al cual se calculará la distancia
    // iniciamos en uno de los extremos
    NSPoint P = A;

    // Para prevenir una división por cero se calcula primero el demoninador de
    // la división. (Se puede dar si A y B son el mismo punto).
    // Podría substituirse por [self modulo2:A a:B]
    float denominador = (B.x-A.x)*(B.x-A.x)+(B.y-A.y)*(B.y-A.y);
    if(denominador !=0){
        // Se calcula el parámetro, que indica la posición del punto P en la recta
        // del segmento
        float u = ((C.x-A.x)*(B.x-A.x)+(C.y-A.y)*(B.y-A.y))/denominador;
        // Si u esta en el intervalo [0,1], el punto P pertenece al segmento
        if(u > 0.0 && u < 1.0) {
            P.x = A.x + u*(B.x-A.x);
            P.y = A.y + u*(B.y-A.y);
        }
        // Si P no pertenece al segmento se toma uno de los extremos para calcular
        // la distancia. Si u < 0 el extremo es A. Si u >=1 el extremos es B.
        else{
            if( u>= 1.0)
                P=B;
        }
    }
    // Se devuelve la distancia entre el punto C y el punto P calculado.
    return [self distancia:P a:C];
}

@end

Como de costumbre los métodos de inicialización, destrucción, recuperar datos y establecer datos no tienen muchos secretos. La única explicación relevante es que he establecido dos puntos cualesquiera como inicio del segmento (-1,1) y (3, -3) . Estricamente seria más correcto inicializarlos en (0,0) pero en este caso no me interesaba para el programa que he preparado como ejemplo.

 

Los métodos intersantes comienzan en modulo2:, calcula el cuadrado de la distancia entre dos puntos dados. Obviamente seria posible calcular la distancia directamente, pero puesto que este cálculo se hace al menos en dos sitios diferentes he preferido ponerlo en un método separado. En este mismo método he optado por escribir lo mínimo posible también se podía utilizar una variable intermedia:

- (float)modulo2:(NSPoint)puntoA a:(NSPoint)puntoB {
    float d =  (puntoB.x-puntoA.x)*(puntoB.x-puntoA.x)+(puntoB.y-puntoA.y)*(puntoB.y-puntoA.y);
    return d;
}

El núcleo del cálculo esta en distanciaPunto: recibe un punto C.

En primer lugar asignamos al punto P el primer extremo del segmento, de esta forma lo inicializamos y además en caso que se al extremo más cercano al punto C, ya estará preparado.

Seguidamente se calcula el denominador de la división que permite obtener el parámetro u. Así evitamos una división por cero en el caso que los extremos del segmento sean el mismo punto. Solamente seguiremoes el cálculo si el denominador es diferente a cero.

En el caso que sea posible se calcula el parámetro u. Y se analiza: si esta en el intervalo [0,1] se calcula el punto P de acuerdo a la fórmula. Si es más grande que 1 el extremo más cercano al punto C sera el extremo B. Si u es más pequeño que 0 el extremo más cercano es A (y no hace falta cambiar nada porque se ha asignado antes).

Al final con el punto P calculado sólo hace falta encontrar la distancia entre este punto y el punto C.

Entradas relacionadas

Etiquetas: , , ,

2 comentarios to “Distancia punto a segmento, segunda parte”

  1. Exequiel Says:

    La verdad me sirvió muchísimo tu explicacion, y el codigo… todo. Excelente!

  2. txes Says:

    +1 Muchas gracias!

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s


A %d blogueros les gusta esto: