2
0
Эх сурвалжийг харах

-add scatter plot with error bars {x,y}
-add simiple regression plot
-nicer handling of points on borders

git-svn-id: https://svn.inf.ethz.ch/svn/lecturers/a2/trunk@6515 8c9fc860-2736-0410-a75d-ab315db34111

eth.hunzikerp 9 жил өмнө
parent
commit
b402f7a7ce
1 өөрчлөгдсөн 142 нэмэгдсэн , 25 устгасан
  1. 142 25
      source/WMSimpleGraphs.Mod

+ 142 - 25
source/WMSimpleGraphs.Mod

@@ -1,14 +1,23 @@
 MODULE WMSimpleGraphs;	(** AUTHOR "Patrick Hunziker"; PURPOSE "Minimum-overhead graph drawing and storing"; *)
+
 (** right-click on navigator thumbnail allows window storage as image file *)
 
+(*ToDo: scale ticks and labels on axes *)
+(* ToDo: eliminate code duplications, e.g. in Init() *)
 (*ToDo: catch NaN and Inf in data and other strategies to avoid erratic window sizes*)
 
 IMPORT Strings, WMGraphics, WMRectangles, Modules, Reals, WM:=WMWindowManager;
 
 CONST Colors=[WMGraphics.Red,WMGraphics.Blue,WMGraphics.Green,WMGraphics.Yellow, WMGraphics.Magenta, WMGraphics.Cyan, WMGraphics.Gray];
 
+TYPE 
+	Regressor=PROCEDURE{DELEGATE}(CONST data: ARRAY [*,*] OF LONGREAL; VAR slope,intercept: LONGREAL);
+
 TYPE
-	Histogram* = OBJECT (WM.Window);
+	Window=OBJECT(WM.Window);
+	END Window;
+	
+	Histogram* = OBJECT (Window);
 	VAR 
 		data:ARRAY [*] OF LONGREAL;
 		width,height:LONGINT;
@@ -38,7 +47,8 @@ TYPE
 		END Draw;
 	END Histogram;
 
-	Graph* = OBJECT (WM.Window);
+	Graph* = OBJECT (Window);
+	CONST border=5;
 	VAR 
 		data:ARRAY [*] OF LONGREAL;
 		width,height:LONGINT;
@@ -61,11 +71,11 @@ TYPE
 		VAR i:LONGINT; mn,mx:LONGINT;
 		BEGIN
 			canvas.Fill(WMRectangles.MakeRect(0, 0, w, h), WMGraphics.White, WMGraphics.ModeCopy);
-			mn:=ENTIER(0.5+min*h / height);
+			mn:=-border+ENTIER(0.5+min*h / height);
 			mx:=ENTIER(0.5+max*h / height);
 			FOR i:=0 TO LEN(data,0)-2 DO
