将 d3force 应用于具有多个组的网络

Apply d3force to a network with multiple groups

我是 d3 的初学者,因此这个问题...我有一个包含一堆节点、链接和组的网络,如下所示:

我想在它上面实现一个 d3-force,这样每个组都有自己的力,当一个组被作用时,另一个组不会受到影响。

注意节点 5 是如何拥有组 0 的,因为它是一个单例。所以所有单身人士都会有组 0.

network = {
  "nodes" : [
    {
      "id":0,
      "x":0,
      "y":0,
      "group":1
    },
    {
      "id":1,
      "x":11,
      "y":-11,
      "group":1
    },
    {
      "id":2,
      "x":22,
      "y":-22,
      "group":1
    },
    {
      "id":3,
      "x":33,
      "y":-33,
      "group":2
    },
    {
      "id":4,
      "x":44,
      "y":-44,
      "group":2
    },
    {
      "id":5,
      "x":55,
      "y":-55,
      "group":0
    }],
  "links" : [
    {
      "from": 0,
      "to"  : 1
    },
    {
      "from": 1,
      "to"  : 2
    },
    {
      "from": 3,
      "to"  : 4
    }
  ]
}

这是一个如何实现的示例,请注意我不得不重新编写分组函数,因为您的函数中有一个错误。

