Skip to content

Commit cb6d92e

Browse files
Filmbostock
authored andcommitted
balanced trees
closes #1589
1 parent 3d1c4f3 commit cb6d92e

File tree

11 files changed

+1553
-1507
lines changed

11 files changed

+1553
-1507
lines changed

docs/marks/tree.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ For example, here is a little family tree of Greek gods.
4141
Plot.plot({
4242
axis: null,
4343
height: 100,
44-
margin: 20,
44+
margin: 10,
45+
marginLeft: 35,
4546
marginRight: 120,
4647
marks: [
4748
Plot.tree(gods, {textStroke: "var(--vp-c-bg)"})
@@ -63,6 +64,7 @@ As a more complete example, here is a visualization of a software package hierar
6364
Plot.plot({
6465
axis: null,
6566
margin: 10,
67+
marginLeft: 30,
6668
marginRight: 160,
6769
width: 688,
6870
height: 1800,
@@ -80,6 +82,7 @@ The **treeLayout** option specifies the layout algorithm. The tree mark uses the
8082
Plot.plot({
8183
axis: null,
8284
margin: 10,
85+
marginLeft: 30,
8386
marginRight: 160,
8487
width: 688,
8588
height: 2400,

src/marks/tree.js

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {cluster as Cluster} from "d3";
22
import {isNoneish} from "../options.js";
33
import {marks} from "../mark.js";
44
import {maybeTreeAnchor, treeLink, treeNode} from "../transforms/tree.js";
5+
import {filter} from "../transforms/basic.js";
56
import {dot} from "./dot.js";
67
import {link} from "./link.js";
78
import {text} from "./text.js";
@@ -27,10 +28,12 @@ export function tree(
2728
title = "node:path",
2829
dx,
2930
dy,
31+
textAnchor,
3032
...options
3133
} = {}
3234
) {
3335
if (dx === undefined) dx = maybeTreeAnchor(options.treeAnchor).dx;
36+
if (textAnchor !== undefined) throw new Error("textAnchor is not a configurable tree option");
3437
return marks(
3538
link(
3639
data,
@@ -50,18 +53,40 @@ export function tree(
5053
),
5154
dotDot ? dot(data, treeNode({fill: fill === undefined ? "node:internal" : fill, title, ...options})) : null,
5255
textText != null
53-
? text(
54-
data,
55-
treeNode({
56-
text: textText,
57-
fill: fill === undefined ? "currentColor" : fill,
58-
stroke: textStroke,
59-
dx,
60-
dy,
61-
title,
62-
...options
63-
})
64-
)
56+
? [
57+
text(
58+
data,
59+
filter(
60+
(d) => !d.internal,
61+
treeNode({
62+
text: textText,
63+
fill: fill === undefined ? "currentColor" : fill,
64+
stroke: textStroke,
65+
dx,
66+
dy,
67+
title,
68+
textAnchor: "start",
69+
...options
70+
})
71+
)
72+
),
73+
text(
74+
data,
75+
filter(
76+
"internal",
77+
treeNode({
78+
text: textText,
79+
fill: fill === undefined ? "currentColor" : fill,
80+
stroke: textStroke,
81+
dx: -dx,
82+
dy,
83+
title,
84+
textAnchor: "end",
85+
...options
86+
})
87+
)
88+
)
89+
]
6590
: null
6691
);
6792
}

src/transforms/tree.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,14 @@ export function treeNode({
4343
layout(root);
4444
for (const node of root.descendants()) {
4545
treeFacet.push(++treeIndex);
46-
treeData[treeIndex] = node.data;
46+
treeData[treeIndex] = {
47+
data: node.data,
48+
name: nodeName(node),
49+
path: nodePath(node),
50+
internal: nodeInternal(node),
51+
depth: node.depth, // nodeDepth(node)
52+
height: node.height // nodeHeight(node)
53+
};
4754
treeAnchor.position(node, treeIndex, X, Y);
4855
for (const o of outputs) o[output_values][treeIndex] = o[output_evaluate](node);
4956
}

test/output/flareCluster.svg

Lines changed: 317 additions & 315 deletions
Loading

test/output/flareIndent.svg

Lines changed: 691 additions & 689 deletions
Loading

test/output/flareTree.svg

Lines changed: 472 additions & 470 deletions
Loading

test/output/greekGods.svg

Lines changed: 19 additions & 17 deletions
Loading

test/plots/flare-cluster.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export async function flareCluster() {
66
return Plot.plot({
77
axis: null,
88
inset: 10,
9+
insetLeft: 30,
910
insetRight: 120,
1011
height: 2400,
1112
marks: Plot.cluster(flare, {path: "name", delimiter: "."})

test/plots/flare-indent.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ export async function flareIndent() {
66
return Plot.plot({
77
axis: null,
88
inset: 10,
9+
insetLeft: 70,
910
insetRight: 120,
1011
round: true,
11-
width: 200,
12+
width: 300,
1213
height: 3600,
1314
marks: Plot.tree(flare, {
1415
strokeWidth: 1,

test/plots/flare-tree.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export async function flareTree() {
66
return Plot.plot({
77
axis: null,
88
inset: 10,
9+
insetLeft: 30,
910
insetRight: 120,
1011
height: 1800,
1112
marks: Plot.tree(flare, {markerEnd: "arrow", path: "name", delimiter: "."})

0 commit comments

Comments
 (0)