|
class Tree { |
|
constructor(width, height, y, children) { |
|
this.w = width |
|
this.h = height |
|
this.y = y |
|
this.c = children |
|
this.cs = children.length |
|
|
|
this.x = 0 |
|
this.prelim = 0 |
|
this.mod = 0 |
|
this.shift = 0 |
|
this.change = 0 |
|
this.tl = null |
|
this.tr = null |
|
this.el = null |
|
this.er = null |
|
|
|
this.msel = 0 |
|
this.mser = 0 |
|
} |
|
} |
|
|
|
function setExtremes(tree) { |
|
if (tree.cs === 0) { |
|
tree.el = tree |
|
tree.er = tree |
|
tree.msel = tree.mser = 0 |
|
} else { |
|
tree.el = tree.c[0].el |
|
tree.msel = tree.c[0].msel |
|
tree.er = tree.c[tree.cs - 1].er |
|
tree.mser = tree.c[tree.cs - 1].mser |
|
} |
|
} |
|
|
|
function bottom(tree) { |
|
return tree.y + tree.h |
|
} |
|
|
|
|
|
|
|
class IYL { |
|
constructor(lowY, index, next) { |
|
this.lowY = lowY |
|
this.index = index |
|
this.next = next |
|
} |
|
} |
|
|
|
function updateIYL(minY, i, ih) { |
|
|
|
while (ih !== null && minY >= ih.lowY) { |
|
|
|
ih = ih.next |
|
} |
|
return new IYL(minY, i, ih) |
|
} |
|
|
|
function distributeExtra(tree, i, si, distance) { |
|
|
|
if (si !== i - 1) { |
|
const nr = i - si |
|
tree.c[si + 1].shift += distance / nr |
|
tree.c[i].shift -= distance / nr |
|
tree.c[i].change -= distance - distance / nr |
|
} |
|
} |
|
|
|
function moveSubtree(tree, i, si, distance) { |
|
|
|
tree.c[i].mod += distance |
|
tree.c[i].msel += distance |
|
tree.c[i].mser += distance |
|
distributeExtra(tree, i, si, distance) |
|
} |
|
|
|
function nextLeftContour(tree) { |
|
return tree.cs === 0 ? tree.tl : tree.c[0] |
|
} |
|
|
|
function nextRightContour(tree) { |
|
return tree.cs === 0 ? tree.tr : tree.c[tree.cs - 1] |
|
} |
|
|
|
function setLeftThread(tree, i, cl, modsumcl) { |
|
const li = tree.c[0].el |
|
li.tl = cl |
|
|
|
const diff = (modsumcl - cl.mod) - tree.c[0].msel |
|
li.mod += diff |
|
|
|
li.prelim -= diff |
|
|
|
tree.c[0].el = tree.c[i].el |
|
tree.c[0].msel = tree.c[i].msel |
|
} |
|
|
|
|
|
function setRightThread(tree, i, sr, modsumsr) { |
|
const ri = tree.c[i].er |
|
ri.tr = sr |
|
const diff = (modsumsr - sr.mod) - tree.c[i].mser |
|
ri.mod += diff |
|
ri.prelim -= diff |
|
tree.c[i].er = tree.c[i - 1].er |
|
tree.c[i].mser = tree.c[i - 1].mser |
|
} |
|
|
|
function seperate(tree, i, ih) { |
|
|
|
let sr = tree.c[i - 1] |
|
let mssr = sr.mod |
|
|
|
let cl = tree.c[i] |
|
let mscl = cl.mod |
|
while (sr !== null && cl !== null) { |
|
if (bottom(sr) > ih.lowY) { |
|
ih = ih.next |
|
} |
|
|
|
const distance = mssr + sr.prelim + sr.w - (mscl + cl.prelim) |
|
if (distance > 0) { |
|
mscl += distance |
|
moveSubtree(tree, i, ih.index, distance) |
|
} |
|
|
|
const sy = bottom(sr) |
|
const cy = bottom(cl) |
|
if (sy <= cy) { |
|
sr = nextRightContour(sr) |
|
if (sr !== null) { |
|
mssr += sr.mod |
|
} |
|
} |
|
if (sy >= cy) { |
|
cl = nextLeftContour(cl) |
|
if (cl !== null) { |
|
mscl += cl.mod |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
if (sr === null && cl !== null) { |
|
setLeftThread(tree, i, cl, mscl) |
|
} else if (sr !== null && cl === null) { |
|
setRightThread(tree, i, sr, mssr) |
|
} |
|
} |
|
|
|
function positionRoot(tree) { |
|
|
|
tree.prelim = |
|
(tree.c[0].prelim + |
|
tree.c[0].mod + |
|
tree.c[tree.cs - 1].mod + |
|
tree.c[tree.cs - 1].prelim + |
|
tree.c[tree.cs - 1].w) / |
|
2 - |
|
tree.w / 2 |
|
} |
|
|
|
function firstWalk(tree) { |
|
if (tree.cs === 0) { |
|
setExtremes(tree) |
|
return |
|
} |
|
|
|
firstWalk(tree.c[0]) |
|
let ih = updateIYL(bottom(tree.c[0].el), 0, null) |
|
for (let i = 1; i < tree.cs; i++) { |
|
firstWalk(tree.c[i]) |
|
const minY = bottom(tree.c[i].er) |
|
seperate(tree, i, ih) |
|
ih = updateIYL(minY, i, ih) |
|
} |
|
positionRoot(tree) |
|
setExtremes(tree) |
|
} |
|
|
|
function addChildSpacing(tree) { |
|
let d = 0 |
|
let modsumdelta = 0 |
|
for (let i = 0; i < tree.cs; i++) { |
|
d += tree.c[i].shift |
|
modsumdelta += d + tree.c[i].change |
|
tree.c[i].mod += modsumdelta |
|
} |
|
} |
|
|
|
function secondWalk(tree, modsum) { |
|
modsum += tree.mod |
|
|
|
tree.x = tree.prelim + modsum |
|
addChildSpacing(tree) |
|
for (let i = 0; i < tree.cs; i++) { |
|
secondWalk(tree.c[i], modsum) |
|
} |
|
} |
|
|
|
function layout(tree) { |
|
firstWalk(tree) |
|
secondWalk(tree, 0) |
|
} |
|
|
|
export { Tree, layout } |
|
|