Solved

Drawing an arch

Posted on 2004-09-24
19
526 Views
Last Modified: 2010-04-05
Hi All,
I have a JPG image of a map and a gif of a little airplane with black being transparent. I want to give the impression that the plane takes off from one city and lands in another. How do I move the plane as well as draw a red 3px antialiased line as the "flight" path between two citis of the map? The flight path should slightly curve up to simulate takeoff and then back down for landing.

The plane should move as the "lead" of the flight path.

Thank you so much!!

P.S. I just want a function to which I can pass four parameters city1x/y and city2x/y
0
Comment
Question by:maxb
  • 9
  • 9
19 Comments
 
LVL 9

Expert Comment

by:ginsonic
Comment Utility
0
 
LVL 33

Expert Comment

by:Slick812
Comment Utility
Hello  maxb, I can sort of see what you want to do, but to have an animation (moving plane) along an Arc is NOT an easy thing to do. What if the first city is directly above the second city? Or the second city is to the left of the first city, does your plane fly backwards? I can do code to make an Arc between to any two points, but the animation may need some more info from you about how you want to do it.
0
 

Author Comment

by:maxb
Comment Utility
What if it was just a straight line? Would it be easier? The plane would always face the desitnation city, I could make like 8 versions with different possible angles?
0
 
LVL 33

Expert Comment

by:Slick812
Comment Utility
well,  straight line sprite movement would be easier, but the arc movement  can be done, , , as to the "Plane" image direction, if you are artistic and can do 8 bitmaps with the plane pointing in the 8 compass directions, then you can use those 8 bitmaps in an array, but you will need to determine which plane to use, do you have much trigonometry experience?

I guess you do not have much experience with animation? I have some code that uses a single plane bitmap, and moves the plane, in an arc, from one point to another on a TPaintBox, it also draws the Arc in red behind the plane. I could post it if your want to see it.
0
 

Author Comment

by:maxb
Comment Utility
Anything would help at this pont, I really have no animation experience. I'm considering the DelphiX components....
0
 
LVL 33

Accepted Solution

by:
Slick812 earned 250 total points
Comment Utility
?????  I must ask,  why would you want to use the DirectX components. ? ?

Here is some code that to me seems to do what you have described. I have done a Circular movement animation before, so I had some code already, otherwize this would have been to much to do. . .

I do NOT use a JPG map image, or a GIF plane image, if you do use those jpg and gif images you will need to convert them to the Map1Bmp TBitmap and the PlaneBmp TBitmap using the Assign method. . . . I have two things on the form a Speed button and a TPaintBox.
I create all of the Global TBitmaps (Map1Bmp, ArcBmp, PlaneBmp, AniBmp) in the FormCreate. . . I create the "Plane" image by line drawing a plane on the Bitmap, and I just use a checker grid image for the Map, but you can use any Bitmap that you need, although You may want to keep the plane bitmap small, I use a 42x42 pixel size for the plane. I only have two plane directions, (image for plane) left to right, and right to left.
This seems to work OK for the points that I tested it for, But if your citys (Start and Finish) point locations are near the edge of Map1Bmp then the plane may "Fly" OFF of the paint box and not be entirely visible, so if this is a problem for you, then you may want to add a "Border" space to the Map1Bmp, to allow the plane to fly off of the regular map and into the border space.


code for PlaneFly -



type
  TPosResult = (prFly, prOut, prShort);

  TForm1 = class(TForm)
    sbut_FlyPlane: TSpeedButton;
    Timer8: TTimer;

  private
    { Private declarations }
    Map1Bmp, ArcBmp, PlaneBmp, AniBmp: TBitmap;
    PixelMov: Integer;
    Start, Finish, CenterArc, Mover: TPoint;
    ArcRad, LenArc: Extended;
    Rev, FinalT: Boolean;
    ArcRect: TRect;

    function FlyPlane(StartPos, FinishPos: TPoint): TPosResult;


