Distancia punto a segmento, quinta parte y final

Por fin el final de esta serie de entradas sobre la distancia de un punto a un segmento. La verdad es que me está costando mucho más la preparación del programa y su documentación que el propio programa. Sospecho que esto de documentar un programa es la parte más complicada de todo. En fin, veamos como se inicializa la ventana, como se dibuja y como se controla el ratón.

Inicializando la vista

- (id)initWithFrame:(NSRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        // Inicializa el marco de la vista.
        NSRect	bounds = [self bounds];
        // Cálcula la escala de la ventana
        // Para el ejemplo se divide la pantalla en 10 divisones horizontales
        // La escala vertical es igual a la horizontal
        escalaX = bounds.size.width/10.0;
        escalaY = bounds.size.height/8.0;
        //El desplazamiento respecto al origen de la ventana
        desplazamientoX= bounds.size.width/2.0;
        desplazamientoY= bounds.size.height/2.0;

        // Establece el valor inicial del punto C
        C = NSMakePoint(2.0, 2.0);
    }
    return self;
}

En la inicialización de la vista se calcula la escala y el desplazamiento de la vista respecto a las coordenadas cartesianas que utilizarán los puntos (ver la entrada anterior, cuestiones de escala y origenes). Para simplificar el programa el número de divisiones y el desplazamiento se calculan a partir de constantes: 10 divisiones horizontales, 8 divisiones verticales y el origen de coordenadas en el centro de la vista. Igualmente el tamaño de la ventana y la vista esta fijados. Espero analizar más adelante la posibilidad de cambiar escalas y origenes en otro programa.

En la misma el mismo método se da un valor inicial al punto C externo al segmento. El valor asignado es, en este caso, totalmente arbitrario. Se ha escogido por que me parecía bien que quedara en esa posición bien diferenciado del segmento.

Dibujo de la vista

// Dibujar la vista
- (void)drawRect:(NSRect)rect {
    // Fondo blanco
    NSRect	bounds = [self bounds];
	[[NSColor whiteColor] set];
	NSRectFill(bounds);

    // Rejilla
    [[NSColor blackColor] set];
    int i,j;
    for (i=0;i<=8;i++){
        for(j=0;j<=10;j++){
            NSPoint punto = NSMakePoint(escalaX*j,escalaY*i);
            NSRect cuadrado = NSMakeRect(punto.x-1.0, punto.y-1.0, 2.0, 2.0);
            NSRectFill(cuadrado);
            }
    }

    // Ejes
    float ancho = bounds.size.width;
    float alto = bounds.size.height;
    [NSBezierPath strokeLineFromPoint:NSMakePoint(0.0,desplazamientoY) toPoint:NSMakePoint(ancho, desplazamientoY)];
    [NSBezierPath strokeLineFromPoint:NSMakePoint(desplazamientoX, 0.0) toPoint:NSMakePoint(desplazamientoX, alto)];

    // Segmento AB
	[[NSColor blueColor] set];
    NSPoint punto1 = [self escalar:[segment A]];
    NSRect cuadrado = NSMakeRect(punto1.x-2.0, punto1.y-2.0, 4.0, 4.0);
    NSRectFill(cuadrado);
    NSPoint punto2 = [self escalar:[segment B]];;
    cuadrado = NSMakeRect(punto2.x-2.0, punto2.y-2.0, 4.0, 4.0);
    NSRectFill(cuadrado);
    [[NSColor greenColor] set];
    [NSBezierPath strokeLineFromPoint:punto1 toPoint:punto2];

    // Punto C
    NSPoint punto3 = [self escalar:C];
    [[NSColor redColor] set];
    cuadrado = NSMakeRect(punto3.x-2.0, punto3.y-2.0, 4.0, 4.0);
    NSRectFill(cuadrado);

    // Distancia de punto a segmento
    [muestraDistancia setFloatValue:[segment distanciaPunto:C]];
}

Aunque no deberia ser difícil de entender hay varios detalles interesantes. Si no tienes claro las diferentes formas de dibujo con Cocoa seria conveniente que las revisaras antes de seguir con este ejemplo.

En primer lugar se dibuja el fondo blanco. Luego la rejilla, cómo no es posible dibujar puntos se dibujan pequeños cuadrados que haran el mismo papel. En tercer lugar se dibuja el segmento. Se puede ver como antes de dibujar el punto se escala a la medida de la vista:

NSPoint punto1 = [self escalar:[segment A]];

En cuarto lugar se dibuja el punto C (igualmente como cuadrado) y se acaba calculando la distancia entre punto y segmento, enviando el adecuado mensaje al objeto [segment distanciaPunto:C] y mostrandolo en pantalla.

Moviendo puntos con el ratón

Para poder mover los puntos con el ratón he optado por hacerlo a partir del arrastre del ratón. Para ello hay que poner el codigo adecuado en el método mouseDragged: Este método proporciona las coordenadas del punto donde se ha producido el arrastre del ratón.

// Analiza el arrastre del ratón
- (void)mouseDragged:(NSEvent *)theEvent {
    // Obtenemos las coordenadas de punto donde se ha hecho el desplazacmiento
	NSPoint	eventLocation = [theEvent locationInWindow];
    NSPoint impacto = [self convertPoint:eventLocation fromView:nil];

    // Puntos a comprobar, se escalan a las dimensiones de la vista
    NSPoint puntoA = [self escalar:[segment A]];
    NSPoint puntoB = [self escalar:[segment B]];
    NSPoint puntoC = [self escalar:C];

    // Resultado del arrastre
    NSPoint punto  = [self reducir:impacto];

    // Se comprueba por orden  si el punto de impacto està cercano
    // a uno de los puntos (extremos A i B del segmento o punto C)
    // si es así se asignan las nuevas coordenadas
    float d = [segment distancia:impacto a: puntoA];
    if (d<=30)
        [segment establecerA:punto];
    else{
        d = [segment distancia:impacto a: puntoB];
        if (d<=30)
            [segment establecerB:punto];
        else{
            d = [segment distancia:impacto a:puntoC];
            if (d<=30)
                C = punto;
        }
    }
    [self setNeedsDisplay:YES];
}

La primera parte permite obtener las coordenadas del punto donde esta ratón al arrastrarlo convertidas a las coordenadas de la vista.Hay que recordar que por defecto el evento da las coordenadas respecto a las coordenadas de la ventana que recibe el evento.

Luego se recuperan los punto que forman el segmento y el punto C convertidos a las coordenadas de la ventana.

En un paso previo se calcula el posible resultado al convertir las coordenadas del punto de arrastre a coordenadas cartesianas. Esto se hace para no tener que hacer el cálculo dentro de cada uno de las posibles opciones.

La parte importante es la selección de cual de los tres punto es el que va a ser arrastado. Esto se ha hecho a partir de sentencia alternativas if. Calculando la distancia entre punto y comprobando que es menor que un valor arbitrario. Este valor arbitrario o sensibilidad (30) lo he escogido por que funciona y en programas más complejos podría ser una parametro a controlar en las preferencias del programa.

Y acaba estableciendo la necesidad de rehacer el dibujo.

Espero que si alguien ha seguido este ejemplo haya aprendido algo de todo lo que yo he aprendido haciendolo y explicandolo.

Hasta otra.

Código fuente del ejemplo

distancia.zip

 

Archivos alojados en Dropbox.com

Entradas relacionadas

Etiquetas: , ,

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: