繁体   English   中英

D3强制布局,其中较大的节点聚集在中心

[英]D3 Force Layout where larger nodes cluster in center

我一直在修改将用于标签云的力布局,并且每个标签由<circle>表示,其半径与该标签的问题成比例。 我的问题是确定如何使更受欢迎的标签趋向集群的中心,而使不那么受欢迎的标签聚集在标签云的边缘周围。 到目前为止,我的代码如下所示:

function(err, results) {
  var nodes = results.tags;
  var width = 1020;
  var height = 800;
  var extent = d3.extent(nodes, function(tag) { return tag.questions.length; });
  var min = extent[0] || 1;
  var max = extent[1];
  var padding = 2;

  var radius = d3.scale.linear()
    .clamp(true)
    .domain(extent)
    .range([15, max * 5 / min]);

  // attempted to make gravity proportional?
  var gravity = d3.scale.pow().exponent(5)
    .domain(extent)
    .range([0, 200]);

  var minRadius = radius.range()[0];
  var maxRadius = radius.range()[1];

  var svg = d3.select('#question_force_layout').append('svg')
    .attr('width', width)
    .attr('height', height);

  var node = svg.selectAll('circle')
      .data(nodes)
    .enter().append('circle')
      .attr('class', 'tag')
      .attr('r', function(tag) { return (tag.radius = radius(tag.questions.length)); });

  var tagForce = d3.layout.force()
    .nodes(results.tags)
    .size([width, height])
    .charge(200) // seemed like an okay effect
    .gravity(function(tag) {
      return gravity(tag.questions.length);
    })
    .on('tick', tagTick)
    .start();

  function tagTick(e) {
    node
      .each(collide(.5))
      .attr('cx', function(d) { return d.x; })
      .attr('cy', function(d) { return d.y; });
  }

  function collide(alpha) {
    var quadtree = d3.geom.quadtree(nodes);

    return function(d) {
      var r = d.radius + maxRadius + padding;
      var nx1 = d.x - r;
      var nx2 = d.x + r;
      var ny1 = d.y - r;
      var ny2 = d.y + r;

      quadtree.visit(function(quad, x1, y1, x2, y2) {
        if (quad.point && (quad.point !== d)) {
          var x = d.x - quad.point.x;
          var y = d.y - quad.point.y;
          var l = Math.sqrt(x * x + y * y);
          var r = d.radius + quad.point.radius + padding;

          if (l < r) {
            l = (l - r) / l * alpha;
            d.x -= x *= l;
            d.y -= y *= l;
            quad.point.x += x;
            quad.point.y += y;
          }
        }

        return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
      });
    };
  }
}

为了让您大致了解要处理的数据量,目前有52个问题和42个标签。 同样,输出通常以如下形式结束:

我希望较大的节点位于中心。

另一种可能性是给节点一些质量,并在碰撞函数中加以考虑。 然后,您可以打开重力,让它们为位置而战。
这个例子经过一阵忙之后就到达了。

这是修改后的碰撞函数...

function Collide(nodes, padding) {
// Resolve collisions between nodes.
  var maxRadius = d3.max(nodes, function(d) {return d.radius});
  return function collide(alpha) {
    var quadtree = d3.geom.quadtree(nodes);
    return function(d) {
      var r = d.radius + maxRadius + padding,
        nx1 = d.x - r,
        nx2 = d.x + r,
        ny1 = d.y - r,
        ny2 = d.y + r;
      quadtree.visit(function(quad, x1, y1, x2, y2) {
        var possible = !(x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1);
        if (quad.point && (quad.point !== d) && possible) {
          var x = d.x - quad.point.x,
            y = d.y - quad.point.y,
            l = Math.sqrt(x * x + y * y),
            r = d.radius + quad.point.radius + padding,
            m = Math.pow(quad.point.radius, 3),
            mq = Math.pow(d.radius, 3),
            mT = m + mq;
          if (l < r) {
            //move the nodes away from each other along the radial (normal) vector
            //taking relative mass into consideration, the sign is already established
            //in calculating x and y and the nodes are modelled as spheres for calculating mass
            l = (r - l) / l * alpha;
            d.x += (x *= l) * m/mT;
            d.y += (y *= l) * m/mT;
            quad.point.x -= x * mq/mT;
            quad.point.y -= y * mq/mT;
          }
        }
        return !possible;
      });
    };
  }
}

具有自排序节点的强制定向图- 位置交换

在此处输入图片说明

特征

  • 加速退火
    退火计算每个滴答完成一次,但是直到alpha降到0.05以下,即仅每n个滴答更新一次viz(n当前为4)。 这大大缩短了达到平衡的时间(大约为2倍)。
  • 力动力学
    力动力学是α的函数,具有两个阶段。 初始阶段具有零电荷,低重力和低阻尼。 旨在最大程度地混合和分类。 第二阶段具有更高的重力,较大的负电荷和更高的阻尼,其目的是清理并稳定节点的外观。
  • 节点之间的碰撞
    基于示例,但进行了增强以根据大小对节点的径向位置进行排序,较大的节点更靠近中心。 每次碰撞都被用作校正相对位置的机会。 如果它们不在适当位置,则将交换碰撞节点的径向坐标(在极坐标中)。 因此,分选效率取决于碰撞中的良好混合。 为了最大化混合效果,所有节点都在图中心的同一点创建。 交换节点时,将保留它们的速度。 这也可以通过更改先前的点( p.pxp.py )来完成。 使用r 3假设节点是球体来计算质量,并根据相对“质量”计算反弹。

