D3 V4: узлы в узле силового макета - PullRequest
0 голосов
/ 22 марта 2019

Я создал диаграмму размещения подключенных узлов, но хочу пойти дальше и вставить узлы (игроков) в каждый узел (команду) (аналогично макету пакета)

Хотя мне удалось извлечь позиции каждого узла (команды) после начала симуляции и центрировать узлы (игроков) в их соответствующих командах, я не могу создать разнесенные узлы (игроков) со столкновением / дрожанием внутри каждого узла (команды).

Я снова попытался запустить новую симуляцию, чтобы получить эффект столкновения, но он не работает.

Проделанная работа: https://bl.ocks.org/dianaow/722db505e9971b50b575ef0051ea0295

Соответствующий код:

  var team_stats1 = d3.nest()
    .key(function(d) { return d.club })
    .entries(data)

  enter()

  // Initialize force simulation
  var simulation = d3.forceSimulation()
    .force("link", d3.forceLink()
      .id(function(d) { return d.id; })
      .strength(function(d) {return d.strength})
    )
    .force("collide", d3.forceCollide().radius(function(d) { return d.size }))

  simulation
      .nodes(nodes)
      .on("tick", update)

  simulation.force("link")
      .links(links)

  // Initialize second force simulation just to get collision between nodes(players) within each node(team)
  var simulation1 = d3.forceSimulation()
    .force("charge", d3.forceManyBody().strength(-300))
    .force("collide", d3.forceCollide().radius(function(d) { return d.size }))

  function enter() {

    path = svg.selectAll('line')
      .data(links).enter().append('line')
      .attr('stroke-linecap', 'round')

    circle = svg.selectAll('circle')
      .data(nodes)
      .enter().append('circle')
      .attr('stroke-width', 2)

    circle.each(function (nodeElement) {
      if(countries.indexOf(nodeElement.id) < 0){
        var idx = nodeElement.id.replace(/[^A-Z0-9]+/ig, "_")
        var data_filtered = team_stats1.filter(d=>d.key==nodeElement.id)[0].values
        svg.selectAll('#' + idx + '.team_players')
          .data(data_filtered)
          .enter().append('circle')
            .attr("class", "team_players")
            .attr('id', idx)
            .attr("r", 3)
            .attr("fill", 'grey')
            .attr("stroke", 'none')
            .attr("stroke-width", 0)  
      }
    })
  }

  function update() {

    path.attr('stroke-width', function(d) {return d.size})
      .attr('stroke', function(d) {return d.stroke})
      .attr('fill', function(d) {return d.stroke})
      .attr('x1', function(d) {return d.source.x})
      .attr('y1', function(d) {return d.source.y})
      .attr('x2', function(d) {return d.target.x})
      .attr('y2', function(d) {return d.target.y})

    circle.attr('r', function(d) {return d.size})
      .attr('fill', function(d) {return d.fill || '#fff'})
      .attr('cx', function(d) {return d.x})
      .attr('cy', function(d) {return d.y})
      .attr('id', function(d) {return d.id.replace(/[^A-Z0-9]+/ig, "_")}) // remove spaces because they cannot be contained in class names

    circle.each(function (nodeElement) {
      if(countries.indexOf(nodeElement.id) < 0){ // filter out the country nodes around team nodes
        var idx = nodeElement.id.replace(/[^A-Z0-9]+/ig, "_")
        var data_filtered = team_stats1.filter(d=>d.key==nodeElement.id)[0].values
        data_filtered.forEach((d,i) => {
          d.x = nodeElement.x
          d.y = nodeElement.y
        })

        var player = svg.selectAll('#' + idx + '.team_players')
          .data(data_filtered)
          .attr('cx', function(d) {return d.x})
          .attr('cy', function(d) {return d.y}) 

        simulation1
          .nodes(data_filtered)
          .on("tick", updatePlayers)

        function updatePlayers() {
          player.attr('cx', function(d) {return d.x})
            .attr('cy', function(d) {return d.y}) 
        }
      }
    })

  }
...