github.com/jbendotnet/noms@v0.0.0-20190904222105-c43e4293ea92/cmd/noms/splore/src/buchheim.js (about)

     1  // Copyright 2017 Attic Labs, Inc. All rights reserved.
     2  // Licensed under the Apache License, version 2.0:
     3  // http://www.apache.org/licenses/LICENSE-2.0
     4  
     5  // @flow
     6  
     7  import {notNull} from './assert.js';
     8  import type {NodeGraph, SploreNode} from './types.js';
     9  
    10  // JavaScript implementation of Christoph Buchheim, Michael Jünger, Sebastian Leipert's tree layout
    11  // algorithm. See: http://dl.acm.org/citation.cfm?id=729576.
    12  //
    13  // Thanks also to Bill Mill for the explanation and Python sample code:
    14  // http://billmill.org/pymag-trees/.
    15  
    16  // TreeNode represents one node of the tree visualization.
    17  
    18  export class TreeNode {
    19    x: number;
    20    y: number;
    21    id: string;
    22    node: SploreNode;
    23    isOpen: boolean;
    24    children: TreeNode[];
    25    parent: ?TreeNode;
    26    thread: ?TreeNode;
    27    offset: number;
    28    ancestor: TreeNode;
    29    change: number;
    30    shift: number;
    31    number: number;
    32    mod: number;
    33  
    34    constructor(
    35      graph: NodeGraph,
    36      id: string,
    37      parent: ?TreeNode,
    38      depth: number,
    39      number: number,
    40      seen: {[key: string]: boolean},
    41    ) {
    42      seen[id] = true;
    43      this.x = -1;
    44      this.y = depth;
    45      this.id = id;
    46      this.node = graph.nodes[id];
    47      this.isOpen = !!graph.open[id];
    48      this.children = (this.isOpen ? graph.links[id] : [])
    49        .concat(graph.keyLinks[id])
    50        .filter(cid => !(cid in seen))
    51        .map((cid, i) => new TreeNode(graph, cid, this, depth + 1, i + 1, seen));
    52      this.parent = parent;
    53      this.thread = null;
    54      this.offset = 0;
    55      this.ancestor = this;
    56      this.change = 0;
    57      this.shift = 0;
    58      this.number = number;
    59      this.mod = 0;
    60    }
    61  
    62    left(): ?TreeNode {
    63      if (this.children.length > 0) {
    64        return this.children[0];
    65      }
    66      return this.thread;
    67    }
    68  
    69    right(): ?TreeNode {
    70      if (this.children.length > 0) {
    71        return this.children[this.children.length - 1];
    72      }
    73      return this.thread;
    74    }
    75  
    76    leftBrother(): ?TreeNode {
    77      let n = null;
    78      if (this.parent) {
    79        for (let i = 0; i < this.parent.children.length; i++) {
    80          const node = this.parent.children[i];
    81          if (node === this) {
    82            return n;
    83          } else {
    84            n = node;
    85          }
    86        }
    87      }
    88      return n;
    89    }
    90  
    91    getLeftMostSibling(): ?TreeNode {
    92      if (this.parent && this !== this.parent.children[0]) {
    93        return this.parent.children[0];
    94      } else {
    95        return null;
    96      }
    97    }
    98  }
    99  
   100  export function layout(tree: TreeNode): void {
   101    firstWalk(tree, 1);
   102    secondWalk(tree, 0, 0);
   103  }
   104  
   105  function firstWalk(v: TreeNode, distance: number): void {
   106    if (v.children.length === 0) {
   107      if (v.getLeftMostSibling()) {
   108        v.x = notNull(v.leftBrother()).x + distance;
   109      } else {
   110        v.x = 0;
   111      }
   112    } else {
   113      let defaultAncestor = v.children[0];
   114      for (let i = 0; i < v.children.length; i++) {
   115        const w = v.children[i];
   116        firstWalk(w, distance);
   117        defaultAncestor = apportion(w, defaultAncestor, distance);
   118      }
   119      executeShifts(v);
   120  
   121      const midpoint = (v.children[0].x + v.children[v.children.length - 1].x) / 2;
   122  
   123      const w = v.leftBrother();
   124      if (w) {
   125        v.x = w.x + distance;
   126        v.mod = v.x - midpoint;
   127      } else {
   128        v.x = midpoint;
   129      }
   130    }
   131  }
   132  
   133  function apportion(v: TreeNode, defaultAncestor: TreeNode, distance: number): TreeNode {
   134    const w = v.leftBrother();
   135    if (w !== null) {
   136      let vir = v;
   137      let vor = v;
   138      let vil = notNull(w);
   139      let vol = notNull(v.getLeftMostSibling());
   140      let sir = v.mod;
   141      let sor = v.mod;
   142      let sil = vil.mod;
   143      let sol = vol.mod;
   144      while (vil.right() && vir.left()) {
   145        vil = notNull(vil.right());
   146        vir = notNull(vir.left());
   147        vol = notNull(vol.left());
   148        vor = notNull(vor.right());
   149        vor.ancestor = v;
   150        const shift = vil.x + sil - (vir.x + sir) + distance;
   151        if (shift > 0) {
   152          const a = ancestor(vil, v, defaultAncestor);
   153          moveSubtree(a, v, shift);
   154          sir = sir + shift;
   155          sor = sor + shift;
   156        }
   157        sil += vil.mod;
   158        sir += vir.mod;
   159        sol += vol.mod;
   160        sor += vor.mod;
   161      }
   162      if (vil.right() && !vor.right()) {
   163        vor.thread = vil.right();
   164        vor.mod += sil - sor;
   165      } else {
   166        if (vir.left() && !vol.left()) {
   167          vol.thread = vir.left();
   168          vol.mod += sir - sol;
   169        }
   170        defaultAncestor = v;
   171      }
   172    }
   173    return defaultAncestor;
   174  }
   175  
   176  function moveSubtree(wl: TreeNode, wr: TreeNode, shift: number): void {
   177    const subtrees = wr.number - wl.number;
   178    wr.change -= shift / subtrees;
   179    wr.shift += shift;
   180    wl.change += shift / subtrees;
   181    wr.x += shift;
   182    wr.mod += shift;
   183  }
   184  
   185  function executeShifts(v: TreeNode): void {
   186    let shift = 0;
   187    let change = 0;
   188    for (let i = v.children.length - 1; i >= 0; i--) {
   189      const w = v.children[i];
   190      w.x += shift;
   191      w.mod += shift;
   192      change += w.change;
   193      shift += w.shift + change;
   194    }
   195  }
   196  
   197  function ancestor(vil: TreeNode, v: TreeNode, defaultAncestor: TreeNode): TreeNode {
   198    if (v.parent && v.parent.children.indexOf(vil.ancestor) > -1) {
   199      return vil.ancestor;
   200    } else {
   201      return defaultAncestor;
   202    }
   203  }
   204  
   205  function secondWalk(v: TreeNode, m: number, depth: number): void {
   206    v.x += m;
   207    v.y = depth;
   208  
   209    for (let i = 0; i < v.children.length; i++) {
   210      secondWalk(v.children[i], m + v.mod, depth + 1);
   211    }
   212  }