├── .gitignore ├── LICENSE ├── README.md ├── examples ├── data │ ├── eastwestcommute.json │ ├── json │ │ ├── eastwestcommute.json │ │ ├── philippines.json │ │ ├── sfcommute.json │ │ ├── test.json │ │ ├── testcrossed.json │ │ ├── testuneven.json │ │ └── world.json │ ├── philippines.json │ ├── sfcommute.json │ ├── test.json │ ├── testcrossed.json │ ├── testuneven.json │ └── world.json ├── ier │ ├── filter.svg │ ├── images │ │ ├── philippines.jpg │ │ └── world.jpg │ ├── index.html │ ├── index.js │ ├── loader.js │ ├── spectrum.js │ └── styles │ │ └── main.css ├── sf │ ├── example.css │ ├── example.js │ ├── filter.svg │ ├── index.html │ └── sample.json └── test │ ├── example.css │ ├── example.js │ ├── filter.svg │ ├── index.html │ └── sample.json ├── graph.js ├── img ├── easteurope1.png ├── easteurope2.png ├── easteurope3.png └── sfcommute.png ├── lib ├── kdtree.js └── philogl.js ├── mingle.js └── screen.png /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Twitter - Author: Nicolas Garcia Belmonte 2 | (http://philogb.github.io/) 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Multilevel Agglomerative Edge Bundling in JavaScript 2 | 3 | This is a JavaScript implementation of the paper [Multilevel Agglomerative Edge Bundling 4 | for Visualizing Large Graphs](http://yifanhu.net/PUB/edge_bundling.pdf) 5 | (Emden R. Gansner, Yifan Hu, Stephen North, Carlos Scheidegger). 6 | 7 | The edge bundling algorithm groups edges together to minimize the amount of 8 | ink used to render a graph. This particular paper introduces a fast 9 | technique to perform edge bundling. 10 | 11 | Take for example this map connecting locations between the east coast in 12 | the US and western Europe: 13 | 14 | ![easteurope image 1](https://raw.github.com/philogb/mingle/master/img/easteurope1.png) 15 | 16 | The algorithm creates a proximity graph for the edges where each of the 17 | edges is represented by a node. Then the algorithm bundles edges as long 18 | as we're saving some ink in the final rendering. Here's an intermediate 19 | step on the bundling animation: 20 | 21 | ![easteurope image 1](https://raw.github.com/philogb/mingle/master/img/easteurope2.png) 22 | 23 | And here's the final result: 24 | 25 | ![easteurope image 1](https://raw.github.com/philogb/mingle/master/img/easteurope3.png) 26 | 27 | 28 | This implementation is solely based on the paper. The license for the code is MIT. 29 | 30 | ## Examples 31 | 32 | This simple example shows links connecting locations in the Bay Area. 33 | The rendering uses 2D Canvas but 34 | could use any other rendering API. 35 | 36 | [You can see an example here](http://philogb.github.io/mingle/examples/sf). 37 | 38 | ![Image of Edge bundling example](https://raw.github.com/philogb/mingle/master/img/sfcommute.png) 39 | 40 | ## Usage 41 | 42 | Given a dataset consisting of an Array of elements with format: 43 | 44 | { 45 | "id": , 46 | "name": , 47 | "data": { 48 | "coords": [ 49 | , 50 | , 51 | , 52 | 53 | ] 54 | } 55 | } 56 | 57 | Then the code to create the bundled edges graph is: 58 | 59 | var bundle = new Bundler(); 60 | bundle.setNodes(json); 61 | bundle.buildNearestNeighborGraph(); 62 | bundle.MINGLE(); 63 | 64 | Finally, to render the graph we provide some helper functions that make 65 | use of 2D canvas: 66 | 67 | bundle.graph.each(function(node) { 68 | ctx.strokeStyle = 'rgba(0, 200, 200, 0.2)'; 69 | ctx.lineWidth = 2; 70 | var edges = node.unbundleEdges(delta); 71 | Bundler.Graph.renderBezier(ctx, edges); 72 | }); 73 | 74 | 75 | 76 | ## External libraries used 77 | 78 | * kdtree implementation by Ubilabs can be found [here](https://github.com/ubilabs/kd-tree-javascript). 79 | MIT license. 80 | * [PhiloGL](http://senchalabs.org/philogl) is used to perform 81 | animations between unbundled and bundled edges. This library is not 82 | required to use the algorithm. MIT license. 83 | 84 | ## TODO 85 | 86 | Provide a more complete documentation and API reference. 87 | 88 | ## Copyright Twitter, Inc. 89 | 90 | The code is copyright Twitter, Inc. with an MIT license. 91 | 92 | Copyright (c) 2013 Twitter - Author: Nicolas Garcia Belmonte 93 | (http://philogb.github.io/) 94 | 95 | Permission is hereby granted, free of charge, to any person obtaining a copy 96 | of this software and associated documentation files (the "Software"), to deal 97 | in the Software without restriction, including without limitation the rights 98 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 99 | copies of the Software, and to permit persons to whom the Software is 100 | furnished to do so, subject to the following conditions: 101 | 102 | The above copyright notice and this permission notice shall be included in 103 | all copies or substantial portions of the Software. 104 | 105 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 106 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 107 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 108 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 109 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 110 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 111 | THE SOFTWARE. 112 | 113 | -------------------------------------------------------------------------------- /examples/data/eastwestcommute.json: -------------------------------------------------------------------------------- 1 | [{"id":"4:-9:4:-1","name":"4:-9:4:-1","data":{"weight":2.772588722239781,"coords":[9.079604132231431,212.58418595041323,706.1853297520662,224.78044049586777]}},{"id":"4:-9:5:0","name":"4:-9:5:0","data":{"weight":4.343805421853684,"coords":[11.315778901946995,213.36485804975155,791.4579943978621,135.70991996170798]}},{"id":"4:-9:5:-1","name":"4:-9:5:-1","data":{"weight":5.043425116919247,"coords":[9.10065538389583,212.6021335224471,716.7953140495868,118.49962809917358]}},{"id":"4:-8:4:0","name":"4:-8:4:0","data":{"weight":6.352629396319567,"coords":[124.00996121604001,222.90412687779974,806.2644763699923,167.2654157291581]}},{"id":"3:-8:4:0","name":"3:-8:4:0","data":{"weight":4.454347296253507,"coords":[113.65814154033986,229.95386949152083,753.6332725696216,217.2126952666822]}},{"id":"4:-8:5:0","name":"4:-8:5:0","data":{"weight":5.950642552587727,"coords":[148.62250887685948,209.36774277685953,777.7110847956199,129.05182305123967]}},{"id":"2:-9:5:0","name":"2:-9:5:0","data":{"weight":4.624972813284271,"coords":[73.21536538719346,343.7809360334293,806.3804039285125,145.86467667024792]}},{"id":"4:-8:5:-1","name":"4:-8:5:-1","data":{"weight":6.985641817639208,"coords":[148.62250887657984,209.36774277649462,731.501374362914,134.11989192073062]}},{"id":"3:-9:4:0","name":"3:-9:4:0","data":{"weight":4.248495242049359,"coords":[35.5047348760333,236.86054685950427,754.5105797520662,203.21666404958694]}},{"id":"2:-9:5:-1","name":"2:-9:5:-1","data":{"weight":5.846438775057725,"coords":[73.44602007816171,343.4904028248179,716.8649634918401,118.16943326033905]}},{"id":"4:-9:5:1","name":"4:-9:5:1","data":{"weight":3.4011973816621555,"coords":[11.374715931404983,213.39197715867772,842.9741583578513,99.92865584876034]}},{"id":"3:-8:5:0","name":"3:-8:5:0","data":{"weight":4.59511985013459,"coords":[98.76321487603308,237.76787603305786,792.6046670960986,138.78615084943306]}},{"id":"4:-8:6:-3","name":"4:-8:6:-3","data":{"weight":3.713572066704308,"coords":[125.76603305785127,223.60305785123967,548.6875206611569,30.742644628099185]}},{"id":"4:-8:5:1","name":"4:-8:5:1","data":{"weight":5.19295685089021,"coords":[125.68557983941945,223.62096739296317,839.5266136920238,99.3141632065329]}},{"id":"4:-8:4:1","name":"4:-8:4:1","data":{"weight":5.337538079701318,"coords":[123.93200006776866,222.95891815619834,842.8863915818182,164.52633906115705]}},{"id":"3:-9:5:-1","name":"3:-9:5:-1","data":{"weight":4.787491742782046,"coords":[20.644873626961196,235.76933807775518,731.8560466293471,134.20937787190084]}},{"id":"4:-8:5:2","name":"4:-8:5:2","data":{"weight":3.1780538303479458,"coords":[123.92401892314045,222.7515546487603,940.4887709735536,68.45844393140499]}},{"id":"3:-8:5:-1","name":"3:-8:5:-1","data":{"weight":5.10594547390058,"coords":[98.90813072975203,237.9312428561983,731.6685702479339,134.11809917355373]}},{"id":"3:-8:4:1","name":"3:-8:4:1","data":{"weight":3.970291913552122,"coords":[113.62210188390215,229.9679533532303,838.5704275167225,213.3948976875662]}},{"id":"4:-9:4:0","name":"4:-9:4:0","data":{"weight":4.3694478524670215,"coords":[10.765380689295135,213.0356028744042,753.2040072633039,217.71632582559613]}},{"id":"4:-8:4:-1","name":"4:-8:4:-1","data":{"weight":4.90527477843843,"coords":[122.50327669449543,223.23196718990695,711.2888679031406,201.91080821487603]}},{"id":"3:-8:3:-1","name":"3:-8:3:-1","data":{"weight":1.791759469228055,"coords":[98.36616654232259,238.1358429768451,705.8164214876033,252.24744628099177]}},{"id":"2:-9:4:-1","name":"2:-9:4:-1","data":{"weight":3.4339872044851463,"coords":[72.0689669421487,346.3219338842975,706.0536392464126,224.84034394206822]}},{"id":"3:-9:4:-1","name":"3:-9:4:-1","data":{"weight":2.6390573296152584,"coords":[17.68513356363637,262.54478352975207,706.0156893139043,224.9668559932025]}},{"id":"4:-8:4:2","name":"4:-8:4:2","data":{"weight":5.337538079701318,"coords":[148.60525262122903,209.33540426966553,973.7656182771349,220.845414615478]}},{"id":"4:-8:5:3","name":"4:-8:5:3","data":{"weight":3.295836866004329,"coords":[79.93572102314045,198.14059029834712,987.7793966247933,142.6298246140496]}},{"id":"3:-9:5:0","name":"3:-9:5:0","data":{"weight":4.060443010546419,"coords":[37.19646273719006,245.04191936446284,735.6357889727124,139.05753418181817]}},{"id":"2:-9:4:1","name":"2:-9:4:1","data":{"weight":3.4339872044851463,"coords":[68.63097978867292,324.7736423609102,849.2563965338843,182.24131752561982]}},{"id":"2:-9:5:1","name":"2:-9:5:1","data":{"weight":3.8501476017100584,"coords":[72.10108071319328,346.3224453323887,845.374225585124,125.17047778099177]}},{"id":"3:-8:4:2","name":"3:-8:4:2","data":{"weight":4.276666119016055,"coords":[98.90989656682771,237.97387681273597,973.7394328278668,220.84430619194924]}},{"id":"4:-8:3:-1","name":"4:-8:3:-1","data":{"weight":4.110873864173311,"coords":[124.32568229548474,222.5709368868264,669.5136363636364,298.0913388429752]}},{"id":"2:-9:6:2","name":"2:-9:6:2","data":{"weight":1.6094379124341003,"coords":[72.10108071319328,346.3224453323887,940.7966012051293,62.11888463073535]}},{"id":"3:-9:5:1","name":"3:-9:5:1","data":{"weight":3.1780538303479458,"coords":[37.71844186073493,281.4820734310422,853.4522411251857,145.39455866137166]}},{"id":"2:-9:4:2","name":"2:-9:4:2","data":{"weight":3.1354942159291497,"coords":[63.56686797023805,324.53799973223374,973.6994396556507,220.83933784197853]}},{"id":"4:-9:4:1","name":"4:-9:4:1","data":{"weight":3.912023005428146,"coords":[9.05453863222737,212.58453785089566,832.9876513520549,159.8824440177292]}},{"id":"2:-9:3:-3","name":"2:-9:3:-3","data":{"weight":0,"coords":[73.43710743801655,343.9200826446281,524.0742148760331,247.52768595041323]}},{"id":"3:-9:3:-1","name":"3:-9:3:-1","data":{"weight":2.6390573296152584,"coords":[37.71844186033056,281.48207343140496,686.0074740795868,250.53105723305785]}},{"id":"3:-8:4:-1","name":"3:-8:4:-1","data":{"weight":2.4849066497880004,"coords":[113.69881748167937,229.94208716725268,706.0156893139043,224.9668559932025]}},{"id":"3:-8:3:2","name":"3:-8:3:2","data":{"weight":2.0794415416798357,"coords":[95.64188908884228,237.6395556774414,931.9996951318523,246.87655173694924]}},{"id":"2:-9:6:-3","name":"2:-9:6:-3","data":{"weight":1.3862943611198906,"coords":[72.77606878883681,346.216621841245,548.5780309176482,30.601603886676628]}},{"id":"4:-8:5:-2","name":"4:-8:5:-2","data":{"weight":0.6931471805599453,"coords":[148.62250887657984,209.36774277649462,652.4425004132231,113.25434793388445]}},{"id":"3:-8:5:1","name":"3:-8:5:1","data":{"weight":3.2188758248682006,"coords":[114.31630189753761,229.3229361731169,824.3966910464704,64.40001750211059]}},{"id":"3:-9:5:3","name":"3:-9:5:3","data":{"weight":1.0986122886681098,"coords":[26.933481702288713,243.93488528144655,986.0014443116357,65.23886125359871]}},{"id":"3:-8:4:3","name":"3:-8:4:3","data":{"weight":1.0986122886681098,"coords":[95.70881201532438,236.95045827083283,989.0633988971553,175.6959635517778]}},{"id":"3:-9:4:1","name":"3:-9:4:1","data":{"weight":3.713572066704308,"coords":[39.703627739777446,277.84547371963697,838.6173606060925,213.21860513170333]}},{"id":"4:-9:3:2","name":"4:-9:3:2","data":{"weight":2.302585092994046,"coords":[11.320957467576058,213.39898227660083,925.4355205785124,245.62909190082644]}},{"id":"4:-8:3:0","name":"4:-8:3:0","data":{"weight":1.0986122886681098,"coords":[124.07775757391225,222.74550262243545,810.9188488106423,235.356721621128]}},{"id":"4:-9:5:2","name":"4:-9:5:2","data":{"weight":2.6390573296152584,"coords":[11.367445149697549,213.29962636699165,910.3863613664612,99.23394208672585]}},{"id":"4:-9:3:-1","name":"4:-9:3:-1","data":{"weight":2.4849066497880004,"coords":[9.054538632231363,212.58453785123965,658.1239385052892,234.29563290495867]}},{"id":"3:-8:6:-3","name":"3:-8:6:-3","data":{"weight":1.6094379124341003,"coords":[95.47185503746853,237.58153525214635,566.5112054052431,29.113177075284625]}},{"id":"4:-8:6:2","name":"4:-8:6:2","data":{"weight":3.332204510175204,"coords":[125.74774371690987,223.59430284317068,941.8855146928267,61.003503772561885]}},{"id":"2:-9:4:0","name":"2:-9:4:0","data":{"weight":4.51085950651685,"coords":[61.53411707603302,325.05093074958677,758.5203076196694,155.62478550330576]}},{"id":"4:-9:3:3","name":"4:-9:3:3","data":{"weight":1.791759469228055,"coords":[9.054538632231363,212.58453785123965,994.3621218490367,310.9166163366114]}},{"id":"3:-9:6:1","name":"3:-9:6:1","data":{"weight":1.0986122886681098,"coords":[67.84319008264458,268.62243214876037,827.2651916694215,62.034192744628115]}},{"id":"4:-9:6:-3","name":"4:-9:6:-3","data":{"weight":0.6931471805599453,"coords":[10.639710997687672,212.98266639208003,554.3022722322314,29.374953037190096]}},{"id":"2:-9:5:3","name":"2:-9:5:3","data":{"weight":2.3978952727983707,"coords":[62.98151652892558,323.6501198347107,986.8594876033059,64.39652892561986]}},{"id":"4:-8:3:2","name":"4:-8:3:2","data":{"weight":3.6109179126442243,"coords":[123.79143652148757,223.7584641355372,945.2961983471075,249.87975206611566]}},{"id":"4:-8:3:3","name":"4:-8:3:3","data":{"weight":2.302585092994046,"coords":[125.74774371690987,223.59430284317068,993.5202883821662,311.02444873748516]}},{"id":"2:-8:5:-1","name":"2:-8:5:-1","data":{"weight":1.791759469228055,"coords":[96.5071818181818,352.23795041322313,684.394694214876,86.42406611570249]}},{"id":"2:-9:3:-1","name":"2:-9:3:-1","data":{"weight":2.772588722239781,"coords":[73.67564823966943,341.69614284793386,659.9421797709917,239.40794519090912]}},{"id":"3:-9:3:-3","name":"3:-9:3:-3","data":{"weight":0,"coords":[66.575518053719,268.4318858132232,523.0471831710744,247.52401221074382]}},{"id":"4:-8:3:1","name":"4:-8:3:1","data":{"weight":2.3978952727983707,"coords":[124.28806483140491,223.02357079999996,860.2606358736109,249.55983730578572]}},{"id":"3:-9:6:-3","name":"3:-9:6:-3","data":{"weight":1.3862943611198906,"coords":[13.743142992562028,261.80479073305787,554.217998646281,29.35748002314051]}},{"id":"2:-8:5:1","name":"2:-8:5:1","data":{"weight":0,"coords":[91.44851418264466,346.1175529752066,846.2853238272727,125.46689034132231]}},{"id":"3:-9:4:2","name":"3:-9:4:2","data":{"weight":2.9444389791664403,"coords":[65.86264462809918,278.4972644628099,973.699439655372,220.83933784214878]}},{"id":"4:-7:5:-1","name":"4:-7:5:-1","data":{"weight":1.6094379124341003,"coords":[157.1978429752066,196.5861487603306,731.8376036714916,134.21667025563377]}},{"id":"4:-7:5:0","name":"4:-7:5:0","data":{"weight":1.6094379124341003,"coords":[185.70473568498596,180.38798394048948,777.5518322872728,127.47360322892567]}},{"id":"4:-7:6:-3","name":"4:-7:6:-3","data":{"weight":0,"coords":[209.99675705412324,190.4807521343938,554.2129686958677,29.35059123223141]}},{"id":"3:-9:3:2","name":"3:-9:3:2","data":{"weight":1.9459101490553132,"coords":[65.45048257438019,244.87761551157024,926.1512396694216,237.6909090909091]}},{"id":"3:-8:6:-2","name":"3:-8:6:-2","data":{"weight":0,"coords":[114.46490957717265,229.26366815130424,595.9342726892562,30.65969452561984]}},{"id":"4:-7:6:-2","name":"4:-7:6:-2","data":{"weight":0,"coords":[211.0398268059032,190.7804386070193,595.3139628264463,30.440372965289328]}},{"id":"2:-9:6:1","name":"2:-9:6:1","data":{"weight":1.0986122886681098,"coords":[62.29485537190083,324.6495727272727,821.5031132231405,35.386095041322335]}},{"id":"3:-9:6:2","name":"3:-9:6:2","data":{"weight":1.6094379124341003,"coords":[18.220008264462834,260.7363140495868,941.9173707532383,61.0275083103507]}},{"id":"3:-7:5:-1","name":"3:-7:5:-1","data":{"weight":1.9459101490553132,"coords":[200.04286797807487,292.5232294792301,734.7195368136988,134.730144201821]}},{"id":"3:-8:3:3","name":"3:-8:3:3","data":{"weight":1.0986122886681098,"coords":[100.33584445619834,236.45006900578514,991.5173306570248,311.5244985834711]}},{"id":"4:-8:6:1","name":"4:-8:6:1","data":{"weight":2.0794415416798357,"coords":[127.32899736956628,222.1516299250758,827.304366175025,62.01274670493609]}},{"id":"4:-9:4:2","name":"4:-9:4:2","data":{"weight":3.1354942159291497,"coords":[9.05453863222737,212.58453785089566,974.7127383208472,220.2485735063972]}},{"id":"3:-8:3:1","name":"3:-8:3:1","data":{"weight":1.3862943611198906,"coords":[104.26373846071542,254.61150580472852,823.3225778638903,258.6863681363327]}},{"id":"4:-7:5:1","name":"4:-7:5:1","data":{"weight":1.3862943611198906,"coords":[166.8085066740178,189.18836121395162,836.5464328734342,134.53280898013074]}},{"id":"4:-9:4:3","name":"4:-9:4:3","data":{"weight":0,"coords":[3.7640933884297274,206.83642396694214,989.5256198347108,175.33402479338844]}},{"id":"2:-8:5:0","name":"2:-8:5:0","data":{"weight":0,"coords":[96.2849412082644,352.2411871661157,806.3804039285125,145.86467667024792]}},{"id":"2:-9:3:2","name":"2:-9:3:2","data":{"weight":0.6931471805599453,"coords":[54.17104824049584,328.5619209024793,982.5217349471076,301.5428487363636]}},{"id":"4:-7:5:-2","name":"4:-7:5:-2","data":{"weight":0,"coords":[213.68548581726765,181.47226329028487,587.1305002284154,98.97353670555486]}},{"id":"4:-7:3:2","name":"4:-7:3:2","data":{"weight":0,"coords":[210.79401256478886,211.5736348717641,933.3242222653823,311.32270917824985]}},{"id":"4:-9:6:2","name":"4:-9:6:2","data":{"weight":0.6931471805599453,"coords":[61.79196018653015,220.03137766841326,942.0945495537916,61.075268443184484]}},{"id":"4:-9:5:3","name":"4:-9:5:3","data":{"weight":0,"coords":[46.61326763058497,210.67149887092165,987.7182336793384,142.42391659321703]}},{"id":"4:-9:3:1","name":"4:-9:3:1","data":{"weight":0.6931471805599453,"coords":[11.082396694214864,212.36694214876033,869.6201652892563,234.25578512396694]}},{"id":"3:-9:3:0","name":"3:-9:3:0","data":{"weight":1.9459101490553132,"coords":[59.42849137247713,309.1956380651949,758.0384866280216,232.83721759067194]}},{"id":"4:-8:3:-2","name":"4:-8:3:-2","data":{"weight":0,"coords":[125.75746556446283,223.5550275479339,595.7845974859504,289.69862748181816]}},{"id":"2:-9:3:3","name":"2:-9:3:3","data":{"weight":0.6931471805599453,"coords":[73.48837861759692,343.686130253698,988.7949249746848,254.76400042712038]}},{"id":"3:-8:6:-1","name":"3:-8:6:-1","data":{"weight":0,"coords":[98.87486658853294,237.9822968747594,681.2745432526664,33.6688332495774]}},{"id":"3:-7:5:0","name":"3:-7:5:0","data":{"weight":0,"coords":[200.06619714253372,292.6193858049818,772.6330232829939,138.8353142821861]}},{"id":"3:-8:5:2","name":"3:-8:5:2","data":{"weight":0.6931471805599453,"coords":[98.87219834710749,237.9567685950413,940.1121900826446,68.2895785123967]}},{"id":"2:-8:4:1","name":"2:-8:4:1","data":{"weight":0,"coords":[96.36165289256203,352.33701652892563,876.9173553719008,161.57653719008263]}},{"id":"2:-9:3:0","name":"2:-9:3:0","data":{"weight":0,"coords":[73.33035732208978,346.3014960017157,747.4377254593828,237.86604242631074]}},{"id":"3:-9:3:3","name":"3:-9:3:3","data":{"weight":0.6931471805599453,"coords":[37.71844186073493,281.4820734310422,993.7112784578262,311.2047137525815]}},{"id":"4:-7:3:1","name":"4:-7:3:1","data":{"weight":0,"coords":[164.26523305785125,194.8675115702479,859.6253090909091,245.25943884297521]}},{"id":"2:-9:6:-2","name":"2:-9:6:-2","data":{"weight":0,"coords":[54.0729090909091,328.5076446280992,578.3017768595041,22.602024793388466]}},{"id":"3:-7:3:-1","name":"3:-7:3:-1","data":{"weight":0,"coords":[200.22273507024792,292.54135662809915,660.0163810509091,239.56019316776855]}},{"id":"2:-9:5:2","name":"2:-9:5:2","data":{"weight":0,"coords":[72.06869476943943,346.3219177648473,909.4735880376829,127.77298656180588]}},{"id":"3:-8:6:1","name":"3:-8:6:1","data":{"weight":0.6931471805599453,"coords":[98.79583957788637,237.88962981476067,827.2853493061857,62.0359538660457]}},{"id":"4:-9:3:0","name":"4:-9:3:0","data":{"weight":0.6931471805599453,"coords":[44.998247933884436,215.17406363636366,760.4757950413225,231.44410826446278]}},{"id":"4:-7:4:0","name":"4:-7:4:0","data":{"weight":1.6094379124341003,"coords":[238.2615238178719,178.36796203625596,811.4860082644628,183.7418917355372]}},{"id":"3:-8:5:3","name":"3:-8:5:3","data":{"weight":0.6931471805599453,"coords":[95.53097034079421,238.11887846863527,990.8884537121481,143.37615613359216]}},{"id":"4:-8:6:0","name":"4:-8:6:0","data":{"weight":0.6931471805599453,"coords":[123.07107992644623,222.5861663289256,787.557314876033,43.28370826446286]}},{"id":"3:-8:3:0","name":"3:-8:3:0","data":{"weight":1.0986122886681098,"coords":[97.07408700082645,237.2222565619835,757.1843569436363,232.53064849752067]}},{"id":"4:-8:3:-3","name":"4:-8:3:-3","data":{"weight":1.0986122886681098,"coords":[146.07542999504133,206.91071494380162,523.4010849743802,247.60030566859507]}},{"id":"4:-9:6:1","name":"4:-9:6:1","data":{"weight":1.0986122886681098,"coords":[69.13075320248369,220.47456370204526,877.1109001410283,58.19273883046105]}},{"id":"3:-8:6:2","name":"3:-8:6:2","data":{"weight":0.6931471805599453,"coords":[95.57683802809916,249.24118302396695,903.0372909440028,31.971115354395863]}},{"id":"3:-7:3:-2","name":"3:-7:3:-2","data":{"weight":0,"coords":[163.2802727272727,310.9920909090909,602.2889421487603,264.2568512396694]}},{"id":"3:-9:4:-2","name":"3:-9:4:-2","data":{"weight":0,"coords":[51.02296776859507,239.2419479338843,623.3408016528927,156.81340743801653]}},{"id":"3:-6:5:0","name":"3:-6:5:0","data":{"weight":0,"coords":[248.41665093374056,275.5539448301505,792.2282410087232,139.30143270639593]}},{"id":"2:-8:3:0","name":"2:-8:3:0","data":{"weight":0,"coords":[94.92864049090905,352.8770908413223,756.2146370280991,232.9850725818182]}},{"id":"2:-9:6:0","name":"2:-9:6:0","data":{"weight":0,"coords":[74.10329214447225,338.74148794975326,779.5793233762963,60.42390053519193]}},{"id":"3:-7:4:1","name":"3:-7:4:1","data":{"weight":0,"coords":[199.77735754115804,292.9439335091461,819.1147454022168,183.05598858031524]}},{"id":"4:-7:4:2","name":"4:-7:4:2","data":{"weight":1.0986122886681098,"coords":[157.29538677685946,196.59048181818181,977.2629479338842,221.58354462809916]}},{"id":"2:-8:4:-1","name":"2:-8:4:-1","data":{"weight":0,"coords":[86.95950413223136,322.15289256198344,675.2710743801653,209.42396694214878]}},{"id":"2:-9:4:-3","name":"2:-9:4:-3","data":{"weight":0,"coords":[55.35537190082639,313.9190082644628,563.5413223140496,172.96363636363637]}},{"id":"4:-8:6:-2","name":"4:-8:6:-2","data":{"weight":0,"coords":[124.17322314049582,222.4931404958678,585.9472510839917,16.701764842707803]}},{"id":"4:-8:4:3","name":"4:-8:4:3","data":{"weight":1.0986122886681098,"coords":[147.74407032891824,209.32187054502512,986.8815472459621,222.5172274134809]}},{"id":"3:-9:5:2","name":"3:-9:5:2","data":{"weight":1.791759469228055,"coords":[35.915836540863815,236.77435679625262,932.7163627707307,88.63928198296873]}},{"id":"4:-7:3:-1","name":"4:-7:3:-1","data":{"weight":0.6931471805599453,"coords":[213.7855371900826,177.12156198347108,710.8404132231406,229.7942975206612]}},{"id":"4:-7:4:1","name":"4:-7:4:1","data":{"weight":1.0986122886681098,"coords":[184.7348842975206,179.6684214876033,855.3971900826446,178.91538016528924]}},{"id":"3:-9:3:1","name":"3:-9:3:1","data":{"weight":0,"coords":[60.69586776859505,308.83156198347103,855.4752066115702,262.8280991735537]}},{"id":"4:-9:6:-1","name":"4:-9:6:-1","data":{"weight":0,"coords":[49.19074380165286,209.65553719008264,679.4517355371901,46.94315702479338]}},{"id":"3:-9:6:-1","name":"3:-9:6:-1","data":{"weight":0,"coords":[26.109090909090895,296.58760330578514,707.9528925619835,26.611570247933855]}},{"id":"3:-9:4:3","name":"3:-9:4:3","data":{"weight":0,"coords":[18.086768595041264,282.4769421487603,989.5870247933884,175.45885123966943]}},{"id":"3:-6:4:0","name":"3:-6:4:0","data":{"weight":0,"coords":[240.46872396694218,262.13262231404957,758.9174041322315,151.08776280991736]}},{"id":"3:-7:6:3","name":"3:-7:6:3","data":{"weight":0,"coords":[222.59338842975205,309.57355371900826,988.1123966942149,43.71900826446284]}},{"id":"4:-9:3:-2","name":"4:-9:3:-2","data":{"weight":0,"coords":[23.896875206611575,215.04674876033056,595.4908603305785,251.19256115702478]}}] 2 | -------------------------------------------------------------------------------- /examples/data/json/eastwestcommute.json: -------------------------------------------------------------------------------- 1 | [{"id":"4:-9:4:-1","name":"4:-9:4:-1","data":{"weight":1,"coords":[9.079604132231431,212.58418595041323,706.1853297520662,224.78044049586777]}},{"id":"4:-9:5:0","name":"4:-9:5:0","data":{"weight":1,"coords":[11.315778901946995,213.36485804975155,791.4579943978621,135.70991996170798]}},{"id":"4:-9:5:-1","name":"4:-9:5:-1","data":{"weight":1,"coords":[9.10065538389583,212.6021335224471,716.7953140495868,118.49962809917358]}},{"id":"4:-8:4:0","name":"4:-8:4:0","data":{"weight":1,"coords":[124.00996121604001,222.90412687779974,806.2644763699923,167.2654157291581]}},{"id":"3:-8:4:0","name":"3:-8:4:0","data":{"weight":1,"coords":[113.65814154033986,229.95386949152083,753.6332725696216,217.2126952666822]}},{"id":"4:-8:5:0","name":"4:-8:5:0","data":{"weight":1,"coords":[148.62250887685948,209.36774277685953,777.7110847956199,129.05182305123967]}},{"id":"2:-9:5:0","name":"2:-9:5:0","data":{"weight":1,"coords":[73.21536538719346,343.7809360334293,806.3804039285125,145.86467667024792]}},{"id":"4:-8:5:-1","name":"4:-8:5:-1","data":{"weight":1,"coords":[148.62250887657984,209.36774277649462,731.501374362914,134.11989192073062]}},{"id":"3:-9:4:0","name":"3:-9:4:0","data":{"weight":1,"coords":[35.5047348760333,236.86054685950427,754.5105797520662,203.21666404958694]}},{"id":"2:-9:5:-1","name":"2:-9:5:-1","data":{"weight":1,"coords":[73.44602007816171,343.4904028248179,716.8649634918401,118.16943326033905]}},{"id":"4:-9:5:1","name":"4:-9:5:1","data":{"weight":1,"coords":[11.374715931404983,213.39197715867772,842.9741583578513,99.92865584876034]}},{"id":"3:-8:5:0","name":"3:-8:5:0","data":{"weight":1,"coords":[98.76321487603308,237.76787603305786,792.6046670960986,138.78615084943306]}},{"id":"4:-8:6:-3","name":"4:-8:6:-3","data":{"weight":1,"coords":[125.76603305785127,223.60305785123967,548.6875206611569,30.742644628099185]}},{"id":"4:-8:5:1","name":"4:-8:5:1","data":{"weight":1,"coords":[125.68557983941945,223.62096739296317,839.5266136920238,99.3141632065329]}},{"id":"4:-8:4:1","name":"4:-8:4:1","data":{"weight":1,"coords":[123.93200006776866,222.95891815619834,842.8863915818182,164.52633906115705]}},{"id":"3:-9:5:-1","name":"3:-9:5:-1","data":{"weight":1,"coords":[20.644873626961196,235.76933807775518,731.8560466293471,134.20937787190084]}},{"id":"4:-8:5:2","name":"4:-8:5:2","data":{"weight":1,"coords":[123.92401892314045,222.7515546487603,940.4887709735536,68.45844393140499]}},{"id":"3:-8:5:-1","name":"3:-8:5:-1","data":{"weight":1,"coords":[98.90813072975203,237.9312428561983,731.6685702479339,134.11809917355373]}},{"id":"3:-8:4:1","name":"3:-8:4:1","data":{"weight":1,"coords":[113.62210188390215,229.9679533532303,838.5704275167225,213.3948976875662]}},{"id":"4:-9:4:0","name":"4:-9:4:0","data":{"weight":1,"coords":[10.765380689295135,213.0356028744042,753.2040072633039,217.71632582559613]}},{"id":"4:-8:4:-1","name":"4:-8:4:-1","data":{"weight":1,"coords":[122.50327669449543,223.23196718990695,711.2888679031406,201.91080821487603]}},{"id":"3:-8:3:-1","name":"3:-8:3:-1","data":{"weight":1,"coords":[98.36616654232259,238.1358429768451,705.8164214876033,252.24744628099177]}},{"id":"2:-9:4:-1","name":"2:-9:4:-1","data":{"weight":1,"coords":[72.0689669421487,346.3219338842975,706.0536392464126,224.84034394206822]}},{"id":"3:-9:4:-1","name":"3:-9:4:-1","data":{"weight":1,"coords":[17.68513356363637,262.54478352975207,706.0156893139043,224.9668559932025]}},{"id":"4:-8:4:2","name":"4:-8:4:2","data":{"weight":1,"coords":[148.60525262122903,209.33540426966553,973.7656182771349,220.845414615478]}},{"id":"4:-8:5:3","name":"4:-8:5:3","data":{"weight":1,"coords":[79.93572102314045,198.14059029834712,987.7793966247933,142.6298246140496]}},{"id":"3:-9:5:0","name":"3:-9:5:0","data":{"weight":1,"coords":[37.19646273719006,245.04191936446284,735.6357889727124,139.05753418181817]}},{"id":"2:-9:4:1","name":"2:-9:4:1","data":{"weight":1,"coords":[68.63097978867292,324.7736423609102,849.2563965338843,182.24131752561982]}},{"id":"2:-9:5:1","name":"2:-9:5:1","data":{"weight":1,"coords":[72.10108071319328,346.3224453323887,845.374225585124,125.17047778099177]}},{"id":"3:-8:4:2","name":"3:-8:4:2","data":{"weight":1,"coords":[98.90989656682771,237.97387681273597,973.7394328278668,220.84430619194924]}},{"id":"4:-8:3:-1","name":"4:-8:3:-1","data":{"weight":1,"coords":[124.32568229548474,222.5709368868264,669.5136363636364,298.0913388429752]}},{"id":"2:-9:6:2","name":"2:-9:6:2","data":{"weight":1,"coords":[72.10108071319328,346.3224453323887,940.7966012051293,62.11888463073535]}},{"id":"3:-9:5:1","name":"3:-9:5:1","data":{"weight":1,"coords":[37.71844186073493,281.4820734310422,853.4522411251857,145.39455866137166]}},{"id":"2:-9:4:2","name":"2:-9:4:2","data":{"weight":1,"coords":[63.56686797023805,324.53799973223374,973.6994396556507,220.83933784197853]}},{"id":"4:-9:4:1","name":"4:-9:4:1","data":{"weight":1,"coords":[9.05453863222737,212.58453785089566,832.9876513520549,159.8824440177292]}},{"id":"2:-9:3:-3","name":"2:-9:3:-3","data":{"weight":1,"coords":[73.43710743801655,343.9200826446281,524.0742148760331,247.52768595041323]}},{"id":"3:-9:3:-1","name":"3:-9:3:-1","data":{"weight":1,"coords":[37.71844186033056,281.48207343140496,686.0074740795868,250.53105723305785]}},{"id":"3:-8:4:-1","name":"3:-8:4:-1","data":{"weight":1,"coords":[113.69881748167937,229.94208716725268,706.0156893139043,224.9668559932025]}},{"id":"3:-8:3:2","name":"3:-8:3:2","data":{"weight":1,"coords":[95.64188908884228,237.6395556774414,931.9996951318523,246.87655173694924]}},{"id":"2:-9:6:-3","name":"2:-9:6:-3","data":{"weight":1,"coords":[72.77606878883681,346.216621841245,548.5780309176482,30.601603886676628]}},{"id":"4:-8:5:-2","name":"4:-8:5:-2","data":{"weight":1,"coords":[148.62250887657984,209.36774277649462,652.4425004132231,113.25434793388445]}},{"id":"3:-8:5:1","name":"3:-8:5:1","data":{"weight":1,"coords":[114.31630189753761,229.3229361731169,824.3966910464704,64.40001750211059]}},{"id":"3:-9:5:3","name":"3:-9:5:3","data":{"weight":1,"coords":[26.933481702288713,243.93488528144655,986.0014443116357,65.23886125359871]}},{"id":"3:-8:4:3","name":"3:-8:4:3","data":{"weight":1,"coords":[95.70881201532438,236.95045827083283,989.0633988971553,175.6959635517778]}},{"id":"3:-9:4:1","name":"3:-9:4:1","data":{"weight":1,"coords":[39.703627739777446,277.84547371963697,838.6173606060925,213.21860513170333]}},{"id":"4:-9:3:2","name":"4:-9:3:2","data":{"weight":1,"coords":[11.320957467576058,213.39898227660083,925.4355205785124,245.62909190082644]}},{"id":"4:-8:3:0","name":"4:-8:3:0","data":{"weight":1,"coords":[124.07775757391225,222.74550262243545,810.9188488106423,235.356721621128]}},{"id":"4:-9:5:2","name":"4:-9:5:2","data":{"weight":1,"coords":[11.367445149697549,213.29962636699165,910.3863613664612,99.23394208672585]}},{"id":"4:-9:3:-1","name":"4:-9:3:-1","data":{"weight":1,"coords":[9.054538632231363,212.58453785123965,658.1239385052892,234.29563290495867]}},{"id":"3:-8:6:-3","name":"3:-8:6:-3","data":{"weight":1,"coords":[95.47185503746853,237.58153525214635,566.5112054052431,29.113177075284625]}},{"id":"4:-8:6:2","name":"4:-8:6:2","data":{"weight":1,"coords":[125.74774371690987,223.59430284317068,941.8855146928267,61.003503772561885]}},{"id":"2:-9:4:0","name":"2:-9:4:0","data":{"weight":1,"coords":[61.53411707603302,325.05093074958677,758.5203076196694,155.62478550330576]}},{"id":"4:-9:3:3","name":"4:-9:3:3","data":{"weight":1,"coords":[9.054538632231363,212.58453785123965,994.3621218490367,310.9166163366114]}},{"id":"3:-9:6:1","name":"3:-9:6:1","data":{"weight":1,"coords":[67.84319008264458,268.62243214876037,827.2651916694215,62.034192744628115]}},{"id":"4:-9:6:-3","name":"4:-9:6:-3","data":{"weight":1,"coords":[10.639710997687672,212.98266639208003,554.3022722322314,29.374953037190096]}},{"id":"2:-9:5:3","name":"2:-9:5:3","data":{"weight":1,"coords":[62.98151652892558,323.6501198347107,986.8594876033059,64.39652892561986]}},{"id":"4:-8:3:2","name":"4:-8:3:2","data":{"weight":1,"coords":[123.79143652148757,223.7584641355372,945.2961983471075,249.87975206611566]}},{"id":"4:-8:3:3","name":"4:-8:3:3","data":{"weight":1,"coords":[125.74774371690987,223.59430284317068,993.5202883821662,311.02444873748516]}},{"id":"2:-8:5:-1","name":"2:-8:5:-1","data":{"weight":1,"coords":[96.5071818181818,352.23795041322313,684.394694214876,86.42406611570249]}},{"id":"2:-9:3:-1","name":"2:-9:3:-1","data":{"weight":1,"coords":[73.67564823966943,341.69614284793386,659.9421797709917,239.40794519090912]}},{"id":"3:-9:3:-3","name":"3:-9:3:-3","data":{"weight":1,"coords":[66.575518053719,268.4318858132232,523.0471831710744,247.52401221074382]}},{"id":"4:-8:3:1","name":"4:-8:3:1","data":{"weight":1,"coords":[124.28806483140491,223.02357079999996,860.2606358736109,249.55983730578572]}},{"id":"3:-9:6:-3","name":"3:-9:6:-3","data":{"weight":1,"coords":[13.743142992562028,261.80479073305787,554.217998646281,29.35748002314051]}},{"id":"2:-8:5:1","name":"2:-8:5:1","data":{"weight":1,"coords":[91.44851418264466,346.1175529752066,846.2853238272727,125.46689034132231]}},{"id":"3:-9:4:2","name":"3:-9:4:2","data":{"weight":1,"coords":[65.86264462809918,278.4972644628099,973.699439655372,220.83933784214878]}},{"id":"4:-7:5:-1","name":"4:-7:5:-1","data":{"weight":1,"coords":[157.1978429752066,196.5861487603306,731.8376036714916,134.21667025563377]}},{"id":"4:-7:5:0","name":"4:-7:5:0","data":{"weight":1,"coords":[185.70473568498596,180.38798394048948,777.5518322872728,127.47360322892567]}},{"id":"4:-7:6:-3","name":"4:-7:6:-3","data":{"weight":1,"coords":[209.99675705412324,190.4807521343938,554.2129686958677,29.35059123223141]}},{"id":"3:-9:3:2","name":"3:-9:3:2","data":{"weight":1,"coords":[65.45048257438019,244.87761551157024,926.1512396694216,237.6909090909091]}},{"id":"3:-8:6:-2","name":"3:-8:6:-2","data":{"weight":1,"coords":[114.46490957717265,229.26366815130424,595.9342726892562,30.65969452561984]}},{"id":"4:-7:6:-2","name":"4:-7:6:-2","data":{"weight":1,"coords":[211.0398268059032,190.7804386070193,595.3139628264463,30.440372965289328]}},{"id":"2:-9:6:1","name":"2:-9:6:1","data":{"weight":1,"coords":[62.29485537190083,324.6495727272727,821.5031132231405,35.386095041322335]}},{"id":"3:-9:6:2","name":"3:-9:6:2","data":{"weight":1,"coords":[18.220008264462834,260.7363140495868,941.9173707532383,61.0275083103507]}},{"id":"3:-7:5:-1","name":"3:-7:5:-1","data":{"weight":1,"coords":[200.04286797807487,292.5232294792301,734.7195368136988,134.730144201821]}},{"id":"3:-8:3:3","name":"3:-8:3:3","data":{"weight":1,"coords":[100.33584445619834,236.45006900578514,991.5173306570248,311.5244985834711]}},{"id":"4:-8:6:1","name":"4:-8:6:1","data":{"weight":1,"coords":[127.32899736956628,222.1516299250758,827.304366175025,62.01274670493609]}},{"id":"4:-9:4:2","name":"4:-9:4:2","data":{"weight":1,"coords":[9.05453863222737,212.58453785089566,974.7127383208472,220.2485735063972]}},{"id":"3:-8:3:1","name":"3:-8:3:1","data":{"weight":1,"coords":[104.26373846071542,254.61150580472852,823.3225778638903,258.6863681363327]}},{"id":"4:-7:5:1","name":"4:-7:5:1","data":{"weight":1,"coords":[166.8085066740178,189.18836121395162,836.5464328734342,134.53280898013074]}},{"id":"4:-9:4:3","name":"4:-9:4:3","data":{"weight":1,"coords":[3.7640933884297274,206.83642396694214,989.5256198347108,175.33402479338844]}},{"id":"2:-8:5:0","name":"2:-8:5:0","data":{"weight":1,"coords":[96.2849412082644,352.2411871661157,806.3804039285125,145.86467667024792]}},{"id":"2:-9:3:2","name":"2:-9:3:2","data":{"weight":1,"coords":[54.17104824049584,328.5619209024793,982.5217349471076,301.5428487363636]}},{"id":"4:-7:5:-2","name":"4:-7:5:-2","data":{"weight":1,"coords":[213.68548581726765,181.47226329028487,587.1305002284154,98.97353670555486]}},{"id":"4:-7:3:2","name":"4:-7:3:2","data":{"weight":1,"coords":[210.79401256478886,211.5736348717641,933.3242222653823,311.32270917824985]}},{"id":"4:-9:6:2","name":"4:-9:6:2","data":{"weight":1,"coords":[61.79196018653015,220.03137766841326,942.0945495537916,61.075268443184484]}},{"id":"4:-9:5:3","name":"4:-9:5:3","data":{"weight":1,"coords":[46.61326763058497,210.67149887092165,987.7182336793384,142.42391659321703]}},{"id":"4:-9:3:1","name":"4:-9:3:1","data":{"weight":1,"coords":[11.082396694214864,212.36694214876033,869.6201652892563,234.25578512396694]}},{"id":"3:-9:3:0","name":"3:-9:3:0","data":{"weight":1,"coords":[59.42849137247713,309.1956380651949,758.0384866280216,232.83721759067194]}},{"id":"4:-8:3:-2","name":"4:-8:3:-2","data":{"weight":1,"coords":[125.75746556446283,223.5550275479339,595.7845974859504,289.69862748181816]}},{"id":"2:-9:3:3","name":"2:-9:3:3","data":{"weight":1,"coords":[73.48837861759692,343.686130253698,988.7949249746848,254.76400042712038]}},{"id":"3:-8:6:-1","name":"3:-8:6:-1","data":{"weight":1,"coords":[98.87486658853294,237.9822968747594,681.2745432526664,33.6688332495774]}},{"id":"3:-7:5:0","name":"3:-7:5:0","data":{"weight":1,"coords":[200.06619714253372,292.6193858049818,772.6330232829939,138.8353142821861]}},{"id":"3:-8:5:2","name":"3:-8:5:2","data":{"weight":1,"coords":[98.87219834710749,237.9567685950413,940.1121900826446,68.2895785123967]}},{"id":"2:-8:4:1","name":"2:-8:4:1","data":{"weight":1,"coords":[96.36165289256203,352.33701652892563,876.9173553719008,161.57653719008263]}},{"id":"2:-9:3:0","name":"2:-9:3:0","data":{"weight":1,"coords":[73.33035732208978,346.3014960017157,747.4377254593828,237.86604242631074]}},{"id":"3:-9:3:3","name":"3:-9:3:3","data":{"weight":1,"coords":[37.71844186073493,281.4820734310422,993.7112784578262,311.2047137525815]}},{"id":"4:-7:3:1","name":"4:-7:3:1","data":{"weight":1,"coords":[164.26523305785125,194.8675115702479,859.6253090909091,245.25943884297521]}},{"id":"2:-9:6:-2","name":"2:-9:6:-2","data":{"weight":1,"coords":[54.0729090909091,328.5076446280992,578.3017768595041,22.602024793388466]}},{"id":"3:-7:3:-1","name":"3:-7:3:-1","data":{"weight":1,"coords":[200.22273507024792,292.54135662809915,660.0163810509091,239.56019316776855]}},{"id":"2:-9:5:2","name":"2:-9:5:2","data":{"weight":1,"coords":[72.06869476943943,346.3219177648473,909.4735880376829,127.77298656180588]}},{"id":"3:-8:6:1","name":"3:-8:6:1","data":{"weight":1,"coords":[98.79583957788637,237.88962981476067,827.2853493061857,62.0359538660457]}},{"id":"4:-9:3:0","name":"4:-9:3:0","data":{"weight":1,"coords":[44.998247933884436,215.17406363636366,760.4757950413225,231.44410826446278]}},{"id":"4:-7:4:0","name":"4:-7:4:0","data":{"weight":1,"coords":[238.2615238178719,178.36796203625596,811.4860082644628,183.7418917355372]}},{"id":"3:-8:5:3","name":"3:-8:5:3","data":{"weight":1,"coords":[95.53097034079421,238.11887846863527,990.8884537121481,143.37615613359216]}},{"id":"4:-8:6:0","name":"4:-8:6:0","data":{"weight":1,"coords":[123.07107992644623,222.5861663289256,787.557314876033,43.28370826446286]}},{"id":"3:-8:3:0","name":"3:-8:3:0","data":{"weight":1,"coords":[97.07408700082645,237.2222565619835,757.1843569436363,232.53064849752067]}},{"id":"4:-8:3:-3","name":"4:-8:3:-3","data":{"weight":1,"coords":[146.07542999504133,206.91071494380162,523.4010849743802,247.60030566859507]}},{"id":"4:-9:6:1","name":"4:-9:6:1","data":{"weight":1,"coords":[69.13075320248369,220.47456370204526,877.1109001410283,58.19273883046105]}},{"id":"3:-8:6:2","name":"3:-8:6:2","data":{"weight":1,"coords":[95.57683802809916,249.24118302396695,903.0372909440028,31.971115354395863]}},{"id":"3:-7:3:-2","name":"3:-7:3:-2","data":{"weight":1,"coords":[163.2802727272727,310.9920909090909,602.2889421487603,264.2568512396694]}},{"id":"3:-9:4:-2","name":"3:-9:4:-2","data":{"weight":1,"coords":[51.02296776859507,239.2419479338843,623.3408016528927,156.81340743801653]}},{"id":"3:-6:5:0","name":"3:-6:5:0","data":{"weight":1,"coords":[248.41665093374056,275.5539448301505,792.2282410087232,139.30143270639593]}},{"id":"2:-8:3:0","name":"2:-8:3:0","data":{"weight":1,"coords":[94.92864049090905,352.8770908413223,756.2146370280991,232.9850725818182]}},{"id":"2:-9:6:0","name":"2:-9:6:0","data":{"weight":1,"coords":[74.10329214447225,338.74148794975326,779.5793233762963,60.42390053519193]}},{"id":"3:-7:4:1","name":"3:-7:4:1","data":{"weight":1,"coords":[199.77735754115804,292.9439335091461,819.1147454022168,183.05598858031524]}},{"id":"4:-7:4:2","name":"4:-7:4:2","data":{"weight":1,"coords":[157.29538677685946,196.59048181818181,977.2629479338842,221.58354462809916]}},{"id":"2:-8:4:-1","name":"2:-8:4:-1","data":{"weight":1,"coords":[86.95950413223136,322.15289256198344,675.2710743801653,209.42396694214878]}},{"id":"2:-9:4:-3","name":"2:-9:4:-3","data":{"weight":1,"coords":[55.35537190082639,313.9190082644628,563.5413223140496,172.96363636363637]}},{"id":"4:-8:6:-2","name":"4:-8:6:-2","data":{"weight":1,"coords":[124.17322314049582,222.4931404958678,585.9472510839917,16.701764842707803]}},{"id":"4:-8:4:3","name":"4:-8:4:3","data":{"weight":1,"coords":[147.74407032891824,209.32187054502512,986.8815472459621,222.5172274134809]}},{"id":"3:-9:5:2","name":"3:-9:5:2","data":{"weight":1,"coords":[35.915836540863815,236.77435679625262,932.7163627707307,88.63928198296873]}},{"id":"4:-7:3:-1","name":"4:-7:3:-1","data":{"weight":1,"coords":[213.7855371900826,177.12156198347108,710.8404132231406,229.7942975206612]}},{"id":"4:-7:4:1","name":"4:-7:4:1","data":{"weight":1,"coords":[184.7348842975206,179.6684214876033,855.3971900826446,178.91538016528924]}},{"id":"3:-9:3:1","name":"3:-9:3:1","data":{"weight":1,"coords":[60.69586776859505,308.83156198347103,855.4752066115702,262.8280991735537]}},{"id":"4:-9:6:-1","name":"4:-9:6:-1","data":{"weight":1,"coords":[49.19074380165286,209.65553719008264,679.4517355371901,46.94315702479338]}},{"id":"3:-9:6:-1","name":"3:-9:6:-1","data":{"weight":1,"coords":[26.109090909090895,296.58760330578514,707.9528925619835,26.611570247933855]}},{"id":"3:-9:4:3","name":"3:-9:4:3","data":{"weight":1,"coords":[18.086768595041264,282.4769421487603,989.5870247933884,175.45885123966943]}},{"id":"3:-6:4:0","name":"3:-6:4:0","data":{"weight":1,"coords":[240.46872396694218,262.13262231404957,758.9174041322315,151.08776280991736]}},{"id":"3:-7:6:3","name":"3:-7:6:3","data":{"weight":1,"coords":[222.59338842975205,309.57355371900826,988.1123966942149,43.71900826446284]}},{"id":"4:-9:3:-2","name":"4:-9:3:-2","data":{"weight":1,"coords":[23.896875206611575,215.04674876033056,595.4908603305785,251.19256115702478]}}] 2 | -------------------------------------------------------------------------------- /examples/data/json/philippines.json: -------------------------------------------------------------------------------- 1 | [{"id":"40:-75","name":"40:-75","data":{"weight":1,"coords":[1304.9106188785065,205.13313795761644,625,322.9166666666667]}},{"id":"35:-100","name":"35:-100","data":{"weight":1,"coords":[1209.076468948578,237.48368228188565,625,322.9166666666667]}},{"id":"20:-105","name":"20:-105","data":{"weight":1,"coords":[1181.5254472449135,288.79406806529823,625,322.9166666666667]}},{"id":"35:-125","name":"35:-125","data":{"weight":1,"coords":[1104.8978917693232,222.4057521535192,625,322.9166666666667]}},{"id":"25:65","name":"25:65","data":{"weight":1,"coords":[391.9190516666667,271.54868000000005,625,322.9166666666667]}},{"id":"45:-80","name":"45:-80","data":{"weight":1,"coords":[1281.6248761222005,193.0510938478069,625,322.9166666666667]}},{"id":"50:0","name":"50:0","data":{"weight":1,"coords":[122.47830117402809,170.945577212123,625,322.9166666666667]}},{"id":"0:125","name":"0:125","data":{"weight":1,"coords":[625.2948749999999,372.7673333333334,625,322.9166666666667]}},{"id":"25:-85","name":"25:-85","data":{"weight":1,"coords":[1278.6603607910317,267.40175071564244,625,322.9166666666667]}},{"id":"15:100","name":"15:100","data":{"weight":1,"coords":[531.5268246666667,317.4073610889487,625,322.9166666666667]}},{"id":"40:-125","name":"40:-125","data":{"weight":1,"coords":[1104.285033721935,218.64141215322877,625,322.9166666666667]}},{"id":"25:55","name":"25:55","data":{"weight":1,"coords":[342.6741522435898,270.12563621794874,625,322.9166666666667]}},{"id":"25:50","name":"25:50","data":{"weight":1,"coords":[327.2232221907142,269.61902790925893,625,322.9166666666667]}},{"id":"35:-80","name":"35:-80","data":{"weight":1,"coords":[1282.7410783215137,219.10498966234206,625,322.9166666666667]}},{"id":"45:-95","name":"45:-95","data":{"weight":1,"coords":[1223.55015315125,187.70838437999998,625,322.9166666666667]}},{"id":"50:-125","name":"50:-125","data":{"weight":1,"coords":[1099.3908258333336,170.12309958333333,625,322.9166666666667]}},{"id":"5:-65","name":"5:-65","data":{"weight":1,"coords":[1348.6648750000002,343.93874999999997,625,322.9166666666667]}},{"id":"-10:110","name":"-10:110","data":{"weight":1,"coords":[572.3727752499999,407.3121169583333,625,322.9166666666667]}},{"id":"35:-120","name":"35:-120","data":{"weight":1,"coords":[1132.379751710694,223.70700436032894,625,322.9166666666667]}},{"id":"30:-85","name":"30:-85","data":{"weight":1,"coords":[1276.5780988379047,257.8464011311371,625,322.9166666666667]}},{"id":"50:-115","name":"50:-115","data":{"weight":1,"coords":[1137.7914459754325,161.92917243945578,625,322.9166666666667]}},{"id":"35:-110","name":"35:-110","data":{"weight":1,"coords":[1168.3756384521098,228.90121287515228,625,322.9166666666667]}},{"id":"50:5","name":"50:5","data":{"weight":1,"coords":[138.224778188561,170.30501845941706,625,322.9166666666667]}},{"id":"50:10","name":"50:10","data":{"weight":1,"coords":[145.17543333333336,175.00413333333324,625,322.9166666666667]}},{"id":"30:35","name":"30:35","data":{"weight":1,"coords":[259.1767268471664,242.55837425205874,625,322.9166666666667]}},{"id":"35:-85","name":"35:-85","data":{"weight":1,"coords":[1260.8408165291974,234.20033959675482,625,322.9166666666667]}},{"id":"25:80","name":"25:80","data":{"weight":1,"coords":[446.97835749999996,264.67448999999993,625,322.9166666666667]}},{"id":"60:5","name":"60:5","data":{"weight":1,"coords":[136.30383072512493,129.5831814216666,625,322.9166666666667]}},{"id":"40:-105","name":"40:-105","data":{"weight":1,"coords":[1175.435311176278,209.5011490373356,625,322.9166666666667]}},{"id":"60:10","name":"60:10","data":{"weight":1,"coords":[157.50712099999996,125.46343662499999,625,322.9166666666667]}},{"id":"35:-10","name":"35:-10","data":{"weight":1,"coords":[87.82166916666665,219.47108291666666,625,322.9166666666667]}},{"id":"5:-75","name":"5:-75","data":{"weight":1,"coords":[1303.88259615292,355.66033516256545,625,322.9166666666667]}},{"id":"15:80","name":"15:80","data":{"weight":1,"coords":[435.8711962100673,321.09990109755364,625,322.9166666666667]}},{"id":"40:115","name":"40:115","data":{"weight":1,"coords":[597.7205262958333,208.7070079829167,625,322.9166666666667]}},{"id":"30:-100","name":"30:-100","data":{"weight":1,"coords":[1211.1040465064334,247.52785958105,625,322.9166666666667]}},{"id":"40:0","name":"40:0","data":{"weight":1,"coords":[114.18785083333334,206.82960458333332,625,322.9166666666667]}},{"id":"-40:145","name":"-40:145","data":{"weight":1,"coords":[716.6616277912862,532.4024791126712,625,322.9166666666667]}},{"id":"55:-10","name":"55:-10","data":{"weight":1,"coords":[84.73795837170664,151.44786934714182,625,322.9166666666667]}},{"id":"50:-5","name":"50:-5","data":{"weight":1,"coords":[112.01395458333334,160.1125695833333,625,322.9166666666667]}},{"id":"20:-70","name":"20:-70","data":{"weight":1,"coords":[1322.855875314875,294.47083227561745,625,322.9166666666667]}},{"id":"40:-80","name":"40:-80","data":{"weight":1,"coords":[1291.3453822952497,212.5853147589021,625,322.9166666666667]}},{"id":"30:50","name":"30:50","data":{"weight":1,"coords":[312.50414811891466,252.59718113673767,625,322.9166666666667]}},{"id":"-35:150","name":"-35:150","data":{"weight":1,"coords":[741.4645488146963,515.7917705365837,625,322.9166666666667]}},{"id":"-35:-75","name":"-35:-75","data":{"weight":1,"coords":[1318.3228075475652,514.403559171715,625,322.9166666666667]}},{"id":"40:-90","name":"40:-90","data":{"weight":1,"coords":[1242.0273367916668,215.31739454166677,625,322.9166666666667]}},{"id":"40:-95","name":"40:-95","data":{"weight":1,"coords":[1227.8464558333333,198.06664375000003,625,322.9166666666667]}},{"id":"0:105","name":"0:105","data":{"weight":1,"coords":[545.4697083333334,369.424125,625,322.9166666666667]}},{"id":"45:-75","name":"45:-75","data":{"weight":1,"coords":[1319.2373769235219,193.92174121044548,625,322.9166666666667]}},{"id":"55:-5","name":"55:-5","data":{"weight":1,"coords":[103.75647253821718,155.48500599086333,625,322.9166666666667]}},{"id":"45:5","name":"45:5","data":{"weight":1,"coords":[135.46777500000005,194.54172916666676,625,322.9166666666667]}},{"id":"-10:105","name":"-10:105","data":{"weight":1,"coords":[557.4175441666667,401.29809124999997,625,322.9166666666667]}},{"id":"-35:145","name":"-35:145","data":{"weight":1,"coords":[707.4370401218488,519.1281413427965,625,322.9166666666667]}},{"id":"40:-10","name":"40:-10","data":{"weight":1,"coords":[74.58732257286727,213.55200664398842,625,322.9166666666667]}},{"id":"40:-85","name":"40:-85","data":{"weight":1,"coords":[1260.0642657500002,204.95706562499993,625,322.9166666666667]}},{"id":"-5:105","name":"-5:105","data":{"weight":1,"coords":[548.8233749999999,387.3250833333334,625,322.9166666666667]}},{"id":"35:140","name":"35:140","data":{"weight":1,"coords":[688.5197983333334,220.08984208333334,625,322.9166666666667]}},{"id":"5:5","name":"5:5","data":{"weight":1,"coords":[135.85512500000002,348.2576666666667,625,322.9166666666667]}},{"id":"-35:-60","name":"-35:-60","data":{"weight":1,"coords":[1369.2728216666665,519.7663375,625,322.9166666666667]}},{"id":"25:-105","name":"25:-105","data":{"weight":1,"coords":[1194.2976190238055,268.08199854753093,625,322.9166666666667]}},{"id":"-30:25","name":"-30:25","data":{"weight":1,"coords":[225.39097380369236,486.21214687601775,625,322.9166666666667]}},{"id":"25:-110","name":"25:-110","data":{"weight":1,"coords":[1155.6691774581366,263.2411667327084,625,322.9166666666667]}},{"id":"55:-115","name":"55:-115","data":{"weight":1,"coords":[1139.4246790601871,153.10072338912266,625,322.9166666666667]}},{"id":"40:-5","name":"40:-5","data":{"weight":1,"coords":[97.06853628158333,206.5973633804167,625,322.9166666666667]}},{"id":"45:-125","name":"45:-125","data":{"weight":1,"coords":[1101.2863910985075,185.10633852547983,625,322.9166666666667]}},{"id":"-30:-50","name":"-30:-50","data":{"weight":1,"coords":[1408.0807456862483,485.3774137505155,625,322.9166666666667]}},{"id":"35:-5","name":"35:-5","data":{"weight":1,"coords":[97.52974499999995,220.08792291666657,625,322.9166666666667]}},{"id":"30:-95","name":"30:-95","data":{"weight":1,"coords":[1229.1081286497567,249.3890161275625,625,322.9166666666667]}},{"id":"35:-90","name":"35:-90","data":{"weight":1,"coords":[1243.3713586764695,230.51184526398367,625,322.9166666666667]}},{"id":"10:-75","name":"10:-75","data":{"weight":1,"coords":[1311.5501116617706,342.3736578339983,625,322.9166666666667]}},{"id":"-45:175","name":"-45:175","data":{"weight":1,"coords":[840.7626478479974,547.0439528276675,625,322.9166666666667]}},{"id":"20:-100","name":"20:-100","data":{"weight":1,"coords":[1198.939928732309,294.10599512021514,625,322.9166666666667]}},{"id":"20:100","name":"20:100","data":{"weight":1,"coords":[524.95925,295.06416666666667,625,322.9166666666667]}},{"id":"25:120","name":"25:120","data":{"weight":1,"coords":[613.6094887500001,279.2907266666667,625,322.9166666666667]}},{"id":"-5:120","name":"-5:120","data":{"weight":1,"coords":[610.6099191666667,395.0627475,625,322.9166666666667]}},{"id":"5:100","name":"5:100","data":{"weight":1,"coords":[523.5264741666667,360.1322941666667,625,322.9166666666667]}},{"id":"-35:20","name":"-35:20","data":{"weight":1,"coords":[189.08879434757702,516.4564905109631,625,322.9166666666667]}},{"id":"-35:140","name":"-35:140","data":{"weight":1,"coords":[690.1295706997546,520.118081965125,625,322.9166666666667]}},{"id":"35:-95","name":"35:-95","data":{"weight":1,"coords":[1226.8775606518632,230.86707540157784,625,322.9166666666667]}},{"id":"45:-5","name":"45:-5","data":{"weight":1,"coords":[94.74607916666666,196.6792591666666,625,322.9166666666667]}},{"id":"45:25","name":"45:25","data":{"weight":1,"coords":[219.24467096162056,195.537055625,625,322.9166666666667]}},{"id":"-25:-50","name":"-25:-50","data":{"weight":1,"coords":[1418.1105310833334,473.17707475000003,625,322.9166666666667]}},{"id":"20:45","name":"20:45","data":{"weight":1,"coords":[289.84066333333334,298.9647370833334,625,322.9166666666667]}},{"id":"-5:-80","name":"-5:-80","data":{"weight":1,"coords":[1279.6294683333335,383.98176125,625,322.9166666666667]}},{"id":"45:10","name":"45:10","data":{"weight":1,"coords":[148.12791534248407,177.6543980188195,625,322.9166666666667]}},{"id":"45:-70","name":"45:-70","data":{"weight":1,"coords":[1335.2876485368986,183.8627140546455,625,322.9166666666667]}},{"id":"55:15","name":"55:15","data":{"weight":1,"coords":[168.4546915690106,156.13387718511876,625,322.9166666666667]}},{"id":"40:10","name":"40:10","data":{"weight":1,"coords":[164.4503638011199,200.3171814578327,625,322.9166666666667]}},{"id":"5:-80","name":"5:-80","data":{"weight":1,"coords":[1297.5554433837815,348.9673538117091,625,322.9166666666667]}},{"id":"60:-135","name":"60:-135","data":{"weight":1,"coords":[1051.7346472767724,131.74205705519103,625,322.9166666666667]}},{"id":"50:-100","name":"50:-100","data":{"weight":1,"coords":[1207.689199084039,167.1255404360519,625,322.9166666666667]}},{"id":"40:25","name":"40:25","data":{"weight":1,"coords":[211.59197916666665,216.1606875,625,322.9166666666667]}},{"id":"40:-115","name":"40:-115","data":{"weight":1,"coords":[1146.954802639722,206.86115609808417,625,322.9166666666667]}},{"id":"-15:130","name":"-15:130","data":{"weight":1,"coords":[657.8926544729271,426.5906325472505,625,322.9166666666667]}},{"id":"25:-100","name":"25:-100","data":{"weight":1,"coords":[1203.313798480286,265.71174280242127,625,322.9166666666667]}},{"id":"-30:155","name":"-30:155","data":{"weight":1,"coords":[750.247344688795,489.56973194515103,625,322.9166666666667]}},{"id":"20:40","name":"20:40","data":{"weight":1,"coords":[275.4148209755176,284.3001977510794,625,322.9166666666667]}},{"id":"10:-65","name":"10:-65","data":{"weight":1,"coords":[1356.3782916666669,332.21875,625,322.9166666666667]}},{"id":"20:-80","name":"20:-80","data":{"weight":1,"coords":[1288.0369925000002,299.84814458333335,625,322.9166666666667]}},{"id":"25:130","name":"25:130","data":{"weight":1,"coords":[644.9406746080799,265.19526263425746,625,322.9166666666667]}},{"id":"20:115","name":"20:115","data":{"weight":1,"coords":[588.03066875,281.8533079166666,625,322.9166666666667]}},{"id":"-40:175","name":"-40:175","data":{"weight":1,"coords":[840.9526289685975,528.7548727076054,625,322.9166666666667]}},{"id":"-30:-60","name":"-30:-60","data":{"weight":1,"coords":[1372.3452983333334,480.4339575,625,322.9166666666667]}},{"id":"30:-90","name":"30:-90","data":{"weight":1,"coords":[1256.12510847625,248.97370611625,625,322.9166666666667]}},{"id":"25:40","name":"25:40","data":{"weight":1,"coords":[277.6444833333333,272.7846229166667,625,322.9166666666667]}},{"id":"-40:-75","name":"-40:-75","data":{"weight":1,"coords":[1308.1476374999997,528.4455483333334,625,322.9166666666667]}},{"id":"40:-120","name":"40:-120","data":{"weight":1,"coords":[1113.6189055044613,210.16071597935013,625,322.9166666666667]}},{"id":"40:15","name":"40:15","data":{"weight":1,"coords":[180.23622252519024,215.6801393719769,625,322.9166666666667]}},{"id":"10:105","name":"10:105","data":{"weight":1,"coords":[549.6579470833333,326.9187966666667,625,322.9166666666667]}},{"id":"-30:30","name":"-30:30","data":{"weight":1,"coords":[241.74179166666667,499.36366666666663,625,322.9166666666667]}},{"id":"5:120","name":"5:120","data":{"weight":1,"coords":[602.4900833333334,361.27770833333335,625,322.9166666666667]}},{"id":"-25:-45","name":"-25:-45","data":{"weight":1,"coords":[1431.7312951391666,470.68680082333333,625,322.9166666666667]}},{"id":"-15:-40","name":"-15:-40","data":{"weight":1,"coords":[1458.1843441666667,420.4349079166667,625,322.9166666666667]}},{"id":"50:20","name":"50:20","data":{"weight":1,"coords":[191.90575604187038,177.05628165122823,625,322.9166666666667]}},{"id":"-20:-50","name":"-20:-50","data":{"weight":1,"coords":[1412.7970833333334,440.74375,625,322.9166666666667]}},{"id":"-5:-40","name":"-5:-40","data":{"weight":1,"coords":[1451.9045833333332,390.49333333333334,625,322.9166666666667]}},{"id":"20:-160","name":"20:-160","data":{"weight":1,"coords":[960.41254261676,287.90859372262946,625,322.9166666666667]}},{"id":"25:45","name":"25:45","data":{"weight":1,"coords":[307.2490433333333,272.16074875,625,322.9166666666667]}},{"id":"-15:-80","name":"-15:-80","data":{"weight":1,"coords":[1291.5018714258854,425.2330336490567,625,322.9166666666667]}},{"id":"-10:115","name":"-10:115","data":{"weight":1,"coords":[592.5769166666666,411.11441666666667,625,322.9166666666667]}},{"id":"60:-150","name":"60:-150","data":{"weight":1,"coords":[987.6449086697543,120.15777424652606,625,322.9166666666667]}},{"id":"35:130","name":"35:130","data":{"weight":1,"coords":[663.8889034050997,232.0716772809226,625,322.9166666666667]}},{"id":"20:-95","name":"20:-95","data":{"weight":1,"coords":[1235.0216125422255,292.4660324996489,625,322.9166666666667]}},{"id":"55:0","name":"55:0","data":{"weight":1,"coords":[118.23796969696971,156.06209469696955,625,322.9166666666667]}},{"id":"65:-150","name":"65:-150","data":{"weight":1,"coords":[996.3835065247342,104.79851782995252,625,322.9166666666667]}},{"id":"-40:150","name":"-40:150","data":{"weight":1,"coords":[733.7135308712906,522.1121285329386,625,322.9166666666667]}},{"id":"60:20","name":"60:20","data":{"weight":1,"coords":[187.64483683510596,127.76624265507974,625,322.9166666666667]}},{"id":"10:-85","name":"10:-85","data":{"weight":1,"coords":[1277.37545,341.8303516666667,625,322.9166666666667]}},{"id":"-5:135","name":"-5:135","data":{"weight":1,"coords":[682.8690833333333,393.8329166666667,625,322.9166666666667]}},{"id":"40:30","name":"40:30","data":{"weight":1,"coords":[233.28996250000003,203.98148458333344,625,322.9166666666667]}},{"id":"-30:-55","name":"-30:-55","data":{"weight":1,"coords":[1393.9456766666667,492.87424,625,322.9166666666667]}},{"id":"45:-85","name":"45:-85","data":{"weight":1,"coords":[1265.6322454166668,197.75632125000004,625,322.9166666666667]}},{"id":"-10:-40","name":"-10:-40","data":{"weight":1,"coords":[1465.773320781706,399.31590663345776,625,322.9166666666667]}},{"id":"40:-100","name":"40:-100","data":{"weight":1,"coords":[1214.970790113441,212.58243074125062,625,322.9166666666667]}},{"id":"35:125","name":"35:125","data":{"weight":1,"coords":[642.1669525,221.4014295833333,625,322.9166666666667]}},{"id":"55:30","name":"55:30","data":{"weight":1,"coords":[227.4724091666667,150.533725,625,322.9166666666667]}},{"id":"30:-110","name":"30:-110","data":{"weight":1,"coords":[1170.3528831243123,255.74758218124578,625,322.9166666666667]}},{"id":"20:-75","name":"20:-75","data":{"weight":1,"coords":[1311.1083450025867,297.760827233943,625,322.9166666666667]}},{"id":"-10:145","name":"-10:145","data":{"weight":1,"coords":[725.7803927181412,414.3601479591237,625,322.9166666666667]}},{"id":"30:-20","name":"30:-20","data":{"weight":1,"coords":[44.766584083788054,256.4224766251944,625,322.9166666666667]}},{"id":"10:5","name":"10:5","data":{"weight":1,"coords":[143.6872147613182,337.28349762462864,625,322.9166666666667]}},{"id":"-35:-65","name":"-35:-65","data":{"weight":1,"coords":[1343.9121070833332,505.4656820833334,625,322.9166666666667]}},{"id":"-5:-50","name":"-5:-50","data":{"weight":1,"coords":[1410.5676141666668,380.9781104166667,625,322.9166666666667]}},{"id":"45:-10","name":"45:-10","data":{"weight":1,"coords":[78.17444476449123,193.81995769599905,625,322.9166666666667]}},{"id":"-5:-85","name":"-5:-85","data":{"weight":1,"coords":[1277.2487920825786,379.3451222316341,625,322.9166666666667]}},{"id":"35:135","name":"35:135","data":{"weight":1,"coords":[678.1500012851928,229.10924081211363,625,322.9166666666667]}},{"id":"-10:40","name":"-10:40","data":{"weight":1,"coords":[275.9445727388934,403.4583397299513,625,322.9166666666667]}},{"id":"5:115","name":"5:115","data":{"weight":1,"coords":[584.7007164019961,360.2498395917234,625,322.9166666666667]}},{"id":"35:35","name":"35:35","data":{"weight":1,"coords":[260.590875138889,233.8411780555556,625,322.9166666666667]}},{"id":"15:145","name":"15:145","data":{"weight":1,"coords":[715.61446369698,318.88749111602334,625,322.9166666666667]}},{"id":"-35:115","name":"-35:115","data":{"weight":1,"coords":[594.6986992130179,510.503356912929,625,322.9166666666667]}},{"id":"15:-60","name":"15:-60","data":{"weight":1,"coords":[1364.1114133422266,320.42878235083754,625,322.9166666666667]}},{"id":"60:15","name":"60:15","data":{"weight":1,"coords":[179.074484764435,127.0340926161067,625,322.9166666666667]}},{"id":"30:75","name":"30:75","data":{"weight":1,"coords":[433.6284369929451,256.3450535577787,625,322.9166666666667]}},{"id":"60:25","name":"60:25","data":{"weight":1,"coords":[216.26794665251765,124.32643887250993,625,322.9166666666667]}},{"id":"50:-10","name":"50:-10","data":{"weight":1,"coords":[91.32579057475448,165.4697239280589,625,322.9166666666667]}},{"id":"-5:100","name":"-5:100","data":{"weight":1,"coords":[530.4614125,378.20680875000005,625,322.9166666666667]}},{"id":"30:-115","name":"30:-115","data":{"weight":1,"coords":[1150.1731523726874,253.8223631605374,625,322.9166666666667]}},{"id":"15:-65","name":"15:-65","data":{"weight":1,"coords":[1358.9472207983083,314.2241137236833,625,322.9166666666667]}},{"id":"45:15","name":"45:15","data":{"weight":1,"coords":[170.3747645833334,197.36440583333342,625,322.9166666666667]}},{"id":"5:-5","name":"5:-5","data":{"weight":1,"coords":[111.37198749999999,351.9543666666667,625,322.9166666666667]}},{"id":"10:10","name":"10:10","data":{"weight":1,"coords":[144.06083333333336,337.407375,625,322.9166666666667]}},{"id":"-30:150","name":"-30:150","data":{"weight":1,"coords":[744.7372590663213,498.9011845901831,625,322.9166666666667]}},{"id":"65:15","name":"65:15","data":{"weight":1,"coords":[177.6426730931963,114.45241995774916,625,322.9166666666667]}},{"id":"60:-10","name":"60:-10","data":{"weight":1,"coords":[86.54298087628452,132.49253550104947,625,322.9166666666667]}},{"id":"50:-120","name":"50:-120","data":{"weight":1,"coords":[1121.201334389556,172.70110227344105,625,322.9166666666667]}},{"id":"35:-115","name":"35:-115","data":{"weight":1,"coords":[1145.5377904227041,235.6184418698416,625,322.9166666666667]}},{"id":"20:75","name":"20:75","data":{"weight":1,"coords":[416.02487538704895,295.6821917098626,625,322.9166666666667]}},{"id":"15:-95","name":"15:-95","data":{"weight":1,"coords":[1235.1122058333335,314.56008625,625,322.9166666666667]}},{"id":"10:-70","name":"10:-70","data":{"weight":1,"coords":[1329.6371666666669,332.38458333333335,625,322.9166666666667]}},{"id":"10:-80","name":"10:-80","data":{"weight":1,"coords":[1296.4036411535023,338.48302515333245,625,322.9166666666667]}},{"id":"45:-90","name":"45:-90","data":{"weight":1,"coords":[1255.8272450855677,195.33681381785925,625,322.9166666666667]}},{"id":"20:-90","name":"20:-90","data":{"weight":1,"coords":[1245.9728750000002,300.3478333333333,625,322.9166666666667]}},{"id":"-45:145","name":"-45:145","data":{"weight":1,"coords":[725.5986531503621,547.6708685748025,625,322.9166666666667]}},{"id":"15:-90","name":"15:-90","data":{"weight":1,"coords":[1240.6951263375572,317.86143971637057,625,322.9166666666667]}},{"id":"50:105","name":"50:105","data":{"weight":1,"coords":[557.8041133333334,175.35110625000004,625,322.9166666666667]}},{"id":"50:-105","name":"50:-105","data":{"weight":1,"coords":[1176.3680652698208,164.71671766997565,625,322.9166666666667]}},{"id":"55:35","name":"55:35","data":{"weight":1,"coords":[268.7105458973065,143.1365891931532,625,322.9166666666667]}},{"id":"40:-110","name":"40:-110","data":{"weight":1,"coords":[1168.6752123805186,209.87442780771175,625,322.9166666666667]}},{"id":"-20:175","name":"-20:175","data":{"weight":1,"coords":[851.5223004166666,445.51308375,625,322.9166666666667]}},{"id":"70:20","name":"70:20","data":{"weight":1,"coords":[191.69674424927595,84.78363475269623,625,322.9166666666667]}},{"id":"5:-60","name":"5:-60","data":{"weight":1,"coords":[1370.2311875,346.66466249999996,625,322.9166666666667]}},{"id":"15:105","name":"15:105","data":{"weight":1,"coords":[542.8206845833333,307.51731458333336,625,322.9166666666667]}},{"id":"60:-155","name":"60:-155","data":{"weight":1,"coords":[977.5737644541666,134.1883491862501,625,322.9166666666667]}},{"id":"-15:30","name":"-15:30","data":{"weight":1,"coords":[231.01945833333335,435.19129166666664,625,322.9166666666667]}},{"id":"30:80","name":"30:80","data":{"weight":1,"coords":[437.66673125,248.57506666666666,625,322.9166666666667]}},{"id":"-30:-75","name":"-30:-75","data":{"weight":1,"coords":[1315.2816774786477,499.89100493269495,625,322.9166666666667]}},{"id":"-35:-55","name":"-35:-55","data":{"weight":1,"coords":[1394.2460766190804,507.336446648528,625,322.9166666666667]}},{"id":"55:10","name":"55:10","data":{"weight":1,"coords":[155.0372542981961,141.43442275992527,625,322.9166666666667]}},{"id":"-45:-75","name":"-45:-75","data":{"weight":1,"coords":[1307.839756108828,544.1143282045969,625,322.9166666666667]}},{"id":"35:-105","name":"35:-105","data":{"weight":1,"coords":[1187.5354070869919,234.62555867108733,625,322.9166666666667]}},{"id":"-35:25","name":"-35:25","data":{"weight":1,"coords":[218.85095833333332,516.40925,625,322.9166666666667]}},{"id":"25:60","name":"25:60","data":{"weight":1,"coords":[354.9854492732105,276.5615984983524,625,322.9166666666667]}},{"id":"-10:120","name":"-10:120","data":{"weight":1,"coords":[610.470375,396.38233333333335,625,322.9166666666667]}},{"id":"15:95","name":"15:95","data":{"weight":1,"coords":[513.0565691666666,304.5813725,625,322.9166666666667]}},{"id":"-5:35","name":"-5:35","data":{"weight":1,"coords":[265.9671670833333,380.32392708333333,625,322.9166666666667]}},{"id":"10:100","name":"10:100","data":{"weight":1,"coords":[522.2229328475983,341.56108950261637,625,322.9166666666667]}},{"id":"-25:-55","name":"-25:-55","data":{"weight":1,"coords":[1399.0463414719898,472.1474189612911,625,322.9166666666667]}},{"id":"25:110","name":"25:110","data":{"weight":1,"coords":[576.2848995258174,277.08590950037234,625,322.9166666666667]}},{"id":"50:15","name":"50:15","data":{"weight":1,"coords":[168.05362958333336,156.2869204166667,625,322.9166666666667]}},{"id":"40:5","name":"40:5","data":{"weight":1,"coords":[123.55646500000006,209.9211008333333,625,322.9166666666667]}},{"id":"15:10","name":"15:10","data":{"weight":1,"coords":[144.23641574740043,320.9167469935329,625,322.9166666666667]}},{"id":"55:40","name":"55:40","data":{"weight":1,"coords":[269.1247878878915,142.70728330181464,625,322.9166666666667]}},{"id":"-40:-60","name":"-40:-60","data":{"weight":1,"coords":[1369.829844998876,521.54197260817,625,322.9166666666667]}},{"id":"50:45","name":"50:45","data":{"weight":1,"coords":[297.6004954166666,172.47916166666664,625,322.9166666666667]}},{"id":"-20:20","name":"-20:20","data":{"weight":1,"coords":[186.3060183333334,455.24207666666666,625,322.9166666666667]}},{"id":"65:-170","name":"65:-170","data":{"weight":1,"coords":[923.3069521427974,106.26164213797165,625,322.9166666666667]}},{"id":"-10:-45","name":"-10:-45","data":{"weight":1,"coords":[1444.6081981992647,400.0517761451425,625,322.9166666666667]}},{"id":"20:105","name":"20:105","data":{"weight":1,"coords":[558.5392994134651,288.6471516118564,625,322.9166666666667]}},{"id":"25:75","name":"25:75","data":{"weight":1,"coords":[414.8339785871118,279.08432821268593,625,322.9166666666667]}},{"id":"60:30","name":"60:30","data":{"weight":1,"coords":[238.81689441708335,125.44516386375005,625,322.9166666666667]}},{"id":"45:75","name":"45:75","data":{"weight":1,"coords":[433.15827362843186,194.85741959768097,625,322.9166666666667]}},{"id":"50:-130","name":"50:-130","data":{"weight":1,"coords":[1090.6703045116935,166.78836279013774,625,322.9166666666667]}},{"id":"-20:70","name":"-20:70","data":{"weight":1,"coords":[404.12059491527424,443.07111116365354,625,322.9166666666667]}},{"id":"-20:-45","name":"-20:-45","data":{"weight":1,"coords":[1429.4408857868627,458.158999162469,625,322.9166666666667]}},{"id":"30:30","name":"30:30","data":{"weight":1,"coords":[242.57754166666666,249.67308333333335,625,322.9166666666667]}},{"id":"-20:145","name":"-20:145","data":{"weight":1,"coords":[719.8086297333333,445.4291749470833,625,322.9166666666667]}},{"id":"-20:-75","name":"-20:-75","data":{"weight":1,"coords":[1319.8509312464923,449.9828852713806,625,322.9166666666667]}},{"id":"40:125","name":"40:125","data":{"weight":1,"coords":[641.6775539500001,218.39015356666664,625,322.9166666666667]}},{"id":"65:10","name":"65:10","data":{"weight":1,"coords":[155.58207299276532,110.94475340152246,625,322.9166666666667]}}] 2 | -------------------------------------------------------------------------------- /examples/data/json/test.json: -------------------------------------------------------------------------------- 1 | [{"id":1,"name":1,"data":{"coords":[100,71.42857,900,71.42857],"weight":5,"color":"rgb(241, 205, 15)"}},{"id":2,"name":2,"data":{"coords":[100,142.8571425,900,142.8571425],"weight":5,"color":"rgb(16, 99, 179)"}},{"id":3,"name":3,"data":{"coords":[100,214.2857,900,214.2857],"weight":5,"color":"rgb(147, 148, 58)"}},{"id":4,"name":4,"data":{"coords":[100,285.71425,900,285.71425],"weight":5,"color":"rgb(187, 71, 149)"}},{"id":5,"name":5,"data":{"coords":[100,357.142857,900,357.142857],"weight":5,"color":"rgb(244, 141, 72)"}},{"id":6,"name":6,"data":{"coords":[100,428.57142500000003,900,428.57142500000003],"weight":5,"color":"rgb(110, 196, 145)"}}] 2 | -------------------------------------------------------------------------------- /examples/data/json/testcrossed.json: -------------------------------------------------------------------------------- 1 | [{"id":1,"name":1,"data":{"coords":[100,71.42857,900,425],"weight":5,"color":"rgb(241, 205, 15)"}},{"id":2,"name":2,"data":{"coords":[100,142.8571425,900,375],"weight":20,"color":"rgb(16, 99, 179)"}},{"id":3,"name":3,"data":{"coords":[100,214.2857,900,325],"weight":5,"color":"rgb(147, 148, 58)"}},{"id":4,"name":4,"data":{"coords":[100,285.71425,900,285.71425],"weight":5,"color":"rgb(187, 71, 149)"}},{"id":5,"name":5,"data":{"coords":[100,357.142857,900,125],"weight":5,"color":"rgb(244, 141, 72)"}},{"id":6,"name":6,"data":{"coords":[100,428.57142500000003,900,75],"weight":5,"color":"rgb(110, 196, 145)"}}] 2 | -------------------------------------------------------------------------------- /examples/data/json/testuneven.json: -------------------------------------------------------------------------------- 1 | [{"id":1,"name":1,"data":{"coords":[100,100,900,100],"weight":5,"color":"rgb(241, 205, 15)"}},{"id":2,"name":2,"data":{"coords":[100,142.8571425,900,142.8571425],"weight":5,"color":"rgb(16, 99, 179)"}},{"id":3,"name":3,"data":{"coords":[100,250,900,250],"weight":5,"color":"rgb(147, 148, 58)"}},{"id":4,"name":4,"data":{"coords":[100,285.71425,900,285.71425],"weight":5,"color":"rgb(187, 71, 149)"}},{"id":5,"name":5,"data":{"coords":[100,300,900,300],"weight":5,"color":"rgb(244, 141, 72)"}},{"id":6,"name":6,"data":{"coords":[100,428.57142500000003,900,428.57142500000003],"weight":5,"color":"rgb(110, 196, 145)"}}] 2 | -------------------------------------------------------------------------------- /examples/data/philippines.json: -------------------------------------------------------------------------------- 1 | [{"id":"40:-75","name":"40:-75","data":{"weight":1,"coords":[1304.9106188785065,205.13313795761644,625,322.9166666666667]}},{"id":"35:-100","name":"35:-100","data":{"weight":1,"coords":[1209.076468948578,237.48368228188565,625,322.9166666666667]}},{"id":"20:-105","name":"20:-105","data":{"weight":1,"coords":[1181.5254472449135,288.79406806529823,625,322.9166666666667]}},{"id":"35:-125","name":"35:-125","data":{"weight":1,"coords":[1104.8978917693232,222.4057521535192,625,322.9166666666667]}},{"id":"25:65","name":"25:65","data":{"weight":1,"coords":[391.9190516666667,271.54868000000005,625,322.9166666666667]}},{"id":"45:-80","name":"45:-80","data":{"weight":1,"coords":[1281.6248761222005,193.0510938478069,625,322.9166666666667]}},{"id":"50:0","name":"50:0","data":{"weight":1,"coords":[122.47830117402809,170.945577212123,625,322.9166666666667]}},{"id":"0:125","name":"0:125","data":{"weight":1,"coords":[625.2948749999999,372.7673333333334,625,322.9166666666667]}},{"id":"25:-85","name":"25:-85","data":{"weight":1,"coords":[1278.6603607910317,267.40175071564244,625,322.9166666666667]}},{"id":"15:100","name":"15:100","data":{"weight":1,"coords":[531.5268246666667,317.4073610889487,625,322.9166666666667]}},{"id":"40:-125","name":"40:-125","data":{"weight":1,"coords":[1104.285033721935,218.64141215322877,625,322.9166666666667]}},{"id":"25:55","name":"25:55","data":{"weight":1,"coords":[342.6741522435898,270.12563621794874,625,322.9166666666667]}},{"id":"25:50","name":"25:50","data":{"weight":1,"coords":[327.2232221907142,269.61902790925893,625,322.9166666666667]}},{"id":"35:-80","name":"35:-80","data":{"weight":1,"coords":[1282.7410783215137,219.10498966234206,625,322.9166666666667]}},{"id":"45:-95","name":"45:-95","data":{"weight":1,"coords":[1223.55015315125,187.70838437999998,625,322.9166666666667]}},{"id":"50:-125","name":"50:-125","data":{"weight":1,"coords":[1099.3908258333336,170.12309958333333,625,322.9166666666667]}},{"id":"5:-65","name":"5:-65","data":{"weight":1,"coords":[1348.6648750000002,343.93874999999997,625,322.9166666666667]}},{"id":"-10:110","name":"-10:110","data":{"weight":1,"coords":[572.3727752499999,407.3121169583333,625,322.9166666666667]}},{"id":"35:-120","name":"35:-120","data":{"weight":1,"coords":[1132.379751710694,223.70700436032894,625,322.9166666666667]}},{"id":"30:-85","name":"30:-85","data":{"weight":1,"coords":[1276.5780988379047,257.8464011311371,625,322.9166666666667]}},{"id":"50:-115","name":"50:-115","data":{"weight":1,"coords":[1137.7914459754325,161.92917243945578,625,322.9166666666667]}},{"id":"35:-110","name":"35:-110","data":{"weight":1,"coords":[1168.3756384521098,228.90121287515228,625,322.9166666666667]}},{"id":"50:5","name":"50:5","data":{"weight":1,"coords":[138.224778188561,170.30501845941706,625,322.9166666666667]}},{"id":"50:10","name":"50:10","data":{"weight":1,"coords":[145.17543333333336,175.00413333333324,625,322.9166666666667]}},{"id":"30:35","name":"30:35","data":{"weight":1,"coords":[259.1767268471664,242.55837425205874,625,322.9166666666667]}},{"id":"35:-85","name":"35:-85","data":{"weight":1,"coords":[1260.8408165291974,234.20033959675482,625,322.9166666666667]}},{"id":"25:80","name":"25:80","data":{"weight":1,"coords":[446.97835749999996,264.67448999999993,625,322.9166666666667]}},{"id":"60:5","name":"60:5","data":{"weight":1,"coords":[136.30383072512493,129.5831814216666,625,322.9166666666667]}},{"id":"40:-105","name":"40:-105","data":{"weight":1,"coords":[1175.435311176278,209.5011490373356,625,322.9166666666667]}},{"id":"60:10","name":"60:10","data":{"weight":1,"coords":[157.50712099999996,125.46343662499999,625,322.9166666666667]}},{"id":"35:-10","name":"35:-10","data":{"weight":1,"coords":[87.82166916666665,219.47108291666666,625,322.9166666666667]}},{"id":"5:-75","name":"5:-75","data":{"weight":1,"coords":[1303.88259615292,355.66033516256545,625,322.9166666666667]}},{"id":"15:80","name":"15:80","data":{"weight":1,"coords":[435.8711962100673,321.09990109755364,625,322.9166666666667]}},{"id":"40:115","name":"40:115","data":{"weight":1,"coords":[597.7205262958333,208.7070079829167,625,322.9166666666667]}},{"id":"30:-100","name":"30:-100","data":{"weight":1,"coords":[1211.1040465064334,247.52785958105,625,322.9166666666667]}},{"id":"40:0","name":"40:0","data":{"weight":1,"coords":[114.18785083333334,206.82960458333332,625,322.9166666666667]}},{"id":"-40:145","name":"-40:145","data":{"weight":1,"coords":[716.6616277912862,532.4024791126712,625,322.9166666666667]}},{"id":"55:-10","name":"55:-10","data":{"weight":1,"coords":[84.73795837170664,151.44786934714182,625,322.9166666666667]}},{"id":"50:-5","name":"50:-5","data":{"weight":1,"coords":[112.01395458333334,160.1125695833333,625,322.9166666666667]}},{"id":"20:-70","name":"20:-70","data":{"weight":1,"coords":[1322.855875314875,294.47083227561745,625,322.9166666666667]}},{"id":"40:-80","name":"40:-80","data":{"weight":1,"coords":[1291.3453822952497,212.5853147589021,625,322.9166666666667]}},{"id":"30:50","name":"30:50","data":{"weight":1,"coords":[312.50414811891466,252.59718113673767,625,322.9166666666667]}},{"id":"-35:150","name":"-35:150","data":{"weight":1,"coords":[741.4645488146963,515.7917705365837,625,322.9166666666667]}},{"id":"-35:-75","name":"-35:-75","data":{"weight":1,"coords":[1318.3228075475652,514.403559171715,625,322.9166666666667]}},{"id":"40:-90","name":"40:-90","data":{"weight":1,"coords":[1242.0273367916668,215.31739454166677,625,322.9166666666667]}},{"id":"40:-95","name":"40:-95","data":{"weight":1,"coords":[1227.8464558333333,198.06664375000003,625,322.9166666666667]}},{"id":"0:105","name":"0:105","data":{"weight":1,"coords":[545.4697083333334,369.424125,625,322.9166666666667]}},{"id":"45:-75","name":"45:-75","data":{"weight":1,"coords":[1319.2373769235219,193.92174121044548,625,322.9166666666667]}},{"id":"55:-5","name":"55:-5","data":{"weight":1,"coords":[103.75647253821718,155.48500599086333,625,322.9166666666667]}},{"id":"45:5","name":"45:5","data":{"weight":1,"coords":[135.46777500000005,194.54172916666676,625,322.9166666666667]}},{"id":"-10:105","name":"-10:105","data":{"weight":1,"coords":[557.4175441666667,401.29809124999997,625,322.9166666666667]}},{"id":"-35:145","name":"-35:145","data":{"weight":1,"coords":[707.4370401218488,519.1281413427965,625,322.9166666666667]}},{"id":"40:-10","name":"40:-10","data":{"weight":1,"coords":[74.58732257286727,213.55200664398842,625,322.9166666666667]}},{"id":"40:-85","name":"40:-85","data":{"weight":1,"coords":[1260.0642657500002,204.95706562499993,625,322.9166666666667]}},{"id":"-5:105","name":"-5:105","data":{"weight":1,"coords":[548.8233749999999,387.3250833333334,625,322.9166666666667]}},{"id":"35:140","name":"35:140","data":{"weight":1,"coords":[688.5197983333334,220.08984208333334,625,322.9166666666667]}},{"id":"5:5","name":"5:5","data":{"weight":1,"coords":[135.85512500000002,348.2576666666667,625,322.9166666666667]}},{"id":"-35:-60","name":"-35:-60","data":{"weight":1,"coords":[1369.2728216666665,519.7663375,625,322.9166666666667]}},{"id":"25:-105","name":"25:-105","data":{"weight":1,"coords":[1194.2976190238055,268.08199854753093,625,322.9166666666667]}},{"id":"-30:25","name":"-30:25","data":{"weight":1,"coords":[225.39097380369236,486.21214687601775,625,322.9166666666667]}},{"id":"25:-110","name":"25:-110","data":{"weight":1,"coords":[1155.6691774581366,263.2411667327084,625,322.9166666666667]}},{"id":"55:-115","name":"55:-115","data":{"weight":1,"coords":[1139.4246790601871,153.10072338912266,625,322.9166666666667]}},{"id":"40:-5","name":"40:-5","data":{"weight":1,"coords":[97.06853628158333,206.5973633804167,625,322.9166666666667]}},{"id":"45:-125","name":"45:-125","data":{"weight":1,"coords":[1101.2863910985075,185.10633852547983,625,322.9166666666667]}},{"id":"-30:-50","name":"-30:-50","data":{"weight":1,"coords":[1408.0807456862483,485.3774137505155,625,322.9166666666667]}},{"id":"35:-5","name":"35:-5","data":{"weight":1,"coords":[97.52974499999995,220.08792291666657,625,322.9166666666667]}},{"id":"30:-95","name":"30:-95","data":{"weight":1,"coords":[1229.1081286497567,249.3890161275625,625,322.9166666666667]}},{"id":"35:-90","name":"35:-90","data":{"weight":1,"coords":[1243.3713586764695,230.51184526398367,625,322.9166666666667]}},{"id":"10:-75","name":"10:-75","data":{"weight":1,"coords":[1311.5501116617706,342.3736578339983,625,322.9166666666667]}},{"id":"-45:175","name":"-45:175","data":{"weight":1,"coords":[840.7626478479974,547.0439528276675,625,322.9166666666667]}},{"id":"20:-100","name":"20:-100","data":{"weight":1,"coords":[1198.939928732309,294.10599512021514,625,322.9166666666667]}},{"id":"20:100","name":"20:100","data":{"weight":1,"coords":[524.95925,295.06416666666667,625,322.9166666666667]}},{"id":"25:120","name":"25:120","data":{"weight":1,"coords":[613.6094887500001,279.2907266666667,625,322.9166666666667]}},{"id":"-5:120","name":"-5:120","data":{"weight":1,"coords":[610.6099191666667,395.0627475,625,322.9166666666667]}},{"id":"5:100","name":"5:100","data":{"weight":1,"coords":[523.5264741666667,360.1322941666667,625,322.9166666666667]}},{"id":"-35:20","name":"-35:20","data":{"weight":1,"coords":[189.08879434757702,516.4564905109631,625,322.9166666666667]}},{"id":"-35:140","name":"-35:140","data":{"weight":1,"coords":[690.1295706997546,520.118081965125,625,322.9166666666667]}},{"id":"35:-95","name":"35:-95","data":{"weight":1,"coords":[1226.8775606518632,230.86707540157784,625,322.9166666666667]}},{"id":"45:-5","name":"45:-5","data":{"weight":1,"coords":[94.74607916666666,196.6792591666666,625,322.9166666666667]}},{"id":"45:25","name":"45:25","data":{"weight":1,"coords":[219.24467096162056,195.537055625,625,322.9166666666667]}},{"id":"-25:-50","name":"-25:-50","data":{"weight":1,"coords":[1418.1105310833334,473.17707475000003,625,322.9166666666667]}},{"id":"20:45","name":"20:45","data":{"weight":1,"coords":[289.84066333333334,298.9647370833334,625,322.9166666666667]}},{"id":"-5:-80","name":"-5:-80","data":{"weight":1,"coords":[1279.6294683333335,383.98176125,625,322.9166666666667]}},{"id":"45:10","name":"45:10","data":{"weight":1,"coords":[148.12791534248407,177.6543980188195,625,322.9166666666667]}},{"id":"45:-70","name":"45:-70","data":{"weight":1,"coords":[1335.2876485368986,183.8627140546455,625,322.9166666666667]}},{"id":"55:15","name":"55:15","data":{"weight":1,"coords":[168.4546915690106,156.13387718511876,625,322.9166666666667]}},{"id":"40:10","name":"40:10","data":{"weight":1,"coords":[164.4503638011199,200.3171814578327,625,322.9166666666667]}},{"id":"5:-80","name":"5:-80","data":{"weight":1,"coords":[1297.5554433837815,348.9673538117091,625,322.9166666666667]}},{"id":"60:-135","name":"60:-135","data":{"weight":1,"coords":[1051.7346472767724,131.74205705519103,625,322.9166666666667]}},{"id":"50:-100","name":"50:-100","data":{"weight":1,"coords":[1207.689199084039,167.1255404360519,625,322.9166666666667]}},{"id":"40:25","name":"40:25","data":{"weight":1,"coords":[211.59197916666665,216.1606875,625,322.9166666666667]}},{"id":"40:-115","name":"40:-115","data":{"weight":1,"coords":[1146.954802639722,206.86115609808417,625,322.9166666666667]}},{"id":"-15:130","name":"-15:130","data":{"weight":1,"coords":[657.8926544729271,426.5906325472505,625,322.9166666666667]}},{"id":"25:-100","name":"25:-100","data":{"weight":1,"coords":[1203.313798480286,265.71174280242127,625,322.9166666666667]}},{"id":"-30:155","name":"-30:155","data":{"weight":1,"coords":[750.247344688795,489.56973194515103,625,322.9166666666667]}},{"id":"20:40","name":"20:40","data":{"weight":1,"coords":[275.4148209755176,284.3001977510794,625,322.9166666666667]}},{"id":"10:-65","name":"10:-65","data":{"weight":1,"coords":[1356.3782916666669,332.21875,625,322.9166666666667]}},{"id":"20:-80","name":"20:-80","data":{"weight":1,"coords":[1288.0369925000002,299.84814458333335,625,322.9166666666667]}},{"id":"25:130","name":"25:130","data":{"weight":1,"coords":[644.9406746080799,265.19526263425746,625,322.9166666666667]}},{"id":"20:115","name":"20:115","data":{"weight":1,"coords":[588.03066875,281.8533079166666,625,322.9166666666667]}},{"id":"-40:175","name":"-40:175","data":{"weight":1,"coords":[840.9526289685975,528.7548727076054,625,322.9166666666667]}},{"id":"-30:-60","name":"-30:-60","data":{"weight":1,"coords":[1372.3452983333334,480.4339575,625,322.9166666666667]}},{"id":"30:-90","name":"30:-90","data":{"weight":1,"coords":[1256.12510847625,248.97370611625,625,322.9166666666667]}},{"id":"25:40","name":"25:40","data":{"weight":1,"coords":[277.6444833333333,272.7846229166667,625,322.9166666666667]}},{"id":"-40:-75","name":"-40:-75","data":{"weight":1,"coords":[1308.1476374999997,528.4455483333334,625,322.9166666666667]}},{"id":"40:-120","name":"40:-120","data":{"weight":1,"coords":[1113.6189055044613,210.16071597935013,625,322.9166666666667]}},{"id":"40:15","name":"40:15","data":{"weight":1,"coords":[180.23622252519024,215.6801393719769,625,322.9166666666667]}},{"id":"10:105","name":"10:105","data":{"weight":1,"coords":[549.6579470833333,326.9187966666667,625,322.9166666666667]}},{"id":"-30:30","name":"-30:30","data":{"weight":1,"coords":[241.74179166666667,499.36366666666663,625,322.9166666666667]}},{"id":"5:120","name":"5:120","data":{"weight":1,"coords":[602.4900833333334,361.27770833333335,625,322.9166666666667]}},{"id":"-25:-45","name":"-25:-45","data":{"weight":1,"coords":[1431.7312951391666,470.68680082333333,625,322.9166666666667]}},{"id":"-15:-40","name":"-15:-40","data":{"weight":1,"coords":[1458.1843441666667,420.4349079166667,625,322.9166666666667]}},{"id":"50:20","name":"50:20","data":{"weight":1,"coords":[191.90575604187038,177.05628165122823,625,322.9166666666667]}},{"id":"-20:-50","name":"-20:-50","data":{"weight":1,"coords":[1412.7970833333334,440.74375,625,322.9166666666667]}},{"id":"-5:-40","name":"-5:-40","data":{"weight":1,"coords":[1451.9045833333332,390.49333333333334,625,322.9166666666667]}},{"id":"20:-160","name":"20:-160","data":{"weight":1,"coords":[960.41254261676,287.90859372262946,625,322.9166666666667]}},{"id":"25:45","name":"25:45","data":{"weight":1,"coords":[307.2490433333333,272.16074875,625,322.9166666666667]}},{"id":"-15:-80","name":"-15:-80","data":{"weight":1,"coords":[1291.5018714258854,425.2330336490567,625,322.9166666666667]}},{"id":"-10:115","name":"-10:115","data":{"weight":1,"coords":[592.5769166666666,411.11441666666667,625,322.9166666666667]}},{"id":"60:-150","name":"60:-150","data":{"weight":1,"coords":[987.6449086697543,120.15777424652606,625,322.9166666666667]}},{"id":"35:130","name":"35:130","data":{"weight":1,"coords":[663.8889034050997,232.0716772809226,625,322.9166666666667]}},{"id":"20:-95","name":"20:-95","data":{"weight":1,"coords":[1235.0216125422255,292.4660324996489,625,322.9166666666667]}},{"id":"55:0","name":"55:0","data":{"weight":1,"coords":[118.23796969696971,156.06209469696955,625,322.9166666666667]}},{"id":"65:-150","name":"65:-150","data":{"weight":1,"coords":[996.3835065247342,104.79851782995252,625,322.9166666666667]}},{"id":"-40:150","name":"-40:150","data":{"weight":1,"coords":[733.7135308712906,522.1121285329386,625,322.9166666666667]}},{"id":"60:20","name":"60:20","data":{"weight":1,"coords":[187.64483683510596,127.76624265507974,625,322.9166666666667]}},{"id":"10:-85","name":"10:-85","data":{"weight":1,"coords":[1277.37545,341.8303516666667,625,322.9166666666667]}},{"id":"-5:135","name":"-5:135","data":{"weight":1,"coords":[682.8690833333333,393.8329166666667,625,322.9166666666667]}},{"id":"40:30","name":"40:30","data":{"weight":1,"coords":[233.28996250000003,203.98148458333344,625,322.9166666666667]}},{"id":"-30:-55","name":"-30:-55","data":{"weight":1,"coords":[1393.9456766666667,492.87424,625,322.9166666666667]}},{"id":"45:-85","name":"45:-85","data":{"weight":1,"coords":[1265.6322454166668,197.75632125000004,625,322.9166666666667]}},{"id":"-10:-40","name":"-10:-40","data":{"weight":1,"coords":[1465.773320781706,399.31590663345776,625,322.9166666666667]}},{"id":"40:-100","name":"40:-100","data":{"weight":1,"coords":[1214.970790113441,212.58243074125062,625,322.9166666666667]}},{"id":"35:125","name":"35:125","data":{"weight":1,"coords":[642.1669525,221.4014295833333,625,322.9166666666667]}},{"id":"55:30","name":"55:30","data":{"weight":1,"coords":[227.4724091666667,150.533725,625,322.9166666666667]}},{"id":"30:-110","name":"30:-110","data":{"weight":1,"coords":[1170.3528831243123,255.74758218124578,625,322.9166666666667]}},{"id":"20:-75","name":"20:-75","data":{"weight":1,"coords":[1311.1083450025867,297.760827233943,625,322.9166666666667]}},{"id":"-10:145","name":"-10:145","data":{"weight":1,"coords":[725.7803927181412,414.3601479591237,625,322.9166666666667]}},{"id":"30:-20","name":"30:-20","data":{"weight":1,"coords":[44.766584083788054,256.4224766251944,625,322.9166666666667]}},{"id":"10:5","name":"10:5","data":{"weight":1,"coords":[143.6872147613182,337.28349762462864,625,322.9166666666667]}},{"id":"-35:-65","name":"-35:-65","data":{"weight":1,"coords":[1343.9121070833332,505.4656820833334,625,322.9166666666667]}},{"id":"-5:-50","name":"-5:-50","data":{"weight":1,"coords":[1410.5676141666668,380.9781104166667,625,322.9166666666667]}},{"id":"45:-10","name":"45:-10","data":{"weight":1,"coords":[78.17444476449123,193.81995769599905,625,322.9166666666667]}},{"id":"-5:-85","name":"-5:-85","data":{"weight":1,"coords":[1277.2487920825786,379.3451222316341,625,322.9166666666667]}},{"id":"35:135","name":"35:135","data":{"weight":1,"coords":[678.1500012851928,229.10924081211363,625,322.9166666666667]}},{"id":"-10:40","name":"-10:40","data":{"weight":1,"coords":[275.9445727388934,403.4583397299513,625,322.9166666666667]}},{"id":"5:115","name":"5:115","data":{"weight":1,"coords":[584.7007164019961,360.2498395917234,625,322.9166666666667]}},{"id":"35:35","name":"35:35","data":{"weight":1,"coords":[260.590875138889,233.8411780555556,625,322.9166666666667]}},{"id":"15:145","name":"15:145","data":{"weight":1,"coords":[715.61446369698,318.88749111602334,625,322.9166666666667]}},{"id":"-35:115","name":"-35:115","data":{"weight":1,"coords":[594.6986992130179,510.503356912929,625,322.9166666666667]}},{"id":"15:-60","name":"15:-60","data":{"weight":1,"coords":[1364.1114133422266,320.42878235083754,625,322.9166666666667]}},{"id":"60:15","name":"60:15","data":{"weight":1,"coords":[179.074484764435,127.0340926161067,625,322.9166666666667]}},{"id":"30:75","name":"30:75","data":{"weight":1,"coords":[433.6284369929451,256.3450535577787,625,322.9166666666667]}},{"id":"60:25","name":"60:25","data":{"weight":1,"coords":[216.26794665251765,124.32643887250993,625,322.9166666666667]}},{"id":"50:-10","name":"50:-10","data":{"weight":1,"coords":[91.32579057475448,165.4697239280589,625,322.9166666666667]}},{"id":"-5:100","name":"-5:100","data":{"weight":1,"coords":[530.4614125,378.20680875000005,625,322.9166666666667]}},{"id":"30:-115","name":"30:-115","data":{"weight":1,"coords":[1150.1731523726874,253.8223631605374,625,322.9166666666667]}},{"id":"15:-65","name":"15:-65","data":{"weight":1,"coords":[1358.9472207983083,314.2241137236833,625,322.9166666666667]}},{"id":"45:15","name":"45:15","data":{"weight":1,"coords":[170.3747645833334,197.36440583333342,625,322.9166666666667]}},{"id":"5:-5","name":"5:-5","data":{"weight":1,"coords":[111.37198749999999,351.9543666666667,625,322.9166666666667]}},{"id":"10:10","name":"10:10","data":{"weight":1,"coords":[144.06083333333336,337.407375,625,322.9166666666667]}},{"id":"-30:150","name":"-30:150","data":{"weight":1,"coords":[744.7372590663213,498.9011845901831,625,322.9166666666667]}},{"id":"65:15","name":"65:15","data":{"weight":1,"coords":[177.6426730931963,114.45241995774916,625,322.9166666666667]}},{"id":"60:-10","name":"60:-10","data":{"weight":1,"coords":[86.54298087628452,132.49253550104947,625,322.9166666666667]}},{"id":"50:-120","name":"50:-120","data":{"weight":1,"coords":[1121.201334389556,172.70110227344105,625,322.9166666666667]}},{"id":"35:-115","name":"35:-115","data":{"weight":1,"coords":[1145.5377904227041,235.6184418698416,625,322.9166666666667]}},{"id":"20:75","name":"20:75","data":{"weight":1,"coords":[416.02487538704895,295.6821917098626,625,322.9166666666667]}},{"id":"15:-95","name":"15:-95","data":{"weight":1,"coords":[1235.1122058333335,314.56008625,625,322.9166666666667]}},{"id":"10:-70","name":"10:-70","data":{"weight":1,"coords":[1329.6371666666669,332.38458333333335,625,322.9166666666667]}},{"id":"10:-80","name":"10:-80","data":{"weight":1,"coords":[1296.4036411535023,338.48302515333245,625,322.9166666666667]}},{"id":"45:-90","name":"45:-90","data":{"weight":1,"coords":[1255.8272450855677,195.33681381785925,625,322.9166666666667]}},{"id":"20:-90","name":"20:-90","data":{"weight":1,"coords":[1245.9728750000002,300.3478333333333,625,322.9166666666667]}},{"id":"-45:145","name":"-45:145","data":{"weight":1,"coords":[725.5986531503621,547.6708685748025,625,322.9166666666667]}},{"id":"15:-90","name":"15:-90","data":{"weight":1,"coords":[1240.6951263375572,317.86143971637057,625,322.9166666666667]}},{"id":"50:105","name":"50:105","data":{"weight":1,"coords":[557.8041133333334,175.35110625000004,625,322.9166666666667]}},{"id":"50:-105","name":"50:-105","data":{"weight":1,"coords":[1176.3680652698208,164.71671766997565,625,322.9166666666667]}},{"id":"55:35","name":"55:35","data":{"weight":1,"coords":[268.7105458973065,143.1365891931532,625,322.9166666666667]}},{"id":"40:-110","name":"40:-110","data":{"weight":1,"coords":[1168.6752123805186,209.87442780771175,625,322.9166666666667]}},{"id":"-20:175","name":"-20:175","data":{"weight":1,"coords":[851.5223004166666,445.51308375,625,322.9166666666667]}},{"id":"70:20","name":"70:20","data":{"weight":1,"coords":[191.69674424927595,84.78363475269623,625,322.9166666666667]}},{"id":"5:-60","name":"5:-60","data":{"weight":1,"coords":[1370.2311875,346.66466249999996,625,322.9166666666667]}},{"id":"15:105","name":"15:105","data":{"weight":1,"coords":[542.8206845833333,307.51731458333336,625,322.9166666666667]}},{"id":"60:-155","name":"60:-155","data":{"weight":1,"coords":[977.5737644541666,134.1883491862501,625,322.9166666666667]}},{"id":"-15:30","name":"-15:30","data":{"weight":1,"coords":[231.01945833333335,435.19129166666664,625,322.9166666666667]}},{"id":"30:80","name":"30:80","data":{"weight":1,"coords":[437.66673125,248.57506666666666,625,322.9166666666667]}},{"id":"-30:-75","name":"-30:-75","data":{"weight":1,"coords":[1315.2816774786477,499.89100493269495,625,322.9166666666667]}},{"id":"-35:-55","name":"-35:-55","data":{"weight":1,"coords":[1394.2460766190804,507.336446648528,625,322.9166666666667]}},{"id":"55:10","name":"55:10","data":{"weight":1,"coords":[155.0372542981961,141.43442275992527,625,322.9166666666667]}},{"id":"-45:-75","name":"-45:-75","data":{"weight":1,"coords":[1307.839756108828,544.1143282045969,625,322.9166666666667]}},{"id":"35:-105","name":"35:-105","data":{"weight":1,"coords":[1187.5354070869919,234.62555867108733,625,322.9166666666667]}},{"id":"-35:25","name":"-35:25","data":{"weight":1,"coords":[218.85095833333332,516.40925,625,322.9166666666667]}},{"id":"25:60","name":"25:60","data":{"weight":1,"coords":[354.9854492732105,276.5615984983524,625,322.9166666666667]}},{"id":"-10:120","name":"-10:120","data":{"weight":1,"coords":[610.470375,396.38233333333335,625,322.9166666666667]}},{"id":"15:95","name":"15:95","data":{"weight":1,"coords":[513.0565691666666,304.5813725,625,322.9166666666667]}},{"id":"-5:35","name":"-5:35","data":{"weight":1,"coords":[265.9671670833333,380.32392708333333,625,322.9166666666667]}},{"id":"10:100","name":"10:100","data":{"weight":1,"coords":[522.2229328475983,341.56108950261637,625,322.9166666666667]}},{"id":"-25:-55","name":"-25:-55","data":{"weight":1,"coords":[1399.0463414719898,472.1474189612911,625,322.9166666666667]}},{"id":"25:110","name":"25:110","data":{"weight":1,"coords":[576.2848995258174,277.08590950037234,625,322.9166666666667]}},{"id":"50:15","name":"50:15","data":{"weight":1,"coords":[168.05362958333336,156.2869204166667,625,322.9166666666667]}},{"id":"40:5","name":"40:5","data":{"weight":1,"coords":[123.55646500000006,209.9211008333333,625,322.9166666666667]}},{"id":"15:10","name":"15:10","data":{"weight":1,"coords":[144.23641574740043,320.9167469935329,625,322.9166666666667]}},{"id":"55:40","name":"55:40","data":{"weight":1,"coords":[269.1247878878915,142.70728330181464,625,322.9166666666667]}},{"id":"-40:-60","name":"-40:-60","data":{"weight":1,"coords":[1369.829844998876,521.54197260817,625,322.9166666666667]}},{"id":"50:45","name":"50:45","data":{"weight":1,"coords":[297.6004954166666,172.47916166666664,625,322.9166666666667]}},{"id":"-20:20","name":"-20:20","data":{"weight":1,"coords":[186.3060183333334,455.24207666666666,625,322.9166666666667]}},{"id":"65:-170","name":"65:-170","data":{"weight":1,"coords":[923.3069521427974,106.26164213797165,625,322.9166666666667]}},{"id":"-10:-45","name":"-10:-45","data":{"weight":1,"coords":[1444.6081981992647,400.0517761451425,625,322.9166666666667]}},{"id":"20:105","name":"20:105","data":{"weight":1,"coords":[558.5392994134651,288.6471516118564,625,322.9166666666667]}},{"id":"25:75","name":"25:75","data":{"weight":1,"coords":[414.8339785871118,279.08432821268593,625,322.9166666666667]}},{"id":"60:30","name":"60:30","data":{"weight":1,"coords":[238.81689441708335,125.44516386375005,625,322.9166666666667]}},{"id":"45:75","name":"45:75","data":{"weight":1,"coords":[433.15827362843186,194.85741959768097,625,322.9166666666667]}},{"id":"50:-130","name":"50:-130","data":{"weight":1,"coords":[1090.6703045116935,166.78836279013774,625,322.9166666666667]}},{"id":"-20:70","name":"-20:70","data":{"weight":1,"coords":[404.12059491527424,443.07111116365354,625,322.9166666666667]}},{"id":"-20:-45","name":"-20:-45","data":{"weight":1,"coords":[1429.4408857868627,458.158999162469,625,322.9166666666667]}},{"id":"30:30","name":"30:30","data":{"weight":1,"coords":[242.57754166666666,249.67308333333335,625,322.9166666666667]}},{"id":"-20:145","name":"-20:145","data":{"weight":1,"coords":[719.8086297333333,445.4291749470833,625,322.9166666666667]}},{"id":"-20:-75","name":"-20:-75","data":{"weight":1,"coords":[1319.8509312464923,449.9828852713806,625,322.9166666666667]}},{"id":"40:125","name":"40:125","data":{"weight":1,"coords":[641.6775539500001,218.39015356666664,625,322.9166666666667]}},{"id":"65:10","name":"65:10","data":{"weight":1,"coords":[155.58207299276532,110.94475340152246,625,322.9166666666667]}}] 2 | -------------------------------------------------------------------------------- /examples/data/test.json: -------------------------------------------------------------------------------- 1 | [{"id":1,"name":1,"data":{"coords":[100,71.42857,900,71.42857],"weight":5,"color":"rgb(241, 205, 15)"}},{"id":2,"name":2,"data":{"coords":[100,142.8571425,900,142.8571425],"weight":5,"color":"rgb(16, 99, 179)"}},{"id":3,"name":3,"data":{"coords":[100,214.2857,900,214.2857],"weight":5,"color":"rgb(147, 148, 58)"}},{"id":4,"name":4,"data":{"coords":[100,285.71425,900,285.71425],"weight":5,"color":"rgb(187, 71, 149)"}},{"id":5,"name":5,"data":{"coords":[100,357.142857,900,357.142857],"weight":5,"color":"rgb(244, 141, 72)"}},{"id":6,"name":6,"data":{"coords":[100,428.57142500000003,900,428.57142500000003],"weight":5,"color":"rgb(110, 196, 145)"}}] 2 | -------------------------------------------------------------------------------- /examples/data/testcrossed.json: -------------------------------------------------------------------------------- 1 | [{"id":1,"name":1,"data":{"coords":[100,71.42857,900,425],"weight":5,"color":"rgb(241, 205, 15)"}},{"id":2,"name":2,"data":{"coords":[100,142.8571425,900,375],"weight":5,"color":"rgb(16, 99, 179)"}},{"id":3,"name":3,"data":{"coords":[100,214.2857,900,325],"weight":5,"color":"rgb(147, 148, 58)"}},{"id":4,"name":4,"data":{"coords":[100,285.71425,900,285.71425],"weight":5,"color":"rgb(187, 71, 149)"}},{"id":5,"name":5,"data":{"coords":[100,357.142857,900,125],"weight":5,"color":"rgb(244, 141, 72)"}},{"id":6,"name":6,"data":{"coords":[100,428.57142500000003,900,75],"weight":5,"color":"rgb(110, 196, 145)"}}] 2 | -------------------------------------------------------------------------------- /examples/data/testuneven.json: -------------------------------------------------------------------------------- 1 | [{"id":1,"name":1,"data":{"coords":[100,100,900,100],"weight":5,"color":"rgb(241, 205, 15)"}},{"id":2,"name":2,"data":{"coords":[100,142.8571425,900,142.8571425],"weight":5,"color":"rgb(16, 99, 179)"}},{"id":3,"name":3,"data":{"coords":[100,250,900,250],"weight":5,"color":"rgb(147, 148, 58)"}},{"id":4,"name":4,"data":{"coords":[100,285.71425,900,285.71425],"weight":5,"color":"rgb(187, 71, 149)"}},{"id":5,"name":5,"data":{"coords":[100,300,900,300],"weight":5,"color":"rgb(244, 141, 72)"}},{"id":6,"name":6,"data":{"coords":[100,428.57142500000003,900,428.57142500000003],"weight":5,"color":"rgb(110, 196, 145)"}}] 2 | -------------------------------------------------------------------------------- /examples/ier/filter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/ier/images/philippines.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philogb/mingle/c7d10b62b55fe614842f855c32bbb113561359b8/examples/ier/images/philippines.jpg -------------------------------------------------------------------------------- /examples/ier/images/world.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philogb/mingle/c7d10b62b55fe614842f855c32bbb113561359b8/examples/ier/images/world.jpg -------------------------------------------------------------------------------- /examples/ier/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Examples for Improved Edge Rendering for MINGLE 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |

33 | Examples for Improved Edge Rendering for MINGLE 34 |

35 |

* means that a new computation for the layout is required. This 36 | might take some time (5 - 10 secs)

37 | 38 |
39 |
40 | 41 | 42 | 43 | 44 | 51 | 52 | 57 | 58 | 59 | 65 | 74 | 75 |
76 |
77 |
Loading please wait...
78 | 79 |
80 | 81 | 82 |
83 | 84 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /examples/ier/index.js: -------------------------------------------------------------------------------- 1 | /*global PhiloGL*/ 2 | (function() { 3 | 4 | PhiloGL.unpack(); 5 | 6 | var delta = 1, 7 | type = 'Quadratic', 8 | neighbors = 10, 9 | angleStrength = 3, 10 | curviness = 1, 11 | margin = 0, 12 | alpha = 0.5, 13 | color = '#00cccc', 14 | clusterColors = ["#FB8072", "#80B1D3", "#8DD3C7", "#FCCDE5", "#FDB462", "#FFFFB3", "#BEBADA", "#CCEBC5", "#B3DE69", "#BC80BD", "#FFED6F"], 15 | colorType = 'Solid', 16 | counter = 0, 17 | dataset = 'philippines', 18 | canvas = document.querySelector('canvas#renderer'), 19 | background = document.querySelector('.world-map'), 20 | dotsCanvas = document.querySelector('canvas#dots'), 21 | ctx = canvas.getContext('2d'), 22 | dotsCtx = dotsCanvas.getContext('2d'), 23 | ink = document.querySelector('#ink'), 24 | container = document.querySelector('.container'), 25 | directionSort = function(a, b) { 26 | var aColor, bColor; 27 | if (Array.isArray(a.data.color)) { 28 | aColor = a.data.color[0]; 29 | bColor = b.data.color[0]; 30 | if (aColor == bColor) { 31 | return 0; 32 | } 33 | return aColor > bColor ? 1 : -1; 34 | } 35 | }, 36 | xSort = function(a, b) { 37 | var diffX = a.data.coords[0] - b.data.coords[0], 38 | diffY = a.data.coords[1] - b.data.coords[1]; 39 | if (!diffX) { 40 | return diffY; 41 | } 42 | return diffX; 43 | }, 44 | ySort = function(a, b) { 45 | var diffX = a.data.coords[0] - b.data.coords[0], 46 | diffY = a.data.coords[1] - b.data.coords[1]; 47 | if (!diffY) { 48 | return diffX; 49 | } 50 | return -diffY; 51 | }, 52 | componentSort = function(a, b) { 53 | var diffX = Math.abs(a.data.coords[0] - b.data.coords[2]), 54 | diffY = Math.abs(a.data.coords[1] - b.data.coords[3]); 55 | 56 | return diffX > diffY ? ySort(a, b) : xSort(a, b); 57 | }, 58 | sortOptions = [ null, componentSort, xSort, ySort, directionSort ], 59 | sortType = null, 60 | options, 61 | imageData, 62 | jsonText, 63 | json, 64 | bundle, 65 | loader; 66 | 67 | function updateBundle() { 68 | bundle.options.angleStrength = angleStrength; 69 | bundle.options.sort = sortType; 70 | bundle.setNodes(JSON.parse(jsonText)); 71 | //ensure the direction is calculated before computing the graph. 72 | bundle.graph.each(function(n) { 73 | if (colorType == 'Direction') { 74 | if (!Array.isArray(n.data.color)) { 75 | n.data.color = Math.random() > 0.5 ? ['#0083ce', '#cf4846'] : ['#0083ce', '#cf4846'].reverse(); 76 | } 77 | } 78 | }); 79 | bundle.buildNearestNeighborGraph(neighbors); 80 | bundle.MINGLE(); 81 | } 82 | 83 | function calculateInk() { 84 | imageData = ctx.getImageData(0, 0, canvas.width, canvas.height).data; 85 | for (var i = 0, l = imageData.length, acum = 0; i < l; i += 4) { 86 | if (imageData[i + 3] != 0) { 87 | acum++; 88 | } 89 | } 90 | ink.innerText = Math.round((acum / (canvas.width * canvas.height)) * 10000) / 100; 91 | } 92 | 93 | (function loop() { 94 | Fx.requestAnimationFrame(loop); 95 | }()); 96 | 97 | function animate(bundle, canvas, ctx) { 98 | var loading = document.querySelector('.loading'); 99 | if (loading && loading.parentNode) { 100 | loading.parentNode.removeChild(loading); 101 | } 102 | 103 | new Fx({ 104 | transition: Fx.Transition.Quart.easeInOut, 105 | duration: 1000 106 | }).start({ 107 | onCompute: function(epsilon) { 108 | delta = epsilon; 109 | render(); 110 | } 111 | }); 112 | 113 | } 114 | 115 | dotsCanvas = document.querySelector('canvas#dots'); 116 | dotsCtx = dotsCanvas.getContext('2d'); 117 | 118 | function renderPoints() { 119 | dotsCtx.fillStyle = '#000'; 120 | dotsCanvas.width = dotsCanvas.width; 121 | json.forEach(function(edge) { 122 | dotsCtx.beginPath(); 123 | dotsCtx.arc(edge.data.coords[0], edge.data.coords[1], options.radius || 1, 0, Math.PI * 2, false); 124 | dotsCtx.fill(); 125 | dotsCtx.closePath(); 126 | dotsCtx.beginPath(); 127 | dotsCtx.arc(edge.data.coords[2], edge.data.coords[3], options.radius || 1, 0, Math.PI * 2, false); 128 | dotsCtx.fill(); 129 | dotsCtx.closePath(); 130 | }); 131 | } 132 | 133 | function render() { 134 | canvas.width = canvas.width; 135 | ctx.strokeStyle = 'rgba(0, 200, 200, 1)'; 136 | ctx.lineWidth = 1; 137 | counter = 0; 138 | bundle.graph.each(function(node) { 139 | var i = counter; 140 | if (node.data.nodeArray) { 141 | node.data.nodeArray.forEach(function(n, j) { 142 | if (colorType == 'Cluster') { 143 | n.data.color = clusterColors[i % clusterColors.length]; 144 | } else if (colorType == 'Solid') { 145 | n.data.color = color; 146 | } else if (colorType == 'Edge') { 147 | n.data.color = clusterColors[j % clusterColors.length]; 148 | } else if (colorType == 'Direction') { 149 | if (!Array.isArray(n.data.color)) { 150 | n.data.color = Math.random() > 0.5 ? ['#0083ce', '#cf4846'] : ['#0083ce', '#cf4846'].reverse(); 151 | } 152 | } 153 | n.data.alpha = alpha; 154 | }); 155 | } 156 | var edges = node.unbundleEdges(delta); 157 | Bundler.Graph['render' + type](ctx, edges, { 158 | delta: delta, 159 | margin: margin, 160 | curviness: curviness, 161 | scale: options.scale 162 | }); 163 | counter++; 164 | }); 165 | calculateInk(); 166 | } 167 | 168 | var bundleLabel = document.querySelector('#bundle-level-span'); 169 | bundleLabel.innerText = Math.round(delta * 1000) / 10 + '%'; 170 | document.querySelector('#bundle-level').addEventListener('mousemove', function() { 171 | delta = +this.value; 172 | bundleLabel.innerText = Math.round(delta * 1000) / 10 + '%'; 173 | render(canvas, ctx, bundle, delta, type, curviness); 174 | }); 175 | 176 | var curvinessLabel = document.querySelector('#curviness-span'); 177 | curvinessLabel.innerText = Math.round(curviness * 1000) / 10 + '%'; 178 | document.querySelector('#curviness').addEventListener('mousemove', function() { 179 | curviness = +this.value; 180 | curvinessLabel.innerText = Math.round(curviness * 1000) / 10 + '%'; 181 | render(canvas, ctx, bundle, delta, type, curviness); 182 | }); 183 | 184 | var marginLabel = document.querySelector('#margin-span'); 185 | marginLabel.innerText = Math.round(margin * 10) / 10; 186 | document.querySelector('#margin').addEventListener('mousemove', function() { 187 | margin = +this.value; 188 | marginLabel.innerText = Math.round(margin * 10) / 10; 189 | render(canvas, ctx, bundle, delta, type, curviness); 190 | }); 191 | 192 | var angleLabel = document.querySelector('#angle-strength-span'); 193 | angleLabel.innerText = Math.round(angleStrength * 10) / 10; 194 | document.querySelector('#angle-strength').addEventListener('change', function() { 195 | angleStrength = +this.value; 196 | angleLabel.innerText = Math.round(angleStrength * 10) / 10; 197 | updateBundle(); 198 | render(canvas, ctx, bundle, delta, type, curviness); 199 | }); 200 | 201 | var neighborsLabel = document.querySelector('#neighbors-span'); 202 | neighborsLabel.innerText = Math.round(neighbors * 10) / 10; 203 | document.querySelector('#neighbors').addEventListener('change', function() { 204 | neighbors = +this.value; 205 | neighborsLabel.innerText = Math.round(neighbors * 10) / 10; 206 | updateBundle(); 207 | render(canvas, ctx, bundle, delta, type, curviness); 208 | }); 209 | 210 | document.querySelector('#line-type').addEventListener('change', function() { 211 | type = this.value; 212 | render(canvas, ctx, bundle, delta, type, curviness); 213 | }); 214 | 215 | document.querySelector('#color').addEventListener('change', function() { 216 | color = this.value; 217 | render(canvas, ctx, bundle, delta, type, curviness); 218 | }); 219 | 220 | var alphaLabel = document.querySelector('#alpha-span'); 221 | alphaLabel.innerText = Math.round(alpha * 10) / 10; 222 | document.querySelector('#alpha').addEventListener('mousemove', function() { 223 | alpha = this.value; 224 | alphaLabel.innerText = Math.round(alpha * 10) / 10; 225 | render(canvas, ctx, bundle, delta, type, curviness); 226 | }); 227 | 228 | document.querySelector('#color-type').addEventListener('change', function() { 229 | colorType = this.value; 230 | render(canvas, ctx, bundle, delta, type, curviness); 231 | }); 232 | 233 | document.querySelector('#dataset').addEventListener('change', function() { 234 | dataset = this.value; 235 | load(); 236 | }); 237 | 238 | document.querySelector('#sort-type').addEventListener('change', function() { 239 | sortType = sortOptions[ this.selectedIndex ]; 240 | updateBundle(); 241 | render(canvas, ctx, bundle, delta, type, curviness); 242 | }); 243 | 244 | loader = new Loader(); 245 | function load() { 246 | options = loader.load(dataset, { 247 | complete: function(data) { 248 | if (!data.length) { 249 | console.warn('no data returned'); 250 | return; 251 | } 252 | if (data.length > 5e3) { 253 | console.warn('data is too big', data.length); 254 | return; 255 | } 256 | background.className = 'world-map ' + dataset; 257 | canvas.width = dotsCanvas.width = options.width; 258 | canvas.height = dotsCanvas.height = options.height; 259 | 260 | canvas.style.width = dotsCanvas.style.width = (options.width / (options.ratio || 1)) + 'px'; 261 | canvas.style.height = dotsCanvas.style.height = (options.height / (options.ratio || 1)) + 'px'; 262 | 263 | container.style.width = canvas.offsetWidth + 'px'; 264 | background.style.width = canvas.offsetWidth + 'px'; 265 | background.style.height = canvas.offsetHeight + 'px'; 266 | 267 | json = data; 268 | renderPoints(); 269 | jsonText = JSON.stringify(json); 270 | console.time('MINGLE'); 271 | bundle = new Bundler(); 272 | bundle.options.angleStrength = angleStrength; 273 | bundle.options.sort = sortType; 274 | bundle.setNodes(json); 275 | bundle.graph.each(function(n) { 276 | if (colorType == 'Direction') { 277 | if (!Array.isArray(n.data.color)) { 278 | n.data.color = Math.random() > 0.5 ? ['#0083ce', '#cf4846'] : ['#0083ce', '#cf4846'].reverse(); 279 | } 280 | } 281 | }); 282 | bundle.buildNearestNeighborGraph(neighbors); 283 | bundle.MINGLE(); 284 | console.timeEnd('MINGLE'); 285 | animate(bundle, canvas, ctx); 286 | } 287 | }); 288 | } 289 | load(); 290 | 291 | })(); 292 | -------------------------------------------------------------------------------- /examples/ier/loader.js: -------------------------------------------------------------------------------- 1 | /*global PhiloGL, Float32Array */ 2 | (function () { 3 | Array.prototype.shuffle = function() { 4 | var len = this.length, 5 | rnd = Math.random, 6 | round = Math.round, 7 | i, index, aux; 8 | 9 | for (i = 0; i < len; ++i) { 10 | index = round(i + rnd() * (len - i - 1)); 11 | aux = this[i]; 12 | this[i] = this[index]; 13 | this[index] = aux; 14 | } 15 | return this; 16 | }; 17 | 18 | function graticulize(n, ratio) { 19 | var mod = n % ratio, 20 | nDiv = Math.floor(n / ratio), 21 | halfRatio = ratio / 2; 22 | 23 | if (mod > halfRatio) { 24 | return (nDiv + 1) * ratio; 25 | } 26 | 27 | return nDiv * ratio; 28 | } 29 | 30 | var colors = ["#E99774", "#F0C532", "#F3861E", "#EAD1A6", "#BA9A43", "#E79A50", "#F6D071", "#CAA572", "#E69C1E", "#F2825B", "#C29E20", "#F2B54D", "#F08741","#EDBF7B","#D59634","#ECBD9B","#F5B527","#F1C957","#E0A855","#C1A481","#EFD195","#E49B62","#C9A35F","#D3AE40","#F1A142","#C99444","#F7BC66","#DFBC5F","#D28E68","#C9A84E"].shuffle(); 31 | var colors2 = ["#8DD3C7", "#FFFFB3", "#BEBADA", "#FB8072", "#80B1D3", "#FDB462", "#B3DE69", "#FCCDE5"/*, "#D9D9D9"*/, "#BC80BD", "#CCEBC5", "#FFED6F"]; 32 | 33 | function Loader(options) { 34 | this.options = options; 35 | } 36 | 37 | Loader.prototype = { 38 | 39 | philippines : { 40 | url: '../data/json/philippines.json', 41 | scale: 2, 42 | latFrom: -90, 43 | latTo: 90, 44 | lonFrom: -180, 45 | lonTo: 180, 46 | width: 1500, 47 | height: 1500 * (90 - -90) / (180 - -180), 48 | ratio: 1, 49 | lineWidth: 0.5, 50 | radius: 3, 51 | fillStyle: 'rgba(0, 200, 200, 0.8)', 52 | strokeStyle: colors2 53 | }, 54 | 55 | sfcommute: { 56 | url: '../data/json/sfcommute.json', 57 | scale: 1.5, 58 | latFrom: 37.37, 59 | latTo: 37.86, 60 | lonFrom: -122.52, 61 | lonTo: -122.0, 62 | ratio: 1, 63 | lineWidth: 2, 64 | radius: 1, 65 | fillStyle: 'rgba(10, 10, 10, 0.01)', 66 | //strokeStyle: colors, 67 | strokeStyle: 'rgba(0, 200, 200, 0.2)', 68 | width: 600, 69 | height: 600 * (37.86 - 37.37) / (-122 - -122.52) 70 | }, 71 | 72 | world: { 73 | url: '../data/json/world.json', 74 | scale: 1, 75 | latFrom: -90, 76 | latTo: 90, 77 | lonFrom: -180, 78 | lonTo: 180, 79 | ratio: 2, 80 | width: 2000, 81 | height: 2000 * (90 - -90) / (180 - -180), 82 | lineWidth: 1, 83 | radius: 3, 84 | fillStyle: 'rgba(10, 10, 10, 0.01)', 85 | //strokeStyle: colors, 86 | strokeStyle: 'rgba(0, 200, 200, 0.1)' 87 | }, 88 | 89 | eastwestcommute: { 90 | url: '../data/json/eastwestcommute.json', 91 | scale: 2, 92 | latFrom: 23.6, 93 | latTo: 67.7, 94 | lonFrom: -89, 95 | lonTo: 32, 96 | ratio: 1, 97 | lineWidth: 2, 98 | fillStyle: 'rgba(10, 10, 10, 1)', 99 | radius: 2, 100 | strokeStyle: colors, 101 | width: 1000, 102 | height: 1000 * (67.7 - 23.6) / (32 - -89) 103 | }, 104 | 105 | test: { 106 | url: '../data/json/test.json', 107 | scale: 1, 108 | width: 1000, 109 | height: 500, 110 | ratio: 1, 111 | lineWidth: 2, 112 | radius: 3, 113 | fillStyle: 'rgba(10, 10, 10, 0.01)', 114 | strokeStyle: 'rgba(0, 200, 200, 0.2)', 115 | }, 116 | 117 | testuneven: { 118 | url: '../data/json/testuneven.json', 119 | scale: 1, 120 | width: 1000, 121 | height: 500, 122 | ratio: 1, 123 | lineWidth: 2, 124 | radius: 3, 125 | fillStyle: 'rgba(10, 10, 10, 0.01)', 126 | strokeStyle: 'rgba(0, 200, 200, 0.2)' 127 | }, 128 | 129 | testcrossed: { 130 | url: '../data/json/testcrossed.json', 131 | scale: 1, 132 | width: 1000, 133 | height: 500, 134 | ratio: 1, 135 | lineWidth: 2, 136 | radius: 3, 137 | fillStyle: 'rgba(10, 10, 10, 0.01)', 138 | //strokeStyle: colors, 139 | strokeStyle: 'rgba(0, 200, 200, 0.2)', 140 | }, 141 | 142 | load: function(key, options) { 143 | this.options = this[key]; 144 | this.options.complete = options.complete; 145 | this.send(); 146 | return this.options; 147 | }, 148 | 149 | send: function() { 150 | var that = this, 151 | options = this.options, 152 | url = options.url; 153 | 154 | new PhiloGL.IO.XHR({ 155 | url: url, 156 | onSuccess: function(text) { 157 | var json = JSON.parse(text); 158 | if (options.parse) { 159 | json = options.parse(json); 160 | } 161 | if (options.complete) { 162 | options.complete(json, options); 163 | } 164 | }, 165 | onError: function() { 166 | console.error('request failed'); 167 | } 168 | }).send(); 169 | } 170 | }; 171 | 172 | this.Loader = Loader; 173 | 174 | })(); 175 | 176 | 177 | -------------------------------------------------------------------------------- /examples/ier/styles/main.css: -------------------------------------------------------------------------------- 1 | /* line 5, ../../app/styles/main.scss */ 2 | 3 | body { 4 | /*background: #fafafa;*/ 5 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 6 | color: #999; 7 | text-align: center; 8 | } 9 | 10 | /* line 12, ../../app/styles/main.scss */ 11 | 12 | header > h1 { 13 | font-family: HelveticaNeue, "Helvetica Neue", Helvetica, Arial, sans-serif; 14 | color: #999; 15 | text-align: center; 16 | font-size: 30px; 17 | font-weight: normal; 18 | height: 30px; 19 | border-bottom: 1px solid #eeeeee; 20 | margin: 10px 30px 20px 30px; 21 | padding-bottom: 15px; 22 | } 23 | 24 | /* line 24, ../../app/styles/main.scss */ 25 | 26 | form { 27 | -webkit-column-count: 2; 28 | -moz-column-count: 2; 29 | column-count: 2; 30 | } 31 | 32 | /* line 28, ../../app/styles/main.scss */ 33 | 34 | .container { 35 | position: relative; 36 | width: 1200px; 37 | margin: 20px auto; 38 | } 39 | 40 | /* line 35, ../../app/styles/main.scss */ 41 | 42 | canvas { 43 | position: absolute; 44 | top: 0; 45 | left: 0; 46 | max-width: 1200px; 47 | max-height: 600px; 48 | } 49 | 50 | /* line 43, ../../app/styles/main.scss */ 51 | 52 | canvas#renderer { 53 | -moz-filter: url(../filter.svg#drop-shadow); 54 | filter: url(../filter.svg#drop-shadow); 55 | -webkit-filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.6)); 56 | filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.6)); 57 | } 58 | 59 | /* line 47, ../../app/styles/main.scss */ 60 | 61 | div.world-map { 62 | position: absolute; 63 | top: 0; 64 | left: 0; 65 | /*opacity: 0.05;*/ 66 | opacity: 0.1; 67 | } 68 | 69 | /* line 54, ../../app/styles/main.scss */ 70 | 71 | .world-map.philippines { 72 | background: url(../images/philippines.jpg) 0 0 no-repeat; 73 | -webkit-background-size: cover; 74 | background-size: cover; 75 | } 76 | 77 | /* line 59, ../../app/styles/main.scss */ 78 | 79 | .world-map.world { 80 | background: url(../images/world.jpg) 0 0 no-repeat; 81 | -webkit-background-size: cover; 82 | background-size: cover; 83 | } 84 | 85 | /* line 64, ../../app/styles/main.scss */ 86 | 87 | .world-map.eastwestcommute { 88 | background: url(../images/world.jpg) -765px -190px repeat; 89 | -webkit-background-size: 300%; 90 | background-size: 300%; 91 | } 92 | 93 | .world-map.testcrossed { 94 | /*background: black;*/ 95 | /*opacity: 1;*/ 96 | /*-webkit-background-size: 300%;*/ 97 | /*background-size: 300%;*/ 98 | } 99 | 100 | /* line 69, ../../app/styles/main.scss */ 101 | 102 | label { 103 | display: block; 104 | text-align: left; 105 | width: 500px; 106 | margin: 10px auto; 107 | } 108 | 109 | /* line 77, ../../app/styles/main.scss */ 110 | 111 | input, 112 | select { 113 | width: 300px; 114 | padding: 0; 115 | margin: 0; 116 | margin-right: 15px; 117 | } 118 | 119 | /* line 84, ../../app/styles/main.scss */ 120 | 121 | span { 122 | color: #444; 123 | } 124 | -------------------------------------------------------------------------------- /examples/sf/example.css: -------------------------------------------------------------------------------- 1 | html { 2 | text-align: center; 3 | font-family: Helvetica Neue; 4 | } 5 | 6 | h1 { 7 | font-weight: normal; 8 | } 9 | 10 | div.loading { 11 | color: #888; 12 | font-style: italic; 13 | margin-top: 150px; 14 | } 15 | 16 | div.description { 17 | text-align: left; 18 | max-width: 700px; 19 | margin: 10px auto; 20 | } 21 | 22 | canvas { 23 | -moz-filter: url(filter.svg#drop-shadow); 24 | filter: url(filter.svg#drop-shadow); 25 | filter: drop-shadow(0 0 4px rgba(0, 0, 0, 0.5)); 26 | -webkit-filter: drop-shadow(0 0 4px rgba(0, 0, 0, 0.5)); 27 | 28 | width: 500px; 29 | height: 594px; 30 | margin: auto; 31 | } 32 | 33 | 34 | label { 35 | display: block; 36 | text-align: left; 37 | width: 500px; 38 | margin: 10px auto; 39 | } 40 | 41 | 42 | input, select { 43 | /*display: block;*/ 44 | width: 400px; 45 | } 46 | -------------------------------------------------------------------------------- /examples/sf/example.js: -------------------------------------------------------------------------------- 1 | /*global PhiloGL, Bundler, IO, Fx*/ 2 | PhiloGL.unpack(); 3 | 4 | var curviness = 0, margin = 0.3, type = 'Bezier', delta; 5 | 6 | function animate(bundle, canvas, ctx) { 7 | var loading = document.querySelector('.loading'); 8 | 9 | loading.parentNode.removeChild(loading); 10 | 11 | canvas.width = 700; 12 | canvas.height = 832; 13 | 14 | new Fx({ 15 | transition: Fx.Transition.Quart.easeInOut, 16 | duration: 5000 17 | }).start({ 18 | onCompute: function(delta) { 19 | canvas.width = canvas.width; 20 | ctx.strokeStyle = 'rgba(0, 200, 200, 0.2)'; 21 | ctx.lineWidth = 1; 22 | bundle.graph.each(function(node) { 23 | var edges = node.unbundleEdges(delta); 24 | Bundler.Graph['render' + type](ctx, edges, { 25 | delta: delta, 26 | margin: margin, 27 | curviness: curviness 28 | }); 29 | }); 30 | } 31 | }); 32 | 33 | Fx.requestAnimationFrame(function loop() { Fx.requestAnimationFrame(loop); }); 34 | } 35 | 36 | window.addEventListener('DOMContentLoaded', function() { 37 | var canvas = document.querySelector('canvas'), 38 | ctx = canvas.getContext('2d'), 39 | bundle; 40 | 41 | function render() { 42 | canvas.width = canvas.width; 43 | ctx.strokeStyle = 'rgba(0, 200, 200, 0.2)'; 44 | ctx.lineWidth = 1; 45 | bundle.graph.each(function(node) { 46 | var edges = node.unbundleEdges(delta); 47 | Bundler.Graph['render' + type](ctx, edges, { 48 | margin: margin, 49 | delta: delta, 50 | curviness: curviness 51 | }); 52 | }); 53 | } 54 | 55 | document.querySelector('#bundle-level').addEventListener('change', function() { 56 | delta = +this.value; 57 | render(canvas, ctx, bundle, delta, type, curviness); 58 | }); 59 | 60 | document.querySelector('#curviness').addEventListener('change', function() { 61 | curviness = +this.value; 62 | render(canvas, ctx, bundle, delta, type, curviness); 63 | }); 64 | 65 | document.querySelector('#margin').addEventListener('change', function() { 66 | margin = +this.value; 67 | render(canvas, ctx, bundle, delta, type, curviness); 68 | }); 69 | 70 | document.querySelector('#line-type').addEventListener('change', function() { 71 | type = this.value; 72 | render(canvas, ctx, bundle, delta, type, curviness); 73 | }); 74 | 75 | new IO.XHR({ 76 | url: 'sample.json', 77 | onSuccess: function(json) { 78 | json = JSON.parse(json); 79 | 80 | bundle = new Bundler(); 81 | bundle.setNodes(json); 82 | bundle.buildNearestNeighborGraph(); 83 | bundle.MINGLE(); 84 | 85 | animate(bundle, canvas, ctx); 86 | } 87 | }).send(); 88 | }); 89 | -------------------------------------------------------------------------------- /examples/sf/filter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/sf/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | A JavaScript implementation of Multilevel Agglomerative Edge Bundling for Visualizing Large Graphs. 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |

13 | Example: Links connecting key locations in San Francisco. 14 |

15 |
16 |
17 | Given a graph connecting points in the bay area we use an algorithm to group edges 18 | to reduce the amount of ink used to draw the graph. This is a JavaScript implementation of 19 | 20 | Multilevel Agglomerative Edge Bundling for Visualizing Large 21 | Graphs. More information here. 23 |
24 |
25 | 26 | 27 | 28 | 29 | 30 | 35 | 36 |
Loading please wait...
37 | 38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /examples/test/example.css: -------------------------------------------------------------------------------- 1 | html { 2 | text-align: center; 3 | font-family: Helvetica Neue; 4 | } 5 | 6 | h1 { 7 | font-weight: normal; 8 | } 9 | 10 | label { 11 | display: block; 12 | text-align: left; 13 | width: 500px; 14 | margin: 10px auto; 15 | } 16 | 17 | div.loading { 18 | color: #888; 19 | font-style: italic; 20 | margin-top: 150px; 21 | } 22 | 23 | div.description { 24 | text-align: left; 25 | max-width: 700px; 26 | margin: 10px auto; 27 | } 28 | 29 | canvas { 30 | -moz-filter: url(filter.svg#drop-shadow); 31 | filter: url(filter.svg#drop-shadow); 32 | filter: drop-shadow(0 0 4px rgba(0, 0, 0, 0.5)); 33 | -webkit-filter: drop-shadow(0 0 4px rgba(0, 0, 0, 0.5)); 34 | 35 | width: 500px; 36 | height: 594px; 37 | margin: auto; 38 | } 39 | 40 | input, select { 41 | /*display: block;*/ 42 | width: 400px; 43 | } 44 | -------------------------------------------------------------------------------- /examples/test/example.js: -------------------------------------------------------------------------------- 1 | /*global PhiloGL, Bundler, IO, Fx*/ 2 | PhiloGL.unpack(); 3 | 4 | window.addEventListener('DOMContentLoaded', function() { 5 | var canvas = document.querySelector('canvas'), 6 | ctx = canvas.getContext('2d'), 7 | delta = 1, 8 | type = 'Bezier', 9 | neighbors = 10, 10 | angleStrength = 10, 11 | curviness = 1, 12 | margin = 0, 13 | jsonText, 14 | bundle; 15 | 16 | function render(canvas, ctx, bundle, delta, type, curviness) { 17 | canvas.width = canvas.width; 18 | ctx.strokeStyle = 'black'; 19 | ctx.lineWidth = 1; 20 | bundle.graph.each(function(node) { 21 | var edges = node.unbundleEdges(delta); 22 | Bundler.Graph['render' + type](ctx, edges, { 23 | curviness: curviness, 24 | delta: delta, 25 | margin: margin 26 | }); 27 | }); 28 | } 29 | 30 | function animate(bundle, canvas, ctx) { 31 | var loading = document.querySelector('.loading'); 32 | 33 | loading.parentNode.removeChild(loading); 34 | 35 | new Fx({ 36 | transition: Fx.Transition.Quart.easeInOut, 37 | duration: 1000 38 | }).start({ 39 | onCompute: function(deltaValue) { 40 | delta = deltaValue; 41 | render(canvas, ctx, bundle, delta, type, curviness); 42 | } 43 | }); 44 | 45 | Fx.requestAnimationFrame(function loop() { Fx.requestAnimationFrame(loop); }); 46 | } 47 | 48 | function updateBundle() { 49 | bundle.options.angleStrength = angleStrength; 50 | bundle.setNodes(JSON.parse(jsonText)); 51 | bundle.buildNearestNeighborGraph(neighbors); 52 | bundle.MINGLE(); 53 | } 54 | 55 | document.querySelector('#bundle-level').addEventListener('change', function() { 56 | delta = +this.value; 57 | render(canvas, ctx, bundle, delta, type, curviness); 58 | }); 59 | 60 | document.querySelector('#curviness').addEventListener('change', function() { 61 | curviness = +this.value; 62 | render(canvas, ctx, bundle, delta, type, curviness); 63 | }); 64 | 65 | document.querySelector('#margin').addEventListener('change', function() { 66 | margin = +this.value; 67 | render(canvas, ctx, bundle, delta, type, curviness); 68 | }); 69 | 70 | document.querySelector('#angle-strength').addEventListener('change', function() { 71 | angleStrength = +this.value; 72 | updateBundle(); 73 | render(canvas, ctx, bundle, delta, type, curviness); 74 | }); 75 | 76 | document.querySelector('#neighbors').addEventListener('change', function() { 77 | neighbors = +this.value; 78 | updateBundle(); 79 | render(canvas, ctx, bundle, delta, type, curviness); 80 | }); 81 | 82 | document.querySelector('#line-type').addEventListener('change', function() { 83 | type = this.value; 84 | render(canvas, ctx, bundle, delta, type, curviness); 85 | }); 86 | 87 | new IO.XHR({ 88 | url: 'sample.json', 89 | onSuccess: function(json) { 90 | jsonText = json; 91 | json = JSON.parse(jsonText); 92 | 93 | bundle = new Bundler({ 94 | angleStrength: angleStrength 95 | }); 96 | bundle.setNodes(json); 97 | bundle.buildNearestNeighborGraph(neighbors); 98 | bundle.MINGLE(); 99 | 100 | animate(bundle, canvas, ctx); 101 | } 102 | }).send(); 103 | }); 104 | -------------------------------------------------------------------------------- /examples/test/filter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | A JavaScript implementation of Multilevel Agglomerative Edge Bundling for Visualizing Large Graphs. 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |

13 | Example: Some links. 14 |

15 |
16 |
17 | A simple testing against a few links. This is a JavaScript implementation of 18 | 19 | Multilevel Agglomerative Edge Bundling for Visualizing Large 20 | Graphs. More information here. 22 |
23 |
24 | 25 | 26 | 27 | 28 | 29 | 34 |
Loading please wait...
35 | 36 |
37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /examples/test/sample.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "name": 1, 5 | "data": { 6 | "coords": [ 7 | 10, 8 | 10, 9 | 10, 10 | 450 11 | ], 12 | "weight": 5, 13 | "color": "rgb(241, 205, 15)" 14 | } 15 | }, 16 | { 17 | "id": 2, 18 | "name": 2, 19 | "data": { 20 | "coords": [ 21 | 30, 22 | 10, 23 | 30, 24 | 450 25 | ], 26 | "weight": 5, 27 | "color": "rgb(16, 99, 179)" 28 | } 29 | }, 30 | { 31 | "id": 3, 32 | "name": 3, 33 | "data": { 34 | "coords": [ 35 | 50, 36 | 10, 37 | 50, 38 | 450 39 | ], 40 | "weight": 5, 41 | "color": "rgb(147, 148, 58)" 42 | } 43 | }, 44 | { 45 | "id": 4, 46 | "name": 4, 47 | "data": { 48 | "coords": [ 49 | 100, 50 | 10, 51 | 100, 52 | 450 53 | ], 54 | "weight": 5, 55 | "color": "rgb(187, 71, 149)" 56 | } 57 | }, 58 | { 59 | "id": 5, 60 | "name": 5, 61 | "data": { 62 | "coords": [ 63 | 200, 64 | 10, 65 | 200, 66 | 450 67 | ], 68 | "weight": 5, 69 | "color": "rgb(244, 141, 72)" 70 | } 71 | }, 72 | { 73 | "id": 6, 74 | "name": 6, 75 | "data": { 76 | "coords": [ 77 | 230, 78 | 10, 79 | 230, 80 | 450 81 | ], 82 | "weight": 5, 83 | "color": "rgb(110, 196, 145)" 84 | } 85 | } 86 | ] 87 | -------------------------------------------------------------------------------- /graph.js: -------------------------------------------------------------------------------- 1 | ;(function () { 2 | 3 | function descriptor(object) { 4 | var desc = {}, p; 5 | for (p in object) { 6 | desc[p] = { 7 | value: object[p], 8 | writable: true, 9 | enumerable: true, 10 | configurable: true 11 | }; 12 | } 13 | return desc; 14 | } 15 | 16 | function merge(proto, object) { 17 | return Object.create(proto, descriptor(object)); 18 | } 19 | 20 | function extend(proto, object) { 21 | var p; 22 | for (p in object) { 23 | proto[p] = object[p]; 24 | } 25 | } 26 | /* 27 | * File: Graph.js 28 | * 29 | */ 30 | 31 | /* 32 | Class: Graph 33 | 34 | A Graph Class that provides useful manipulation functions. You can find more manipulation methods in the object. 35 | 36 | An instance of this class can be accessed by using the *graph* parameter of any tree or graph visualization. 37 | 38 | Example: 39 | 40 | (start code js) 41 | //create new visualization 42 | var viz = new $jit.Viz(options); 43 | //load JSON data 44 | viz.loadJSON(json); 45 | //access model 46 | viz.graph; // instance 47 | (end code) 48 | 49 | Implements: 50 | 51 | The following methods are implemented in 52 | 53 | - 54 | - 55 | - 56 | - 57 | - 58 | - 59 | - 60 | 61 | */ 62 | 63 | var Graph = function(opt) { 64 | this.opt = merge({ 65 | node: {} 66 | }, opt || {}); 67 | this.nodes = {}; 68 | this.edges = {}; 69 | }; 70 | 71 | Graph.fromJSON = function(json) { 72 | var nodes = json.nodes, 73 | edges = json.edges, 74 | Node = Graph.Node, 75 | Edge = Graph.Edge, 76 | graph = new Graph(), 77 | k; 78 | 79 | for (k in nodes) { 80 | nodes[k] = Node.fromJSON(nodes[k]); 81 | } 82 | 83 | graph.nodes = nodes; 84 | 85 | for (k in edges) { 86 | edges[k] = Edge.fromJSON(graph, edges[k]); 87 | } 88 | 89 | graph.edges = edges; 90 | 91 | return graph; 92 | }; 93 | 94 | Graph.prototype = { 95 | clear: function() { 96 | this.nodes = {}; 97 | this.edges = {}; 98 | }, 99 | 100 | //serialize 101 | toJSON: function() { 102 | var nodes = [], 103 | edges = [], 104 | gNodes = this.nodes, 105 | gEdges = this.edges, 106 | k, from, to; 107 | 108 | for (k in gNodes) { 109 | nodes.push(gNodes[k].toJSON()); 110 | } 111 | 112 | for (from in gEdges) { 113 | for (to in gEdges[from]) { 114 | edges.push(gEdges[from][to].toJSON()); 115 | } 116 | } 117 | 118 | return { nodes: nodes, edges: edges }; 119 | }, 120 | 121 | /* 122 | Method: getNode 123 | 124 | Returns a by *id*. 125 | 126 | Parameters: 127 | 128 | id - (string) A id. 129 | 130 | Example: 131 | 132 | (start code js) 133 | var node = graph.getNode('nodeId'); 134 | (end code) 135 | */ 136 | getNode: function(id) { 137 | if(this.hasNode(id)) return this.nodes[id]; 138 | return false; 139 | }, 140 | 141 | /* 142 | Method: get 143 | 144 | An alias for . Returns a node by *id*. 145 | 146 | Parameters: 147 | 148 | id - (string) A id. 149 | 150 | Example: 151 | 152 | (start code js) 153 | var node = graph.get('nodeId'); 154 | (end code) 155 | */ 156 | get: function(id) { 157 | return this.getNode(id); 158 | }, 159 | 160 | /* 161 | Method: getByName 162 | 163 | Returns a by *name*. 164 | 165 | Parameters: 166 | 167 | name - (string) A name. 168 | 169 | Example: 170 | 171 | (start code js) 172 | var node = graph.getByName('someName'); 173 | (end code) 174 | */ 175 | getByName: function(name) { 176 | for(var id in this.nodes) { 177 | var n = this.nodes[id]; 178 | if(n.name == name) return n; 179 | } 180 | return false; 181 | }, 182 | 183 | /* 184 | Method: getEdge 185 | 186 | Returns a object connecting nodes with ids *id* and *id2*. 187 | 188 | Parameters: 189 | 190 | id - (string) A id. 191 | id2 - (string) A id. 192 | */ 193 | getEdge: function (id, id2) { 194 | if(id in this.edges) { 195 | return this.edges[id][id2]; 196 | } 197 | return false; 198 | }, 199 | 200 | /* 201 | Method: addNode 202 | 203 | Adds a node. 204 | 205 | Parameters: 206 | 207 | obj - An object with the properties described below 208 | 209 | id - (string) A node id 210 | name - (string) A node's name 211 | data - (object) A node's data hash 212 | 213 | See also: 214 | 215 | 216 | */ 217 | addNode: function(obj) { 218 | if(!this.nodes[obj.id]) { 219 | var edges = this.edges[obj.id] = {}; 220 | this.nodes[obj.id] = new Graph.Node(merge({ 221 | 'id': obj.id, 222 | 'name': obj.name, 223 | 'data': merge(obj.data || {}, {}), 224 | 'adjacencies': edges 225 | }, this.opt.node)); 226 | } 227 | return this.nodes[obj.id]; 228 | }, 229 | 230 | /* 231 | Method: addEdge 232 | 233 | Connects nodes specified by *obj* and *obj2*. If not found, nodes are created. 234 | 235 | Parameters: 236 | 237 | obj - (object) A object. 238 | obj2 - (object) Another object. 239 | data - (object) A data object. Used to store some extra information in the object created. 240 | 241 | See also: 242 | 243 | , 244 | */ 245 | addEdge: function (obj, obj2, data) { 246 | if(!this.hasNode(obj.id)) { this.addNode(obj); } 247 | if(!this.hasNode(obj2.id)) { this.addNode(obj2); } 248 | obj = this.nodes[obj.id]; obj2 = this.nodes[obj2.id]; 249 | if(!obj.adjacentTo(obj2)) { 250 | var adjsObj = this.edges[obj.id] = this.edges[obj.id] || {}; 251 | var adjsObj2 = this.edges[obj2.id] = this.edges[obj2.id] || {}; 252 | adjsObj[obj2.id] = adjsObj2[obj.id] = new Graph.Edge(obj, obj2, data, this.Edge, this.Label); 253 | return adjsObj[obj2.id]; 254 | } 255 | return this.edges[obj.id][obj2.id]; 256 | }, 257 | 258 | /* 259 | Method: removeNode 260 | 261 | Removes a matching the specified *id*. 262 | 263 | Parameters: 264 | 265 | id - (string) A node's id. 266 | 267 | */ 268 | removeNode: function(id) { 269 | if(this.hasNode(id)) { 270 | delete this.nodes[id]; 271 | var adjs = this.edges[id]; 272 | for(var to in adjs) { 273 | delete this.edges[to][id]; 274 | } 275 | delete this.edges[id]; 276 | } 277 | }, 278 | 279 | /* 280 | Method: removeEdge 281 | 282 | Removes a matching *id1* and *id2*. 283 | 284 | Parameters: 285 | 286 | id1 - (string) A id. 287 | id2 - (string) A id. 288 | */ 289 | removeEdge: function(id1, id2) { 290 | delete this.edges[id1][id2]; 291 | delete this.edges[id2][id1]; 292 | }, 293 | 294 | /* 295 | Method: hasNode 296 | 297 | Returns a boolean indicating if the node belongs to the or not. 298 | 299 | Parameters: 300 | 301 | id - (string) Node id. 302 | */ 303 | hasNode: function(id) { 304 | return id in this.nodes; 305 | }, 306 | 307 | /* 308 | Method: empty 309 | 310 | Empties the Graph 311 | 312 | */ 313 | empty: function() { this.nodes = {}; this.edges = {}; } 314 | 315 | }; 316 | 317 | /* 318 | Class: Graph.Node 319 | 320 | A node. 321 | 322 | Implements: 323 | 324 | methods. 325 | 326 | The following methods are implemented by 327 | 328 | - 329 | - 330 | - 331 | - 332 | - 333 | - 334 | - 335 | - 336 | */ 337 | Graph.Node = function(opt) { 338 | var innerOptions = { 339 | 'id': '', 340 | 'name': '', 341 | 'data': {}, 342 | 'adjacencies': {} 343 | }; 344 | extend(this, merge(innerOptions, opt)); 345 | }; 346 | 347 | Graph.Node.fromJSON = function(json) { 348 | return new Graph.Node(json); 349 | }; 350 | 351 | Graph.Node.prototype = { 352 | toJSON: function() { 353 | return { 354 | id: this.id, 355 | name: this.name, 356 | data: this.serializeData(this.data) 357 | }; 358 | }, 359 | 360 | serializeData: function(data) { 361 | var serializedData = {}, 362 | parents = data.parents, 363 | parentsCopy, i, l; 364 | 365 | if (parents) { 366 | parentsCopy = Array(parents.length); 367 | for (i = 0, l = parents.length; i < l; ++i) { 368 | parentsCopy[i] = parents[i].toJSON(); 369 | } 370 | } 371 | 372 | for (i in data) { 373 | serializedData[i] = data[i]; 374 | } 375 | 376 | delete serializedData.parents; 377 | delete serializedData.bundle; 378 | serializedData = JSON.parse(JSON.stringify(serializedData)); 379 | 380 | if (parentsCopy) { 381 | serializedData.parents = parentsCopy; 382 | } 383 | 384 | return serializedData; 385 | }, 386 | 387 | /* 388 | Method: adjacentTo 389 | 390 | Indicates if the node is adjacent to the node specified by id 391 | 392 | Parameters: 393 | 394 | id - (string) A node id. 395 | 396 | Example: 397 | (start code js) 398 | node.adjacentTo('nodeId') == true; 399 | (end code) 400 | */ 401 | adjacentTo: function(node) { 402 | return node.id in this.adjacencies; 403 | }, 404 | 405 | /* 406 | Method: getAdjacency 407 | 408 | Returns a object connecting the current and the node having *id* as id. 409 | 410 | Parameters: 411 | 412 | id - (string) A node id. 413 | */ 414 | getEdge: function(id) { 415 | return this.adjacencies[id]; 416 | }, 417 | 418 | /* 419 | Method: toString 420 | 421 | Returns a String with information on the Node. 422 | 423 | */ 424 | toString: function() { 425 | return 'Node(' + JSON.stringify([this.id, this.name, this.data, this.adjacencies]) + ')'; 426 | } 427 | }; 428 | 429 | /* 430 | Class: Graph.Edge 431 | 432 | A adjacence (or edge) connecting two . 433 | 434 | Implements: 435 | 436 | methods. 437 | 438 | See also: 439 | 440 | , 441 | 442 | Properties: 443 | 444 | nodeFrom - A connected by this edge. 445 | nodeTo - Another connected by this edge. 446 | data - Node data property containing a hash (i.e {}) with custom options. 447 | */ 448 | Graph.Edge = function(nodeFrom, nodeTo, data) { 449 | this.nodeFrom = nodeFrom; 450 | this.nodeTo = nodeTo; 451 | this.data = data || {}; 452 | }; 453 | 454 | Graph.Edge.fromJSON = function(graph, edgeJSON) { 455 | return new Graph.Edge(graph.get(edgeJSON.nodeFrom), 456 | graph.get(edgeJSON.nodeTo), 457 | edgeJSON.data); 458 | }; 459 | 460 | Graph.Edge.prototype.toJSON = function() { 461 | return { 462 | nodeFrom: this.nodeFrom.id, 463 | nodeTo: this.nodeTo.id, 464 | data: this.data 465 | }; 466 | }; 467 | 468 | /* 469 | Object: Graph.Util 470 | 471 | traversal and processing utility object. 472 | 473 | Note: 474 | 475 | For your convenience some of these methods have also been appended to and classes. 476 | */ 477 | Graph.Util = { 478 | /* 479 | filter 480 | 481 | For internal use only. Provides a filtering function based on flags. 482 | */ 483 | filter: function(param) { 484 | if(!param || !(typeof param == 'string')) return function() { return true; }; 485 | var props = param.split(" "); 486 | return function(elem) { 487 | for(var i=0; i by *id*. 499 | 500 | Also implemented by: 501 | 502 | 503 | 504 | Parameters: 505 | 506 | graph - (object) A instance. 507 | id - (string) A id. 508 | 509 | Example: 510 | 511 | (start code js) 512 | $jit.Graph.Util.getNode(graph, 'nodeid'); 513 | //or... 514 | graph.getNode('nodeid'); 515 | (end code) 516 | */ 517 | getNode: function(graph, id) { 518 | return graph.nodes[id]; 519 | }, 520 | 521 | /* 522 | Method: eachNode 523 | 524 | Iterates over nodes performing an *action*. 525 | 526 | Also implemented by: 527 | 528 | . 529 | 530 | Parameters: 531 | 532 | graph - (object) A instance. 533 | action - (function) A callback function having a as first formal parameter. 534 | 535 | Example: 536 | (start code js) 537 | $jit.Graph.Util.eachNode(graph, function(node) { 538 | alert(node.name); 539 | }); 540 | //or... 541 | graph.eachNode(function(node) { 542 | alert(node.name); 543 | }); 544 | (end code) 545 | */ 546 | eachNode: function(graph, action, flags) { 547 | var filter = this.filter(flags); 548 | for(var i in graph.nodes) { 549 | if(filter(graph.nodes[i])) action(graph.nodes[i]); 550 | } 551 | }, 552 | 553 | /* 554 | Method: each 555 | 556 | Iterates over nodes performing an *action*. It's an alias for . 557 | 558 | Also implemented by: 559 | 560 | . 561 | 562 | Parameters: 563 | 564 | graph - (object) A instance. 565 | action - (function) A callback function having a as first formal parameter. 566 | 567 | Example: 568 | (start code js) 569 | $jit.Graph.Util.each(graph, function(node) { 570 | alert(node.name); 571 | }); 572 | //or... 573 | graph.each(function(node) { 574 | alert(node.name); 575 | }); 576 | (end code) 577 | */ 578 | each: function(graph, action, flags) { 579 | this.eachNode(graph, action, flags); 580 | }, 581 | 582 | /* 583 | Method: eachEdge 584 | 585 | Iterates over adjacencies applying the *action* function. 586 | 587 | Also implemented by: 588 | 589 | . 590 | 591 | Parameters: 592 | 593 | node - (object) A . 594 | action - (function) A callback function having as first formal parameter. 595 | 596 | Example: 597 | (start code js) 598 | $jit.Graph.Util.eachEdge(node, function(adj) { 599 | alert(adj.nodeTo.name); 600 | }); 601 | //or... 602 | node.eachEdge(function(adj) { 603 | alert(adj.nodeTo.name); 604 | }); 605 | (end code) 606 | */ 607 | eachEdge: function(node, action, flags) { 608 | var adj = node.adjacencies, filter = this.filter(flags); 609 | for(var id in adj) { 610 | var a = adj[id]; 611 | if(filter(a)) { 612 | if(a.nodeFrom != node) { 613 | var tmp = a.nodeFrom; 614 | a.nodeFrom = a.nodeTo; 615 | a.nodeTo = tmp; 616 | } 617 | action(a, id); 618 | } 619 | } 620 | }, 621 | 622 | /* 623 | Method: computeLevels 624 | 625 | Performs a BFS traversal setting the correct depth for each node. 626 | 627 | Also implemented by: 628 | 629 | . 630 | 631 | Note: 632 | 633 | The depth of each node can then be accessed by 634 | >node.depth 635 | 636 | Parameters: 637 | 638 | graph - (object) A . 639 | id - (string) A starting node id for the BFS traversal. 640 | startDepth - (optional|number) A minimum depth value. Default's 0. 641 | 642 | */ 643 | computeLevels: function(graph, id, startDepth, flags) { 644 | startDepth = startDepth || 0; 645 | var filter = this.filter(flags); 646 | this.eachNode(graph, function(elem) { 647 | elem._flag = false; 648 | elem.depth = -1; 649 | }, flags); 650 | var root = graph.getNode(id); 651 | root.depth = startDepth; 652 | var queue = [root]; 653 | while(queue.length != 0) { 654 | var node = queue.pop(); 655 | node._flag = true; 656 | this.eachEdge(node, function(adj) { 657 | var n = adj.nodeTo; 658 | if(n._flag == false && filter(n) && !adj._hiding) { 659 | if(n.depth < 0) n.depth = node.depth + 1 + startDepth; 660 | queue.unshift(n); 661 | } 662 | }, flags); 663 | } 664 | }, 665 | 666 | /* 667 | Method: eachBFS 668 | 669 | Performs a BFS traversal applying *action* to each . 670 | 671 | Also implemented by: 672 | 673 | . 674 | 675 | Parameters: 676 | 677 | graph - (object) A . 678 | id - (string) A starting node id for the BFS traversal. 679 | action - (function) A callback function having a as first formal parameter. 680 | 681 | Example: 682 | (start code js) 683 | $jit.Graph.Util.eachBFS(graph, 'mynodeid', function(node) { 684 | alert(node.name); 685 | }); 686 | //or... 687 | graph.eachBFS('mynodeid', function(node) { 688 | alert(node.name); 689 | }); 690 | (end code) 691 | */ 692 | eachBFS: function(graph, id, action, flags) { 693 | var filter = this.filter(flags); 694 | this.clean(graph); 695 | var queue = [graph.getNode(id)]; 696 | while(queue.length != 0) { 697 | var node = queue.pop(); 698 | if (!node) return; 699 | node._flag = true; 700 | action(node, node.depth); 701 | this.eachEdge(node, function(adj) { 702 | var n = adj.nodeTo; 703 | if(n._flag == false && filter(n) && !adj._hiding) { 704 | n._flag = true; 705 | queue.unshift(n); 706 | } 707 | }, flags); 708 | } 709 | }, 710 | 711 | /* 712 | Method: eachLevel 713 | 714 | Iterates over a node's subgraph applying *action* to the nodes of relative depth between *levelBegin* and *levelEnd*. 715 | In case you need to break the iteration, *action* should return false. 716 | 717 | Also implemented by: 718 | 719 | . 720 | 721 | Parameters: 722 | 723 | node - (object) A . 724 | levelBegin - (number) A relative level value. 725 | levelEnd - (number) A relative level value. 726 | action - (function) A callback function having a as first formal parameter. 727 | 728 | */ 729 | eachLevel: function(node, levelBegin, levelEnd, action, flags) { 730 | var d = node.depth, filter = this.filter(flags), that = this, shouldContinue = true; 731 | levelEnd = levelEnd === false? Number.MAX_VALUE -d : levelEnd; 732 | (function loopLevel(node, levelBegin, levelEnd) { 733 | if(!shouldContinue) return; 734 | var d = node.depth, ret; 735 | if(d >= levelBegin && d <= levelEnd && filter(node)) ret = action(node, d); 736 | if(typeof ret !== "undefined") shouldContinue = ret; 737 | if(shouldContinue && d < levelEnd) { 738 | that.eachEdge(node, function(adj) { 739 | var n = adj.nodeTo; 740 | if(n.depth > d) loopLevel(n, levelBegin, levelEnd); 741 | }); 742 | } 743 | })(node, levelBegin + d, levelEnd + d); 744 | }, 745 | 746 | /* 747 | Method: eachSubgraph 748 | 749 | Iterates over a node's children recursively. 750 | 751 | Also implemented by: 752 | 753 | . 754 | 755 | Parameters: 756 | node - (object) A . 757 | action - (function) A callback function having a as first formal parameter. 758 | 759 | Example: 760 | (start code js) 761 | $jit.Graph.Util.eachSubgraph(node, function(node) { 762 | alert(node.name); 763 | }); 764 | //or... 765 | node.eachSubgraph(function(node) { 766 | alert(node.name); 767 | }); 768 | (end code) 769 | */ 770 | eachSubgraph: function(node, action, flags) { 771 | this.eachLevel(node, 0, false, action, flags); 772 | }, 773 | 774 | /* 775 | Method: eachSubnode 776 | 777 | Iterates over a node's children (without deeper recursion). 778 | 779 | Also implemented by: 780 | 781 | . 782 | 783 | Parameters: 784 | node - (object) A . 785 | action - (function) A callback function having a as first formal parameter. 786 | 787 | Example: 788 | (start code js) 789 | $jit.Graph.Util.eachSubnode(node, function(node) { 790 | alert(node.name); 791 | }); 792 | //or... 793 | node.eachSubnode(function(node) { 794 | alert(node.name); 795 | }); 796 | (end code) 797 | */ 798 | eachSubnode: function(node, action, flags) { 799 | this.eachLevel(node, 1, 1, action, flags); 800 | }, 801 | 802 | /* 803 | Method: anySubnode 804 | 805 | Returns *true* if any subnode matches the given condition. 806 | 807 | Also implemented by: 808 | 809 | . 810 | 811 | Parameters: 812 | node - (object) A . 813 | cond - (function) A callback function returning a Boolean instance. This function has as first formal parameter a . 814 | 815 | Example: 816 | (start code js) 817 | $jit.Graph.Util.anySubnode(node, function(node) { return node.name == "mynodename"; }); 818 | //or... 819 | node.anySubnode(function(node) { return node.name == 'mynodename'; }); 820 | (end code) 821 | */ 822 | anySubnode: function(node, cond, flags) { 823 | var flag = false; 824 | cond = cond || function() { return true; }; 825 | var c = typeof cond == 'string'? function(n) { return n[cond]; } : cond; 826 | this.eachSubnode(node, function(elem) { 827 | if(c(elem)) flag = true; 828 | }, flags); 829 | return flag; 830 | }, 831 | 832 | /* 833 | Method: getSubnodes 834 | 835 | Collects all subnodes for a specified node. 836 | The *level* parameter filters nodes having relative depth of *level* from the root node. 837 | 838 | Also implemented by: 839 | 840 | . 841 | 842 | Parameters: 843 | node - (object) A . 844 | level - (optional|number) Default's *0*. A starting relative depth for collecting nodes. 845 | 846 | Returns: 847 | An array of nodes. 848 | 849 | */ 850 | getSubnodes: function(node, level, flags) { 851 | var ans = [], that = this; 852 | level = level || 0; 853 | var levelStart, levelEnd; 854 | if(Array.isArray(level) == 'array') { 855 | levelStart = level[0]; 856 | levelEnd = level[1]; 857 | } else { 858 | levelStart = level; 859 | levelEnd = Number.MAX_VALUE - node.depth; 860 | } 861 | this.eachLevel(node, levelStart, levelEnd, function(n) { 862 | ans.push(n); 863 | }, flags); 864 | return ans; 865 | }, 866 | 867 | 868 | /* 869 | Method: getParents 870 | 871 | Returns an Array of which are parents of the given node. 872 | 873 | Also implemented by: 874 | 875 | . 876 | 877 | Parameters: 878 | node - (object) A . 879 | 880 | Returns: 881 | An Array of . 882 | 883 | Example: 884 | (start code js) 885 | var pars = $jit.Graph.Util.getParents(node); 886 | //or... 887 | var pars = node.getParents(); 888 | 889 | if(pars.length > 0) { 890 | //do stuff with parents 891 | } 892 | (end code) 893 | */ 894 | getParents: function(node) { 895 | var ans = []; 896 | this.eachEdge(node, function(adj) { 897 | var n = adj.nodeTo; 898 | if(n.depth < node.depth) ans.push(n); 899 | }); 900 | return ans; 901 | }, 902 | 903 | /* 904 | Method: isDescendantOf 905 | 906 | Returns a boolean indicating if some node is descendant of the node with the given id. 907 | 908 | Also implemented by: 909 | 910 | . 911 | 912 | 913 | Parameters: 914 | node - (object) A . 915 | id - (string) A id. 916 | 917 | Example: 918 | (start code js) 919 | $jit.Graph.Util.isDescendantOf(node, "nodeid"); //true|false 920 | //or... 921 | node.isDescendantOf('nodeid');//true|false 922 | (end code) 923 | */ 924 | isDescendantOf: function(node, id) { 925 | if(node.id == id) return true; 926 | var pars = this.getParents(node), ans = false; 927 | for ( var i = 0; !ans && i < pars.length; i++) { 928 | ans = ans || this.isDescendantOf(pars[i], id); 929 | } 930 | return ans; 931 | }, 932 | 933 | /* 934 | Method: clean 935 | 936 | Cleans flags from nodes. 937 | 938 | Also implemented by: 939 | 940 | . 941 | 942 | Parameters: 943 | graph - A instance. 944 | */ 945 | clean: function(graph) { this.eachNode(graph, function(elem) { elem._flag = false; }); } 946 | }; 947 | 948 | //Append graph methods to 949 | ['get', 'getNode', 'each', 'eachNode', 'computeLevels', 'eachBFS', 'clean'].forEach(function(m) { 950 | Graph.prototype[m] = function() { 951 | return Graph.Util[m].apply(Graph.Util, [this].concat(Array.prototype.slice.call(arguments))); 952 | }; 953 | }); 954 | 955 | //Append node methods to 956 | ['eachEdge', 'eachLevel', 'eachSubgraph', 'eachSubnode', 'anySubnode', 'getSubnodes', 'getParents', 'isDescendantOf'].forEach(function(m) { 957 | Graph.Node.prototype[m] = function() { 958 | return Graph.Util[m].apply(Graph.Util, [this].concat(Array.prototype.slice.call(arguments))); 959 | }; 960 | }); 961 | 962 | this.Graph = Graph; 963 | })(); 964 | 965 | -------------------------------------------------------------------------------- /img/easteurope1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philogb/mingle/c7d10b62b55fe614842f855c32bbb113561359b8/img/easteurope1.png -------------------------------------------------------------------------------- /img/easteurope2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philogb/mingle/c7d10b62b55fe614842f855c32bbb113561359b8/img/easteurope2.png -------------------------------------------------------------------------------- /img/easteurope3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philogb/mingle/c7d10b62b55fe614842f855c32bbb113561359b8/img/easteurope3.png -------------------------------------------------------------------------------- /img/sfcommute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philogb/mingle/c7d10b62b55fe614842f855c32bbb113561359b8/img/sfcommute.png -------------------------------------------------------------------------------- /lib/kdtree.js: -------------------------------------------------------------------------------- 1 | ;(function () { 2 | 3 | /** 4 | * k-d Tree JavaScript - V 1.0 5 | * 6 | * https://github.com/ubilabs/kd-tree-javascript 7 | * 8 | * @author Mircea Pricop , 2012 9 | * @author Martin Kleppe , 2012 10 | * @author Ubilabs http://ubilabs.net, 2012 11 | * @license MIT License 12 | */ 13 | 14 | function Node(obj, dimension, parent) { 15 | this.obj = obj; 16 | this.left = null; 17 | this.right = null; 18 | this.parent = parent; 19 | this.dimension = dimension; 20 | } 21 | 22 | function kdTree(points, metric, dimensions) { 23 | 24 | var self = this; 25 | 26 | function buildTree(points, depth, parent) { 27 | var dim = depth % dimensions.length, 28 | median, 29 | node; 30 | 31 | if (points.length === 0) { 32 | return null; 33 | } 34 | if (points.length === 1) { 35 | return new Node(points[0], dim, parent); 36 | } 37 | 38 | points.sort(function (a, b) { 39 | return a[dimensions[dim]] - b[dimensions[dim]]; 40 | }); 41 | 42 | median = Math.floor(points.length / 2); 43 | node = new Node(points[median], dim, parent); 44 | node.left = buildTree(points.slice(0, median), depth + 1, node); 45 | node.right = buildTree(points.slice(median + 1), depth + 1, node); 46 | 47 | return node; 48 | } 49 | 50 | // Reloads a serialied tree 51 | function loadTree (data) { 52 | // Just need to restore the `parent` parameter 53 | self.root = data; 54 | 55 | function restoreParent (root) { 56 | if (root.left) { 57 | root.left.parent = root; 58 | restoreParent(root.left); 59 | } 60 | 61 | if (root.right) { 62 | root.right.parent = root; 63 | restoreParent(root.right); 64 | } 65 | } 66 | 67 | restoreParent(self.root); 68 | } 69 | 70 | // If points is not an array, assume we're loading a pre-built tree 71 | if (!Array.isArray(points)) loadTree(points, metric, dimensions); 72 | else this.root = buildTree(points, 0, null); 73 | 74 | // Convert to a JSON serializable structure; this just requires removing 75 | // the `parent` property 76 | this.toJSON = function (src) { 77 | if (!src) src = this.root; 78 | var dest = new Node(src.obj, src.dimension, null); 79 | if (src.left) dest.left = self.toJSON(src.left); 80 | if (src.right) dest.right = self.toJSON(src.right); 81 | return dest; 82 | }; 83 | 84 | this.insert = function (point) { 85 | function innerSearch(node, parent) { 86 | 87 | if (node === null) { 88 | return parent; 89 | } 90 | 91 | var dimension = dimensions[node.dimension]; 92 | if (point[dimension] < node.obj[dimension]) { 93 | return innerSearch(node.left, node); 94 | } else { 95 | return innerSearch(node.right, node); 96 | } 97 | } 98 | 99 | var insertPosition = innerSearch(this.root, null), 100 | newNode, 101 | dimension; 102 | 103 | if (insertPosition === null) { 104 | this.root = new Node(point, 0, null); 105 | return; 106 | } 107 | 108 | newNode = new Node(point, (insertPosition.dimension + 1) % dimensions.length, insertPosition); 109 | dimension = dimensions[insertPosition.dimension]; 110 | 111 | if (point[dimension] < insertPosition.obj[dimension]) { 112 | insertPosition.left = newNode; 113 | } else { 114 | insertPosition.right = newNode; 115 | } 116 | }; 117 | 118 | this.remove = function (point) { 119 | var node; 120 | 121 | function nodeSearch(node) { 122 | if (node === null) { 123 | return null; 124 | } 125 | 126 | if (node.obj === point) { 127 | return node; 128 | } 129 | 130 | var dimension = dimensions[node.dimension]; 131 | 132 | if (point[dimension] < node.obj[dimension]) { 133 | return nodeSearch(node.left, node); 134 | } else { 135 | return nodeSearch(node.right, node); 136 | } 137 | } 138 | 139 | function removeNode(node) { 140 | var nextNode, 141 | nextObj, 142 | pDimension; 143 | 144 | function findMax(node, dim) { 145 | var dimension, 146 | own, 147 | left, 148 | right, 149 | max; 150 | 151 | if (node === null) { 152 | return null; 153 | } 154 | 155 | dimension = dimensions[dim]; 156 | if (node.dimension === dim) { 157 | if (node.right !== null) { 158 | return findMax(node.right, dim); 159 | } 160 | return node; 161 | } 162 | 163 | own = node.obj[dimension]; 164 | left = findMax(node.left, dim); 165 | right = findMax(node.right, dim); 166 | max = node; 167 | 168 | if (left !== null && left.obj[dimension] > own) { 169 | max = left; 170 | } 171 | 172 | if (right !== null && right.obj[dimension] > max.obj[dimension]) { 173 | max = right; 174 | } 175 | return max; 176 | } 177 | 178 | function findMin(node, dim) { 179 | var dimension, 180 | own, 181 | left, 182 | right, 183 | min; 184 | 185 | if (node === null) { 186 | return null; 187 | } 188 | 189 | dimension = dimensions[dim]; 190 | 191 | if (node.dimension === dim) { 192 | if (node.left !== null) { 193 | return findMin(node.left, dim); 194 | } 195 | return node; 196 | } 197 | 198 | own = node.obj[dimension]; 199 | left = findMin(node.left, dim); 200 | right = findMin(node.right, dim); 201 | min = node; 202 | 203 | if (left !== null && left.obj[dimension] < own) { 204 | min = left; 205 | } 206 | if (right !== null && right.obj[dimension] < min.obj[dimension]) { 207 | min = right; 208 | } 209 | return min; 210 | } 211 | 212 | if (node.left === null && node.right === null) { 213 | if (node.parent === null) { 214 | self.root = null; 215 | return; 216 | } 217 | 218 | pDimension = dimensions[node.parent.dimension]; 219 | 220 | if (node.obj[pDimension] < node.parent.obj[pDimension]) { 221 | node.parent.left = null; 222 | } else { 223 | node.parent.right = null; 224 | } 225 | return; 226 | } 227 | 228 | if (node.left !== null) { 229 | nextNode = findMax(node.left, node.dimension); 230 | } else { 231 | nextNode = findMin(node.right, node.dimension); 232 | } 233 | 234 | nextObj = nextNode.obj; 235 | removeNode(nextNode); 236 | node.obj = nextObj; 237 | 238 | } 239 | 240 | node = nodeSearch(self.root); 241 | 242 | if (node === null) { return; } 243 | 244 | removeNode(node); 245 | }; 246 | 247 | this.nearest = function (point, maxNodes, maxDistance) { 248 | var i, 249 | result, 250 | bestNodes; 251 | 252 | bestNodes = new BinaryHeap( 253 | function (e) { return -e[1]; } 254 | ); 255 | 256 | function nearestSearch(node) { 257 | var bestChild, 258 | dimension = dimensions[node.dimension], 259 | ownDistance = metric(point, node.obj), 260 | linearPoint = {}, 261 | linearDistance, 262 | otherChild, 263 | i; 264 | 265 | function saveNode(node, distance) { 266 | bestNodes.push([node, distance]); 267 | if (bestNodes.size() > maxNodes) { 268 | bestNodes.pop(); 269 | } 270 | } 271 | 272 | for (i = 0; i < dimensions.length; i += 1) { 273 | if (i === node.dimension) { 274 | linearPoint[dimensions[i]] = point[dimensions[i]]; 275 | } else { 276 | linearPoint[dimensions[i]] = node.obj[dimensions[i]]; 277 | } 278 | } 279 | 280 | linearDistance = metric(linearPoint, node.obj); 281 | 282 | if (node.right === null && node.left === null) { 283 | if (bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[1]) { 284 | saveNode(node, ownDistance); 285 | } 286 | return; 287 | } 288 | 289 | if (node.right === null) { 290 | bestChild = node.left; 291 | } else if (node.left === null) { 292 | bestChild = node.right; 293 | } else { 294 | if (point[dimension] < node.obj[dimension]) { 295 | bestChild = node.left; 296 | } else { 297 | bestChild = node.right; 298 | } 299 | } 300 | 301 | nearestSearch(bestChild); 302 | 303 | if (bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[1]) { 304 | saveNode(node, ownDistance); 305 | } 306 | 307 | if (bestNodes.size() < maxNodes || Math.abs(linearDistance) < bestNodes.peek()[1]) { 308 | if (bestChild === node.left) { 309 | otherChild = node.right; 310 | } else { 311 | otherChild = node.left; 312 | } 313 | if (otherChild !== null) { 314 | nearestSearch(otherChild); 315 | } 316 | } 317 | } 318 | 319 | if (maxDistance) { 320 | for (i = 0; i < maxNodes; i += 1) { 321 | bestNodes.push([null, maxDistance]); 322 | } 323 | } 324 | 325 | nearestSearch(self.root); 326 | 327 | result = []; 328 | 329 | for (i = 0; i < maxNodes; i += 1) { 330 | if (bestNodes.content[i]) { 331 | result.push([bestNodes.content[i][0].obj, bestNodes.content[i][1]]); 332 | } 333 | } 334 | return result; 335 | }; 336 | 337 | this.balanceFactor = function () { 338 | function height(node) { 339 | if (node === null) { 340 | return 0; 341 | } 342 | return Math.max(height(node.left), height(node.right)) + 1; 343 | } 344 | 345 | function count(node) { 346 | if (node === null) { 347 | return 0; 348 | } 349 | return count(node.left) + count(node.right) + 1; 350 | } 351 | 352 | return height(self.root) / (Math.log(count(self.root)) / Math.log(2)); 353 | }; 354 | } 355 | 356 | // Binary heap implementation from: 357 | // http://eloquentjavascript.net/appendix2.html 358 | 359 | function BinaryHeap(scoreFunction){ 360 | this.content = []; 361 | this.scoreFunction = scoreFunction; 362 | } 363 | 364 | BinaryHeap.prototype = { 365 | push: function(element) { 366 | // Add the new element to the end of the array. 367 | this.content.push(element); 368 | // Allow it to bubble up. 369 | this.bubbleUp(this.content.length - 1); 370 | }, 371 | 372 | pop: function() { 373 | // Store the first element so we can return it later. 374 | var result = this.content[0]; 375 | // Get the element at the end of the array. 376 | var end = this.content.pop(); 377 | // If there are any elements left, put the end element at the 378 | // start, and let it sink down. 379 | if (this.content.length > 0) { 380 | this.content[0] = end; 381 | this.sinkDown(0); 382 | } 383 | return result; 384 | }, 385 | 386 | peek: function() { 387 | return this.content[0]; 388 | }, 389 | 390 | remove: function(node) { 391 | var len = this.content.length; 392 | // To remove a value, we must search through the array to find 393 | // it. 394 | for (var i = 0; i < len; i++) { 395 | if (this.content[i] == node) { 396 | // When it is found, the process seen in 'pop' is repeated 397 | // to fill up the hole. 398 | var end = this.content.pop(); 399 | if (i != len - 1) { 400 | this.content[i] = end; 401 | if (this.scoreFunction(end) < this.scoreFunction(node)) 402 | this.bubbleUp(i); 403 | else 404 | this.sinkDown(i); 405 | } 406 | return; 407 | } 408 | } 409 | throw new Error("Node not found."); 410 | }, 411 | 412 | size: function() { 413 | return this.content.length; 414 | }, 415 | 416 | bubbleUp: function(n) { 417 | // Fetch the element that has to be moved. 418 | var element = this.content[n]; 419 | // When at 0, an element can not go up any further. 420 | while (n > 0) { 421 | // Compute the parent element's index, and fetch it. 422 | var parentN = Math.floor((n + 1) / 2) - 1, 423 | parent = this.content[parentN]; 424 | // Swap the elements if the parent is greater. 425 | if (this.scoreFunction(element) < this.scoreFunction(parent)) { 426 | this.content[parentN] = element; 427 | this.content[n] = parent; 428 | // Update 'n' to continue at the new position. 429 | n = parentN; 430 | } 431 | // Found a parent that is less, no need to move it further. 432 | else { 433 | break; 434 | } 435 | } 436 | }, 437 | 438 | sinkDown: function(n) { 439 | // Look up the target element and its score. 440 | var length = this.content.length, 441 | element = this.content[n], 442 | elemScore = this.scoreFunction(element); 443 | 444 | while(true) { 445 | // Compute the indices of the child elements. 446 | var child2N = (n + 1) * 2, child1N = child2N - 1; 447 | // This is used to store the new position of the element, 448 | // if any. 449 | var swap = null; 450 | // If the first child exists (is inside the array)... 451 | if (child1N < length) { 452 | // Look it up and compute its score. 453 | var child1 = this.content[child1N], 454 | child1Score = this.scoreFunction(child1); 455 | // If the score is less than our element's, we need to swap. 456 | if (child1Score < elemScore) 457 | swap = child1N; 458 | } 459 | // Do the same checks for the other child. 460 | if (child2N < length) { 461 | var child2 = this.content[child2N], 462 | child2Score = this.scoreFunction(child2); 463 | if (child2Score < (swap == null ? elemScore : child1Score)){ 464 | swap = child2N; 465 | } 466 | } 467 | 468 | // If the element needs to be moved, swap it, and continue. 469 | if (swap != null) { 470 | this.content[n] = this.content[swap]; 471 | this.content[swap] = element; 472 | n = swap; 473 | } 474 | // Otherwise, we are done. 475 | else { 476 | break; 477 | } 478 | } 479 | } 480 | }; 481 | 482 | this.KdTree = kdTree; 483 | 484 | })(); 485 | 486 | -------------------------------------------------------------------------------- /mingle.js: -------------------------------------------------------------------------------- 1 | /*global define, Float32Array */ 2 | (function () { 3 | 4 | 5 | //General convenience functions and constants 6 | Math.PHI = (1 + Math.sqrt(5)) / 2; 7 | 8 | function $dist(a, b) { 9 | var diffX = a[0] - b[0], 10 | diffY = a[1] - b[1]; 11 | return Math.sqrt(diffX * diffX + diffY * diffY); 12 | } 13 | 14 | function $norm(a) { 15 | return Math.sqrt(a[0] * a[0] + a[1] * a[1]); 16 | } 17 | 18 | function $normalize(a) { 19 | var n = $norm(a); 20 | return $mult(1 / n, a); 21 | } 22 | 23 | function $lerp(a, b, delta) { 24 | return [ a[0] * (1 - delta) + b[0] * delta, 25 | a[1] * (1 - delta) + b[1] * delta ]; 26 | } 27 | 28 | function $add(a, b) { 29 | return [ a[0] + b[0], a[1] + b[1] ]; 30 | } 31 | 32 | function $sub(a, b) { 33 | return [ a[0] - b[0], a[1] - b[1] ]; 34 | } 35 | 36 | function $dot(a, b) { 37 | return a[0] * b[0] + a[1] * b[1]; 38 | } 39 | 40 | function $mult(k, a) { 41 | return [ a[0] * k, a[1] * k ]; 42 | } 43 | 44 | function $lerpPoint(from, to, delta) { 45 | return [ $lerp(from[0], to[0], delta), $lerp(from[1], to[1], delta) ]; 46 | } 47 | 48 | function cloneJSON(json) { 49 | return JSON.parse( JSON.stringify( json ) ); 50 | } 51 | 52 | function cloneEdge(json) { 53 | var i, l = json.length, ans = Array(json.length); 54 | for (i = 0; i < l; ++i) { 55 | ans[i] = { 56 | node: json[i].node, 57 | pos: json[i].pos, 58 | normal: json[i].normal && json[i].normal.slice() 59 | }; 60 | } 61 | return ans; 62 | } 63 | 64 | //Extend generic Graph class with bundle methods and rendering options 65 | function expandEdgesHelper(node, array, collect) { 66 | var coords = node.data.coords, i, l, p, ps; 67 | 68 | if (!array.length) { 69 | array.push([ (coords[0] + coords[2]) / 2, 70 | (coords[1] + coords[3]) / 2 ]); 71 | } 72 | 73 | array.unshift([ coords[0], coords[1] ]); 74 | array.push ([ coords[2], coords[3] ]); 75 | ps = node.data.parents; 76 | if (ps) { 77 | for (i = 0, l = ps.length; i < l; ++i) { 78 | expandEdgesHelper(ps[i], array.slice(), collect); 79 | } 80 | } else { 81 | collect.push(array); 82 | } 83 | } 84 | 85 | function setNormalVector(nodeFrom, nodeTo) { 86 | var node = nodeFrom || nodeTo, dir, coords, normal; 87 | if (!nodeFrom || !nodeTo) { 88 | coords = node.data.coords; 89 | dir = [ coords[2] - coords[0], coords[3] - coords[1] ]; 90 | normal = [ -dir[1], dir[0] ]; 91 | normal = $mult(normal, 1 / $norm(normal)); 92 | } 93 | return normal; 94 | } 95 | 96 | function createPosItem(node, pos, index, total) { 97 | return { 98 | node: node,//.toJSON(), 99 | pos: pos, 100 | normal: null 101 | }; 102 | } 103 | 104 | //Extend generic Graph class with bundle methods and rendering options 105 | function expandEdgesRichHelper(node, array, collect) { 106 | var coords = node.data.coords, i, l, p, ps, a, posItem; 107 | ps = node.data.parents; 108 | if (ps) { 109 | for (i = 0, l = ps.length; i < l; ++i) { 110 | a = array.slice(); 111 | if (!a.length) { 112 | p = [ (coords[0] + coords[2]) / 2, (coords[1] + coords[3]) / 2 ]; 113 | posItem = createPosItem(node, p, i, l); 114 | a.push(posItem); 115 | } 116 | 117 | posItem = createPosItem(node, [ coords[0], coords[1] ], i, l); 118 | a.unshift(posItem); 119 | posItem = createPosItem(node, [ coords[2], coords[3] ], i, l); 120 | a.push (posItem); 121 | 122 | expandEdgesRichHelper(ps[i], a, collect); 123 | } 124 | } else { 125 | a = array.slice(); 126 | if (!a.length) { 127 | p = [ (coords[0] + coords[2]) / 2, (coords[1] + coords[3]) / 2 ]; 128 | posItem = createPosItem(node, p, 0, 1); 129 | a.push(posItem); 130 | } 131 | 132 | posItem = createPosItem(node, [ coords[0], coords[1] ], 0, 1); 133 | a.unshift(posItem); 134 | posItem = createPosItem(node, [ coords[2], coords[3] ], 0, 1); 135 | a.push (posItem); 136 | 137 | collect.push(a); 138 | } 139 | } 140 | 141 | Graph.Node.prototype.expandEdges = function() { 142 | if (this.expandedEdges) { 143 | return this.expandedEdges; 144 | } 145 | var ans = []; 146 | expandEdgesRichHelper(this, [], ans); 147 | this.expandedEdges = ans; 148 | return ans; 149 | }; 150 | 151 | Graph.Node.prototype.unbundleEdges = function(delta) { 152 | var expandedEdges = this.expandEdges(), 153 | ans = Array(expandedEdges.length), 154 | min = Math.min, 155 | i, l, j, n, edge, edgeCopy, normal, x0, xk, xk_x0, xi, xi_x0, xi_bar, dot, norm, norm2, c, last; 156 | 157 | delta = delta || 0; 158 | this.unbundledEdges = this.unbundledEdges || {}; 159 | 160 | if ((delta === 0 || delta === 1) && 161 | this.unbundledEdges[delta]) { 162 | return this.unbundledEdges[delta]; 163 | } 164 | 165 | for (i = 0, l = expandedEdges.length; i < l; ++i) { 166 | edge = expandedEdges[i]; 167 | last = edge.length -1; 168 | edgeCopy = cloneEdge(edge); 169 | //edgeCopy = cloneJSON(edge); 170 | x0 = edge[0].pos; 171 | xk = edge[last].pos; 172 | xk_x0 = $sub(xk, x0); 173 | 174 | edgeCopy[0].unbundledPos = edgeCopy[0].pos.slice(); 175 | normal = $sub(edgeCopy[1].pos, edgeCopy[0].pos); 176 | normal = $normalize([ -normal[1], normal[0] ]); 177 | edgeCopy[0].normal = normal; 178 | 179 | edgeCopy[last].unbundledPos = edgeCopy[edge.length - 1].pos.slice(); 180 | normal = $sub(edgeCopy[last].pos, edgeCopy[last -1].pos); 181 | normal = $normalize([ -normal[1], normal[0] ]); 182 | edgeCopy[last].normal = normal; 183 | 184 | for (j = 1, n = edge.length -1; j < n; ++j) { 185 | xi = edge[j].pos; 186 | xi_x0 = $sub(xi, x0); 187 | dot = $dot(xi_x0, xk_x0); 188 | norm = $dist(xk, x0); 189 | norm2 = norm * norm; 190 | c = dot / norm2; 191 | xi_bar = $add(x0, $mult(c, xk_x0)); 192 | edgeCopy[j].unbundledPos = $lerp(xi_bar, xi, delta); 193 | normal = $sub(edgeCopy[j + 1].pos, edgeCopy[j - 1].pos); 194 | normal = $normalize([ -normal[1], normal[0] ]); 195 | edgeCopy[j].normal = normal; 196 | } 197 | ans[i] = edgeCopy; 198 | } 199 | 200 | if (delta === 0 || delta === 1) { 201 | this.unbundledEdges[delta] = ans; 202 | } 203 | 204 | return ans; 205 | }; 206 | 207 | Graph.Render = { 208 | renderLine: function(ctx, edges, options) { 209 | options = options || {}; 210 | var lineWidth = options.lineWidth || 1, 211 | fillStyle = options.fillStyle || 'gray', 212 | i, l, j, n, e, pos; 213 | 214 | ctx.fillStyle = fillStyle; 215 | ctx.lineWidth = lineWidth; 216 | for (i = 0, l = edges.length; i < l; ++i) { 217 | e = edges[i]; 218 | ctx.beginPath(); 219 | for (j = 0, n = e.length; j < n; ++j) { 220 | pos = e[j].unbundledPos; 221 | if (j == 0) { 222 | ctx.moveTo(pos[0], pos[1]); 223 | } else { 224 | ctx.lineTo(pos[0], pos[1]); 225 | } 226 | } 227 | ctx.stroke(); 228 | ctx.closePath(); 229 | } 230 | }, 231 | 232 | renderQuadratic: function(ctx, edges, options) { 233 | options = options || {}; 234 | var lineWidth = options.lineWidth || 1, 235 | fillStyle = options.fillStyle || 'gray', 236 | margin = (options.margin || 0) * (options.delta || 0), 237 | lengthBefore, lengthAfter, 238 | index, i, l, j, k, n, e, node, pos, pos0, pos1, pos2, pos3, pos01, pos02, pos03, pos04, colorFrom, colorTo, grd, 239 | midPos, quadStart, weightStart, posStart, nodeStart, posItem, posItemStart, 240 | dist, distMin, nodeArray, nodeLength; 241 | 242 | ctx.fillStyle = fillStyle; 243 | ctx.lineWidth = lineWidth; 244 | 245 | for (i = 0, l = edges.length; i < l; ++i) { 246 | e = edges[i]; 247 | quadStart = null; 248 | posStart = null; 249 | nodeStart = e[0].node; 250 | ctx.lineWidth = (Math.max(1, nodeStart.data.weight) || 1) * (options.scale || 1); 251 | if (nodeStart.data.color && Array.isArray(nodeStart.data.color)) { 252 | colorFrom = nodeStart.data.color[0]; 253 | colorTo = nodeStart.data.color[1]; 254 | grd = ctx.createLinearGradient(nodeStart.data.coords[0], 255 | nodeStart.data.coords[1], 256 | nodeStart.data.coords[2], 257 | nodeStart.data.coords[3]); 258 | grd.addColorStop(0, colorFrom); 259 | grd.addColorStop(0.4, colorFrom); 260 | grd.addColorStop(0.6, colorTo); 261 | grd.addColorStop(1, colorTo); 262 | ctx.strokeStyle = grd; 263 | } else { 264 | ctx.strokeStyle = nodeStart.data.color || ctx.strokeStyle; 265 | } 266 | ctx.globalAlpha = nodeStart.data.alpha == undefined ? 1 : nodeStart.data.alpha; 267 | ctx.beginPath(); 268 | for (j = 0, n = e.length; j < n; ++j) { 269 | posItem = e[j]; 270 | pos = posItem.unbundledPos; 271 | if (j !== 0) { 272 | pos0 = posStart || e[j - 1].unbundledPos; 273 | pos = this.adjustPosition(nodeStart.id, posItem, pos, margin, options.delta || 0); 274 | midPos = $lerp(pos0, pos, 0.5); 275 | pos1 = $lerp(pos0, midPos, j === 1 ? 0 : options.curviness || 0); 276 | pos3 = pos; 277 | pos2 = $lerp(midPos, pos3, j === n - 1 ? 1 : (1 - (options.curviness || 0))); 278 | //ctx.lineCap = 'butt';//'round'; 279 | //ctx.beginPath(); 280 | if (quadStart) { 281 | //ctx.strokeStyle = 'black'; 282 | ctx.moveTo(quadStart[0], quadStart[1]); 283 | ctx.quadraticCurveTo(pos0[0], pos0[1], pos1[0], pos1[1]); 284 | //ctx.stroke(); 285 | //ctx.closePath(); 286 | } 287 | //ctx.beginPath(); 288 | //ctx.strokeStyle = 'red'; 289 | ctx.moveTo(pos1[0], pos1[1]); 290 | ctx.lineTo(pos2[0], pos2[1]); 291 | //ctx.stroke(); 292 | //ctx.closePath(); 293 | quadStart = pos2; 294 | posStart = pos; 295 | } 296 | } 297 | ctx.stroke(); 298 | ctx.closePath(); 299 | } 300 | }, 301 | 302 | adjustPosition: function(id, posItem, pos, margin, delta) { 303 | var nodeArray = posItem.node.data.nodeArray, 304 | epsilon = 1, 305 | nodeLength, index, lengthBefore, 306 | lengthAfter, k, node; 307 | 308 | if (nodeArray) { 309 | nodeLength = nodeArray.length; 310 | index = Infinity; 311 | lengthBefore = 0; 312 | lengthAfter = 0; 313 | for (k = 0; k < nodeLength; ++k) { 314 | node = nodeArray[k]; 315 | if (node.id == id) { 316 | index = k; 317 | } 318 | if (k < index) { 319 | lengthBefore += (node.data.weight || 0) + margin; 320 | } else if (k > index) { 321 | lengthAfter += (node.data.weight || 0) + margin; 322 | } 323 | } 324 | //remove -margin to get the line weight into account. 325 | //pos = $add(pos, $mult((lengthBefore - (lengthBefore + lengthAfter) / 2) * -margin, posItem.normal)); 326 | pos = $add(pos, $mult((lengthBefore - (lengthBefore + lengthAfter) / 2) * Math.min(epsilon, delta), posItem.normal)); 327 | } 328 | 329 | return pos; 330 | }, 331 | 332 | renderBezier: function(ctx, edges, options) { 333 | options = options || {}; 334 | var pct = options.curviness || 0, 335 | i, l, j, n, e, pos, midpoint, c1, c2, start, end; 336 | 337 | for (i = 0, l = edges.length; i < l; ++i) { 338 | e = edges[i]; 339 | start = e[0].unbundledPos; 340 | ctx.strokeStyle = e[0].node.data.color || ctx.strokeStyle; 341 | ctx.lineWidth = e[0].node.data.weight || 1; 342 | midpoint = e[(e.length - 1) / 2].unbundledPos; 343 | if (e.length > 3) { 344 | c1 = e[1].unbundledPos; 345 | c2 = e[(e.length - 1) / 2 - 1].unbundledPos; 346 | end = $lerp(midpoint, c2, 1 - pct); 347 | ctx.beginPath(); 348 | ctx.moveTo(start[0], start[1]); 349 | ctx.bezierCurveTo(c1[0], c1[1], c2[0], c2[1], end[0], end[1]); 350 | c1 = e[(e.length - 1) / 2 + 1].unbundledPos; 351 | c2 = e[e.length - 2].unbundledPos; 352 | end = e[e.length - 1].unbundledPos; 353 | if (1 - pct) { 354 | //line to midpoint + pct of something 355 | start = $lerp(midpoint, c1, 1 - pct); 356 | ctx.lineTo(start[0], start[1]); 357 | } 358 | ctx.bezierCurveTo(c1[0], c1[1], c2[0], c2[1], end[0], end[1]); 359 | ctx.stroke(); 360 | ctx.closePath(); 361 | } else { 362 | ctx.beginPath(); 363 | ctx.moveTo(start[0], start[1]); 364 | end = e[e.length -1].unbundledPos; 365 | ctx.lineTo(end[0], end[1]); 366 | } 367 | } 368 | } 369 | }; 370 | 371 | 372 | //Edge bundling algorithm class. 373 | function Bundler(options) { 374 | this.options = options || {}; 375 | this.graph = new Graph(); 376 | this.kdTree = null; 377 | } 378 | 379 | //copy static methods to render lines and other from Graph 380 | Bundler.Graph = Graph.Render; 381 | 382 | Bundler.prototype = { 383 | setNodes: function(nodes) { 384 | var i, l, graph = this.graph; 385 | graph.clear(); 386 | for (i = 0, l = nodes.length; i < l; ++i) { 387 | graph.addNode(nodes[i]); 388 | } 389 | }, 390 | 391 | buildKdTree: function() { 392 | var nodeArray = []; 393 | this.graph.each(function(n) { 394 | var coords = n.data.coords; 395 | n.x = coords[0]; 396 | n.y = coords[1]; 397 | n.z = coords[2]; 398 | n.w = coords[3]; 399 | nodeArray.push(n); 400 | }); 401 | 402 | this.kdTree = new KdTree(nodeArray, function(a, b) { 403 | var diff0 = a.x - b.x, 404 | diff1 = a.y - b.y, 405 | diff2 = a.z - b.z, 406 | diff3 = a.w - b.w; 407 | 408 | return Math.sqrt(diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3); 409 | }, ['x', 'y', 'z', 'w']); 410 | }, 411 | 412 | buildNearestNeighborGraph: function(k) { 413 | k = k || 10; 414 | var graph = this.graph, node, dist, kdTree; 415 | this.buildKdTree(); 416 | kdTree = this.kdTree; 417 | graph.each(function(n) { 418 | var nodes = kdTree.nearest(n, k), i, l; 419 | for (i = 0, l = nodes.length; i < l; ++i) { 420 | node = nodes[i][0]; 421 | dist = nodes[i][1]; 422 | if (node.id != n.id) { 423 | graph.addEdge(n, node); 424 | } 425 | } 426 | }); 427 | }, 428 | 429 | computeIntermediateNodePositions: function(node) { 430 | var m1, m2, centroids, a, b, c, tau, f, res; 431 | if (!node.data.nodes) { 432 | return; 433 | } 434 | centroids = this.getCentroids(node.data.nodes); 435 | f = this.costFunction.bind(this, node, centroids); 436 | a = 0; 437 | b = 1; 438 | c = 0.72; //because computers 439 | tau = 0.1; 440 | res = this.goldenSectionSearch(a, b, c, tau, f); 441 | f(res); //set m1 and m2; 442 | }, 443 | 444 | costFunction: function(node, centroids, x) { 445 | var top, bottom, m1, m2, ink, alpha, p; 446 | x /= 2; 447 | top = centroids[0]; 448 | bottom = centroids[1]; 449 | m1 = $lerp(top, bottom, x); 450 | m2 = $lerp(top, bottom, 1 - x); 451 | node.data.m1 = m1; 452 | node.data.m2 = m2; 453 | delete node.data.ink; 454 | ink = this.getInkValue(node); 455 | alpha = this.getMaxTurningAngleValue(node, m1, m2); 456 | p = this.options.angleStrength || 1.2; 457 | return ink * (1 + Math.sin(alpha) / p); 458 | }, 459 | 460 | goldenSectionSearch: function(a, b, c, tau, f) { 461 | var phi = Math.PHI, 462 | resphi = 2 - Math.PHI, 463 | abs = Math.abs, x; 464 | 465 | if (c - b > b - a) { 466 | x = b + resphi * (c - b); 467 | } else { 468 | x = b - resphi * (b - a); 469 | } 470 | if (abs(c - a) < tau * (abs(b) + abs(x))) { 471 | return (c + a) / 2; 472 | } 473 | if (f(x) < f(b)) { 474 | if (c - b > b - a) { 475 | return this.goldenSectionSearch(b, x, c, tau, f); 476 | } 477 | return this.goldenSectionSearch(a, x, b, tau, f); 478 | } 479 | if (c - b > b - a) { 480 | return this.goldenSectionSearch(a, b, x, tau, f); 481 | } 482 | return this.goldenSectionSearch(x, b, c, tau, f); 483 | }, 484 | 485 | getCentroids: function(nodes) { 486 | var topCentroid = [0, 0], 487 | bottomCentroid = [0, 0], 488 | coords, i, l; 489 | 490 | for (i = 0, l = nodes.length; i < l; ++i) { 491 | coords = nodes[i].data.coords; 492 | topCentroid[0] += coords[0]; 493 | topCentroid[1] += coords[1]; 494 | bottomCentroid[0] += coords[2]; 495 | bottomCentroid[1] += coords[3]; 496 | } 497 | 498 | topCentroid[0] /= l; 499 | topCentroid[1] /= l; 500 | bottomCentroid[0] /= l; 501 | bottomCentroid[1] /= l; 502 | 503 | return [ topCentroid, bottomCentroid ]; 504 | }, 505 | 506 | getInkValue: function(node, depth) { 507 | var data = node.data, 508 | sqrt = Math.sqrt, 509 | coords, diffX, diffY, 510 | m1, m2, acum, i, l, nodes, 511 | ni; 512 | 513 | depth = depth || 0; 514 | 515 | //bundled node 516 | if (!depth && (data.bundle || data.nodes)) { 517 | nodes = data.bundle ? data.bundle.data.nodes : data.nodes; 518 | m1 = data.m1; 519 | m2 = data.m2; 520 | acum = 0; 521 | for (i = 0, l = nodes.length; i < l; ++i) { 522 | ni = nodes[i]; 523 | coords = ni.data.coords; 524 | diffX = m1[0] - coords[0]; 525 | diffY = m1[1] - coords[1]; 526 | acum += $norm([ diffX, diffY ]); 527 | diffX = m2[0] - coords[2]; 528 | diffY = m2[1] - coords[3]; 529 | acum += $norm([ diffX, diffY ]); 530 | acum += this.getInkValue(ni, depth + 1); 531 | } 532 | if (!depth) { 533 | acum += $dist(m1, m2); 534 | } 535 | return (node.data.ink = acum); 536 | } 537 | 538 | //coalesced node 539 | if (data.parents) { 540 | nodes = data.parents; 541 | m1 = [ data.coords[0], data.coords[1] ]; 542 | m2 = [ data.coords[2], data.coords[3] ]; 543 | acum = 0; 544 | for (i = 0, l = nodes.length; i < l; ++i) { 545 | ni = nodes[i]; 546 | coords = ni.data.coords; 547 | diffX = m1[0] - coords[0]; 548 | diffY = m1[1] - coords[1]; 549 | acum += $norm([ diffX, diffY ]); 550 | diffX = m2[0] - coords[2]; 551 | diffY = m2[1] - coords[3]; 552 | acum += $norm([ diffX, diffY ]); 553 | acum += this.getInkValue(ni, depth + 1); 554 | } 555 | //only add the distance if this is the first recursion 556 | if (!depth) { 557 | acum += $dist(m1, m2); 558 | } 559 | return (node.data.ink = acum); 560 | } 561 | 562 | //simple node 563 | if (depth) { 564 | return (node.data.ink = 0); 565 | } 566 | coords = node.data.coords; 567 | diffX = coords[0] - coords[2]; 568 | diffY = coords[1] - coords[3]; 569 | return (node.data.ink = $norm([ diffX, diffY ])); 570 | 571 | }, 572 | 573 | getMaxTurningAngleValue: function(node, m1, m2) { 574 | var sqrt = Math.sqrt, 575 | abs = Math.abs, 576 | acos = Math.acos, 577 | m2Tom1 = [ m1[0] - m2[0], m1[1] - m2[1] ], 578 | m1Tom2 = [ -m2Tom1[0], -m2Tom1[1] ], 579 | m1m2Norm = $norm(m2Tom1), 580 | angle = 0, nodes, vec, norm, dot, angleValue, 581 | x, y, coords, i, l, n; 582 | 583 | if (node.data.bundle || node.data.nodes) { 584 | nodes = node.data.bundle ? node.data.bundle.data.nodes : node.data.nodes; 585 | for (i = 0, l = nodes.length; i < l; ++i) { 586 | coords = nodes[i].data.coords; 587 | vec = [ coords[0] - m1[0], coords[1] - m1[1] ]; 588 | norm = $norm(vec); 589 | dot = vec[0] * m2Tom1[0] + vec[1] * m2Tom1[1]; 590 | angleValue = abs(acos(dot / norm / m1m2Norm)); 591 | angle = angle < angleValue ? angleValue : angle; 592 | 593 | vec = [ coords[2] - m2[0], coords[3] - m2[1] ]; 594 | norm = $norm(vec); 595 | dot = vec[0] * m1Tom2[0] + vec[1] * m1Tom2[1]; 596 | angleValue = abs(acos(dot / norm / m1m2Norm)); 597 | angle = angle < angleValue ? angleValue : angle; 598 | } 599 | 600 | return angle; 601 | } 602 | 603 | return -1; 604 | }, 605 | 606 | getCombinedNode: function(node1, node2, data) { 607 | node1 = node1.data.bundle || node1; 608 | node2 = node2.data.bundle || node2; 609 | 610 | var id = node1.id + '-' + node2.id, 611 | name = node1.name + '-' + node2.name, 612 | nodes1 = node1.data.nodes || [ node1 ], 613 | nodes2 = node2.data.nodes || [ node2 ], 614 | weight1 = node1.data.weight || 0, 615 | weight2 = node2.data.weight || 0, 616 | nodes = [], ans; 617 | 618 | if (node1.id == node2.id) { 619 | return node1; 620 | } 621 | nodes.push.apply(nodes, nodes1); 622 | nodes.push.apply(nodes, nodes2); 623 | data = data || {}; 624 | data.nodes = nodes; 625 | data.nodeArray = (node1.data.nodeArray || []).concat(node2.data.nodeArray || []); 626 | data.weight = weight1 + weight2; 627 | ans = { 628 | id: id, 629 | name: name, 630 | data: data 631 | }; 632 | 633 | this.computeIntermediateNodePositions(ans); 634 | 635 | return ans; 636 | }, 637 | 638 | coalesceNodes: function(nodes) { 639 | var node = nodes[0], 640 | data = node.data, 641 | m1 = data.m1, 642 | m2 = data.m2, 643 | weight = nodes.reduce(function(acum, n) { return acum + (n.data.weight || 0); }, 0), 644 | coords = data.coords, 645 | bundle = data.bundle, 646 | nodeArray = [], 647 | i, l; 648 | 649 | if (m1) { 650 | coords = [ m1[0], m1[1], m2[0], m2[1] ]; 651 | 652 | //flattened nodes for cluster. 653 | for (i = 0, l = nodes.length; i < l; ++i) { 654 | nodeArray.push.apply(nodeArray, nodes[i].data.nodeArray || (nodes[i].data.parents ? [] : [ nodes[i] ])); 655 | } 656 | 657 | if (this.options.sort) { 658 | nodeArray.sort(this.options.sort); 659 | } 660 | 661 | //if (!nodeArray.length || (typeof nodeArray[0].id == 'string')) { 662 | //debugger; 663 | //} 664 | 665 | return { 666 | id: bundle.id, 667 | name: bundle.id, 668 | data: { 669 | nodeArray: nodeArray, 670 | parents: nodes, 671 | coords: coords, 672 | weight: weight, 673 | parentsInk: bundle.data.ink 674 | } 675 | }; 676 | } 677 | 678 | return nodes[0]; 679 | }, 680 | 681 | bundle: function(combinedNode, node1, node2) { 682 | var graph = this.graph; 683 | 684 | node1.data.bundle = combinedNode; 685 | node2.data.bundle = combinedNode; 686 | 687 | node1.data.ink = combinedNode.data.ink; 688 | node1.data.m1 = combinedNode.data.m1; 689 | node1.data.m2 = combinedNode.data.m2; 690 | //node1.data.nodeArray = combinedNode.data.nodeArray; 691 | 692 | node2.data.ink = combinedNode.data.ink; 693 | node2.data.m1 = combinedNode.data.m1; 694 | node2.data.m2 = combinedNode.data.m2; 695 | //node2.data.nodeArray = combinedNode.data.nodeArray; 696 | }, 697 | 698 | updateGraph: function(graph, groupedNode, nodes, ids) { 699 | var i, l, n, connections, 700 | checkConnection = function(e) { 701 | var nodeToId = e.nodeTo.id; 702 | if (!ids[nodeToId]) { 703 | connections.push(e.nodeTo); 704 | } 705 | }; 706 | for (i = 0, l = nodes.length; i < l; ++i) { 707 | n = nodes[i]; 708 | connections = []; 709 | n.eachEdge(checkConnection); 710 | graph.removeNode(n.id); 711 | } 712 | graph.addNode(groupedNode); 713 | for (i = 0, l = connections.length; i < l; ++i) { 714 | graph.addEdge(groupedNode, connections[i]); 715 | } 716 | }, 717 | 718 | coalesceGraph: function() { 719 | var graph = this.graph, 720 | newGraph = new Graph(), 721 | groupsIds = {}, 722 | maxGroup = -Infinity, 723 | nodes, i, l, ids, groupedNode, connections, 724 | updateGraph = this.updateGraph, 725 | coalesceNodes = this.coalesceNodes.bind(this); 726 | 727 | graph.each(function(node) { 728 | var group = node.data.group; 729 | if (maxGroup < group) { 730 | maxGroup = group; 731 | } 732 | if (!groupsIds[group]) { 733 | groupsIds[group] = {}; 734 | } 735 | groupsIds[group][node.id] = node; 736 | }); 737 | 738 | maxGroup++; 739 | while (maxGroup--) { 740 | ids = groupsIds[maxGroup]; 741 | nodes = []; 742 | for (i in ids) { 743 | nodes.push(ids[i]); 744 | } 745 | if (nodes.length) { 746 | groupedNode = coalesceNodes(nodes); 747 | updateGraph(graph, groupedNode, nodes, ids); 748 | } 749 | } 750 | }, 751 | 752 | getMaximumInkSavingNeighbor: function(n) { 753 | var nodeFrom = n, 754 | getInkValue = this.getInkValue.bind(this), 755 | inkFrom = getInkValue(nodeFrom), 756 | combineNodes = this.getCombinedNode.bind(this), 757 | inkTotal = Infinity, 758 | bundle = Array(2), 759 | combinedBundle; 760 | 761 | n.eachEdge(function(e) { 762 | var nodeTo = e.nodeTo, 763 | inkTo = getInkValue(nodeTo), 764 | combined = combineNodes(nodeFrom, nodeTo), 765 | inkUnion = getInkValue(combined), 766 | inkValue = inkUnion - (inkFrom + inkTo); 767 | 768 | if (inkTotal > inkValue) { 769 | inkTotal = inkValue; 770 | bundle[0] = nodeFrom; 771 | bundle[1] = nodeTo; 772 | combinedBundle = combined; 773 | } 774 | }); 775 | 776 | return { 777 | bundle: bundle, 778 | inkTotal: inkTotal, 779 | combined: combinedBundle 780 | }; 781 | }, 782 | 783 | MINGLE: function() { 784 | var edgeProximityGraph = this.graph, 785 | that = this, 786 | totalGain = 0, 787 | ungrouped = -1, 788 | gain = 0, 789 | k = 0, 790 | clean = function(n) { n.data.group = ungrouped; }, 791 | nodeMingle = function(node) { 792 | if (node.data.group == ungrouped) { 793 | var ans = that.getMaximumInkSavingNeighbor(node), 794 | bundle = ans.bundle, 795 | u = bundle[0], 796 | v = bundle[1], 797 | combined = ans.combined, 798 | gainUV = -ans.inkTotal; 799 | 800 | //graph has been collapsed and is now only one node 801 | if (!u && !v) { 802 | gain = -Infinity; 803 | return; 804 | } 805 | 806 | if (gainUV > 0) { 807 | that.bundle(combined, u, v); 808 | gain += gainUV; 809 | if (v.data.group != ungrouped) { 810 | u.data.group = v.data.group; 811 | } else { 812 | u.data.group = v.data.group = k; 813 | } 814 | } else { 815 | u.data.group = k; 816 | } 817 | k++; 818 | } 819 | }; 820 | 821 | do { 822 | gain = 0; 823 | k = 0; 824 | edgeProximityGraph.each(clean); 825 | edgeProximityGraph.each(nodeMingle); 826 | this.coalesceGraph(); 827 | totalGain += gain; 828 | } while (gain > 0); 829 | } 830 | }; 831 | 832 | this.Bundler = Bundler; 833 | 834 | return Bundler; 835 | 836 | })(); 837 | 838 | -------------------------------------------------------------------------------- /screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philogb/mingle/c7d10b62b55fe614842f855c32bbb113561359b8/screen.png --------------------------------------------------------------------------------