WMSimpleGraphs.Mod 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. MODULE WMSimpleGraphs; (** AUTHOR "Patrick Hunziker"; PURPOSE "Minimum-overhead graph drawing and storing"; *)
  2. (** right-click on navigator thumbnail allows window storage as image file *)
  3. (*ToDo: scale ticks and labels on axes *)
  4. (* ToDo: eliminate code duplications, e.g. in Init() *)
  5. (*ToDo: catch NaN and Inf in data and other strategies to avoid erratic window sizes*)
  6. IMPORT Strings, WMGraphics, WMRectangles, Modules, Reals, WM:=WMWindowManager;
  7. CONST Colors=[WMGraphics.Red,WMGraphics.Blue,WMGraphics.Green,WMGraphics.Yellow, WMGraphics.Magenta, WMGraphics.Cyan, WMGraphics.Gray];
  8. TYPE
  9. Regressor=PROCEDURE{DELEGATE}(CONST data: ARRAY [*,*] OF LONGREAL; VAR slope,intercept: LONGREAL);
  10. TYPE
  11. Window=OBJECT(WM.Window);
  12. END Window;
  13. Histogram* = OBJECT (Window);
  14. VAR
  15. data:ARRAY [*] OF LONGREAL;
  16. width,height:LONGINT;
  17. PROCEDURE &New*(CONST data: ARRAY [*] OF LONGREAL; CONST title: ARRAY OF CHAR);
  18. VAR max:LONGREAL;
  19. BEGIN
  20. SELF.data:=data;
  21. max:=MAX(data);
  22. width:=LEN(data,0); height:=ENTIER(max)+1;
  23. Init(10*width, 10*height, FALSE);
  24. WM.GetDefaultManager().Add(PosX, PosY, SELF, {WM.FlagFrame,WM.FlagClose});
  25. NewWindowPos(GetWidth());
  26. SetTitle(Strings.NewString(title));
  27. SetPointerInfo(manager.pointerCrosshair);
  28. END New;
  29. PROCEDURE Draw*(canvas : WMGraphics.Canvas; w, h, q : LONGINT);
  30. VAR i:LONGINT;
  31. BEGIN
  32. canvas.Fill(WMRectangles.MakeRect(0, 0, w, h), WMGraphics.White, WMGraphics.ModeCopy);
  33. FOR i:=0 TO LEN(data,0)-1 DO
  34. canvas.Fill(WMRectangles.MakeRect( i*w DIV width , h-ENTIER(data[i]*h / height),
  35. (i+1)*w DIV width , h), WMGraphics.Black, WMGraphics.ModeCopy);
  36. END;
  37. INC(timestamp);
  38. END Draw;
  39. END Histogram;
  40. Graph* = OBJECT (Window);
  41. CONST border=5;
  42. VAR
  43. data:ARRAY [*] OF LONGREAL;
  44. width,height:LONGINT;
  45. max,min:LONGREAL;
  46. PROCEDURE &New*(CONST data: ARRAY [*] OF LONGREAL; CONST title: ARRAY OF CHAR);
  47. BEGIN
  48. SELF.data:=data;
  49. max:=MAX(data);
  50. min:=MIN(0, MIN(data));
  51. width:=LEN(data,0); height:=ENTIER(max-min)+1;
  52. Init(10*width, 10*height, FALSE);
  53. WM.GetDefaultManager().Add(PosX, PosY, SELF, {WM.FlagFrame,WM.FlagClose});
  54. NewWindowPos(GetWidth());
  55. SetTitle(Strings.NewString(title));
  56. SetPointerInfo(manager.pointerCrosshair);
  57. END New;
  58. PROCEDURE Draw*(canvas : WMGraphics.Canvas; w, h, q : LONGINT);
  59. VAR i:LONGINT; mn,mx:LONGINT;
  60. BEGIN
  61. canvas.Fill(WMRectangles.MakeRect(0, 0, w, h), WMGraphics.White, WMGraphics.ModeCopy);
  62. mn:=-border+ENTIER(0.5+min*h / height);
  63. mx:=ENTIER(0.5+max*h / height);
  64. FOR i:=0 TO LEN(data,0)-2 DO
  65. canvas.Line(border+i*w DIV width, h+mn-ENTIER(0.5+data[i]*h / height),
  66. border+(i+1)*w DIV width, h+mn-ENTIER(0.5+data[i+1]*h / height),
  67. WMGraphics.Black, WMGraphics.ModeCopy);
  68. END;
  69. IF mn#0 THEN canvas.Line(0, h+mn, w, h+mn, WMGraphics.Black, WMGraphics.ModeCopy); END;
  70. INC(timestamp);
  71. END Draw;
  72. END Graph;
  73. GraphXY* = OBJECT (Window);
  74. CONST border=5;
  75. VAR
  76. data:ARRAY [*,*] OF LONGREAL;
  77. width,height:LONGINT;
  78. minx,miny,maxx,maxy:LONGREAL;
  79. PROCEDURE &New*(CONST data: ARRAY [*,*] OF LONGREAL; CONST title: ARRAY OF CHAR);
  80. BEGIN
  81. SELF.data:=data;
  82. maxx:=MAX(0,MAX(data[0]));maxy:=MAX(0,MAX(data[1]));
  83. minx:=MIN(0, MIN(data[0])); miny:=MIN(0, MIN(data[1]));
  84. width:=ENTIER(maxx-minx)+1; height:=ENTIER(maxy-miny)+1;
  85. Init(10*width, 10*height, FALSE);
  86. WM.GetDefaultManager().Add(PosX, PosY, SELF, {WM.FlagFrame,WM.FlagClose});
  87. NewWindowPos(GetWidth());
  88. SetTitle(Strings.NewString(title));
  89. SetPointerInfo(manager.pointerCrosshair);
  90. END New;
  91. PROCEDURE Draw*(canvas : WMGraphics.Canvas; w, h, q : LONGINT);
  92. VAR i:LONGINT; mnw,mnh,mxw,mxh:LONGINT; scalex,scaley:REAL;
  93. BEGIN
  94. canvas.Fill(WMRectangles.MakeRect(0, 0, w, h), WMGraphics.White, WMGraphics.ModeCopy);
  95. scalex:=w/width; scaley:=h/height;
  96. mnw:=-border+ENTIER(0.5+minx* scalex); mxw:=ENTIER(0.5+maxx* scalex);
  97. mnh:=-border+ENTIER(0.5+miny* scaley); mxh:=ENTIER(0.5+maxy* scaley);
  98. FOR i:=0 TO LEN(data,1)-2 DO
  99. canvas.Line(-mnw+ENTIER(0.5+data[0,i]*scalex), h+mnh-ENTIER(0.5+data[1,i]*scaley),
  100. -mnw+ENTIER(0.5+data[0,i+1]*scalex), h+mnh-ENTIER(0.5+data[1,i+1]*scaley),
  101. WMGraphics.Blue, WMGraphics.ModeCopy);
  102. END;
  103. IF mnh#0 THEN canvas.Line(0, h+mnh, w, h+mnh, WMGraphics.Black, WMGraphics.ModeCopy) END;
  104. IF mnw#0 THEN canvas.Line(-mnw, 0, -mnw, h, WMGraphics.Black, WMGraphics.ModeCopy) END;
  105. INC(timestamp);
  106. END Draw;
  107. END GraphXY;
  108. (** scatter plot with optional error bars.
  109. data[0,..]: x coordinates
  110. data[1,..]: y coordinates
  111. optional data[2,..]: y error bars
  112. optional data[3,..]: x error bars *)
  113. Scatter* = OBJECT (Window);
  114. CONST border=5;
  115. VAR
  116. data:ARRAY [*,*] OF LONGREAL;
  117. width,height,pointsize:LONGINT;
  118. maxx,maxy,minx,miny:LONGREAL;
  119. PROCEDURE &New*(CONST data: ARRAY [*,*] OF LONGREAL; CONST title: ARRAY OF CHAR);
  120. BEGIN
  121. SELF.data:=data;
  122. SELF.pointsize:= pointsize DIV 2;
  123. maxx:=MAX(0,MAX(data[0]));maxy:=MAX(0,MAX(data[1]));
  124. minx:=MIN(0, MIN(data[0])); miny:=MIN(0, MIN(data[1]));
  125. width:=ENTIER(maxx-minx)+1;
  126. height:=ENTIER(maxy-miny)+1;
  127. Init(10*width, 10*height,FALSE);
  128. WM.GetDefaultManager().Add(PosX, PosY, SELF, {WM.FlagFrame,WM.FlagClose});
  129. NewWindowPos(GetWidth());
  130. SetTitle(Strings.NewString(title));
  131. SetPointerInfo(manager.pointerCrosshair);
  132. END New;
  133. PROCEDURE Draw*(canvas : WMGraphics.Canvas; w, h, q : LONGINT);
  134. VAR i:LONGINT; mnw,mnh,mxw,mxh, x,y, ex,ey:LONGINT; scalex,scaley:REAL; rect:WMRectangles.Rectangle;
  135. BEGIN
  136. canvas.Fill(WMRectangles.MakeRect(0, 0, w, h), WMGraphics.White, WMGraphics.ModeCopy);
  137. scalex:=w/width;
  138. scaley:=h/height;
  139. mnw:=-border+ENTIER(0.5+minx* scalex); mxw:=ENTIER(0.5+maxx* scalex);
  140. mnh:=-border+ENTIER(0.5+miny* scaley); mxh:=ENTIER(0.5+maxy* scaley);
  141. FOR i:=0 TO LEN(data,1)-1 DO
  142. x:=-mnw+ENTIER(0.5+data[0,i]*scalex);
  143. y:=h+mnh-ENTIER(0.5+data[1,i]*scaley);
  144. WMRectangles.SetRect(rect, x-1,y-1,x+2,y+2 );
  145. canvas.Fill(rect, WMGraphics.Black, WMGraphics.ModeCopy);
  146. IF LEN(data,0)>2 THEN (* vertical error bars*)
  147. ey:=ENTIER(0.5+data[2,i]*scaley);
  148. canvas.Line(x, y-ey, x, y+ey,WMGraphics.Blue, WMGraphics.ModeCopy);
  149. IF LEN(data,0)>3 THEN (*horizontal error bars*)
  150. ex:=ENTIER(0.5+data[3,i]*scalex);
  151. canvas.Line(x-ex, y, x+ex, y,WMGraphics.Red, WMGraphics.ModeCopy);
  152. END;
  153. END;
  154. END;
  155. IF mnh#0 THEN canvas.Line(0, h+mnh, w, h+mnh, WMGraphics.Black, WMGraphics.ModeCopy) END;
  156. IF mnw#0 THEN canvas.Line(-mnw, 0, -mnw, h, WMGraphics.Black, WMGraphics.ModeCopy) END;
  157. INC(timestamp);
  158. END Draw;
  159. END Scatter;
  160. TYPE Regression*= OBJECT (Scatter)
  161. VAR slope,intercept: LONGREAL;
  162. PROCEDURE &Initialize*(CONST data: ARRAY [*,*] OF LONGREAL; CONST title: ARRAY OF CHAR; regressor:Regressor);
  163. BEGIN
  164. New(data,title);
  165. regressor(data,slope,intercept);
  166. END Initialize;
  167. PROCEDURE Draw*(canvas : WMGraphics.Canvas; w, h, q : LONGINT);
  168. VAR mnw,mnh,x,y,xx,yy:LONGINT; scalex,scaley, x0, y0, x1, y1:LONGREAL;
  169. BEGIN
  170. Draw^(canvas,w,h,q);
  171. x0:= minx;
  172. y0:= x0*slope+intercept;
  173. IF (y0<miny) THEN
  174. y0:=miny; x0:=(y0-intercept)/slope;
  175. ELSIF y1>maxy THEN
  176. y0:=maxy; x0:=(y0-intercept)/slope;
  177. END;
  178. x1:=maxx;
  179. y1:=x1*slope+intercept;
  180. IF (y1<miny) THEN
  181. y1:=miny; x1:=(y1-intercept)/slope;
  182. ELSIF y1>maxy THEN
  183. y1:=maxy; x1:=(y1-intercept)/slope;
  184. END;
  185. scalex:=w/width;
  186. scaley:=h/height;
  187. mnw:=-border+ENTIER(0.5+minx* scalex);
  188. mnh:=-border+ENTIER(0.5+miny* scaley);
  189. x:=-mnw+ENTIER(0.5+x0*scalex);
  190. y:=h+mnh-ENTIER(0.5+y0*scaley);
  191. xx:=-mnw+ENTIER(0.5+x1*scalex);
  192. yy:=h+mnh-ENTIER(0.5+y1*scaley);
  193. canvas.Line(x,y,xx,yy,WMGraphics.Red, WMGraphics.ModeCopy);
  194. END Draw;
  195. END Regression;
  196. Graphs* = OBJECT (Window);
  197. CONST border=5;
  198. VAR
  199. data:ARRAY [*,*] OF LONGREAL;
  200. width,height:LONGINT;
  201. max,min:LONGREAL;
  202. PROCEDURE &New*(CONST data: ARRAY [*,*] OF LONGREAL; CONST title: ARRAY OF CHAR);
  203. BEGIN
  204. SELF.data:=data;
  205. max:=MAX(data);
  206. min:=MIN(0, MIN(data));
  207. width:=LEN(data,1); height:=ENTIER(max-min)+1;
  208. Init(10*width, 10*height, FALSE);
  209. WM.GetDefaultManager().Add(PosX, PosY, SELF, {WM.FlagFrame,WM.FlagClose});
  210. NewWindowPos(GetWidth());
  211. SetTitle(Strings.NewString(title));
  212. SetPointerInfo(manager.pointerCrosshair);
  213. END New;
  214. PROCEDURE Draw*(canvas : WMGraphics.Canvas; w, h, q : LONGINT);
  215. VAR i,j:LONGINT; mn,mx:LONGINT;
  216. BEGIN
  217. canvas.Fill(WMRectangles.MakeRect(0, 0, w, h), WMGraphics.White, WMGraphics.ModeCopy);
  218. mn:=-border+ENTIER(0.5+min*h / height);
  219. mx:=ENTIER(0.5+max*h / height);
  220. FOR j:=0 TO LEN(data,0)-1 DO
  221. FOR i:=0 TO LEN(data,1)-2 DO
  222. canvas.Line(border+i*w DIV width, h+mn-ENTIER(0.5+data[j,i]*h / height),
  223. border+(i+1)*w DIV width, h+mn-ENTIER(0.5+data[j,i+1]*h / height),
  224. Colors[j MOD LEN(Colors,0)], WMGraphics.ModeCopy);
  225. END;
  226. END;
  227. IF mn#0 THEN canvas.Line(0, h+mn, w, h+mn, WMGraphics.Black, WMGraphics.ModeCopy); END;
  228. INC(timestamp);
  229. END Draw;
  230. END Graphs;
  231. (** display matrix values in checkerboard like fashion. positive values are in black/grey/white, negative values in red*)
  232. Matrix* = OBJECT (Window);
  233. VAR
  234. data:ARRAY [*,*] OF LONGREAL;
  235. width,height:LONGINT;
  236. max,min, offset, gain:LONGREAL;
  237. PROCEDURE &New*(CONST data: ARRAY [*,*] OF LONGREAL; CONST title: ARRAY OF CHAR);
  238. BEGIN
  239. SELF.data:=data;
  240. min:=MIN(data); max:=MAX(data);
  241. max:=MAX(ABS(min), ABS(max));
  242. min:=MIN(0, min);
  243. IF max=0 THEN max:=1 END;
  244. width:=MAX(1,LEN(data,0));
  245. height:=MAX(1,LEN(data,1));
  246. Init(width, height, TRUE);
  247. offset:=0; gain:=255/max;
  248. IF( width<10) OR (height<10) THEN
  249. bounds := WMGraphics.MakeRectangle(0, 0, 10*width, 10*height);(* grow small images *)
  250. END;
  251. WM.GetDefaultManager().Add(PosX, PosY, SELF, {WM.FlagFrame,WM.FlagClose});
  252. NewWindowPos(GetWidth());
  253. SetTitle(Strings.NewString(title));
  254. SetPointerInfo(manager.pointerCrosshair);
  255. END New;
  256. PROCEDURE Draw*(canvas : WMGraphics.Canvas; w, h, q : LONGINT);
  257. VAR col: WMGraphics.Color; x,y:LONGINT; val:LONGREAL; valI:LONGINT;
  258. BEGIN
  259. FOR y:=0 TO LEN(data,0)-1 DO
  260. FOR x:=0 TO LEN(data,1)-1 DO
  261. val:=data[y,x]; IF Reals.IsNaNL(val) THEN val:=0 END;
  262. valI:=ENTIER(offset+gain*val);
  263. valI:=MAX(-255, MIN( 255, valI));
  264. IF valI>=0 THEN col:=WMGraphics.RGBAToColor(valI,valI,valI,255);
  265. ELSE col:=WMGraphics.RGBAToColor(-valI,0,0,255);
  266. END;
  267. canvas.Fill(WMRectangles.MakeRect(x*w DIV width, h-ENTIER(0.5+(y+1)*h/height),
  268. (x+1)*w DIV width, h-ENTIER(0.5+y*h/height)),
  269. col, WMGraphics.ModeCopy);
  270. END;
  271. END;
  272. INC(timestamp);
  273. END Draw;
  274. END Matrix;
  275. PROCEDURE NewWindowPos(dx:LONGINT);
  276. BEGIN
  277. INC(Pos,dx);
  278. PosX:=Pos MOD 900;
  279. PosY:=100+ (Pos DIV 900)*100 MOD 700;
  280. END NewWindowPos;
  281. PROCEDURE DummyRegressor(CONST data: ARRAY [*,*] OF LONGREAL; VAR slope,intercept:LONGREAL);
  282. BEGIN
  283. slope:=1; intercept:=2;
  284. END DummyRegressor;
  285. VAR Pos, PosX,PosY: LONGINT;
  286. PROCEDURE Demo*;
  287. VAR h:Histogram; g:Graph; k: Graphs; gx:GraphXY; m:Matrix; s:Scatter; r:Regression;
  288. BEGIN {EXCLUSIVE}
  289. NEW(h, [4,7,8,4,5,9,6,5,3,2,12,17,3,0,2], "Histogram");
  290. NEW(g, [4,7,8,4,5,9,6,5,3,2,12,17,3,-3,2], "Graph");
  291. NEW(k, [[-2,7,8,4,5,9,6,4,7,8,4,5,9,6],
  292. [5,3,2,12,21,3,0,5,3,-2,12,17,4,1]], "MultiGraph");
  293. NEW(gx, [[0.2,-1,0,1,5,9,6,4,7,3,4,5,9,6],
  294. [0.2,3,4,7,12,3,0,5,3,-2,12,17,4,1]], "GraphXY");
  295. NEW(s, [ [0.2,-1,0,1,5,9,6,4,7,3,4,5,9,6],
  296. [0.2,3,4,7,12,3,0,5,3,-2,12,17,4,1],
  297. [1,1,1,1,1,1,1,1,1,1,1,1,1,1],
  298. [0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5]], "Scatter with x and y error bars");
  299. NEW(r, [ [0.2,-1,0,1,5,9,6,4,7,3,4,5,9,6],
  300. [0.2,3,4,7,12,3,0,5,3,-2,12,17,4,1],
  301. [1,1,1,1,1,1,1,1,1,1,1,1,1,1]], "Regression", DummyRegressor);
  302. NEW(m, [[1,2,3,4],[4,3,2,4],[5,4,-2,-6],[3,1,0,-1]], "Matrix");
  303. END Demo;
  304. PROCEDURE Cleanup;
  305. VAR manager:WM.WindowManager; w,remove:WM.Window;
  306. BEGIN {EXCLUSIVE}
  307. manager:=WM.GetDefaultManager();
  308. manager.lock.AcquireWrite;
  309. w:=manager.GetFirst();
  310. WHILE w#NIL DO
  311. remove:=w;
  312. w:=manager.GetNext(w);
  313. IF (remove#NIL)& (remove IS Window) THEN manager.Remove(remove) END;
  314. END;
  315. manager.lock.ReleaseWrite;
  316. END Cleanup;
  317. BEGIN
  318. Modules.InstallTermHandler(Cleanup);
  319. Pos:=0; NewWindowPos(0);
  320. END WMSimpleGraphs.
  321. SystemTools.Free WMSimpleGraphs ~
  322. WMSimpleGraphs.Demo ~