Радиус узла D3 зависит от количества ссылок: свойство веса

Я пытаюсь создать форсированный граф с помощью D3. На данный момент радиус узла зависит от пары ключ-значение в JSON (d.size).

Мне известно о свойстве d3.weight, которое можно использовать для подсчета количества ссылок и связи с атрибутом radius круга, но я почему-то не смог заставить его работать.

Пожалуйста, помогите мне с этим.

Найдите код ниже:

d3.json('graph.json', (error, graph) => {
  const width = 1200;
  const height = 900;

  const simulation = d3.forceSimulation()
    .nodes(graph.nodes)
    .force('link', d3.forceLink().id(d => d.id))
    .force('charge', d3.forceManyBody().strength([-605]))
    .force('center', d3.forceCenter(width / 2, height / 2))
    .on('tick', ticked);

  simulation.force('link')
    .links(graph.links)
    .distance([140]);

  const R = 30;

  const svg = d3.select('body').append('svg')
    .attr('width', width)
    .attr('height', height);

  // add defs-marker
  // add defs-markers
  svg.append('svg:defs').selectAll('marker')
    .data([{ id: 'end-arrow', opacity: 1 }, { id: 'end-arrow-fade', opacity: 0.1 }])
    .enter().append('marker')
      .attr('id', d => d.id)
      .attr('viewBox', '0 0 10 10')
      .attr('refX', 2 * R)
      .attr('refY', 5)
      .attr('markerWidth', 4)
      .attr('markerHeight', 4)
      .attr('orient', 'auto')
      .append('svg:path')
        .attr('d', 'M0,0 L0,10 L10,5 z')
        .style('opacity', d => d.opacity);

  let link = svg.selectAll('line')
    .data(graph.links)
    .enter().append('line');

  link  
    .attr('class', 'link')
    .attr('marker-end', 'url(#end-arrow)')
    .on('mouseout', fade(1));

  let node = svg.selectAll('.node')
    .data(graph.nodes)
    .enter().append('g')
    .attr('class', 'node');

  node.append('circle')
    .attr('r', function (d) {
                return (d.size * 12);
            })
    .on('mouseover', fade(0.1))
    .on('mouseout', fade(1))
    .call(d3.drag()
      .on("start", dragstarted)
      .on("drag", dragged)
      .on("end", dragended));

  node.append('text')
    .attr('x', 0)
    .attr('dy', '.35em')
    .text(d => d.name);

  function ticked() {
    link
      .attr('x1', d => d.source.x)
      .attr('y1', d => d.source.y)
      .attr('x2', d => d.target.x)
      .attr('y2', d => d.target.y);

    node
      .attr('transform', d => `translate(${d.x},${d.y})`);
  }


  function dragstarted(d) {
    if (!d3.event.active) simulation.alphaTarget(0.3).restart();
    d.fx = d.x;
    d.fy = d.y;
  }

  function dragged(d) {
    d.fx = d3.event.x;
    d.fy = d3.event.y;
  }

  function dragended(d) {
    if (!d3.event.active) simulation.alphaTarget(0);
    d.fx = null;
    d.fy = null;
  }

  const linkedByIndex = {};
  graph.links.forEach(d => {
    linkedByIndex[`${d.source.index},${d.target.index}`] = 1;
  });

  function isConnected(a, b) {
    return linkedByIndex[`${a.index},${b.index}`] || linkedByIndex[`${b.index},${a.index}`] || a.index === b.index;
  }

  function fade(opacity) {
    return d => {
      node.style('stroke-opacity', function (o) {
        const thisOpacity = isConnected(d, o) ? 1 : opacity;
        this.setAttribute('fill-opacity', thisOpacity);
        return thisOpacity;
      });

      link.style('stroke-opacity', o => (o.source === d || o.target === d ? 1 : opacity));
      link.attr('marker-end', o => (opacity === 1 || o.source === d || o.target === d ? 'url(#end-arrow)' : 'url(#end-arrow-fade)'));
    };
  }
})

Структура JSON выглядит следующим образом:

{
    "nodes": [
        {
            "name": "A",
            "id": 0,
            "size": 1
        },
        {
            "name": "D",
            "id": 1,
            "size": 2
        },
        {
            "name": "K",
            "id": 2,
            "size": 3
        }
    ],
    "links": [
        {
            "source": 0,     //id of the soure application
            "target": 1      //id of the destination application
        },
        {
            "source": 0,
            "target": 2
        },
        {
            "source": 3,
            "target": 4
        }
    ]
}

1 ответ

Решение

d3.v4 не поддерживает свойство веса. Поэтому я думаю, что вам придется самостоятельно рассчитывать вес узла. попробуй так.

node.append("circle")
   .attr("r", function(d) {      
     d.weight = link.filter(function(l) {
       return l.source.index == d.index || l.target.index == d.index
     }).size();      
     var minRadius = 10;
     return minRadius + (d.weight * 2);
   });

В d3.v3 мы имеем свойство веса и можем использовать его, как показано ниже.

node.append("circle")
  .attr("r", function(d) {
    var minRadius = 10;
    return minRadius + (d.weight * 2);
  });

Пример скрипки - https://jsfiddle.net/gilsha/9d6edrte/

Другие вопросы по тегам