Distancia punto a segmento, cuarta parte

Cuestiones de escala y origenes

En anteriores entradas ya he explicado la teoria del cálculo de de la distancia de un punto a un segmento, también he mostrado un ejemplo de una clase escrita con Objective-C que realiza este cálculo y luego he comenzado a explicar un programa que utiliza esta clase. En este ejemplo los puntos que marcan los extremos del segmento y el punto externo al segmento solamente podrán tener valores enteros y además limitados a valores pequeños (entre -10 y 10).

Para poder representar adecuadamente estos punto en una vista hace falta “traducir” las coordenadas de los puntos a coordenadas en la pantalla. Esto es lo que intentaré mostrar en esta entrada.

Coordenadas cartesianas

La representación habitual de puntos, vectores, rectas y otros elementos geometricos se hace utilizando las coordenadas cartesianas. A cada punto del plano se le hace corresponder un par de valores (x, y) que definen su posición respecto a los ejes coordenados. De cara a la programación hace falta notar que las estas medidas no son absolutas. Simplemente se pide que la distancia entre cada una de la marcas de los ejes sean iguales y a partir de esta distancia se calculan las coordenadas. Cuando programemos nuestro sistema haba que traducir estas distancias a valores absolutos, normalmente en píxels.

Coordenadas en una ventana Cocoa

Para indicar las coordenadas de un punto en una ventana Cocoa el sistema que se utiliza esta basado en el sistema anterior, con un par de diferencias muy significativas:

  1. El origen de coordenadas esta en la esquina inferior izquierda de la ventana
  2. Las medidas son en puntos

Esto último representa un cambio respecto a la forma de trabajar habitual en informática. De forma tradicional la medida más utilizada es un píxel, el elemento más pequeño que se puede represantar en la pantalla del ordenador. Mac OS X desde el principio comenzo a cambiar la forma de trabajar al utilizar Quartz para dibujar en pantalla. En este sistema basado en PDF el sistema no guarda los píxels que componen una ventana, un recta o cualquier otro elemento, sino que los guarda de forma vectorial. Esto permite que lo que se muestra por pantalla sea fácilmente trasladable a la impresora (o a cualquier otro dispositivo de salida) y ciertas virguerias visuales muy propias de OS X.

Este “punto” ha respetado la tradicional definición de 1/72 de pulgada, con lo cual el sistema gráfico de OS X se ha de encargar de calcular los píxeles necesarios para llenar el “punto” segun la resolución del dispositivo de salida. No solo eso, sino que además a partir de OS X 10.5 Leopard es posible que el usuario cambie la escala con la cual se representa el punto. Este cambio todavía no esta muy presente en el sistema y se espera que al hacer las pantallas más grandes cada vez sea más utilizado en el futuro.

A efectos prácticos de programar esto significa que tendremos que conocer muy bien las opciones que Cocoa nos ofrece para calcular puntos y coordenadas. Olvidandonos de los píxels y dejando su gestión al sistema. Puedes obtener más información en: Cocoa Drawing Guide: Coordinate Systems Basics.

Escala y desplazamiento

Así pués vistas las diferencias entre los dos sistemas de coordenadas hay dos cosas por hacer, la primera es convertir las medidas cartesianas a los puntos de las ventanas Cocoa (y al contrario) con un factor de escala. Lo segundo es trasladar el origen de coordenadas utilizando un desplazamiento.

Matemáticamente para convertir un punto en coordenadas cartesianas a las coordenadas de la ventana hay que hacer el siguiente cálculo:

Xv = Xc * escalaX + desplazamientoX;

Donde Xv es la coordenada cartesiana y Xv la coordenada de la ventana. Para la coordenada Y el cálculo es el equivalente.

Para hacer el camino contrario, de coordenadas de la ventana a coordenadas cartesianas seria:

Xc = (Xv – desplazamientoY)/escalaX;

Un detalle importante es dar los valores adecuados al factor de escala (escalaX) y al desplazamiento (desplazamientoX). En el programa de ejemplo los he calculado a partir de las medidas de la ventana y el número de divisiones cartesianas que queria mostrar. En un programa más complejo esto nos permitiria crear una herramienta de zoom y centrar la vista en un punto concreto marcado por el ratón, por ejemplo.

También importante observar que la escala en las coordenadas horizontales no tiene por que ser la misma que en las verticales; pero que es recomendable que sean iguales para no deformar los objetos.

Código de ejemplo

En el programa de ejemplo la vista personalizada SVista contiene las variables y funciones necesarias para hacer la conversión entre los puntos. La Interface quedará:

#import <Cocoa/Cocoa.h>
#import "segmento.h"

@interface SVista : NSView {
   IBOutlet Segmento *segment;         // El segmento, se inicializara en IB
   IBOutlet id muestraDistancia;       // Muestra la distancia calculada en la ventana
   NSPoint C;                          // Punto externo al segmento
   float escalaX;                      // escala de la rejilla
   float escalaY;
   float desplazamientoX;
   float desplazamientoY;
}
- (NSPoint)escalar:(NSPoint)punto;
- (NSPoint)reducir:(NSPoint)punto;
@end

Cómo puede verse se han definido las variables para las escalas y los desplazamientos horizontales. Igualmente estan definidas las funciones que nos permiten convertir los puntos a unas coordenadas u otras según nuestro interés.

En la implementación de SVista.m estarán las funciones:

// Convierte el punto a la escala de la vista
-(NSPoint)escalar:(NSPoint)punto{
    NSPoint p;
    p.x = punto.x*escalaX+desplazamientoX;
    p.y = punto.y*escalaY+desplazamientoY;
    return p;
}

// Convierte de la escala de la vista a natural
-(NSPoint)reducir:(NSPoint)punto{
    NSPoint p;
    p.x = (punto.x-desplazamientoX)/escalaX;
    p.y = (punto.y-desplazamientoY)/escalaY;
    return p;
}

Redondeo

Para que el ejemplo fuera más sencillo de observar, interesa que los puntos que forman el segmento y el punto exterior solo pudieran tener valores enteros. En este caso al desplazar un punto habrá que aproximarlo al entero más cercano. Para ello se puede utilizar la función floor() que es estándar de C incluida en la librería matemática “math.h“. Un pequeño detalle es que la funciona floor() no redondea, solamente da el entero inferior al valor introducido, para hacer el redondeo hay que sumar el valor 0,5 al valor que nos intesesa redondear.

De esta forma el código queda:

// Convierte el punto a la escala de la vista
-(NSPoint)escalar:(NSPoint)punto{
    NSPoint p;
    p.x = punto.x*escalaX+desplazamientoX;
    p.y = punto.y*escalaY+desplazamientoY;
    return p;
}

// Convierte de la escala de la vista a natural
-(NSPoint)reducir:(NSPoint)punto{
    NSPoint p;
    // floor calcula el entero inferior al punto calculado, para redondear hay que
    // sumar 0.5
    p.x = floor((punto.x-desplazamientoX)/escalaX+0.5);
    p.y = floor((punto.y-desplazamientoY)/escalaY+0.5);
    return p;
}

Y esto es todo por ahora, quedan pendientes de explicar los “pequeños” detalles del dibujo de los puntos y cómo mover los puntos utilizando el ratón. Pero todo llegará a su hora.

Entradas Relacionadas

Etiquetas: , , ,

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

  1. malo Says:

    asdasdasd cama
    es malo este sitio

  2. nidya Says:

    hola solo keria que me eyudaran a resolver un problama no,,, sobre ejemplos de ejes de coordenadas con sus puntos

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: