70 lines
1.5 KiB
Plaintext
70 lines
1.5 KiB
Plaintext
// Convert x into its expansion in base b, as an array of integers (ascending powers of b)
|
|
toBase = (b, x) => {
|
|
if (x == 0) { return [0] }
|
|
|
|
let ret = [];
|
|
while (x > 0) {
|
|
ret.push(x % b);
|
|
x = Math.floor(x / b);
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
// Interpret xs as an expansion in base b (ascending powers of b)
|
|
fromBase = (b, xs) => {
|
|
let plval = 1
|
|
return xs.reduce((a, x) => {
|
|
let new_a = x * plval;
|
|
plval *= b;
|
|
return a + new_a;
|
|
}, 0)
|
|
}
|
|
|
|
// Map an expansion to a complex number (as a 2-array).
|
|
// The expansion is interpreted in base `b`.
|
|
// `n` terms of the series are used, with geometric ratio `c`
|
|
adicAngles = (expansion, b, n, c) => {
|
|
return d3.range(n).reduce((a, i) => {
|
|
let angle = fromBase(b, expansion.slice(null, i + 1)) / (b ** (i + 1));
|
|
let x = c**i * Math.cos(2 * Math.PI * angle);
|
|
let y = c**i * Math.sin(2 * Math.PI * angle);
|
|
return [a[0] + x, a[1] + y];
|
|
}, [0, 0]);
|
|
}
|
|
|
|
pointCount = 1024;
|
|
expansions = d3.range(pointCount).map((x) => toBase(base, x));
|
|
|
|
embedding = expansions.map(
|
|
(x) => adicAngles(x, embedBase, 15, geometric)
|
|
);
|
|
|
|
viewof base = Inputs.range([2, 10], {
|
|
value: 2,
|
|
step: 1,
|
|
label: "Base of expansions (b)",
|
|
});
|
|
|
|
viewof embedBase = Inputs.range([1.1, 10], {
|
|
value: 2,
|
|
step: 0.1,
|
|
label: "Embedding base (p)",
|
|
});
|
|
|
|
viewof geometric = Inputs.range([0.005, 0.995], {
|
|
value: 0.9,
|
|
step: 0.005,
|
|
label: "Geometric ratio (c)",
|
|
});
|
|
|
|
plot = Plot.plot({
|
|
grid: true,
|
|
inset: 10,
|
|
// aspectRatio: 1,
|
|
width: 640,
|
|
height: 480,
|
|
marks: [
|
|
Plot.dot(embedding, { r: 1 })
|
|
]
|
|
});
|