Skip to content

Instantly share code, notes, and snippets.

@arbalest
Created December 26, 2020 20:38
Show Gist options
  • Save arbalest/ebdd2b103c767923355b47e4594ca9c7 to your computer and use it in GitHub Desktop.
Save arbalest/ebdd2b103c767923355b47e4594ca9c7 to your computer and use it in GitHub Desktop.
A very basic layout of a binary tree in HTML and Javascript.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>DOM Binary Tree Demo</title>
<style>
* {
box-sizing: border-box;
}
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
.leaf-node {
border-radius: 3px;
/*padding: 0.25rem;*/
}
body.light-theme {
color: #303030;
background-color: #d0d2d5;
}
.light-theme .leaf-node {
background-color: #fefefe;
box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
}
body.dark-theme {
color: #d0d2d5;
background-color: #303030;
}
.dark-theme .leaf-node {
background-color: #101010;
box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
}
#canvas {
position: relative;
top: 0;
left: 0;
}
</style>
</head>
<body class="light-theme">
<div id="canvas">
</div>
<script>
const canvasEl = document.getElementById('canvas');
let data = {
root: {
label: "A employee.state is MI",
el: false,
y: {
label: "A.Y employee.location is Detroit",
el: false,
y: {
label: "AY.Y pass",
el: false,
},
n: {
label: "AY.N fail",
el: false,
}
},
n: {
label: "A.N employee.state is WI",
el: false,
y: {
label: "AN.Y employee.union is Y OR employee.location is Green Bay",
el: false,
y: {
label: "ANY.Y pass",
el: false,
},
n: {
label: "ANY.N fail",
el: false,
}
},
n: {
label: "AN.N fail",
el: false,
}
}
}
};
const layout = {
depths: [],
maxHeight: 0,
maxWidth: 0,
maxDepth: 0,
origin: {x: 0, y: 300},
cur: {x: 0, y: 0},
extents: {w: 0, h: 0}
};
function createLeafNode(label, depth) {
let n = document.createElement('div');
const offset = {x: 0, y: 0};
const xPos = 0;
const yPos = 0;
n.style.position = 'absolute';
n.style.left = `${xPos}px`;
n.style.top = `${yPos}px`;
n.className = 'leaf-node';
n.innerHTML = label;
canvasEl.append(n);
return n;
}
function getNodeHeights(n) {
}
function createNodes(node, depth) {
// first pass: get all nodes created with their heights
if (typeof node === 'undefined' || node === false) return;
createNodes(node.y, depth + 1);
createNodes(node.n, depth + 1);
node.el = createLeafNode(node.label, depth);
}
function calcNodeSize(node, depth) {
if (typeof node === 'undefined' || node === false) return;
const w = node.el.clientWidth;
const h = node.el.clientHeight;
if (layout.maxWidth < w) layout.maxWidth = w;
if (layout.maxHeight < h) layout.maxHeight = h;
if (layout.depths.length < depth + 1) {
layout.depths.push({
maxWidth: 0,
maxHeight: 0,
nodeCount: 0
});
}
const ld = layout.depths[depth];
if (ld.maxWidth < w) ld.maxWidth = w;
if (ld.maxHeight < h) ld.maxHeight = h;
ld.nodeCount++;
calcNodeSize(node.y, depth + 1);
calcNodeSize(node.n, depth + 1);
}
function findExtents(node, depth) {
if (typeof node === 'undefined' || node === false) return;
// Vertical extent: find the tallest node, and calculate the height of the deepest level
// by assuming it will have all of its nodes, as tall as the tallest node.
layout.extents.h = layout.maxHeight * Math.pow(2, layout.depths.length);
for (let i = 0; i < layout.depths.length; ++i) {
layout.extents.w += layout.depths[i].maxWidth;
}
const extentsEl = document.createElement('div');
extentsEl.style.width = `${layout.extents.w}px`;
extentsEl.style.height = `${layout.extents.h}px`;
extentsEl.style.border = '2px solid red';
extentsEl.style.position = 'absolute';
extentsEl.style.top = '0px';
extentsEl.style.left = '0px';
canvasEl.append(extentsEl);
}
function layoutNodes(node, depth, origin) {
if (typeof node === 'undefined' || node === false) return;
// Position this node, centered in the origin
let pos = {x: origin.x, y: origin.y};
pos.y = origin.y + (origin.h / 2) - (node.el.clientHeight / 2);
node.el.style.left = `${pos.x}px`;
node.el.style.top = `${pos.y}px`;
if (layout.depths.length <= depth + 1) {
return;
}
const newWidth = layout.depths[depth + 1].maxWidth;
const newHeight = origin.h / 2;
const newXOrigin = layout.depths[depth].maxWidth;
const newYOrigin = (layout.extents.h / Math.pow(2, depth)) + origin.y;
layoutNodes(node.n, depth + 1, {x: pos.x + newXOrigin, y: origin.y, w: newWidth, h: newHeight});
layoutNodes(node.y, depth + 1, {x: pos.x + newXOrigin, y: origin.y + newHeight, w: newWidth, h: newHeight});
}
createNodes(data.root, 0);
calcNodeSize(data.root, 0);
findExtents(data.root, 0);
layoutNodes(data.root, 0, {x: 0, y: 0, w: layout.depths[0].maxWidth, h: layout.extents.h});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment