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