var networkData = {
  "nodes": [{
    "id": "0",
    "x": 1509.9862,
    "y": -609.1013,
    "row_count": 2
  }, {
    "id": "1",
    "x": 1645.9578,
    "y": -85.06705,
    "row_count": 3
  }, {
    "id": "2",
    "x": 1948.1533,
    "y": -469.3646,
    "row_count": 2
  }, {
    "id": "3",
    "x": 1490.8839,
    "y": -705.9929,
    "row_count": 2
  }, {
    "id": "4",
    "x": 2370.9739,
    "y": -114.61766,
    "row_count": 2
  }, {
    "id": "5",
    "x": 1788.3419,
    "y": -460.89978,
    "row_count": 2
  }, {
    "id": "6",
    "x": 1601.6083,
    "y": -459.14755,
    "row_count": 2
  }, {
    "id": "7",
    "x": 1967.1221,
    "y": -179.95412,
    "row_count": 2
  }, {
    "id": "8",
    "x": 2305.796,
    "y": -398.09714,
    "row_count": 2
  }, {
    "id": "9",
    "x": 2075.197,
    "y": -119.19522,
    "row_count": 2
  }, {
    "id": "10",
    "x": 144.70703,
    "y": -492.124,
    "row_count": 2
  }, {
    "id": "11",
    "x": 1782.7756,
    "y": -95.29288,
    "row_count": 2
  }, {
    "id": "12",
    "x": 2490.2249,
    "y": -395.42737,
    "row_count": 4
  }, {
    "id": "13",
    "x": 1434.9111,
    "y": -507.22018,
    "row_count": 2
  }, {
    "id": "14",
    "x": 1918.9606,
    "y": -132.97313,
    "row_count": 2
  }, {
    "id": "15",
    "x": 150.78381,
    "y": -375.75558,
    "row_count": 4
  }, {
    "id": "16",
    "x": 117.40755,
    "y": -313.40042,
    "row_count": 2
  }, {
    "id": "17",
    "x": 128.44798,
    "y": -569.65533,
    "row_count": 2
  }, {
    "id": "18",
    "x": 2241.112,
    "y": -397.3375,
    "row_count": 5
  }, {
    "id": "19",
    "x": 93.45022,
    "y": -510.2351,
    "row_count": 2
  }, {
    "id": "20",
    "x": 2554.5647,
    "y": -395.42737,
    "row_count": 2
  }, {
    "id": "21",
    "x": 1552.0585,
    "y": -175.00697,
    "row_count": 2
  }, {
    "id": "22",
    "x": 1839.4581,
    "y": -137.50499,
    "row_count": 2
  }, {
    "id": "23",
    "x": 88.88344,
    "y": -374.78418,
    "row_count": 2
  }, {
    "id": "24",
    "x": 1588.2749,
    "y": -583.3556,
    "row_count": 2
  }, {
    "id": "25",
    "x": 115.34364,
    "y": -439.38092,
    "row_count": 5
  }, {
    "id": "26",
    "x": 1527.4338,
    "y": -425.7689,
    "row_count": 2
  }, {
    "id": "27",
    "x": 1466.7098,
    "y": -580.9009,
    "row_count": 1
  }, {
    "id": "28",
    "x": 1614.1482,
    "y": -508.55847,
    "row_count": 1
  }, {
    "id": "29",
    "x": 1653.5695,
    "y": -705.9929,
    "row_count": 2
  }, {
    "id": "30",
    "x": 1555.2239,
    "y": -705.9929,
    "row_count": 2
  }, {
    "id": "31",
    "x": 2226.4775,
    "y": -111.886284,
    "row_count": 2
  }, {
    "id": "32",
    "x": 1458.8619,
    "y": -462.99728,
    "row_count": 2
  }, {
    "id": "33",
    "x": 1541.3425,
    "y": -599.3965,
    "row_count": 1
  }, {
    "id": "34",
    "x": 1572.1593,
    "y": -445.52554,
    "row_count": 1
  }, {
    "id": "35",
    "x": 1804.5553,
    "y": -705.9929,
    "row_count": 2
  }, {
    "id": "36",
    "x": 2299.7493,
    "y": -113.641975,
    "row_count": 2
  }, {
    "id": "37",
    "x": 1447.5342,
    "y": -536.6993,
    "row_count": 1
  }, {
    "id": "38",
    "x": 1633.1509,
    "y": -212.4338,
    "row_count": 2
  }, {
    "id": "39",
    "x": 1782.9479,
    "y": -406.7332,
    "row_count": 2
  }, {
    "id": "40",
    "x": 1955.5413,
    "y": -705.9929,
    "row_count": 2
  }, {
    "id": "41",
    "x": 2151.4285,
    "y": -111.490974,
    "row_count": 2
  }, {
    "id": "42",
    "x": 1598.4803,
    "y": -553.35645,
    "row_count": 1
  }, {
    "id": "43",
    "x": 1500.1262,
    "y": -441.0764,
    "row_count": 2
  }, {
    "id": "44",
    "x": 1496.883,
    "y": -587.66406,
    "row_count": 1
  }, {
    "id": "45",
    "x": 1596.4176,
    "y": -483.6955,
    "row_count": 1
  }, {
    "id": "46",
    "x": 1455.5319,
    "y": -492.96527,
    "row_count": 2
  }, {
    "id": "47",
    "x": 1563.5104,
    "y": -579.53015,
    "row_count": 1
  }, {
    "id": "48",
    "x": 2037.2152,
    "y": -292.1214,
    "row_count": 2
  }, {
    "id": "49",
    "x": 282.7343,
    "y": -516.7465,
    "row_count": 2
  }, {
    "id": "50",
    "x": 167.14136,
    "y": -428.0224,
    "row_count": 2
  }, {
    "id": "51",
    "x": 2003.0924,
    "y": -437.64548,
    "row_count": 2
  }, {
    "id": "52",
    "x": 99.81963,
    "y": -175.3665,
    "row_count": 2
  }, {
    "id": "53",
    "x": 179.92572,
    "y": -468.74872,
    "row_count": 3
  }, {
    "id": "54",
    "x": 1717.0072,
    "y": -705.9929,
    "row_count": 2
  }, {
    "id": "55",
    "x": 2007.9512,
    "y": -233.56764,
    "row_count": 2
  }, {
    "id": "56",
    "x": 1542.7771,
    "y": -445.58582,
    "row_count": 1
  }, {
    "id": "57",
    "x": 1617.18,
    "y": -149.27104,
    "row_count": 5
  }, {
    "id": "58",
    "x": 1469.2253,
    "y": -555.9452,
    "row_count": 1
  }, {
    "id": "59",
    "x": 1595.1055,
    "y": -524.9397,
    "row_count": 1
  }, {
    "id": "60",
    "x": 1484.0056,
    "y": -464.2597,
    "row_count": 1
  }, {
    "id": "61",
    "x": 1523.9562,
    "y": -581.0812,
    "row_count": 1
  }, {
    "id": "62",
    "x": 2425.8848,
    "y": -395.42737,
    "row_count": 4
  }, {
    "id": "63",
    "x": 108.56919,
    "y": -243.38808,
    "row_count": 4
  }, {
    "id": "64",
    "x": 1867.9934,
    "y": -705.9929,
    "row_count": 2
  }, {
    "id": "65",
    "x": 1996.9839,
    "y": -123.64043,
    "row_count": 3
  }, {
    "id": "66",
    "x": 1948.1533,
    "y": -405.92636,
    "row_count": 2
  }, {
    "id": "67",
    "x": 1572.0945,
    "y": -470.88098,
    "row_count": 1
  }, {
    "id": "68",
    "x": 1465.1875,
    "y": -518.3188,
    "row_count": 1
  }, {
    "id": "69",
    "x": 1572.8579,
    "y": -554.43756,
    "row_count": 1
  }, {
    "id": "70",
    "x": 2106.5273,
    "y": -705.9929,
    "row_count": 2
  }, {
    "id": "71",
    "x": 1519.5193,
    "y": -458.15424,
    "row_count": 1
  }, {
    "id": "72",
    "x": 1494.6997,
    "y": -561.67957,
    "row_count": 2
  }, {
    "id": "73",
    "x": 1579.804,
    "y": -504.15784,
    "row_count": 1
  }, {
    "id": "74",
    "x": 1482.025,
    "y": -489.7116,
    "row_count": 1
  }, {
    "id": "75",
    "x": 2175.6614,
    "y": -396.56552,
    "row_count": 2
  }, {
    "id": "76",
    "x": 1541.8685,
    "y": -563.2375,
    "row_count": 1
  }, {
    "id": "77",
    "x": 1547.088,
    "y": -472.80585,
    "row_count": 1
  }, {
    "id": "78",
    "x": 1485.3854,
    "y": -532.9362,
    "row_count": 1
  }, {
    "id": "79",
    "x": 89.103745,
    "y": -110.00311,
    "row_count": 2
  }, {
    "id": "80",
    "x": 1555.3121,
    "y": -116.60735,
    "row_count": 2
  }, {
    "id": "81",
    "x": 1426.544,
    "y": -705.9929,
    "row_count": 2
  }, {
    "id": "82",
    "x": 1566.833,
    "y": -530.323,
    "row_count": 2
  }, {
    "id": "83",
    "x": 1688.4487,
    "y": -172.91998,
    "row_count": 2
  }, {
    "id": "84",
    "x": 68.698006,
    "y": -573.6171,
    "row_count": 2
  }, {
    "id": "85",
    "x": 1764.1693,
    "y": -158.18617,
    "row_count": 2
  }, {
    "id": "86",
    "x": 219.68867,
    "y": -397.56903,
    "row_count": 2
  }, {
    "id": "87",
    "x": 1730.5239,
    "y": -424.70016,
    "row_count": 2
  }, {
    "id": "88",
    "x": 1837.7125,
    "y": -415.37985,
    "row_count": 2
  }, {
    "id": "89",
    "x": 2439.1343,
    "y": -114.35799,
    "row_count": 2
  }, {
    "id": "90",
    "x": 1852.3489,
    "y": -475.04614,
    "row_count": 3
  }, {
    "id": "91",
    "x": 1715.4718,
    "y": -70.03096,
    "row_count": 2
  }, {
    "id": "92",
    "x": 2018.9792,
    "y": -705.9929,
    "row_count": 2
  }, {
    "id": "93",
    "x": 2169.9653,
    "y": -705.9929,
    "row_count": 2
  }, {
    "id": "94",
    "x": 1508.6908,
    "y": -480.54773,
    "row_count": 4
  }, {
    "id": "95",
    "x": 1699.9895,
    "y": -126.77199,
    "row_count": 2
  }, {
    "id": "96",
    "x": 2110.977,
    "y": -395.80646,
    "row_count": 3
  }, {
    "id": "97",
    "x": 2123.8252,
    "y": -164.62895,
    "row_count": 2
  }, {
    "id": "98",
    "x": 1517.4421,
    "y": -551.2502,
    "row_count": 1
  }, {
    "id": "99",
    "x": 266.0747,
    "y": -450.88815,
    "row_count": 2
  }, {
    "id": "100",
    "x": 1555.2823,
    "y": -497.76593,
    "row_count": 1
  }, {
    "id": "101",
    "x": 1497.4402,
    "y": -510.5418,
    "row_count": 1
  }, {
    "id": "102",
    "x": 1542.789,
    "y": -536.3863,
    "row_count": 1
  }, {
    "id": "103",
    "x": 1528.8363,
    "y": -494.63577,
    "row_count": 1
  }, {
    "id": "104",
    "x": 1515.9209,
    "y": -525.9001,
    "row_count": 1
  }, {
    "id": "105",
    "x": 1526.9814,
    "y": -515.76776,
    "row_count": 1
  }, {
    "id": "106",
    "x": 1737.522,
    "y": -129.01044,
    "row_count": 2
  }],
  "links": [{
    "from": 1,
    "to": 57
  }, {
    "from": 1,
    "to": 91
  }, {
    "from": 2,
    "to": 51
  }, {
    "from": 2,
    "to": 66
  }, {
    "from": 3,
    "to": 30
  }, {
    "from": 3,
    "to": 81
  }, {
    "from": 4,
    "to": 36
  }, {
    "from": 4,
    "to": 89
  }, {
    "from": 5,
    "to": 87
  }, {
    "from": 5,
    "to": 88
  }, {
    "from": 5,
    "to": 90
  }, {
    "from": 7,
    "to": 14
  }, {
    "from": 7,
    "to": 55
  }, {
    "from": 7,
    "to": 65
  }, {
    "from": 8,
    "to": 18
  }, {
    "from": 9,
    "to": 41
  }, {
    "from": 9,
    "to": 65
  }, {
    "from": 9,
    "to": 97
  }, {
    "from": 10,
    "to": 25
  }, {
    "from": 10,
    "to": 50
  }, {
    "from": 10,
    "to": 53
  }, {
    "from": 11,
    "to": 22
  }, {
    "from": 11,
    "to": 85
  }, {
    "from": 11,
    "to": 91
  }, {
    "from": 11,
    "to": 106
  }, {
    "from": 12,
    "to": 20
  }, {
    "from": 12,
    "to": 62
  }, {
    "from": 14,
    "to": 22
  }, {
    "from": 14,
    "to": 65
  }, {
    "from": 15,
    "to": 16
  }, {
    "from": 15,
    "to": 23
  }, {
    "from": 15,
    "to": 25
  }, {
    "from": 15,
    "to": 50
  }, {
    "from": 15,
    "to": 86
  }, {
    "from": 16,
    "to": 23
  }, {
    "from": 16,
    "to": 63
  }, {
    "from": 17,
    "to": 19
  }, {
    "from": 17,
    "to": 84
  }, {
    "from": 18,
    "to": 75
  }, {
    "from": 19,
    "to": 25
  }, {
    "from": 19,
    "to": 84
  }, {
    "from": 21,
    "to": 57
  }, {
    "from": 21,
    "to": 80
  }, {
    "from": 22,
    "to": 85
  }, {
    "from": 23,
    "to": 25
  }, {
    "from": 25,
    "to": 50
  }, {
    "from": 25,
    "to": 53
  }, {
    "from": 29,
    "to": 54
  }, {
    "from": 31,
    "to": 36
  }, {
    "from": 31,
    "to": 41
  }, {
    "from": 35,
    "to": 64
  }, {
    "from": 38,
    "to": 57
  }, {
    "from": 38,
    "to": 83
  }, {
    "from": 39,
    "to": 87
  }, {
    "from": 39,
    "to": 88
  }, {
    "from": 40,
    "to": 92
  }, {
    "from": 41,
    "to": 97
  }, {
    "from": 48,
    "to": 55
  }, {
    "from": 49,
    "to": 99
  }, {
    "from": 50,
    "to": 53
  }, {
    "from": 50,
    "to": 86
  }, {
    "from": 51,
    "to": 66
  }, {
    "from": 52,
    "to": 63
  }, {
    "from": 52,
    "to": 79
  }, {
    "from": 57,
    "to": 80
  }, {
    "from": 57,
    "to": 83
  }, {
    "from": 70,
    "to": 93
  }, {
    "from": 75,
    "to": 96
  }, {
    "from": 83,
    "to": 85
  }, {
    "from": 83,
    "to": 95
  }, {
    "from": 83,
    "to": 106
  }, {
    "from": 85,
    "to": 95
  }, {
    "from": 85,
    "to": 106
  }, {
    "from": 86,
    "to": 99
  }, {
    "from": 88,
    "to": 90
  }, {
    "from": 91,
    "to": 106
  }, {
    "from": 95,
    "to": 106
  }],
  "node_count": 107,
  "link_count": 77
}


