Hola amigos espero que estén de lo mejor y me alegro de saludarlos de nuevo y de que me acompañen a realizar más aplicaciones en C++ Builder. En esta ocasión vamos a comenzar con el trazo de líneas mediante el algoritmo conocido como DDA (Analizador Diferencial Digital).
Antes de codificar este algoritmo vamos a explicar un poco de como funciona. Lo que hace el algoritmo de la línea DDA es ir calculando las posiciones de cada uno de los pixeles a lo largo de una línea. Esta tarea la lleva a cabo realizando pasos o incrementos unitarios en una de las coordenadas, y realizando los cálculos necesarios para encontrar el valor de la otra coordenada. Básicamente ese es el funcionamiento del algoritmo.
Bien pues comenzamos con nuestra aplicación para dibujar nuestra línea DDA. Ésta aplicación tendrá bastantes componentes dentro de la Forma, dichos componentes los tenemos en la siguiente lista:
-1 PaintBox
-1 ColorDialog
-7 Timer (todos tienen que estar con su propiedad Enabled en false)
-1 Panel
Dentro del Panel tenemos los siguientes componentes:
-1 Label
-1 ComboBox, y en su propiedad Items agregamos las siguientes cadenas:
-Cono
-Círculo
-Polígono
-Efecto Propio 1
-Efecto Propio 3
-Efecto Propio 4
-3 Button
-1GroupBox
Dentro del GroupBox tendremos los siguientes componentes:
-5 Label
-4 Edit
-1 SpeedButton (podemos crear nuestro propio Bitmap para agregarlo a su propiedad Glyph, lo hacemos desde el Image Editor accesible desde el menú Tools de C++ Builder)
Y por la parte visual de nuestra aplicación eso sería todo. Los componentes pueden acomodarlos de la manera que ustedes gusten, en mi caso yo lo hice de la siguiente manera:
Ahora vamos con la parte del código. Nuestra línea DDA la dibujaremos desde una clase que vamos a crear nosotros mismos. Para hacer esto abrimos el menú "File", después la primera opción "New" y seleccionamos "Unit". A nuestra nueva clase la llamaremos "Graficos".
Para crear nuestra clase gráficos primero nos vamos al archivo H de la nueva unidad que creamos, y definimos el constructor el cual recibirá el Canvas del componente donde estemos dibujando y lo asignará al de la clase Graficos para con el dibujar la línea DDA, además también definiremos el método LineaDDA(), el cual tendrá 2 sobrecargas, la primera para dibujar la línea normalmente y la segunda para dibujar la línea con un color que hayamos elegido. El código del archivo H de nuestra clase Graficos lo tenemos a continuación:
class Graficos
{
TCanvas *canvas;
public:
Graficos(TCanvas *CanvaS)
{
canvas = CanvaS;
}
int round(float n);
void LineaDDA(int x1, int y1, int x2, int y2);
void LineaDDA(int x1, int y1, int x2, int y2, TColor color);
};
Debemos cuidar que el código anterior lo hayamos escrito antes de la línea:
#endif
Ahora en el archivo .cpp, en la parte superior escribimos la línea de código para incluir la clase Math en nuestra aplicación ya que la utilizaremos más adelante:
#include <Math.h>
Ahora debajo de la línea de código:
#pragma package(smart_init)
comenzamos a escribir nuestros métodos para la línea DDA.
Primero escribiremos un método propio para redondear los números que deseemos. El código es bastante sencillo y lo tenemos a continuación:
int Graficos::round(float n)
{
return int(n+0.5);
}
//-----------------------------------------------------------------------------
Ahora codificamos el método para dibujar nuestra línea DDA los parámetros que necesitamos son las coordenadas iniciales de la línea y las coordenadas finales. El código para dibujar la línea DDA es el siguiente:
void Graficos::LineaDDA(int x1, int y1, int x2, int y2)
{
int dx, dy, steps, k;
float x_inc, y_inc, x, y;
dx = x2 - x1;
dy = y2 - y1;
if (abs(dx) > abs(dy))
steps = abs(dx);
else
steps = abs(dy);
x_inc = (float)dx/steps;
y_inc = (float)dy/steps;
x = x1;
y = y1;
canvas->Pixels[floor(x+0.5)][floor(y+0.5)];
for (k = 1; k < steps+1; k++)
{
x = x + x_inc;
y = y + y_inc;
canvas->Pixels[floor(x+0.5)][floor(y+0.5)];
}
}
//-------------------------------------------------------------------------------
Ahora escribimos la segunda sobrecarga del método LineaDDA(), en la cual incluimos el color personalizado de la línea, el código es prácticamente el mismo:
void Graficos::LineaDDA(int x1, int y1, int x2, int y2, TColor color)
{
int dx, dy, steps, k;
float x_inc, y_inc, x, y;
dx = x2 - x1;
dy = y2 - y1;
if (abs(dx) > abs(dy))
steps = abs(dx);
else
steps = abs(dy);
x_inc = (float)dx/steps;
y_inc = (float)dy/steps;
x = x1;
y = y1;
canvas->Pixels[floor(x+0.5)][floor(y+0.5)] = color;
for (k = 1; k < steps+1; k++)
{
x = x + x_inc;
y = y + y_inc;
canvas->Pixels[floor(x+0.5)][floor(y+0.5)] = color;
}
}
//-------------------------------------------------------------------------------
Y con eso tenemos lista nuestra clase Graficos desde la cual podremos dibujar nuestra Linea DDA.
Ahora nos vamos al archivo .cpp de nuestra aplicación y ahí declaramos de manera global las siguientes variables, las cuales son 4 enteros que serán las coordenadas iniciales y finales de la línea en el momento de dibujarla de manera dinámica con el mouse, y un puntero de la clase Graficos para poder llamar los métodos LineaDDA():
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
Graficos *graf;
int x1, y1, x2, y2;
//---------------------------------------------------------------------------
Ahora en el evento en el que llamamos a la creación de nuestra Forma colocamos el siguiente código para inicializar los valores y crear el objeto "graf":
_fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
graf = new Graficos(PaintBox1->Canvas);
ComboBox1->ItemIndex = 0;
}
//---------------------------------------------------------------------------
Bien ahora abrimos el evento OnMouseDown del PaintBox que habíamos colocado en la Forma y en el evento escribimos el siguiente código:
void __fastcall TForm1::PaintBox1MouseDown(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{
if (Shift.Contains(ssLeft))
{
x1 = X;
y1 = Y;
}
}
//---------------------------------------------------------------------------
Ahora abrimos el evento OnMouseMove también del PaintBox y escribimos el código siguiente:
void __fastcall TForm1::PaintBox1MouseMove(TObject *Sender,
TShiftState Shift, int X, int Y)
{
if(Shift.Contains(ssLeft))
PaintBox1MouseUp(Sender, mbLeft, Shift, X, Y);
}
//---------------------------------------------------------------------------
Y finalmente abrimos el evento OnMouseUp del PaintBox y tecleamos el código que tenemos a continuación:
void __fastcall TForm1::PaintBox1MouseUp(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{
if(Button==mbLeft)
{
x2 = X;
y2 = Y;
Refresh();
graf->LineaDDA(x1, y1, x2, y2, Canvas->Pen->Color);
Edit1->Text = x1;
Edit2->Text = y1;
Edit3->Text = x2;
Edit4->Text = y2;
}
}
//---------------------------------------------------------------------------
Ahora comenzamos con la codificación de los componentes primero hacemos doble clic en el SpeedButton y escribimos el siguiente código:
void __fastcall TForm1::SpeedButton1Click(TObject *Sender)
{
if (ColorDialog1->Execute())
Canvas->Pen->Color = ColorDialog1->Color;
}
//---------------------------------------------------------------------------
Ahora damos doble clic en el Button1 y escribimos el siguiente código:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
PaintBox1->Refresh();
graf->LineaDDA(Edit1->Text.ToInt(), Edit2->Text.ToInt(), Edit3->Text.ToInt(), Edit4->Text.ToInt(), Canvas->Pen->Color);
}
//---------------------------------------------------------------------------
Ahora damos clic en el Button2 el cual iniciará unos efectos que vamos a formar con las líneas que vayamos dibujando, dependiendo de cual de los efectos hayamos elegido en el ComboBox1:
void __fastcall TForm1::Button2Click(TObject *Sender)
{
switch(ComboBox2->ItemIndex)
{
case 0:
Timer1->Enabled = true;
break;
case 1:
Timer2->Enabled = true;
break;
case 2:
Timer3->Enabled = true;
break;
case 3:
Timer4->Enabled = true;
break;
case 4:
Timer5->Enabled = true;
break;
case 5:
Timer6->Enabled = true;
break;
case 6:
Timer7->Enabled = true;
break;
}
}
//---------------------------------------------------------------------------
Después damos doble clic en el Button3 el cual detendrá el efecto con líneas que este seleccionado en el ComboBox1:
void __fastcall TForm1::Button3Click(TObject *Sender)
{
switch(ComboBox2->ItemIndex)
{
case 0:
Timer1->Enabled = false;
break;
case 1:
Timer2->Enabled = false;
break;
case 2:
Timer3->Enabled = false;
break;
case 3:
Timer4->Enabled = false;
break;
case 4:
Timer5->Enabled = false;
break;
case 5:
Timer6->Enabled = false;
break;
case 6:
Timer7->Enabled = false;
break;
}
}
//---------------------------------------------------------------------------
Ahora vamos a codificar los efectos que se irán formando cada efecto estará dentro de un Timer. El primero de nuestros efectos formará un cono dibujando líneas que generen ese efecto, el código es el siguiente:
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
int angle;
TColor color;
float x_pos, y_pos;
int x_center = PaintBox1->ClientWidth/4;
int y_center = PaintBox1->ClientHeight/4;
int length = 80;
for (angle=0; angle<360; angle++)
{
color = (TColor)random(52548899);
y_pos = y_center+length*sin(angle/180.0*M_PI);
x_pos = x_center+length*cos(angle/180.0*M_PI);
graf->LineaDDA(x_center, y_center, x_pos+80, y_pos+180, color);
}
}
//---------------------------------------------------------------------------
El segundo efecto formará un circulo dibujando líneas, el código lo tenemos a continuación:
void __fastcall TForm1::Timer2Timer(TObject *Sender)
{
int angle;
TColor color;
float x_pos, y_pos;
int x_center = PaintBox1->ClientWidth/4;
int y_center = PaintBox1->ClientHeight/4;
int length = 80;
for (angle=0; angle<360; angle++)
{
color = (TColor)random(52548899);
y_pos = y_center+length*sin(angle/180.0*M_PI);
x_pos = x_center+length*cos(angle/180.0*M_PI);
graf->LineaDDA(x_center, y_center, x_pos, y_pos, color);
}
}
//---------------------------------------------------------------------------
El tercero de los efecto es un cuadrado dibujado por líneas que girará y formara el efecto de 2 círculos circuncéntricos:
void __fastcall TForm1::Timer3Timer(TObject *Sender)
{
float x_pos, y_pos, x_pos2, y_pos2, x_pos3, y_pos3;
int x_center = PaintBox1->ClientWidth/2;
int y_center = PaintBox1->ClientHeight/2;
int length = 120;
int angle;
TColor color;
double hypot = sqrt(length * length + length * length);
for (angle = 0; angle < 360; angle++)
{
color = (TColor)random(52548899);
y_pos = (float)(y_center + length * sin(angle / 180.0 * M_PI));
x_pos = (float)(x_center + length * cos(angle / 180.0 * M_PI));
y_pos2 = (float)(y_center + hypot * sin((angle + 45) / 180.0 * M_PI));
x_pos2 = (float)(x_center + hypot * cos((angle + 45) / 180.0 * M_PI));
graf->LineaDDA(x_pos, y_pos, x_pos2, y_pos2, color);
y_pos3 = (float)(y_center + length * sin((angle + 90) / 180.0 * M_PI));
x_pos3 = (float)(x_center + length * cos((angle + 90) / 180.0 * M_PI));
graf->LineaDDA(x_pos2, y_pos2, x_pos3, y_pos3, color);
graf->LineaDDA(x_pos3, y_pos3, x_center, y_center, color);
Sleep(10);
}
}
//---------------------------------------------------------------------------
El efecto siguiente dará la impresión de estar dibujando un diamante mediante líneas:
void __fastcall TForm1::Timer4Timer(TObject *Sender)
{
int angle;
TColor color;
float x_pos, y_pos;
int x_center = PaintBox1->ClientWidth/4;
int y_center = PaintBox1->ClientHeight/4;
int length = 80;
for (angle=0; angle<360; angle++)
{
color = (TColor)random(52548899);
y_pos = y_center+length*sin(angle/180.0*M_PI)*3;
x_pos = x_center+length*cos(angle/180.0*M_PI*10);
graf->LineaDDA(x_pos+130, x_center+130, y_center+130, y_pos+130, color);//(x_center, y_center, x_pos, y_pos, color);
}
}
//---------------------------------------------------------------------------
El siguiente efecto son líneas que se van dibujando desde el centro hacia las esquinas para terminar dibujando un cuadrado con un círculo inscrito dentro:
void __fastcall TForm1::Timer5Timer(TObject *Sender)
{
Refresh();
int i,x,posx,posy;
float y;
posx=PaintBox1->ClientWidth-48;
posy=PaintBox1->ClientHeight-48;
TColor color;
for(i=0,x=posx,y=posy-200;i<21;x-=15,y+=10,i++)
{
color = (TColor)random(52548899);
graf->LineaDDA(x,posy,posx,y, color);
Sleep(10);
}
posx=44;
posy=PaintBox1->ClientHeight-48;
for(i=0,x=posx,y=posy-200;i<21;x+=15,y+=10,i++)
{
color = (TColor)random(52548899);
graf->LineaDDA(x,posy,posx,y,color);
Sleep(10);
}
posx=44;
posy=44;
for(i=0,x=posx,y=posy+200;i<21;x+=15,y-=10,i++)
{
color = (TColor)random(52548899);
graf->LineaDDA(x,posx,posy,y,color);
Sleep(10);
}
posx=PaintBox1->ClientWidth-48;
posy=44;
for(i=0,x=posx,y=posy+200;i<21;x-=15,y-=10,i++)
{
color = (TColor)random(52548899);
graf->LineaDDA(x,posy,posx,y,color);
Sleep(10);
}
}
//---------------------------------------------------------------------------
El penúltimo de los efectos dibujará líneas verticales y horizontales para que quede finalmente una cuadrícula:
void __fastcall TForm1::Timer6Timer(TObject *Sender)
{
Refresh();
TColor color;
color = (TColor)random(52548899);
for(int x = 0; x < Width; x += 20)
{
graf->LineaDDA(x, 0, x, Height, color);
Sleep(10);
}
for(int y = 0; y < Height; y += 20)
{
graf->LineaDDA(0, y, Width, y, color);
Sleep(10);
}
}
//---------------------------------------------------------------------------
El último de los efectos dibujará dos 2 círculos a la vez para terminar formando un efecto de un ovalo al centro rodeado de líneas a su alrededor:
void __fastcall TForm1::Timer7Timer(TObject *Sender)
{
float x_pos, y_pos, x_pos2, y_pos2, x_pos3, y_pos3;
int x_center = PaintBox1->ClientWidth/2-50;
int y_center = PaintBox1->ClientHeight/2;
int length = 120;
int angle;
TColor color;
double hypot = sqrt(length * length + length * length);
for (angle = 0; angle < 360; angle++)
{
color = (TColor)random(52548899);
y_pos = (float)(y_center + length * sin(angle / 180.0 * M_PI));
x_pos = (float)(x_center + length * cos(angle / 180.0 * M_PI));
y_pos2 = (float)(y_center + hypot * sin((angle + 45) / 180.0 * M_PI));
x_pos2 = (float)(x_center + hypot * cos((angle + 45) / 180.0 * M_PI));
graf->LineaDDA(x_pos+100, y_pos, x_pos2, y_pos2, color);
y_pos3 = (float)(y_center + length * sin((angle + 90) / 180.0 * M_PI));
x_pos3 = (float)(x_center + length * cos((angle + 90) / 180.0 * M_PI));
graf->LineaDDA(x_pos2+100, y_pos2, x_pos3, y_pos3, color);
Sleep(10);
}
}
//---------------------------------------------------------------------------
Y con eso terminamos amigos, este programa estuvo un poco largo y medio complicado pero finalmente lo terminamos. Ahora solo compilamos y corremos nuestra aplicación y podemos dibujar la línea dando clic y arrastrando el mouse. Y podemos dar clic al botón "Iniciar" para comenzar a ver nuestros efectos, nuestra aplicación debería verse de la siguiente manera:


Y bien amigos pues eso es todo por esta ocasión espero que hayan comprendido muy bien la aplicación del día de hoy ya que con ella continuaremos trabajando para realizar aplicaciones mas adelante. Por lo pronto me despido de ustedes y los espero en la próxima ocasión para continuar practicando. Hasta la vista chavos!.
No hay comentarios:
Publicar un comentario