<*PRAGMA LL*> MODULECAUTION: Don't change without also changing AddChildForest; IMPORT Animate, Axis, RefList, MG, MGPublic, MGV, Pts, RefListUtils, R2, R2Box, Thread, VBT; EXCEPTION Fatal; <* FATAL Fatal, Thread.Alerted *> REVEAL V = PublicV BRANDED OBJECT OVERRIDES reshape := MGV.ReshapeLeaveOrigin; setBorder := SetBorderV; shape := ShapeV; setRoot := SetRootV; init := InitV; END; REVEAL SubTree = PublicSubTree BRANDED OBJECT OVERRIDES init := InitSubTree; addChild := AddChildDefault; removeChild := RemoveChildDefault; route := RouteDefault; link := LinkError; succ := SuccError; pred := PredDefault; nth := NthDefault; iterate := IterateDefault; bounds := BoundsError; calculateSize := CalculateSizeError; translate := TranslateError; END; REVEAL GenericTree = GenericTreePublic BRANDED OBJECT OVERRIDES bounds := BoundsGeneric; succ := SuccGeneric; addChild := AddChildGeneric; removeChild := RemoveChildGeneric; link := LinkGeneric; calculateSize := CalculateSizeGeneric; translate := TranslateGeneric; END; REVEAL Forest = ForestPublic BRANDED OBJECT OVERRIDES init := InitForest; END; PROCEDURE GenericTree InitV (v: V): MG.V = BEGIN EVAL MG.V.init(v); LOCK v.mu DO IF v.root # NIL THEN v.displayList.addBefore(v, v.root); END; END; RETURN v; END InitV; PROCEDUREShapeV (v: V; axis: Axis.T; <* UNUSED *>n: CARDINAL): VBT.SizeRange = VAR sr : VBT.SizeRange; size: REAL; BEGIN LOCK v.mu DO IF v.root = NIL THEN size := 0.0; ELSIF axis = Axis.T.Hor THEN size := v.root.width ELSE size := v.root.height END; sr.pref := Pts.ToScreenPixels(v, 2.0 * v.border[axis] + size, axis) END; sr.lo := 0; sr.hi := MAX(sr.pref + 1, VBT.DefaultShape.hi); RETURN sr; END ShapeV; PROCEDURESetRootV (v: V; root: SubTree) = VAR bounds: R2Box.T; BEGIN LOCK v.mu DO IF v.root # NIL THEN v.displayList.remove(v, v.root) END; v.root := root; IF root # NIL THEN bounds := root.graphic.appearance.boundingBox(root.graphic, v); v.displayList.addBefore(v, v.root); root.setVisible(v, 0.0); END; END; VBT.NewShape(v); VBT.Mark(v); END SetRootV; PROCEDURESetBorderV (v: V; border: ARRAY Axis.T OF REAL) = BEGIN LOCK v.mu DO v.border := border END; VBT.NewShape(v); VBT.Mark(v); END SetBorderV; PROCEDURERelayoutAncestors (node: SubTree; v: V) = BEGIN WHILE node # NIL DO node.dirty := TRUE; node.calculateSize(v); node := node.parent; END; v.root.translate( v, v.nw[1] - v.border[Axis.T.Ver], v.nw[0] + v.border[Axis.T.Hor]); END RelayoutAncestors; <* LL < v.mu *> PROCEDURESetRoot (root: SubTree; v: V; ) = BEGIN v.setRoot(root); LOCK v.mu DO RelayoutAncestors(root, v); END; VBT.NewShape(v); MGV.Animation(v); END SetRoot; <* LL < v.mu *> PROCEDUREAddChild (node: SubTree; v: V; pred, new: SubTree) = BEGIN LOCK v.mu DO <* ASSERT((pred = NIL OR pred.parent = node) AND new.parent = NIL) *> node.addChild(v, pred, new); RelayoutAncestors(node, v); END; VBT.NewShape(v); MGV.Animation(v); END AddChild; PROCEDURERemoveChild (node: SubTree; v: V; child: SubTree) = BEGIN LOCK v.mu DO <* ASSERT( child.parent = node) *> node.removeChild(v, child); RelayoutAncestors(node, v); END; VBT.NewShape(v); MGV.Animation(v); END RemoveChild; PROCEDURERoute (ancestor: SubTree; v: V; descendant: SubTree): MG.Group = BEGIN LOCK v.mu DO RETURN ancestor.route(v, descendant); END; END Route; PROCEDURESucc (node: SubTree; v: V; pred: SubTree): SubTree = BEGIN LOCK v.mu DO <* ASSERT (pred = NIL OR pred.parent = node) *> RETURN node.succ(v, pred); END; END Succ; PROCEDUREPred (node: SubTree; v: V; succ: SubTree): SubTree = BEGIN LOCK v.mu DO <* ASSERT (succ = NIL OR succ.parent = node) *> RETURN node.pred(v, succ); END; END Pred; PROCEDURENth (node: SubTree; v: V; n: CARDINAL): SubTree = BEGIN LOCK v.mu DO RETURN node.nth(v, n); END; END Nth; PROCEDURENumChildren (node: SubTree; v: V): INTEGER = BEGIN LOCK v.mu DO RETURN node.numChildren; END; END NumChildren; PROCEDUREParent (node: SubTree; v: V): SubTree = BEGIN LOCK v.mu DO RETURN node.parent; END; END Parent; PROCEDUREIterate (node: SubTree; v: V; iter: ChildrenIterator) = BEGIN LOCK v.mu DO node.iterate(v, iter); END; END Iterate; PROCEDUREInitSubTree (node: SubTree; v: V; graphic: MG.T): SubTree = BEGIN EVAL MG.Group.init(node); IF node.id # MG.NoID THEN MGPublic.Register(v, node.id, node); END; LOCK v.mu DO IF node.linker = NIL THEN node.linker := linkerDefault END; node.graphic := graphic; node.addBefore(v, graphic); node.calculateSize(v); MG.TranslateToLocked(graphic, v, R2.Origin, TRUE); MG.SetPosLocked(node, R2.Origin, v); node.setVisible(v, 0.0); END; RETURN node END InitSubTree; PROCEDURELinkerForest (<* UNUSED *> l : Linker; <* UNUSED *> v : V; <* UNUSED *> parent, child: SubTree ): LinkerRec = BEGIN RETURN LinkerRec{NIL, NIL}; END LinkerForest; CONST R2Epsilon = R2.T{0.001, 0.001}; PROCEDUREInitForest (node: Forest; v: V): Forest = BEGIN node.linker := NEW(Linker, new := LinkerForest); RETURN GenericTree.init(node, v, NEW(MG.Rectangle, visible := 0.0, weight := 0.0).init( R2.Origin, R2Epsilon)) END InitForest; PROCEDUREBoundsError (<*UNUSED *> node : SubTree; <*UNUSED *> v : MG.V): R2Box.T = BEGIN RAISE Fatal END BoundsError; PROCEDUREBoundsGeneric ( node : SubTree; v : MG.V): R2Box.T = VAR pos := MG.PosLocked(node, v); bounds := node.graphic.appearance.boundingBox(node.graphic, v); w := pos[0] - node.width / 2.0; n := pos[1] + (bounds[1].hi - bounds[1].lo) / 2.0; e := w + node.width; s := n - node.height; BEGIN RETURN R2Box.FromEdges(w, e, s, n); END BoundsGeneric; PROCEDUREAddChildDefault ( node : SubTree; v : V; <* UNUSED *> pred : SubTree; child: SubTree ) = BEGIN child.parent := node; child.setVisible(v, 1.0); INC(node.numChildren); END AddChildDefault; PROCEDURECenter (node: GenericTree; v: V): R2.T = BEGIN RETURN R2Box.Middle(node.graphic.bounds(v)); END Center; PROCEDURELinkerNewDefault (<* UNUSED *> l : Linker; v : V; parent, child: SubTree ): LinkerRec = VAR link := NEW(MG.Line, weight := 2.0).init( to := Center(parent, v), from := Center(child, v)); BEGIN RETURN LinkerRec{parentLink := NEW(MG.LineEnd, line := link, controlsFrom := FALSE).init(), childLink := NEW(MG.LineEnd, line := link, controlsFrom := TRUE).init()} END LinkerNewDefault;
PROCEDUREWe need to compute the vector which will move node to the correct north, west (relative to the parent)AddChildGeneric (node: GenericTree; v: V; pred, child: SubTree) = VAR predTail: RefList.T; new := NARROW(child, GenericTree); nlr := node.linker.new(v, node, child); BEGIN SubTree.addChild(node, v, pred, child); IF pred = NIL THEN node.children := RefList.Cons(child, node.children); ELSE predTail := FindGenericChild(node, pred); predTail.tail := RefList.Cons(child, predTail.tail); END; (* assumes if one end is NIL then both are *) IF nlr.parentLink # NIL THEN new.linkEndParent := nlr.parentLink; new.linkEnd := nlr.childLink; new.linkEnd.setVisible(v, new.visible); (* painting order should be node.graphic, new.graphic, new.linkend linkEndParent doesn't get painted *) node.addAfter(v, new.linkEndParent); (* bottom *) new.addAfter(v, new.linkEnd); (* bottom *) END; node.addBefore(v, new, node.graphic); (* below graphic *) END AddChildGeneric; PROCEDURERemoveChildDefault ( node : SubTree; <* UNUSED *> v : V; child: SubTree ) = BEGIN child.parent := NIL; DEC(node.numChildren); END RemoveChildDefault; PROCEDURERemoveChildGeneric (node: GenericTree; v: V; child: SubTree) = VAR ch := NARROW(child, GenericTree); BEGIN SubTree.removeChild(node, v, child); RefListUtils.DeleteQ(node.children, child); (* assumes if one end is NIL then both are *) IF ch.linkEndParent # NIL THEN node.remove(v, ch.linkEndParent); ch.remove(v, ch.linkEnd); END; node.remove(v, ch); END RemoveChildGeneric; PROCEDURECalculateSizeError (<* UNUSED *> node: SubTree; <* UNUSED *> v: V) = BEGIN RAISE Fatal END CalculateSizeError; PROCEDURECalculateSizeGeneric (node: GenericTree; v: V) = VAR width, height := 0.0; bounds: R2Box.T; size: R2.T; child := node.succ(v, NIL); BEGIN WHILE child # NIL DO width := width + child.width; height := MAX(height, child.height); child := node.succ(v, child); END; IF node.numChildren > 0 THEN height := height + node.dyChildren END; width := width + FLOAT(MAX(0, node.numChildren - 1)) * node.dxChildren; bounds := node.graphic.appearance.boundingBox(node.graphic, v); size := R2Box.Size(bounds); node.width := MAX(size[0], width); node.height := size[1] + height; END CalculateSizeGeneric; PROCEDURETranslateError (<* UNUSED *> node : SubTree; <* UNUSED *> v : V; <* UNUSED *> north, west: REAL ) = BEGIN RAISE Fatal END TranslateError; TYPE FromOrigin = Animate.Linear OBJECT OVERRIDES length := FOLength; doStep := FODoStep; END; PROCEDUREFOLength (<* UNUSED *> fo: FromOrigin; <* UNUSED *> v : MG.V; <* UNUSED *> mg: MG.T ): INTEGER = BEGIN RETURN 1 END FOLength; PROCEDUREFODoStep (fo : FromOrigin; time : REAL; timePrev: REAL; v : MG.V; mg : MG.T ) = BEGIN IF timePrev = 0.0 AND time # 0.0 THEN MG.RTranslateLocked(mg, v, fo.vector); END; IF time = 1.0 AND timePrev # 1.0 THEN mg.setVisible(v, 1.0); END; END FODoStep; CONST Epsilon = 0.01; PROCEDURELinearAnimation (v: V; vector: R2.T; mg: SubTree): BOOLEAN = VAR a: Animate.T; BEGIN IF ABS(vector[0]) > Epsilon OR ABS(vector[1]) > Epsilon THEN IF v.animations = NIL THEN v.animations := NEW(Animate.Group).init() END; IF MG.PosLocked(mg, v) = R2.Origin THEN a := NEW(FromOrigin, vector := vector).init(); ELSE a := NEW(Animate.Linear, vector := vector).init() END; v.animations.add(v, NEW(Animate.Composite, t := a, mg := mg)); RETURN TRUE; ELSE RETURN mg.dirty; END; END LinearAnimation; PROCEDUREParentPos (parent: SubTree; v: V): R2.T = BEGIN IF parent = NIL THEN RETURN R2.Origin; ELSE RETURN MG.PosLocked(parent, v); END; END ParentPos;
PROCEDURERefList.First(FindGenericChild(node: Generic; ch: SubTree)) = chTranslateGeneric (node: GenericTree; v: V; north, west: REAL) = VAR westCh, northCh: REAL; child := node.succ(v, NIL); ppos := ParentPos(node.parent, v); bounds := node.graphic.appearance.boundingBox(node.graphic, v); size := R2Box.Size(bounds); middle := R2Box.Middle(bounds); BEGIN IF LinearAnimation( v, R2.T{ppos[0] + west + node.width / 2.0 - middle[0], ppos[1] + north + size[1] / 2.0 - bounds[1].hi}, node) THEN (* translate each child so top is dyChildren below graphic's south and left edge is dxChildren from prev's right edge *) northCh := -size[1] - node.dyChildren; westCh := -node.width / 2.0; WHILE child # NIL DO child.translate(v, northCh, westCh); westCh := westCh + child.width + node.dxChildren; child := node.succ(v, child); END; END; END TranslateGeneric; PROCEDURERouteDefault (node: SubTree; v: V; descendant: SubTree): MG.Group = VAR group := NEW(MG.Group).init(); BEGIN WHILE descendant # node DO group.addBefore(v, descendant); VAR link := descendant.link(v); BEGIN IF link # NIL THEN group.addAfter(v, descendant.link(v)); END; END; descendant := descendant.parent; END; group.addBefore(v, node); RETURN group; END RouteDefault; PROCEDURELinkError (<* UNUSED *> node: SubTree; <* UNUSED *> v: V): MG.T = BEGIN RAISE Fatal END LinkError; PROCEDURELinkGeneric (node: GenericTree; <* UNUSED *> v: V): MG.T = BEGIN RETURN node.linkEnd END LinkGeneric; PROCEDURESuccError (<* UNUSED *> node: SubTree; <* UNUSED *> v : V; <* UNUSED *> pred: SubTree ): SubTree = BEGIN RAISE Fatal; END SuccError;
PROCEDUREFindGenericChild (node: GenericTree; ch: SubTree): RefList.T = VAR children := node.children; BEGIN WHILE children.head # ch DO children := children.tail; END; RETURN children; END FindGenericChild; PROCEDURESuccGeneric (node: GenericTree; <* UNUSED *>v: V; pred: SubTree): SubTree = VAR predTail: RefList.T; BEGIN IF pred = NIL THEN predTail := node.children ELSE predTail := FindGenericChild(node, pred).tail; END; IF predTail = NIL THEN RETURN NIL ELSE RETURN predTail.head END; END SuccGeneric; PROCEDUREPredDefault (node: SubTree; v: V; succ: SubTree): SubTree = VAR pred: SubTree := NIL; next: SubTree := node.succ(v, NIL); BEGIN WHILE next # succ DO pred := next; next := node.succ(v, pred); END; RETURN pred END PredDefault; PROCEDURENthDefault (node: SubTree; v: V; n: CARDINAL): SubTree = VAR ch := node.succ(v, NIL); BEGIN FOR i := 1 TO n DO ch := node.succ(v, ch); END; RETURN ch; END NthDefault; PROCEDUREIterateDefault (node: SubTree; v: V; iter: ChildrenIterator) = VAR ch := node.succ(v, NIL); BEGIN WHILE ch # NIL AND iter.proc(ch) DO ch := node.succ(v, ch); END; END IterateDefault; BEGIN linkerDefault := NEW(Linker, new := LinkerNewDefault); END GenericTree.