Saturday, 1 November 2008

Jugando con TCanvas: Conectando Objetos en Delphi (Parte I).


A quien no le gustan los componentes visuales, que muestran objetos conectados entre si?. Muchas veces me he preguntado como empezaría a crear una aplicación de ese estilo, y como podría plasmar esa idea en la pantalla. Pasa por ejemplo con la representación de árboles:


A veces es más interesante mostrar los datos de esta manera, que no con un simple TreeView. Es mucho más visual de esta manera, pero a la vez más costosa de representar, ya que dependes de un espacio de trabajo, y habría que montar todo un entorno visual para hacerlo posible.

¿Y como se haría esto en delphi?

Bien, la respuesta sencilla es: jugando con el TCanvas.
Lo general, sería crear herramientas utilizando OpenGL u otras tecnologías para trabajar con gráficos, pero intentaré mostrar como he plasmado yo esa idea.

Lo que yo he hecho, es heredar de la Clase TImage, y en esta subclase, preparar mis propiedades.


type
TArrayPoint = array of TPoint;

TItemImage = class(TImage)
private
Position: TArrayPoint;
Id: integer;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy(); override;
procedure CalculePoints();
end;




El resultado de la aplicación es el siguiente:



El programa dibuja cajas de conexión. Cada caja tiene 4 puntos de conexión, que están ubicados en las zonas medias de cada línea:

El cálculo de los puntos es muy sencillo, y cada vez que se mueve el objeto se recalculan:


procedure TItemImage.CalculePoints();
begin
Position[0].X := (Self.Width) div 2 + Self.left;
Position[0].Y := Self.Top;
Position[1].X := Self.Width + Self.left;
Position[1].Y := Self.top + (Self.height) div 2;
Position[2].X := (Self.Width) div 2 + Self.left;
Position[2].Y := Self.top + Self.height;
Position[3].X := Self.left;
Position[3].Y := Self.top + (Self.height) div 2;
end;


Por lo tanto, para hacer posible la conexión entre los puntos, debemos buscar la distancia más corta entre estos puntos y hacer la conexión entre ellos. También hacer que cuando un cuadro se mueva, la conexión también cambie. Para buscar la distancia más corta solo tenemos que hacer:



procedure TForm1.RecuperaPuntsLinies(Image1: TItemImage; Image2: TItemImage; var P1: TPoint; var P2: TPoint);
procedure BuscarMesPetit(P1, P2: TArrayPoint; var R1, R2: TPoint);
var
dist, calc: double;
I: Integer;
j: Integer;
begin
dist := 0.0;
for I := 0 to Length(P1) - 1 do
begin
for j := 0 to Length(P2) - 1 do
begin
calc := Sqrt(sqr(P2[j].x - P1[i].x) + sqr(P2[j].y - P1[i].y));
if (dist = 0) or (calc < dist) then
begin
dist := calc;
R1.X := P1[i].X;
R1.Y := P1[i].Y;
R2.X := P2[j].X;
R2.Y := P2[j].Y;
end;
end;
end;
end;
begin
BuscarMesPetit(Image1.Position, Image2.Position, P1, P2);
end;


Este pequeño algoritmo recupera la distáncia geométrica entre dos puntos, y devuelve los puntos más cercanos entre las dos imágenes:



Podeis encontrar el ejecutable aqui: Object Linking.

El tema de las conexiones, lo he planteado diferente. Guardo un pequeño objeto con parejas de connexiones. És decir, cuando con el mouse selecciono uno de los objetos haciendo botón derecho, aparece un popup menú para crear la conexión. Una vez emparejo 2, la conexión se crea y se hace visible:


Al hacer click sobre cada objeto, se hacen visibles sus propiedades en el Statusbar de la aplicación. Luego estas propiedades se pueden representar en cualquier componente para poderlas editar o representarlas de otra forma. Aún está en fase de desarrollo, y tengo que comprobar si hay mejor forma de hacer esto o no, ya sea en delphi o en otros lenguajes.


0 comments:

Post a Comment