/* creating groups */
// union-find is a data structure that can union two sets and check
// whether two element in the same set.

var rootNode = {};

function group2(nodes, links) {
  nodes.forEach((n, i) => n._gid = i + 1);

  const nodeMap = nodes.reduce((result, node) => {
    result[node.id] = node;
    return result
  }, {});

  const linkedNodes = (node, group) => {
    return links.filter(link => link.source == node.id || link.target == node.id)
      .map(link => nodeMap[link.source] === node ? nodeMap[link.target] : nodeMap[link.source])
      .filter(n => n.group != group)
  }

  const infect = (node, group) => {
    node.group = group;
    linkedNodes(node, group).map(n => infect(n, group))
  }

  let n = nodes.find(node => !node.group);
  while (n) {
    infect(n, n._gid);
    n = nodes.find(node => !node.group)
  }
}
/*
function group(nodes, links) {
  // create n set with each set has the node as its only element
  links.forEach(function(link, i) {
    link.source = link.from;
    link.target = link.to;
  });

  nodes.forEach(function(node, i) {
    node.weight = 1;
    rootNode[node.id] = node.id;
  });

  // union each set that has a link between them
  links.forEach(function(link, i) {
    union(link.from, link.to);
  });

  // for each unioned set, group nodes together
  var id = 1;
  var groupIdCnt = {};
  var groupIds = {};
  var groups = {};

  nodes.forEach(function(node, i) {
    var f = find(node.id);
    if (typeof groupIds[f] === 'undefined') {
      groupIds[f] = id;
      groupIdCnt[id] = 1;
      id++;
    } else {
      groupIdCnt[groupIds[f]]++;
    }

    if (groupIdCnt[groupIds[f]] === 1) {
      node['group'] = 0;
      //console.log(node)
    } else {
      node['group'] = groupIds[f];
      //console.log(node)
    }

    if (typeof groups[node['group']] === 'undefined') {
      groups[node['group']] = [];
    }
    groups[node['group']].push(node);
  });

  return Object.values(groups);

}

// find rootNode of each set
function find(node) {
  // if it is the root, return
  if (rootNode[node] === node) {
    return node;
  }
  // if not, find the rootNode and point to it
  rootNode[node] = find(rootNode[node]);
  return rootNode[node];
}

// update the rootNode of set which includes node1 to the rootNode of set which includes node 2
function union(node1, node2) {
  rootNode[find(node1)] = find(node2);
} */

