Public
Edited
Nov 29, 2023
Insert cell
Insert cell
import {p5, ParticleSystem} from "@tmcw/p5"
Insert cell
data = {
return {
children: [{
children: [
{ children: [{
children:null
}] },
{ children: null },
]
},
{ children: null },
{ children: [
{children:[
{children: null},
{children: null}
]},
{children:null},
{children:[
{children:null},
{children:null},
{children:null},
{children:null},
{children:null},
{children:null},
]},
] }
]
}
}
Insert cell
data1 = {
return {
children: [{
children: [
{ children: [{
children:null
}] },
{ children: null },
]
},
{ children: null },
{ children: [
{children:[
{children: null},
{children: null}
]},
{children:null},
{children:[
{children:null},
{children:null},
{children:null},
{children:null},
{children:null},
{children:null},
]},
] }
]
}
}
Insert cell
Insert cell

function firstTraverse(node,level = 1,visitor) {

if(Array.isArray(node.children)) {
for (let i = 0; i < node.children.length; ++i) {
const child = node.children[i];
if(i === 0) {
child.isLeftMost = true;
} else {
child.previousSlibling = node.children[i - 1];
}
firstTraverse(child,level + 1,visitor);
}
}
node.y = (level - 1) * 50;
if(node.isLeftMost || level === 1) {
node.x = 0;
} else {
node.x = node.previousSlibling.x + 90;
}
visitor(node);
}

Insert cell
run = p5(sketch => {
sketch.setup = function() {
sketch.createCanvas(width, 210);
sketch.background(0,0,0,0)
sketch.fill(6, 209, 57);

let startX = 30;
let startY = 30
firstTraverse(data,1,(node) => {
setTimeout(() => {
sketch.circle(node.x + startX,node.y+ startY,30);
},0)
if(Array.isArray(node.children)) {
for (const child of node.children) {
sketch.line(node.x + startX,node.y+startY,child.x+ startX,child.y + startY);
}
}
});
};
})
Insert cell
Insert cell
function firstTraverse1(node,level = 1,visitor =() =>{}) {
node.y = (level - 1) * 50;

if(Array.isArray(node.children)) {
for (let i = 0; i < node.children.length; ++i) {
const child = node.children[i];
if(i === 0) {
child.isLeftMost = true;
} else {
child.previousSlibling = node.children[i - 1];
}
firstTraverse1(child,level + 1,visitor);
}
if(node.children.length === 1) {
node.x = 0;
} else {
node.x = (node.children[0].x + node.children[node.children.length - 1].x) / 2;
}
} else {
if(node.isLeftMost || level === 1) {
node.x = 0;
} else {
node.x = node.previousSlibling.x + 90;
}
}

visitor(node);
}
Insert cell
run1 = p5(sketch => {
sketch.setup = function() {
sketch.createCanvas(width, 210);
sketch.background(0,0,0,0)
sketch.fill(6, 209, 57);

let startX = 30;
let startY = 30
firstTraverse1(data1,1,(node) => {
setTimeout(() => {
sketch.circle(node.x + startX,node.y+ startY,30);
// sketch.text(node.x + startX,node.y+ startY,node.x + startX);
},0)
if(Array.isArray(node.children)) {
for (const child of node.children) {
sketch.line(node.x + startX,node.y+startY,child.x+ startX,child.y + startY);
}
}
});
};
})
Insert cell
Insert cell
function firstTraverse2(node, level = 1) {
node.mod = 0;
node.y = (level - 1) * 50;
if (Array.isArray(node.children)) {

for (let i = 0; i < node.children.length; ++i) {
const child = node.children[i];
if (i === 0) {
child.isLeftMost = true;
} else {
child.previousSlibling = node.children[i - 1];
}
firstTraverse2(child, level + 1);
}

if (node.children.length === 1) {
node.x = 0;
} else {
node.x = (node.children[0].x + node.children[node.children.length - 1].x) / 2;
}

} else {
if (node.isLeftMost || level === 1) {
node.x = 0;
} else {
node.x = node.previousSlibling.x + 90;
}
}

}
Insert cell
function secondWalk(node, contours = new Map(), modSum = 0) {
// 当前节点是否需要右移
if (contours.get(node.y) == undefined || node.x + modSum > contours.get(node.y)) {
// node.x + modSum > 右轮廓,不需要右移
// 更新当前层级的右轮廓
contours.set(node.y, node.x + modSum);
} else {
// node.x + modSum < 右轮廓,需要右移
// 当前 node 的右移量要足够大,我们使用 右轮廓 - node.x 获取于右轮廓重叠的量,然后再加上节点宽度。
node.mod = contours.get(node.y) - node.x + 30;
}
// 把当前节点右移的偏移值给 modSum,传递到子节点判断。
modSum +=node.mod;
if(Array.isArray(node.children)) {
for (let i = 0; i < node.children.length; ++i) {
const child = node.children[i];
secondWalk(child,contours,modSum);
}
}
}
Insert cell
run2 = p5(sketch => {
sketch.setup = function() {
sketch.createCanvas(width, 210);
sketch.background(0,0,0,0)
sketch.fill(6, 209, 57);

let startX = 30;
let startY = 30;
const visitor = (node) => {
setTimeout(() => {
sketch.circle(node.x + startX,node.y+ startY,30);
// if(node.text === 'b') {
// sketch.fill(6, 20, 57);
// console.log(node.x)
// sketch.circle(node.x + 1 + startX+ 5,node.y+ startY,30);
// sketch.fill(6, 209, 57);
// }
},0)
if(Array.isArray(node.children)) {
for (const child of node.children) {
sketch.line(node.x + startX,node.y+startY,child.x+ startX,child.y + startY);
}
}
};
const d = JSON.parse(JSON.stringify(data2));
firstTraverse1(d);
secondWalk(d);
applyModifications(d,0,visitor);
};
})
Insert cell
function applyModifications(node,modSum,visitor) {
modSum += node.mod;
node.x += modSum;
if(Array.isArray(node.children)) {
for (let i = 0; i < node.children.length; ++i) {
const child = node.children[i];
applyModifications(child, modSum,visitor);
}
// node.x = (node.children[0].x + node.children[node.children.length - 1].x) / 2;
}
visitor(node);
}
Insert cell
data2 = {
return {
children: [{
children: [
{ children: [{
children:null
}] },
{ children: null },
]
},
{ children: null },
{ children: [
{children:[
{ text:'c',
children: null
},
{children: null}
]},
{children:null},
{
text: 'a',
children:[
{
text: 'b1',
children:null
},
{text: 'b2',children:null},
{text: 'b3',children:null},
{text: 'b4',children:null},
{text: 'b5',children:null},
{text: 'b6',children:null},
]},
] }
]
}
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more