Published
Edited
Feb 8, 2020
Fork of Demo
Insert cell
md`# Title`
Insert cell
function draw_path(parts) {
return svg`<svg width="800" height="800" version="1.1" xmlns="http://www.w3.org/2000/svg">
${parts.map(p => `<line x1=${p.x1} y1=${p.y1} x2=${p.x2} y2=${p.y2} fill=none stroke=black stroke-opacity=${p.opacity}></line>`).join('')}
</svg>`
}
Insert cell
{
let last = {x1: 0, x2: 0, y1: 0, y2: 0, opacity: 0};
let parts = [last];
for (let i = 0; i < 100; ++i) {
let next = {x1: last.x2, y1: last.y2, x2: 8 * (i * i % 100), y2: (i * 8), opacity: 1 - (i / 400.0)};
parts.push(next);
last = next;
}
return draw_path(parts);
}
Insert cell
{
let last = {x1: 0, x2: 400, y1: 0, y2: 400, opacity: 0};
let parts = [last];
let max = 1000;
let x = 30;
for (let i = 0; i < max; ++i) {
let frac = i * 1.0 / max;
let next = {
x1: last.x2,
y1: last.y2,
x2: Math.sin(frac * x) * frac * 400 + 400,
y2: Math.cos(frac * x) * frac * 400 + 400,
opacity: 1 - frac,
};
parts.push(next);
last = next;
}
return draw_path(parts);
}
Insert cell
phi = (() => {
let est = 1;
for (let i = 0; i < 100; i++)
est = 1 + 1.0 / est;
return est;
})();
Insert cell
count = (upper) => Array.from(Array(upper).keys());
Insert cell
rad = (theta) => [Math.sin(theta), Math.cos(theta)];
Insert cell
dist = ([ax, ay], [bx, by]) => Math.sqrt((bx - ax) ** 2 + (by - ay) ** 2);
Insert cell
neg = ([vx, vy]) => [-vx, -vy];
Insert cell
add = ([ax, ay], [bx, by]) => [ax + bx, ay + by];
Insert cell
scale = (k, [vx, vy]) => [vx * k, vy * k];
Insert cell
tween = (a, b, b_frac) => add(a, scale(b_frac, add(neg(a), b)));
Insert cell
push = (dist_to_scale, fixed, start) =>
add(fixed,
scale(dist_to_scale(dist(fixed, start)),
add(neg(fixed), start)));
Insert cell
pushes = (dist_to_scale_fixeds, start) =>
dist_to_scale_fixeds.reduce((next, [dts, fixed]) => push(dts, fixed, next), start);
Insert cell
{
function steps(iter, f) {
let f0 = f(0.0);
let last = {x1: 0, y1: 0, x2: f0[0], y2: f0[1], opacity: 0};
let parts = [last];
for (let i = 0; i < iter; ++i) {
let frac = i * 1.0 / iter;
let [nextx, nexty] = f(frac);
let next = {
x1: last.x2, y1: last.y2,
x2: nextx, y2: nexty,
opacity: Math.sqrt(1 - frac),
};
parts.push(next);
last = next;
}
return draw_path(parts);
}
let standard_dist_to_scale = (d) => (1 - 1 / (1 + (d * 0.03 + .5) ** 5));
return steps(20000, (frac) =>
pushes(
count(50).map(i => [
(d) => (1 + (standard_dist_to_scale(d / 400 * 200) - 1) * (i / (5 + i))),
add([400, 400], scale(30*(i + 4) ** .7, rad(phi * 2 * Math.PI * i)))
]),
add(add(
[400, 400],
scale(frac * 400,
rad(frac * 400))),
scale(400 * 0.06 * frac * frac,
rad(0.936420 + 400 * 7.9 * frac)))));
}
Insert cell
Insert cell
rotate_origin = (theta, [x, y]) => ([Math.cos(theta)*x - Math.sin(theta)*y, Math.sin(theta)*x + Math.cos(theta)*y])
Insert cell
rotate = (point, center, theta) => add(center, rotate_origin(theta, add(neg(center), point)))
Insert cell
rotate([1, 0], [0, 1], Math.PI)
Insert cell
pairs_nocycle = (arr) => count(arr.length - 1).map(i => [arr[i], arr[i + 1]]);
Insert cell
pairs = (arr) => count(arr.length).map(i => [arr[i], arr[(i + 1) % arr.length]]);
Insert cell
line = (from, to, opacity) => ({
x1: from[0], y1: from[1],
x2: to[0], y2: to[1],
opacity: opacity === undefined ? 1 : opacity,
});
Insert cell
around = (n, center, first_point) => count(n).map(i => rotate(first_point, center, i * 2 * Math.PI / n));
Insert cell
ngon = (n, center, first_point) => pairs(around(n, center, first_point)).map(([l, r]) => line(l, r));
Insert cell
draw_path(
count(30).map(level =>
around(12 / (level % 3 + 1), [400, 400], [400 + 25 * 2 ** (level * 1/3.0), 400])
.map((c) => ngon(6, c, push(d => 2.0/3, [400, 400], c))).flat()).flat()
);
Insert cell
around_iter = (iter, n, center, first) => {
if (n ** iter > 1000)
throw "around_iter says no";
return count(iter).reduce(
(acc) => [...new Set(
acc.map(c => around(n, c, add(c, add(neg(center), first)))).flat())], [center])
}
Insert cell
draw_path(
around_iter(4, 5, [400, 400], [400, 505])
.map(c => ngon(5, c, [c[0], c[1] + 50])).flat()
);
Insert cell
cube = (dx, dy, dz, front) => [
line(front, add(front, dx)),
line(front, add(front, dy)),
line(front, add(front, dz)),
line(add(front, dx), add(front, add(dx, dy))),
line(add(front, dy), add(front, add(dx, dy))),
line(add(front, dx), add(front, add(dx, dz))),
line(add(front, dz), add(front, add(dx, dz))),
line(add(front, dy), add(front, add(dy, dz))),
line(add(front, dz), add(front, add(dy, dz))),
];
Insert cell
Insert cell
draw_path(cube([90, -50], [-90, -50], [0, 100], [400, 400]))
Insert cell
Insert cell
draw_path(pairs_nocycle(
count(28000).map(i => i * 0.2 / 15).map(i => [
[220.0, i],
[140.0, i / 6],
[17.0, i / 23],
].map(([r, frac]) => scale(r, rad(Math.PI * frac))).reduce(add, [400, 400]))
).map(([a,b]) => line(a, b)));
Insert cell
{
let p = point_table(2);
p.add([0, 0]);
p.add([1, 1]);
p.add([2, 2]);
return p.within([0, 0], 1.9);
}
Insert cell
{
let point_pairs = [[[300, 400], [500, 400]]];
let result_point_pairs = [...point_pairs];
let theta = Math.PI * 2 * (.5 - 1 / 6);
let k = Math.sqrt(2) / 2;
count(9).map(i => {
point_pairs = point_pairs.flatMap(([a, b]) => [
[b, tween(b, rotate(a, b, theta), k)],
[a, tween(a, rotate(b, a, theta), k)],
]);
result_point_pairs = result_point_pairs.concat(point_pairs);
});
return draw_path(result_point_pairs.map(([a,b]) => line(a, b)));
}
Insert cell
{
let point_pairs = [[[400, 650], [400, 400]]];
let theta = Math.PI * 2 / 3.5;
let k = 0.658;
count(15).map(i => {
point_pairs = point_pairs.flatMap(([a, b]) => [
[b, tween(b, rotate(a, b, theta), k)],
[b, tween(b, rotate(a, b, -theta), k)],
]);
});
return draw_path(point_pairs.map(([a,b]) => line(a, b)));
}
Insert cell
point_table = (pixel_size) => {
let data = {};
let bucket = (x, y) => `${Math.floor(x / pixel_size)},${Math.floor(y / pixel_size)}`;
let int_bucket_min_dist = (x, y, bucketx, buckety) => {
let round_toward = (value, toward) => value > toward
? (Math.floor(value) > toward ? Math.floor(value) : toward)
: (Math.ceil(value) < toward ? Math.ceil(value) : toward);
let zero_stop = (value) => value < 0 ? 0 : value;
let x_dist = zero_stop(Math.abs(x - round_toward(bucketx + .5, x)));
let y_dist = zero_stop(Math.abs(y - round_toward(buckety + .5, y)));
return dist([0,0], [x_dist, y_dist]);
};
let buckets_in = (_x, _y, _r) => {
let r = _r / pixel_size;
let x = _x / pixel_size;
let y = _y / pixel_size;
let buckets = [];
let start_bucket = [Math.floor(x), Math.floor(y)];
let iter_limit = Math.ceil(r + 1);
for (let ox = -iter_limit; ox < iter_limit + 1; ox++) {
for (let oy = -iter_limit; oy < iter_limit; oy++) {
let o = add(start_bucket, [ox, oy]);
if (int_bucket_min_dist(x, y, o[0], o[1]) <= r) {
buckets.push(o);
}
}
}
return buckets;
};
return {
within: (origin, r) => {
let [x, y] = origin;
return buckets_in(x, y, r)
.map(([bx, by]) => data[bucket(bx * pixel_size, by * pixel_size)])
.filter(pts => pts !== undefined)
.flat()
.filter(pt => dist([x, y], pt) <= r);
},
add: (pt) => {
let [x, y] = pt;
let b = bucket(x, y);
if (data[b] === undefined) {
data[b] = [];
}
data[b].push(pt);
}
};
}
Insert cell
// f : (dist -> count) -> delta_direction;
drive_car = (f) => {
let direction = 0;
let last = [320, 340];
let points = [];
let table = point_table(14);
let assert_larger = (larger, smaller) => {
if (larger <= smaller)
throw `${larger} is not larger than ${smaller}`;
return smaller;
};
let count_in = (xy, max_dist) => assert_larger(1000, table.within(xy, max_dist).length);
let step_len = 0.8;
count(30000).map(i => {
let next = add(last, scale(step_len, rad(direction)));
direction += f((dist) => count_in(next, dist * step_len));
last = next;
points.push(last);
table.add(last);
});
return draw_path(pairs_nocycle(points).map(([a, b], i) => line(a, b, 1 - .8 * i / (points.length - 0.9))));
}
Insert cell
drive_car((count_inside) => (-1 + Math.atan(30 - 1.8 * count_inside(7) + 5 * count_inside(0.1))) * .02)
Insert cell
drive_car((count_inside) => (-1 + Math.atan(30 - 1.8 * count_inside(7) + 12 * count_inside(2))) * .02)
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