Syntax10.Scn.Fnt Syntax10b.Scn.Fnt InfoElems Alloc Syntax10.Scn.Fnt StampElems Alloc 2 Apr 96 "Title": CrazyFiller "Author": Christoph Steindl (CS) "Abstract": Implements a new handler for the filler viewers. Filler viewers are dummy viewers which are visible if no other viewers are on the screen (as well in the user track as in the system track). Then the filler viewers are painted with Mandlebrot sets. You can zoom into the figures selecting a rectangular area with the left mouse. You can restore the initial figure by pressing the setup button. "Keywords": filler "Version": 1.0 "From": 02.02.95 16:26:50 "Until": "Changes": selection is restricted so that zoomed area fits into the filler viewer without distortion "Hints": Use System.Open CrazyFiller.Tool Syntax10i.Scn.Fnt FoldElems Syntax10.Scn.Fnt redraw Syntax10.Scn.Fnt define new zooming area Syntax10.Scn.Fnt restore to full size MODULE CrazyFiller; (* Christoph Steindl (CS) 02.02.95 - 10 Feb 95 *) IMPORT Display, Viewers, Oberon, In, Out, Input; CONST ML = 2; MM = 1; MR = 0; (* mouse keys *) filler = 1; bound = 10; CrazyFiller* = POINTER TO CrazyFillerDesc; Drawer* = POINTER TO DrawerDesc; Region* = POINTER TO RegionDesc; DrawerDesc* = RECORD (Oberon.TaskDesc) filler: CrazyFiller; dx, dy: LONGREAL; END; CrazyFillerDesc* = RECORD; vwr: Viewers.Viewer; regions: Region; drawer: Drawer; xMin, xMax, yMin, yMax: LONGREAL END; RegionDesc* = RECORD x, y, w, h: INTEGER; next: Region END; fillerHandler: Display.Handler; userFiller, systemFiller: CrazyFiller; maxIter*: INTEGER; regsPerCycle*: INTEGER; PROCEDURE Min(x, y: INTEGER): INTEGER; BEGIN IF x < y THEN RETURN x ELSE RETURN y END END Min; PROCEDURE Max(x, y: INTEGER): INTEGER; BEGIN IF x > y THEN RETURN x ELSE RETURN y END END Max; PROCEDURE DrawMandelbrodt; VAR this: Drawer; p, q, h1, h2, x, y, x0, y0: LONGREAL; filler: CrazyFiller; region: Region; k1, k2, k3, k4, k5, i, j, count: INTEGER; allBlack: BOOLEAN; PROCEDURE Dot (col, x, y: INTEGER); BEGIN IF col = maxIter THEN Display.ReplConst(Display.white, x, y, 1, 1, Display.replace) ELSE Display.ReplConst(col MOD 15, x, y, 1, 1, Display.replace) END END Dot; PROCEDURE Eval (i, j: INTEGER; VAR k: INTEGER); BEGIN k := 0; x := 0; y := 0; p := filler.xMin + (i - filler.vwr.X) * this.dx; q := filler.yMin + (j - filler.vwr.Y) * this.dy; REPEAT h1 := x * x; h2 := y * y; x0 := h1 - h2 + p; y0 := 2 * x * y + q; x := x0; y := y0; INC(k) UNTIL (k >= maxIter) OR (h1 + h2 > bound); END Eval; PROCEDURE Divide (x, y, w, h: INTEGER; VAR regions: Region); VAR xHalf, yHalf: INTEGER; tmp: Region; BEGIN xHalf := w DIV 2; yHalf := h DIV 2; IF xHalf # 0 THEN IF yHalf # 0 THEN NEW(tmp); tmp.x := x; tmp.y := y; tmp.w := xHalf; tmp.h := yHalf; tmp.next := regions; regions := tmp; NEW(tmp); tmp.x := x + xHalf; tmp.y := y; tmp.w := w - xHalf; tmp.h := yHalf; tmp.next := regions; regions := tmp; NEW(tmp); tmp.x := x; tmp.y := y + yHalf; tmp.w := xHalf; tmp.h := h - yHalf; tmp.next := regions; regions := tmp; NEW(tmp); tmp.x := x + xHalf; tmp.y := y + yHalf; tmp.w := w - xHalf; tmp.h := h - yHalf; tmp.next := regions; regions := tmp; ELSE NEW(tmp); tmp.x := x; tmp.y := y; tmp.w := xHalf; tmp.h := 1; tmp.next := regions; regions := tmp; NEW(tmp); tmp.x := x + xHalf; tmp.y := y; tmp.w := w - xHalf; tmp.h := 1; tmp.next := regions; regions := tmp; END ELSE IF yHalf # 0 THEN NEW(tmp); tmp.x := x; tmp.y := y; tmp.w := 1; tmp.h := yHalf; tmp.next := regions; regions := tmp; NEW(tmp); tmp.x := x; tmp.y := y + yHalf; tmp.w := 1; tmp.h := h - yHalf; tmp.next := regions; regions := tmp; ELSE Eval(x, y, xHalf); Dot(xHalf, x, y) END END END Divide; BEGIN this := Oberon.CurTask(Drawer); filler := this.filler; region := filler.regions; filler.regions := filler.regions.next; count := regsPerCycle; WHILE (count > 0) & (region # NIL) DO Eval(region.x, region.y, k1); Eval(region.x + region.w - 1, region.y, k2); Eval(region.x, region.y + region.h - 1, k3); Eval(region.x + region.w - 1, region.y + region.h - 1, k4); Dot(k1, region.x, region.y); Dot(k2, region.x + region.w - 1, region.y); Dot(k3, region.x, region.y + region.h - 1); Dot(k4, region.x + region.w - 1, region.y + region.h - 1); allBlack := (k1 = k2) & (k2 = k3) & (k3 = k4); FOR i := region.x + 1 TO region.x + region.w - 2 DO Eval(i, region.y, k5); Dot(k5, i, region.y); allBlack := allBlack & (k5 = k1); Eval(i, region.y + region.h - 1, k5); Dot(k5, i, region.y + region.h - 1); allBlack := allBlack & (k5 = k1) END; FOR j := region.y + 1 TO region.y + region.h - 2 DO Eval(region.x, j, k5); Dot(k5, region.x, j); allBlack := allBlack & (k5 = k1); Eval(region.x + region.w - 1, j, k5); Dot(k5, region.x + region.w - 1, j); allBlack := allBlack & (k5 = k1) END; IF allBlack & (region.w > 2) & (region.h > 2) THEN IF k1 = maxIter THEN Display.ReplConst(Display.white, region.x + 1, region.y + 1, region.w - 2, region.h - 2, Display.replace) ELSE Display.ReplConst(k1 MOD 15, region.x + 1, region.y + 1, region.w - 2, region.h - 2, Display.replace) END ELSIF (region.w > 2) & (region.h > 2) THEN Divide(region.x + 1, region.y + 1, region.w - 2, region.h - 2, filler.regions); END; DEC(count); region := filler.regions; IF (filler.regions # NIL) & (count > 0) THEN filler.regions := filler.regions.next END END; IF region = NIL THEN Oberon.Remove(this) END END DrawMandelbrodt; PROCEDURE DragRect (filler: CrazyFiller; f: Display.Frame; x0, y0, x1, y1: INTEGER; VAR x2, y2: INTEGER; VAR keysum: SET); VAR keys: SET; x, y: INTEGER; PROCEDURE ReplConst(x, y, w, h: INTEGER); BEGIN IF w < 0 THEN x := x + w; w := - w END; IF h < 0 THEN y := y + h; h := - h END; IF (w # 0) & (h # 0) THEN Display.ReplConst(Display.white, x, y, w, h, Display.invert) END END ReplConst; PROCEDURE FlipRect(x0, y0, x1, y1, x2, y2: INTEGER); BEGIN ReplConst(x0 + 1, y1, x1 - x0 - 2, 1); ReplConst(x1 - 1, y1, 1, y0 - y1); ReplConst(x1 - 1, y0 - 1, x2 - x1, 1); ReplConst(x2 - 1, y2, 1, y0 - y2); ReplConst(x0 + 1, y2, x2 - x0 - 2, 1); ReplConst(x0, y2, 1, y1 - y2) END FlipRect; BEGIN keys := keysum; FlipRect(x0, y0, x0 + 1, y0 - 1, x1, y1); (* draw initial rectangle *) WHILE keys # {} DO Input.Mouse(keys, x, y); Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, x, y); keysum := keysum + keys; x2 := Min(Max(x, f.X), f.X + f.W); (* confine x2 to frame f *) y2 := Min(Max(y, f.Y), f.Y + f.H); (* confine y2 to frame f *) IF y2 < SHORT(ENTIER(y0 - ABS(x2 - x0) * filler.vwr.H / filler.vwr.W + 0.5)) THEN y2 := SHORT(ENTIER(y0 - ABS(x2 - x0) * filler.vwr.H / filler.vwr.W + 0.5)) ELSIF y2 > SHORT(ENTIER(y0 + ABS(x2 - x0) * filler.vwr.H / filler.vwr.W + 0.5)) THEN y2 := SHORT(ENTIER(y0 + ABS(x2 - x0) * filler.vwr.H / filler.vwr.W + 0.5)) END; IF x2 < SHORT(ENTIER(x0 - ABS(y2 - y0) * filler.vwr.W / filler.vwr. H + 0.5)) THEN x2 := SHORT(ENTIER(x0 - ABS(y2 - y0) * filler.vwr.W / filler.vwr. H + 0.5)) ELSIF x2 > SHORT(ENTIER(x0 + ABS(y2 - y0) * filler.vwr.W / filler.vwr.H + 0.5)) THEN x2 := SHORT(ENTIER(x0 + ABS(y2 - y0) * filler.vwr.W / filler.vwr. H + 0.5)) END; IF (x2 # x1) OR (y2 # y1) THEN FlipRect(x0, y0, x1, y1, x2, y2); x1 := x2; y1 := y2 END END; FlipRect(x0, y0, x0 + 1, y0 - 1, x1, y1) (* erase spanned rectangle *) END DragRect; PROCEDURE InitDrawer* (VAR drawer: DrawerDesc; W, H: INTEGER; filler: CrazyFiller; draw: Oberon.Handler); BEGIN drawer.handle := draw; drawer.safe := FALSE; drawer.filler := filler; drawer.dx := (drawer.filler.xMax - drawer.filler.xMin) / W; drawer.dy := (drawer.filler.yMax - drawer.filler.yMin) / H; END InitDrawer; PROCEDURE InitFiller (filler: CrazyFiller; vwr: Viewers.Viewer); BEGIN filler.xMin := -2.25; filler.xMax := 0.75; filler.yMin := -1.125; filler.yMax := 1.125; filler.vwr := vwr; END InitFiller; PROCEDURE InstallCustomHandler* (h: Display.Handler); VAR m: Viewers.ViewerMsg; BEGIN IF h = fillerHandler THEN RETURN END; m.id := Viewers.restore; IF userFiller.regions # NIL THEN userFiller.regions := NIL; Oberon.Remove(userFiller.drawer) END; userFiller.vwr.handle := h; userFiller.vwr.handle(userFiller.vwr, m); IF systemFiller.regions # NIL THEN systemFiller.regions := NIL; Oberon.Remove(systemFiller.drawer) END; systemFiller.vwr.handle := h; systemFiller.vwr.handle(systemFiller.vwr, m) END InstallCustomHandler; PROCEDURE DefaultHandler* (f: Display.Frame; VAR m: Display.FrameMsg); BEGIN WITH f: Viewers.Viewer DO IF m IS Oberon.InputMsg THEN WITH m: Oberon.InputMsg DO IF m.id=Oberon.track THEN Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, m.X, m.Y) END END ELSIF m IS Oberon.ControlMsg THEN WITH m: Oberon.ControlMsg DO IF m.id=Oberon.mark THEN Oberon.DrawCursor(Oberon.Pointer, Oberon.Star, m.X, m.Y) END END ELSIF m IS Viewers.ViewerMsg THEN WITH m: Viewers.ViewerMsg DO IF (m.id=Viewers.restore) & (f.W > 0) & (f.H > 0) THEN Oberon.RemoveMarks(f.X, f.Y, f.W, f.H); Display.ReplConst(Display.black, f.X, f.Y, f.W, f.H, Display.replace) ELSIF (m.id=Viewers.modify) & (m.Y < f.Y) THEN Oberon.RemoveMarks(f.X, m.Y, f.W, f.Y-m.Y); Display.ReplConst(Display.black, f.X, m.Y, f.W, f.Y-m.Y, Display.replace) END END END END DefaultHandler; PROCEDURE CrazyHandler* (f: Display.Frame; VAR m: Display.FrameMsg); VAR drawer: Drawer; x, y: INTEGER; filler, oldFiller: CrazyFiller; redrawMsg: Viewers.ViewerMsg; PROCEDURE Redraw(y, h: INTEGER); VAR region: Region; BEGIN IF filler.regions # NIL THEN filler.regions := NIL; Oberon.Remove(filler.drawer) END; Oberon.RemoveMarks(f.X, f.Y, f.W, f.H); NEW(drawer); InitDrawer(drawer^, f.W, h, filler, DrawMandelbrodt); filler.drawer := drawer; NEW(region); region.x := f.X; region.y := y; region.w := f.W; region.h := h; filler.regions := region; Display.ReplConst(Display.black, f.X, y, f.W, h, Display.replace); Oberon.Install(drawer) END Redraw; BEGIN WITH f: Viewers.Viewer DO WITH m: Viewers.ViewerMsg DO IF f.X = 0 THEN filler := userFiller ELSE filler := systemFiller END; IF m.id = Viewers.restore THEN IF (f.W > 0) & (f.H > 0) THEN Redraw(f.Y, f.H) ELSE IF filler.regions # NIL THEN filler.regions := NIL; Oberon.Remove(filler.drawer) END END ELSIF m.id = Viewers.modify THEN Redraw(m.Y, m.H) ELSIF m.id = Viewers.suspend THEN IF filler.regions # NIL THEN filler.regions := NIL; Oberon.Remove(filler.drawer) END END | m: Oberon.InputMsg DO IF m.id = Oberon.track THEN (* mouse event *) Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, m.X, m.Y); IF ML IN m.keys THEN IF m.X < userFiller.vwr.X + userFiller.vwr.W THEN (* click in user filler *) filler := userFiller ELSE filler := systemFiller END; DragRect(filler, f, m.X, m.Y, m.X + 2, m.Y - 2, x, y, m.keys); (* m.X, m.Y is the upper left corner; x, y is the lower right corner *) IF m.keys # {ML, MM, MR} THEN NEW(oldFiller); oldFiller^ := filler^; filler.yMin := oldFiller.yMin + (oldFiller.yMax - oldFiller.yMin) / oldFiller.vwr.H * (Min(y, m.Y) - oldFiller.vwr.Y); filler.yMax := oldFiller.yMin + (oldFiller.yMax - oldFiller.yMin) / oldFiller.vwr.H * (Max(y, m.Y) - oldFiller.vwr.Y); filler.xMin := oldFiller.xMin + (oldFiller.xMax - oldFiller.xMin) / oldFiller.vwr.W * (Min(x, m.X) - oldFiller.vwr.X); filler.xMax := oldFiller.xMin + (oldFiller.xMax - oldFiller.xMin) / oldFiller.vwr.W * (Max(x, m.X) - oldFiller.vwr.X); redrawMsg.id := Viewers.restore; filler.vwr.handle(filler.vwr, redrawMsg); END END ELSE DefaultHandler(f, m) END | m: Oberon.ControlMsg DO IF m.id = Oberon.neutralize THEN userFiller.xMin := -2.25; userFiller.xMax := 0.75; userFiller.yMin := -1.125; userFiller.yMax := 1.125; systemFiller.xMin := -2.25; systemFiller.xMax := 0.75; systemFiller.yMin := -1.125; systemFiller.yMax := 1.125; redrawMsg.id := Viewers.restore; userFiller.vwr.handle(userFiller.vwr, redrawMsg); systemFiller.vwr.handle(systemFiller.vwr, redrawMsg) ELSE DefaultHandler(f, m) END ELSE DefaultHandler(f, m) END END CrazyHandler; PROCEDURE InstallDefault*; BEGIN InstallCustomHandler(DefaultHandler) END InstallDefault; PROCEDURE InstallCrazy*; BEGIN InstallCustomHandler(CrazyHandler) END InstallCrazy; PROCEDURE SetMaxIter*; BEGIN In.Open; In.Int(maxIter) END SetMaxIter; PROCEDURE SetRegsPerCycle*; BEGIN In.Open; In.Int(regsPerCycle) END SetRegsPerCycle; PROCEDURE ShowParams*; BEGIN IF (userFiller.vwr # NIL) & (userFiller.vwr.H > 0) THEN Out.Ln; Out.String("User filler:"); Out.Ln; Out.String(" Range:"); Out.Ln; Out.String(" xMin = "); Out.LongReal(userFiller.xMin, 20); Out.String(", xMax = "); Out.LongReal(userFiller.xMax, 20); Out.Ln; Out.String(" yMin = "); Out.LongReal(userFiller.yMin, 20); Out.String(", yMax = "); Out.LongReal(userFiller.yMax, 20); Out.Ln; Out.String(" Height: "); Out.Int(userFiller.vwr.H, 0); Out.Ln; Out.String(" Width: "); Out.Int(userFiller.vwr.W, 0); Out.Ln; Out.String(" Iterations: "); Out.Int(maxIter, 0); Out.Ln; Out.String(" Bound: "); Out.Int(bound, 0); Out.Ln; Out.String(" Rectangles per cycle: "); Out.Int(regsPerCycle, 0) END; IF (systemFiller.vwr # NIL) & (systemFiller.vwr.H > 0) THEN Out.Ln; Out.String("System filler:"); Out.Ln; Out.String(" Range:"); Out.Ln; Out.String(" xMin = "); Out.LongReal(systemFiller.xMin, 20); Out.String(", xMax = "); Out.LongReal(systemFiller.xMax, 20); Out.Ln; Out.String(" yMin = "); Out.LongReal(systemFiller.yMin, 20); Out.String(", yMax = "); Out.LongReal(systemFiller.yMax, 20); Out.Ln; Out.String(" Height: "); Out.Int(systemFiller.vwr.H, 0); Out.Ln; Out.String(" Width: "); Out.Int(systemFiller.vwr.W, 0); Out.Ln; Out.String(" Iterations: "); Out.Int(maxIter, 0); Out.Ln; Out.String(" Bound: "); Out.Int(bound, 0); Out.Ln; Out.String(" Rectangles per cycle: "); Out.Int(regsPerCycle, 0) END ShowParams; PROCEDURE Init; VAR cur: Viewers.Viewer; BEGIN fillerHandler := NIL; maxIter := 100; regsPerCycle := 20; NEW(userFiller); NEW(systemFiller); cur := Viewers.This(0, 0); WHILE cur.state # filler DO cur := Viewers.Next(cur) END; InitFiller(userFiller, cur); cur := Viewers.This(cur.W, 0); WHILE cur.state # filler DO cur := Viewers.Next(cur) END; InitFiller(systemFiller, cur) END Init; BEGIN Init END CrazyFiller.InstallCrazy CrazyFiller.InstallDefault CrazyFiller.ShowParams CrazyFiller.SetMaxIter 30