Syntax10.Scn.Fnt MODULE KeplerGraphs; (* J. Templ, 30.10.90 *) IMPORT SYSTEM, KeplerPorts, Display, Files, Oberon, Modules, Types, Texts; CONST draw* = 0; restore* = 1; (* notify op-codes *) ptSize = 12; maxNofpts = 4; (* graph = {star} {configuration} 0X. star = header contents. configuration = header contents. header = typeref [typename]. typeref = compact-integer. typename = qualident 0X. contents = {byte}. *) TYPE Object* = POINTER TO ObjectDesc; ObjectDesc* = RECORD END ; Star* = POINTER TO StarDesc; StarDesc* = RECORD (ObjectDesc) x*, y*, refcnt*, ref: INTEGER; sel*: BOOLEAN; next* : Star; END ; Constellation* = POINTER TO ConsDesc; ConsDesc* = RECORD (ObjectDesc) nofpts*: INTEGER; p*: ARRAY maxNofpts OF Star; next*: Constellation; END ; Planet* = POINTER TO PlanetDesc; PlanetDesc* = RECORD (StarDesc) c*: Constellation; END; Graph* = POINTER TO GraphDesc; Notifier* = PROCEDURE (op: INTEGER; G: Graph; O: Object; P: KeplerPorts.Port); GraphDesc* = RECORD (ObjectDesc) cons*, lastcons: Constellation; stars*, laststar: Star; seltime*: LONGINT; notify*: Notifier; END ; StarTab = POINTER TO ARRAY OF LONGINT; loading*: Graph; update: KeplerPorts.BalloonPort; nofpt: INTEGER; starTab: StarTab; noftypes: LONGINT; typTab: ARRAY 256 OF LONGINT; del, delG: Graph; (* ---------------------------------- abstract methods ---------------------------------- *) PROCEDURE (self: Object) Draw* (P: KeplerPorts.Port); END Draw; PROCEDURE (self: Object) Read* (VAR R: Files.Rider); END Read; PROCEDURE (self: Object) Write* (VAR R: Files.Rider); END Write; (* ---------------------------------- auxiliary procedures ---------------------------------- *) PROCEDURE err(s0, s1: ARRAY OF CHAR); VAR W: Texts.Writer; BEGIN Texts.OpenWriter(W); Texts.WriteString(W, s0); Texts.WriteString(W, s1); Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf) END err; PROCEDURE err2(s0, s1: ARRAY OF CHAR); VAR W: Texts.Writer; BEGIN Texts.OpenWriter(W); Texts.WriteString(W, s0); Texts.WriteString(W, s1); Texts.Append(Oberon.Log, W.buf) END err2; PROCEDURE ReadObj* (VAR R: Files.Rider; VAR x: Object); VAR ref: LONGINT; m: Modules.Module; t: Types.Type; module, type: ARRAY 32 OF CHAR; BEGIN x := NIL; Files.ReadNum(R, ref); IF ref = noftypes THEN Files.ReadString(R, module); Files.ReadString(R, type); m := Modules.ThisMod(module); IF m # NIL THEN t := Types.This(m, type); IF t # NIL THEN typTab[ref] := SYSTEM.VAL(LONGINT, t); INC(noftypes); Types.NewObj(x, t); x.Read(R) ELSE err("-- type not found: ", type) END ELSE err2("-- error: ", Modules.importing); IF Modules.res = 2 THEN err(" not an obj-file", "") ELSIF Modules.res = 3 THEN err2(" imports ", Modules.imported); err(" with bad key", ""); ELSIF Modules.res = 4 THEN err(" corrupted obj file", "") ELSIF Modules.res = 7 THEN err(" not enough space", "") END; (*Modules.res := 0*) END ELSIF ref # -1 THEN Types.NewObj(x, SYSTEM.VAL(Types.Type, typTab[ref])); x.Read(R) END END ReadObj; PROCEDURE WriteObj* (VAR R: Files.Rider; x: Object); VAR typ: Types.Type; i: LONGINT; BEGIN IF x # NIL THEN typ := Types.TypeOf(x); i := 0; WHILE (i < noftypes) & (SYSTEM.VAL(LONGINT, typ) # typTab[i]) DO INC(i) END ; IF i = noftypes THEN Files.WriteNum(R, i); typTab[i] := SYSTEM.VAL(LONGINT, typ); INC(noftypes); Files.WriteString(R, typ.module.name); Files.WriteString(R, typ.name) ELSE Files.WriteNum(R, i) END ; x.Write(R) ELSE Files.WriteNum(R, -1) END END WriteObj; PROCEDURE GetType* (o: Object; VAR module, type: ARRAY OF CHAR); VAR t: Types.Type; BEGIN t := Types.TypeOf(o); COPY(t.module.name, module); COPY(t.name, type) END GetType; PROCEDURE Reset*; BEGIN nofpt := 0; noftypes := 0 END Reset; PROCEDURE GetStar (n: INTEGER): Star; VAR s: Star; BEGIN s := SYSTEM.VAL(Star, starTab[n]); INC(s.refcnt); RETURN s END GetStar; (* ---------------------------------- Star methods ---------------------------------- *) PROCEDURE (self: Star) Draw* (P: KeplerPorts.Port); BEGIN IF self.sel THEN P.FillRect(self.x - ptSize, self.y - ptSize, ptSize*2 + P.scale, ptSize*2 + P.scale, Display.white, 5, Display.invert) END END Draw; PROCEDURE (self: Star) Read* (VAR R: Files.Rider); VAR h: LONGINT; BEGIN self.sel := FALSE; Files.ReadNum(R, h); self.x := SHORT(h); Files.ReadNum(R, h); self.y := SHORT(h) END Read; PROCEDURE (self: Star) Write* (VAR R: Files.Rider); BEGIN Files.WriteNum(R, self.x); Files.WriteNum(R, self.y) END Write; (* ---------------------------------- Constellation methods ---------------------------------- *) PROCEDURE (self: Constellation) State* (): INTEGER; (* unselected = 0; partially selected = 1; totally selected = 2 *) VAR sum, i: INTEGER; BEGIN sum := 0; i := 0; WHILE i < self.nofpts DO IF self.p[i].sel THEN INC(sum) END ; INC(i) END ; IF sum = 0 THEN RETURN 0 ELSIF sum = self.nofpts THEN RETURN 2 ELSE RETURN 1 END END State; PROCEDURE (self: Constellation) Read* (VAR R: Files.Rider); VAR ref, i: LONGINT; BEGIN i := 0; Files.ReadNum(R, ref); self.nofpts := SHORT(ref); i := 0; WHILE i < self.nofpts DO Files.ReadNum(R, ref); self.p[i] := GetStar(SHORT(ref)); INC(i) END END Read; PROCEDURE (self: Constellation) Write* ( VAR R: Files.Rider); VAR i: INTEGER; BEGIN i := 0; Files.WriteNum(R, self.nofpts); WHILE i < self.nofpts DO Files.WriteNum(R, self.p[i].ref); INC(i) END END Write; (* ---------------------------------- Planet methods ---------------------------------- *) PROCEDURE (self: Planet) Draw* (P: KeplerPorts.Port); BEGIN IF self.sel THEN P.DrawRect(self.x - ptSize, self.y - ptSize, ptSize*2, ptSize*2, Display.white, Display.invert) END END Draw; PROCEDURE (self: Planet) Calc*; END Calc; PROCEDURE (self: Planet) Read* (VAR R: Files.Rider); VAR o: Object; BEGIN self.Read^(R); ReadObj(R, o); self.c := o(Constellation) END Read; PROCEDURE (self: Planet) Write* (VAR R: Files.Rider); BEGIN self.Write^(R); WriteObj(R, self.c) END Write; (* ---------------------------------- Graphic methods ---------------------------------- *) PROCEDURE (G: Graph) Append*(o: Object); BEGIN IF o IS Star THEN WITH o: Star DO IF G.stars = NIL THEN G.stars := o ELSE G.laststar.next := o END ; G.laststar := o; o.next := NIL END ELSE WITH o: Constellation DO IF G.cons = NIL THEN G.cons := o ELSE G.lastcons.next := o END ; G.lastcons := o; o.next := NIL; G.notify(draw, G, o, NIL) END END END Append; PROCEDURE (G: Graph) FlipSelection*(p: Star); BEGIN IF p.sel THEN G.notify(draw, G, p, NIL); p.sel := FALSE ELSE p.sel := TRUE; G.notify(draw, G, p, NIL); G.seltime := Oberon.Time() END END FlipSelection; PROCEDURE DependsOn(c: Constellation; s: Star): BOOLEAN; VAR i: INTEGER; p: Star; BEGIN i := 0; WHILE i < c.nofpts DO p := c.p[i]; IF p = s THEN RETURN TRUE ELSIF (p IS Planet) & DependsOn(p(Planet).c, s) THEN RETURN TRUE END ; INC(i) END ; RETURN FALSE END DependsOn; PROCEDURE (G: Graph) Move*(s: Star; dx, dy: INTEGER); VAR p: Star; c: Constellation; BEGIN KeplerPorts.InitBalloon(update); c := G.cons; WHILE c # NIL DO IF DependsOn(c, s) THEN c.Draw(update) END ; c := c.next END ; p := s^.next; WHILE p # NIL DO IF (p IS Planet) & DependsOn(p(Planet).c, s) THEN p.Draw(update) END ; p := p.next END ; s.Draw(update); INC(s.x, dx); INC(s.y, dy); s.Draw(update); p := s^.next; WHILE p # NIL DO IF (p IS Planet) & DependsOn(p(Planet).c, s) THEN p(Planet).Calc; p.Draw(update) END ; p := p.next END ; c := G.cons; WHILE c # NIL DO IF DependsOn(c, s) THEN c.Draw(update) END ; c := c.next END ; G.notify(restore, G, NIL, update) END Move; PROCEDURE (G: Graph) MoveSelection*(dx, dy: INTEGER); VAR p: Star; c: Constellation; BEGIN KeplerPorts.InitBalloon(update); p := G.stars; WHILE p # NIL DO (*expand selection*) IF ~p.sel & (p IS Planet) & (p(Planet).c.State() > 0) THEN p.sel := TRUE END ; p := p.next END ; c := G.cons; WHILE c # NIL DO IF c.State() # 0 THEN c.Draw(update) END ; c := c.next END ; p := G.stars; WHILE p # NIL DO IF p.sel THEN p.Draw(update); IF p IS Planet THEN p(Planet).Calc ELSE INC(p.x, dx); INC(p.y, dy) END ; p.Draw(update) END ; p := p.next END ; c := G.cons; WHILE c # NIL DO IF c.State() # 0 THEN c.Draw(update) END ; c := c.next END ; G.notify(restore, G, NIL, update) END MoveSelection; PROCEDURE ReverseStars(G: Graph); VAR p, first, next: Star; BEGIN p := G.stars; G.laststar := p; first := NIL; WHILE p # NIL DO next := p.next; p.next := first; first := p; p := next END ; G.stars := first END ReverseStars; PROCEDURE Release (self: Constellation); VAR i: INTEGER; s: Star; BEGIN i := 0; WHILE i < self.nofpts DO s := self.p[i]; DEC(s.refcnt); INC(i) END END Release; PROCEDURE CutCons (G: Graph; prevc, c: Constellation); BEGIN IF prevc = NIL THEN G.cons := c.next ELSE prevc.next := c.next END ; IF del.cons = NIL THEN del.cons := c ELSE del.lastcons.next := c END ; del.lastcons := c; IF G.lastcons = c THEN G.lastcons:= prevc END ; Release(c); c.Draw(update) END CutCons; PROCEDURE CutStar (G:Graph; prevs, s: Star); BEGIN IF prevs = NIL THEN G.stars := s.next ELSE prevs.next := s.next END ; IF del.stars = NIL THEN del.stars := s ELSE del.laststar.next := s END ; del.laststar := s; IF G.laststar = s THEN G.laststar := prevs END ; IF s IS Planet THEN Release(s(Planet).c) END ; s.ref := 0; s.Draw(update) END CutStar; PROCEDURE DelStar(G: Graph; o: Object); VAR s, prevs: Star; BEGIN s := G.stars; prevs := NIL; WHILE (s # NIL) & (s # o) DO prevs := s; s := s.next END ; IF s # NIL THEN CutStar(G, prevs, s) END END DelStar; PROCEDURE (G: Graph) Delete* (o: Object); VAR c, prevc: Constellation; i: INTEGER; BEGIN KeplerPorts.InitBalloon(update); delG := G; del.cons := NIL; del.stars := NIL; IF o IS Constellation THEN c := G.cons; prevc := NIL; WHILE (c # NIL) & (c # o) DO prevc := c; c := c.next END ; IF c # NIL THEN CutCons(G, prevc, c); i := 0; WHILE i < c.nofpts DO IF (c.p[i].refcnt = 0) & ~(c.p[i] IS Planet) THEN DelStar(G, c.p[i]) END ; INC(i) END END ELSE ASSERT(o(Star).refcnt = 0); IF o IS Planet THEN c := o(Planet).c; Release(c); i := 0; WHILE i < c.nofpts DO IF (c.p[i].refcnt = 0) & ~(c.p[i] IS Planet) THEN DelStar(G, c.p[i]) END ; INC(i) END END ; DelStar(G, o) END ; IF del.cons # NIL THEN del.lastcons.next := NIL END ; IF del.stars # NIL THEN del.laststar.next := NIL END ; G.notify(restore, G, NIL, update) END Delete; PROCEDURE (G: Graph) DeleteSelection* (minstate: INTEGER); VAR s, prevs: Star; c, prevc: Constellation; BEGIN delG := G; KeplerPorts.InitBalloon(update); (*move all constellations with (State >= minstate) into del buffer*) c := G.cons; prevc := NIL; del.cons := NIL; WHILE c # NIL DO IF c.State() >= minstate THEN CutCons(G, prevc, c) ELSE prevc := c END ; c := c.next END ; IF del.cons # NIL THEN del.lastcons.next := NIL END ; (*move all unused stars and planets with refcnt=0 & c.State>=minstate into del buffer*) ReverseStars(G); s := G.stars; prevs := NIL; del.stars := NIL; WHILE s # NIL DO IF (s.refcnt = 0) & (~(s IS Planet) OR s.sel OR (s(Planet).c.State() >= minstate)) THEN CutStar(G, prevs, s) ELSE prevs := s END ; s := s.next END ; ReverseStars(G) ; IF del.stars # NIL THEN del.laststar.next := NIL; ReverseStars(del) END ; G.notify(restore, G, NIL, update) END DeleteSelection; PROCEDURE (G: Graph) All* (op: INTEGER); (* deselect = 0; select = 1 *) VAR p: Star; BEGIN p := G.stars; KeplerPorts.InitBalloon(update); WHILE p # NIL DO IF (op = 1) # p.sel THEN IF p.sel THEN p.Draw(update); p.sel := FALSE ELSE p.sel := TRUE; p.Draw(update); G.seltime := Oberon.Time() END END ; p := p.next END ; IF op = 0 THEN G.seltime := -1 END ; G.notify(restore, G, NIL, update) END All; PROCEDURE Store(G: Graph; VAR R: Files.Rider; all: BOOLEAN); VAR p, dummy: Star; c: Constellation; BEGIN p := G.stars; NEW(dummy); WHILE p # NIL DO IF all OR (p.sel & ~(p IS Planet)) THEN WriteObj(R, p); p.ref := nofpt; INC(nofpt) ELSIF p.sel & (p(Planet).c.State() = 2) THEN WriteObj(R, p); p.ref := nofpt; INC(nofpt) ELSIF p.sel & (p(Planet).c.State() # 2) THEN dummy^ := p^; WriteObj(R, dummy); p.ref := nofpt; INC(nofpt) END ; p := p.next END ; c := G.cons; WHILE c # NIL DO IF all OR (c.State()=2) THEN WriteObj(R, c) END ; c := c.next END ; Files.WriteNum(R, -1) END Store; PROCEDURE (G: Graph) Draw* (P: KeplerPorts.Port); VAR s: Star; c: Constellation; BEGIN c := G.cons; WHILE c # NIL DO c.Draw(P); c := c.next END ; s := G.stars; WHILE s # NIL DO s.Draw(P); s := s.next END END Draw; PROCEDURE (G: Graph) Write* (VAR R: Files.Rider); BEGIN Store(G, R, TRUE) END Write; PROCEDURE (G: Graph) WriteSel* (VAR R: Files.Rider); BEGIN Store(G, R, FALSE) END WriteSel; PROCEDURE DoubleStarTab; VAR h: StarTab; i: LONGINT; BEGIN i := 0; NEW(h, LEN(starTab^)*2); WHILE i < LEN(starTab^) DO h[i] := starTab[i]; INC(i) END ; starTab := h END DoubleStarTab; PROCEDURE (G: Graph) Read* (VAR R: Files.Rider); VAR o, o0: Object; BEGIN loading := G; G.stars := NIL; G.laststar := NIL; G.cons := NIL; G.lastcons := NIL; G.seltime := -1; ReadObj(R, o0); o := o0; WHILE o # NIL DO (* append without notification *) WITH o: Star DO IF G.stars = NIL THEN G.stars := o ELSE G.laststar.next := o END ; G.laststar := o; o.next := NIL; IF nofpt = LEN(starTab^) THEN DoubleStarTab END ; starTab[nofpt] := SYSTEM.VAL(LONGINT, o); INC(nofpt) | o: Constellation DO IF G.cons = NIL THEN G.cons := o ELSE G.lastcons.next := o END ; G.lastcons := o; o.next := NIL END ; ReadObj(R, o) END END Read; PROCEDURE Old*(name: ARRAY OF CHAR): Graph; VAR F: Files.File; R: Files.Rider; o: Object; BEGIN F := Files.Old(name); IF F # NIL THEN Files.Set(R, F, 0); Reset; ReadObj(R, o); IF R.res = 0 THEN RETURN o(Graph) ELSE RETURN NIL END ELSE RETURN NIL END END Old; PROCEDURE *Dummy(op: INTEGER; g: Graph; c: Object; f: KeplerPorts.Port); END Dummy; PROCEDURE (G: Graph) CopySelection* (from: Graph; dx, dy: INTEGER); VAR cpBuf: Files.File; R: Files.Rider; c, nextc: Constellation; p, nextp: Star; buf: Graph; BEGIN cpBuf := Files.New(""); Files.Set(R, cpBuf, 0); Reset; from.WriteSel(R); Files.Set(R, cpBuf, 0); Types.NewObj(buf, Types.TypeOf(from)); buf.notify := Dummy; Reset; buf.Read(R); p := buf.stars; WHILE p # NIL DO nextp := p.next; INC(p.x, dx); INC(p.y, dy); IF (p.refcnt > 0) OR (p IS Planet) THEN G.Append(p) END; p := nextp END ; c := buf.cons; KeplerPorts.InitBalloon(update); WHILE c # NIL DO c.Draw(update); nextc := c.next; IF G.cons = NIL THEN G.cons := c ELSE G.lastcons.next := c END ; G.lastcons := c; c.next := NIL; c := nextc END ; G.notify(restore, G, NIL, update) END CopySelection; PROCEDURE (G: Graph) SendToBack* (o: Object); VAR i: INTEGER; s: Star; c: Constellation; BEGIN WITH o: Star DO s := G.stars; IF o # s THEN WHILE s.next # o DO s := s.next END ; s.next := o.next; o.next := G.stars; G.stars := o; IF G.laststar = o THEN G.laststar := s END ; IF o IS Planet THEN (* preserve topological order *) c := o(Planet).c; FOR i := 0 TO c.nofpts-1 DO G.SendToBack(c.p[i]) END END END | o: Constellation DO KeplerPorts.InitBalloon(update); c := G.cons; IF o # c THEN WHILE c.next # o DO c := c.next END ; c.next := o.next; o.next := G.cons; G.cons := o; IF G.lastcons = o THEN G.lastcons := c END ; o.Draw(update); G.notify(restore, G, NIL, update) END END END SendToBack; PROCEDURE Unrelease(c: Constellation); VAR i: INTEGER; BEGIN i := 0; WHILE i < c.nofpts DO INC(c.p[i].refcnt); INC(i) END END Unrelease; PROCEDURE Recall*; VAR s, nexts: Star; c, nextc: Constellation; BEGIN IF delG # NIL THEN s := del.stars; WHILE s # NIL DO nexts := s.next; s.sel := FALSE; delG.Append(s); IF s IS Planet THEN Unrelease(s(Planet).c) END ; s := nexts END ; c := del.cons; WHILE c # NIL DO nextc := c.next; delG.Append(c); Unrelease(c); c := nextc END ; delG := NIL; del.cons := NIL; del.lastcons := NIL; del.stars := NIL; del.laststar := NIL END END Recall; BEGIN NEW(update); NEW(del); NEW(starTab, 1) END KeplerGraphs.