Public
Edited
Feb 2
Importers
Insert cell
Insert cell
_cm = require("charmingjs@0.0.6")
Insert cell
cm = ({
svg,
html,
flow,
map,
line,
area,
areaRadial,
$: _cm.$,
randomNoise
})
Insert cell
Insert cell
function exclude(proxy) {
const keys = ["then", "next", "return"];
return new Proxy(proxy, {
get: (target, name) => {
if (keys.includes(name)) return;
return Reflect.get(target, name);
}
});
}
Insert cell
svg = exclude(_cm.svg)
Insert cell
html = exclude(_cm.html)
Insert cell
Insert cell
svg.svg({ width: 100, height: 100 }, [
svg.circle({ cx: 50, cy: 50, r: 40, fill: "black" })
])
Insert cell
html.span(["hello world"])
Insert cell
Insert cell
flow = {
const flow = _cm.flow;
return () => {
const f = flow();

function event(state, key, callback, options) {
const l = (event) => callback(state, event);
window.addEventListener(key, l, options);
return () => window.removeEventListener(key, l);
}

function animate(state, key, callback, options = {}) {
const { frameRate } = options;
const delay = frameRate ? 1000 / frameRate : undefined;
let count = 0;
const timer = d3.interval((elapsed) => {
callback(state, { elapsed, frameCount: count });
count++;
}, delay);
return () => timer.stop();
}

function flow_on(key, callback, options) {
return this.observe((state) => {
const call = key === "loop" ? animate : event;
return call(state, key, callback, options);
});
}

const proto = f.constructor.prototype;
f.constructor.prototype.on = flow_on;
return f;
};
}
Insert cell
Insert cell
counter = html.span([1])
Insert cell
timer = html.span([0])
Insert cell
flow()
.let("n", 2)
.on("click", (d) => (counter.textContent = counter.textContent * d.n))
.on(
"loop",
(d, { elapsed, frameCount }) =>
(timer.textContent = ((elapsed / (d.n * 5)) | 0) + "-" + frameCount)
)
.join()
Insert cell
Insert cell
function assign(create) {
return (data, options) => {
const generator = create();
for (const key in options) {
const value = options[key];
generator[key](value);
}
return generator(data);
};
}
Insert cell
line = assign(d3.line)
Insert cell
area = assign(d3.area)
Insert cell
areaRadial = assign(d3.areaRadial)
Insert cell
line([0, 1, 2, 3], { x: (d) => d, y: (_, i) => i })
Insert cell
area([0, 1, 2, 3], { x: (d) => d, y: (_, i) => i })
Insert cell
Insert cell
map = function (x, domainMin, domainMax, rangeMin, rangeMax) {
if (arguments.length === 3)
(rangeMin = domainMin),
(rangeMax = domainMax),
(domainMin = 0),
(domainMax = 1);
const scale = d3.scaleLinear([domainMin, domainMax], [rangeMin, rangeMax]);
return scale(x);
}
Insert cell
map(5, 0, 10, 0, 100)
Insert cell
map(0.5, 0, 10)
Insert cell
randomNoise = {
const PERLIN_YWRAPB = 4;
const PERLIN_YWRAP = 1 << PERLIN_YWRAPB;
const PERLIN_ZWRAPB = 8;
const PERLIN_ZWRAP = 1 << PERLIN_ZWRAPB;
const PERLIN_SIZE = 4095;

function scaledCosine(i) {
return 0.5 * (1.0 - Math.cos(i * Math.PI));
}

function randomLcg(seed) {
const m = 4294967296;
const a = 1664525;
const c = 1013904223;
let z = (seed == null ? Math.random() * m : seed) >>> 0;
return () => {
z = (a * z + c) % m;
return z / m;
};
}

function noiseSeed(seed) {
const random = randomLcg(seed);
const perlin = new Array(PERLIN_SIZE + 1);
for (let i = 0; i < PERLIN_SIZE + 1; i++) {
perlin[i] = random();
}
return perlin;
}

// Adapting from P5.js
// @see https://github.com/processing/p5.js/blob/1e6b0caa1e8a8dff3280917d2fd9a84d2e7126ba/src/math/noise.js#L200
return function randomNoise(
lo = 0,
hi = 1,
{ octaves = 4, seed = d3.randomUniform(100000)(), falloff = 0.5 } = {}
) {
const map = d3.scaleLinear([0, 1], [lo, hi]);
const perlin = noiseSeed(seed);

const noise = (x, y = 0, z = 0) => {
if (x < 0) x = -x;
if (y < 0) y = -y;
if (z < 0) z = -z;

let xi = Math.floor(x),
yi = Math.floor(y),
zi = Math.floor(z);
let xf = x - xi;
let yf = y - yi;
let zf = z - zi;
let rxf, ryf;

let r = 0;
let ampl = 0.5;

let n1, n2, n3;

for (let o = 0; o < octaves; o++) {
let of = xi + (yi << PERLIN_YWRAPB) + (zi << PERLIN_ZWRAPB);

rxf = scaledCosine(xf);
ryf = scaledCosine(yf);

n1 = perlin[of & PERLIN_SIZE];
n1 += rxf * (perlin[(of + 1) & PERLIN_SIZE] - n1);
n2 = perlin[(of + PERLIN_YWRAP) & PERLIN_SIZE];
n2 += rxf * (perlin[(of + PERLIN_YWRAP + 1) & PERLIN_SIZE] - n2);
n1 += ryf * (n2 - n1);

of += PERLIN_ZWRAP;
n2 = perlin[of & PERLIN_SIZE];
n2 += rxf * (perlin[(of + 1) & PERLIN_SIZE] - n2);
n3 = perlin[(of + PERLIN_YWRAP) & PERLIN_SIZE];
n3 += rxf * (perlin[(of + PERLIN_YWRAP + 1) & PERLIN_SIZE] - n3);
n2 += ryf * (n3 - n2);

n1 += scaledCosine(zf) * (n2 - n1);

r += n1 * ampl;
ampl *= falloff;
xi <<= 1;
xf *= 2;
yi <<= 1;
yf *= 2;
zi <<= 1;
zf *= 2;

if (xf >= 1.0) {
xi++;
xf--;
}
if (yf >= 1.0) {
yi++;
yf--;
}
if (zf >= 1.0) {
zi++;
zf--;
}
}
return r;
};

return (x0 = 0, y0 = 0, z0 = 0) => {
const t = map(noise(x0, y0, z0));
return t;
};
};
}
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