var
  Form1: TForm1;

implementation

{$R *.DFM}

uses math;



procedure TForm1.FormCreate(Sender: TObject);
var
i: Integer;
begin
PlaneBmp := TBitmap.Create;
// PlaneBmp is the base Image for your Plane
// You will probally load your plane image from a file or GIF
// you will need to convert your GIF to PlaneBmp maybe -
//  PlaneBmp.Assign(PlaneGIF);
// then you will NOT need the code below for drawing a Plane
PlaneBmp.Canvas.Brush.Color := 0;
PlaneBmp.Width := 42;
PlaneBmp.Height := 42;

// + + + + + + + + + + + + + + + + + + + + + + +
// the following code just draws something to represent a Plane Image
PlaneBmp.Canvas.Pen.Color := $E8DFDF;
PlaneBmp.Canvas.Pen.Width := 7;
PlaneBmp.Canvas.MoveTo(12,19);
PlaneBmp.Canvas.LineTo(21,10);

PlaneBmp.Canvas.Pen.Color := $CBCBCB;
PlaneBmp.Canvas.Pen.Width := 8;
PlaneBmp.Canvas.MoveTo(3,21);
PlaneBmp.Canvas.LineTo(27,21);
PlaneBmp.Canvas.Pen.Width := 6;
PlaneBmp.Canvas.LineTo(38,21);
PlaneBmp.Canvas.Pen.Color := $E8DFDF;
PlaneBmp.Canvas.Pen.Width := 7;
PlaneBmp.Canvas.MoveTo(12,23);
PlaneBmp.Canvas.LineTo(21,32);
PlaneBmp.Canvas.Pen.Width := 4;
PlaneBmp.Canvas.MoveTo(37,18);
PlaneBmp.Canvas.LineTo(41,12);
PlaneBmp.Canvas.Pen.Color := clWhite;
PlaneBmp.Canvas.Pen.Width := 1;
PlaneBmp.Canvas.MoveTo(3,17);
PlaneBmp.Canvas.LineTo(28,17);
PlaneBmp.Canvas.Pen.Color := $908080;
PlaneBmp.Canvas.Pen.Width := 2;
PlaneBmp.Canvas.MoveTo(1,19);
PlaneBmp.Canvas.LineTo(5,19);
for i := 0 to 4 do
  begin
  PlaneBmp.Canvas.MoveTo(9+(i*4),21);
  PlaneBmp.Canvas.LineTo(11+(i*4),21);
  end;  // end of plane draw
// + + + + + + + + + + + + + + + + + + + + + +
//PlaneBmp.Transparent := True;

AniBmp := TBitmap.Create;
// AniBmp if the Bitmap used for the animation and is the same size as the PlaneBmp
AniBmp.Assign(PlaneBmp);
AniBmp.Transparent := True;
// You must Set AniBmp TransParent to true

Map1Bmp := TBitmap.Create;
// Map1Mbp is the base Image for your MAP background
// You will probally load your MAP image from a file or JPG
// you will need to convert your JPG to Map1Bmp maybe -
//  PlaneBmp.Assign(MapJPG);
// then you will NOT need the code below for drawing a Map
Map1Bmp.Width := 449;
Map1Bmp.Height := 321;

Map1Bmp.Canvas.Brush.Color := $70B870;
Map1Bmp.Canvas.Rectangle(PaintBox4.ClientRect);
Map1Bmp.Canvas.Brush.Color := 0;
Map1Bmp.Canvas.Brush.Style := bsCross;
Map1Bmp.Canvas.Rectangle(PaintBox4.ClientRect);

// you will draw your animation on PaintBox4
// so set the width and height of paintBox4 to Map1Bmp
PaintBox4.Width := Map1Bmp.Width;
PaintBox4.Height := Map1Bmp.Height;

