Wednesday, 29 July 2009

Jugando con Elipses en Delphi

Hoy os traigo una pequeña aplicación didáctica en delphi que dibuja elipses (mejor dicho círculos, pero utilizo el metodo elipse del TCanvas para dibujarlo) y detecta las colisiones entre estas arrastrando el mouse por encima. Observareis que es muy simple pero sirve para jugar un poco con el canvas y el ratón y ver que tipo de aplicaciones visuales podemos llegar a hacer con delphi. Al pasar el mouse por encima del objeto detecta su paso y muestra un color de colisión. Además al mover una de las elipses hacia otro objeto, se detecta la colisión entre estos y se frena el movimiento si dejarte mover el objeto. La detección es muy sencilla, mediante la posición del mouse se comprueba que la distancia de éste con el centro de cualquiera de las otras elipses sea mayor que la distancia del objeto a arrastrar y las otras elipses, un principio muy simple pero que funciona muy bien y sin tener que hacer cálculos desorbitados. Aquí os dejo la aplicación Thundax Draw Ellipses, y parte del código fuente y un pantallazo de la aplicación:


La clase TEllipse:

unit ThundaxEllipse;

interface

uses
    types, Graphics, Classes, Controls;

type
    TEllipse = class(TObject)
    private
        FborderColor: TColor;
        Flength: integer;
        FlineWidth: integer;
        FCollisionColor: TColor;
        FColor: TColor;
        Fposition: TPoint;
        FCanvas: TCanvas;
        FDrag: boolean;
        Fid: string;
        Fradium: integer;
        FCenter: TPoint;
        procedure SetborderColor(const Value: TColor);
        procedure SetCollisionColor(const Value: TColor);
        procedure SetColor(const Value: TColor);
        procedure Setlength(const Value: integer);
        procedure SetlineWidth(const Value: integer);
        procedure Setposition(const Value: TPoint);
        procedure SetDrag(const Value: boolean);
        procedure SetCenter(const Value: TPoint);
        procedure Setid(const Value: string);
        procedure Setradium(const Value: integer);
    public
        property position: TPoint read Fposition write Setposition;
        property id: string read Fid write Setid;
        property Drag: boolean read FDrag write SetDrag;
        property length: integer read Flength write Setlength;
        property Color: TColor read FColor write SetColor;
        property borderColor: TColor read FborderColor write SetborderColor;
        property lineWidth: integer read FlineWidth write SetlineWidth;
        property CollisionColor: TColor read FCollisionColor write SetCollisionColor;
        constructor Create(Canvas: TCanvas; center: TPoint; length: integer);
        function PointInside(x, y: integer): boolean;
        procedure SetMousePosition(x, y: integer);
        procedure Draw(x, y: integer);
        procedure EnableDrag(x, y: integer);
        property Center: TPoint read FCenter write SetCenter;
        property radium: integer read Fradium write Setradium;
    end;
implementation

{ TEllipse }

constructor TEllipse.Create(Canvas: TCanvas; center: TPoint; length: integer);
begin
    FCanvas := Canvas;
    Fposition := center;
    FLength := length;
    FDrag := false;
    FCenter := point(Fposition.x + (Flength div 2), Fposition.y + (Flength div 2));
    FRadium := FLength div 2;
end;

procedure TEllipse.Draw(x, y: integer);
begin
    if FDrag then
        SetMousePosition(x, y);
    if PointInside(x, y) then
        FCanvas.Brush.Color := FCollisionColor
else
        FCanvas.Brush.Color := FColor;

    FCanvas.Pen.Color := FborderColor;
    FCanvas.Pen.Width := 1;
    FCanvas.Ellipse(Fposition.x, Fposition.y, Fposition.x + FLength, Fposition.y + FLength);
    FCanvas.Font.Color := clWhite;
    FCanvas.TextOut(Fposition.x + (Flength div 2) - 5, Fposition.y + (Flength div 2) - 7, FId);
