Public
Edited
Nov 11, 2022
2 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
canvas1 = {
const context = DOM.context2d(width, height);

var pathPoints;

// Background
{
// context.fillStyle = "hsl(216deg 100% 13%)";
// context.fillRect(0, 0, width, height);
context.canvas.style.background = "hsl(216deg 50% 13%)";
}

// Draw points
{
function draw(pts, yOffset = 0, radius = 5, opacityPostFix = "a0") {
const maxV = d3.max(pts, (e) => e.v);
const minV = d3.min(pts, (e) => e.v);

pts.map((pts) => {
const { x, y, v } = pts;

(context.fillStyle =
d3.color(colorMap((v - minV) / (maxV - minV))).hex() +
opacityPostFix),
context.beginPath(),
context.arc(
constants.xScale(x),
constants.yScale(y + yOffset),
radius,
0,
Math.PI * 2
),
context.fill();
});
}

const radius = width / constants.numX / 3;
const gridFieldLst = [];
for (const key in gridField) {
gridFieldLst.push(gridField[key]);
}

draw(gridFieldLst, 0, radius, "90");
}

return context.canvas;
}
Insert cell
points
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
points = data.points
Insert cell
path = mkPath(mkLinks(data.points, data.map))
Insert cell
pathList = {
const t = new Date();
const lst = [...new Array(repeat)];
const pathLst = lst.map((e) => mkPath(mkLinks(data.points, data.map)));
constants["simulationTime"] = new Date() - t;
return pathLst;
}
Insert cell
function mkPath(links) {
const { numX, numY } = constants;
return dijkstra.find_path(links, "0-0", ij2str(numX, numY));
}
Insert cell
Insert cell
data = {
const { numX, numY } = constants;

const points = [];
const map = {};

var pnt, x, y, v, dt;

// Build dataset with points and map
for (let i = 0; i < numX + 1; ++i) {
for (let j = 0; j < numY + 1; ++j) {
x = i / numX;
y = j / numY;
const { fi, fj } = getField(i, j);

pnt = {
i,
j,
x,
y,
fi,
fj
};

map[ij2str(i, j)] = pnt;

points.push(pnt);
}
}

// Compute v and dt for every point
points.map((pnt, ii) => {
const { i, j } = pnt;
v = computeV(i, j, map);
v = v ? v : 0;
dt = v === 0 ? 100 : 1 / v;
pnt.v = v;
pnt.dt = dt;
});

return { points, map };
}
Insert cell
// Convert point index to str (unique)
function str2ij(str) {
const pair = str.split("-").map((e) => parseInt(e));
return { i: pair[0], j: pair[1] };
}
Insert cell
// Convert str (unique) to point index
function ij2str(i, j) {
return i + "-" + j;
}
Insert cell
function getField(i, j) {
var fi =
i === 0 ? 0 : gridField[ij2str(i, j)].v - gridField[ij2str(i - 1, j)].v;
var fj =
j === 0 ? 0 : gridField[ij2str(i, j)].v - gridField[ij2str(i, j - 1)].v;

fi = fi ? fi : 0;
fj = fj ? fj : 0;

return { fi, fj };
}
Insert cell
// Compute the velocity version 2.0
function computeV(i, j, map) {
var cumFi = 0,
cumFj = 0;

const fixJ = 0;
for (let ii = 0; ii < i; ++ii) {
const { fi } = map[ij2str(ii + 1, fixJ)];
cumFi += fi;
}

const fixI = i;
for (let jj = 0; jj < j; ++jj) {
const { fj } = map[ij2str(fixI, jj + 1)];
cumFj += fj;
}

const v = Math.sqrt(2 * (cumFi + cumFj));
return v;
}
Insert cell
constants = {
return {
sqrt2: Math.sqrt(2),
mass: 1,
gravity: 9.8,
numX: numX,
numY: parseInt((numX / width) * height),
xScale: d3.scaleLinear().domain([-0.1, 1.1]).range([0, width]),
yScale: d3.scaleLinear().domain([-0.1, 1.1]).range([0, height])
};
}
Insert cell
height = (width * 9) / 16
Insert cell
Insert cell
Insert cell
dijkstra = require("https://bundle.run/dijkstrajs@1.0.2")
Insert cell
d3 = require("d3")
Insert cell
Insert cell
period = 0.05
Insert cell
gridField = {
reBuildFieldBtn;
const noise = new Noise(3);
const grid = {};
const { numX, numY } = constants;
for (let i = 0; i < numX + 1; ++i) {
for (let j = 0; j < numY + 1; ++j) {
grid[ij2str(i, j)] = {
x: i / numX,
y: j / numY,
v: noise.noise(i * period, j * period)
};
}
}
return grid;
}
Insert cell
class Noise {
static lerp(t, a, b) {
return a + t * (b - a);
}
static grad2d(i, x, y) {
const v = (i & 1) === 0 ? x : y;
return (i & 2) === 0 ? -v : v;
}
constructor(octaves = 1) {
this.p = new Uint8Array(512);
this.octaves = octaves;
this.init();
}
init() {
for (let i = 0; i < 512; ++i) {
this.p[i] = Math.random() * 256;
}
}
noise2d(x2d, y2d) {
const X = Math.floor(x2d) & 255;
const Y = Math.floor(y2d) & 255;
const x = x2d - Math.floor(x2d);
const y = y2d - Math.floor(y2d);
const fx = (3 - 2 * x) * x * x;
const fy = (3 - 2 * y) * y * y;
const p0 = this.p[X] + Y;
const p1 = this.p[X + 1] + Y;
return Noise.lerp(
fy,
Noise.lerp(
fx,
Noise.grad2d(this.p[p0], x, y),
Noise.grad2d(this.p[p1], x - 1, y)
),
Noise.lerp(
fx,
Noise.grad2d(this.p[p0 + 1], x, y - 1),
Noise.grad2d(this.p[p1 + 1], x - 1, y - 1)
)
);
}
noise(x, y) {
let e = 1,
k = 1,
s = 0;
for (let i = 0; i < this.octaves; ++i) {
e *= 0.5;
s += e * (1 + this.noise2d(k * x, k * y)) / 2;
k *= 2;
}
return s;
}
}
Insert cell
import { samples } from "@mbostock/1d-poisson-disk-sampling"
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