Public
Edited
Jan 12, 2024
1 star
Insert cell
Insert cell
Insert cell
Insert cell
{
let plt;

let plot = () => {
let triangle = rndTriangle(),
nodes = rawNodes.map((d) => computeSdf(triangle, d));

plt = Plot.plot({
grid: true,
aspectRatio: 1.0,
x: { nice: true, domain: d3.extent(nodes, (d) => d.x) },
y: { nice: true, domain: d3.extent(nodes, (d) => d.y) },
color: { nice: true, legend: true, scheme: "RdBu", domain: [-2.5, 2.5] },
marks: [
Plot.line(triangle, { x: "x", y: "y", fill: "black", opacity: 1.0 }),
Plot.contour(nodes, {
x: "x",
y: "y",
fill: "distance",
opacity: 0.8,
interval: 0.1
}),
Plot.link(nodes, {
x1: "x",
y1: "y",
x2: "npx",
y2: "npy",
stroke: "distance",
strokeWidth: 0.5,
strokeOpacity: 0.5
}),
Plot.dot(nodes, {
x: "x",
y: "y",
fill: (d) => (d.distance > 0 ? "white" : "red"),
r: 1
}),
Plot.dot(nodes, {
x: "npx",
y: "npy",
fill: (d) => (d.distance > 0 ? "black" : "red"),
r: 1
})
]
});
};

while (toggle) {
let now = performance.now();
plot();
yield plt;
}

plot();
yield plt;
}
Insert cell
nodes = {
rawNodes.map((d) => computeSdf(triangle, d));

return rawNodes;
}
Insert cell
rawNodes = {
let nodes = [],
n = 200;

for (let i = 0; i < n; ++i) {
nodes.push({ x: rnd(), y: rnd(), i });
}

return nodes;
}
Insert cell
computeSdf(triangle)
Insert cell
triangle = {
button;
let tri = rndTriangle();

return tri;
}
Insert cell
Insert cell
computeSdf = (triangle, pos = { x: 0, y: 0 }) => {
const mx = d3.mean(triangle, (d) => d.x),
my = d3.mean(triangle, (d) => d.y);

let x,
y,
cx,
cy,
x0,
y0,
vx,
vy,
r2,
dot,
sdf = () => {
let distance, nearestPnt;

dot = x * vx + y * vy;

// Do nothing if the (x, y) is on the same side with the (cx, cy)
if ((x * vy - y * vx) * (cx * vy - cy * vx) > 0) {
let r = dot / r2;
distance = dist(x - r * vx, y - r * vy);
nearestPnt = { x: x0 + r * vx, y: y0 + r * vy };
return { distance, nearestPnt, outside: false };
}

if (dot < 0) {
nearestPnt = { x: x0, y: y0 };
distance = Math.sqrt(dist(x, y));
} else if (dot > r2) {
nearestPnt = { x: x0 + vx, y: y0 + vy };
distance = dist(vx - x, vy - y);
} else {
let r = dot / r2;
nearestPnt = { x: x0 + r * vx, y: y0 + r * vy };
distance = dist(x - r * vx, y - r * vy);
}

return { distance, nearestPnt, outside: true };
};

let sdfs = [];

for (let i = 0; i < triangle.length; ++i) {
x0 = triangle[(i + 1) % triangle.length].x;
y0 = triangle[(i + 1) % triangle.length].y;
vx = triangle[i].x - x0;
vy = triangle[i].y - y0;
x = pos.x - x0;
y = pos.y - y0;
cx = mx - x0;
cy = my - y0;
r2 = vx * vx + vy * vy;

sdfs.push(sdf());
}

let outsides = sdfs
.filter((d) => d.outside)
.sort((a, b) => a.distance - b.distance),
insides = sdfs
.filter((d) => !d.outsides)
.sort((a, b) => a.distance - b.distance);

if (outsides.length > 0) {
return Object.assign(pos, {
npx: outsides[0].nearestPnt.x,
npy: outsides[0].nearestPnt.y,
distance: outsides[0].distance
});
}

return Object.assign(pos, {
npx: insides[0].nearestPnt.x,
npy: insides[0].nearestPnt.y,
distance: -insides[0].distance
});
}
Insert cell
dist = (x, y) => {
return Math.sqrt(x * x + y * y);
}
Insert cell
rndTriangle = (name = "Triangle1", secs = undefined) => {
secs = secs || performance.now() / 1000 / 30;

let nodes = [],
scale = d3.scaleLinear().domain([-1, 1]).range([-2, 2]),
x,
y;

polygonSetup.map(({ i, rx, ry }) => {
x = noiseGen.noise3D(secs, i + rx, i + rx + 100);
y = noiseGen.noise3D(secs, i + ry, i + ry + 200);
nodes.push({ x: scale(x), y: scale(y), i, name });
});

nodes = d3.polygonHull(nodes.map((d) => [d.x, d.y]));
nodes = nodes.map((d, i) => Object.assign({ i, name }, { x: d[0], y: d[1] }));

// for (let i = 0; i < 3; ++i) {
// x = noiseGen.noise3D(secs, i + 101, i + 230);
// y = noiseGen.noise3D(secs, i + 350, i + 460);
// nodes.push({ x: scale(x), y: scale(y), i, name });
// }

// return d3.polygonHull(nodes);

return nodes;
}
Insert cell
polygonSetup = {
const n = 9,
rndInt = d3.randomUniform(100, 1000),
rndXYs = [];

for (let i = 0; i < n; ++i) {
rndXYs.push({ i, rx: rndInt(), ry: rndInt() });
}

return rndXYs;
}
Insert cell
rnd = d3.randomNormal()
Insert cell
Insert cell
noiseGen = new simplexNoise(d3.randomUniform()())
Insert cell
d3 = require("d3")
Insert cell
simplexNoise = require("simplex-noise@2.4.0")
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