Как динамически назначить центр d3.forceRadial с помощью функций? - PullRequest
0 голосов
/ 04 января 2019

У меня есть набор данных, который имеет родительские и дочерние узлы.Родительские узлы могут связываться с другими родительскими узлами, а дочерние узлы могут связываться с их родительскими узлами.Что я хочу сделать, это поместить дочерние узлы в радиальном направлении вокруг родительских узлов.Узлы выглядят примерно так

parent: [{
    id: 1,
    type: 'parent',
    x: ,
    y: ,
    vx: ,
    vy: ,
}, {
    id: 2
    x: ,
    y: ,
    vx: ,
    vy: ,
}]

child: [{
    id: 1,
    type: 'child',
    parent_node: {
        id: 1,
        x: ,
        y: ,
        vx: ,
        vy: ,
    },
    x: ,
    y: ,
    vx: ,
    vy: ,
}]

Итак, у меня есть детали родительского узла, такие как его x и y внутри дочерних узлов.

Я пытался назначить x и y (как в центре) динамически, но не смог найти способ сделать это:

force('radial', d3.forceRadial()
    .radius(node => {
        if (node.type === 'child') {
            return 10
        }
        return 0
    })
    .x(node => {
        if (node.type === 'child') {
            return node.parent_node.x
        }
        return 0
    })
    .y(node => {
        if (node.type === 'child') {
            return node.parent_node.y
        }
        return 0
    })
)

Можно ли что-то подобное сделать?Так что центр для каждого дочернего узла может быть предоставлен динамически, в зависимости от положения их родителя?

Ответы [ 2 ]

0 голосов
/ 04 января 2019

Некоторое время назад я создал версию d3.forceRadial, которая принимает функцию для установки позиций x и y.Вы можете увидеть запрос здесь , а код здесь

Этот запрос на получение еще не был принят, так как он довольно старый и не имеет комментариевот Майка Бостока (создатель D3), я думаю, этого никогда не будет.Итак, если вы хотите, вы можете использовать этот пользовательский d3.forceRadial, скопировав функцию по ссылке: https://pastebin.com/75j8vj3C

Затем просто используйте ваши функции позиционирования с настраиваемой силой, например:

simulation.force("radial", customRadial(radius, foo, bar))
//positioning functions--------------------------^----^

Или:

simulation.force("radial", customRadial()
    .radius(radius)
    .x(foo)
    .y(bar))

Где foo и bar - ваши функции для позиций x и y.

Вот пример:

var svg = d3.select("body")
  .append("svg")
  .attr("width", 600)
  .attr("height", 200);

var data = d3.range(500).map(function(d) {
  return {
    node: "foo" + d,
    centerX: 100 + ~~((d / 100) % 20) * 100
  }
});

var simulation = d3.forceSimulation(data)
  .force("radius", customRadial().radius(40)
    .x(function(d) {
      return d.centerX
    })
    .y(100)
    .strength(1))
  .force("collide", d3.forceCollide().radius(3).strength(.8));

var nodes = svg.selectAll(null)
  .data(data)
  .enter()
  .append("circle")
  .attr("r", 2.5)
  .style("fill", "teal")

simulation.on("tick", tick);

function tick() {
  nodes.attr("cx", function(d) {
      return d.x
    })
    .attr("cy", function(d) {
      return d.y
    });
}


function customRadial(radius, x, y) {

  var constant = function(x) {
    return function() {
      return x;
    };
  };

  var nodes,
    strength = constant(0.1),
    strengths,
    radiuses,
    xs,
    ys;

  if (typeof radius !== "function") radius = constant(+radius);
  if (typeof x !== "function") x = constant(x == null ? 0 : +x);
  if (typeof y !== "function") y = constant(y == null ? 0 : +y);

  function force(alpha) {
    for (var i = 0, n = nodes.length; i < n; ++i) {
      var node = nodes[i],
        dx = node.x - xs[i] || 1e-6,
        dy = node.y - ys[i] || 1e-6,
        r = Math.sqrt(dx * dx + dy * dy),
        k = (radiuses[i] - r) * strengths[i] * alpha / r;
      node.vx += dx * k;
      node.vy += dy * k;
    }
  }

  function initialize() {
    if (!nodes) return;
    var i, n = nodes.length;
    strengths = new Array(n);
    radiuses = new Array(n);
    xs = new Array(n);
    ys = new Array(n);
    for (i = 0; i < n; ++i) {
      radiuses[i] = +radius(nodes[i], i, nodes);
      xs[i] = +x(nodes[i], i, nodes);
      ys[i] = +y(nodes[i], i, nodes);
      strengths[i] = isNaN(radiuses[i]) ? 0 : +strength(nodes[i], i, nodes);
    }
  }

  force.initialize = function(_) {
    nodes = _, initialize();
  };

  force.strength = function(_) {
    return arguments.length ? (strength = typeof _ === "function" ? _ : constant(+_), initialize(), force) : strength;
  };

  force.radius = function(_) {
    return arguments.length ? (radius = typeof _ === "function" ? _ : constant(+_), initialize(), force) : radius;
  };

  force.x = function(_) {
    return arguments.length ? (x = typeof _ === "function" ? _ : constant(+_), initialize(), force) : x;
  };

  force.y = function(_) {
    return arguments.length ? (y = typeof _ === "function" ? _ : constant(+_), initialize(), force) : y;
  };

  return force;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

Если вы не хотите (или не можете по какой-либо причине) использовать этот пользовательский код, простой обходной путь создает несколькосимуляции , по одному для каждой группы элементов, как я делал в этой визуализации данных .

0 голосов
/ 04 января 2019

потому что d3.forceRadial().x().y() не принимает функции.

в соответствии с исходным кодом (d3v5) они принимают только цифры.

...