@ -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); | |||
} |