end;

procedure TEllipse.EnableDrag(x, y: integer);
begin
    if PointInside(x, y) then
        Fdrag := true;
end;

function TEllipse.PointInside(x, y: integer): boolean;
var
    x0, y0, a, b: integer;
    point2: TPoint;
begin
    point2 := Point(Fposition.x + Flength, FPosition.y + Flength);
    x0 := (Fposition.x + point2.x) div 2;
    y0 := (Fposition.y + point2.y) div 2;
    a := (point2.x - Fposition.x) div 2;
    b := (point2.y - Fposition.y) div 2;
    result := (Sqr((x - x0) / a) + Sqr((y - y0) / b) <= 1);
end;

procedure TEllipse.SetborderColor(const Value: TColor);
begin
    FborderColor := Value;
end;

procedure TEllipse.SetCenter(const Value: TPoint);
begin
    FCenter := Value;
end;

procedure TEllipse.SetCollisionColor(const Value: TColor);
begin
    FCollisionColor := Value;
end;

procedure TEllipse.SetColor(const Value: TColor);
begin
    FColor := Value;
end;

procedure TEllipse.SetDrag(const Value: boolean);
begin
    FDrag := Value;
end;

procedure TEllipse.Setid(const Value: string);
begin
    Fid := Value;
end;

procedure TEllipse.Setlength(const Value: integer);
begin
    Flength := Value;
end;

procedure TEllipse.SetlineWidth(const Value: integer);
begin
    FlineWidth := Value;
end;

procedure TEllipse.SetMousePosition(x, y: integer);
begin
    FPosition := point(x - (Flength div 2), y - (Flength div 2));
    FCenter := point(Fposition.x + (Flength div 2), Fposition.y + (Flength div 2));
end;

procedure TEllipse.Setposition(const Value: TPoint);
begin
    Fposition := Value;
end;

procedure TEllipse.Setradium(const Value: integer);
begin
    Fradium := Value;
end;

end.


El cálculo de las colisiones:

procedure TForm3.Image1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var
    i, j: integer;
    Collide: boolean;
    textCollision: string;
    Collisions: array[1..20] of integer;
begin
    DrawRectangle(clWhite, clWhite);
    Memo1.lines.Clear;
    textCollision := '';
    Collide := false;
    for i := 1 to 20 do
        Collisions[i] := -1;

    for i := 1 to number do
    begin
        for j := 1 to number do
            if Ellipses[i].drag and
                distanciaMouse(Point(x, y), Ellipses[i].center, Ellipses[j].center) and
                Collision(ellipses[i], ellipses[j]) then
            begin
                Memo1.lines.add('Collide True ->' + inttostr(i) + ' ' + inttostr(j));
                Collide := true;
                Collisions[i] := i;
            end;
    end;

    for i := 1 to number do
    begin
        if Collisions[i] = -1 then
            ellipses[i].Draw(x, y)
        else
            ellipses[i].draw(ellipses[i].center.x, ellipses[i].center.y);
        Memo1.lines.add('Collision ' + inttostr(Collisions[i]));
    end;

    if collide then
        textCollision := 'Collision!';

    StatusBar1.Panels[0].Text := 'x : ' + inttostr(x) + ' y : ' + inttostr(y) + ' ' + textCollision;
end;

function TForm3.DistanciaMouse(p1, p2, p3: TPoint): boolean;
begin
    result := false;
    if (p2.x <> p3.x) and (p2.y <> p3.y) then
        result := (distancia(p1, p3) < distancia(p2, p3))
end;

function TForm3.Collision(p1, p2: TEllipse): boolean;
begin
    result := false;
    if p1 <> p2 then
        result := distancia(p1.center, p2.center) < (2 * (p1.radium));
end;

function TForm3.distancia(p1, p2: TPoint): double;
begin
    result := sqrt(sqr(p1.x - p2.x) + sqr(p1.y - p2.y));
end;


0 comments:

Post a Comment