如何在D3中制作覆盖不同大小节点的凸包

时间:2015-11-14 12:20:57

标签: d3.js social-networking convex-hull

我遇到了D3的凸包实现的另一个限制:它没有调整到节点的大小。嗯,我没想到会自动这样做,但这似乎是人们使用D3.js的数据可视化的一个相当标准的方面,所以我期望找到它的修复。我还没有找到这样的解决方案。

一个明显且不令人满意的解决方案是找到该组中最大节点的大小,并将其设置为船体的行程宽度。不幸的是,这看起来很糟糕,因此节点大小变化很大。

我已经尝试将插入假点的想法扩展到有1个或2个成员的群体的帐户。在这种情况下,我为每个节点添加了四个点,这些点位于节点的N,S,E和W边界。

var groupPath = function(d) {
var fakePoints = [];  // This adjusts convex hulls for groups with fewer than 3 nodes by adding virtual nodes.
if (d.length == 1 || d.length == 2) {
   fakePoints = [ [d[0].x + 0.001, d[0].y - 0.001],
                  [d[0].x - 0.001, d[0].y + 0.001],
                  [d[0].x - 0.001, d[0].y + 0.001]]; }     
   return "M" + 
   d3.geom.hull(d.map(function(i) { return [
        [(i.x), (i.y + (2 + (4 * i.propertyValue)))],
        [(i.x + (2 + (4 * i.propertyValue))), (i.y)],
        [(i.x), (i.y - (2 + (4 * i.propertyValue)))],
        [(i.x - (2 + (4 * i.propertyValue))), (i.y) ]]; })
      .concat(fakePoints))  //do not forget to append the fakePoints to the input data
      .join("L") 
      + "Z";
};

...其中(2 + (4 * i.propertyValue))是节点的半径。然后我将笔划宽度设置为从节点边缘到凸包外边缘的距离......填充。这似乎是一个好主意,但它完全没有用。结果非常令人费解,因为它甚至没有创建那些点的凸包......并且顺序很重要。你可以在this JSFiddle中看到它实际上做了什么。

我首先认为假点导致问题,也许它们在某种程度上,但如果你删除它们,凸包对于1或2个节点的组不起作用......这很奇怪,因为我以为我已经在凸包的输入上增加了4个点。一个(可能)的可能性是我没有以正确的方式添加这四个点,但我不知道哪种方式与此不同,是正确的方法。

对于那些更了解D3如何创建凸包的人来说,也许有一个简单的解决方法。也就是说,也许有人可以看到我的方法出了什么问题并帮我修复它,或者也许有人已经有了更好的方法来做到这一点。

1 个答案:

答案 0 :(得分:0)

我之前尝试的问题是由d3.geom.hull函数中的四个虚拟点使用map函数引起的。这创建了嵌套的点数组,而不是简单的点列表。我找不到javascript的“flatten”函数,所以我使用d3.geom.hull将这些点放在forEach()函数之外,然后像这样输入它们:

var groupPath = function(d) {
    var fakePoints = [];  
    d.forEach(function(element) { fakePoints = fakePoints.concat([  // "0.7071" is the sine and cosine of 45 degree for corner points.
       [(element.x), (element.y + (2 + (4 * element.propertyValue)))],
       [(element.x + 0.7071 * (2 + (4 * element.propertyValue))), (element.y + 0.7071 * (2 + (4 * element.propertyValue)))],
       [(element.x + (2 + (4 * element.propertyValue))), (element.y)],
       [(element.x + 0.7071 * (2 + (4 * element.propertyValue))), (element.y - 0.7071 * (2 + (4 * element.propertyValue)))],
       [(element.x), (element.y - (2 + (4 * element.propertyValue)))],
       [(element.x - 0.7071 * (2 + (4 * element.propertyValue))), (element.y - 0.7071 * (2 + (4 * element.propertyValue)))],
       [(element.x - (2 + (4 * element.propertyValue))), (element.y)],
       [(element.x - 0.7071 * (2 + (4 * element.propertyValue))), (element.y + 0.7071 * (2 + (4 * element.propertyValue)))]
       ]); 
    })
    return "M" + d3.geom.hull( fakePoints ).join("L") + "Z";
};

正如您所看到的,这可以通过为每个节点添加8个点来实现,以使凸包从每个方向都是圆形的。这样做也消除了添加fakePoints以考虑具有1或2个成员的组的需要。虽然它看起来仍然不像正常的例子那样不适应节点大小,但如果有必要,可能会为你的情况改善美学。

This JSFiddle有一个包含多层组的工作示例,这样每个组的凸包都会缠绕并适应节点的大小。该可视化仍然需要在其他方面工作(例如重新绘制船体上的边缘并使链接长度适应组成员资格),但这些是其他问题。这个问题已经解决了,但我仍然愿意看到更好的答案或改进版本。