var links = networkData.links;
var nodes = networkData.nodes;

for (var i = 0; i < nodes.length; i++) {
  nodes[i].level = 1;
}

for (var i = 0; i < links.length; i++) {
  links[i].source = links[i].from;
  links[i].target = links[i].to;
}

var groups = group2(nodes, links)





/** ====== D3 FORCE CODE =======*/
function getNodeColor(node, neighbors) {
  return node.level === 1 ? 'red' : 'gray'
}


function getLinkColor(node, link) {
  return isNeighborLink(node, link) ? 'green' : '#000'
}

var width = 600
var height = 600
var color = d3.scaleOrdinal(d3.schemeCategory10);
var svg = d3.select('svg')
svg.attr('width', width).attr('height', height)

// simulation setup with all forces
var linkForce = d3
  .forceLink()
  .id(function(link) {
    return link.id
  })
  .strength(function(link) {
    return 1
  })

var chargeForce = d3.forceManyBody()
  .strength(function(node) {
    if (links.find(link => link.source == node.id || link.target == node.id)) {
      return -100
    } else {
      return 0
    }
  }).distanceMax([100])

var simulation = d3
  .forceSimulation()
  .force('link', linkForce)
  .force('charge', chargeForce)
  .force('center', d3.forceCenter(width / 2, height / 2))


