Lexico
...el poder de lo simple...
Marzo.4.2010

Tutorial Rotar Imagen

Se realizará un reloj analógico. Para ello se necesita controlar dos tipos de eventos:
1. Cambios por avance del tiempo
2. Actualización de la imagen

Debido a la necesidad de manejar eventos se creará la aplicación con una interfaz gráfica mediante la descripción de la clase rotar de manera que herede las características (derivada_de) definidas en la clase Form de .NET y permita agregar las particularidades de la presente aplicación.

Lo primero se hará creando un objeto temporizador (tempo) de la clase Timer, iniciando su funcionamiento (Start), programando un algoritmo controlador (alerte) e inscribiéndolo como manejador del evento Tick para que el Sistema operativo sepa cual mensaje enviar a la aplicación rotar. El algoritmo alerte se encargará de actualizar el estado del reloj.

Lo segundo se hará en el algoritmo alerte mediante el crecimiento de tres objetos sencillos (ángulo_horas, ángulo_minutos y ángulo_segundos) cada vez que tempo genere un Tick y en consecuencia Windows alerte a rotar. En alerte también se ordena a rotar actualizar (Refresh) la pintura. Realmente ésto lo que hace es generar un evento de pintura (Paint) que Windows detecta y en consecuencia envía el mensaje pinte (activa ese algoritmo inscrito como manejador).

clase rotar derivada_de Form
publicos
el objeto tempo es un Timer

mensajes
rotar
{
rotar.manejador(rotar.Paint, pinte)
rotar.manejador(tempo.Tick, alerte)
tempo.Start
}

pinte(deme nc, deme det es un PaintEventArgs)
{

}

alerte(deme nc, deme det es un EventArgs)
{

rotar.Refresh
}

Ahora, se incluyen los siguientes detalles:

1. Definir los objetos que faltan (ángulo_horas, ángulo_minutos y ángulo_segundos) como atributos de la clase para que sean visibles desde todos los algoritmos de ella pues les deben ser comunes: en uno se inician, en otro se actualizan y en el otro se usan. También se definen los deltas o incrementos (dh, dm y ds) que deben ser sumados cada segundo a los respectivos ángulos y se calculan cuando empiece a ejecutarse la aplicación rotar. Se define el objeto lápiz necesario para dibujar (DrawLine) las rayas utilizando la zona gráfica que envía Windows (det.Graphics).

2. Iniciar los objetos que contienen los ángulos para horario, minutero y segundero (claro, en el constructor de la clase rotar que es el primer algoritmo que se ejecutará). Se actualizarán en el algoritmo alerte. También se establece el valor para los incrementos que deben ocurrir a cada segundo (1000 milisegundos en tempo.Interval).

3. Pintar las manecillas del reloj con sus respectivas inclinaciones (ángulo_horas, ángulo_minutos y ángulo_segundos).

El dibujo de las líneas se hace cada vez que se haya transformado la gráfica: se desplaza (TranslateTransform) al sitio de rotación (quedando el 0,0 de Graphics en 150,150 del área de usuario de rotar), luego se gira (RotateTransform) a Graphics el ángulo apropiado y se dibuja (DrawLine). Luego se devuelven las transformaciones para dejar la gráfica en su estado original y aislarla de las futuras transformaciones pues de no hacerlo ellas se acumularían y debería hacerse un cálculo más complejo.


clase rotar derivada_de Form
publicos
el objeto tempo es un Timer
los objetos ángulo_horas, ángulo_minutos, ángulo_segundos, dh, dm, ds son cantidades
el objeto lápiz es un Pen(Color.Blue)

mensajes
rotar
{
rotar.Manejador(rotar.Paint, pinte)
rotar.Manejador(tempo.Tick, alerte)
tempo.Start
copie 1000 en tempo.Interval
copie -90 en ángulo_horas, ángulo_minutos, ángulo_segundos
copie 360/(12*60*60) en dh
copie 360/(60*60) en dm
copie 360/60 en ds

}

pinte(deme nc, deme det es un PaintEventArgs)
{
det.Graphics.TranslateTransform(150, 150)
det.Graphics.RotateTransform(ángulo_horas)
det.Graphics.DrawLine(lápiz, 0, 0, 50, 0)
det.Graphics.RotateTransform(
-ángulo_horas)
det.Graphics.TranslateTransform(
-150, -150)

det.Graphics.TranslateTransform(150, 150)
det.Graphics.RotateTransform(ángulo_minutos)
det.Graphics.DrawLine(lápiz, 0, 0, 70, 0)
det.Graphics.RotateTransform(
-ángulo_minutos)
det.Graphics.TranslateTransform(
-150, -150)

det.Graphics.TranslateTransform(150, 150)
det.Graphics.RotateTransform(ángulo_segundos)
det.Graphics.DrawLine(lápiz, 0, 0, 100, 0)
det.Graphics.RotateTransform(
-ángulo_segundos)
det.Graphics.TranslateTransform(
-150, -150)
}

alerte(deme nc, deme det es un EventArgs)
{
copie ángulo_horas    + dh en ángulo_horas
copie ángulo_minutos  + dm en ángulo_minutos
copie ángulo_segundos + ds en ángulo_segundos

rotar.Refresh
}

Solo falta sincronizar los ángulos de las manecillas para que inicien en la dirección según el tiempo real pues se han iniciado arbitrariamente en 0 sin tenerlo en cuenta. Para ello puede utilizarse la clase DateTime (en .NET no es una clase, pero desde Lexico todo se ve como clases), su método static (en Lexico exclusivo) Now que captura la fecha y hora real de la máquina, y los atributos Hour, Minute y Second que son cantidades como se muestra en éste ejemplo.

Los cálculos para la iniciación de los ángulos deben responder a la pregunta cual es el ángulo para las manecillas cuando se tienen las cantidades Hour, Minute y Second ?:

Si las manecillas hacia arriba son la dirección para 0 y los ángulos positivos son en el sentido del recorrido de las manecillas (convención usualmente empleada en programación y particularmente en la clase Graphics de .NET) se tiene que en -90 grados se representará el 0 (en .NET el 0 es hacia la derecha). Cual debe ser el ángulo para el valor real de Hour, Minute y Second ?

El segundero debe moverse 60 veces para completar un giro de 360 grados por lo que el delta de giro ds será 360/60=6 grados
Ahora, si cada segundo debe aumentarse el ángulo en ds, cual ángulo debe ser para el valor inicial Second ?

Recuérdese que al cálculo debe restarse 90 grados para tener en cuenta que el 0 grados sea hacia arriba.