bezier2.pas 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. Program Bezier2;
  2. { This program draws Bezier curves in the slow, simple, recursive
  3. way. When it first runs, you enter points in the window by
  4. clicking the left mouse button. After you double click on the
  5. last point, the program begins drawing the curve.
  6. Since this is a highly recursive program, it's speed decreases
  7. dramatically as you enter more points. It can handle six or
  8. seven points with reasonable speed, but if you enter ten you
  9. might want to go see a movie while it draws. It also uses
  10. more stack space as you enter more points, but I hasn't blown
  11. a 4k stack yet.
  12. }
  13. {
  14. Translated to fpc pascal from pcq pascal.
  15. Updated the source a bit.
  16. 04 Apr 2001.
  17. Changed to use systemvartags, OpenScreenTags
  18. and OpenWindowTags. Also Text to Gtext.
  19. 09 Nov 2002.
  20. [email protected]
  21. }
  22. uses exec, intuition, agraphics, utility;
  23. type
  24. PointRec = Record
  25. X, Y : integer;
  26. end;
  27. Const
  28. w : pWindow = Nil;
  29. s : pScreen = Nil;
  30. {
  31. This will make the new look for screen.
  32. SA_Pens, Integer(pens)
  33. }
  34. pens : array [0..0] of integer = (not 0);
  35. Var
  36. m : pMessage;
  37. rp : pRastPort;
  38. PointCount : integer;
  39. Points : Array [1..15] of PointRec;
  40. t, tprime : Real;
  41. LastX, LastY : integer;
  42. Procedure CleanUpAndDie;
  43. begin
  44. if w <> Nil then begin
  45. Forbid;
  46. repeat until GetMsg(w^.UserPort) = Nil;
  47. CloseWindow(w);
  48. Permit;
  49. end;
  50. if s <> Nil then
  51. CloseScreen(s);
  52. halt(0);
  53. end;
  54. Procedure DrawLine;
  55. begin
  56. GfxMove(rp, Points[PointCount].X, Points[PointCount].Y);
  57. Draw(rp, LastX, LastY);
  58. end;
  59. Procedure GetPoints;
  60. var
  61. LastSeconds,
  62. LastMicros : longint;
  63. IM : pIntuiMessage;
  64. StoreMsg : tIntuiMessage;
  65. Leave : Boolean;
  66. OutOfBounds : Boolean;
  67. BorderLeft, BorderRight,
  68. BorderTop, BorderBottom : integer;
  69. Procedure AddPoint;
  70. begin
  71. Inc(PointCount);
  72. with Points[PointCount] do begin
  73. X := StoreMsg.MouseX;
  74. Y := StoreMsg.MouseY;
  75. end;
  76. with StoreMsg do begin
  77. LastX := MouseX;
  78. LastY := MouseY;
  79. LastSeconds := Seconds;
  80. LastMicros := Micros;
  81. end;
  82. SetAPen(rp, 2);
  83. SetDrMd(rp, JAM1);
  84. DrawEllipse(rp, LastX, LastY, 5, 3);
  85. SetAPen(rp, 3);
  86. SetDrMd(rp, COMPLEMENT);
  87. DrawLine;
  88. end;
  89. Function CheckForExit : Boolean;
  90. { This function determines whether the user wanted to stop
  91. entering points. I added the position tests because my
  92. doubleclick time is too long, and I was too lazy to dig
  93. out Preferences to change it. }
  94. begin
  95. with StoreMsg do
  96. CheckForExit := DoubleClick(LastSeconds, LastMicros,
  97. Seconds, Micros) and
  98. (Abs(MouseX - Points[PointCount].X) < 5) and
  99. (Abs(MouseY - Points[PointCount].Y) < 3);
  100. end;
  101. Procedure ClearIt;
  102. { This just clears the screen when you enter your first point }
  103. begin
  104. SetDrMd(rp, JAM1);
  105. SetAPen(rp, 0);
  106. RectFill(rp, BorderLeft, BorderTop,
  107. BorderRight, BorderBottom);
  108. SetDrMd(rp, COMPLEMENT);
  109. SetAPen(rp, 3);
  110. end;
  111. begin
  112. GfxMove(rp, 252, 30);
  113. GfxText(rp, 'Enter points by pressing the left mouse button', 46);
  114. GfxMove(rp, 252, 40);
  115. GfxText(rp, 'Double click on the last point to begin drawing', 47);
  116. ModifyIDCMP(w, IDCMP_CLOSEWINDOW or IDCMP_MOUSEBUTTONS or IDCMP_MOUSEMOVE);
  117. SetDrMd(rp, COMPLEMENT);
  118. PointCount := 0;
  119. Leave := False;
  120. OutOfBounds := False;
  121. BorderLeft := w^.BorderLeft;
  122. BorderRight := 639 - w^.BorderRight;
  123. BorderTop := w^.BorderTop;
  124. BorderBottom := 189 - w^.BorderBottom;
  125. repeat
  126. IM := pIntuiMessage(WaitPort(w^.UserPort));
  127. IM := pIntuiMessage(GetMsg(w^.UserPort));
  128. StoreMsg := IM^;
  129. ReplyMsg(pMessage(IM));
  130. case StoreMsg.IClass of
  131. IDCMP_MOUSEMOVE : if PointCount > 0 then begin
  132. if not OutOfBounds then
  133. DrawLine;
  134. LastX := StoreMsg.MouseX;
  135. LastY := StoreMsg.MouseY;
  136. if (LastX > BorderLeft) and
  137. (LastX < BorderRight) and
  138. (LastY > BorderTop) and
  139. (LastY < BorderBottom) then begin
  140. DrawLine;
  141. OutOfBounds := False;
  142. end else
  143. OutOfBounds := True;
  144. end;
  145. IDCMP_MOUSEBUTTONS : if StoreMsg.Code = SELECTUP then begin
  146. if PointCount > 0 then
  147. Leave := CheckForExit
  148. else
  149. ClearIt;
  150. if (not Leave) and (not OutOfBounds) then
  151. AddPoint;
  152. end;
  153. IDCMP_CLOSEWINDOW : CleanUpAndDie;
  154. end;
  155. until Leave or (PointCount > 14);
  156. if not Leave then
  157. DrawLine;
  158. ModifyIDCMP(w, IDCMP_CLOSEWINDOW);
  159. SetDrMd(rp, JAM1);
  160. SetAPen(rp, 1);
  161. end;
  162. {
  163. These two function just implement the de Casteljau
  164. algorithm, which looks like:
  165. r r-1 r-1
  166. B = (1-t) * B + t * B
  167. i i i+1
  168. Where r and i are meant to be subscripts and superscripts. R is
  169. a level number, where zero represents the data points and
  170. (the number of points - 1) represents the curve points. I is
  171. the point numbers, starting from zero normally but in this
  172. program starting from 1. t is the familiar 'parameter' running
  173. from 0 to 1 in arbitrary increments.
  174. }
  175. Function BezierX(r, i : integer) : Real;
  176. begin
  177. if r = 0 then
  178. BezierX := real(Points[i].X)
  179. else
  180. BezierX := tprime * BezierX(Pred(r), i) + t * BezierX(Pred(r), Succ(i));
  181. end;
  182. Function BezierY(r, i : integer) : Real;
  183. begin
  184. if r = 0 then
  185. BezierY := real(Points[i].Y)
  186. else
  187. BezierY := tprime * BezierY(Pred(r), i) + t * BezierY(Pred(r), Succ(i));
  188. end;
  189. Procedure DrawBezier;
  190. var
  191. increment : Real;
  192. begin
  193. increment := 0.01; { This could be a function of PointCount }
  194. t := 0.0;
  195. tprime := 1.0;
  196. GfxMove(rp, Trunc(BezierX(Pred(PointCount), 1)),
  197. Trunc(BezierY(Pred(PointCount), 1)));
  198. t := t + increment;
  199. tprime := 1.0 - t;
  200. while t <= 1.0 do begin
  201. Draw(rp, Trunc(BezierX(Pred(PointCount), 1)),
  202. Trunc(BezierY(Pred(PointCount), 1)));
  203. t := t + increment;
  204. tprime := 1.0 - t;
  205. if GetMsg(w^.UserPort) <> Nil then
  206. CleanUpAndDie;
  207. end;
  208. t := 1.0;
  209. tprime := 0.0;
  210. Draw(rp, Trunc(BezierX(Pred(PointCount), 1)),
  211. Trunc(BezierY(Pred(PointCount), 1)));
  212. end;
  213. begin
  214. s := OpenScreenTags(nil,[
  215. SA_Pens, AsTag(@pens),
  216. SA_Depth, 2,
  217. SA_DisplayID, HIRES_KEY,
  218. SA_Title, AsTag('Simple Bezier Curves'),
  219. TAG_END]);
  220. if s = NIL then CleanUpAndDie;
  221. w := OpenWindowTags(nil,[
  222. WA_IDCMP, IDCMP_CLOSEWINDOW,
  223. WA_Left, 0,
  224. WA_Top, s^.BarHeight +1,
  225. WA_Width, s^.Width,
  226. WA_Height, s^.Height - (s^.BarHeight + 1),
  227. WA_DepthGadget, ltrue,
  228. WA_DragBar, ltrue,
  229. WA_CloseGadget, ltrue,
  230. WA_ReportMouse, ltrue,
  231. WA_SmartRefresh, ltrue,
  232. WA_Activate, ltrue,
  233. WA_Title, AsTag('Close the Window to Quit'),
  234. WA_CustomScreen, AsTag(s),
  235. TAG_END]);
  236. IF w=NIL THEN CleanUpAndDie;
  237. rp := w^.RPort;
  238. GetPoints;
  239. DrawBezier;
  240. m := WaitPort(w^.UserPort);
  241. Forbid;
  242. repeat
  243. m := GetMsg(w^.UserPort);
  244. until m = nil;
  245. Permit;
  246. CleanUpAndDie;
  247. end.