D3.js Convex Hull有2个数据点

时间:2015-06-04 23:34:32

标签: javascript d3.js visualization convex-hull

Hull Geom的API声明:“假设顶点数组的长度大于3。如果顶点长度<= 3,则返回[]。” (https://github.com/mbostock/d3/wiki/Hull-Geom

我需要在2个节点周围绘制凸包。我正在使用力布局,因此凸包需要是动态的,因为如果我点击一个节点并将其拖动,它就会在节点周围移动。我的代码目前基于此示例:http://bl.ocks.org/donaldh/2920551

对于上下文,这就是我想要绘制凸包的内容:

当有3个节点时,它可以工作:

3 nodes

以下是我想要绘制凸包的内容(不适用于上面示例中的代码,因为Hull Geom只会采用3 +顶点的数组):

2 Nodes

我理解凸壳的传统使用永远不会只涉及两个点,但我尝试在2个节点周围绘制椭圆,矩形等,它看起来并不像3个节点那样好。

据我所知,Hull Geom最终会吐出一个用于路径的字符串,所以我可能会为2个节点编写一个Hull Geom的修改版本。

非常感谢有关如何为2个节点编写修改过的Hull Geom或任何一般建议以解决我的问题的任何建议。

2 个答案:

答案 0 :(得分:3)

基本上,您需要至少有一个非常接近线的假点才能达到预期效果。这可以在groupPath函数中实现。

对于长度为2的d,您可以创建一个临时数组并将其附加到map函数的结果中,如下所示:

var groupPath = function(d) {
    var fakePoints = [];
    if (d.values.length == 2)
    {
        //[dx, dy] is the direction vector of the line
        var dx = d.values[1].x - d.values[0].x;
        var dy = d.values[1].y - d.values[0].y;

        //scale it to something very small
        dx *= 0.00001; dy *= 0.00001;

        //orthogonal directions to a 2D vector [dx, dy] are [dy, -dx] and [-dy, dx]
        //take the midpoint [mx, my] of the line and translate it in both directions
        var mx = (d.values[0].x + d.values[1].x) * 0.5;
        var my = (d.values[0].y + d.values[1].y) * 0.5;
        fakePoints = [ [mx + dy, my - dx],
                [mx - dy, my + dx]];
        //the two additional points will be sufficient for the convex hull algorithm
    }

    //do not forget to append the fakePoints to the input data
    return "M" + 
        d3.geom.hull(d.values.map(function(i) { return [i.x, i.y]; })
        .concat(fakePoints))
    .join("L") 
    + "Z";
}

这里有一个fiddle,其中有一个工作示例。

答案 1 :(得分:1)

Isolin有一个很好的解决方案,但它可以简化。它不是将虚拟点放在中点的线上,而是将虚拟点基本上添加到现有点之上......以不易察觉的量进行偏移。我修改了Isolin的代码来处理具有1或2个节点的组的情况。

var groupPath = function(d) {
   var fakePoints = [];  
   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]; })
       .concat(fakePoints))  //do not forget to append the fakePoints to the group data
       .join("L") + "Z";
};