简体   繁体   English

D3:对词云使用强制布局

[英]D3: Using force layout for word clouds

I'm working on a tag visualization where tags transition between different force-directed layouts. 我正在进行标签可视化,其中标签在不同的力导向布局之间转换。

I had few issues figuring out how to transition from a bubble chart to a node chart, but I'm a bit stuck as to how to get the charts to transition into a word cloud. 我几乎没有考虑如何从气泡图转换到节点图表,但我对如何将图表转换为文字云有点困惑。 My difficulties largely stem from my inexperience at writing custom clustering/collision detection functions. 我的困难很大程度上源于我在编写自定义聚类/碰撞检测功能方面缺乏经验。

I declare the forces as globals and then stop and start them when the user clicks a button: 我将force声明为全局变量,然后在用户单击按钮时停止并启动它们:

var force1 = d3.layout.force()
    .size([width, height])
    .charge(0)
    .gravity(0.02)
    .on("tick", ticka);

//layout for node chart
var force2 = d3.layout.force()
    .size([width, height])
    .charge(-50)
    .gravity(0.005)
    .linkDistance(120)
    .on("tick", tickb);

//layout for bubble chart
var force3 = d3.layout.force()
    .size([width, height])
    .charge(0)
    .gravity(0.02)
    .on("tick", tickc);

Relevant node/link functions are added to the force when the function that draws the nodes is called (as data changes according to a slider value). 当调用绘制节点的函数时(因为数据根据滑块值而变化),相关节点/链接函数被添加到强制中。

The code for creating node data is as follows: 创建节点数据的代码如下:

nodes = splicedCounts.map(function(d, e) {
    var choice;
    var i = 0,
    r = d[1],
    d = { count: d[1],
          sentiment: d[2]/d[1],
          cluster: i,
          radius: radScale(r),
          name: d[0],
          index: e,
          x: Math.cos(i / m * 2 * Math.PI) * 200 + width / 2 + Math.random(),
          y: Math.sin(i / m * 2 * Math.PI) * 200 + height / 2 + Math.random()
    };
    if (!clusters[i] || (r > clusters[i].radius))
        clusters[i] = d;
    return d;
});

In order to keep this question relatively brief, the code I use for drawing the bubble chart is derivative of this example: http://bl.ocks.org/mbostock/7881887 and the code for drawing the node chart are similarly generic (I am happy to provide this code if it would help to solve my issue). 为了使这个问题保持相对简短,我用于绘制气泡图的代码是这个例子的衍生物: http//bl.ocks.org/mbostock/7881887 ,绘制节点图的代码同样通用(I我很乐意提供此代码,如果它有助于解决我的问题)。

This is where my issue comes in: 这是我的问题所在:

I found this nice example for collision detection between rectangles and incorporated it into my code. 我找到了这个矩形之间碰撞检测的好例子 ,并将其合并到我的代码中。 However, since I'm using SVG text and the font size changes on transition, I opted to estimate the text size/bounding box size based on text-length and radius. 但是,由于我在转换时使用SVG文本并且字体大小发生了变化,因此我选择根据文本长度和半径估计文本大小/边界框大小。

The entire "tick" functions for the word chart are below. 字图的整个“滴答”功能如下。

