Возможно, я чего-то здесь упускаю, но работа с силой силы позиционирования x (и y) может помочь обеспечить правильное выполнение заказа.Сила forceX или forceY по умолчанию равна 0,1, сила реализована следующим образом:
значение 0,1 указывает, что узел должен переместиться на десятую часть пути от своего текущего положения x к целиХ-позиция с каждым приложением.При более высоких значениях узлы быстрее перемещаются в целевую позицию, часто за счет других сил или ограничений.Значение вне диапазона [0,1] не рекомендуется.( docs )
Таким образом, мы можем увеличить силу forceX, а для более свободного перемещения узлов по оси x мы можем уменьшить силу Y - позволяя узлам перепрыгивать друг на другас большей легкостью - уменьшение силы столкновения тоже может помочь.
Я не помечаю кружок ниже (вместо этого они последовательно затенены), но я запускаю проверку, чтобы убедиться, что они в порядке (регистрируется в консоли в конце симуляции), приведенный ниже фрагмент изменяет толькосилы x и y (не сила столкновения):
var height = 300;
var width = 500;
var data = d3.range(30).map(function(d,i) {
return { size: Math.random()+1, index: i}
});
var svg = d3.select("body")
.append("svg")
.attr("width",width)
.attr("height",height);
var x = d3.scaleLinear()
.domain([1,30])
.range([50,width-50]);
var color = d3.scaleLinear()
.domain([0,29])
.range(["#ccc","#000"])
var simulation = d3.forceSimulation()
.force("x", d3.forceX(d => x(d.index)).strength(0.20))
.force("y", d3.forceY(height / 2).strength(0.05))
.force("collide", d3.forceCollide().radius(d=> d.size*10))
.alpha(1).restart();
var circles = svg.selectAll(null)
.data(data)
.enter()
.append("circle")
.attr("r", function(d) { return d.size * 10; })
.attr("fill", function(d,i) { return color(i); })
simulation.nodes(data)
.on("tick",tick)
.on("end",verify);
function tick() {
circles
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
}
function verify() {
var n = 0;
for(var i = 0; i < data.length - 1; i++) {
if(data[i].x > data[i+1].x) n++;
}
console.log(n + " out of place");
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
Фрагмент размещает 30 кругов в области 500x300 без особых проблем: я тестировал несколько раз с 0 без места.Размещение 100 кружков здесь вызовет проблемы: кружки не смогут поменяться местами в такой тесной области: может потребоваться дальнейшая модификация сил, но предпочтительным может быть график большего размера (в отличие от крошечного фрагмента)тоже.
Другой вариант - модифицировать силы на протяжении всего периода симуляции: начните с сильной силы х и низкой силы столкновения, затем медленно набирайте столкновение, чтобы последующее столкновение было сведено к минимуму.Вот пример изменения сил в функции тика - хотя, этот пример для длины ссылки, а не для размещения на x - но адаптация не должна быть слишком сложной.
Еще одна возможность состояла бы в том, чтобы поддерживать высокий уровень альфа-канала до тех пор, пока все круги не будут должным образом упорядочены по оси x, а затем не начать охлаждаться, опять же, это должно происходить в функции тика.