var dragDrop = d3.drag().on('start', function(node) {
  simulation.nodes().forEach(n => {
    if (n.group != node.group) {
      n.fx = n.x
      n.fy = n.y
    }
  })

}).on('drag', function(node) {
  simulation.alphaTarget(0.7).restart()
  node.fx = d3.event.x
  node.fy = d3.event.y
}).on('end', function(node) {
  simulation.nodes().forEach(node => {
    node.fx = null
    node.fy = null
  })
  simulation.alphaTarget(0).restart()
})

var linkElements = svg.append("g")
  .attr("class", "links")
  .selectAll("line")
  .data(links)
  .enter().append("line")
  .attr("stroke-width", 1)
  .attr("stroke", "rgba(50, 50, 50, 0.2)")

var nodeElements = svg.append("g")
  .attr("class", "nodes")
  .selectAll("circle")
  .data(nodes)
  .enter().append("circle")
  .attr("r", 5)
  .attr("fill", d => color(d.group))
  .call(dragDrop)



simulation.nodes(nodes).on('tick', () => {
  nodeElements
    .attr('cx', function(node) {
      return node.x
    })
    .attr('cy', function(node) {
      return node.y
    })

  linkElements
    .attr('x1', function(link) {
      return link.source.x
    })
    .attr('y1', function(link) {
      return link.source.y
    })
    .attr('x2', function(link) {
      return link.target.x
    })
    .attr('y2', function(link) {
      return link.target.y
    })
})

simulation.force("link").links(links)
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="1000" height="600"></svg>