FractalDemo.Mod 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. MODULE FractalDemo; (** AUTHOR "TF"; PURPOSE "Draw Mandelbrot-Set using n - processors (otherwise not optimized)"; *)
  2. IMPORT
  3. Objects, XML, WMComponents, WMGraphics, WMGraphicUtilities, Raster, KernelLog,
  4. Kernel, Strings, Math, WMProperties, WMRectangles;
  5. CONST MaxIter = 2048;
  6. TYPE
  7. WorkUnit = POINTER TO RECORD
  8. terminate : BOOLEAN;
  9. x0, y0, x1, y1: LONGREAL;
  10. w, h, maxIter, ys, ye : LONGINT;
  11. result : POINTER TO ARRAY OF LONGINT;
  12. next : WorkUnit;
  13. END;
  14. WorkUnitQ = OBJECT
  15. VAR work: WorkUnit;
  16. PROCEDURE Add(wu : WorkUnit);
  17. BEGIN {EXCLUSIVE}
  18. wu.next := work; work := wu
  19. END Add;
  20. PROCEDURE Get() : WorkUnit;
  21. VAR result:WorkUnit;
  22. BEGIN {EXCLUSIVE}
  23. AWAIT(work # NIL);
  24. result := work; work := work.next;
  25. RETURN result
  26. END Get;
  27. END WorkUnitQ;
  28. CalcThread = OBJECT
  29. VAR
  30. alive : BOOLEAN;
  31. i, j : LONGINT;
  32. dx, dy : LONGREAL;
  33. wu : WorkUnit;
  34. workQ, resultQ : WorkUnitQ;
  35. x, y : LONGREAL;
  36. PROCEDURE &Init*(workQ, resultQ:WorkUnitQ);
  37. BEGIN
  38. alive := TRUE; SELF.workQ := workQ; SELF.resultQ := resultQ
  39. END Init;
  40. PROCEDURE StartWork;
  41. BEGIN
  42. NEW(wu.result, wu.w * wu.h);
  43. dx := (wu.x1 - wu.x0) / wu.w; dy := (wu.y1 - wu.y0) / wu.h
  44. END StartWork;
  45. PROCEDURE Finished;
  46. BEGIN {EXCLUSIVE}
  47. alive := FALSE
  48. END Finished;
  49. PROCEDURE AwaitFinished;
  50. BEGIN {EXCLUSIVE}
  51. AWAIT(~alive)
  52. END AwaitFinished;
  53. PROCEDURE Calc(x, y : LONGREAL) : LONGINT;
  54. VAR re, im, re2, im2 : LONGREAL; i : LONGINT;
  55. BEGIN
  56. re := x; im := y; i := 1;
  57. WHILE i < wu.maxIter DO
  58. re2 := re * re; im2 := im * im;
  59. IF re2 + im2 > 4 (* out *) THEN RETURN i END;
  60. im := 2 * re * im + y;
  61. re := re2 - im2 + x;
  62. INC(i)
  63. END;
  64. RETURN 0
  65. END Calc;
  66. BEGIN {ACTIVE, PRIORITY(Objects.Low)}
  67. WHILE alive DO
  68. wu := workQ.Get();
  69. IF wu.terminate THEN alive := FALSE
  70. ELSE
  71. StartWork;
  72. y := wu.y0; i := 0;
  73. WHILE (i < wu.h) DO
  74. j := 0;
  75. x := wu.x0;
  76. WHILE (j < wu.w) DO
  77. wu.result[i * wu.w + j] := Calc(x, y);
  78. x := x + dx;
  79. INC(j)
  80. END;
  81. y := y + dy;
  82. INC(i)
  83. END;
  84. wu.next := NIL;
  85. resultQ.Add(wu)
  86. END
  87. END;
  88. Finished
  89. END CalcThread;
  90. List = POINTER TO RECORD
  91. next: List;
  92. x0,x1,y0,y1: LONGREAL;
  93. depth: LONGINT;
  94. END;
  95. MandelbrotSetViewer = OBJECT (WMComponents.VisualComponent)
  96. VAR
  97. nofProcesses : WMProperties.Int32Property;
  98. backBmp : Raster.Image;
  99. bc : WMGraphics.BufferCanvas;
  100. dy : LONGREAL;
  101. list, l : List;
  102. selecting : BOOLEAN;
  103. x0, y0, x1, y1 : LONGINT;
  104. palette : ARRAY MaxIter OF WMGraphics.Color;
  105. workQ, resultQ : WorkUnitQ;
  106. recalcNeeded, alive, calculating : BOOLEAN;
  107. PROCEDURE &Init*;
  108. VAR i, x: LONGINT;
  109. BEGIN
  110. Init^;
  111. SetNameAsString(StrMandelbrotSetViewer);
  112. NEW(backBmp);
  113. NEW(nofProcesses, PrototypeNofProcesses, NIL, NIL); properties.Add(nofProcesses);
  114. NEW(list);
  115. list.x0 := -2; list.y0 := -2; list.x1 := 2; list.y1 := 2;
  116. list.depth := MaxIter;
  117. (* calculate a "nice" palette *)
  118. FOR i := 0 TO MaxIter-1 DO
  119. x := ENTIER(200 * Math.sqrt(Math.sqrt(Math.sin(i / MaxIter*Math.pi / 2)))) + 55;
  120. palette[i] := WMGraphics.RGBAToColor(x, x*2 MOD 255, x*4 MOD 255, 255)
  121. END;
  122. alive := TRUE; calculating := FALSE
  123. END Init;
  124. PROCEDURE Recalc;
  125. VAR w, h, p, i, j: LONGINT;
  126. str : ARRAY 32 OF CHAR;
  127. t : Kernel.MilliTimer;
  128. t2 : LONGINT;
  129. processes : POINTER TO ARRAY OF CalcThread;
  130. wu : WorkUnit;
  131. BEGIN
  132. IF bc = NIL THEN recalcNeeded := FALSE; RETURN END;
  133. calculating := TRUE;
  134. Invalidate;
  135. w := bounds.GetWidth(); h := bounds.GetHeight();
  136. IF w <= 0 THEN w := 1 END; IF h <= 0 THEN h := 1 END;
  137. NEW(workQ); NEW(resultQ);
  138. NEW(processes, nofProcesses.Get());
  139. Kernel.SetTimer(t, 0);
  140. (* initialize processes *)
  141. FOR p := 0 TO nofProcesses.Get() - 1 DO NEW(processes[p], workQ, resultQ) END;
  142. dy := (list.y1 - list.y0) / h;
  143. (* fill workQ *)
  144. FOR i := 0 TO h - 1 DO
  145. NEW(wu);
  146. wu.x0 := list.x0; wu.y0 := list.y0 + dy * i; wu.x1 := list.x1; wu.y1 := wu.y0 + dy;
  147. wu.h := 1; wu.w := w; wu.ys := i; wu.ye := i;
  148. wu.maxIter := list.depth;
  149. workQ.Add(wu)
  150. END;
  151. (* empty resultQ *)
  152. FOR i := 0 TO h - 1 DO
  153. wu := resultQ.Get();
  154. FOR j := 0 TO wu.w - 1 DO
  155. bc.SetPixel(j, wu.ys, palette[wu.result[j] MOD MaxIter], WMGraphics.ModeCopy)
  156. END
  157. END;
  158. (* send killer workunits *)
  159. FOR i := 0 TO nofProcesses.Get() - 1 DO
  160. NEW(wu); wu.terminate := TRUE;
  161. workQ.Add(wu)
  162. END;
  163. t2 := Kernel.Elapsed(t);
  164. KernelLog.Enter;
  165. KernelLog.String(" #CPU="); KernelLog.Int(nofProcesses.Get(), 2);
  166. KernelLog.String(" ms="); KernelLog.Int(t2, 0); KernelLog.Ln;
  167. KernelLog.String(" co-ordinates:"); KernelLog.Ln;
  168. KernelLog.String(" "); Strings.FloatToStr(list.x0, 20, 19, 0, str); KernelLog.String(str); KernelLog.String(" / ");
  169. Strings.FloatToStr(list.y0, 20, 19, 0, str); KernelLog.String(str); KernelLog.Ln;
  170. KernelLog.String(" "); Strings.FloatToStr(list.x1, 20, 19, 0, str); KernelLog.String(str); KernelLog.String(" / ");
  171. Strings.FloatToStr(list.y1, 20, 19, 0, str); KernelLog.String(str); KernelLog.Ln;
  172. KernelLog.String(" depth = "); KernelLog.Int(list.depth,0); KernelLog.Ln;
  173. KernelLog.Exit;
  174. recalcNeeded := FALSE;
  175. Invalidate;
  176. calculating := FALSE
  177. END Recalc;
  178. PROCEDURE PointerDown*(x,y : LONGINT; keys : SET);
  179. BEGIN
  180. IF calculating THEN RETURN END;
  181. IF (keys*{0}#{}) THEN
  182. NEW(l);
  183. l.x0 := list.x0 + x/bounds.GetWidth()*(list.x1-list.x0);
  184. l.y0 := list.y0 + y/bounds.GetHeight()*(list.y1-list.y0);
  185. selecting := TRUE; x0 := x; y0 := y; x1 := x; y1 := y
  186. ELSIF (list.next # NIL) THEN
  187. list := list.next; l := NIL
  188. END
  189. END PointerDown;
  190. PROCEDURE PointerMove*(x,y : LONGINT; keys : SET);
  191. BEGIN
  192. IF calculating THEN RETURN END;
  193. x1 := x; y1 := y;
  194. IF selecting THEN Invalidate END
  195. END PointerMove;
  196. PROCEDURE PointerUp*(x,y : LONGINT; keys : SET);
  197. VAR t: LONGREAL;
  198. BEGIN
  199. IF calculating THEN RETURN END;
  200. selecting := FALSE;
  201. IF (l # NIL) THEN
  202. l.x1 := list.x0 + x/bounds.GetWidth()*(list.x1-list.x0);
  203. l.y1 := l.y0 + (l.x1 - l.x0); (*list.y0 + y/GetHeight()*(list.y1-list.y0);*)
  204. IF (l.x1 < l.x0) THEN t := l.x1; l.x1 := l.x0; l.x0 := t END;
  205. IF (l.y1 < l.y0) THEN t := l.y1; l.y1 := l.y0; l.y0 := t END;
  206. l.depth := list.depth * 2;
  207. l.next := list;
  208. list := l;
  209. l := NIL
  210. END;
  211. NeedRecalc
  212. END PointerUp;
  213. PROCEDURE NeedRecalc;
  214. BEGIN {EXCLUSIVE}
  215. recalcNeeded := TRUE;
  216. END NeedRecalc;
  217. PROCEDURE PrepareBuffer;
  218. VAR w, h:LONGINT;
  219. BEGIN
  220. w := bounds.GetWidth(); h := bounds.GetHeight();
  221. IF w <= 0 THEN w := 1 END; IF h <= 0 THEN h := 1 END;
  222. Raster.Create(backBmp, w, h, Raster.BGR888);
  223. NEW(bc, backBmp);
  224. IF (w > 1) & (h > 1) THEN NeedRecalc END
  225. END PrepareBuffer;
  226. PROCEDURE Resized*;
  227. BEGIN
  228. Resized^;
  229. PrepareBuffer;
  230. END Resized;
  231. PROCEDURE DrawBackground*(canvas : WMGraphics.Canvas);
  232. VAR str, t : ARRAY 32 OF CHAR;
  233. BEGIN
  234. canvas.DrawImage(0, 0, backBmp, WMGraphics.ModeCopy);
  235. IF selecting THEN
  236. WMGraphicUtilities.RectGlassShade(canvas,
  237. WMRectangles.MakeRect(
  238. MIN(x0, x1),
  239. MIN(y0, y1),
  240. MAX(x1, x0),
  241. MAX(y1, y0)), 5, FALSE)
  242. END;
  243. IF recalcNeeded THEN
  244. canvas.SetColor(LONGINT(0FFFF00FFH));
  245. canvas.DrawString(20, 20, "Calculating...");
  246. str := "Processes : "; Strings.IntToStr( nofProcesses.Get(), t);
  247. Strings.Append(str, t);
  248. canvas.DrawString(20, 40, str);
  249. END;
  250. END DrawBackground;
  251. PROCEDURE Finalize*;
  252. BEGIN
  253. Finalize^;
  254. BEGIN {EXCLUSIVE} alive := FALSE END
  255. END Finalize;
  256. PROCEDURE Initialize*;
  257. BEGIN
  258. Initialize^;
  259. PrepareBuffer;
  260. BEGIN {EXCLUSIVE} (* initialized := TRUE *) (* implicitly done in Initialize, initialize is read-only *) END;
  261. END Initialize;
  262. BEGIN {ACTIVE}
  263. BEGIN {EXCLUSIVE}
  264. AWAIT(initialized OR ~alive)
  265. END;
  266. WHILE alive DO
  267. BEGIN {EXCLUSIVE}
  268. AWAIT(~alive OR recalcNeeded)
  269. END;
  270. IF alive THEN Recalc END
  271. END
  272. END MandelbrotSetViewer;
  273. VAR
  274. PrototypeNofProcesses : WMProperties.Int32Property;
  275. StrMandelbrotSetViewer : Strings.String;
  276. PROCEDURE GenMandelbrotSetViewer*() : XML.Element;
  277. VAR x : MandelbrotSetViewer;
  278. BEGIN
  279. NEW(x); RETURN x
  280. END GenMandelbrotSetViewer;
  281. PROCEDURE InitPrototypes;
  282. BEGIN
  283. StrMandelbrotSetViewer := Strings.NewString("MandelbrotSetViewer");
  284. NEW(PrototypeNofProcesses, NIL, Strings.NewString("NofProcesses"), Strings.NewString("number of processes to use"));
  285. END InitPrototypes;
  286. BEGIN
  287. InitPrototypes;
  288. END FractalDemo.
  289. ComponentViewer.Open FractalDemo.XML ~