MODULE Curves; (*NW 8.11.90 / 1.2.91*)
	IMPORT Display, Files, Printer, Oberon, Graphics, GraphicFrames;

	TYPE Curve* = POINTER TO CurveDesc;

		CurveDesc* = RECORD (Graphics.ObjectDesc)
				kind*, lw*: INTEGER
			END ;

		(*kind: 0 = up-line, 1 = down-line, 2 = circle, 3 = ellipse*)

		VAR method*: Graphics.Method;

	PROCEDURE dot(f: GraphicFrames.Frame; col: INTEGER; x, y: LONGINT);
	BEGIN
		IF (f.X <= x) & (x < f.X1) & (f.Y <= y) & (y < f.Y1) THEN Display.Dot(col, x, y, 0) END
	END dot;

	PROCEDURE mark(f: GraphicFrames.Frame; col, x, y: INTEGER);
	BEGIN
		IF (f.X <= x) & (x+4 < f.X1) & (f.Y <= y) & (y+4 < f.Y1) THEN
			Display.ReplConst(col, x, y, 4, 4, 0)
		END
	END mark;

	PROCEDURE line(f: GraphicFrames.Frame; col: INTEGER; x, y, w, h, d: LONGINT);
		VAR x1, y1, u: LONGINT;
	BEGIN
		IF h < w THEN
			x1 := x+w; u := (h-w) DIV 2;
			IF d = -1 THEN INC(y, h) END ;
			WHILE x < x1 DO
				dot(f, col, x, y); INC(x);
				IF u < 0 THEN INC(u, h) ELSE INC(u, h-w); INC(y, d) END
			END
		ELSE y1 := y+h; u := (w-h) DIV 2;
			IF d = -1 THEN INC(x, w) END ;
			WHILE y < y1 DO
				dot(f, col, x, y); INC(y);
				IF u < 0 THEN INC(u, w) ELSE INC(u, w-h); INC(x, d) END
			END
		END
	END line;

	PROCEDURE circle(f: GraphicFrames.Frame; col: INTEGER; x0, y0, r: LONGINT);
		VAR x, y, u: LONGINT;
	BEGIN u := 1 - r; x := r; y := 0;
		WHILE y <= x DO
			dot(f, col, x0+x, y0+y); dot(f, col, x0+y, y0+x); dot(f, col, x0-y, y0+x);
			dot(f, col, x0-x, y0+y);
			dot(f, col, x0-x, y0-y); dot(f, col, x0-y, y0-x); dot(f, col, x0+y, y0-x); dot(f, col, x0+x, y0-y);
			IF u < 0 THEN INC(u, 2*y+3) ELSE INC(u, 2*(y-x)+5); DEC(x) END ;
			INC(y)
		END
	END circle;

	PROCEDURE ellipse(f: GraphicFrames.Frame; col: INTEGER; x0, y0, a, b: LONGINT);
		VAR x, y, y1, aa, bb, d, g, h: LONGINT;
	BEGIN aa := a*a; bb := b*b;
		h := (aa DIV 4) - b*aa + bb; g := (9*aa DIV 4) - 3*b*aa + bb; x := 0; y := b;
		WHILE g < 0 DO
			dot(f, col, x0+x, y0+y); dot(f, col, x0-x, y0+y); dot(f, col, x0-x, y0-y); dot(f, col, x0+x, y0-y);
			IF h < 0 THEN d := (2*x+3)*bb; INC(g, d)
			ELSE d := (2*x+3)*bb - 2*(y-1)*aa; INC(g, d + 2*aa); DEC(y)
			END ;
			INC(h, d); INC(x)
		END ;
		y1 := y; h := (bb DIV 4) - a*bb + aa; x := a; y := 0;
		WHILE y <= y1 DO
			dot(f, col, x0+x, y0+y); dot(f, col, x0-x, y0+y); dot(f, col, x0-x, y0-y); dot(f, col, x0+x, y0-y);
			IF h < 0 THEN INC(h, (2*y+3)*aa) ELSE INC(h, (2*y+3)*aa - 2*(x-1)*bb); DEC(x) END ;
			INC(y)
		END
	END ellipse;

	PROCEDURE New*;
		VAR c: Curve;
	BEGIN NEW(c); c.do := method; Graphics.new := c
	END New;

	PROCEDURE* Copy(src, dst: Graphics.Object);
	BEGIN dst(Curve)^ := src(Curve)^
	END Copy;

	PROCEDURE* Draw(obj: Graphics.Object; VAR M: Graphics.Msg);
		VAR x, y, w, h, col: INTEGER; f: GraphicFrames.Frame;
	BEGIN
		WITH M: GraphicFrames.DrawMsg DO
			x := obj.x + M.x; y := obj.y + M.y; w := obj.w; h := obj.h; f := M.f;
			IF M.col = Display.black THEN col := obj.col ELSE col := M.col END ;
			IF (x < f.X1) & (f.X <= x+w) & (y < f.Y1) & (f.Y <= y+h) THEN
				IF obj(Curve).kind = 0 THEN (*up-line*)
					IF M.mode = 0 THEN
						IF obj.selected THEN mark(f, Display.white, x, y) END ;
						line(f, col, x, y, w, h, 1)
					ELSIF M.mode = 1 THEN mark(f, Display.white, x, y)
					ELSIF M.mode = 2 THEN mark(f, Display.black, x, y)
					ELSE mark(f, Display.black, x, y); line(f, Display.black, x, y, w, h, 1)
					END
				ELSIF obj(Curve).kind = 1 THEN (*down-line*)
					IF M.mode = 0 THEN
						IF obj.selected THEN mark(f, Display.white, x, y+h) END ;
						line(f, col, x, y, w, h, -1)
					ELSIF M.mode = 1 THEN mark(f, Display.white, x, y+h)
					ELSIF M.mode = 2 THEN mark(f, Display.black, x, y+h)
					ELSE mark(f, Display.black, x, y+h); line(f, Display.black, x, y, w, h, -1)
					END
				ELSIF obj(Curve).kind = 2 THEN (*circle*)
					w := w DIV 2;
					IF M.mode = 0 THEN
						IF obj.selected THEN mark(f, Display.white, x+w, y-4) END ;
						circle(f, col, x+w, y+w, w)
					ELSIF M.mode = 1 THEN mark(f, Display.white, x+w, y-4)
					ELSIF M.mode = 2 THEN mark(f, Display.black, x+w, y-4)
					ELSE mark(f, Display.black, x+w, y-4); circle(f, Display.black, x+w, y+w, w)
					END
				ELSIF obj(Curve).kind = 3 THEN (*ellipse*)
					w := w DIV 2; h := h DIV 2;
					IF M.mode = 0 THEN
						IF obj.selected THEN mark(f, Display.white, x+w, y-4) END ;
						ellipse(f, col, x+w, y+h, w, h)
					ELSIF M.mode = 1 THEN mark(f, Display.white, x+w, y-4)
					ELSIF M.mode = 2 THEN mark(f, Display.black, x+w, y-4)
					ELSE mark(f, Display.black, x+w, y-4); ellipse(f, Display.black, x+w, y+h, w, h)
					END
				END
			END
		END
	END Draw;

	PROCEDURE* Selectable(obj: Graphics.Object; x, y: INTEGER): BOOLEAN;
		VAR xm, y0, w, h: INTEGER;
	BEGIN
		IF obj(Curve).kind <= 1 THEN (*line*)
			w := obj.w; h := obj.h;
			IF obj(Curve).kind = 1 THEN y0 := obj.y + h; h := -h ELSE y0 := obj.y END ;
			RETURN
				(obj.x <= x) & (x < obj.x + w) & (ABS(LONG(y-y0)*w - LONG(x-obj.x)*h) < w*4)
		ELSE (*circle or ellipse*)
			xm := obj.w DIV 2 + obj.x;
			RETURN (xm - 4 <= x) & (x <= xm + 4) & (obj.y - 4 <= y) & (y <= obj.y + 4)
		END
	END Selectable;

	PROCEDURE* Handle(obj: Graphics.Object; VAR M: Graphics.Msg);
	BEGIN
		IF M IS Graphics.ColorMsg THEN obj.col := M(Graphics.ColorMsg).col END
	END Handle;

	PROCEDURE* Read(obj: Graphics.Object; VAR R: Files.Rider; VAR C: Graphics.Context);
		VAR len: SHORTINT;
	BEGIN Files.Read(R, len); Graphics.ReadInt(R, obj(Curve).kind);
		Graphics.ReadInt(R, obj(Curve).lw)
	END Read;

	PROCEDURE* Write(obj: Graphics.Object; cno: SHORTINT;
		VAR W: Files.Rider; VAR C: Graphics.Context);
	BEGIN Graphics.WriteObj(W, cno, obj);
		Files.Write(W, 4); Graphics.WriteInt(W, obj(Curve).kind);
		Graphics.WriteInt(W, obj(Curve).lw)
	END Write;

	PROCEDURE* Print(obj: Graphics.Object; x, y: INTEGER);
		VAR x0, y0: INTEGER;
	BEGIN
		IF obj(Curve).kind = 0 THEN
			x0 := obj.x * 4 + x; y0 := obj.y * 4 + y;
			Printer.Line(x0, y0, obj.w * 4 + x0, obj.h * 4 + y0)
		ELSIF obj(Curve).kind = 1 THEN
			x0 := obj.x * 4 + x; y0 := obj.y * 4 + y;
			Printer.Line(x0, obj.h * 4 + y0, obj.w * 4 + x0, y0)
		ELSIF obj(Curve).kind = 2 THEN
			Printer.Circle((obj.x*2 + obj.w)*2 + x, (obj.y*2 + obj.h)*2 + y, obj.w*2)
		ELSE
			Printer.Ellipse((obj.x*2 + obj.w)*2 + x, (obj.y*2 + obj.h)*2 + y, obj.w*2, obj.h*2)
		END
	END Print;

	PROCEDURE MakeLine*; (*command*)
		VAR x0, x1, y0, y1: INTEGER;
			c: Curve;
			G: GraphicFrames.Frame;
	BEGIN G := GraphicFrames.Focus();
		IF (G # NIL) & (G.mark.next # NIL) THEN
			GraphicFrames.Deselect(G);
			x0 := G.mark.x; y0 := G.mark.y; x1 := G.mark.next.x; y1 := G.mark.next.y;
			NEW(c); c.col := Oberon.CurCol;
			c.w := ABS(x1-x0); c.h := ABS(y1-y0); c.lw := Graphics.width;
			IF x0 <= x1 THEN c.x := x0;
				IF y0 <= y1 THEN c.kind := 0; c.y := y0 ELSE c.kind := 1; c.y := y1 END
			ELSE c.x := x1;
				IF y1 < y0 THEN c.kind := 0; c.y := y1 ELSE c.kind := 1; c.y := y0 END
			END ;
			DEC(c.x, G.x); DEC(c.y, G.y); c.do := method;
			Graphics.Add(G.graph, c);
			GraphicFrames.Defocus(G); GraphicFrames.DrawObj(G, c)
		END
	END MakeLine;

	PROCEDURE MakeCircle*; (*command*)
		VAR x0, y0, r: INTEGER;
			c: Curve;
			G: GraphicFrames.Frame;
	BEGIN G := GraphicFrames.Focus();
		IF (G # NIL) & (G.mark.next # NIL) THEN
			GraphicFrames.Deselect(G);
			x0 := G.mark.x; y0 := G.mark.y; r := ABS(G.mark.next.x-x0);
			IF r > 4 THEN
				NEW(c); c.x := x0 - r - G.x; c.y := y0 - r - G.y; c.w := 2*r+1; c.h := c.w;
				c.kind := 2; c.col := Oberon.CurCol;
				c.lw := Graphics.width; c.do := method;
				Graphics.Add(G.graph, c);
				GraphicFrames.Defocus(G); GraphicFrames.DrawObj(G, c)
			END
		END
	END MakeCircle;

	PROCEDURE MakeEllipse*; (*command*)
		VAR x0, y0, a, b: INTEGER;
			c: Curve;
			G: GraphicFrames.Frame;
	BEGIN G := GraphicFrames.Focus();
		IF (G # NIL) & (G.mark.next # NIL) & (G.mark.next.next # NIL) THEN
			GraphicFrames.Deselect(G);
			x0 := G.mark.x; y0 := G.mark.y;
			a := ABS(G.mark.next.x-x0); b := ABS(G.mark.next.next.y - y0);
			IF (a > 4) & (b > 4) THEN
				NEW(c); c.x := x0 - a - G.x; c.y := y0 - b - G.y; c.w := 2*a+1; c.h := 2*b+1;
				c.kind := 3; c.col := Oberon.CurCol;
				c.lw := Graphics.width; c.do := method;
				Graphics.Add(G.graph, c);
				GraphicFrames.Defocus(G); GraphicFrames.DrawObj(G, c)
			END
		END
	END MakeEllipse;

BEGIN NEW(method); method.module := "Curves"; method.allocator := "New";
	method.new := New; method.copy := Copy; method.draw := Draw;
	method.selectable := Selectable; method.handle := Handle;
	method.read := Read; method.write := Write; method.print := Print
END Curves.