How to create a pan from composite image in Delphi
Asked Answered
E

2

6

I'm kind of new to delphi graphics methods and I'm stucked at creating a ... viewport , thats how I call it while i was doing it for a project. I'm sorry I can't provide any code for it but I'm stuck at the logic part , searching google pointed me to some OnPaint , Draw methods. But those are not what I'm trying to accomplish, since I have , for example:

  1. A 1600x1000 background image anchored to the client's top/bottom/right and left.
  2. Multiple TImage elements placed at set x/y coords.
  3. A "hotspot" like a map element in HTML where I can set the clickable areas (for the images i'm placing at step 2)
  4. No zoom needed.
  5. And the most important thing, while the background is dragged, those TImages placed on top of the background need to be dragged too.

My logic (in HTML/jQuery) was to create a #viewportBinder (which was the div i was dragging, transparent bg), followed by another div inside it called #viewtown (1600x1000, the background) which contains the divs (those TImages) placed at set coordinates in CSS.

So when I am dragging the viewportBinder, jQuery sets the new x/y on the #viewport. Implicitly, the divs (TImages) inside the #viewport are moving because the parent was positioned relative.

Does anybody have any experience with this kind of project ? Any snippet of code ?

To be more specific i'll give you my html example of what i accomplised and what i want to port into Delphi code: http://www.youtube.com/watch?v=9iYqzvZFnGA

Sorry if i'm not clear enough, i have no starting point since I have no experience with this in delphi at all. (using RAD Studio 2010)

Enlarge answered 2/1, 2013 at 16:50 Comment(2)
Is your problem just in dragging buildings according to terrain?Prolate
The "terrain" is not resizeable , it's just 1600x1000, so i need to make that draggable in the client (which may be smaller than 1600x1000) implicitly, max constraints to client are 1600x1000 and i need that terain (background) to be draggable around the client form. No resize nothing needed.Enlarge
W
6

A very short example how it could be realized in an easy way.

You would use a Paintbox for painting, 1 Backimage, an array of Records with info and transparent pngimages.

Canvas can be manipulated in offset/zoom/rotation. Moving and hitdetection would happen in mousedown and mousemove.

It's not complete, but might give you an idea how it could be done.

[delphi]
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls,PNGImage, StdCtrls;

type
  TBuilding=Record   // record for building informations
    Pos:TPoint;
    PNGImage:TPngImage;
    // what ever needed
  End;

  TBuildingArray=Array of TBuilding; // array of buildings

  TForm1 = class(TForm)
    PaintBox1: TPaintBox;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure PaintBox1Paint(Sender: TObject);
    procedure PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure Button1Click(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private-Deklarationen }
    FXoffs,FYOffs,FZoom:Double; // offset and zoom for painting
    FMouseDownPoint:TPoint;
    FBackGroundPNG:TPNGImage;
    FBuildingArray:TBuildingArray;
    procedure Check4Hit(X, Y: Integer);
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation
uses Math;
{$R *.dfm}


Procedure SetCanvasZoomAndRotation(ACanvas:TCanvas;Zoom:Double;Angle:Double;CenterpointX,CenterpointY:Double);
var
    form : tagXFORM;
    Winkel:Double;
begin
      Winkel := DegToRad(Angle);
      SetGraphicsMode(ACanvas.Handle, GM_ADVANCED);
      SetMapMode(ACanvas.Handle,MM_ANISOTROPIC);
      form.eM11 := Zoom * cos( Winkel);
      form.eM12 := Zoom *Sin( Winkel)  ;
      form.eM21 := Zoom * (-sin( Winkel));
      form.eM22 := Zoom * cos( Winkel) ;
      form.eDx := CenterpointX;
      form.eDy := CenterpointY;
      SetWorldTransform(ACanvas.Handle,form);
end;

Procedure ResetCanvas(ACanvas:TCanvas);
begin
   SetCanvasZoomAndRotation(ACanvas , 1, 0, 0,0);
end;


procedure TForm1.FormCreate(Sender: TObject);
var
 Path:String;
 i:Integer;
begin
   FZoom := 1;
   DoubleBuffered := true;
   Path := ExtractFilePath(Paramstr(0));
   FBackGroundPNG:=TPNGImage.Create;
   FBackGroundPNG.LoadFromFile(Path + 'infect.png');
   SetLength(FBuildingArray,3);
   for I := 0 to High(FBuildingArray)  do
      begin
         FBuildingArray[i].PNGImage := TPngImage.Create;
         FBuildingArray[i].PNGImage.LoadFromFile(Path + Format('B%d.png',[i]));
         FBuildingArray[i].Pos.X := I * 300;
         FBuildingArray[i].Pos.Y := Random(1000);
      end;

end;
procedure TForm1.FormDestroy(Sender: TObject);
var
 i:Integer;
begin
   for I := 0 to High(FBuildingArray)  do
      begin
         FBuildingArray[i].PNGImage.Free;
      end;
   FBackGroundPNG.Free;
end;


procedure TForm1.Button1Click(Sender: TObject);
begin
  if FZoom=0.5 then FZoom := 1 else FZoom := 0.5;
  PaintBox1.Invalidate;
end;

procedure TForm1.Check4Hit(X,Y:Integer);
var
 i,Index:Integer;
 R:TRect;
 P:TPoint;
begin
   index := -1;
   for I := 0 to High(FBuildingArray)  do
      begin
         R := Rect(FBuildingArray[i].Pos.X,FBuildingArray[i].Pos.Y
                    ,FBuildingArray[i].Pos.X + FBuildingArray[i].PNGImage.Width
                    ,FBuildingArray[i].Pos.Y + FBuildingArray[i].PNGImage.Height);
         P := Point(Round((x - FXOffs)/FZoom) ,Round((y - FYOffs)/FZoom));
         if PtInRect(R,P) then Index := i;
      end;
   if index > -1 then
      begin
        Caption := Format('Last hit %d',[index]);
      end
   else Caption := 'No Hit';
end;

procedure TForm1.PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
   Check4Hit(X,Y);
   FMouseDownPoint.X := X;
   FMouseDownPoint.Y := Y;
end;

procedure TForm1.PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
   if ssLeft in Shift then
      begin
         FXoffs := -( FMouseDownPoint.X - X) ;
         FYoffs := -( FMouseDownPoint.Y - Y) ;
         if FXoffs>0 then FXoffs := 0;
         if FYoffs>0 then FYoffs := 0;
         PaintBox1.Invalidate;
      end;
end;

procedure TForm1.PaintBox1Paint(Sender: TObject);
var
 i:Integer;
begin
   SetCanvasZoomAndRotation(PaintBox1.Canvas,FZoom,0,FXoffs,FYOffs);
   PaintBox1.Canvas.Draw(0,0,FBackGroundPNG);
   for I := 0 to High(FBuildingArray)  do
      begin
         PaintBox1.Canvas.Draw(FBuildingArray[i].Pos.X,FBuildingArray[i].Pos.Y,FBuildingArray[i].PNGImage);
      end;
end;

end.

[/delphi]
Walcoff answered 2/1, 2013 at 21:48 Comment(2)
I'll give it a try and see what I can do. Please continue following the question. And thanks for now, I'll accept the answer as soon as I will see the idea coming to life (probably in an hour or so)Enlarge
For what's worth it, if someone has problems with the background (offsets) being reset to 0,0 while dragging, follow this: declar a tPoint : TPoint; under {public} section, then at the MouseDown event set tPoint.x := Trunc(FXoffs); and tPoint.y := Trunc(FYoffs); since we need to get the int out of an doulbe. Then at the MouseMove event, add tempx and tempy for their respective formulas. For example: FXoffs := -( FMouseDownPoint.X - X) + tempx; and FYoffs := -( FMouseDownPoint.Y - Y) + tempy; this way program knows where he left off the dragObject last time. Regards.Enlarge
P
0

Sorry, but for last several years i working with Lazarus instead of Delphi. But tis article will be informative: http://wiki.lazarus.freepascal.org/Developing_with_Graphics#Create_a_custom_control_which_draws_itself About relative coordinates nothing to say - it is simple. About dragging: A long time ago in a galaxy far, far away.. that was something like:

// To start dragging
procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;
// To stop dragging
procedure WMLButtonUp(var Message: TWMLButtonUp); message WM_LBUTTONUP;
// To perform dragging
procedure WMMouseMove(var Message: TWMMouseMove); message WM_MOUSEMOVE;
Prolate answered 2/1, 2013 at 23:38 Comment(1)
Yes, well TPaintBox already has these as Events called OnMouseDown / OnMouseMove, but I'll consider bummi's answer since it's not that low level programming. I mean, why doing all the hard work when Embarcadero already implemented the "best" methods to accomplish something likfe this in the standard library. Thanks for your time though.Enlarge

© 2022 - 2024 — McMap. All rights reserved.