Javascript D3围绕一组圆绘制外壳

Javascript D3围绕一组圆绘制外壳,javascript,d3.js,force-layout,convex-hull,Javascript,D3.js,Force Layout,Convex Hull,我想用d3绘制一个分组的力定向图 我已经用圆圈画出了这张图。但是我现在想用一条路径(外壳线)连接圆的交点。如果不连接交点,则围绕这组圆绘制外壳线就足够了。我试过这个例子。但是我有文本和圆圈,包括文本和链接 var vertices = new Array(); var width = 960, height = 500; var color = d3.scale.category10(); var r = 6; var force = d3.layout.force().size([wi

我想用d3绘制一个分组的力定向图

我已经用圆圈画出了这张图。但是我现在想用一条路径(外壳线)连接圆的交点。如果不连接交点,则围绕这组圆绘制外壳线就足够了。我试过这个例子。但是我有文本和圆圈,包括文本和链接

var vertices = new Array();
var width = 960,
    height = 500;
var color = d3.scale.category10();
var r = 6;
var force = d3.layout.force().size([width, height]);
var svg = d3.select("body").append("svg").attr("width", width).attr("height", height).attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
$(function() {

    var json = "{\"nodes\":[{\"name\":\"language\",\"group\":1,\"fontsize\":\"45px\",\"title\":null},{\"name\":\"english\",\"group\":1,\"fontsize\":\"35px\",\"title\":null},{\"name\":\"languages\",\"group\":1,\"fontsize\":\"21px\",\"title\":null},{\"name\":\"speak\",\"group\":1,\"fontsize\":\"16px\",\"title\":null},{\"name\":\"religion\",\"group\":1,\"fontsize\":\"16px\",\"title\":null},{\"name\":\"words\",\"group\":1,\"fontsize\":\"16px\",\"title\":null},{\"name\":\"living\",\"group\":1,\"fontsize\":\"16px\",\"title\":null},{\"name\":\"adobe\",\"group\":2,\"fontsize\":\"15px\",\"title\":null},{\"name\":\"malayalam\",\"group\":1,\"fontsize\":\"15px\",\"title\":null},{\"name\":\"learn\",\"group\":1,\"fontsize\":\"15px\",\"title\":null},{\"name\":\"multilanguage\",\"group\":3,\"fontsize\":\"15px\",\"title\":null},{\"name\":\"different\",\"group\":1,\"fontsize\":\"15px\",\"title\":null},{\"name\":\"sarcasm\",\"group\":1,\"fontsize\":\"15px\",\"title\":null},{\"name\":\"linkedin\",\"group\":4,\"fontsize\":\"15px\",\"title\":null},{\"name\":\"hindi\",\"group\":1,\"fontsize\":\"15px\",\"title\":null},{\"name\":\"indesign\",\"group\":5,\"fontsize\":\"15px\",\"title\":null},{\"name\":\"city\",\"group\":1,\"fontsize\":\"15px\",\"title\":null},{\"name\":\"spanish\",\"group\":1,\"fontsize\":\"15px\",\"title\":null},{\"name\":\"religious\",\"group\":1,\"fontsize\":\"15px\",\"title\":null},{\"name\":\"real\",\"group\":1,\"fontsize\":\"15px\",\"title\":null}],\"links\":[{\"source\":0,\"target\":1,\"value\":1},{\"source\":0,\"target\":2,\"value\":1},{\"source\":0,\"target\":3,\"value\":1},{\"source\":0,\"target\":4,\"value\":1},{\"source\":0,\"target\":5,\"value\":1},{\"source\":1,\"target\":2,\"value\":1},{\"source\":1,\"target\":3,\"value\":1},{\"source\":1,\"target\":5,\"value\":1},{\"source\":1,\"target\":6,\"value\":1},{\"source\":2,\"target\":3,\"value\":1},{\"source\":2,\"target\":4,\"value\":1},{\"source\":2,\"target\":5,\"value\":1},{\"source\":3,\"target\":5,\"value\":1},{\"source\":3,\"target\":8,\"value\":1},{\"source\":4,\"target\":5,\"value\":1},{\"source\":4,\"target\":6,\"value\":1},{\"source\":4,\"target\":11,\"value\":1},{\"source\":5,\"target\":6,\"value\":1},{\"source\":6,\"target\":2,\"value\":1},{\"source\":6,\"target\":11,\"value\":1},{\"source\":6,\"target\":18,\"value\":1},{\"source\":8,\"target\":0,\"value\":1},{\"source\":8,\"target\":2,\"value\":1},{\"source\":8,\"target\":14,\"value\":1},{\"source\":9,\"target\":0,\"value\":1},{\"source\":9,\"target\":1,\"value\":1},{\"source\":9,\"target\":2,\"value\":1},{\"source\":9,\"target\":3,\"value\":1},{\"source\":9,\"target\":8,\"value\":1},{\"source\":11,\"target\":0,\"value\":1},{\"source\":11,\"target\":1,\"value\":1},{\"source\":11,\"target\":2,\"value\":1},{\"source\":11,\"target\":3,\"value\":1},{\"source\":12,\"target\":0,\"value\":1},{\"source\":12,\"target\":1,\"value\":1},{\"source\":12,\"target\":2,\"value\":1},{\"source\":12,\"target\":3,\"value\":1},{\"source\":12,\"target\":14,\"value\":1},{\"source\":14,\"target\":0,\"value\":1},{\"source\":14,\"target\":1,\"value\":1},{\"source\":14,\"target\":2,\"value\":1},{\"source\":14,\"target\":3,\"value\":1},{\"source\":14,\"target\":5,\"value\":1},{\"source\":16,\"target\":0,\"value\":1},{\"source\":16,\"target\":1,\"value\":1},{\"source\":16,\"target\":2,\"value\":1},{\"source\":16,\"target\":9,\"value\":1},{\"source\":16,\"target\":11,\"value\":1},{\"source\":17,\"target\":0,\"value\":1},{\"source\":17,\"target\":1,\"value\":1},{\"source\":17,\"target\":2,\"value\":1},{\"source\":17,\"target\":3,\"value\":1},{\"source\":18,\"target\":2,\"value\":1},{\"source\":18,\"target\":4,\"value\":1},{\"source\":18,\"target\":5,\"value\":1},{\"source\":18,\"target\":11,\"value\":1},{\"source\":19,\"target\":0,\"value\":1},{\"source\":19,\"target\":1,\"value\":1},{\"source\":19,\"target\":2,\"value\":1},{\"source\":19,\"target\":3,\"value\":1},{\"source\":19,\"target\":5,\"value\":1}]}";

    json = htmlDecode(json);

    json = $.parseJSON(json);

    svg.append("svg:rect").attr("width", width).attr("height", height).style("stroke", "#fff").style("fill", "#fff");

    force.nodes(json.nodes).links(json.links).gravity(0.05).linkDistance(120).charge(-200).start();

    var node = svg.selectAll(".node").data(json.nodes).enter().append("g").attr("class", "node");
    var link = svg.selectAll(".link").data(json.links).enter().append("line").attr("class", "link").style("stroke-opacity", "0.2");

    node.append('circle').attr('r', function(d) {
        var tmprad = parseInt(d.fontsize.replace('px', '')) * (d.name.length / 3);
        if (tmprad > r) r = tmprad;
        return tmprad;
    }).style('fill', '#ffffff').style('stroke', function(d) {
        return color(d.group)
    });

    node.selectAll('text').data(json.nodes).enter().append("text").attr("text-anchor", "middle").attr("dx", 2).attr("dy", ".35em").attr('original-title', function(d) {
        return d.title
    }).attr("style", function(d) {
        return "font-size:" + d.fontsize
    }).text(function(d) {
        return d.name
    }).attr("style", function(d) {
        return "font-size:" + d.fontsize
    }).style('fill', function(d) {
        return color(d.group)
    }).style("cursor", "pointer").call(force.drag);

    var cx = new Array();
    var cy = new Array();

    node.attr("cx", function(d) {
        cx.push(d.x);
    }).attr("cy", function(d) {
        cy.push(d.y);
    });

    cx.forEach(function(o, i) {
        vertices.push(new Array(cx[i], cy[i]));
    });

    var nodes = vertices.map(Object);

    var groups = d3.nest().key(function(d) {
        return d;
    }).entries(nodes);

    var groupPath = function(d) {
        return "M" + d3.geom.hull(d.values.map(function(i) {
            return [i.x, i.y];
        })).join("L") + "Z";
    };

    var groupFill = function(d, i) {
        return color(i & 3);
    };

    svg.style("opacity", 1e-6).transition().duration(1000).style("opacity", 1);

    force.on("tick", function() {    
        link.attr("x1", function(d) {
            return d.source.x;
        }).attr("y1", function(d) {
            return d.source.y;
        }).attr("x2", function(d) {
            return d.target.x;
        }).attr("y2", function(d) {
            return d.target.y;
        });

        node.attr("cx", function(d) {
            return d.x = Math.max(r, Math.min(width - r, d.x));
        }).attr("cy", function(d) {
            return d.y = Math.max(r, Math.min(height - r, d.y));
        });

        node.selectAll('circle').attr("transform", function(d) {
            return "translate(" + d.x + "," + d.y + ")"
        });

        // reposition text
        node.selectAll('text').attr("transform", function(d) {
            return "translate(" + d.x + "," + d.y + ")"
        });
    });
});

function htmlEncode(value) {
    return $('<div/>').text(value).html();
}

function htmlDecode(value) {
    return $('<div/>').html(value).text();
}

function move() {
    vertices[0] = d3.svg.mouse(this);
    update();
}

function click() {
    vertices.push(d3.svg.mouse(this));
    update();
}

function update() {
    svg.selectAll("path")
        .data([d3.geom.hull(vertices)])
        .attr("d", function(d) {
            return "M" + d.join("L") + "Z";
        })
       .enter()
        .append("svg:path")
        .attr("d", function(d) {
            return "M" + d.join("L") + "Z";
        });
    svg.selectAll("nodes")
        .data(vertices.slice(1))
       .enter()
        .append("svg:circle")
        .attr("transform", function(d) {
             return "translate(" + d + ")";
        });
}​
var顶点=新数组();
可变宽度=960,
高度=500;
var color=d3.scale.category10();
var r=6;
var-force=d3.layout.force().size([width,height]);
var svg=d3.select(“body”).append(“svg”).attr(“width”,width).attr(“height”,height).attr(“transform”,“translate”(“+width/2+”,“+height/2+”));
$(函数(){
目前,一个“字体大小大小”10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 px 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10,”,”,”,”,”10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10:null},{\'name\':\'religation\',\'group\':1,\'“16px\ \”,“16px\钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱钱:“15px\”,“title\”:null},{“name\”:“learn\”,“group”\:1、1、1、1、1、3、1、1、3、1、1、1、1、1、1、1、1、1、1、10”多语言”1、10、组”3、3、3、3、3、1、1、3、3、3、3、3、3、3、1、1、1、1、10、10、10、1、10、1、1、1、1、10、1、10、10、10、10、多语言”多语言”多语言”,”,”,”,”,”,”,”,”,”多语言”组”10、3、组”3、3、3、3、3、3、3、3、3、3、3、3、3、3、3、3、3、3、3、3、3、3、3、3、3、10、3、3、1、10、10、3、3、3、3、3、3、10、3、3、10、3、10、10、10、10、10、10、“fontsize\”:“15px\”,“title\”:null},{“name\”“印度洋”的一部分,以及“印度洋”的一部分,一个“组”的一部分,一部分,“组”的一部分,一部分,“标题”的一部分:零零,,,{“名”名称”的一部分,一部分,“组”的一部分,一部分,一部分,“组”的一部分,一部分,一部分,一部分,一部分,一部分,一部分,一部分,一部分,“名名”名称::::::,,,,“名名名”的一部分,一部分,一部分,一部分,”一部分,一部分,一部分,一部分,一部分,一部分,一部分,一部分,一部分,一个“组”的;一组:5,“组:5,,,,,,,,,,,“组组:5,”一部分,一组:5,”一部分,一部分,一部分,一部分,一部分,一部分,一部分,一部分,一部分,一部分,一部分,一部分,一部分\“,\“组\”:1,\“fontsize\:\“15px\”,\“title\”:null},“名称”是一名名名为“名名名名”的:“实名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名名\'source\':1,\'target\':2,\'value\':1},{\'source\':1,\'target\':3\“值”1,”源:1,”1,,““值”1,”1,““值”1,”1,,““源”1,”1,”1,“目标”1,”1,“目标”1:6,“值”1:1,”1,”1,”1,”1,”1,”1,”1,,““源”1,”1,““源”1”1,”源:1:1,1,”1,,““值”源:1,”1,”1,”1,”1,”1,“目标:1,”1,”1,”1,”1,”1,”1,“目标:1,”1,”1,”1,”1,”1,”1,”1,“目标:1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,“目标:1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1值\“:1},{\“源\”:4,\“目标\”:5,\“值\”:1},{\“源\”:4,\”“目标”11,“价值”1:1,,,{“来源”1.1,”1.1,”,”1.1,,,,{“来源”1.1,”1.1,,{“来源”5,“目标”5,“目标”6,“目标”6,““价值”6,:1,““价值”1:1,”1.1,”1.1,,,{“源”目标”目标”目标”目标:6;6,”6,“目标”目标:6,“目标”6,“目标”6,”10,”10,”10,”10,”目标:6,“目标:6,”1,”10,”10,”10,”10,”10,“目标:1,”1,”10,”10,”10,”10,”10,”10,”10,”10,”目标:6,“目标:6,“目标:6,”10,”10,”10,”10,”10,”10,”10,”10,”10,”10,”10,”10,”10,”目标:6,“目标:6,“目标:6,“目标:6,”10,”10,”10,”10,”10,”,”,”,”\“:2,\“value\”:1},{\“source\”:8,\“target\”:14,\“value\”:1},“源”源:9.9,“源”源:““““““\ \ \ \ \”:9”9,“源”源”源:9“源”源:9,“源”源:9,“源”源:9”源:9,“目标”目标:9,“目标”目标:9,“目标”目标:2,“目标”目标:2,“值”目标:1,”目标:0.00,”目标:1:1,,,,““源”源”源:目标”目标:1:9,“目标”目标”目标:1,”目标:2,“目标”目标:2,”目标:2,“目标:1,”目标:1,”目标:1,”目标:2,“目标:1,”目标:1,”目标:1,”目标:1,”目标:1,”目标:1,,,,“:1,”目标:1,”目标:1,”目标:1,,“:1,,,“:1,”目标”目标:1,,,“:1,,“:1,,,“:1,”目标:1,”目标\'source\':11,\'target\':2,\'value\':1},{\'source\':11,\'target\“3”3,“值”1:3,“值”1:1:1,”1,,,,,““值”1:3,,,,,““源”1:12,““目标”12,““目标”1,”1,“值”1:1,”1,“值”1:1,1,”1,”1,““值”1:1:1,,““源”源”1:12,““目标”3,,,,,,,,,““源”源”12,”12,“目标”1,”1,”1,“目标”1,”值:1,”1,”1,”1,”1,”1,”1,”1,”1,”1,“值:1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1:1,\'value\':1},{\'source\':14,\'target\':2,\'value\':1}{\“源”14,“源”14人:14,“目标”14人:14,“目标”14人:14“源”14,“目标”1人:5,“值”1人:5,“源”1人:16,“源”16人:14,“目标”14人:14,“目标”14人:14,“目标”10人:14,“目标”目标:3人:目标:3人:3人:3人:3人:14,“源”14,“目标”14人:14人:14人:14,“源”14人:14,“目标”14,“目标”14人:14人:14,“目标”10人:14,“目标”10人:14,“目标”10人:14,“目标”10人:1人:14,“目标:14,“目标”10人:14,“目标”1人:14,“源:1人:14,”10人:14,“源:14,”10人:14,“源:14,“目标”10人,10人,”10人,10人,10人,10人,10人,”10人,10 source\':17,\'target\':0,\'value\':1},{\'source\':17,\'“目标”2,“价值”1:1,““目标”1:1:1”1:1,”1 1:1:1,”1,”1,”源:17,“目标”1:17,“目标”1:17,“目标”1:3,“目标”3,“价值”1:1,”1,”1,“价值”1:1,”1,”1:1,1,”1,,,““源”源”1:1:1:17,“目标”目标”1:1:1:1,”1:1,”1,”1:1:1:1,”1:1:1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,,,,““源:1”源:1:1,”1,”1,”1,”1,”1,”1,”1,”17,“目标:1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”1,”\':0,\'value\':1},{\'source\':19,\'target\':1,\'value\“:1},{\'source\':19,\'target\':2,\'value\':1},{\'source\':19,\'target\':3,\'value\':1},{\'source\':19,\'target\':5,\'value\':1}”;
json=htmlDecode(json);
json=$.parseJSON(json);
svg.append(“svg:rect”).attr(“width”,width)。attr(“height”,height)。style(“stroke”,“#fff”)。style(“fill”,“#fff”);
force.nodes(json.nodes).links(json.links).gravity(0.05).linkDistance(120).charge(-200).start();
var node=svg.selectAll(“.node”).data(json.nodes).enter().append(“g”).attr(“class”,“node”);
var link=svg.selectAll(“.link”).data(json.links).enter().append(“line”).attr(“class”,“link”).style(“笔划不透明度”,“0.2”);
node.append('circle').attr('r',函数(d){
var tmprad=parseInt(d.fontsize.replace('px','')*(d.name.length/3);
如果(tmprad>r)r=tmprad;
返回tmprad;
}).style('fill','#ffffff')。style('stroke',function(d){
返回颜色(d组)
});
node.selectAll('text').data(json.nodes).enter().append(“text”).attr(“文本锚定”,“中间”).attr(“dx”,2).attr(“dy”,“.35em”).attr('original-title',函数(d){
返回d.title
}).attr(“风格”,功能(d){
返回“字体大小:+d.fontsize”
}).文本(功能(d){
返回d.name
}).attr(“风格”,功能(d){
返回“字体大小:+d.fontsize”
}).样式(“填充”,功能(d){
返回颜色(d.grou)
svg.selectAll("path")
        .data(groups)
        .attr("d", groupPath)
        .enter().insert("path", "g")
        .style("fill", groupFill)
        .style("stroke", groupFill)
        .style("stroke-width", 100)
        .style("stroke-linejoin", "round")
        .style("opacity", .2)
        .attr("d", groupPath);