keyframes = {
const keyframes = [];
let ka, a, kb, b;
const frameColors = new Map(Array.from(names).map(
name => [name, name == selectedCountry ? colors.selected : colors.neutral])
)
for ([[ka, a], [kb, b]] of d3.pairs(datevalues)) {
let targets = Array.from(names, (name) => {
const d1 = a.get(name);
const d2 = b.get(name);
return {
name: name, current: d1.rank, from: d1.rank, initial: d1.rank, target: d2.rank, done: false };
});
const currentMap = new Map(targets.map((d) => [d.current, d]));
const targetsMap = new Map(targets.map((d) => [d.name, d]));
let targetSelected = targetsMap.get(selectedCountry);
targets = targets.filter(
(d) => d.current <= totalBoxes || d.target <= totalBoxes
);
targets.forEach((d) => {
d.current = Math.min(d.current, totalBoxes + 1);
d.target = Math.min(d.target, totalBoxes + 1);
d.diffSelected = d.current - targetSelected.current;
});
targets.sort((c1, c2) => {
const val1 = Math.min(c1.current, c1.target);
const val2 = Math.min(c2.current, c2.target);
if (val1 < val2) return -1;
else if (val2 < val1) return 1;
else {
var diff1 = Math.abs(c1.current - c1.target);
var diff2 = Math.abs(c2.current - c2.target);
if (diff1 == diff2) return c1.current - c2.current;
else return diff1 - diff2;
}
});
const toMove = new Set()
toMove.add(targets[0].name)
let moveAtOnce = 3;
while (targets.length > 0) {
const frameData = _.cloneDeep(Array.from(a.values()));
frameData.sort((d1,d2) => {
if (d1.rank == d2.rank) return d1.cum_value - d2.cumValue;
else return d1.rank - d2.rank;
})
frameData.forEach(d => {
const moveData = targetsMap.get(d.name);
if (toMove.has(d.name)) {
d.rank = moveData.current;
if (moveData.target == moveData.current) {
moveData.done = true;
toMove.delete(d.name);
}
else {
const from = moveData.from
if (toMove.size < moveAtOnce) {
const movingTo = currentMap.get(moveData.target);
toMove.add(movingTo.name);
}
let speed = Math.max(2, Math.floor(Math.abs(moveData.current - moveData.target) / 2));
if (targetSelected.done && moveData.current > targetSelected.curret) {
speed += 2;
moveAtOnce = 5;
}
if (Math.abs(moveData.diffSelected) <= 5) {
speed = 1;
}
if (moveData.target < moveData.current) {
moveData.current = Math.max(moveData.target, moveData.current - speed);
}
else {
moveData.current = Math.min(moveData.target, moveData.current + speed);
}
}
}
else if (targetsMap.has(d.name)) {
if (moveData.done) {
d.rank = moveData.target;
}
else {
d.rank = moveData.current;
}
}
d.i = (d.rank-1) % boxesPerRow;
d.j = Math.floor((d.rank-1) / boxesPerRow);
if (d.j % 2 == 1) {
d.i = boxesPerRow - d.i - 1
}
d.x = x(d.i);
// d.x += (d.j % 2 == 0) ? -7 : 7;
d.y = d.j < rowsCount ? y(d.j) : y.range()[1] + 50;
})
const datesDiff = kb - ka;
frameData.forEach(d => {
const countryTarget = targetsMap.get(d.name);
const diffBefore = countryTarget.diffSelected;
const diffAfter = countryTarget.current - targetSelected.current;
if (diffBefore <= 0 && diffAfter > 0) {
if (frameColors.get(d.name) == colors.neutral)
frameColors.set(d.name, colors.passedBy)
else
frameColors.set(d.name, colors.neutral)
}
if (diffBefore >= 0 && diffAfter < 0) {
if (frameColors.get(d.name) == colors.neutral)
frameColors.set(d.name, colors.passes)
else
frameColors.set(d.name, colors.neutral)
}
countryTarget.diffSelected = diffAfter;
d.color = frameColors.get(d.name);
if (countryTarget.done && !d.line) {
const line = {
x1: x_lines(countryTarget.initial),
y1: y_lines(new Date(ka)),
x2: x_lines(countryTarget.current),
y2: y_lines(new Date(kb)),
};
d.line = line;
}
countryTarget.from = countryTarget.current;
})
keyframes.push([new Date(ka), frameData.filter(d => d.rank <= totalBoxes)]);
targets = targets.filter(d => !d.done);
if (toMove.size < moveAtOnce && toMove.size != targets.length) {
const notMoving = targets.filter(d => !toMove.has(d.name))
if (notMoving.length > 0) {
toMove.add(notMoving[0].name);
}
}
// break;
}
}
return keyframes;
}