ArcBmp := TBitmap.Create;
// ArcBmp is the map bitmap copy that you draw the partial Arc on while flying
ArcBmp.Assign(Map1Bmp); // ArcBmp must be Same Size as Map1Bmp
end;


procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
// be sure to Free all of your Bitmaps
FreeAndNil(Map1Bmp);
FreeAndNil(PlaneBmp);
FreeAndNil(AniBmp);
FreeAndNil(ArcBmp);
end;


procedure TForm1.PaintBox4Paint(Sender: TObject);
begin
// not sure of the methods you will need?
// paints the background Map1Bmp on the paintbox
PaintBox4.Canvas.Draw(0,0, Map1Bmp);
end;


    // this function prepares the Bitmaps and gets the ARc rectangle
function TForm1.FlyPlane(StartPos, FinishPos: TPoint): TPosResult;
var
bmpRect: TRect;
Arc1, Arc2: TPoint;
Angle, Rad1, Len1: Extended;
begin
PixelMov := 12; // pixel distance to move plane each Timer8 event
Result := prFly;
bmpRect := Rect(0,0,Map1Bmp.width, Map1Bmp.Height);
if not PtInRect(bmpRect, StartPos) or not PtInRect(bmpRect, FinishPos) then
  begin
  // test to see if the points are inside the Map1Bmp bitmap
  // if not in Rect, then ERROR
  Result := prOut;
  Exit;
  end;

Start := StartPos;
Finish := FinishPos;

if Finish.x < Start.x then
  begin
  // test to see if the Finish is left or right of Start
  Rev := True;
  // if Finish is left of Start then Rev is True and reverse the Arc1, and Arc2
  // Rev tells the direction of movement, left to right, or right to left
  Arc1 := Finish;
  Arc2 := Start;
  end else
  begin
  Rev := False;
  Arc1 := Start;
  Arc2 := Finish;
  end;

Len1 := Sqrt(SumOfSquares([abs(Arc2.y-Arc1.y),abs(Arc2.x-Arc1.x)]));
// get the pixel distance between Start and Finish with Sqrt(SumOfSquares
if Len1 < PixelMov then
  begin
  // if pixel distance is less than pixelMove then ERROR
  Result := prShort;
  Exit;
  end;

Angle := 180.0 * (1 + ArcTan2(Arc2.y-Arc1.y, Arc2.x-Arc1.x) / Pi);
// get the angle for the Start and Finish points
ArcRad := DegToRad(Angle);
// save the Radian as the Global  ArcRad, which will be used in the Timer8 Event
CenterArc.x := Round(Arc1.x-Cos(ArcRad)* (Len1 / 2.0));
CenterArc.y := Round(Arc1.y-Sin(ArcRad)* (Len1 / 2.0));
// calculate the Center point of your line between Start and Finish in CenterArc
Rad1 := DegToRad(Angle+90.0);
// rotate your angle 90 degrees from the line between Start and Finish
CenterArc.x := Round(CenterArc.x-Cos(Rad1)*Len1);
CenterArc.y := Round(CenterArc.y-Sin(Rad1)*Len1);
{calculate the Center point of your Arc Rectangle in CenterArc, by moving
 then length between the Start and Finish (Len1), 90 degrees from the
 center Point of the line between Start and Finish, which is in CenterArc}
LenArc := Sqrt(SumOfSquares([abs(CenterArc.y-Arc1.y),abs(CenterArc.x-Arc1.x)]));
// get the radius of the Arc Circle in LenArc with Sqrt(SumOfSquares

// now plot your Rectangle for your Arc (ArcRect), using the radius and center point
ArcRect.Left := Round(CenterArc.x-LenArc);
ArcRect.Top := Round(CenterArc.y-LenArc);
ArcRect.Right := Round(CenterArc.x+LenArc);
ArcRect.Bottom := Round(CenterArc.y+LenArc);

