Public
Edited
Sep 3, 2023
Paused
Importers
2 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function genPolygonCP(options = {}) {
const {
randomSeed = 1,
n = 20,
rigidity = 2,
minRigidity = 0,
rigidityPeriods = 0,
variability = 100,
iterations = 100,
tieDensity = 0.5,
tieStrength = 0.5
} = options;
const rand = d3.randomLcg(randomSeed);
const factor = variability / 100;
const lengths = d3.range(n).map(() => 1 + rand() * factor);
const totalLength = lengths.reduce((a, b) => a + b, 0);
const avgLength = totalLength / n;
const R = totalLength / (Math.PI * 2);
let angle = 0;
const nodes = [];
const links = [];
let prevLen = lengths[n - 1];
for (let i = 0; i < n; i++) {
const len = lengths[i];
const x = R + R * Math.cos(angle);
const y = R + R * Math.sin(angle);
const r = len / 2;
angle += ((len + prevLen) / 2 / totalLength) * (2 * Math.PI);
nodes.push({ x, y, r });
prevLen = len;
}
const rigidityVariation = cyclicVariation(
n,
minRigidity,
rigidity,
rigidityPeriods,
rand()
);
for (let i = 0; i < n; i++) {
const r = rigidityVariation[i];
for (let k = 1; k <= 1 + r; k++) {
let prev = (i + n - k) % n;
let distance = Math.hypot(
nodes[i].x - nodes[prev].x,
nodes[i].y - nodes[prev].y
);
links.push({ source: prev, target: i, distance });
}
}
const m = Math.trunc(n * tieDensity);
let pools = [d3.range(n)];
for (let i = 0; i < m; i++) {
pools.sort((a, b) => a.length - b.length);
let pool = pools.pop();
let a = Math.trunc(rand() * (pool.length - 1));
let b = Math.trunc(rand() * (pool.length - 1));
if (a > b) [a, b] = [b, a];
const source = pool[a];
const target = pool[b];
if (Math.abs(source - target) <= 2) {
pools.push(pool);
continue;
}
pools.push(pool.slice(a, b));
pools.push(pool.slice(b).concat(pool.slice(0, a)));
let d = Math.hypot(
nodes[source].x - nodes[target].x,
nodes[source].y - nodes[target].y
);
links.push({
source,
target,
distance: d * (1 - tieStrength) + tieStrength * avgLength
});
}
const sim = d3
.forceSimulation(nodes)
.alphaMin(0.1)
.force(
"link",
d3.forceLink(links).distance((link) => link.distance)
)
.force("center", d3.forceCenter().x(R).y(R) /*.strength(1)*/)
.force("many", d3.forceManyBody().strength(0.2 / R))
.force(
"collide",
d3.forceCollide().radius((node) => node.r)
)
.stop();
if (!iterations) {
return { sim, nodes, links, R };
} else {
sim.tick(iterations);
return nodes.map(({ x, y }) => [x / R / 2, y / R / 2]);
}
}
Insert cell
mutable randomSeed = 0
Insert cell
{
randomize;
mutable randomSeed += 1;
}
Insert cell
function selfIntersecting(polygon) {
const n = polygon.length;
for (let i = 0; i < n; i++) {
let a = polygon[i];
let b = polygon[i + 1];
for (let j = i + 2; j < n - 1; j++) {
let c = polygon[j];
let d = polygon[(j + 1) % n];
if (vec2.segmentsIntersect(a, b, c, d)) {
const p = vec2.lineIntersection([], a, b, c, d);
return true;
}
}
}
return false;
}
Insert cell
function cyclicVariation(n, min, max, periods = 1, phase = 0) {
const dv = max - min;
const da = (Math.PI * 2 * periods) / n;
const out = [];
phase *= 2 * Math.PI;
for (let i = 0; i < n; i++) {
const a = phase + i * da;
out.push(((Math.cos(a) + 1) * dv) / 2 + min);
}
return out;
}
Insert cell
data = cyclicVariation(100, 0, 2, 0.001, 0).map((x, i) => ({
i,
x: Math.round(x)
}))
Insert cell
data
X
i
Y
x
Color
Size
Facet X
Facet Y
Mark
Auto
Type Chart, then Shift-Enter. Ctrl-space for more options.

Insert cell
import { vec2 } from "@esperanc/vec2-utils"
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