Public
Edited
Jun 3, 2024
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
let plt,
// Prevent negative kk value
kk = ((-k % optionForm.n) + optionForm.n) % optionForm.n;

computeXY(k);

let offsetDistance = shapeData[kk].distance;

plt = Plot.plot({
x: { nice: true, domain: [-2, d3.max(roadData, (d) => d.y) + 2] },
y: {
nice: true,
domain: [-d3.max(shapeData, (d) => d.r), d3.max(shapeData, (d) => d.r)],
reverse: true
},
color: { legend: true },
aspectRatio: 1.0,
grid: true,
marks: [
// Plot circle
Plot.dot(shapeData, {
x: (d) => d.y + offsetDistance,
y: "x",
r: 2,
fill: (d) => "gray" //"theta"
}),
Plot.dot(roadData, { x: "y", y: "x", fill: "v" }),
Plot.dot(roadData, {
x: (d) => d.y - d3.max(roadData, (d) => d.y),
y: "x",
fill: "v"
}),
Plot.dot(roadData, {
x: (d) => d.y + d3.max(roadData, (d) => d.y),
y: "x",
fill: "v"
}),
// Plot connection
Plot.arrow(shapeData.slice(kk, kk + 1), {
x1: (d) => 0 + d.distance,
x2: (d) => d.y + d.distance,
y1: (d) => 0,
y2: "x"
}),
Plot.ruleY([0])
]
});

return plt;
}
Insert cell
computeXY = (k = 0) => {
let x,
y,
x0,
y0,
phi = (k / optionForm.n) * Math.PI * 2;

shapeData.map((d) => {
x = d.r * Math.cos(d.theta + phi);
y = d.r * Math.sin(d.theta + phi);
Object.assign(d, { x, y });
});

return shapeData;
}
Insert cell
roadData = {
let data = shapeData.map((d) => {
let { distance, r, v } = d;
return Object.assign({}, { y: distance, x: r, v });
});

return data;
}
Insert cell
shapeData = {
refreshButton;
let { n, octave, shape } = optionForm;

let noiseGen = new simplexNoise(performance.now());

// ---------------------------------
let scaleTheta = d3
.scaleLinear()
.domain([0, n])
.range([0, 2 * Math.PI]),
data = [];

// ---------------------------------
// Make data
let theta,
dtheta = scaleTheta(1),
r;

// ---------------------------------
// Make ellipse shape data
if (shape === "ellipse") {
let p = 1,
e = 0.5;

let dist = (x, y) => {
return Math.sqrt(x * x + y * y);
};

for (let i = 0; i < n; ++i) {
theta = scaleTheta(i);
r = p / (1 + e * Math.cos(theta));
data.push({ r, theta, dtheta });
}
}

// ---------------------------------
// Make glass shape data
if (shape == "glass") {
let a = 3,
b = 1;

let dist = (x, y) => {
return x * x + y * y;
};

for (let i = 0; i < n; ++i) {
theta = scaleTheta(i);
r = dist(a * Math.cos(theta), b * Math.sin(theta));
data.push({ r, theta, dtheta });
}
}

// ---------------------------------
// Make heart shape data
if (shape === "heart") {
let a = 3,
b = 1,
c = Math.sqrt(a * a - b * b);

let dist = (x, y) => {
return Math.sqrt(x * x + y * y);
};

for (let i = 0; i < n; ++i) {
theta = scaleTheta(i);
r = dist(a * Math.cos(theta) + c, b * Math.sin(theta));
r = 1 - Math.cos(theta);
data.push({ r, theta, dtheta });
}
}

// ---------------------------------
// Make random shape data
if (shape === "random") {
for (let i = 0; i < n; ++i) {
theta = scaleTheta(i);
r = noiseGen.noise2D(0.5, (i / n) * octave);
data.push({ r, theta, dtheta });
}

// ---------------------------------
// Make data a circle
let scale = d3
.scaleLinear()
.domain([0, n])
.range([data[0].r, data[n - 1].r]);

data.map((d, i) => {
r = d.r - scale(i) + 2;
Object.assign(d, { r });
});
}

// ---------------------------------
// Compute rotating situation
let v,
distance = 0;
data.map((d, i) => {
v = d.r * d.dtheta;
Object.assign(d, { v, distance });
distance += v;
});

return data;
}
Insert cell
shapeData
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
simplexNoise = await require("simplex-noise@2.4.0");
Insert cell
d3 = require('d3')
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