-				canvas.Line(i*w DIV width, 	h+mn-ENTIER(0.5+data[i]*h / height),  
-							(i+1)*w DIV width, h+mn-ENTIER(0.5+data[i+1]*h / height), 
+				canvas.Line(border+i*w DIV width, 	h+mn-ENTIER(0.5+data[i]*h / height),  
+							border+(i+1)*w DIV width, h+mn-ENTIER(0.5+data[i+1]*h / height), 
 							WMGraphics.Black, WMGraphics.ModeCopy);
 			END;
 			IF mn#0 THEN canvas.Line(0, h+mn, w, h+mn, WMGraphics.Black, WMGraphics.ModeCopy);	END;
@@ -73,18 +83,19 @@ TYPE
 		END Draw;
 END Graph;
 
-GraphXY* = OBJECT (WM.Window);
+GraphXY* = OBJECT (Window);
+	CONST border=5;
 	VAR 
 		data:ARRAY [*,*] OF LONGREAL;
 		width,height:LONGINT;
-		max,min:LONGREAL;
+		minx,miny,maxx,maxy:LONGREAL;
 
 		PROCEDURE &New*(CONST data: ARRAY [*,*] OF LONGREAL; CONST title: ARRAY OF CHAR);
 		BEGIN
 			SELF.data:=data;
-			max:=MAX(0,MAX(data));
-			min:=MIN(0, MIN(data)); 
-			width:=ENTIER(max-min)+1; height:=ENTIER(max-min)+1;
+			maxx:=MAX(0,MAX(data[0]));maxy:=MAX(0,MAX(data[1]));
+			minx:=MIN(0, MIN(data[0])); miny:=MIN(0, MIN(data[1])); 
+			width:=ENTIER(maxx-minx)+1; height:=ENTIER(maxy-miny)+1;
 			Init(10*width, 10*height, FALSE);
 			WM.GetDefaultManager().Add(PosX, PosY, SELF, {WM.FlagFrame,WM.FlagClose});
 			NewWindowPos(GetWidth());
@@ -97,8 +108,8 @@ GraphXY* = OBJECT (WM.Window);
 		BEGIN
 			canvas.Fill(WMRectangles.MakeRect(0, 0, w, h), WMGraphics.White, WMGraphics.ModeCopy);
 			scalex:=w/width; scaley:=h/height;
-			mnw:=ENTIER(0.5+min* scalex);	mxw:=ENTIER(0.5+max* scalex);
-			mnh:=ENTIER(0.5+min* scaley);	mxh:=ENTIER(0.5+max* scaley);
+			mnw:=-border+ENTIER(0.5+minx* scalex);	mxw:=ENTIER(0.5+maxx* scalex);
+			mnh:=-border+ENTIER(0.5+miny* scaley);	mxh:=ENTIER(0.5+maxy* scaley);
 			FOR i:=0 TO LEN(data,1)-2 DO
 				canvas.Line(-mnw+ENTIER(0.5+data[0,i]*scalex), 	h+mnh-ENTIER(0.5+data[1,i]*scaley),  
 							-mnw+ENTIER(0.5+data[0,i+1]*scalex), h+mnh-ENTIER(0.5+data[1,i+1]*scaley), 
@@ -110,7 +121,104 @@ GraphXY* = OBJECT (WM.Window);
 		END Draw;
 END GraphXY;
 
-Graphs* = OBJECT (WM.Window);
+(** scatter plot with optional error bars. 
+data[0,..]: x coordinates
+data[1,..]: y coordinates
+optional data[2,..]: y error bars
+optional data[3,..]: x error bars *)
+Scatter* = OBJECT (Window);
+	CONST border=5;
+	VAR 
+		data:ARRAY [*,*] OF LONGREAL;
+		width,height,pointsize:LONGINT;
+		maxx,maxy,minx,miny:LONGREAL;
+
+		PROCEDURE &New*(CONST data: ARRAY [*,*] OF LONGREAL; CONST title: ARRAY OF CHAR);
+		BEGIN
+			SELF.data:=data;
+			SELF.pointsize:= pointsize DIV 2;
+			maxx:=MAX(0,MAX(data[0]));maxy:=MAX(0,MAX(data[1]));
+			minx:=MIN(0, MIN(data[0])); miny:=MIN(0, MIN(data[1])); 
+			width:=ENTIER(maxx-minx)+1; 
+			height:=ENTIER(maxy-miny)+1;
+			Init(10*width, 10*height,FALSE);
+			WM.GetDefaultManager().Add(PosX, PosY, SELF, {WM.FlagFrame,WM.FlagClose});
+			NewWindowPos(GetWidth());
+			SetTitle(Strings.NewString(title));
+			SetPointerInfo(manager.pointerCrosshair);
+		END New;
+		
+		PROCEDURE Draw*(canvas : WMGraphics.Canvas; w, h, q : LONGINT);
+		VAR i:LONGINT; mnw,mnh,mxw,mxh, x,y, ex,ey:LONGINT; scalex,scaley:REAL; rect:WMRectangles.Rectangle;
+		BEGIN
+			canvas.Fill(WMRectangles.MakeRect(0, 0, w, h), WMGraphics.White, WMGraphics.ModeCopy);
+			scalex:=w/width; 
+			scaley:=h/height;
+			mnw:=-border+ENTIER(0.5+minx* scalex);	mxw:=ENTIER(0.5+maxx* scalex);
+			mnh:=-border+ENTIER(0.5+miny* scaley);	mxh:=ENTIER(0.5+maxy* scaley);
+			FOR i:=0 TO LEN(data,1)-1 DO
+				x:=-mnw+ENTIER(0.5+data[0,i]*scalex);
+				y:=h+mnh-ENTIER(0.5+data[1,i]*scaley);
+				WMRectangles.SetRect(rect, x-1,y-1,x+2,y+2 );
+				canvas.Fill(rect, WMGraphics.Black, WMGraphics.ModeCopy);
+				IF LEN(data,0)>2 THEN (* vertical error bars*)
+					ey:=ENTIER(0.5+data[2,i]*scaley);
+					canvas.Line(x, y-ey, x, y+ey,WMGraphics.Blue, WMGraphics.ModeCopy);  
+					IF LEN(data,0)>3 THEN (*horizontal error bars*)
+						ex:=ENTIER(0.5+data[3,i]*scalex);
+						canvas.Line(x-ex, y, x+ex, y,WMGraphics.Red, WMGraphics.ModeCopy);  
+					END;
+				END;
+			END;
+			IF mnh#0 THEN canvas.Line(0, h+mnh, w, h+mnh, WMGraphics.Black, WMGraphics.ModeCopy)	END;
+			IF mnw#0 THEN canvas.Line(-mnw, 0, -mnw, h, WMGraphics.Black, WMGraphics.ModeCopy)	END;
+			INC(timestamp);
+		END Draw;
+END Scatter;
+
+TYPE Regression*= OBJECT (Scatter)
+	VAR slope,intercept: LONGREAL;
+	
+	PROCEDURE &Initialize*(CONST data: ARRAY [*,*] OF LONGREAL; CONST title: ARRAY OF CHAR; regressor:Regressor);
+	BEGIN
+		New(data,title);
+		regressor(data,slope,intercept);
+	END Initialize;
+	
+	PROCEDURE Draw*(canvas : WMGraphics.Canvas; w, h, q : LONGINT);
+	VAR mnw,mnh,x,y,xx,yy:LONGINT; scalex,scaley, x0, y0, x1, y1:LONGREAL; 
+	BEGIN
+		Draw^(canvas,w,h,q);
+		x0:= minx; 
+		y0:= x0*slope+intercept;
+		IF (y0<miny) THEN
+			y0:=miny; x0:=(y0-intercept)/slope;
+		ELSIF y1>maxy THEN
+			y0:=maxy; x0:=(y0-intercept)/slope;
+		 END;
+		 x1:=maxx;
+		 y1:=x1*slope+intercept;
+		 IF (y1<miny) THEN
+			y1:=miny; x1:=(y1-intercept)/slope;
+		ELSIF y1>maxy THEN
+			y1:=maxy; x1:=(y1-intercept)/slope;
+		 END;
+		 scalex:=w/width; 
+		scaley:=h/height;
+		mnw:=-border+ENTIER(0.5+minx* scalex);	
+		mnh:=-border+ENTIER(0.5+miny* scaley);	
+		 x:=-mnw+ENTIER(0.5+x0*scalex);
+		 y:=h+mnh-ENTIER(0.5+y0*scaley);
+		 xx:=-mnw+ENTIER(0.5+x1*scalex);
+		 yy:=h+mnh-ENTIER(0.5+y1*scaley);
+		 canvas.Line(x,y,xx,yy,WMGraphics.Red, WMGraphics.ModeCopy);  
+	END Draw;
+	
+	END Regression;
+
+
+Graphs* = OBJECT (Window);
+	CONST border=5;
 	VAR 
 		data:ARRAY [*,*] OF LONGREAL;
 		width,height:LONGINT;
@@ -133,12 +241,12 @@ Graphs* = OBJECT (WM.Window);
 		VAR i,j:LONGINT; mn,mx:LONGINT;
 		BEGIN
 			canvas.Fill(WMRectangles.MakeRect(0, 0, w, h), WMGraphics.White, WMGraphics.ModeCopy);
-			mn:=ENTIER(0.5+min*h / height);
+			mn:=-border+ENTIER(0.5+min*h / height);
 			mx:=ENTIER(0.5+max*h / height);
 			FOR j:=0 TO LEN(data,0)-1 DO
 				FOR i:=0 TO LEN(data,1)-2 DO
-					canvas.Line(i*w DIV width, 	h+mn-ENTIER(0.5+data[j,i]*h / height),  
-								(i+1)*w DIV width, h+mn-ENTIER(0.5+data[j,i+1]*h / height), 
+					canvas.Line(border+i*w DIV width, 	h+mn-ENTIER(0.5+data[j,i]*h / height),  
+								border+(i+1)*w DIV width, h+mn-ENTIER(0.5+data[j,i+1]*h / height), 
 								Colors[j MOD LEN(Colors,0)], WMGraphics.ModeCopy);
 				END;
 			END;
@@ -148,7 +256,7 @@ Graphs* = OBJECT (WM.Window);
 END Graphs;
 
 (** display matrix values in checkerboard like fashion. positive values are in black/grey/white, negative values in red*)
-Matrix* = OBJECT (WM.Window);
+Matrix* = OBJECT (Window);
 	VAR 
 		data:ARRAY [*,*] OF LONGREAL;
 		width,height:LONGINT;
@@ -201,17 +309,29 @@ BEGIN
 	PosY:=100+ (Pos DIV 900)*100 MOD 700;
 END NewWindowPos;
 
+PROCEDURE DummyRegressor(CONST data: ARRAY [*,*] OF LONGREAL; VAR slope,intercept:LONGREAL);
+BEGIN
+	slope:=1; intercept:=2;
+END DummyRegressor;
+
 VAR Pos, PosX,PosY: LONGINT;
 
 PROCEDURE Demo*;
-VAR h:Histogram; g:Graph; k: Graphs; gx:GraphXY; m:Matrix;
+VAR h:Histogram; g:Graph; k: Graphs; gx:GraphXY; m:Matrix; s:Scatter; r:Regression;
 BEGIN {EXCLUSIVE}
 	NEW(h, [4,7,8,4,5,9,6,5,3,2,12,17,3,0,2], "Histogram"); 
 	NEW(g, [4,7,8,4,5,9,6,5,3,2,12,17,3,-3,2], "Graph"); 
 	NEW(k, [[-2,7,8,4,5,9,6,4,7,8,4,5,9,6],
-			[5,3,2,12,21,3,0,5,3,-2,12,17,3,1]], "MultiGraph"); 
-	NEW(gx, [[-2,-1,0,1,5,9,6,4,7,3,4,5,9,6],
-				[1,3,4,7,12,3,0,5,3,-2,12,17,3,1]], "GraphXY"); 
+			[5,3,2,12,21,3,0,5,3,-2,12,17,4,1]], "MultiGraph"); 
+	NEW(gx, [[0.2,-1,0,1,5,9,6,4,7,3,4,5,9,6],
+				[0.2,3,4,7,12,3,0,5,3,-2,12,17,4,1]], "GraphXY"); 
+	NEW(s, [  [0.2,-1,0,1,5,9,6,4,7,3,4,5,9,6],
+				[0.2,3,4,7,12,3,0,5,3,-2,12,17,4,1],
+				[1,1,1,1,1,1,1,1,1,1,1,1,1,1],
+				[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"); 
+	NEW(r, [  [0.2,-1,0,1,5,9,6,4,7,3,4,5,9,6],
+				[0.2,3,4,7,12,3,0,5,3,-2,12,17,4,1],
+				[1,1,1,1,1,1,1,1,1,1,1,1,1,1]], "Regression", DummyRegressor); 
 	NEW(m, [[1,2,3,4],[4,3,2,4],[5,4,-2,-6],[3,1,0,-1]], "Matrix"); 
 END Demo;
 
@@ -224,10 +344,7 @@ BEGIN {EXCLUSIVE}
 	WHILE w#NIL DO
 		remove:=w;
 		w:=manager.GetNext(w);
-		IF (remove#NIL)&
-			((remove IS Histogram) OR  (remove IS Graph)  OR  (remove IS GraphXY) OR  (remove IS Graphs) OR (remove IS Matrix))
-		THEN manager.Remove(remove);
-		END;
+		IF (remove#NIL)& (remove IS Window) THEN manager.Remove(remove) END;
 	END;
 	manager.lock.ReleaseWrite;
 END Cleanup;