function tickc(e) {
    node = nodeGroup.selectAll(".node");
    var nodeText = nodeGroup.selectAll(".node text");
    node.each(cluster(5 * e.alpha * e.alpha));
    var k = e.alpha;
    nodeText.each(function(a, i) {
        var compWidth = d3.select(this).attr("bWidth");
        var compHeight = d3.select(this).attr("bHeight");
        nodes.slice(i + 1).forEach(function(b) {
          // console.log(a);
          var lineWidthA = a["name"].length * a["radius"]/2.5;
          var lineHeightA = a["radius"]/0.9;

          var lineWidthB = b["name"].length * b["radius"]/2.5;
          var lineHeightB = b["radius"]/0.9;
          dx =  (a.x - b.x)
          dy =  (a.y - b.y)    
          adx = Math.abs(dx)
          ady = Math.abs(dy)
          mdx = (1 + 0.07) * (lineWidthA + lineWidthB)/2
          mdy = (1 + 0.07) * (lineHeightA + lineHeightB)/2
          if (adx < mdx  &&  ady < mdy) {          
            l = Math.sqrt(dx * dx + dy * dy)

            lx = (adx - mdx) / l * k
            ly = (ady - mdy) / l * k

            // choose the direction with less overlap
            if (lx > ly  &&  ly > 0)
                 lx = 0;
            else if (ly > lx  &&  lx > 0)
                 ly = 0;

            dx *= lx
            dy *= ly
            a.x -= dx
            a.y -= dy
            b.x += dx
            b.y += dy
          }
        });
  });
node.select("circle")
    .attr("cx", function(d) { return d.x; })
    .attr("cy", function(d) { return d.y; });
node.select("text")
    .attr("x", function(d) { return d.x; })
    .attr("y", function(d) { return d.y; });
}
// Move d to be adjacent to the cluster node.
function cluster2(alpha) {
  return function(d) {
    var cluster = clusters[d.cluster];
    if (cluster === d) return;
    var x = d.x - cluster.x,
    y = d.y - cluster.y,
    l = Math.sqrt(x * x + y * y),
    r = (d["name"].length * d["radius"]) + (cluster["name"].length * cluster["radius"]);

  };
}

I was unsure of how to conclude the clustering function so as to move the nodes appropriately. 我不确定如何结束聚类功能以便适当地移动节点。 I tried to adapt the standard cluster function, ie 我试图调整标准的集群功能,即

// Move d to be adjacent to the cluster node.
function cluster(alpha) {
  return function(d) {
    var cluster = clusters[d.cluster];
    if (cluster === d) return;
    var x = d.x - cluster.x,
        y = d.y - cluster.y,
        l = Math.sqrt(x * x + y * y),
        r = d.radius + cluster.radius;
    if (l != r) {
      l = (l - r) / l * alpha;
      d.x -= x *= l;
      d.y -= y *= l;
      cluster.x += x;
      cluster.y += y;
    }
  };
} 

to be more similar to the aforementioned rectangular cluster force layout but without luck (I'm afraid I no longer have copies of my exact attempts). 更像是上面提到的矩形集群力布局,但没有运气(我担心我不再拥有我的确切尝试的副本)。

I'm afraid I can't attach images due to my lack of reputation but I can try to find a way to provide them if it would help. 由于我缺乏声誉,我担心我无法附加图像,但我可以尝试找到一种方法来提供它们,如果它会有所帮助。 The overlap problem with the word cloud is minor (most words resolve into adjacent but not touching positions) but, if possible, I'd like it to resolve as perfectly as the bubble chart. 单词云的重叠问题很小(大多数单词会分解为相邻但不会触及的位置)但是,如果可能的话,我希望它能像气泡图一样完美地解析。 I'm pretty sure that these issues arose from a.) the unfinished cluster function and b.) my hack at using text length and radius to estimate text size rather than proper bounding box coords, but I'm not sure exactly how to fix these things. 我很确定这些问题来自于。)未完成的集群函数和b。)我使用文本长度和半径来估计文本大小而不是正确的边界框坐标,但我不确定如何修复这些东西。

I'd recommend using the d3-cloud package which should do a lot of what you need. 我建议使用d3-cloud软件包,它应该d3-cloud您的需求。 If not, then at least it's a good starting point https://github.com/jasondavies/d3-cloud 如果没有,那么至少它是一个很好的起点https://github.com/jasondavies/d3-cloud

The way it seems to work is by calculating a bounds for each word and then resolving collisions between those bounds. 它似乎工作的方式是计算每个单词的边界,然后解决这些边界之间的冲突。 You can see that here 你可以看到, 在这里

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

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