提取

function Collide(nodes, padding) {
    // Resolve collisions between nodes.
    var maxRadius = d3.max(nodes, function(d) {
        return d.q.radius
    });
    return function collide(alpha) {
        var quadtree = d3.geom.quadtree(nodes);
        return function(d) {
            var r   = d.radius + maxRadius + padding,
                nx1 = d.x - r,
                nx2 = d.x + r,
                ny1 = d.y - r,
                ny2 = d.y + r;
            quadtree.visit(function v(quad, x1, y1, x2, y2) {
                var possible = !(x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1);
                if(quad.point && (quad.point !== d) && possible) {
                    var x = d.x - quad.point.x,
                        y = d.y - quad.point.y,
                        l = Math.sqrt(x * x + y * y),
                        r = d.radius + quad.point.radius + padding;
                    if(l < r) {
                        for(; Math.abs(l) == 0;) {
                            x = Math.round(Math.random() * r);
                            y = Math.round(Math.random() * r);
                            l = Math.sqrt(x * x + y * y);
                        }
                        ;
                        //move the nodes away from each other along the radial (normal) vector
                        //taking relative size into consideration, the sign is already established
                        //in calculating x and y
                        l = (r - l) / l * alpha;

                        // if the nodes are in the wrong radial order for there size, swap radius ordinate
                        var rel = d.radius / quad.point.radius, bigger = (rel > 1),
                            rad = d.r / quad.point.r, farther = rad > 1;
                        if(bigger && farther || !bigger && !farther) {
                            var d_r = d.r;
                            d.r = quad.point.r;
                            quad.point.r = d_r;
                            d_r = d.pr;
                            d.pr = quad.point.pr;
                            quad.point.pr = d_r;
                        }
                        // move nodes apart but preserve their velocity
                        d.x += (x *= l);
                        d.y += (y *= l);
                        d.px += x;
                        d.py += y;
                        quad.point.x -= x;
                        quad.point.y -= y;
                        quad.point.px -= x;
                        quad.point.py -= y;
                    }
                }
                return !possible;
            });
        };
    }
}  

头寸交换加动量: 头寸交换+动量

这有点快,但看起来也更自然...

在此处输入图片说明

附加的功能

  • 碰撞排序事件
    交换节点时,将保留较大节点的速度,同时加快较小节点的速度。 因此,由于较小的节点从碰撞点抛出而提高了分类效率。 使用r 3假设节点是球体来计算质量,并根据相对“质量”计算反弹。

     function Collide(nodes, padding) { // Resolve collisions between nodes. var maxRadius = d3.max(nodes, function(d) { return d.radius }); return function collide(alpha) { var quadtree = d3.geom.quadtree(nodes), hit = false; return function c(d) { var r = d.radius + maxRadius + padding, nx1 = dx - r, nx2 = dx + r, ny1 = dy - r, ny2 = dy + r; quadtree.visit(function v(quad, x1, y1, x2, y2) { var possible = !(x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1); if(quad.point && (quad.point !== d) && possible) { var x = dx - quad.point.x, y = dy - quad.point.y, l = (Math.sqrt(x * x + y * y)), r = (d.radius + quad.point.radius + padding), mq = Math.pow(quad.point.radius, 3), m = Math.pow(d.radius, 3); if(hit = (l < r)) { for(; Math.abs(l) == 0;) { x = Math.round(Math.random() * r); y = Math.round(Math.random() * r); l = Math.sqrt(x * x + y * y); } //move the nodes away from each other along the radial (normal) vector //taking relative size into consideration, the sign is already established //in calculating x and y l = (r - l) / l * (1 + alpha); // if the nodes are in the wrong radial order for there size, swap radius ordinate var rel = m / mq, bigger = rel > 1, rad = dr / quad.point.r, farther = rad > 1; if(bigger && farther || !bigger && !farther) { var d_r = dr; dr = quad.point.r; quad.point.r = d_r; d_r = d.pr; d.pr = quad.point.pr; quad.point.pr = d_r; } // move nodes apart but preserve the velocity of the biggest one // and accelerate the smaller one dx += (x *= l); dy += (y *= l); d.px += x * bigger || -alpha; d.py += y * bigger || -alpha; quad.point.x -= x; quad.point.y -= y; quad.point.px -= x * !bigger || -alpha; quad.point.py -= y * !bigger || -alpha; } } return !possible; }); }; } } 

这是我为其添加的内容:

var x = width / 2;
var y = height / 2;

var ring = d3.scale.linear()
  .clamp(true)
  .domain([35, 80]) // range of radius
  .range([Math.min(x, y) - 35, 0]);
// smallest radius attracted to edge (35 -> Math.min(x, y) - 35)
// largest radius attracted toward center (80 -> 0)

function tagTick(e) {
  node
    .each(gravity(.1 * e.alpha)) // added this line
    .each(collide(.5))
    .attr('cx', function(d) { return d.x; })
    .attr('cy', function(d) { return d.y; });
}

function gravity(alpha) {
  return function(d) {
    var angle = Math.atan2(y - d.y, x - d.x); // angle from center
    var rad = ring(d.radius); // radius of ring of attraction

    // closest point on ring of attraction
    var rx = x - Math.cos(angle) * rad;
    var ry = y - Math.sin(angle) * rad;

    // move towards point
    d.x += (rx - d.x) * alpha;
    d.y += (ry - d.y) * alpha;
  };
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM