fGradLines.pas 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. unit fGradLines;
  2. (* ***** BEGIN LICENSE BLOCK *****
  3. * Version: MPL 1.1 or LGPL 2.1 with linking exception
  4. *
  5. * The contents of this file are subject to the Mozilla Public License Version
  6. * 1.1 (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. * http://www.mozilla.org/MPL/
  9. *
  10. * Software distributed under the License is distributed on an "AS IS" basis,
  11. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. * for the specific language governing rights and limitations under the
  13. * License.
  14. *
  15. * Alternatively, the contents of this file may be used under the terms of the
  16. * Free Pascal modified version of the GNU Lesser General Public License
  17. * Version 2.1 (the "FPC modified LGPL License"), in which case the provisions
  18. * of this license are applicable instead of those above.
  19. * Please see the file LICENSE.txt for additional information concerning this
  20. * license.
  21. *
  22. * The Original Code is Gradient Lines Example
  23. *
  24. * The Initial Developer of the Original Code is
  25. * Alex A. Denisov
  26. *
  27. * Portions created by the Initial Developer are Copyright (C) 2000-2005
  28. * the Initial Developer. All Rights Reserved.
  29. *
  30. * Contributor(s):
  31. *
  32. * ***** END LICENSE BLOCK ***** *)
  33. interface
  34. {$I GR32.inc}
  35. uses
  36. {$IFDEF FPC} LCLIntf, LResources, Buttons, {$ENDIF} SysUtils, Classes,
  37. Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls,
  38. {$IFDEF COMPILERXE2_UP}Types, {$ENDIF}
  39. GR32, GR32_Blend, GR32_Image, GR32_LowLevel;
  40. type
  41. TVector2f = record
  42. X, Y: Single;
  43. end;
  44. TLine = class
  45. public
  46. Bitmap: TBitmap32;
  47. P1, P2: TVector2f; // positions
  48. V1, V2: TVector2f; // velocities
  49. C1, C2, C3: TColor32; // colors that define gradient pattern
  50. t1, t2, t3: Single;
  51. MaxVelocity: Single;
  52. constructor Create(ABitmap: TBitmap32);
  53. procedure Advance(DeltaT: Single);
  54. function GetLength: Single;
  55. procedure Paint;
  56. end;
  57. { TFormGradientLines }
  58. TFormGradientLines = class(TForm)
  59. BtnAddOne: TButton;
  60. BtnAddTen: TButton;
  61. BtnClear: TButton;
  62. LblTotal: TLabel;
  63. Memo: TMemo;
  64. PaintBox: TPaintBox32;
  65. PnlTotalLines: TPanel;
  66. RgpDraw: TRadioGroup;
  67. RgpFade: TRadioGroup;
  68. RepaintOpt: TCheckBox;
  69. procedure FormCreate(Sender: TObject);
  70. procedure RepaintOptClick(Sender: TObject);
  71. procedure BtnAddOneClick(Sender: TObject);
  72. procedure BtnAddTenClick(Sender: TObject);
  73. procedure BtnClearClick(Sender: TObject);
  74. procedure RgpFadeClick(Sender: TObject);
  75. procedure RgpDrawClick(Sender: TObject);
  76. protected
  77. Lines: array of TLine;
  78. P: TPoint; // mouse shift
  79. M: Boolean; // mouse down flag
  80. FadeCount: Integer;
  81. Pass: Integer;
  82. DrawPasses: Integer;
  83. procedure AppEventsIdle(Sender: TObject; var Done: Boolean);
  84. public
  85. procedure AddLine;
  86. procedure AddLines(N: Integer);
  87. end;
  88. var
  89. FormGradientLines: TFormGradientLines;
  90. implementation
  91. {$IFDEF FPC}
  92. {$R *.lfm}
  93. {$ELSE}
  94. {$R *.dfm}
  95. {$ENDIF}
  96. uses Math;
  97. function VectorAdd(const A, B: TVector2f): TVector2f;
  98. begin
  99. Result.X := A.X + B.X;
  100. Result.Y := A.Y + B.Y;
  101. end;
  102. function VectorSub(const A, B: TVector2f): TVector2f;
  103. begin
  104. Result.X := A.X - B.X;
  105. Result.Y := A.Y - B.Y;
  106. end;
  107. function VectorLen(const A: TVector2f): Single;
  108. begin
  109. Result := SqRt(SqR(A.X) + SqR(A.Y));
  110. end;
  111. function VectorDot(const A, B: TVector2f): Single;
  112. begin
  113. Result := A.X * B.X + A.Y * B.Y;
  114. end;
  115. function VectorScale(const A: TVector2f; Factor: Single): TVector2f;
  116. begin
  117. Result.X := A.X * Factor;
  118. Result.Y := A.Y * Factor;
  119. end;
  120. { TLine }
  121. constructor TLine.Create(ABitmap: TBitmap32);
  122. begin
  123. Bitmap := ABitmap;
  124. MaxVelocity := 1;
  125. end;
  126. procedure TLine.Advance(DeltaT: Single);
  127. const
  128. COne400 : Single = 1 / 400;
  129. COne300 : Single = 1 / 300;
  130. procedure AdvancePoint(var P, V: TVector2f; t: Single);
  131. begin
  132. { apply velocities }
  133. P := VectorAdd(P, VectorScale(V, t));
  134. { reflect from walls }
  135. if P.X < 0 then
  136. begin
  137. P.X := 0;
  138. V.X := -V.X;
  139. end;
  140. if P.X >= FormGradientLines.PaintBox.Width then
  141. begin
  142. P.X := FormGradientLines.PaintBox.Width - 1;
  143. V.X := - V.X;
  144. end;
  145. if P.Y < 0 then
  146. begin
  147. P.Y := 0;
  148. V.Y := -V.Y;
  149. end;
  150. if P.Y >= FormGradientLines.PaintBox.Height then
  151. begin
  152. P.Y := FormGradientLines.PaintBox.Height - 1;
  153. V.Y := - V.Y
  154. end;
  155. { change velocity a little bit }
  156. V.X := V.X + t * (Random - 0.5) * 0.25;
  157. V.Y := V.Y + t * (Random - 0.5) * 0.25;
  158. { limit velocity }
  159. if VectorLen(V) > MaxVelocity then
  160. V := VectorScale(V, 1 / VectorLen(V));
  161. end;
  162. begin
  163. AdvancePoint(P1, V1, DeltaT);
  164. AdvancePoint(P2, V2, DeltaT);
  165. C1 := HSLtoRGB(t1, Sin(t1 * 0.55) * 0.4 + 0.6, 0.5);
  166. C1 := SetAlpha(C1, Round(Sin(t1) * 25 + 50));
  167. t1 := t1 + Random * COne300;
  168. C2 := HSLtoRGB(t2, Sin(t2 * 0.55) * 0.4 + 0.6, 0.5);
  169. C2 := SetAlpha(C2, Round(Sin(t2) * 25 + 50));
  170. t2 := t2 + Random * COne400;
  171. C3 := HSLtoRGB(t3, Sin(t3 * 0.55) * 0.4 + 0.6, 0.5);
  172. C3 := SetAlpha(C3, Round(Sin(t3) * 25 + 50));
  173. t3 := t3 + Random * COne400;
  174. end;
  175. function TLine.GetLength: Single;
  176. begin
  177. Result := VectorLen(VectorSub(P1, P2));
  178. end;
  179. procedure TLine.Paint;
  180. var
  181. L: Single;
  182. begin
  183. // this shows how to draw a gradient line
  184. L := GetLength;
  185. if L < 1 then Exit;
  186. Bitmap.SetStipple([C1, C2, C3]);
  187. Bitmap.StippleStep := 2 / L; {2 = 3 - 1 = Number of colors in a pattern - 1}
  188. Bitmap.StippleCounter := 0;
  189. Bitmap.LineFSP(P1.X, P1.Y, P2.X, P2.Y);
  190. end;
  191. { TFormGradientLines }
  192. procedure TFormGradientLines.FormCreate(Sender: TObject);
  193. begin
  194. FadeCount := 0;
  195. DrawPasses := 2;
  196. Application.OnIdle := AppEventsIdle;
  197. end;
  198. procedure TFormGradientLines.AddLine;
  199. var
  200. L: TLine;
  201. begin
  202. SetLength(Lines, Length(Lines) + 1);
  203. L := TLine.Create(PaintBox.Buffer);
  204. Lines[High(Lines)] := L;
  205. L.t1 := Random * 3;
  206. L.t2 := Random * 3;
  207. L.t3 := Random * 3;
  208. L.P1.X := Random(PaintBox.Buffer.Width div 2 - 1);
  209. L.P2.X := Random(PaintBox.Buffer.Width div 2 - 1);
  210. L.P1.Y := Random(PaintBox.Buffer.Height div 2 - 1);
  211. L.P2.Y := Random(PaintBox.Buffer.Height div 2 - 1);
  212. PnlTotalLines.Caption := IntToStr(Length(Lines));
  213. end;
  214. procedure TFormGradientLines.AddLines(N: Integer);
  215. var
  216. Index: Integer;
  217. begin
  218. for Index := 0 to N - 1 do AddLine;
  219. end;
  220. procedure TFormGradientLines.AppEventsIdle(Sender: TObject; var Done: Boolean);
  221. var
  222. I, J: Integer;
  223. P: PColor32;
  224. begin
  225. for J := 0 to DrawPasses - 1 do
  226. for I := 0 to High(Lines) do
  227. begin
  228. Lines[I].Advance(1);
  229. Lines[I].Paint;
  230. end;
  231. if FadeCount > 0 then
  232. begin
  233. if Pass = 0 then with PaintBox.Buffer do
  234. begin
  235. P := @Bits[0];
  236. for I := 0 to Width * Height -1 do
  237. begin
  238. BlendMem($10000000, P^);
  239. Inc(P);
  240. end;
  241. EMMS;
  242. end;
  243. Dec(Pass);
  244. if (Pass < 0) or (Pass > FadeCount) then Pass := FadeCount;
  245. // we're doing unsafe operations above, so force a complete invalidation
  246. // so that wrong output of repaint optimizer doesn't show.
  247. PaintBox.ForceFullInvalidate;
  248. end
  249. else
  250. PaintBox.Invalidate;
  251. end;
  252. procedure TFormGradientLines.BtnAddOneClick(Sender: TObject);
  253. begin
  254. AddLine;
  255. end;
  256. procedure TFormGradientLines.BtnAddTenClick(Sender: TObject);
  257. begin
  258. AddLines(10);
  259. end;
  260. procedure TFormGradientLines.BtnClearClick(Sender: TObject);
  261. var
  262. Index: Integer;
  263. begin
  264. for Index := High(Lines) downto 0 do Lines[Index].Free;
  265. Lines := nil;
  266. PaintBox.Buffer.Clear;
  267. PnlTotalLines.Caption := '0';
  268. end;
  269. procedure TFormGradientLines.RgpFadeClick(Sender: TObject);
  270. const
  271. FC: array [0..2] of Integer = (0, 7, 1);
  272. begin
  273. FadeCount := FC[RgpFade.ItemIndex];
  274. end;
  275. procedure TFormGradientLines.RgpDrawClick(Sender: TObject);
  276. begin
  277. DrawPasses := (RgpDraw.ItemIndex + 1) * 3 - 2;
  278. end;
  279. procedure TFormGradientLines.RepaintOptClick(Sender: TObject);
  280. begin
  281. if RepaintOpt.Checked then
  282. PaintBox.RepaintMode := rmOptimizer
  283. else
  284. PaintBox.RepaintMode := rmFull;
  285. end;
  286. end.