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