home *** CD-ROM | disk | FTP | other *** search
/ AMIGA PD 1 / AMIGA-PD-1.iso / Programme_zum_Heft / Programmieren / Kurztests / PascalPCQ / Examples / Bezier.p < prev    next >
Text File  |  1990-07-17  |  8KB  |  315 lines

  1. Program Bezier;
  2.  
  3. {  This program draws Bezier curves in the slow, simple, recursive
  4.    way.  When it first runs, you enter points in the window by
  5.    clicking the left mouse button.  After you double click on the
  6.    last point, the program begins drawing the curve.
  7.  
  8.    Since this is a highly recursive program, it's speed decreases
  9.    dramatically as you enter more points.  It can handle six or
  10.    seven points with reasonable speed, but if you enter ten you
  11.    might want to go see a movie while it draws.  It also uses
  12.    more stack space as you enter more points, but I hasn't blown
  13.    a 4k stack yet.
  14. }
  15.  
  16. {$I "Include:Exec/Interrupts.i" for Forbid and Permit }
  17. {$I "Include:Exec/Libraries.i" for Open and Close }
  18. {$I "Include:Exec/Ports.i" for the Message stuff }
  19. {$I "Include:Intuition/Intuition.i" for window & screen structures and functions }
  20. {$I "Include:Graphics/Pens.i" for drawing stuff }
  21. {$I "Include:Graphics/Text.i" for GText }
  22. {$I "Include:Graphics/Graphics.i"}
  23.  
  24. type
  25.     PointRec = Record
  26.         X, Y : Short;
  27.     end;
  28.     
  29. Const
  30.     w  : WindowPtr = Nil;
  31.     s  : Address   = Nil;
  32.  
  33. {  The following definitions mean that the start-up code will
  34.    not create an output window for this program if it is run
  35.    from the Workbench.  Therefore this program should NOT use
  36.    ReadLn and WriteLn. }
  37.  
  38.     StdInName : Address = Nil;
  39.     StdOutName: Address = Nil;
  40. Var
  41.     m  : MessagePtr;
  42.     rp : RastPortPtr;
  43.  
  44.     PointCount : Short;
  45.     Points : Array [1..15] of PointRec;
  46.  
  47.     t, tprime : Real;
  48.  
  49.     LastX, LastY : Short;
  50.  
  51. Procedure CleanUpAndDie;
  52. begin
  53.     if w <> Nil then begin
  54.     Forbid;
  55.     repeat until GetMsg(w^.UserPort) = Nil;
  56.     CloseWindow(w);
  57.     Permit;
  58.     end;
  59.     if s <> Nil then
  60.     CloseScreen(s);
  61.     CloseLibrary(GfxBase);
  62.     Exit(0);
  63. end;
  64.  
  65. Function OpenTheScreen() : Boolean;
  66. var
  67.     ns : NewScreenPtr;
  68. begin
  69.     new(ns);
  70.     with ns^ do begin
  71.     LeftEdge := 0;
  72.     TopEdge  := 0;
  73.     Width    := 640;
  74.     Height   := 200;
  75.     Depth    := 2;
  76.     DetailPen := 3;
  77.     BlockPen  := 2;
  78.     ViewModes := 32768;
  79.     SType     := CUSTOMSCREEN_f;
  80.     Font      := nil;
  81.     DefaultTitle := "Simple Bezier Curves";
  82.     Gadgets   := nil;
  83.     CustomBitMap := nil;
  84.     end;
  85.     s := OpenScreen(ns);
  86.     dispose(ns);
  87.     OpenTheScreen := s <> nil;
  88. end;
  89.  
  90. Function OpenTheWindow() : Boolean;
  91. var
  92.     nw : NewWindowPtr;
  93. begin
  94.     new(nw);
  95.     with nw^ do begin
  96.     LeftEdge := 0;
  97.     TopEdge := 11;
  98.     Width := 640;
  99.     Height := 189;
  100.  
  101.     DetailPen := -1;
  102.     BlockPen  := -1;
  103.     IDCMPFlags := CLOSEWINDOW_f;
  104.     Flags := WINDOWDRAG + WINDOWDEPTH + REPORTMOUSE_f +
  105.          WINDOWCLOSE + SMART_REFRESH + ACTIVATE;
  106.     FirstGadget := nil;
  107.     CheckMark := nil;
  108.     Title := "Close the Window to Quit";
  109.     Screen := s;
  110.     BitMap := nil;
  111.     MinWidth := 50;
  112.     MaxWidth := -1;
  113.     MinHeight := 20;
  114.     MaxHeight := -1;
  115.     WType := CUSTOMSCREEN_f;
  116.     end;
  117.  
  118.     w := OpenWindow(nw);
  119.     dispose(nw);
  120.     OpenTheWindow := w <> nil;
  121. end;
  122.  
  123. Procedure DrawLine;
  124. begin
  125.     Move(rp, Points[PointCount].X, Points[PointCount].Y);
  126.     Draw(rp, LastX, LastY);
  127. end;
  128.  
  129. Procedure GetPoints;
  130. var
  131.     LastSeconds,
  132.     LastMicros    : Integer;
  133.     IM : IntuiMessagePtr;
  134.     StoreMsg : IntuiMessage;
  135.     Leave : Boolean;
  136.     OutOfBounds : Boolean;
  137.     BorderLeft, BorderRight,
  138.     BorderTop, BorderBottom : Short;
  139.  
  140.     Procedure AddPoint;
  141.     begin
  142.     Inc(PointCount);
  143.     with Points[PointCount] do begin
  144.         X := StoreMsg.MouseX;
  145.         Y := StoreMsg.MouseY;
  146.     end;
  147.     with StoreMsg do begin
  148.         LastX := MouseX;
  149.         LastY := MouseY;
  150.         LastSeconds := Seconds;
  151.         LastMicros := Micros;
  152.     end;
  153.     SetAPen(rp, 2);
  154.     SetDrMd(rp, JAM1);
  155.     DrawEllipse(rp, LastX, LastY, 5, 3);
  156.     SetAPen(rp, 3);
  157.     SetDrMd(rp, COMPLEMENT);
  158.     DrawLine;
  159.     end;
  160.  
  161.     Function CheckForExit : Boolean;
  162.     {   This function determines whether the user wanted to stop
  163.     entering points.  I added the position tests because my
  164.     doubleclick time is too long, and I was too lazy to dig
  165.     out Preferences to change it. }
  166.     begin
  167.     with StoreMsg do
  168.         CheckForExit := DoubleClick(LastSeconds, LastMicros,
  169.                     Seconds, Micros) and
  170.                 (Abs(MouseX - Points[PointCount].X) < 5) and
  171.                 (Abs(MouseY - Points[PointCount].Y) < 3);
  172.     end;
  173.  
  174.     Procedure ClearIt;
  175.     {  This just clears the screen when you enter your first point }
  176.     begin
  177.     SetDrMd(rp, JAM1);
  178.     SetAPen(rp, 0);
  179.     RectFill(rp, BorderLeft, BorderTop,
  180.              BorderRight, BorderBottom);
  181.     SetDrMd(rp, COMPLEMENT);
  182.     SetAPen(rp, 3);
  183.     end;
  184.  
  185. begin
  186.     Move(rp, 252, 20);
  187.     GText(rp, "Enter points by pressing the left mouse button", 46);
  188.     Move(rp, 252, 30);
  189.     GText(rp, "Double click on the last point to begin drawing", 47);
  190.     ModifyIDCMP(w, CLOSEWINDOW_f + MOUSEBUTTONS_f + MOUSEMOVE_f);
  191.     SetDrMd(rp, COMPLEMENT);
  192.     PointCount := 0;
  193.     Leave := False;
  194.     OutOfBounds := False;
  195.     BorderLeft := w^.BorderLeft;
  196.     BorderRight := 639 - w^.BorderRight;
  197.     BorderTop := w^.BorderTop;
  198.     BorderBottom := 189 - w^.BorderBottom;
  199.     repeat
  200.         IM := IntuiMessagePtr(WaitPort(w^.UserPort));
  201.         IM := IntuiMessagePtr(GetMsg(w^.UserPort));
  202.         StoreMsg := IM^;
  203.         ReplyMsg(MessagePtr(IM));
  204.         case StoreMsg.Class of
  205.            MOUSEMOVE_f : if PointCount > 0 then begin
  206.                  if not OutOfBounds then
  207.                  DrawLine;
  208.                         LastX := StoreMsg.MouseX;
  209.                         LastY := StoreMsg.MouseY;
  210.                  if (LastX > BorderLeft) and
  211.                 (LastX < BorderRight) and
  212.                 (LastY > BorderTop) and
  213.                 (LastY < BorderBottom) then begin
  214.                  DrawLine;
  215.                  OutOfBounds := False;
  216.                  end else
  217.                  OutOfBounds := True;
  218.                     end;
  219.            MOUSEBUTTONS_f : if StoreMsg.Code = SELECTUP then begin
  220.                        if PointCount > 0 then
  221.                     Leave := CheckForExit
  222.                 else
  223.                     ClearIt;
  224.                        if (not Leave) and (not OutOfBounds) then
  225.                     AddPoint;
  226.                        end;
  227.            CLOSEWINDOW_f : CleanUpAndDie;
  228.         end;
  229.     until Leave or (PointCount > 14);
  230.     if not Leave then
  231.         DrawLine;
  232.     ModifyIDCMP(w, CLOSEWINDOW_f);
  233.     SetDrMd(rp, JAM1);
  234.     SetAPen(rp, 1);
  235. end;
  236.  
  237. {
  238.    These two function just implement the de Casteljau
  239. algorithm, which looks like:
  240.  
  241.          r            r-1         r-1
  242.     B  = (1-t) * B    +  t * B
  243.          i            i           i+1
  244.  
  245.    Where r and i are meant to be subscripts and superscripts.  R is
  246.    a level number, where zero represents the data points and
  247.    (the number of points - 1) represents the curve points.  I is
  248.    the point numbers, starting from zero normally but in this
  249.    program starting from 1.  t is the familiar 'parameter' running
  250.    from 0 to 1 in arbitrary increments.
  251. }
  252.  
  253. Function BezierX(r, i : Short) : Real;
  254. begin
  255.     if r = 0 then
  256.     BezierX := Float(Points[i].X)
  257.     else
  258.     BezierX := tprime * BezierX(Pred(r), i) + t * BezierX(Pred(r), Succ(i));
  259. end;
  260.  
  261. Function BezierY(r, i : Short) : Real;
  262. begin
  263.     if r = 0 then
  264.     BezierY := Float(Points[i].Y)
  265.     else
  266.     BezierY := tprime * BezierY(Pred(r), i) + t * BezierY(Pred(r), Succ(i));
  267. end;
  268.  
  269. Procedure DrawBezier;
  270. var
  271.     increment : Real;
  272. begin
  273.     increment := 0.01; { This could be a function of PointCount }
  274.     t := 0.0;
  275.     tprime := 1.0;
  276.     Move(rp, Trunc(BezierX(Pred(PointCount), 1)),
  277.          Trunc(BezierY(Pred(PointCount), 1)));
  278.     t := t + increment;
  279.     tprime := 1.0 - t;
  280.     while t <= 1.0 do begin
  281.     Draw(rp, Trunc(BezierX(Pred(PointCount), 1)),
  282.          Trunc(BezierY(Pred(PointCount), 1)));
  283.     t := t + increment;
  284.     tprime := 1.0 - t;
  285.     if GetMsg(w^.UserPort) <> Nil then
  286.         CleanUpAndDie;
  287.     end;
  288.     t := 1.0;
  289.     tprime := 0.0;
  290.     Draw(rp, Trunc(BezierX(Pred(PointCount), 1)),
  291.          Trunc(BezierY(Pred(PointCount), 1)));
  292. end;
  293.  
  294. begin
  295.     GfxBase := OpenLibrary("graphics.library", 0);
  296.     if GfxBase <> nil then begin
  297.     if OpenTheScreen() then begin
  298.         if OpenTheWindow() then begin
  299.             rp := w^.RPort;
  300.             GetPoints;
  301.             DrawBezier;
  302.         m := WaitPort(w^.UserPort);
  303.         Forbid;
  304.         repeat
  305.             m := GetMsg(w^.UserPort);
  306.         until m = nil;
  307.         CloseWindow(w);
  308.         Permit;
  309.         end;
  310.         CloseScreen(s);
  311.     end;
  312.     CloseLibrary(GfxBase);
  313.     end;
  314. end.
  315.