Public
Edited
Mar 6
Importers
Insert cell
Insert cell
extension = ({
character,
randomNoise,
draggable,
measureText
})
Insert cell
SVG.svg([
character({ text: "A", width: 50, fill: "red", "font-weight": "bold" })
])
Insert cell
draggable = ({ ondrag, onstart, onend }, nodes) => {
const drag = d3
.drag()
.on("drag", ondrag)
.on("start", onstart)
.on("end", onend);
const node = SVG.g({}, nodes);
d3.select(node).call(drag);
return node;
}
Insert cell
function character({ text, x = 0, y = 0, width, height, ...style }) {
const [w, h] = measureText(text);
let scaleX = width === undefined ? height / h : width / w;
let scaleY = height === undefined ? width / w : height / h;
if (width === undefined && height === undefined) (scaleX = 1), (scaleY = 1);
return SVG.g(
{ x, y, transform: `translate(${x}, ${y}) scale(${scaleX}, ${scaleY})` },
[SVG.text({ dy: "1em", ...style }, [text])]
);
}
Insert cell
function measureText(text, { fontSize = 14 } = {}) {
function createSpan(text) {
const span = document.createElement("span");
span.style.position = "absolute";
span.style.visibility = "hidden";
span.style.whiteSpace = "pre";
span.style.fontSize = fontSize + "px";
span.textContent = text;
return span;
}

const span = createSpan(text);
document.body.appendChild(span);
const { width, height } = span.getBoundingClientRect();
document.body.removeChild(span);
return [width, height];
}
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
SVG = cm.SVG
Insert cell
cm = require("charmingjs@0.0.9")
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