// The ArcBmp will be used in the Timer8 event to erase the plane and draw the Arc
ArcBmp.Canvas.Draw(0,0, Map1Bmp); // COPY the Map1Bmp to the ArcBmp

// + + + + + + + + + + + + + + + + + + + + + + + + + + +
// I added the draw Ellipses below for my own start and finish visual reference
// but you will probally not need this for your map
ArcBmp.Canvas.Pen.Color := 0;
ArcBmp.Canvas.Pen.Width := 1;
ArcBmp.Canvas.Brush.Color := $E0F1;
ArcBmp.Canvas.Ellipse(Finish.x-6, Finish.y-6, Finish.x+6, Finish.y+6);
ArcBmp.Canvas.Brush.Color := $DF0000;
ArcBmp.Canvas.Ellipse(Start.x-6, Start.y-6, Start.x+6, Start.y+6);
// + + + + + + + + + + + + + + + + + + + + + + + + + + + +

// the AniBmp is the "Plane" image used in the Timer8 event for the animation
if Rev then
  AniBmp.Canvas.Draw(0,0, PlaneBmp)
  //BitBlt(AniBmp.canvas.handle,0, 0,AniBmp.width,AniBmp.height,
  //       PlaneBmp.canvas.handle,0,0,SRCCOPY)
  else // I need to Reverse the plane pic if it is NOT Rev
  begin
  StretchBlt(AniBmp.canvas.handle,0, 0,AniBmp.width,AniBmp.height,
      PlaneBmp.canvas.handle,AniBmp.width-1,0,-AniBmp.width,AniBmp.height,SRCCOPY);
    {by changing the source left and right positions, the bitmap is drawn reversed}
  AniBmp.Transparent := False;
  AniBmp.Transparent := True;
  end;

ArcBmp.Canvas.Pen.Color := $3333FF;
ArcBmp.Canvas.Pen.Width := 3;
// this pen color and width for ArcBmp is used in the Timer8 event for Arc Draw


Mover.x := 0; // Mover.x keeps track of the Movement Position of the plane in Timer8 event
// Mover.x will be increased or decreased in Timer8 event depending on direction of movement
Mover.y := Round(Len1 / PixelMov);  // Mover.y is the MAX Movement for Mover.x
if Rev then // if Rev then Mover.x will decrease, so negate Mover.y
  Mover.y := -Mover.y;
Timer8.Interval := 100; // Takeoff speed
FinalT := False; // FinalT will make the Final draw in Timer8 event, to erase the plane
Timer8.Enabled := True; // start the Timer to begin Flight
end;




procedure TForm1.Timer8Timer(Sender: TObject);
var
LinePnt: TPoint;
Angle, Rad1: Extended;
begin
if FinalT then // FinalT if the LAST draw to erase the Plane
  begin
  Timer8.Enabled := False; // Be sure to kill the timer
  ArcBmp.Canvas.Draw(0,0, Map1Bmp); // Renew the ArcBmp, COPY the Map1Bmp

  // + + + + + + + + + + + + + + + + + + + + + + +
  // Ellipses below are for my own reference , you can leave them out
  ArcBmp.Canvas.Pen.Color := 0;
  ArcBmp.Canvas.Pen.Width := 1;
  ArcBmp.Canvas.Draw(0,0, Map1Bmp);
  ArcBmp.Canvas.Brush.Color := $E0F1;
  ArcBmp.Canvas.Ellipse(Finish.x-6, Finish.y-6, Finish.x+6, Finish.y+6);
  ArcBmp.Canvas.Brush.Color := $DF0000;
  ArcBmp.Canvas.Ellipse(Start.x-6, Start.y-6, Start.x+6, Start.y+6);
  ArcBmp.Canvas.Pen.Color := $3333FF;
  ArcBmp.Canvas.Pen.Width := 3;
  // + + + + + + + + + + + +  ++ + + + + + + + + + + + + ++

  if Rev then  // Draw the final Arc from start to finish
    begin
    ArcBmp.Canvas.Arc(ArcRect.left,ArcRect.top,ArcRect.right,ArcRect.bottom,
                      Start.x, Start.y,Finish.x, Finish.y);
    PaintBox4.Canvas.Draw(0, 0, ArcBmp); // erases the plane and shows final Arc
    Exit;  // be sure to Exit
    end else
    begin
    ArcBmp.Canvas.Arc(ArcRect.left,ArcRect.top,ArcRect.right,ArcRect.bottom,
                      Finish.x, Finish.y,Start.x, Start.y);
    PaintBox4.Canvas.Draw(0, 0, ArcBmp);
    Exit;
    end;
  end; // FinalT


