@ -0,0 +1,373 @@ | |||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> | |||||
<html> | |||||
<head> | |||||
<title>Graph | Multiline text</title> | |||||
<style type="text/css"> | |||||
#mygraph { | |||||
width: 900px; | |||||
height: 900px; | |||||
border: 1px solid lightgray; | |||||
} | |||||
</style> | |||||
<script type="text/javascript" src="../../dist/vis.js"></script> | |||||
<script type="text/javascript"> | |||||
function draw() { | |||||
// create some nodes | |||||
var nodes = [ | |||||
{id:0,"labelIGNORE":"Myriel","group":1}, | |||||
{id:1,"labelIGNORE":"Napoleon","group":1}, | |||||
{id:2,"labelIGNORE":"Mlle.Baptistine","group":1}, | |||||
{id:3,"labelIGNORE":"Mme.Magloire","group":1}, | |||||
{id:4,"labelIGNORE":"CountessdeLo","group":1}, | |||||
{id:5,"labelIGNORE":"Geborand","group":1}, | |||||
{id:6,"labelIGNORE":"Champtercier","group":1}, | |||||
{id:7,"labelIGNORE":"Cravatte","group":1}, | |||||
{id:8,"labelIGNORE":"Count","group":1}, | |||||
{id:9,"labelIGNORE":"OldMan","group":1}, | |||||
{id:10,"labelIGNORE":"Labarre","group":2}, | |||||
{id:11,"labelIGNORE":"Valjean","group":2}, | |||||
{id:12,"labelIGNORE":"Marguerite","group":3}, | |||||
{id:13,"labelIGNORE":"Mme.deR","group":2}, | |||||
{id:14,"labelIGNORE":"Isabeau","group":2}, | |||||
{id:15,"labelIGNORE":"Gervais","group":2}, | |||||
{id:16,"labelIGNORE":"Tholomyes","group":3}, | |||||
{id:17,"labelIGNORE":"Listolier","group":3}, | |||||
{id:18,"labelIGNORE":"Fameuil","group":3}, | |||||
{id:19,"labelIGNORE":"Blacheville","group":3}, | |||||
{id:20,"labelIGNORE":"Favourite","group":3}, | |||||
{id:21,"labelIGNORE":"Dahlia","group":3}, | |||||
{id:22,"labelIGNORE":"Zephine","group":3}, | |||||
{id:23,"labelIGNORE":"Fantine","group":3}, | |||||
{id:24,"labelIGNORE":"Mme.Thenardier","group":4}, | |||||
{id:25,"labelIGNORE":"Thenardier","group":4}, | |||||
{id:26,"labelIGNORE":"Cosette","group":5}, | |||||
{id:27,"labelIGNORE":"Javert","group":4}, | |||||
{id:28,"labelIGNORE":"Fauchelevent","group":0}, | |||||
{id:29,"labelIGNORE":"Bamatabois","group":2}, | |||||
{id:30,"labelIGNORE":"Perpetue","group":3}, | |||||
{id:31,"labelIGNORE":"Simplice","group":2}, | |||||
{id:32,"labelIGNORE":"Scaufflaire","group":2}, | |||||
{id:33,"labelIGNORE":"Woman1","group":2}, | |||||
{id:34,"labelIGNORE":"Judge","group":2}, | |||||
{id:35,"labelIGNORE":"Champmathieu","group":2}, | |||||
{id:36,"labelIGNORE":"Brevet","group":2}, | |||||
{id:37,"labelIGNORE":"Chenildieu","group":2}, | |||||
{id:38,"labelIGNORE":"Cochepaille","group":2}, | |||||
{id:39,"labelIGNORE":"Pontmercy","group":4}, | |||||
{id:40,"labelIGNORE":"Boulatruelle","group":6}, | |||||
{id:41,"labelIGNORE":"Eponine","group":4}, | |||||
{id:42,"labelIGNORE":"Anzelma","group":4}, | |||||
{id:43,"labelIGNORE":"Woman2","group":5}, | |||||
{id:44,"labelIGNORE":"MotherInnocent","group":0}, | |||||
{id:45,"labelIGNORE":"Gribier","group":0}, | |||||
{id:46,"labelIGNORE":"Jondrette","group":7}, | |||||
{id:47,"labelIGNORE":"Mme.Burgon","group":7}, | |||||
{id:48,"labelIGNORE":"Gavroche","group":8}, | |||||
{id:49,"labelIGNORE":"Gillenormand","group":5}, | |||||
{id:50,"labelIGNORE":"Magnon","group":5}, | |||||
{id:51,"labelIGNORE":"Mlle.Gillenormand","group":5}, | |||||
{id:52,"labelIGNORE":"Mme.Pontmercy","group":5}, | |||||
{id:53,"labelIGNORE":"Mlle.Vaubois","group":5}, | |||||
{id:54,"labelIGNORE":"Lt.Gillenormand","group":5}, | |||||
{id:55,"labelIGNORE":"Marius","group":8}, | |||||
{id:56,"labelIGNORE":"BaronessT","group":5}, | |||||
{id:57,"labelIGNORE":"Mabeuf","group":8}, | |||||
{id:58,"labelIGNORE":"Enjolras","group":8}, | |||||
{id:59,"labelIGNORE":"Combeferre","group":8}, | |||||
{id:60,"labelIGNORE":"Prouvaire","group":8}, | |||||
{id:61,"labelIGNORE":"Feuilly","group":8}, | |||||
{id:62,"labelIGNORE":"Courfeyrac","group":8}, | |||||
{id:63,"labelIGNORE":"Bahorel","group":8}, | |||||
{id:64,"labelIGNORE":"Bossuet","group":8}, | |||||
{id:65,"labelIGNORE":"Joly","group":8}, | |||||
{id:66,"labelIGNORE":"Grantaire","group":8}, | |||||
{id:67,"labelIGNORE":"MotherPlutarch","group":9}, | |||||
{id:68,"labelIGNORE":"Gueulemer","group":4}, | |||||
{id:69,"labelIGNORE":"Babet","group":4}, | |||||
{id:70,"labelIGNORE":"Claquesous","group":4}, | |||||
{id:71,"labelIGNORE":"Montparnasse","group":4}, | |||||
{id:72,"labelIGNORE":"Toussaint","group":5}, | |||||
{id:73,"labelIGNORE":"Child1","group":10}, | |||||
{id:74,"labelIGNORE":"Child2","group":10}, | |||||
{id:75,"labelIGNORE":"Brujon","group":4}, | |||||
{id:76,"labelIGNORE":"Mme.Hucheloup","group":8} | |||||
]; | |||||
// create some edges | |||||
var edges = [ | |||||
{"from":1,"to":0,"valueIGNORED":1}, | |||||
{"from":2,"to":0,"valueIGNORED":8}, | |||||
{"from":3,"to":0,"valueIGNORED":10}, | |||||
{"from":3,"to":2,"valueIGNORED":6}, | |||||
{"from":4,"to":0,"valueIGNORED":1}, | |||||
{"from":5,"to":0,"valueIGNORED":1}, | |||||
{"from":6,"to":0,"valueIGNORED":1}, | |||||
{"from":7,"to":0,"valueIGNORED":1}, | |||||
{"from":8,"to":0,"valueIGNORED":2}, | |||||
{"from":9,"to":0,"valueIGNORED":1}, | |||||
{"from":11,"to":10,"valueIGNORED":1}, | |||||
{"from":11,"to":3,"valueIGNORED":3}, | |||||
{"from":11,"to":2,"valueIGNORED":3}, | |||||
{"from":11,"to":0,"valueIGNORED":5}, | |||||
{"from":12,"to":11,"valueIGNORED":1}, | |||||
{"from":13,"to":11,"valueIGNORED":1}, | |||||
{"from":14,"to":11,"valueIGNORED":1}, | |||||
{"from":15,"to":11,"valueIGNORED":1}, | |||||
{"from":17,"to":16,"valueIGNORED":4}, | |||||
{"from":18,"to":16,"valueIGNORED":4}, | |||||
{"from":18,"to":17,"valueIGNORED":4}, | |||||
{"from":19,"to":16,"valueIGNORED":4}, | |||||
{"from":19,"to":17,"valueIGNORED":4}, | |||||
{"from":19,"to":18,"valueIGNORED":4}, | |||||
{"from":20,"to":16,"valueIGNORED":3}, | |||||
{"from":20,"to":17,"valueIGNORED":3}, | |||||
{"from":20,"to":18,"valueIGNORED":3}, | |||||
{"from":20,"to":19,"valueIGNORED":4}, | |||||
{"from":21,"to":16,"valueIGNORED":3}, | |||||
{"from":21,"to":17,"valueIGNORED":3}, | |||||
{"from":21,"to":18,"valueIGNORED":3}, | |||||
{"from":21,"to":19,"valueIGNORED":3}, | |||||
{"from":21,"to":20,"valueIGNORED":5}, | |||||
{"from":22,"to":16,"valueIGNORED":3}, | |||||
{"from":22,"to":17,"valueIGNORED":3}, | |||||
{"from":22,"to":18,"valueIGNORED":3}, | |||||
{"from":22,"to":19,"valueIGNORED":3}, | |||||
{"from":22,"to":20,"valueIGNORED":4}, | |||||
{"from":22,"to":21,"valueIGNORED":4}, | |||||
{"from":23,"to":16,"valueIGNORED":3}, | |||||
{"from":23,"to":17,"valueIGNORED":3}, | |||||
{"from":23,"to":18,"valueIGNORED":3}, | |||||
{"from":23,"to":19,"valueIGNORED":3}, | |||||
{"from":23,"to":20,"valueIGNORED":4}, | |||||
{"from":23,"to":21,"valueIGNORED":4}, | |||||
{"from":23,"to":22,"valueIGNORED":4}, | |||||
{"from":23,"to":12,"valueIGNORED":2}, | |||||
{"from":23,"to":11,"valueIGNORED":9}, | |||||
{"from":24,"to":23,"valueIGNORED":2}, | |||||
{"from":24,"to":11,"valueIGNORED":7}, | |||||
{"from":25,"to":24,"valueIGNORED":13}, | |||||
{"from":25,"to":23,"valueIGNORED":1}, | |||||
{"from":25,"to":11,"valueIGNORED":12}, | |||||
{"from":26,"to":24,"valueIGNORED":4}, | |||||
{"from":26,"to":11,"valueIGNORED":31}, | |||||
{"from":26,"to":16,"valueIGNORED":1}, | |||||
{"from":26,"to":25,"valueIGNORED":1}, | |||||
{"from":27,"to":11,"valueIGNORED":17}, | |||||
{"from":27,"to":23,"valueIGNORED":5}, | |||||
{"from":27,"to":25,"valueIGNORED":5}, | |||||
{"from":27,"to":24,"valueIGNORED":1}, | |||||
{"from":27,"to":26,"valueIGNORED":1}, | |||||
{"from":28,"to":11,"valueIGNORED":8}, | |||||
{"from":28,"to":27,"valueIGNORED":1}, | |||||
{"from":29,"to":23,"valueIGNORED":1}, | |||||
{"from":29,"to":27,"valueIGNORED":1}, | |||||
{"from":29,"to":11,"valueIGNORED":2}, | |||||
{"from":30,"to":23,"valueIGNORED":1}, | |||||
{"from":31,"to":30,"valueIGNORED":2}, | |||||
{"from":31,"to":11,"valueIGNORED":3}, | |||||
{"from":31,"to":23,"valueIGNORED":2}, | |||||
{"from":31,"to":27,"valueIGNORED":1}, | |||||
{"from":32,"to":11,"valueIGNORED":1}, | |||||
{"from":33,"to":11,"valueIGNORED":2}, | |||||
{"from":33,"to":27,"valueIGNORED":1}, | |||||
{"from":34,"to":11,"valueIGNORED":3}, | |||||
{"from":34,"to":29,"valueIGNORED":2}, | |||||
{"from":35,"to":11,"valueIGNORED":3}, | |||||
{"from":35,"to":34,"valueIGNORED":3}, | |||||
{"from":35,"to":29,"valueIGNORED":2}, | |||||
{"from":36,"to":34,"valueIGNORED":2}, | |||||
{"from":36,"to":35,"valueIGNORED":2}, | |||||
{"from":36,"to":11,"valueIGNORED":2}, | |||||
{"from":36,"to":29,"valueIGNORED":1}, | |||||
{"from":37,"to":34,"valueIGNORED":2}, | |||||
{"from":37,"to":35,"valueIGNORED":2}, | |||||
{"from":37,"to":36,"valueIGNORED":2}, | |||||
{"from":37,"to":11,"valueIGNORED":2}, | |||||
{"from":37,"to":29,"valueIGNORED":1}, | |||||
{"from":38,"to":34,"valueIGNORED":2}, | |||||
{"from":38,"to":35,"valueIGNORED":2}, | |||||
{"from":38,"to":36,"valueIGNORED":2}, | |||||
{"from":38,"to":37,"valueIGNORED":2}, | |||||
{"from":38,"to":11,"valueIGNORED":2}, | |||||
{"from":38,"to":29,"valueIGNORED":1}, | |||||
{"from":39,"to":25,"valueIGNORED":1}, | |||||
{"from":40,"to":25,"valueIGNORED":1}, | |||||
{"from":41,"to":24,"valueIGNORED":2}, | |||||
{"from":41,"to":25,"valueIGNORED":3}, | |||||
{"from":42,"to":41,"valueIGNORED":2}, | |||||
{"from":42,"to":25,"valueIGNORED":2}, | |||||
{"from":42,"to":24,"valueIGNORED":1}, | |||||
{"from":43,"to":11,"valueIGNORED":3}, | |||||
{"from":43,"to":26,"valueIGNORED":1}, | |||||
{"from":43,"to":27,"valueIGNORED":1}, | |||||
{"from":44,"to":28,"valueIGNORED":3}, | |||||
{"from":44,"to":11,"valueIGNORED":1}, | |||||
{"from":45,"to":28,"valueIGNORED":2}, | |||||
{"from":47,"to":46,"valueIGNORED":1}, | |||||
{"from":48,"to":47,"valueIGNORED":2}, | |||||
{"from":48,"to":25,"valueIGNORED":1}, | |||||
{"from":48,"to":27,"valueIGNORED":1}, | |||||
{"from":48,"to":11,"valueIGNORED":1}, | |||||
{"from":49,"to":26,"valueIGNORED":3}, | |||||
{"from":49,"to":11,"valueIGNORED":2}, | |||||
{"from":50,"to":49,"valueIGNORED":1}, | |||||
{"from":50,"to":24,"valueIGNORED":1}, | |||||
{"from":51,"to":49,"valueIGNORED":9}, | |||||
{"from":51,"to":26,"valueIGNORED":2}, | |||||
{"from":51,"to":11,"valueIGNORED":2}, | |||||
{"from":52,"to":51,"valueIGNORED":1}, | |||||
{"from":52,"to":39,"valueIGNORED":1}, | |||||
{"from":53,"to":51,"valueIGNORED":1}, | |||||
{"from":54,"to":51,"valueIGNORED":2}, | |||||
{"from":54,"to":49,"valueIGNORED":1}, | |||||
{"from":54,"to":26,"valueIGNORED":1}, | |||||
{"from":55,"to":51,"valueIGNORED":6}, | |||||
{"from":55,"to":49,"valueIGNORED":12}, | |||||
{"from":55,"to":39,"valueIGNORED":1}, | |||||
{"from":55,"to":54,"valueIGNORED":1}, | |||||
{"from":55,"to":26,"valueIGNORED":21}, | |||||
{"from":55,"to":11,"valueIGNORED":19}, | |||||
{"from":55,"to":16,"valueIGNORED":1}, | |||||
{"from":55,"to":25,"valueIGNORED":2}, | |||||
{"from":55,"to":41,"valueIGNORED":5}, | |||||
{"from":55,"to":48,"valueIGNORED":4}, | |||||
{"from":56,"to":49,"valueIGNORED":1}, | |||||
{"from":56,"to":55,"valueIGNORED":1}, | |||||
{"from":57,"to":55,"valueIGNORED":1}, | |||||
{"from":57,"to":41,"valueIGNORED":1}, | |||||
{"from":57,"to":48,"valueIGNORED":1}, | |||||
{"from":58,"to":55,"valueIGNORED":7}, | |||||
{"from":58,"to":48,"valueIGNORED":7}, | |||||
{"from":58,"to":27,"valueIGNORED":6}, | |||||
{"from":58,"to":57,"valueIGNORED":1}, | |||||
{"from":58,"to":11,"valueIGNORED":4}, | |||||
{"from":59,"to":58,"valueIGNORED":15}, | |||||
{"from":59,"to":55,"valueIGNORED":5}, | |||||
{"from":59,"to":48,"valueIGNORED":6}, | |||||
{"from":59,"to":57,"valueIGNORED":2}, | |||||
{"from":60,"to":48,"valueIGNORED":1}, | |||||
{"from":60,"to":58,"valueIGNORED":4}, | |||||
{"from":60,"to":59,"valueIGNORED":2}, | |||||
{"from":61,"to":48,"valueIGNORED":2}, | |||||
{"from":61,"to":58,"valueIGNORED":6}, | |||||
{"from":61,"to":60,"valueIGNORED":2}, | |||||
{"from":61,"to":59,"valueIGNORED":5}, | |||||
{"from":61,"to":57,"valueIGNORED":1}, | |||||
{"from":61,"to":55,"valueIGNORED":1}, | |||||
{"from":62,"to":55,"valueIGNORED":9}, | |||||
{"from":62,"to":58,"valueIGNORED":17}, | |||||
{"from":62,"to":59,"valueIGNORED":13}, | |||||
{"from":62,"to":48,"valueIGNORED":7}, | |||||
{"from":62,"to":57,"valueIGNORED":2}, | |||||
{"from":62,"to":41,"valueIGNORED":1}, | |||||
{"from":62,"to":61,"valueIGNORED":6}, | |||||
{"from":62,"to":60,"valueIGNORED":3}, | |||||
{"from":63,"to":59,"valueIGNORED":5}, | |||||
{"from":63,"to":48,"valueIGNORED":5}, | |||||
{"from":63,"to":62,"valueIGNORED":6}, | |||||
{"from":63,"to":57,"valueIGNORED":2}, | |||||
{"from":63,"to":58,"valueIGNORED":4}, | |||||
{"from":63,"to":61,"valueIGNORED":3}, | |||||
{"from":63,"to":60,"valueIGNORED":2}, | |||||
{"from":63,"to":55,"valueIGNORED":1}, | |||||
{"from":64,"to":55,"valueIGNORED":5}, | |||||
{"from":64,"to":62,"valueIGNORED":12}, | |||||
{"from":64,"to":48,"valueIGNORED":5}, | |||||
{"from":64,"to":63,"valueIGNORED":4}, | |||||
{"from":64,"to":58,"valueIGNORED":10}, | |||||
{"from":64,"to":61,"valueIGNORED":6}, | |||||
{"from":64,"to":60,"valueIGNORED":2}, | |||||
{"from":64,"to":59,"valueIGNORED":9}, | |||||
{"from":64,"to":57,"valueIGNORED":1}, | |||||
{"from":64,"to":11,"valueIGNORED":1}, | |||||
{"from":65,"to":63,"valueIGNORED":5}, | |||||
{"from":65,"to":64,"valueIGNORED":7}, | |||||
{"from":65,"to":48,"valueIGNORED":3}, | |||||
{"from":65,"to":62,"valueIGNORED":5}, | |||||
{"from":65,"to":58,"valueIGNORED":5}, | |||||
{"from":65,"to":61,"valueIGNORED":5}, | |||||
{"from":65,"to":60,"valueIGNORED":2}, | |||||
{"from":65,"to":59,"valueIGNORED":5}, | |||||
{"from":65,"to":57,"valueIGNORED":1}, | |||||
{"from":65,"to":55,"valueIGNORED":2}, | |||||
{"from":66,"to":64,"valueIGNORED":3}, | |||||
{"from":66,"to":58,"valueIGNORED":3}, | |||||
{"from":66,"to":59,"valueIGNORED":1}, | |||||
{"from":66,"to":62,"valueIGNORED":2}, | |||||
{"from":66,"to":65,"valueIGNORED":2}, | |||||
{"from":66,"to":48,"valueIGNORED":1}, | |||||
{"from":66,"to":63,"valueIGNORED":1}, | |||||
{"from":66,"to":61,"valueIGNORED":1}, | |||||
{"from":66,"to":60,"valueIGNORED":1}, | |||||
{"from":67,"to":57,"valueIGNORED":3}, | |||||
{"from":68,"to":25,"valueIGNORED":5}, | |||||
{"from":68,"to":11,"valueIGNORED":1}, | |||||
{"from":68,"to":24,"valueIGNORED":1}, | |||||
{"from":68,"to":27,"valueIGNORED":1}, | |||||
{"from":68,"to":48,"valueIGNORED":1}, | |||||
{"from":68,"to":41,"valueIGNORED":1}, | |||||
{"from":69,"to":25,"valueIGNORED":6}, | |||||
{"from":69,"to":68,"valueIGNORED":6}, | |||||
{"from":69,"to":11,"valueIGNORED":1}, | |||||
{"from":69,"to":24,"valueIGNORED":1}, | |||||
{"from":69,"to":27,"valueIGNORED":2}, | |||||
{"from":69,"to":48,"valueIGNORED":1}, | |||||
{"from":69,"to":41,"valueIGNORED":1}, | |||||
{"from":70,"to":25,"valueIGNORED":4}, | |||||
{"from":70,"to":69,"valueIGNORED":4}, | |||||
{"from":70,"to":68,"valueIGNORED":4}, | |||||
{"from":70,"to":11,"valueIGNORED":1}, | |||||
{"from":70,"to":24,"valueIGNORED":1}, | |||||
{"from":70,"to":27,"valueIGNORED":1}, | |||||
{"from":70,"to":41,"valueIGNORED":1}, | |||||
{"from":70,"to":58,"valueIGNORED":1}, | |||||
{"from":71,"to":27,"valueIGNORED":1}, | |||||
{"from":71,"to":69,"valueIGNORED":2}, | |||||
{"from":71,"to":68,"valueIGNORED":2}, | |||||
{"from":71,"to":70,"valueIGNORED":2}, | |||||
{"from":71,"to":11,"valueIGNORED":1}, | |||||
{"from":71,"to":48,"valueIGNORED":1}, | |||||
{"from":71,"to":41,"valueIGNORED":1}, | |||||
{"from":71,"to":25,"valueIGNORED":1}, | |||||
{"from":72,"to":26,"valueIGNORED":2}, | |||||
{"from":72,"to":27,"valueIGNORED":1}, | |||||
{"from":72,"to":11,"valueIGNORED":1}, | |||||
{"from":73,"to":48,"valueIGNORED":2}, | |||||
{"from":74,"to":48,"valueIGNORED":2}, | |||||
{"from":74,"to":73,"valueIGNORED":3}, | |||||
{"from":75,"to":69,"valueIGNORED":3}, | |||||
{"from":75,"to":68,"valueIGNORED":3}, | |||||
{"from":75,"to":25,"valueIGNORED":3}, | |||||
{"from":75,"to":48,"valueIGNORED":1}, | |||||
{"from":75,"to":41,"valueIGNORED":1}, | |||||
{"from":75,"to":70,"valueIGNORED":1}, | |||||
{"from":75,"to":71,"valueIGNORED":1}, | |||||
{"from":76,"to":64,"valueIGNORED":1}, | |||||
{"from":76,"to":65,"valueIGNORED":1}, | |||||
{"from":76,"to":66,"valueIGNORED":1}, | |||||
{"from":76,"to":63,"valueIGNORED":1}, | |||||
{"from":76,"to":62,"valueIGNORED":1}, | |||||
{"from":76,"to":48,"valueIGNORED":1}, | |||||
{"from":76,"to":58,"valueIGNORED":1} | |||||
]; | |||||
// create a graph | |||||
var container = document.getElementById('mygraph'); | |||||
var data = { | |||||
nodes: nodes, | |||||
edges: edges | |||||
}; | |||||
var options = {stabilize: false}; | |||||
var graph = new vis.Graph(container, data, options); | |||||
} | |||||
</script> | |||||
</head> | |||||
<body onload="draw()"> | |||||
<div id="mygraph"></div> | |||||
</body> | |||||
</html> |
@ -0,0 +1,539 @@ | |||||
/** | |||||
* Created by Alex on 2/6/14. | |||||
*/ | |||||
var physicsMixin = { | |||||
/** | |||||
* Before calculating the forces, we check if we need to cluster to keep up performance and we check | |||||
* if there is more than one node. If it is just one node, we dont calculate anything. | |||||
* | |||||
* @private | |||||
*/ | |||||
_initializeForceCalculation : function() { | |||||
// stop calculation if there is only one node | |||||
if (this.nodeIndices.length == 1) { | |||||
this.nodes[this.nodeIndices[0]]._setForce(0,0); | |||||
} | |||||
else { | |||||
// if there are too many nodes on screen, we cluster without repositioning | |||||
if (this.nodeIndices.length > this.constants.clustering.clusterThreshold && this.constants.clustering.enabled == true) { | |||||
this.clusterToFit(this.constants.clustering.reduceToNodes, false); | |||||
} | |||||
// we now start the force calculation | |||||
this._calculateForcesBarnesHut(); | |||||
// this._calculateForcesOriginal(); | |||||
} | |||||
}, | |||||
/** | |||||
* Calculate the external forces acting on the nodes | |||||
* Forces are caused by: edges, repulsing forces between nodes, gravity | |||||
* @private | |||||
*/ | |||||
_calculateForcesOriginal : function() { | |||||
// Gravity is required to keep separated groups from floating off | |||||
// the forces are reset to zero in this loop by using _setForce instead | |||||
// of _addForce | |||||
// var startTimeAll = Date.now(); | |||||
this._calculateGravitationalForces(1); | |||||
// var startTimeRepulsion = Date.now(); | |||||
// All nodes repel eachother. | |||||
this._calculateRepulsionForces(); | |||||
// var endTimeRepulsion = Date.now(); | |||||
// the edges are strings | |||||
this._calculateSpringForces(1); | |||||
// var endTimeAll = Date.now(); | |||||
// echo("Time repulsion part:", endTimeRepulsion - startTimeRepulsion); | |||||
// echo("Time total force calc:", endTimeAll - startTimeAll); | |||||
}, | |||||
/** | |||||
* Calculate the external forces acting on the nodes | |||||
* Forces are caused by: edges, repulsing forces between nodes, gravity | |||||
* @private | |||||
*/ | |||||
_calculateForcesBarnesHut : function() { | |||||
// Gravity is required to keep separated groups from floating off | |||||
// the forces are reset to zero in this loop by using _setForce instead | |||||
// of _addForce | |||||
// var startTimeAll = Date.now(); | |||||
this._clearForces(); | |||||
// var startTimeRepulsion = Date.now(); | |||||
// All nodes repel eachother. | |||||
this._calculateBarnesHutForces(); | |||||
// var endTimeRepulsion = Date.now(); | |||||
// the edges are strings | |||||
this._calculateSpringForces(1); | |||||
// var endTimeAll = Date.now(); | |||||
// echo("Time repulsion part:", endTimeRepulsion - startTimeRepulsion); | |||||
// echo("Time total force calc:", endTimeAll - startTimeAll); | |||||
}, | |||||
_clearForces : function() { | |||||
var node; | |||||
var nodes = this.nodes; | |||||
for (var i = 0; i < this.nodeIndices.length; i++) { | |||||
node = nodes[this.nodeIndices[i]]; | |||||
node._setForce(0, 0); | |||||
node.updateDamping(this.nodeIndices.length); | |||||
} | |||||
}, | |||||
_calculateGravitationalForces : function(boost) { | |||||
var dx, dy, angle, fx, fy, node, i; | |||||
var nodes = this.nodes; | |||||
var gravity = 0.08 * boost; | |||||
for (i = 0; i < this.nodeIndices.length; i++) { | |||||
node = nodes[this.nodeIndices[i]]; | |||||
// gravity does not apply when we are in a pocket sector | |||||
if (this._sector() == "default") { | |||||
dx = -node.x;// + screenCenterPos.x; | |||||
dy = -node.y;// + screenCenterPos.y; | |||||
angle = Math.atan2(dy, dx); | |||||
fx = Math.cos(angle) * gravity; | |||||
fy = Math.sin(angle) * gravity; | |||||
} | |||||
else { | |||||
fx = 0; | |||||
fy = 0; | |||||
} | |||||
node._setForce(fx, fy); | |||||
node.updateDamping(this.nodeIndices.length); | |||||
} | |||||
}, | |||||
_calculateRepulsionForces : function() { | |||||
var dx, dy, angle, distance, fx, fy, clusterSize, | |||||
repulsingForce, node1, node2, i, j; | |||||
var nodes = this.nodes; | |||||
// approximation constants | |||||
var a_base = (-2/3); | |||||
var b = 4/3; | |||||
// repulsing forces between nodes | |||||
var minimumDistance = this.constants.nodes.distance; | |||||
//var steepness = 10; | |||||
// we loop from i over all but the last entree in the array | |||||
// j loops from i+1 to the last. This way we do not double count any of the indices, nor i == j | |||||
for (i = 0; i < this.nodeIndices.length-1; i++) { | |||||
node1 = nodes[this.nodeIndices[i]]; | |||||
for (j = i+1; j < this.nodeIndices.length; j++) { | |||||
node2 = nodes[this.nodeIndices[j]]; | |||||
clusterSize = (node1.clusterSize + node2.clusterSize - 2); | |||||
dx = node2.x - node1.x; | |||||
dy = node2.y - node1.y; | |||||
distance = Math.sqrt(dx * dx + dy * dy); | |||||
// clusters have a larger region of influence | |||||
minimumDistance = (clusterSize == 0) ? this.constants.nodes.distance : (this.constants.nodes.distance * (1 + clusterSize * this.constants.clustering.distanceAmplification)); | |||||
var a = a_base / minimumDistance; | |||||
if (distance < 2*minimumDistance) { // at 2.0 * the minimum distance, the force is 0.000045 | |||||
angle = Math.atan2(dy, dx); | |||||
if (distance < 0.5*minimumDistance) { // at 0.5 * the minimum distance, the force is 0.993307 | |||||
repulsingForce = 1.0; | |||||
} | |||||
else { | |||||
repulsingForce = a * distance + b; // linear approx of 1 / (1 + Math.exp((distance / minimumDistance - 1) * steepness)) | |||||
} | |||||
// amplify the repulsion for clusters. | |||||
repulsingForce *= (clusterSize == 0) ? 1 : 1 + clusterSize * this.constants.clustering.forceAmplification; | |||||
fx = Math.cos(angle) * repulsingForce; | |||||
fy = Math.sin(angle) * repulsingForce ; | |||||
node1._addForce(-fx, -fy); | |||||
node2._addForce(fx, fy); | |||||
} | |||||
} | |||||
} | |||||
}, | |||||
_calculateSpringForces : function(boost) { | |||||
var dx, dy, angle, fx, fy, springForce, length, edgeLength, edge, edgeId, clusterSize; | |||||
var edges = this.edges; | |||||
// forces caused by the edges, modelled as springs | |||||
for (edgeId in edges) { | |||||
if (edges.hasOwnProperty(edgeId)) { | |||||
edge = edges[edgeId]; | |||||
if (edge.connected) { | |||||
// only calculate forces if nodes are in the same sector | |||||
if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { | |||||
clusterSize = (edge.to.clusterSize + edge.from.clusterSize - 2); | |||||
dx = (edge.to.x - edge.from.x); | |||||
dy = (edge.to.y - edge.from.y); | |||||
edgeLength = edge.length; | |||||
// this implies that the edges between big clusters are longer | |||||
edgeLength += clusterSize * this.constants.clustering.edgeGrowth; | |||||
length = Math.sqrt(dx * dx + dy * dy); | |||||
angle = Math.atan2(dy, dx); | |||||
springForce = 0.02 * (edgeLength - length) * boost; | |||||
fx = Math.cos(angle) * springForce; | |||||
fy = Math.sin(angle) * springForce; | |||||
edge.from._addForce(-fx, -fy); | |||||
edge.to._addForce(fx, fy); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
}, | |||||
_calculateBarnesHutForces : function() { | |||||
this._formBarnesHutTree(); | |||||
var nodes = this.nodes; | |||||
var nodeIndices = this.nodeIndices; | |||||
var node; | |||||
var nodeCount = nodeIndices.length; | |||||
var barnesHutTree = this.barnesHutTree; | |||||
this.theta = 0.2; | |||||
this.graviationalConstant = -10000; | |||||
// place the nodes one by one recursively | |||||
for (var i = 0; i < nodeCount; i++) { | |||||
node = nodes[nodeIndices[i]]; | |||||
// starting with root is irrelevant, it never passes the BarnesHut condition | |||||
this._getForceContribution(barnesHutTree.root.children.NW,node); | |||||
this._getForceContribution(barnesHutTree.root.children.NE,node); | |||||
this._getForceContribution(barnesHutTree.root.children.SW,node); | |||||
this._getForceContribution(barnesHutTree.root.children.SE,node); | |||||
} | |||||
}, | |||||
_getForceContribution : function(parentBranch,node) { | |||||
// we get no force contribution from an empty region | |||||
if (parentBranch.childrenCount > 0) { | |||||
var dx,dy,distance; | |||||
// get the distance from the center of mass to the node. | |||||
dx = parentBranch.CenterOfMass.x - node.x; | |||||
dy = parentBranch.CenterOfMass.y - node.y; | |||||
distance = Math.sqrt(dx * dx + dy * dy); | |||||
if (distance > 0) { // distance is 0 if it looks to apply a force on itself. | |||||
// we invert it here because we need the inverted distance for the force calculation too. | |||||
var distanceInv = 1/distance; | |||||
// BarnesHut condition | |||||
if (parentBranch.size * distanceInv > this.theta) { | |||||
// Did not pass the condition, go into children if available | |||||
if (parentBranch.childrenCount == 4) { | |||||
this._getForceContribution(parentBranch.children.NW,node); | |||||
this._getForceContribution(parentBranch.children.NE,node); | |||||
this._getForceContribution(parentBranch.children.SW,node); | |||||
this._getForceContribution(parentBranch.children.SE,node); | |||||
} | |||||
else { // parentBranch must have only one node, if it was empty we wouldnt be here | |||||
this._getForceOnNode(parentBranch, node, dx ,dy, distanceInv); | |||||
} | |||||
} | |||||
else { | |||||
this._getForceOnNode(parentBranch, node, dx ,dy, distanceInv); | |||||
} | |||||
} | |||||
} | |||||
}, | |||||
_getForceOnNode : function(parentBranch, node, dx ,dy, distanceInv) { | |||||
// even if the parentBranch only has one node, its Center of Mass is at the right place (the node in this case). | |||||
var gravityForce = this.graviationalConstant * parentBranch.mass * node.mass * distanceInv * distanceInv; | |||||
var angle = Math.atan2(dy, dx); | |||||
var fx = Math.cos(angle) * gravityForce; | |||||
var fy = Math.sin(angle) * gravityForce; | |||||
node._addForce(fx, fy); | |||||
}, | |||||
_formBarnesHutTree : function() { | |||||
var nodes = this.nodes; | |||||
var nodeIndices = this.nodeIndices; | |||||
var node; | |||||
var nodeCount = nodeIndices.length; | |||||
var minX = Number.MAX_VALUE, | |||||
minY = Number.MAX_VALUE, | |||||
maxX =-Number.MAX_VALUE, | |||||
maxY =-Number.MAX_VALUE; | |||||
// get the range of the nodes | |||||
for (var i = 0; i < nodeCount; i++) { | |||||
var x = nodes[nodeIndices[i]].x; | |||||
var y = nodes[nodeIndices[i]].y; | |||||
if (x < minX) { minX = x; } | |||||
if (x > maxX) { maxX = x; } | |||||
if (y < minY) { minY = y; } | |||||
if (y > maxY) { maxY = y; } | |||||
} | |||||
// make the range a square | |||||
var sizeDiff = Math.abs(maxX - minX) - Math.abs(maxY - minY); // difference between X and Y | |||||
if (sizeDiff > 0) {minY -= 0.5 * sizeDiff; maxY += 0.5 * sizeDiff;} // xSize > ySize | |||||
else {minX += 0.5 * sizeDiff; maxY -= 0.5 * sizeDiff;} // xSize < ySize | |||||
// construct the barnesHutTree | |||||
var barnesHutTree = {root:{ | |||||
CenterOfMass:{x:0,y:0}, // Center of Mass | |||||
mass:0, | |||||
range:{minX:minX,maxX:maxX,minY:minY,maxY:maxY}, | |||||
size: Math.abs(maxX - minX), | |||||
children: {data:null}, | |||||
childrenCount: 4 | |||||
}}; | |||||
this._splitBranch(barnesHutTree.root); | |||||
// place the nodes one by one recursively | |||||
for (i = 0; i < nodeCount; i++) { | |||||
node = nodes[nodeIndices[i]]; | |||||
this._placeInTree(barnesHutTree.root,node); | |||||
} | |||||
// make global | |||||
this.barnesHutTree = barnesHutTree | |||||
}, | |||||
_updateBranchMass : function(parentBranch, node) { | |||||
var totalMass = parentBranch.mass + node.mass; | |||||
var totalMassInv = 1/totalMass; | |||||
parentBranch.CenterOfMass.x = parentBranch.CenterOfMass.x * parentBranch.mass + node.x * node.mass; | |||||
parentBranch.CenterOfMass.x *= totalMassInv; | |||||
parentBranch.CenterOfMass.y = parentBranch.CenterOfMass.y * parentBranch.mass + node.y * node.mass; | |||||
parentBranch.CenterOfMass.y *= totalMassInv; | |||||
parentBranch.mass = totalMass; | |||||
}, | |||||
_placeInTree : function(parentBranch,node) { | |||||
// update the mass of the branch. | |||||
this._updateBranchMass(parentBranch,node); | |||||
if (parentBranch.children.NW.range.maxX > node.x) { // in NW or SW | |||||
if (parentBranch.children.NW.range.maxY > node.y) { // in NW | |||||
this._placeInRegion(parentBranch,node,"NW"); | |||||
} | |||||
else { // in SW | |||||
this._placeInRegion(parentBranch,node,"SW"); | |||||
} | |||||
} | |||||
else { // in NE or SE | |||||
if (parentBranch.children.NE.range.maxY > node.y) { // in NE | |||||
this._placeInRegion(parentBranch,node,"NE"); | |||||
} | |||||
else { // in SE | |||||
this._placeInRegion(parentBranch,node,"SE"); | |||||
} | |||||
} | |||||
}, | |||||
_placeInRegion : function(parentBranch,node,region) { | |||||
switch (parentBranch.children[region].childrenCount) { | |||||
case 0: // place node here | |||||
parentBranch.children[region].children.data = node; | |||||
parentBranch.children[region].childrenCount = 1; | |||||
this._updateBranchMass(parentBranch.children[region],node); | |||||
break; | |||||
case 1: // convert into children | |||||
this._splitBranch(parentBranch.children[region]); | |||||
this._placeInTree(parentBranch.children[region],node); | |||||
break; | |||||
case 4: // place in branch | |||||
this._placeInTree(parentBranch.children[region],node); | |||||
break; | |||||
} | |||||
}, | |||||
_splitBranch : function(parentBranch) { | |||||
// if the branch is filled with a node, replace the node in the new subset. | |||||
var containedNode = null; | |||||
if (parentBranch.childrenCount == 1) { | |||||
containedNode = parentBranch.children.data; | |||||
parentBranch.mass = 0; parentBranch.CenterOfMass.x = 0; parentBranch.CenterOfMass.y = 0; | |||||
} | |||||
parentBranch.childrenCount = 4; | |||||
parentBranch.children.data = null; | |||||
this._insertRegion(parentBranch,"NW"); | |||||
this._insertRegion(parentBranch,"NE"); | |||||
this._insertRegion(parentBranch,"SW"); | |||||
this._insertRegion(parentBranch,"SE"); | |||||
if (containedNode != null) { | |||||
this._placeInTree(parentBranch,containedNode); | |||||
} | |||||
}, | |||||
/** | |||||
* This function subdivides the region into four new segments. | |||||
* Specifically, this inserts a single new segment. | |||||
* It fills the children section of the parentBranch | |||||
* | |||||
* @param parentBranch | |||||
* @param region | |||||
* @param parentRange | |||||
* @private | |||||
*/ | |||||
_insertRegion : function(parentBranch, region) { | |||||
var minX,maxX,minY,maxY; | |||||
switch (region) { | |||||
case "NW": | |||||
minX = parentBranch.range.minX; | |||||
maxX = parentBranch.range.minX + parentBranch.size; | |||||
minY = parentBranch.range.minY; | |||||
maxY = parentBranch.range.minY + parentBranch.size; | |||||
break; | |||||
case "NE": | |||||
minX = parentBranch.range.minX + parentBranch.size; | |||||
maxX = parentBranch.range.maxX; | |||||
minY = parentBranch.range.minY; | |||||
maxY = parentBranch.range.minY + parentBranch.size; | |||||
break; | |||||
case "SW": | |||||
minX = parentBranch.range.minX; | |||||
maxX = parentBranch.range.minX + parentBranch.size; | |||||
minY = parentBranch.range.minY + parentBranch.size; | |||||
maxY = parentBranch.range.maxY; | |||||
break; | |||||
case "SE": | |||||
minX = parentBranch.range.minX + parentBranch.size; | |||||
maxX = parentBranch.range.maxX; | |||||
minY = parentBranch.range.minY + parentBranch.size; | |||||
maxY = parentBranch.range.maxY; | |||||
break; | |||||
} | |||||
parentBranch.children[region] = { | |||||
CenterOfMass:{x:0,y:0}, | |||||
mass:0, | |||||
range:{minX:minX,maxX:maxX,minY:minY,maxY:maxY}, | |||||
size: 0.5 * parentBranch.size, | |||||
children: {data:null}, | |||||
childrenCount: 0 | |||||
}; | |||||
}, | |||||
_drawTree : function(ctx,color) { | |||||
if (this.barnesHutTree !== undefined) { | |||||
ctx.lineWidth = 2; | |||||
this._drawBranch(this.barnesHutTree.root,ctx,color); | |||||
} | |||||
}, | |||||
_drawBranch : function(branch,ctx,color) { | |||||
if (color === undefined) { | |||||
color = "#FF0000"; | |||||
} | |||||
if (branch.childrenCount == 4) { | |||||
this._drawBranch(branch.children.NW,ctx); | |||||
this._drawBranch(branch.children.NE,ctx); | |||||
this._drawBranch(branch.children.SE,ctx); | |||||
this._drawBranch(branch.children.SW,ctx); | |||||
} | |||||
ctx.strokeStyle = color; | |||||
ctx.beginPath(); | |||||
ctx.moveTo(branch.range.minX,branch.range.minY); | |||||
ctx.lineTo(branch.range.maxX,branch.range.minY); | |||||
ctx.stroke(); | |||||
ctx.beginPath(); | |||||
ctx.moveTo(branch.range.maxX,branch.range.minY); | |||||
ctx.lineTo(branch.range.maxX,branch.range.maxY); | |||||
ctx.stroke(); | |||||
ctx.beginPath(); | |||||
ctx.moveTo(branch.range.maxX,branch.range.maxY); | |||||
ctx.lineTo(branch.range.minX,branch.range.maxY); | |||||
ctx.stroke(); | |||||
ctx.beginPath(); | |||||
ctx.moveTo(branch.range.minX,branch.range.maxY); | |||||
ctx.lineTo(branch.range.minX,branch.range.minY); | |||||
ctx.stroke(); | |||||
/* | |||||
if (branch.mass > 0) { | |||||
ctx.circle(branch.CenterOfMass.x, branch.CenterOfMass.y, 3*branch.mass); | |||||
ctx.stroke(); | |||||
} | |||||
*/ | |||||
} | |||||
}; | |||||
function echo() { | |||||
switch (arguments.length) { | |||||
case 1: | |||||
echoN1(arguments[0]); break; | |||||
case 2: | |||||
echoN2(arguments[0],arguments[1]); break; | |||||
case 3: | |||||
echoN3(arguments[0],arguments[1],arguments[2]); break; | |||||
} | |||||
} | |||||
function echoN1(message) { | |||||
console.log(message); | |||||
} | |||||
function echoN2(message1,message2) { | |||||
console.log(message1,message2); | |||||
} | |||||
function echoN3(message1,message2,message3) { | |||||
console.log(message1,message2,message3); | |||||
} |