function simulate() {
const spacing = 6;
const debug = false;
const antigravity = 0.4;
function distBetween(a, b) {
return (
Math.sqrt(
Math.pow(b.pos.x - a.pos.x, 2) + Math.pow(b.pos.y - a.pos.y, 2)
) -
a.size / 2 -
b.size / 2 -
spacing
);
}
function calcPos(s, R) {
let rd = s.size * antigravity;
let y = Math.sin(s.p + 0.5 * Math.PI) * (R + rd);
let x = Math.cos(s.p + 0.5 * Math.PI) * (R + rd);
return { x, y };
}
function assertStillValid(state) {
for (let i = 0; i < 6; i++) {
let a = state[i];
let b = state[i + 1];
if (b.p < a.p) {
throw new Error(
`no longer valid at circle ${i}. State: ${JSON.stringify(
state.map((s) => s.p)
)}`
);
}
}
}
function totalDistBetween(state) {
let total = 0;
for (let i = 0; i < 7; i++) {
let a = state[i];
let b = state[(i + 1) % 7];
total += distBetween(a, b);
}
return total;
}
function thread({ state, R }) {
let totalSteps = 0;
for (let i = 0; i < 6; i++) {
debug && console.log(" thread", i);
let a = state[i];
let b = state[i + 1];
let d,
steps = 0;
while (
((d = distBetween(a, b)),
++totalSteps,
++steps < 1000 && Math.abs(d) > 0.1)
) {
for (let j = i + 1; j < 7; j++) {
let b = state[j];
b.p = Math.min(
2 * Math.PI - 0.05,
b.p + -Math.sign(d) * 0.0001 * Math.PI
);
b.pos = calcPos(b, R);
}
debug &&
console.log(" ", state.map((s) => s.p.toFixed(2)).join(", "));
debug && assertStillValid(state);
}
}
return { state, R, totalSteps };
}
let R = 150;
let steps = 0;
let state = emotions.map((emotion, i) => {
const size = diameter[values[emotions[i]]];
const color = colors[emotions[i]];
const p = (i / 7) * 2 * Math.PI;
let s = { i, emotion, size, color, p };
s.pos = calcPos(s, R);
return s;
});
debug && assertStillValid(state);
let totalDist,
iterations = 0;
while (
((totalDist = totalDistBetween(state)),
++iterations < 10_000 && Math.abs(totalDist) > 1)
) {
R += -Math.sign(totalDist) * 0.05;
state = state.map((s) => {
s.pos = calcPos(s, R);
return s;
});
debug && assertStillValid(state);
let r = thread({ state, R });
state = r.state;
steps += r.totalSteps;
debug && assertStillValid(state);
}
return { state, iterations, steps };
}