//calculate the move distance from Start to PixelMov*Mover.x
LinePnt.x := Round(Start.x-Cos(ArcRad)*(PixelMov*Mover.x));
LinePnt.y := Round(Start.y-Sin(ArcRad)*(PixelMov*Mover.x));
Angle := 180.0 * (1 + ArcTan2(LinePnt.y-CenterArc.y, LinePnt.x-CenterArc.x) / Pi);
// get the new Angle from center point to Line point
Rad1 := DegToRad(Angle);
LinePnt.x := Round(CenterArc.x-Cos(Rad1)*LenArc);
LinePnt.y := Round(CenterArc.y-Sin(Rad1)*LenArc);
// use the Arc radius in LenArc to get the plane Position in LinePnt

if Rev then
  begin
  if Mover.x < -1 then // DO NOT draw any Arc on the first movement
  ArcBmp.Canvas.Arc(ArcRect.left,ArcRect.top,ArcRect.right,ArcRect.bottom,
                    Start.x, Start.y, LinePnt.x, LinePnt.y);
  // Draw a partial Arc from Start to LinePnt
  end else
  if Mover.x > 1 then // DO NOT draw any Arc on the first movement
  ArcBmp.Canvas.Arc(ArcRect.left,ArcRect.top,ArcRect.right,ArcRect.bottom,
                    LinePnt.x, LinePnt.y, Start.x, Start.y);


PaintBox4.Canvas.Draw(0, 0, ArcBmp);  // Erase plane and show partial Arc
PaintBox4.Canvas.Draw(LinePnt.x-(AniBmp.width div 2),
                      LinePnt.y-(AniBmp.height div 2), AniBmp);
// you need to OffSet the Plane Draw by HALF of the AniBmp width and height

if not Rev then
  begin
  Inc(Mover.x);
  if Mover.x = 4 then Timer8.Interval := 42; // flight speed
  if Mover.x = Mover.y-4 then Timer8.Interval := 100; // landing speed
  if (Mover.x > Mover.y) or (Mover.x > 3000) then
    begin
    // you need to stop the flight when Mover.x is larger than Mover.y
    FinalT := True; // ends timer8
    Timer8.Interval := 560; // let plane rest over Finish for half second
    end;
  end else
  begin
  Dec(Mover.x);
  if Mover.x = -4 then Timer8.Interval := 42;
  if Mover.x = Mover.y+3 then Timer8.Interval := 100;
  if (Mover.x < Mover.y) or (Mover.x < -3000) then
    begin
    FinalT := True;
    Timer8.Interval := 560;
    end;
  end;

end;





procedure TForm1.sbut_FlyPlaneClick(Sender: TObject);
var
Takeoff, Landing: TPoint;

begin
// this button Click will do the FlyPlane function
TakeOff.x :=  241;
TakeOff.y := 252;

Landing.x := 41;
Landing.y := 60;

if FlyPlane(TakeOff, Landing) <> prFly then
  ShowMessage('ERROR - Plane did not Fly');
end;



 = = = = = = = = =  = = = = = =  = = = = = = = = = =  = = = = = =  = = =

I hope this can help you, I realize that this may seem like alot of code to do this, and you may not understand much of it, I have taken the time to try and add some helpful comments for you. . . Ask Questions if you need more info
0
 

