posts - 2,  comments - 8,  trackbacks - 0
  Tuesday, October 25, 2005

Recientemente, para una chorradilla que estoy haciendo en casa en mis (poooocos) ratos libres, me vi en la necesidad de tener que calcular todos los puntos existentes en la línea recta que separa dos puntos cualesquiera de la pantalla.

O sea, dado un punto de origen definido en coordenadas de pantalla (X e Y) y un punto de destino, situados ambos en cualquier sitio de la pantalla y sin que tengan que estar a la misma altura ni horizontal ni vertical, se trata de obtener toda la sucesión de puntos que separan el origen y el destino.

Vale, cosa fácil... en VB6 ya me vi en las mismas y me acordé de que hay una función del API, llamada LineDDA, que hace precisamente eso... pues nada, la portamos al .net, ¿no? Sí, pero... hay un problemilla que en realidad no es tal hasta que lo haces la primera vez...

El caso es que LineDDA es una de esas curiosas funciones del API de Win32 que funcionan mediante el uso de callbacks, o sea, funciones de retorno... es decir, tú llamas a LineDDA pasándole como parámetro un puntero a una función que has definido en el código, y LineDDA llama a esa función una vez por cada punto que encuentra en la línea recta que se quiere obtener.

Y como era la primera vez que me he enfrentado al caso, he tenido que dar un par de vueltecillas hasta que he encontrado la solución; es bastante fácil gracias al uso de los delegados.

Los delegados son tipos de referencia; o sea... algo así como instancias de procedimientos que hacen referencia a otro procedimiento declarado en otra (o en la misma) clase, siempre y cuando sus argumentos y tipos devueltos sean los mismos. En el ejemplo se ve mejor y creo que se comprende bastante bien.

Para probar LineDDA, lo mejor es crear una nueva aplicación Windows con VB.net. Una vez hecho esto, añadiremos un nuevo módulo al proyecto con este código:


'Este es el delegado. Al instanciar un nuevo objeto de tipo LineDDACallBack, haremos
'referencia a otra función que debe tener la misma estructura de parámetros y tipo
'de retorno
Public Delegate Function LineDDACallback(ByVal x As Integer, ByVal y As Integer, _
ByVal lpData As Integer) As Integer 'Esta es la declaración del API de Win32 LineDDA, adaptada a VB.net. Como se ve, uno 'de los parámetros que le pasamos es nuestra función delegada (en realidad, un puntero 'a la misma para que LineDDA pueda llamarla para darnos el resultado que queremos) Public Declare Function LineDDA Lib "gdi32" (ByVal n1 As Integer, ByVal n2 As Integer, _
ByVal n3 As Integer, ByVal n4 As Integer, ByVal lpLineDDAProc As LineDDACallback, _
ByVal lParam As Integer) As Integer

Después, en Form1.vb escribiremos el código necesario para invocar a la función:


'Declarar estas dos variables a nivel de formulario
Private m_Puntos() As System.Drawing.Point
Private m_NumeroPuntos As Integer = 0

'Esta es la función real a la que llamará LineDDA, suministrándonos uno a uno los
'puntos existentes en la línea recta
Private Function _lineddacbk(ByVal x As Integer, ByVal y As Integer, _
ByVal lpData As Integer) As Integer     'Aumentamos en 1 el nº de puntos     m_NumeroPuntos += 1     'Redimensionamos el array que contiene los puntos     ReDim Preserve m_Puntos(m_NumeroPuntos - 1)     'Creamos un nuevo punto con la información que nos da LineDDA     Dim oPunto As New System.Drawing.Point(x, y)     'Y lo añadimos al array     m_Puntos(m_NumeroPuntos - 1) = oPunto End Function 'Por fin, la función que lo hace todo... Public Function CalculaLinea(ByVal Origen As System.Drawing.Point, _
ByVal Destino As System.Drawing.Point) As System.Drawing.Point()     'Declaramos e instanciamos la función de retorno de llamada (callback)     Dim oCallback As New LineDDACallback(AddressOf _lineddacbk)     'Como se ve, lo que hacemos es declarar una función delegada que hace referencia
'a nuestra función _lineddacbk
    'Ahora llamamos a LineDDA     Dim iRes As Integer = LineDDA(Origen.X, Origen.Y, Destino.X, Destino.Y, _
oCallback, 0)     'Y ya está. Podeis ejecutar paso a paso, vereis como la función _lineddacbk es
'llamada una vez por cada punto
existente en la recta que separa Origen de Destino     Return m_Puntos End Function

Así de fácil. Probadlo, es muy divertido.

P.S. Como muy bien me ha hecho notar Miguel Jiménez, es más que probable que esta función utilice internamente el algoritmo de Bresenham.

Hala, ya está. Todo borrado.

Jo, ha dolido un poco.

En fin... después de (¡uf, cómo pasa el tiempo!) más de un año sin postear, aquí estoy de nuevo dispuesto a guerrear, aunque esta vez en VB.net. He tenido que cambiar el título del blog... lo del VB6 ya hace tiempo que pasó a la historia, al menos para mí.

¿Qué ha pasado en este tiempo?

Pues veamos... en lo personal, he tenido dos niñas y mi vida es ahora un poco más ajetreada. Por poner un ejemplo, si antes era una marejadilla ahora es un tsunami del copón. ¡Qué cosas! Si hace nada (¿?) estaba con mis colegas por ahí todo el día de birras y mus... ¿verdad, Raúl?

Y en lo profesional... por fin dejé el VB6, que fue mi lenguaje "materno" por así decirlo durante más de 8 años, y cambié a VB.net (con alguna cosilla esporádica en C#). Y el cambio la verdad es que no fue nada traumático, al contrario, lo agradecí y me sentó la mar de bien, que ya estaba un poco "amuermao" con tanto VB. Y bueno, como hace ya más de un año del cambio, pues ya soy un crack en el VB.net este, al igual que lo fui en VB6 en su día (como no tengo abuela, pues eso). Me he pasado estos... no sé, 14 ó 15 meses, programando la que sin duda debe ser una de las mayores aplicaciones (en magnitud de datos transportados entre el servidor y los clientes) existentes en España hechas en ASP.net. Ojalá pudiérais verla, estoy muy orgulloso... en fin, lo malo es que la entrada es privada y la hacemos con seguridad de certificados de usuario, así que no hay nada que hacer. El caso es que a base de currar como un negro sin tener ni pajolera idea (que es, como todos sabréis, como mejor aprende uno), pues me he puesto las pilas en VB.net, ASP.net y Javascript (del que no había escrito ni una sola línea de código antes de esto).

Y el caso es que llevaba tiempo diciéndome "joer, lo de ClearScreen tenías que retomarlo otra vez", así que aquí estoy. Y como soy así de radical (eso dice mi mujer), he borrado todos los posts referentes a VB6, a pesar de que tenía algunas clases muy chulas y un par de artículos sobre subclasificación que merecían la pena. Pero renovarse o morir, ¿no?

Pues hala, esta noche o mañana mismo empiezo a postear en serio.

Salu2 a tod@s y hola de nuevo.