bezier.pas 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. Program Bezier;
  2. {
  3. This program draws Bezier curves using the degree elevation
  4. method. For large numbers of points (more than 10, for
  5. example) this is faster than the recursive way.
  6. }
  7. {
  8. Changed the source to use 2.0+.
  9. Looks a lot better.
  10. Added CloseWindowSafely.
  11. Made the window dynamic, it will
  12. adjust the size after the screen size.
  13. 9 May 1998.
  14. Translated the source to fpc.
  15. 20 Aug 1998.
  16. [email protected]
  17. }
  18. uses exec, intuition, graphics, utility;
  19. {$I tagutils.inc}
  20. type
  21. PointRec = packed Record
  22. X, Y : Real;
  23. end;
  24. Const
  25. w : pWindow = Nil;
  26. s : pScreen = Nil;
  27. ltrue : longint = 1;
  28. {
  29. This will make the new look for screen.
  30. SA_Pens, Integer(pens)
  31. }
  32. pens : array [0..0] of integer = (not 0);
  33. Var
  34. m : pMessage;
  35. rp : pRastPort;
  36. PointCount : Word;
  37. Points : Array [1..200] of PointRec;
  38. t, tprime : Real;
  39. LastX, LastY : Word;
  40. tags : array[0..13] of tTagItem;
  41. Procedure CleanUpAndDie;
  42. begin
  43. if w <> Nil then CloseWindow(w);
  44. if s <> Nil then CloseScreen(s);
  45. if Gfxbase <> nil then CloseLibrary(GfxBase);
  46. Halt(0);
  47. end;
  48. Procedure DrawLine;
  49. begin
  50. Move(rp, Trunc(Points[PointCount].X), Trunc(Points[PointCount].Y));
  51. Draw(rp, LastX, LastY);
  52. end;
  53. Procedure GetPoints;
  54. var
  55. LastSeconds,
  56. LastMicros : Longint;
  57. IM : pIntuiMessage;
  58. StoreMsg : tIntuiMessage;
  59. Leave : Boolean;
  60. OutOfBounds : Boolean;
  61. BorderLeft, BorderRight,
  62. BorderTop, BorderBottom : Word;
  63. dummy : Boolean;
  64. Procedure AddPoint;
  65. begin
  66. Inc(PointCount);
  67. with Points[PointCount] do begin
  68. X := Real(StoreMsg.MouseX);
  69. Y := Real(StoreMsg.MouseY);
  70. end;
  71. with StoreMsg do begin
  72. LastX := MouseX;
  73. LastY := MouseY;
  74. LastSeconds := Seconds;
  75. LastMicros := Micros;
  76. end;
  77. SetAPen(rp, 2);
  78. SetDrMd(rp, JAM1);
  79. DrawEllipse(rp, LastX, LastY, 5, 3);
  80. SetAPen(rp, 3);
  81. SetDrMd(rp, COMPLEMENT);
  82. DrawLine;
  83. end;
  84. Function CheckForExit : Boolean;
  85. { This function determines whether the user wanted to stop
  86. entering points. I added the position tests because my
  87. doubleclick time is too long, and I was too lazy to dig
  88. out Preferences to change it. }
  89. begin
  90. with StoreMsg do
  91. CheckForExit := DoubleClick(LastSeconds, LastMicros,
  92. Seconds, Micros) and
  93. (Abs(MouseX - Trunc(Points[PointCount].X)) < 5) and
  94. (Abs(MouseY - TRunc(Points[PointCount].Y)) < 3);
  95. end;
  96. Procedure ClearIt;
  97. { This just clears the screen when you enter your first point }
  98. begin
  99. SetDrMd(rp, JAM1);
  100. SetAPen(rp, 0);
  101. RectFill(rp, BorderLeft, BorderTop,
  102. BorderRight, BorderBottom);
  103. SetDrMd(rp, COMPLEMENT);
  104. SetAPen(rp, 3);
  105. end;
  106. begin
  107. dummy := ModifyIDCMP(w, IDCMP_CLOSEWINDOW or IDCMP_MOUSEBUTTONS or IDCMP_MOUSEMOVE);
  108. SetDrMd(rp, COMPLEMENT);
  109. PointCount := 0;
  110. Leave := False;
  111. OutOfBounds := False;
  112. BorderLeft := w^.BorderLeft;
  113. BorderRight := (w^.Width - w^.BorderRight) -1;
  114. BorderTop := w^.BorderTop;
  115. BorderBottom := (w^.Height - w^.BorderBottom) -1;
  116. repeat
  117. IM := pIntuiMessage(WaitPort(w^.UserPort));
  118. IM := pIntuiMessage(GetMsg(w^.UserPort));
  119. StoreMsg := IM^;
  120. ReplyMsg(pMessage(IM));
  121. case StoreMsg.IClass of
  122. IDCMP_MOUSEMOVE : if PointCount > 0 then begin
  123. if not OutOfBounds then
  124. DrawLine;
  125. LastX := StoreMsg.MouseX;
  126. LastY := StoreMsg.MouseY;
  127. if (LastX > BorderLeft) and
  128. (LastX < BorderRight) and
  129. (LastY > BorderTop) and
  130. (LastY < BorderBottom) then begin
  131. DrawLine;
  132. OutOfBounds := False;
  133. end else
  134. OutOfBounds := True;
  135. end;
  136. IDCMP_MOUSEBUTTONS : if StoreMsg.Code = SELECTUP then begin
  137. if PointCount > 0 then
  138. Leave := CheckForExit
  139. else
  140. ClearIt;
  141. if (not Leave) and (not OutOfBounds) then
  142. AddPoint;
  143. end;
  144. IDCMP_CLOSEWINDOW : CleanUpAndDie;
  145. end;
  146. until Leave or (PointCount > 50);
  147. if not Leave then
  148. DrawLine;
  149. dummy := ModifyIDCMP(w, IDCMP_CLOSEWINDOW);
  150. SetDrMd(rp, JAM1);
  151. SetAPen(rp, 1);
  152. end;
  153. Procedure Elevate;
  154. var
  155. t, tprime,
  156. RealPoints : Real;
  157. i : Integer;
  158. begin
  159. Inc(PointCount);
  160. RealPoints := Real(PointCount);
  161. Points[PointCount] := Points[Pred(PointCount)];
  162. for i := Pred(PointCount) downto 2 do
  163. with Points[i] do begin
  164. t := Real(i) / RealPoints;
  165. tprime := 1.0 - t;
  166. X := t * Points[Pred(i)].X + tprime * X;
  167. Y := t * Points[Pred(i)].Y + tprime * Y;
  168. end;
  169. end;
  170. Procedure DrawCurve;
  171. var
  172. i : Integer;
  173. begin
  174. Move(rp, Trunc(Points[1].X), Trunc(Points[1].Y));
  175. for i := 2 to PointCount do
  176. Draw(rp, Round(Points[i].X), Round(Points[i].Y));
  177. end;
  178. Procedure DrawBezier;
  179. var
  180. i : Word;
  181. begin
  182. SetAPen(rp, 2);
  183. while PointCount < 100 do begin
  184. Elevate;
  185. DrawCurve;
  186. if GetMsg(w^.UserPort) <> Nil then
  187. CleanUpAndDie;
  188. end;
  189. SetAPen(rp, 1);
  190. DrawCurve;
  191. end;
  192. begin
  193. GfxBase := OpenLibrary(GRAPHICSNAME,37);
  194. tags[0] := TagItem(SA_Pens, Long(@pens));
  195. tags[1] := TagItem(SA_Depth, 2);
  196. tags[2] := TagItem(SA_DisplayID, HIRES_KEY);
  197. tags[3] := TagItem(SA_Title, Long(PChar('Simple Bezier Curves'#0)));
  198. tags[4].ti_Tag := TAG_END;
  199. s := OpenScreenTagList(nil, @tags);
  200. if s = NIL then CleanUpAndDie;
  201. tags[0] := TagItem(WA_IDCMP, IDCMP_CLOSEWINDOW);
  202. tags[1] := TagItem(WA_Left, 0);
  203. tags[2] := TagItem(WA_Top, s^.BarHeight +1);
  204. tags[3] := TagItem(WA_Width, s^.Width);
  205. tags[4] := TagItem(WA_Height, s^.Height - (s^.BarHeight + 1));
  206. tags[5] := TagItem(WA_DepthGadget, ltrue);
  207. tags[6] := TagItem(WA_DragBar, ltrue);
  208. tags[7] := TagItem(WA_CloseGadget, ltrue);
  209. tags[8] := TagItem(WA_ReportMouse, ltrue);
  210. tags[9] := TagItem(WA_SmartRefresh, ltrue);
  211. tags[10] := TagItem(WA_Activate, ltrue);
  212. tags[11] := TagItem(WA_Title, long(PChar('Close the Window to Quit'#0)));
  213. tags[12] := TagItem(WA_CustomScreen, long(s));
  214. tags[13].ti_Tag := TAG_END;
  215. w := OpenWindowTagList(nil, @tags);
  216. IF w=NIL THEN CleanUpAndDie;
  217. rp := w^.RPort;
  218. Move(rp, 252, 20);
  219. Text(rp, PChar('Enter points by pressing the left mouse button'#0), 46);
  220. Move(rp, 252, 30);
  221. Text(rp, PChar('Double click on the last point to begin drawing'#0), 47);
  222. repeat
  223. GetPoints; { Both these routines will quit if }
  224. DrawBezier; { the window is closed. }
  225. until False;
  226. CleanUpAndDie;
  227. end.