Author Comment

by:maxb
Comment Utility
Wouldnt delphix allow me to have a map background and a plane sprite animation for smooth movement? Thank you so much for the code, I'll play with it tonight.
0
 
LVL 33

Expert Comment

by:Slick812
Comment Utility
oh yea, I did not mean that delphix could not do it, but with modern computers (above 800 MHz) and modern graphics cards (8 MBytes and up) you got pretty good graphics speed, if you know what you are doing, and can do some sprite movements that are good, but the delphix is a whole thing to learn, just for a little plane movement. . . . if you wanted a fully navigatable 3D environment, with  several 3D planes with laser guns and jet flames, that would explode in to smoke and flying wreckadge, then I can see going to delphix
0
 

Author Comment

by:maxb
Comment Utility
I had some issues with my delphi last night, it kept on saying it was missing some config file. I'll have to reinstall it tonight, im sorry for the delay.

On the map, I want to have an animated gif? to designate the possible destinations, is that prety simple to animate without flicker? I just want like a circle that sort of looks 3d that starts small and ends larger sort of pulsating. Any tips? :)
0
Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

 
LVL 33

Expert Comment

by:Slick812
Comment Utility
???
no tips, but for me, I would start out simple and learn how to do something basic (a NON animated plane movemant animation with out ARC or circular movement) and then try and add  new features and improvements as I learned what to do and how to do it. . . you ask -
" is that prety simple to animate without flicker"
I do NOT think it would be simple for someone who has not done animation.
0
 
LVL 33

Expert Comment

by:Slick812
Comment Utility
I remember that I gave some code for animation in another EE question at

http://www.experts-exchange.com/Programming/Programming_Languages/Delphi/Q_20718280.html

but the questioner did not understand it, my animation code in the first example that I give on this question might be a good place to start for a simple (or not) animation example
0
 

Author Comment

by:maxb
Comment Utility
LOL Thats hilarious. Thank you so much for your help, can't wait to get it all together hopefully tonight.
0
 

Author Comment

by:maxb
Comment Utility
the plane animation worked awesome, but I have an issue paqying $31 for a timer, is there another approach for the animation?
0
 
LVL 33

Expert Comment

by:Slick812
Comment Utility
I'm glad you got it to work, I am not sure about the price you mention  $31, maybe you are joking?
but I am not getting what you are saying (asking), If it's a joke , , I don't get it. . . . . . . .
0
 

Author Comment

by:maxb
Comment Utility
Oh... the only multimedia timer I was able to find was shareware and cost $31 to run without IDE... is there another?
0
 
LVL 33

Expert Comment

by:Slick812
Comment Utility
???
I can not understand what you are talking about?
There is no multimedia timer in the code for the plane animation, I use a plain old delphi  TTimer

If you are some how referring to the -

http://www.experts-exchange.com/Programming/Programming_Languages/Delphi/Q_20718280.html

question, it does use a   "multimedia timer"  , , but it is  IN THE CODE, it is NOT a separate component, it is called by API functions to setup, start and finish a multimedia timer event
0
 

Author Comment

by:maxb
Comment Utility
I'm sorry, i was talking about the other animation, not the plane one.

Delphi hiccups at TimeCaps: TTimeCaps; and says it has no idea what TTimeCaps is.
0
 
LVL 33

Expert Comment

by:Slick812
Comment Utility
OK, I forgot to place the

uses mmsystem;

in the code

TTimeCaps in in the mmsystem.pas source code
0
 

Author Comment

by:maxb
Comment Utility
Thank you SO much for all your help
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
Sending a Secure fax is easy with eFax Corporate (http://www.enterprise.efax.com). First, Just open a new email message.  In the To field, type your recipient's fax number @efaxsend.com. You can even send a secure international fax — just include t…
This video discusses moving either the default database or any database to a new volume.

728 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now