Public
Edited
Nov 11, 2022
2 stars
Estimate the Brachistochrone using Monte Carlo Simulation V2
Estimate the Brachistochrone using Monte Carlo Simulation
Also listed in…
Math
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