├── LICENSE ├── README.md ├── box └── box.js ├── bullet ├── README.md └── bullet.js ├── chernoff ├── README.md └── chernoff.js ├── cubehelix ├── README.md └── cubehelix.js ├── fisheye ├── README.md └── fisheye.js ├── force_labels ├── README.md └── force_labels.js ├── geo ├── polyhedron │ ├── README.md │ └── polyhedron.js ├── projection │ └── README.md └── tile │ ├── README.md │ └── tile.js ├── geodesic ├── README.md └── geodesic.js ├── geom └── contour │ ├── README.md │ └── contour.js ├── graph ├── README.md ├── data │ ├── cities-matrix.json │ ├── cities.csv │ └── miserables.json ├── graph.js └── index.html ├── hexbin ├── README.md └── hexbin.js ├── hive ├── README.md └── hive.js ├── horizon ├── README.md └── horizon.js ├── interpolate-zoom ├── README.md ├── interpolate-zoom-test.js └── interpolate-zoom.js ├── jsonp ├── README.md └── jsonp.js ├── keybinding ├── README.md └── keybinding.js ├── lasso ├── README.md └── lasso.js ├── longscroll ├── README.md └── longscroll.js ├── qq ├── README.md └── qq.js ├── rollup ├── README.md └── rollup.js ├── sankey ├── README.md └── sankey.js ├── simplify └── README.md ├── superformula └── superformula.js └── urlencode ├── urlencode-test.js └── urlencode.js /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2015, Michael Bostock 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * The name Michael Bostock may not be used to endorse or promote products 15 | derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT, 21 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 24 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 26 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # D3 Plugins 2 | 3 | These plugins are not compatible with the latest version of D3, V4. For the latest versions, see: 4 | 5 | * [d3-hexbin](https://github.com/d3/d3-hexbin) 6 | * [d3-sankey](https://github.com/d3/d3-sankey) 7 | 8 | Please note: the plugins in this repository will soon be broken up into separate repositories for easier distributed ownership. If you have a new plugin you’d like to share, please add a link to it from the [D3 plugins wiki](https://github.com/mbostock/d3/wiki/Plugins). 9 | -------------------------------------------------------------------------------- /box/box.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | // Inspired by http://informationandvisualization.de/blog/box-plot 4 | d3.box = function() { 5 | var width = 1, 6 | height = 1, 7 | duration = 0, 8 | domain = null, 9 | value = Number, 10 | whiskers = boxWhiskers, 11 | quartiles = boxQuartiles, 12 | tickFormat = null; 13 | 14 | // For each small multiple… 15 | function box(g) { 16 | g.each(function(d, i) { 17 | d = d.map(value).sort(d3.ascending); 18 | var g = d3.select(this), 19 | n = d.length, 20 | min = d[0], 21 | max = d[n - 1]; 22 | 23 | // Compute quartiles. Must return exactly 3 elements. 24 | var quartileData = d.quartiles = quartiles(d); 25 | 26 | // Compute whiskers. Must return exactly 2 elements, or null. 27 | var whiskerIndices = whiskers && whiskers.call(this, d, i), 28 | whiskerData = whiskerIndices && whiskerIndices.map(function(i) { return d[i]; }); 29 | 30 | // Compute outliers. If no whiskers are specified, all data are "outliers". 31 | // We compute the outliers as indices, so that we can join across transitions! 32 | var outlierIndices = whiskerIndices 33 | ? d3.range(0, whiskerIndices[0]).concat(d3.range(whiskerIndices[1] + 1, n)) 34 | : d3.range(n); 35 | 36 | // Compute the new x-scale. 37 | var x1 = d3.scale.linear() 38 | .domain(domain && domain.call(this, d, i) || [min, max]) 39 | .range([height, 0]); 40 | 41 | // Retrieve the old x-scale, if this is an update. 42 | var x0 = this.__chart__ || d3.scale.linear() 43 | .domain([0, Infinity]) 44 | .range(x1.range()); 45 | 46 | // Stash the new scale. 47 | this.__chart__ = x1; 48 | 49 | // Note: the box, median, and box tick elements are fixed in number, 50 | // so we only have to handle enter and update. In contrast, the outliers 51 | // and other elements are variable, so we need to exit them! Variable 52 | // elements also fade in and out. 53 | 54 | // Update center line: the vertical line spanning the whiskers. 55 | var center = g.selectAll("line.center") 56 | .data(whiskerData ? [whiskerData] : []); 57 | 58 | center.enter().insert("line", "rect") 59 | .attr("class", "center") 60 | .attr("x1", width / 2) 61 | .attr("y1", function(d) { return x0(d[0]); }) 62 | .attr("x2", width / 2) 63 | .attr("y2", function(d) { return x0(d[1]); }) 64 | .style("opacity", 1e-6) 65 | .transition() 66 | .duration(duration) 67 | .style("opacity", 1) 68 | .attr("y1", function(d) { return x1(d[0]); }) 69 | .attr("y2", function(d) { return x1(d[1]); }); 70 | 71 | center.transition() 72 | .duration(duration) 73 | .style("opacity", 1) 74 | .attr("y1", function(d) { return x1(d[0]); }) 75 | .attr("y2", function(d) { return x1(d[1]); }); 76 | 77 | center.exit().transition() 78 | .duration(duration) 79 | .style("opacity", 1e-6) 80 | .attr("y1", function(d) { return x1(d[0]); }) 81 | .attr("y2", function(d) { return x1(d[1]); }) 82 | .remove(); 83 | 84 | // Update innerquartile box. 85 | var box = g.selectAll("rect.box") 86 | .data([quartileData]); 87 | 88 | box.enter().append("rect") 89 | .attr("class", "box") 90 | .attr("x", 0) 91 | .attr("y", function(d) { return x0(d[2]); }) 92 | .attr("width", width) 93 | .attr("height", function(d) { return x0(d[0]) - x0(d[2]); }) 94 | .transition() 95 | .duration(duration) 96 | .attr("y", function(d) { return x1(d[2]); }) 97 | .attr("height", function(d) { return x1(d[0]) - x1(d[2]); }); 98 | 99 | box.transition() 100 | .duration(duration) 101 | .attr("y", function(d) { return x1(d[2]); }) 102 | .attr("height", function(d) { return x1(d[0]) - x1(d[2]); }); 103 | 104 | // Update median line. 105 | var medianLine = g.selectAll("line.median") 106 | .data([quartileData[1]]); 107 | 108 | medianLine.enter().append("line") 109 | .attr("class", "median") 110 | .attr("x1", 0) 111 | .attr("y1", x0) 112 | .attr("x2", width) 113 | .attr("y2", x0) 114 | .transition() 115 | .duration(duration) 116 | .attr("y1", x1) 117 | .attr("y2", x1); 118 | 119 | medianLine.transition() 120 | .duration(duration) 121 | .attr("y1", x1) 122 | .attr("y2", x1); 123 | 124 | // Update whiskers. 125 | var whisker = g.selectAll("line.whisker") 126 | .data(whiskerData || []); 127 | 128 | whisker.enter().insert("line", "circle, text") 129 | .attr("class", "whisker") 130 | .attr("x1", 0) 131 | .attr("y1", x0) 132 | .attr("x2", width) 133 | .attr("y2", x0) 134 | .style("opacity", 1e-6) 135 | .transition() 136 | .duration(duration) 137 | .attr("y1", x1) 138 | .attr("y2", x1) 139 | .style("opacity", 1); 140 | 141 | whisker.transition() 142 | .duration(duration) 143 | .attr("y1", x1) 144 | .attr("y2", x1) 145 | .style("opacity", 1); 146 | 147 | whisker.exit().transition() 148 | .duration(duration) 149 | .attr("y1", x1) 150 | .attr("y2", x1) 151 | .style("opacity", 1e-6) 152 | .remove(); 153 | 154 | // Update outliers. 155 | var outlier = g.selectAll("circle.outlier") 156 | .data(outlierIndices, Number); 157 | 158 | outlier.enter().insert("circle", "text") 159 | .attr("class", "outlier") 160 | .attr("r", 5) 161 | .attr("cx", width / 2) 162 | .attr("cy", function(i) { return x0(d[i]); }) 163 | .style("opacity", 1e-6) 164 | .transition() 165 | .duration(duration) 166 | .attr("cy", function(i) { return x1(d[i]); }) 167 | .style("opacity", 1); 168 | 169 | outlier.transition() 170 | .duration(duration) 171 | .attr("cy", function(i) { return x1(d[i]); }) 172 | .style("opacity", 1); 173 | 174 | outlier.exit().transition() 175 | .duration(duration) 176 | .attr("cy", function(i) { return x1(d[i]); }) 177 | .style("opacity", 1e-6) 178 | .remove(); 179 | 180 | // Compute the tick format. 181 | var format = tickFormat || x1.tickFormat(8); 182 | 183 | // Update box ticks. 184 | var boxTick = g.selectAll("text.box") 185 | .data(quartileData); 186 | 187 | boxTick.enter().append("text") 188 | .attr("class", "box") 189 | .attr("dy", ".3em") 190 | .attr("dx", function(d, i) { return i & 1 ? 6 : -6; }) 191 | .attr("x", function(d, i) { return i & 1 ? width : 0; }) 192 | .attr("y", x0) 193 | .attr("text-anchor", function(d, i) { return i & 1 ? "start" : "end"; }) 194 | .text(format) 195 | .transition() 196 | .duration(duration) 197 | .attr("y", x1); 198 | 199 | boxTick.transition() 200 | .duration(duration) 201 | .text(format) 202 | .attr("y", x1); 203 | 204 | // Update whisker ticks. These are handled separately from the box 205 | // ticks because they may or may not exist, and we want don't want 206 | // to join box ticks pre-transition with whisker ticks post-. 207 | var whiskerTick = g.selectAll("text.whisker") 208 | .data(whiskerData || []); 209 | 210 | whiskerTick.enter().append("text") 211 | .attr("class", "whisker") 212 | .attr("dy", ".3em") 213 | .attr("dx", 6) 214 | .attr("x", width) 215 | .attr("y", x0) 216 | .text(format) 217 | .style("opacity", 1e-6) 218 | .transition() 219 | .duration(duration) 220 | .attr("y", x1) 221 | .style("opacity", 1); 222 | 223 | whiskerTick.transition() 224 | .duration(duration) 225 | .text(format) 226 | .attr("y", x1) 227 | .style("opacity", 1); 228 | 229 | whiskerTick.exit().transition() 230 | .duration(duration) 231 | .attr("y", x1) 232 | .style("opacity", 1e-6) 233 | .remove(); 234 | }); 235 | d3.timer.flush(); 236 | } 237 | 238 | box.width = function(x) { 239 | if (!arguments.length) return width; 240 | width = x; 241 | return box; 242 | }; 243 | 244 | box.height = function(x) { 245 | if (!arguments.length) return height; 246 | height = x; 247 | return box; 248 | }; 249 | 250 | box.tickFormat = function(x) { 251 | if (!arguments.length) return tickFormat; 252 | tickFormat = x; 253 | return box; 254 | }; 255 | 256 | box.duration = function(x) { 257 | if (!arguments.length) return duration; 258 | duration = x; 259 | return box; 260 | }; 261 | 262 | box.domain = function(x) { 263 | if (!arguments.length) return domain; 264 | domain = x == null ? x : d3.functor(x); 265 | return box; 266 | }; 267 | 268 | box.value = function(x) { 269 | if (!arguments.length) return value; 270 | value = x; 271 | return box; 272 | }; 273 | 274 | box.whiskers = function(x) { 275 | if (!arguments.length) return whiskers; 276 | whiskers = x; 277 | return box; 278 | }; 279 | 280 | box.quartiles = function(x) { 281 | if (!arguments.length) return quartiles; 282 | quartiles = x; 283 | return box; 284 | }; 285 | 286 | return box; 287 | }; 288 | 289 | function boxWhiskers(d) { 290 | return [0, d.length - 1]; 291 | } 292 | 293 | function boxQuartiles(d) { 294 | return [ 295 | d3.quantile(d, .25), 296 | d3.quantile(d, .5), 297 | d3.quantile(d, .75) 298 | ]; 299 | } 300 | 301 | })(); 302 | -------------------------------------------------------------------------------- /bullet/README.md: -------------------------------------------------------------------------------- 1 | # Bullet Chart 2 | 3 | [Demo](http://bl.ocks.org/mbostock/4061961) and [vertical version](http://bl.ocks.org/jasondavies/5452290). 4 | 5 | Designed by Stephen Few, a bullet chart “provides a rich display of data in a 6 | small space.” A variation on a bar chart, bullet charts compare a given 7 | quantitative measure (such as profit or revenue) against qualitative ranges 8 | (e.g., poor, satisfactory, good) and related markers (e.g., the same measure a 9 | year ago). Layout inspired by 10 | [Stephen Few](http://www.perceptualedge.com/articles/misc/Bullet_Graph_Design_Spec.pdf). 11 | Implementation based on work by 12 | [Clint Ivy](http://projects.instantcognition.com/protovis/bulletchart/), 13 | Jamie Love of [N-Squared Software](http://www.nsquaredsoftware.com/) and 14 | [Jason Davies](http://www.jasondavies.com/). 15 | -------------------------------------------------------------------------------- /bullet/bullet.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | // Chart design based on the recommendations of Stephen Few. Implementation 4 | // based on the work of Clint Ivy, Jamie Love, and Jason Davies. 5 | // http://projects.instantcognition.com/protovis/bulletchart/ 6 | d3.bullet = function() { 7 | var orient = "left", 8 | reverse = false, 9 | vertical = false, 10 | ranges = bulletRanges, 11 | markers = bulletMarkers, 12 | measures = bulletMeasures, 13 | width = 380, 14 | height = 30, 15 | xAxis = d3.svg.axis(); 16 | 17 | // For each small multiple… 18 | function bullet(g) { 19 | g.each(function(d, i) { 20 | var rangez = ranges.call(this, d, i).slice().sort(d3.descending), 21 | markerz = markers.call(this, d, i).slice().sort(d3.descending), 22 | measurez = measures.call(this, d, i).slice().sort(d3.descending), 23 | g = d3.select(this), 24 | extentX, 25 | extentY; 26 | 27 | var wrap = g.select("g.wrap"); 28 | if (wrap.empty()) wrap = g.append("g").attr("class", "wrap"); 29 | 30 | if (vertical) { 31 | extentX = height, extentY = width; 32 | wrap.attr("transform", "rotate(90)translate(0," + -width + ")"); 33 | } else { 34 | extentX = width, extentY = height; 35 | wrap.attr("transform", null); 36 | } 37 | 38 | // Compute the new x-scale. 39 | var x1 = d3.scale.linear() 40 | .domain([0, Math.max(rangez[0], markerz[0], measurez[0])]) 41 | .range(reverse ? [extentX, 0] : [0, extentX]); 42 | 43 | // Retrieve the old x-scale, if this is an update. 44 | var x0 = this.__chart__ || d3.scale.linear() 45 | .domain([0, Infinity]) 46 | .range(x1.range()); 47 | 48 | // Stash the new scale. 49 | this.__chart__ = x1; 50 | 51 | // Derive width-scales from the x-scales. 52 | var w0 = bulletWidth(x0), 53 | w1 = bulletWidth(x1); 54 | 55 | // Update the range rects. 56 | var range = wrap.selectAll("rect.range") 57 | .data(rangez); 58 | 59 | range.enter().append("rect") 60 | .attr("class", function(d, i) { return "range s" + i; }) 61 | .attr("width", w0) 62 | .attr("height", extentY) 63 | .attr("x", reverse ? x0 : 0) 64 | 65 | d3.transition(range) 66 | .attr("x", reverse ? x1 : 0) 67 | .attr("width", w1) 68 | .attr("height", extentY); 69 | 70 | // Update the measure rects. 71 | var measure = wrap.selectAll("rect.measure") 72 | .data(measurez); 73 | 74 | measure.enter().append("rect") 75 | .attr("class", function(d, i) { return "measure s" + i; }) 76 | .attr("width", w0) 77 | .attr("height", extentY / 3) 78 | .attr("x", reverse ? x0 : 0) 79 | .attr("y", extentY / 3); 80 | 81 | d3.transition(measure) 82 | .attr("width", w1) 83 | .attr("height", extentY / 3) 84 | .attr("x", reverse ? x1 : 0) 85 | .attr("y", extentY / 3); 86 | 87 | // Update the marker lines. 88 | var marker = wrap.selectAll("line.marker") 89 | .data(markerz); 90 | 91 | marker.enter().append("line") 92 | .attr("class", "marker") 93 | .attr("x1", x0) 94 | .attr("x2", x0) 95 | .attr("y1", extentY / 6) 96 | .attr("y2", extentY * 5 / 6); 97 | 98 | d3.transition(marker) 99 | .attr("x1", x1) 100 | .attr("x2", x1) 101 | .attr("y1", extentY / 6) 102 | .attr("y2", extentY * 5 / 6); 103 | 104 | var axis = g.selectAll("g.axis").data([0]); 105 | axis.enter().append("g").attr("class", "axis"); 106 | axis.attr("transform", vertical ? null : "translate(0," + extentY + ")") 107 | .call(xAxis.scale(x1)); 108 | }); 109 | d3.timer.flush(); 110 | } 111 | 112 | // left, right, top, bottom 113 | bullet.orient = function(_) { 114 | if (!arguments.length) return orient; 115 | orient = _ + ""; 116 | reverse = orient == "right" || orient == "bottom"; 117 | xAxis.orient((vertical = orient == "top" || orient == "bottom") ? "left" : "bottom"); 118 | return bullet; 119 | }; 120 | 121 | // ranges (bad, satisfactory, good) 122 | bullet.ranges = function(_) { 123 | if (!arguments.length) return ranges; 124 | ranges = _; 125 | return bullet; 126 | }; 127 | 128 | // markers (previous, goal) 129 | bullet.markers = function(_) { 130 | if (!arguments.length) return markers; 131 | markers = _; 132 | return bullet; 133 | }; 134 | 135 | // measures (actual, forecast) 136 | bullet.measures = function(_) { 137 | if (!arguments.length) return measures; 138 | measures = _; 139 | return bullet; 140 | }; 141 | 142 | bullet.width = function(_) { 143 | if (!arguments.length) return width; 144 | width = +_; 145 | return bullet; 146 | }; 147 | 148 | bullet.height = function(_) { 149 | if (!arguments.length) return height; 150 | height = +_; 151 | return bullet; 152 | }; 153 | 154 | return d3.rebind(bullet, xAxis, "tickFormat"); 155 | }; 156 | 157 | function bulletRanges(d) { 158 | return d.ranges; 159 | } 160 | 161 | function bulletMarkers(d) { 162 | return d.markers; 163 | } 164 | 165 | function bulletMeasures(d) { 166 | return d.measures; 167 | } 168 | 169 | function bulletWidth(x) { 170 | var x0 = x(0); 171 | return function(d) { 172 | return Math.abs(x(d) - x0); 173 | }; 174 | } 175 | 176 | })(); 177 | -------------------------------------------------------------------------------- /chernoff/README.md: -------------------------------------------------------------------------------- 1 | # Chernoff faces 2 | 3 | Demo: 4 | 5 | Implements [Chernoff faces](http://en.wikipedia.org/wiki/Chernoff_face) for d3. 6 | 7 | Exposes 8 parameters through functons to control the facial expression. 8 | * **face** shape of the face `{0..1}` 9 | * **hair** shape of the hair `{-1..1}` 10 | * **mouth** shape of the mouth `{-1..1}` 11 | * **noseh** height of the nose `{0..1}` 12 | * **nosew** width of the nose `{0..1}` 13 | * **eyeh** height of the eyes `{0..1}` 14 | * **eyew** width of the eyes `{0..1}` 15 | * **brow** slant of the brows `{-1..1}` 16 | -------------------------------------------------------------------------------- /chernoff/chernoff.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | function sign(num) { 3 | if(num > 0) { 4 | return 1; 5 | } else if(num < 0) { 6 | return -1; 7 | } else { 8 | return 0; 9 | } 10 | } 11 | 12 | // Implements Chernoff faces (http://en.wikipedia.org/wiki/Chernoff_face). 13 | // Exposes 8 parameters through functons to control the facial expression. 14 | // face -- shape of the face {0..1} 15 | // hair -- shape of the hair {-1..1} 16 | // mouth -- shape of the mouth {-1..1} 17 | // noseh -- height of the nose {0..1} 18 | // nosew -- width of the nose {0..1} 19 | // eyeh -- height of the eyes {0..1} 20 | // eyew -- width of the eyes {0..1} 21 | // brow -- slant of the brows {-1..1} 22 | function d3_chernoff() { 23 | var facef = 0.5, // 0 - 1 24 | hairf = 0, // -1 - 1 25 | mouthf = 0, // -1 - 1 26 | nosehf = 0.5, // 0 - 1 27 | nosewf = 0.5, // 0 - 1 28 | eyehf = 0.5, // 0 - 1 29 | eyewf = 0.5, // 0 - 1 30 | browf = 0, // -1 - 1 31 | 32 | line = d3.svg.line() 33 | .interpolate("cardinal-closed") 34 | .x(function(d) { return d.x; }) 35 | .y(function(d) { return d.y; }), 36 | bline = d3.svg.line() 37 | .interpolate("basis-closed") 38 | .x(function(d) { return d.x; }) 39 | .y(function(d) { return d.y; }); 40 | 41 | function chernoff(a) { 42 | if(a instanceof Array) { 43 | a.each(__chernoff); 44 | } else { 45 | d3.select(this).each(__chernoff); 46 | } 47 | } 48 | 49 | function __chernoff(d) { 50 | var ele = d3.select(this), 51 | facevar = (typeof(facef) === "function" ? facef(d) : facef) * 30, 52 | hairvar = (typeof(hairf) === "function" ? hairf(d) : hairf) * 80, 53 | mouthvar = (typeof(mouthf) === "function" ? mouthf(d) : mouthf) * 7, 54 | nosehvar = (typeof(nosehf) === "function" ? nosehf(d) : nosehf) * 10, 55 | nosewvar = (typeof(nosewf) === "function" ? nosewf(d) : nosewf) * 10, 56 | eyehvar = (typeof(eyehf) === "function" ? eyehf(d) : eyehf) * 10, 57 | eyewvar = (typeof(eyewf) === "function" ? eyewf(d) : eyewf) * 10, 58 | browvar = (typeof(browf) === "function" ? browf(d) : browf) * 3; 59 | 60 | var face = [{x: 70, y: 60}, {x: 120, y: 80}, 61 | {x: 120-facevar, y: 110}, {x: 120-facevar, y: 160}, 62 | {x: 20+facevar, y: 160}, {x: 20+facevar, y: 110}, 63 | {x: 20, y: 80}]; 64 | ele.selectAll("path.face").data([face]).enter() 65 | .append("path") 66 | .attr("class", "face") 67 | .attr("d", bline); 68 | 69 | var hair = [{x: 70, y: 60}, {x: 120, y: 80}, 70 | {x: 140, y: 45-hairvar}, {x: 120, y: 45}, 71 | {x: 70, y: 30}, {x: 20, y: 45}, 72 | {x: 0, y: 45-hairvar}, {x: 20, y: 80}]; 73 | ele.selectAll("path.hair").data([hair]).enter() 74 | .append("path") 75 | .attr("class", "hair") 76 | .attr("d", bline); 77 | 78 | var mouth = [{x: 70, y: 130+mouthvar}, 79 | {x: 110-facevar, y: 135-mouthvar}, 80 | {x: 70, y: 140+mouthvar}, 81 | {x: 30+facevar, y: 135-mouthvar}]; 82 | ele.selectAll("path.mouth").data([mouth]).enter() 83 | .append("path") 84 | .attr("class", "mouth") 85 | .attr("d", line); 86 | 87 | var nose = [{x: 70, y: 110-nosehvar}, 88 | {x: 70+nosewvar, y: 110+nosehvar}, 89 | {x: 70-nosewvar, y: 110+nosehvar}]; 90 | ele.selectAll("path.nose").data([nose]).enter() 91 | .append("path") 92 | .attr("class", "nose") 93 | .attr("d", line); 94 | 95 | var leye = [{x: 55, y: 90-eyehvar}, {x: 55+eyewvar, y: 90}, 96 | {x: 55, y: 90+eyehvar}, {x: 55-eyewvar, y: 90}]; 97 | var reye = [{x: 85, y: 90-eyehvar}, {x: 85+eyewvar, y: 90}, 98 | {x: 85, y: 90+eyehvar}, {x: 85-eyewvar, y: 90}]; 99 | ele.selectAll("path.leye").data([leye]).enter() 100 | .append("path") 101 | .attr("class", "leye") 102 | .attr("d", bline); 103 | ele.selectAll("path.reye").data([reye]).enter() 104 | .append("path") 105 | .attr("class", "reye") 106 | .attr("d", bline); 107 | 108 | ele.append("path") 109 | .attr("class", "lbrow") 110 | .attr("d", "M" + (55-eyewvar/1.7-sign(browvar)) + "," + 111 | (87-eyehvar+browvar) + " " + 112 | (55+eyewvar/1.7-sign(browvar)) + "," + 113 | (87-eyehvar-browvar)); 114 | ele.append("path") 115 | .attr("class", "rbrow") 116 | .attr("d", "M" + (85-eyewvar/1.7+sign(browvar)) + "," + 117 | (87-eyehvar-browvar) + " " + 118 | (85+eyewvar/1.7+sign(browvar)) + "," + 119 | (87-eyehvar+browvar)); 120 | } 121 | 122 | chernoff.face = function(x) { 123 | if(!arguments.length) return facef; 124 | facef = x; 125 | return chernoff; 126 | }; 127 | 128 | chernoff.hair = function(x) { 129 | if(!arguments.length) return hairf; 130 | hairf = x; 131 | return chernoff; 132 | }; 133 | 134 | chernoff.mouth = function(x) { 135 | if(!arguments.length) return mouthf; 136 | mouthf = x; 137 | return chernoff; 138 | }; 139 | 140 | chernoff.noseh = function(x) { 141 | if(!arguments.length) return nosehf; 142 | nosehf = x; 143 | return chernoff; 144 | }; 145 | 146 | chernoff.nosew = function(x) { 147 | if(!arguments.length) return nosewf; 148 | nosewf = x; 149 | return chernoff; 150 | }; 151 | 152 | chernoff.eyeh = function(x) { 153 | if(!arguments.length) return eyehf; 154 | eyehf = x; 155 | return chernoff; 156 | }; 157 | 158 | chernoff.eyew = function(x) { 159 | if(!arguments.length) return eyewf; 160 | eyewf = x; 161 | return chernoff; 162 | }; 163 | 164 | chernoff.brow = function(x) { 165 | if(!arguments.length) return browf; 166 | browf = x; 167 | return chernoff; 168 | }; 169 | 170 | return chernoff; 171 | } 172 | 173 | d3.chernoff = function() { 174 | return d3_chernoff(Object); 175 | }; 176 | })(); 177 | -------------------------------------------------------------------------------- /cubehelix/README.md: -------------------------------------------------------------------------------- 1 | An interpolator that implements Dave Green’s [cubehelix color scheme](http://www.mrao.cam.ac.uk/~dag/CUBEHELIX/). 2 | 3 | See [bl.ocks.org/11413789](http://bl.ocks.org/mbostock/11413789) and [bl.ocks.org/11415064](http://bl.ocks.org/mbostock/11415064) for examples. 4 | 5 | # d3.interpolateCubehelix(a, b) 6 | 7 | Returns a cubehelix interpolator between the two colors a and b, using a gamma correction value of 1. The two colors are typically specified in HSL color space. For example, the default cubehelix color scheme is: 8 | 9 | ```js 10 | var cubehelix = d3.interpolateCubehelix("hsl(300,50%,0%)", "hsl(-240,50%,100%)"); 11 | ``` 12 | 13 | The hue of color a determines the starting hue angle in degrees, while the hue of color b determines the ending hue angle in degrees; the hue angle is interpolated linearly for intermediate values. Likewise for saturation and lightness, which are specified in percentages. (The [d3.hsl](https://github.com/mbostock/d3/wiki/Colors#d3_hsl) constructor can also be used.) 14 | 15 | If either color a or b is not an HSL color, the colors are converted to HSL color space first. However, it is recommended that you specify each color’s hue, saturation and lightness explicitly: cubehelix interpolation does not actually use the HSL color space, and thus the terms hue, saturation and lightness here are similar but slightly different to their meaning in HSL color space. 16 | 17 | The return value of the interpolator is a hexadecimal RGB string. 18 | 19 | To convert from the original color scheme’s START, ROTS and HUE parameters, use the following equalities: 20 | 21 | * starting hue angle = (START - 1) * 120 22 | * ending hue angle = (START - 1) * 120 + ROTS * 360 23 | * saturation = HUE / 2 24 | 25 | For example, the default color scheme’s settings are: 26 | 27 | * START = 0.5 28 | * ROTS = -1.5 29 | * HUE = 1.0 30 | 31 | These correspond to the following start and end colors: 32 | 33 | * hsl(300, 50%, 0%) 34 | * hsl(-240, 50%, 100%) 35 | 36 | While the original cubehelix color scheme always uses the full range of lightness from 0% to 100%, note that it is possible in this interpolator to use a subset of this range by specifying start and end colors with different lightnesses. 37 | 38 | # d3.interpolateCubehelix.gamma(gamma) 39 | 40 | Returns a cubehelix interpolator factory with the specified gamma correction value. For example, to create the default cubehelix color scheme with a gamma correction value of 1.2: 41 | 42 | ```js 43 | var cubehelix = d3.interpolateCubehelix.gamma(1.2)("hsl(300,50%,0%)", "hsl(-240,50%,100%)"); 44 | ``` 45 | 46 | # d3.scale.cubehelix 47 | 48 | Constructs a new [linear scale](https://github.com/mbostock/d3/wiki/Quantitative-Scales) with the interpolator [d3.interpolateCubehelix](#interpolateCubehelix), the domain [0, 1] and the range [hsl(300°, 50%, 0%), hsl(-240°, 50%, 100%)]. This is merely a convenience function. 49 | -------------------------------------------------------------------------------- /cubehelix/cubehelix.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var radians = Math.PI / 180; 3 | 4 | d3.scale.cubehelix = function() { 5 | return d3.scale.linear() 6 | .range([d3.hsl(300, .5, 0), d3.hsl(-240, .5, 1)]) 7 | .interpolate(d3.interpolateCubehelix); 8 | }; 9 | 10 | d3.interpolateCubehelix = d3_interpolateCubehelix(1); 11 | d3.interpolateCubehelix.gamma = d3_interpolateCubehelix; 12 | 13 | function d3_interpolateCubehelix(γ) { 14 | return function(a, b) { 15 | a = d3.hsl(a); 16 | b = d3.hsl(b); 17 | 18 | var ah = (a.h + 120) * radians, 19 | bh = (b.h + 120) * radians - ah, 20 | as = a.s, 21 | bs = b.s - as, 22 | al = a.l, 23 | bl = b.l - al; 24 | 25 | if (isNaN(bs)) bs = 0, as = isNaN(as) ? b.s : as; 26 | if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; 27 | 28 | return function(t) { 29 | var h = ah + bh * t, 30 | l = Math.pow(al + bl * t, γ), 31 | a = (as + bs * t) * l * (1 - l), 32 | cosh = Math.cos(h), 33 | sinh = Math.sin(h); 34 | return "#" 35 | + hex(l + a * (-0.14861 * cosh + 1.78277 * sinh)) 36 | + hex(l + a * (-0.29227 * cosh - 0.90649 * sinh)) 37 | + hex(l + a * (+1.97294 * cosh)); 38 | }; 39 | }; 40 | } 41 | 42 | function hex(v) { 43 | var s = (v = v <= 0 ? 0 : v >= 1 ? 255 : v * 255 | 0).toString(16); 44 | return v < 0x10 ? "0" + s : s; 45 | } 46 | })(); 47 | -------------------------------------------------------------------------------- /fisheye/README.md: -------------------------------------------------------------------------------- 1 | # Fisheye Distortion 2 | 3 | Demo: 4 | 5 | Implements a fisheye distortion for two-dimensional layouts. Based on Sarkar and Brown’s [Graphical Fisheye Views of Graphs](http://dl.acm.org/citation.cfm?id=142763) (CHI '92), as well as [Flare](http://flare.prefuse.org/)'s [FisheyeDistortion](http://flare.prefuse.org/api/flare/vis/operator/distortion/FisheyeDistortion.html) and [Sigma.js](http://sigmajs.org/)'s [fisheye example](http://sigmajs.org/examples/a_plugin_example_advanced.html). 6 | 7 | When constructing a fisheye distortion, you can specify the radius and distortion factor: 8 | 9 | ```js 10 | var fisheye = d3.fisheye.circular() 11 | .radius(200) 12 | .distortion(2); 13 | ``` 14 | 15 | Typically, you then update the focal point of the distortion on mousemove: 16 | 17 | ```js 18 | svg.on("mousemove", function() { 19 | fisheye.focus(d3.mouse(this)); 20 | }); 21 | ``` 22 | 23 | The distortion operator takes as input an object with `x` and `y` attributes, and returns a new object with `x`, `y` and `z` attributes. The returned object represents the distorted position of the input object; the `z` property is a scaling factor so that you can optionally distort the size of elements as well. 24 | 25 | For example, to apply fisheye distortion to a force layout, stash the distorted positions in a `display` property on each node, and then use the distorted positions to update the nodes and links: 26 | 27 | ```js 28 | svg.on("mousemove", function() { 29 | fisheye.focus(d3.mouse(this)); 30 | 31 | node.each(function(d) { d.fisheye = fisheye(d); }) 32 | .attr("cx", function(d) { return d.fisheye.x; }) 33 | .attr("cy", function(d) { return d.fisheye.y; }) 34 | .attr("r", function(d) { return d.fisheye.z * 4.5; }); 35 | 36 | link.attr("x1", function(d) { return d.source.fisheye.x; }) 37 | .attr("y1", function(d) { return d.source.fisheye.y; }) 38 | .attr("x2", function(d) { return d.target.fisheye.x; }) 39 | .attr("y2", function(d) { return d.target.fisheye.y; }); 40 | }); 41 | ``` 42 | 43 | There's also a d3.fisheye.scale for Cartesian distortion; see the above demo for an example. 44 | -------------------------------------------------------------------------------- /fisheye/fisheye.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | d3.fisheye = { 3 | scale: function(scaleType) { 4 | return d3_fisheye_scale(scaleType(), 3, 0); 5 | }, 6 | circular: function() { 7 | var radius = 200, 8 | distortion = 2, 9 | k0, 10 | k1, 11 | focus = [0, 0]; 12 | 13 | function fisheye(d) { 14 | var dx = d.x - focus[0], 15 | dy = d.y - focus[1], 16 | dd = Math.sqrt(dx * dx + dy * dy); 17 | if (!dd || dd >= radius) return {x: d.x, y: d.y, z: dd >= radius ? 1 : 10}; 18 | var k = k0 * (1 - Math.exp(-dd * k1)) / dd * .75 + .25; 19 | return {x: focus[0] + dx * k, y: focus[1] + dy * k, z: Math.min(k, 10)}; 20 | } 21 | 22 | function rescale() { 23 | k0 = Math.exp(distortion); 24 | k0 = k0 / (k0 - 1) * radius; 25 | k1 = distortion / radius; 26 | return fisheye; 27 | } 28 | 29 | fisheye.radius = function(_) { 30 | if (!arguments.length) return radius; 31 | radius = +_; 32 | return rescale(); 33 | }; 34 | 35 | fisheye.distortion = function(_) { 36 | if (!arguments.length) return distortion; 37 | distortion = +_; 38 | return rescale(); 39 | }; 40 | 41 | fisheye.focus = function(_) { 42 | if (!arguments.length) return focus; 43 | focus = _; 44 | return fisheye; 45 | }; 46 | 47 | return rescale(); 48 | } 49 | }; 50 | 51 | function d3_fisheye_scale(scale, d, a) { 52 | 53 | function fisheye(_) { 54 | var x = scale(_), 55 | left = x < a, 56 | range = d3.extent(scale.range()), 57 | min = range[0], 58 | max = range[1], 59 | m = left ? a - min : max - a; 60 | if (m == 0) m = max - min; 61 | return (left ? -1 : 1) * m * (d + 1) / (d + (m / Math.abs(x - a))) + a; 62 | } 63 | 64 | fisheye.distortion = function(_) { 65 | if (!arguments.length) return d; 66 | d = +_; 67 | return fisheye; 68 | }; 69 | 70 | fisheye.focus = function(_) { 71 | if (!arguments.length) return a; 72 | a = +_; 73 | return fisheye; 74 | }; 75 | 76 | fisheye.copy = function() { 77 | return d3_fisheye_scale(scale.copy(), d, a); 78 | }; 79 | 80 | fisheye.nice = scale.nice; 81 | fisheye.ticks = scale.ticks; 82 | fisheye.tickFormat = scale.tickFormat; 83 | return d3.rebind(fisheye, scale, "domain", "range"); 84 | } 85 | })(); 86 | -------------------------------------------------------------------------------- /force_labels/README.md: -------------------------------------------------------------------------------- 1 | # D3 Force Labels v 0.1 2 | 3 | Demo: 4 | 5 | Generates an automatic and dynamic positioning for labels, using the d3 force layout. Once a```force_labels``` object has been created, simply call the bound function ```update``` with a selection of the objects you want to attach labels to as an argument. The force_labels object is a d3.force object which allows full control over the charge, gravity, theta etc. 6 | 7 | At each tick the following occurs: 8 | 9 | - Center of each object (anchor position) is determined by the SVG Bounding Box of that object and stored in object ```anchorPos``` under x,y 10 | - The position of the label element is determined by a force layout where anchors are fixed nodes and labels are floating. New position for each label is stored in object ```labelPos``` under x,y 11 | 12 | Both the ```anchorPos``` and ```labelPos``` are inserted in the ```__data__``` variable of the object being labeled. This allows easy access when drawing the labels and connectors. 13 | 14 | In the demo the label and link are created as svg objects on the same data selection as the anchors. As the position information is embedded in ```__data__``` the redraw function is simply: 15 | 16 | ```js 17 | function redrawLabels() { 18 | labelBox 19 | .attr("transform",function(d) { return "translate("+d.labelPos.x+" "+d.labelPos.y+")"}); 20 | 21 | links 22 | .attr("x1",function(d) { return d.anchorPos.x}) 23 | .attr("y1",function(d) { return d.anchorPos.y}) 24 | .attr("x2",function(d) { return d.labelPos.x}) 25 | .attr("y2",function(d) { return d.labelPos.y}); 26 | } 27 | ``` 28 | 29 | -------------------------------------------------------------------------------- /force_labels/force_labels.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | d3.force_labels = function force_labels() { 3 | var labels = d3.layout.force(); 4 | 5 | // Update the position of the anchor based on the center of bounding box 6 | function updateAnchor() { 7 | if (!labels.selection) return; 8 | labels.selection.each(function(d) { 9 | var bbox = this.getBBox(), 10 | x = bbox.x + bbox.width / 2, 11 | y = bbox.y + bbox.height / 2; 12 | 13 | d.anchorPos.x = x; 14 | d.anchorPos.y = y; 15 | 16 | // If a label position does not exist, set it to be the anchor position 17 | if (d.labelPos.x == null) { 18 | d.labelPos.x = x; 19 | d.labelPos.y = y; 20 | } 21 | }); 22 | } 23 | 24 | //The anchor position should be updated on each tick 25 | labels.on("tick.labels", updateAnchor); 26 | 27 | // This updates all nodes/links - retaining any previous labelPos on updated nodes 28 | labels.update = function(selection) { 29 | labels.selection = selection; 30 | var nodes = [], links = []; 31 | selection[0].forEach(function(d) { 32 | if(d && d.__data__) { 33 | var data = d.__data__; 34 | 35 | if (!d.labelPos) d.labelPos = {fixed: false}; 36 | if (!d.anchorPos) d.anchorPos = {fixed: true}; 37 | 38 | // Place position objects in __data__ to make them available through 39 | // d.labelPos/d.anchorPos for different elements 40 | data.labelPos = d.labelPos; 41 | data.anchorPos = d.anchorPos; 42 | 43 | links.push({target: d.anchorPos, source: d.labelPos}); 44 | nodes.push(d.anchorPos); 45 | nodes.push(d.labelPos); 46 | } 47 | }); 48 | labels 49 | .stop() 50 | .nodes(nodes) 51 | .links(links); 52 | updateAnchor(); 53 | labels.start(); 54 | }; 55 | return labels; 56 | }; 57 | })(); 58 | -------------------------------------------------------------------------------- /geo/polyhedron/README.md: -------------------------------------------------------------------------------- 1 | # Polyhedral Geographic Projections 2 | 3 | Examples: 4 | 5 | * [Gnomonic Butterfly Map](http://www.jasondavies.com/maps/gnomonic-butterfly/) 6 | * [Waterman Butterfly Map](http://www.jasondavies.com/maps/waterman-butterfly/) 7 | * [Collignon Butterfly Map](http://www.jasondavies.com/maps/collignon-butterfly/) 8 | -------------------------------------------------------------------------------- /geo/polyhedron/polyhedron.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | var ε = 1e-6, 4 | π = Math.PI, 5 | radians = π / 180, 6 | degrees = 180 / π; 7 | 8 | // Creates a polyhedral projection. 9 | // * root: a spanning tree of polygon faces. Nodes are automatically 10 | // augmented with a transform matrix. 11 | // * face: a function that returns the appropriate node for a given {λ, φ} 12 | // point (radians). 13 | // * r: rotation angle for final polyhedron net. Defaults to -π / 6 (for 14 | // butterflies). 15 | d3.geo.polyhedron = function(root, face, r) { 16 | 17 | r = r == null ? -π / 6 : r; // TODO automate 18 | 19 | recurse(root, {transform: [ 20 | Math.cos(r), Math.sin(r), 0, 21 | -Math.sin(r), Math.cos(r), 0 22 | ]}); 23 | 24 | function recurse(node, parent) { 25 | node.edges = faceEdges(node.face); 26 | if (parent) { 27 | // Find shared edge. 28 | if (parent.face) { 29 | var shared = node.shared = sharedEdge(node.face, parent.face), 30 | m = matrix(shared.map(parent.project), shared.map(node.project)); 31 | node.transform = parent.transform ? multiply(parent.transform, m) : m; 32 | // Replace shared edge in parent edges array. 33 | var edges = parent.edges; 34 | for (var i = 0, n = edges.length; i < n; ++i) { 35 | if (pointEqual(shared[0], edges[i][1]) && pointEqual(shared[1], edges[i][0])) edges[i] = node; 36 | if (pointEqual(shared[0], edges[i][0]) && pointEqual(shared[1], edges[i][1])) edges[i] = node; 37 | } 38 | var edges = node.edges; 39 | for (var i = 0, n = edges.length; i < n; ++i) { 40 | if (pointEqual(shared[0], edges[i][0]) && pointEqual(shared[1], edges[i][1])) edges[i] = parent; 41 | if (pointEqual(shared[0], edges[i][1]) && pointEqual(shared[1], edges[i][0])) edges[i] = parent; 42 | } 43 | } else { 44 | node.transform = parent.transform; 45 | } 46 | } 47 | if (node.children) { 48 | node.children.forEach(function(child) { 49 | recurse(child, node); 50 | }); 51 | } 52 | return node; 53 | } 54 | 55 | function forward(λ, φ) { 56 | var node = face(λ, φ), 57 | point = node.project([λ * degrees, φ * degrees]), 58 | t; 59 | if (t = node.transform) { 60 | return [ 61 | t[0] * point[0] + t[1] * point[1] + t[2], 62 | -(t[3] * point[0] + t[4] * point[1] + t[5]) 63 | ]; 64 | } 65 | point[1] = -point[1]; 66 | return point; 67 | } 68 | 69 | // Naive inverse! A faster solution would use bounding boxes, or even a 70 | // polygonal quadtree. 71 | if (hasInverse(root)) forward.invert = function(x, y) { 72 | var coordinates = faceInvert(root, [x, -y]); 73 | return coordinates && (coordinates[0] *= radians, coordinates[1] *= radians, coordinates); 74 | }; 75 | 76 | function faceInvert(node, coordinates) { 77 | var invert = node.project.invert, 78 | t = node.transform, 79 | point = coordinates; 80 | if (t) { 81 | t = inverseTransform(t); 82 | point = [ 83 | t[0] * point[0] + t[1] * point[1] + t[2], 84 | (t[3] * point[0] + t[4] * point[1] + t[5]) 85 | ]; 86 | } 87 | if (invert && node === faceDegrees(p = invert(point))) return p; 88 | var p, 89 | children = node.children; 90 | for (var i = 0, n = children && children.length; i < n; ++i) { 91 | if (p = faceInvert(children[i], coordinates)) return p; 92 | } 93 | } 94 | 95 | function faceDegrees(coordinates) { 96 | return face(coordinates[0] * radians, coordinates[1] * radians); 97 | } 98 | 99 | var projection = d3.geo.projection(forward), 100 | stream_ = projection.stream; 101 | 102 | projection.stream = function(stream) { 103 | var rotate = projection.rotate(), 104 | rotateStream = stream_(stream), 105 | sphereStream = (projection.rotate([0, 0]), stream_(stream)); 106 | projection.rotate(rotate); 107 | rotateStream.sphere = function() { 108 | sphereStream.polygonStart(); 109 | sphereStream.lineStart(); 110 | outline(sphereStream, root); 111 | sphereStream.lineEnd(); 112 | sphereStream.polygonEnd(); 113 | }; 114 | return rotateStream; 115 | }; 116 | 117 | return projection; 118 | }; 119 | 120 | d3.geo.polyhedron.butterfly = function(faceProjection) { 121 | 122 | faceProjection = faceProjection || function(face) { 123 | var centroid = d3.geo.centroid({type: "MultiPoint", coordinates: face}); 124 | return d3.geo.gnomonic().scale(1).translate([0, 0]).rotate([-centroid[0], -centroid[1]]); 125 | }; 126 | 127 | var faces = d3.geo.polyhedron.octahedron.map(function(face) { 128 | return {face: face, project: faceProjection(face)}; 129 | }); 130 | 131 | [-1, 0, 0, 1, 0, 1, 4, 5].forEach(function(d, i) { 132 | var node = faces[d]; 133 | node && (node.children || (node.children = [])).push(faces[i]); 134 | }); 135 | 136 | return d3.geo.polyhedron(faces[0], function(λ, φ) { 137 | return faces[ 138 | λ < -π / 2 ? φ < 0 ? 6 : 4 139 | : λ < 0 ? φ < 0 ? 2 : 0 140 | : λ < π / 2 ? φ < 0 ? 3 : 1 141 | : φ < 0 ? 7 : 5]; 142 | }); 143 | }; 144 | 145 | d3.geo.polyhedron.waterman = function(faceProjection) { 146 | 147 | faceProjection = faceProjection || function(face) { 148 | var centroid = face.length === 6 ? d3.geo.centroid({type: "MultiPoint", coordinates: face}) : face[0]; 149 | return d3.geo.gnomonic().scale(1).translate([0, 0]).rotate([-centroid[0], -centroid[1]]); 150 | }; 151 | 152 | var octahedron = d3.geo.polyhedron.octahedron; 153 | 154 | var w5 = octahedron.map(function(face) { 155 | var xyz = face.map(cartesian), 156 | n = xyz.length, 157 | a = xyz[n - 1], 158 | b, 159 | hexagon = []; 160 | for (var i = 0; i < n; ++i) { 161 | b = xyz[i]; 162 | hexagon.push(spherical([ 163 | a[0] * 0.9486832980505138 + b[0] * 0.31622776601683794, 164 | a[1] * 0.9486832980505138 + b[1] * 0.31622776601683794, 165 | a[2] * 0.9486832980505138 + b[2] * 0.31622776601683794 166 | ]), spherical([ 167 | b[0] * 0.9486832980505138 + a[0] * 0.31622776601683794, 168 | b[1] * 0.9486832980505138 + a[1] * 0.31622776601683794, 169 | b[2] * 0.9486832980505138 + a[2] * 0.31622776601683794 170 | ])); 171 | a = b; 172 | } 173 | return hexagon; 174 | }); 175 | 176 | var cornerNormals = []; 177 | 178 | var parents = [-1, 0, 0, 1, 0, 1, 4, 5]; 179 | 180 | w5.forEach(function(hexagon, j) { 181 | var face = octahedron[j], 182 | n = face.length, 183 | normals = cornerNormals[j] = []; 184 | for (var i = 0; i < n; ++i) { 185 | w5.push([ 186 | face[i], 187 | hexagon[(i * 2 + 2) % (2 * n)], 188 | hexagon[(i * 2 + 1) % (2 * n)] 189 | ]); 190 | parents.push(j); 191 | normals.push(cross( 192 | cartesian(hexagon[(i * 2 + 2) % (2 * n)]), 193 | cartesian(hexagon[(i * 2 + 1) % (2 * n)]) 194 | )); 195 | } 196 | }); 197 | 198 | var faces = w5.map(function(face) { 199 | return { 200 | project: faceProjection(face), 201 | face: face 202 | }; 203 | }); 204 | 205 | parents.forEach(function(d, i) { 206 | var parent = faces[d]; 207 | parent && (parent.children || (parent.children = [])).push(faces[i]); 208 | }); 209 | 210 | return d3.geo.polyhedron(faces[0], face).center([0, 45]); 211 | 212 | function face(λ, φ) { 213 | var cosφ = Math.cos(φ), 214 | p = [cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ)]; 215 | 216 | var hexagon = λ < -π / 2 ? φ < 0 ? 6 : 4 217 | : λ < 0 ? φ < 0 ? 2 : 0 218 | : λ < π / 2 ? φ < 0 ? 3 : 1 219 | : φ < 0 ? 7 : 5; 220 | 221 | var n = cornerNormals[hexagon]; 222 | 223 | return faces[ 224 | dot(n[0], p) < 0 ? 8 + 3 * hexagon 225 | : dot(n[1], p) < 0 ? 8 + 3 * hexagon + 1 226 | : dot(n[2], p) < 0 ? 8 + 3 * hexagon + 2 227 | : hexagon]; 228 | } 229 | }; 230 | 231 | function outline(stream, node, parent) { 232 | var point, 233 | edges = node.edges, 234 | n = edges.length, 235 | edge, 236 | multiPoint = {type: "MultiPoint", coordinates: node.face}, 237 | notPoles = node.face.filter(function(d) { return Math.abs(d[1]) !== 90; }), 238 | bounds = d3.geo.bounds({type: "MultiPoint", coordinates: notPoles}), 239 | inside = false, 240 | j = -1, 241 | dx = bounds[1][0] - bounds[0][0]; 242 | // TODO 243 | var centroid = dx === 180 || dx === 360 244 | ? [(bounds[0][0] + bounds[1][0]) / 2, (bounds[0][1] + bounds[1][1]) / 2] 245 | : d3.geo.centroid(multiPoint); 246 | // First find the shared edge… 247 | if (parent) while (++j < n) { 248 | if (edges[j] === parent) break; 249 | } 250 | ++j; 251 | for (var i = 0; i < n; ++i) { 252 | edge = edges[(i + j) % n]; 253 | if (Array.isArray(edge)) { 254 | if (!inside) { 255 | stream.point((point = d3.geo.interpolate(edge[0], centroid)(ε))[0], point[1]); 256 | inside = true; 257 | } 258 | stream.point((point = d3.geo.interpolate(edge[1], centroid)(ε))[0], point[1]); 259 | } else { 260 | inside = false; 261 | if (edge !== parent) outline(stream, edge, node); 262 | } 263 | } 264 | } 265 | 266 | // TODO generate on-the-fly to avoid external modification. 267 | var octahedron = [ 268 | [0, 90], 269 | [-90, 0], [0, 0], [90, 0], [180, 0], 270 | [0, -90] 271 | ]; 272 | 273 | d3.geo.polyhedron.octahedron = [ 274 | [0, 2, 1], 275 | [0, 3, 2], 276 | [5, 1, 2], 277 | [5, 2, 3], 278 | [0, 1, 4], 279 | [0, 4, 3], 280 | [5, 4, 1], 281 | [5, 3, 4] 282 | ].map(function(face) { 283 | return face.map(function(i) { 284 | return octahedron[i]; 285 | }); 286 | }); 287 | 288 | var φ1 = Math.atan(Math.SQRT1_2) * degrees; 289 | 290 | var cube = [ 291 | [0, φ1], [90, φ1], [180, φ1], [-90, φ1], 292 | [0, -φ1], [90, -φ1], [180, -φ1], [-90, -φ1] 293 | ]; 294 | 295 | d3.geo.polyhedron.cube = [ 296 | [0, 3, 2, 1], // N 297 | [0, 1, 5, 4], 298 | [1, 2, 6, 5], 299 | [2, 3, 7, 6], 300 | [3, 0, 4, 7], 301 | [4, 5, 6, 7] // S 302 | ].map(function(face) { 303 | return face.map(function(i) { 304 | return cube[i]; 305 | }); 306 | }); 307 | 308 | // Finds a shared edge given two clockwise polygons. 309 | function sharedEdge(a, b) { 310 | var x, y, n = a.length, found = null; 311 | for (var i = 0; i < n; ++i) { 312 | x = a[i]; 313 | for (var j = b.length; --j >= 0;) { 314 | y = b[j]; 315 | if (x[0] === y[0] && x[1] === y[1]) { 316 | if (found) return [found, x]; 317 | found = x; 318 | } 319 | } 320 | } 321 | } 322 | 323 | // Note: 6-element arrays are used to denote the 3x3 affine transform matrix: 324 | // [a, b, c, 325 | // d, e, f, 326 | // 0, 0, 1] - this redundant row is left out. 327 | 328 | // Transform matrix for [a0, a1] -> [b0, b1]. 329 | function matrix(a, b) { 330 | var u = subtract(a[1], a[0]), 331 | v = subtract(b[1], b[0]), 332 | φ = angle(u, v), 333 | s = length(u) / length(v); 334 | 335 | return multiply([ 336 | 1, 0, a[0][0], 337 | 0, 1, a[0][1] 338 | ], multiply([ 339 | s, 0, 0, 340 | 0, s, 0 341 | ], multiply([ 342 | Math.cos(φ), Math.sin(φ), 0, 343 | -Math.sin(φ), Math.cos(φ), 0 344 | ], [ 345 | 1, 0, -b[0][0], 346 | 0, 1, -b[0][1] 347 | ]))); 348 | } 349 | 350 | // Inverts a transform matrix. 351 | function inverseTransform(m) { 352 | var k = 1 / (m[0] * m[4] - m[1] * m[3]); 353 | return [ 354 | k * m[4], -k * m[1], k * (m[1] * m[5] - m[2] * m[4]), 355 | -k * m[3], k * m[0], k * (m[2] * m[3] - m[0] * m[5]) 356 | ]; 357 | } 358 | 359 | // Multiplies two 3x2 matrices. 360 | function multiply(a, b) { 361 | return [ 362 | a[0] * b[0] + a[1] * b[3], 363 | a[0] * b[1] + a[1] * b[4], 364 | a[0] * b[2] + a[1] * b[5] + a[2], 365 | a[3] * b[0] + a[4] * b[3], 366 | a[3] * b[1] + a[4] * b[4], 367 | a[3] * b[2] + a[4] * b[5] + a[5] 368 | ]; 369 | } 370 | 371 | // Subtracts 2D vectors. 372 | function subtract(a, b) { 373 | return [a[0] - b[0], a[1] - b[1]]; 374 | } 375 | 376 | // Magnitude of a 2D vector. 377 | function length(v) { 378 | return Math.sqrt(v[0] * v[0] + v[1] * v[1]); 379 | } 380 | 381 | // Angle between two 2D vectors. 382 | function angle(a, b) { 383 | return Math.atan2(a[0] * b[1] - a[1] * b[0], a[0] * b[0] + a[1] * b[1]); 384 | } 385 | 386 | function dot(a, b) { 387 | for (var i = 0, n = a.length, s = 0; i < n; ++i) s += a[i] * b[i]; 388 | return s; 389 | } 390 | 391 | function cross(a, b) { 392 | return [ 393 | a[1] * b[2] - a[2] * b[1], 394 | a[2] * b[0] - a[0] * b[2], 395 | a[0] * b[1] - a[1] * b[0] 396 | ]; 397 | } 398 | 399 | // Converts 3D Cartesian to spherical coordinates (degrees). 400 | function spherical(cartesian) { 401 | return [ 402 | Math.atan2(cartesian[1], cartesian[0]) * degrees, 403 | Math.asin(Math.max(-1, Math.min(1, cartesian[2]))) * degrees 404 | ]; 405 | } 406 | 407 | // Converts spherical coordinates (degrees) to 3D Cartesian. 408 | function cartesian(coordinates) { 409 | var λ = coordinates[0] * radians, 410 | φ = coordinates[1] * radians, 411 | cosφ = Math.cos(φ); 412 | return [ 413 | cosφ * Math.cos(λ), 414 | cosφ * Math.sin(λ), 415 | Math.sin(φ) 416 | ]; 417 | } 418 | 419 | // Tests equality of two spherical points. 420 | function pointEqual(a, b) { 421 | return a && b && a[0] === b[0] && a[1] === b[1]; 422 | } 423 | 424 | // Converts an array of n face vertices to an array of n + 1 edges. 425 | function faceEdges(face) { 426 | var n = face.length, 427 | edges = []; 428 | for (var a = face[n - 1], i = 0; i < n; ++i) edges.push([a, a = face[i]]); 429 | return edges; 430 | } 431 | 432 | function hasInverse(node) { 433 | return node.project.invert || node.children && node.children.some(hasInverse); 434 | } 435 | 436 | })(); 437 | -------------------------------------------------------------------------------- /geo/projection/README.md: -------------------------------------------------------------------------------- 1 | Moved to [d3-geo-projection](https://github.com/d3/d3-geo-projection/). 2 | -------------------------------------------------------------------------------- /geo/tile/README.md: -------------------------------------------------------------------------------- 1 | # Zoomable Tiles 2 | 3 | * Panning & Zooming: 4 | * Clipping: 5 | * Vector Tiles: 6 | * Raster Tiles & Vector Overlay: 7 | 8 | A layout for determining which 256x256 quadtree tiles to display in a rectangular viewport, based on a scale and translate. This layout can be used to create a simple slippy map, or render standard map tiles (e.g., MapBox, CloudMade) as a base layer behind a geographic projection. 9 | -------------------------------------------------------------------------------- /geo/tile/tile.js: -------------------------------------------------------------------------------- 1 | d3.geo.tile = function() { 2 | var size = [960, 500], 3 | scale = 256, 4 | translate = [size[0] / 2, size[1] / 2], 5 | zoomDelta = 0; 6 | 7 | function tile() { 8 | var z = Math.max(Math.log(scale) / Math.LN2 - 8, 0), 9 | z0 = Math.round(z + zoomDelta), 10 | k = Math.pow(2, z - z0 + 8), 11 | origin = [(translate[0] - scale / 2) / k, (translate[1] - scale / 2) / k], 12 | tiles = [], 13 | cols = d3.range(Math.max(0, Math.floor(-origin[0])), Math.max(0, Math.ceil(size[0] / k - origin[0]))), 14 | rows = d3.range(Math.max(0, Math.floor(-origin[1])), Math.max(0, Math.ceil(size[1] / k - origin[1]))); 15 | 16 | rows.forEach(function(y) { 17 | cols.forEach(function(x) { 18 | tiles.push([x, y, z0]); 19 | }); 20 | }); 21 | 22 | tiles.translate = origin; 23 | tiles.scale = k; 24 | 25 | return tiles; 26 | } 27 | 28 | tile.size = function(_) { 29 | if (!arguments.length) return size; 30 | size = _; 31 | return tile; 32 | }; 33 | 34 | tile.scale = function(_) { 35 | if (!arguments.length) return scale; 36 | scale = _; 37 | return tile; 38 | }; 39 | 40 | tile.translate = function(_) { 41 | if (!arguments.length) return translate; 42 | translate = _; 43 | return tile; 44 | }; 45 | 46 | tile.zoomDelta = function(_) { 47 | if (!arguments.length) return zoomDelta; 48 | zoomDelta = +_; 49 | return tile; 50 | }; 51 | 52 | return tile; 53 | }; 54 | -------------------------------------------------------------------------------- /geodesic/README.md: -------------------------------------------------------------------------------- 1 | # Geodesic Grid 2 | 3 | Example: 4 | -------------------------------------------------------------------------------- /geodesic/geodesic.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var φ = 1.618033988749895, 3 | ρ = 180 / Math.PI; 4 | 5 | var vertices = [ 6 | [1,φ,0], [-1,φ,0], [1,-φ,0], [-1,-φ,0], 7 | [0,1,φ], [0,-1,φ], [0,1,-φ], [0,-1,-φ], 8 | [φ,0,1], [-φ,0,1], [φ,0,-1], [-φ,0,-1] 9 | ]; 10 | 11 | var faces = [ 12 | [0,1,4], [1,9,4], [4,9,5], [5,9,3], [2,3,7], 13 | [3,2,5], [7,10,2], [0,8,10], [0,4,8], [8,2,10], 14 | [8,4,5], [8,5,2], [1,0,6], [11,1,6], [3,9,11], 15 | [6,10,7], [3,11,7], [11,6,7], [6,0,10], [9,1,11] 16 | ].map(function(face) { 17 | return face.map(function(i) { 18 | return vertices[i]; 19 | }); 20 | }); 21 | 22 | d3.geodesic = { 23 | multipolygon: function(n) { 24 | return { 25 | type: "MultiPolygon", 26 | coordinates: subdivideFaces(~~n).map(function(face) { 27 | face = face.map(project); 28 | face.push(face[0]); 29 | face = [face]; 30 | return face; 31 | }) 32 | }; 33 | }, 34 | polygons: function(n) { 35 | return d3.geodesic.multipolygon(~~n).coordinates.map(function(face) { 36 | return {type: "Polygon", coordinates: face}; 37 | }); 38 | }, 39 | multilinestring: function(n) { 40 | return { 41 | type: "MultiLineString", 42 | coordinates: subdivideEdges(~~n).map(function(edge) { 43 | return edge.map(project); 44 | }) 45 | }; 46 | } 47 | }; 48 | 49 | function subdivideFaces(n) { 50 | return d3.merge(faces.map(function(face) { 51 | var i01 = interpolate(face[0], face[1]), 52 | i02 = interpolate(face[0], face[2]), 53 | faces = []; 54 | 55 | faces.push([ 56 | face[0], 57 | i01(1 / n), 58 | i02(1 / n) 59 | ]); 60 | 61 | for (var i = 1; i < n; ++i) { 62 | var i1 = interpolate(i01(i / n), i02(i / n)), 63 | i2 = interpolate(i01((i + 1) / n), i02((i + 1) / n)); 64 | for (var j = 0; j <= i; ++j) { 65 | faces.push([ 66 | i1(j / i), 67 | i2(j / (i + 1)), 68 | i2((j + 1) / (i + 1)) 69 | ]); 70 | } 71 | for (var j = 0; j < i; ++j) { 72 | faces.push([ 73 | i1(j / i), 74 | i2((j + 1) / (i + 1)), 75 | i1((j + 1) / i) 76 | ]); 77 | } 78 | } 79 | 80 | return faces; 81 | })); 82 | } 83 | 84 | function subdivideEdges(n) { 85 | var edges = {}; 86 | 87 | subdivideFaces(n).forEach(function(face) { 88 | add(face[0], face[1]); 89 | add(face[1], face[2]); 90 | add(face[2], face[0]); 91 | }); 92 | 93 | function add(p0, p1) { 94 | var t; 95 | if (p0[0] < p1[0] || (p0[0] == p1[0] && (p0[1] < p1[1] || (p0[1] == p1[1] && p0[2] < p1[2])))) t = p0, p0 = p1, p1 = t; 96 | edges[p0.map(round) + " " + p1.map(round)] = [p0, p1]; 97 | } 98 | 99 | function round(d) { 100 | return d3.round(d, 4); 101 | } 102 | 103 | return d3.values(edges); 104 | } 105 | 106 | function interpolate(p0, p1) { 107 | var x0 = p0[0], 108 | y0 = p0[1], 109 | z0 = p0[2], 110 | x1 = p1[0] - x0, 111 | y1 = p1[1] - y0, 112 | z1 = p1[2] - z0; 113 | return function(t) { 114 | return [ 115 | x0 + t * x1, 116 | y0 + t * y1, 117 | z0 + t * z1 118 | ]; 119 | }; 120 | } 121 | 122 | function project(p) { 123 | var x = p[0], 124 | y = p[1], 125 | z = p[2]; 126 | return [ 127 | Math.atan2(y, x) * ρ, 128 | Math.acos(z / Math.sqrt(x * x + y * y + z * z)) * ρ - 90 129 | ]; 130 | } 131 | })(); 132 | -------------------------------------------------------------------------------- /geom/contour/README.md: -------------------------------------------------------------------------------- 1 | # Contour Plots 2 | 3 | Demo: 4 | 5 | Computes a contour for a given *grid function* using the [marching squares](http://en.wikipedia.org/wiki/Marching_squares) algorithm. Returns the contour polygon as an array of points. The grid function takes two arguments, *x* and *y*, and returns true if the specified point is inside the contour, and false for points outside the contour. The contour plugin can also take an optional starting point [x, y] on the grid. 6 | -------------------------------------------------------------------------------- /geom/contour/contour.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | d3.geom.contour = function(grid, start) { 4 | var s = start || d3_geom_contourStart(grid), // starting point 5 | c = [], // contour polygon 6 | x = s[0], // current x position 7 | y = s[1], // current y position 8 | dx = 0, // next x direction 9 | dy = 0, // next y direction 10 | pdx = NaN, // previous x direction 11 | pdy = NaN, // previous y direction 12 | i = 0; 13 | 14 | do { 15 | // determine marching squares index 16 | i = 0; 17 | if (grid(x-1, y-1)) i += 1; 18 | if (grid(x, y-1)) i += 2; 19 | if (grid(x-1, y )) i += 4; 20 | if (grid(x, y )) i += 8; 21 | 22 | // determine next direction 23 | if (i === 6) { 24 | dx = pdy === -1 ? -1 : 1; 25 | dy = 0; 26 | } else if (i === 9) { 27 | dx = 0; 28 | dy = pdx === 1 ? -1 : 1; 29 | } else { 30 | dx = d3_geom_contourDx[i]; 31 | dy = d3_geom_contourDy[i]; 32 | } 33 | 34 | // update contour polygon 35 | if (dx != pdx && dy != pdy) { 36 | c.push([x, y]); 37 | pdx = dx; 38 | pdy = dy; 39 | } 40 | 41 | x += dx; 42 | y += dy; 43 | } while (s[0] != x || s[1] != y); 44 | 45 | return c; 46 | }; 47 | 48 | // lookup tables for marching directions 49 | var d3_geom_contourDx = [1, 0, 1, 1,-1, 0,-1, 1,0, 0,0,0,-1, 0,-1,NaN], 50 | d3_geom_contourDy = [0,-1, 0, 0, 0,-1, 0, 0,1,-1,1,1, 0,-1, 0,NaN]; 51 | 52 | function d3_geom_contourStart(grid) { 53 | var x = 0, 54 | y = 0; 55 | 56 | // search for a starting point; begin at origin 57 | // and proceed along outward-expanding diagonals 58 | while (true) { 59 | if (grid(x,y)) { 60 | return [x,y]; 61 | } 62 | if (x === 0) { 63 | x = y + 1; 64 | y = 0; 65 | } else { 66 | x = x - 1; 67 | y = y + 1; 68 | } 69 | } 70 | } 71 | 72 | })(); 73 | -------------------------------------------------------------------------------- /graph/README.md: -------------------------------------------------------------------------------- 1 | # d3.graph 2 | 3 | This plugin is not currently in active development. 4 | 5 | For a more complete set of graph analysis tools, see the work of [Elijah Meeks and Maya Krishnan](http://dhs.stanford.edu/dh/networks/). 6 | 7 | A plugin for manipulating [graph data structures](http://opendatastructures.org/ods-cpp/12_Graphs.html). 8 | 9 | 10 | ## Todo 11 | 12 | * link nodes by name. see this [discussion on the force layout](https://groups.google.com/forum/?fromgroups#!topic/d3-js/LWuhBeEipz4) 13 | 14 | * adjacency list 15 | * graph traversal 16 | * hypergraph 17 | 18 | ## Matrix 19 | 20 | Create a matrix 21 | 22 | ```js 23 | var matrix = d3.graph.matrix([ 24 | [1,1,0], 25 | [0,0,0], 26 | [2,1,1] 27 | ]); 28 | ``` 29 | 30 | Get an edge value 31 | 32 | ```js 33 | matrix(i,j) 34 | ``` 35 | 36 | Check if edge exists (non-zero) 37 | 38 | ```js 39 | matrix.has(i,j) 40 | ``` 41 | 42 | Set an edge value 43 | 44 | ```js 45 | matrix.set(i,j,value) 46 | ``` 47 | 48 | Remove an edge 49 | 50 | ```js 51 | matrix.remove(i,j) 52 | ``` 53 | 54 | Get outgoing edge indices 55 | 56 | ```js 57 | matrix.outE(i) 58 | ``` 59 | 60 | Get incoming edge indices 61 | 62 | ```js 63 | matrix.inE(i) 64 | ``` 65 | 66 | ### todo 67 | 68 | * documentation 69 | * changing size 70 | 71 | ## Basics 72 | 73 | Convert [matrix](http://en.wikipedia.org/wiki/Adjacency_matrix) to [list](http://opendatastructures.org/ods-cpp/12_2_Graph_as_Collection_Li.html) 74 | 75 | ```js 76 | d3.graph.matrixToList([ 77 | [0,1,0], 78 | [1,0,0], 79 | [1,1,1] 80 | ]) 81 | /* 82 | [ 83 | {"source":0,"target":0,"value":0},{"source":0,"target":1,"value":1},{"source":0,"target":2,"value":0}, 84 | {"source":1,"target":0,"value":1},{"source":1,"target":1,"value":0},{"source":1,"target":2,"value":0}, 85 | {"source":2,"target":0,"value":1},{"source":2,"target":1,"value":1},{"source":2,"target":2,"value":1} 86 | ] 87 | */ 88 | ``` 89 | 90 | Convert list to matrix 91 | 92 | ```js 93 | d3.graph.listToMatrix([ 94 | {"source":0,"target":1,"value":1}, 95 | {"source":1,"target":0,"value":1}, 96 | {"source":2,"target":0,"value":1}, 97 | {"source":2,"target":1,"value":1}, 98 | {"source":2,"target":2,"value":1} 99 | ]) 100 | /* 101 | [0,1,0], 102 | [1,0,0], 103 | [1,1,1] 104 | */ 105 | ``` 106 | 107 | ## Stateful Use 108 | 109 | ```js 110 | var graph = d3.graph(); 111 | ``` 112 | 113 | Load a matrix 114 | 115 | ```js 116 | graph.matrix([ 117 | [0,1,0], 118 | [1,0,0], 119 | [1,1,1] 120 | ]); 121 | ``` 122 | 123 | Get the graph as list of links 124 | 125 | ```js 126 | graph.links(); 127 | ``` 128 | 129 | Get the graph as matrix 130 | 131 | ```js 132 | graph.matrix(); 133 | ``` 134 | 135 | Nodes and links can be modified by passing in a value. This will overwrite existing data. 136 | 137 | ```js 138 | graph.nodes(['red', 'purple', 'orange']); 139 | graph.links([{"source":0,"target":0,"value":0},{"source":0,"target":1,"value":1},{"source":0,"target":2,"value":0}]); 140 | ``` 141 | 142 | Get a description of the graph 143 | 144 | ```js 145 | graph.description(); 146 | // "d3.graph with 3 nodes and 9 links" 147 | ``` 148 | -------------------------------------------------------------------------------- /graph/data/cities-matrix.json: -------------------------------------------------------------------------------- 1 | [ 2 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.00020615580539426687,0], 3 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], 4 | [0,0,0.006697839492217511,0.004195012544212019,0.002392362950985978,0.004166244937465728,0.0010882916599248707,0.005024621548810736,0,0,0,0.0002972456999111755,0,0,0,0,0.000215743502273761,0.0014382900151584067,0.001462270547626666,0.0035477639729594983,0.0006808032522768457,0,0.0005081976117990942,0.01030756446432287,0,0.005532683677375547,0.0009492858615495386,0.00085819596703263,0,0,0.0003547763972959499,0.0014238836312462134,0.007378416939103883,0.012498779973475276,0], 5 | [0,0,0.0028573865721265874,0.0010307564464322868,0.0010211822978762211,0.0013424130463634651,0.0003979278074153877,0.0014190965569681805,0,0,0,0.00024451110902005296,0,0,0,0,0.00012465360775685237,0.0012225555451002646,0.0006280551130622947,0.0019129329459331762,0.0003451887004164557,0,0.00033560100353696156,0.005187201429951476,0,0.002296485982191037,0.00028765800303168134,0.00023012730564690698,0,0,0,0.0004890041536088679,0.0029484764666434958,0.005038621483020077,0], 6 | [0,0,0.0025170075265272115,0.0011985750126320056,0.0008677701155886958,0.0012656843746806552,0.00016780050176848078,0.0014814188447387971,0,0,0,0.00017738819864797494,0,0,0,0,0,0.0007671060725157214,0.0005177717603551599,0.0011793815544417793,0.00034039259392280386,0,0.0002349188960327493,0.004415534088548194,0.00016300439527482897,0.0017019855501530668,0.00025889039628538944,0.00020135969890061506,0,0,0,0.0004027193978012301,0.0024594723130346276,0.004295676587284994,0], 7 | [0,0,0.005197136867132299,0.002205396087674128,0.0014382900151584067,0.0024067241738200767,0.0006520356455305537,0.0024978140683369855,0,0,0,0.00025889039628538944,0,0,0,0,0.0002109473957801092,0.00111227219239313,0.0009876276168518964,0.0027567225290536126,0.00044107470142701615,0,0.00045066239830651034,0.00796822061902001,0,0.0033032618961550647,0.000512984686077127,0.00047943000505280223,0,0,0.00018697589552746908,0.0011170592666711627,0.00550378058739497,0.009631954736026908,0], 8 | [0,0,0.001840991348528399,0.0006855903265548785,0.00029244959341752363,0.0008006607535400462,0.00018697589552746908,0.0006951644751109444,0,0,0,0,0,0,0,0,0,0.0002780703061521872,0.00020615580539426687,0.0005945004320379699,0.00017738819864797494,0,0,0.0013903289502218887,0,0.000834215434564371,0.00020615580539426687,0,0,0,0,0.00020615580539426687,0.0012465360775685236,0.0020327904471963767,0], 9 | [0,0,0.005403071383243901,0.0021478608741815446,0.0012656843746806552,0.002114261032079125,0.0005992875063160028,0.00256496859146373,0,0,0,0.00022533119915325517,0,0,0,0,0.00016300439527482897,0.0010930787342029037,0.0007910414439058858,0.002512220452249179,0.0004650687822187036,0,0.00032601330665746743,0.00648648564673455,0,0.0030156309897702398,0.0004698558564967364,0.0003499848069101075,0,0,0.00013424130463634654,0.0009205182548032468,0.004506623983065103,0.007757318384317995,0], 10 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], 11 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], 12 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.00022053960876741282,0,0,0,0.00033560100353696156,0,0.00024451110902005296,0,0,0,0,0,0,0.00018697589552746908,0.00013903289502218886,0], 13 | [0,0,0.0004218993076680279,0.00024930269940589525,0.00015821280488898665,0.00024451110902005296,0,0.00026368650277904123,0,0,0,0,0,0,0,0,0,0.00042669089805387017,0.0003451887004164557,0.0007958736792620133,0.0002109473957801092,0,0,0.0010643111274566117,0,0.0005848811224038094,0.00016780050176848078,0,0,0,0,0.00011985750126320056,0.0008054478278180791,0.0005753069738477438,0], 14 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], 15 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.00016780050176848078,0,0,0,0,0,0,0,0,0,0,0], 16 | [0,0,0.00016780050176848078,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.00015821280488898665,0,0,0,0.00037874789754858995,0,0.00014382900151584067,0,0,0,0,0,0,0.00015821280488898665,0.00016780050176848078,0], 17 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.00012465360775685237,0,0,0,0.0001965681085147727,0,0,0,0,0,0,0,0,0.00011506139476954876,0.00015341669839533483,0], 18 | [0,0,0.00018697589552746908,0.00022533119915325517,0.00014382900151584067,0.00023971500252640112,0,0.00022533119915325517,0,0,0,0.00011985750126320056,0,0,0,0,0.00020615580539426687,0.0006663968683646524,0.00032601330665746743,0.0013567742691975636,0.00027327419965853536,0,0.00024930269940589525,0.0019512747012355339,0,0.0008485766573984697,0,0,0,0,0,0,0.0008869635737789219,0.0008821313384227944,0], 19 | [0,0,0.0015245928353972823,0.0013136454396171734,0.000757486762881561,0.000973221232939703,0.0002109473957801092,0.0011890008640759399,0,0,0,0.0004362785949333643,0,0,0,0,0.0006040745805940356,0.0021430286388254166,0.0017067726244310996,0.004909460799669379,0.0010403757560664474,0,0.000915731180525214,0.011065367354751094,0.00042669089805387017,0.0037683306783737685,0.0005848811224038094,0.0004027193978012301,0.00012465360775685237,0,0.0002972456999111755,0.00047943000505280223,0.004708042391367241,0.004257334831982636,0], 20 | [0,0,0.001586915123167899,0.000915731180525214,0.0006088616548720685,0.0009636470843836373,0.0002349188960327493,0.001025969372154254,0,0,0,0.0002972456999111755,0,0,0,0,0.00018218430514162676,0.0012704714489586882,0.0007287191561352691,0.0034087581745841666,0.0005753069738477438,0,0.000431487004547522,0.00489004153608868,0,0.002186202629483902,0.0003451887004164557,0.0003308048970433097,0.00011985750126320056,0,0.00014382900151584067,0.0005225588346331928,0.002914921785619171,0.0035286156758473674,0], 21 | [0,0,0.0037155825391592175,0.0030060116801360797,0.0014766317704607644,0.0025313687493613104,0.0004842170793308351,0.002454685238756595,0,0,0.0002780703061521872,0.0009492858615495386,0,0.00012944519814269472,0.00012465360775685237,0.00014862510800949248,0.0012273426193782975,0.00512984686077127,0.00372036961343725,0.008298799710672846,0.0025793298142978284,0,0.002775915987243839,0.019714165420657476,0.00029244959341752363,0.00735944928630413,0.000810234902096112,0.0006232680387842618,0.00027327419965853536,0,0.0003451887004164557,0.0010691433628127393,0.008063962104580668,0.009665373933816949,0], 22 | [0,0,0.0009253053290812796,0.0005800940481257767,0.0003883355944280841,0.0005225588346331928,0.00011985750126320056,0.0006616097940866194,0,0,0,0.00026368650277904123,0,0,0,0,0.00020615580539426687,0.0013136454396171734,0.0007766802210717871,0.0026464391763464778,0.00025889039628538944,0,0.00044107470142701615,0.003753924294461575,0,0.0016012763460019977,0.00023012730564690698,0.00011985750126320056,0,0,0.00014382900151584067,0.0003308048970433097,0.001836204274250366,0.002732741996585354,0], 23 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.00014862510800949248,0,0,0,0,0,0,0,0,0,0,0], 24 | [0,0,0.0009253053290812796,0.0006903774008329114,0.0005034105375210612,0.0006424163358963933,0,0.0007047386236670101,0,0,0,0.0001917720020211209,0,0,0,0,0.0003835440040422418,0.001318432513895206,0.0007095708590231376,0.003749137220183542,0.0006040745805940356,0,0.0006328421873403276,0.006194293471462165,0.00012465360775685237,0.001999235766172052,0.00018218430514162676,0,0,0,0,0.0003883355944280841,0.002905347637063105,0.003955297541685618,0], 25 | [0.00011506139476954876,0,0.013304001995902884,0.008270348231473216,0.006213261124261919,0.007143579333011705,0.0013663935788317241,0.008687184982286834,0,0,0.0005561586767356122,0.0015293799096753153,0,0.0003451887004164557,0.0003691602006690958,0.00031162950328432145,0.002425917632010303,0.011558977938325615,0.0062277126692522064,0.022595442203096134,0.003801885359398093,0.00015821280488898665,0.006654484857246647,0.04282760518949924,0.00044587080792066793,0.01687127555459927,0.0016540244852165488,0.0009972017654079621,0.00026368650277904123,0,0.0007910414439058858,0.003394351790671973,0.022125766990911774,0.03680085931776823,0], 26 | [0,0,0,0,0.00022533119915325517,0,0,0,0,0,0,0,0,0,0,0,0,0.00029244959341752363,0,0.0003835440040422418,0,0,0,0.0006855903265548785,0,0.00020135969890061506,0,0,0,0,0,0,0.0002972456999111755,0.00034039259392280386,0], 27 | [0,0,0.006098551985901509,0.0033080489704330977,0.0017307531568993588,0.0026464391763464778,0.0006663968683646524,0.003399138864950006,0,0,0.00023971500252640112,0.0006424163358963933,0,0,0.00015821280488898665,0,0.0006951644751109444,0.0038881881796369684,0.0027854901357999046,0.007671060725157215,0.0014287158666023408,0,0.001586915123167899,0.016439535648014418,0.00016780050176848078,0.006390744161173892,0.0009396665519153783,0.000810234902096112,0.0001965681085147727,0,0.0006136487291501015,0.001591702197445932,0.008231961315092764,0.010662530538146817,0], 28 | [0,0,0.0010643111274566117,0.00044587080792066793,0.0002540988058995471,0.0005897133577599371,0.00012944519814269472,0.0005369652185453861,0,0,0,0.00015341669839533483,0,0,0,0,0,0.0005992875063160028,0.0004890041536088679,0.0009013247966130207,0.0002540988058995471,0,0.00014382900151584067,0.001586915123167899,0,0.0009540729358275716,0.000215743502273761,0.00023012730564690698,0,0,0,0.00022533119915325517,0.0014335029408803737,0.001351987194919531,0], 29 | [0,0,0.0006808032522768457,0.0003883355944280841,0.00015821280488898665,0.00025889039628538944,0,0.0002349188960327493,0,0,0,0,0,0,0,0,0,0.0002828618965380295,0.0001965681085147727,0.0005800940481257767,0,0,0,0.0008006607535400462,0,0.0005369652185453861,0.00016780050176848078,0.00013424130463634654,0,0,0,0.00012465360775685237,0.0007143579333011704,0.0006424163358963933,0], 30 | [0,0,0.00011985750126320056,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.00011985750126320056,0.00014382900151584067,0.00022053960876741282,0,0,0,0.00023012730564690698,0,0.0001917720020211209,0,0,0,0,0,0,0.00015821280488898665,0.0001725966082621326,0], 31 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.00014382900151584067,0], 32 | [0,0,0.00027327419965853536,0.00016300439527482897,0,0.00020135969890061506,0,0.00011985750126320056,0,0,0,0,0,0,0,0,0,0.0002109473957801092,0.00022053960876741282,0.00033560100353696156,0,0,0,0.0003308048970433097,0,0.00041230709468072425,0,0,0,0,0,0,0.0002780703061521872,0.0003308048970433097,0], 33 | [0,0,0.0011458268734174546,0.00044107470142701615,0.0002828618965380295,0.0005945004320379699,0,0.0007622738371595939,0,0,0,0,0,0,0,0,0.00016780050176848078,0.00041710320117437603,0.0002780703061521872,0.0005897133577599371,0.00016300439527482897,0,0.00020135969890061506,0.0020423645957524422,0,0.0008965377223349877,0.00012944519814269472,0,0,0,0,0.00016300439527482897,0.0007383384657694295,0.001160233257329648,0], 34 | [0,0,0.004760880852737981,0.0028286189653802954,0.001860184806718625,0.0029101347113411383,0.0005081976117990942,0.002804638432912036,0,0,0.00012944519814269472,0.0005848811224038094,0,0,0.00014862510800949248,0,0.0007479126143254953,0.0034662933880767505,0.0023108472050251353,0.005192169148541887,0.0013040261299830129,0,0.0012656843746806552,0.012053040132681082,0.00013903289502218886,0.005940036601789287,0.0007239320818572362,0.0006040745805940356,0.00014382900151584067,0,0.0002972456999111755,0.000915731180525214,0.005911585122589658,0.006841451720558497,0], 35 | [0.00015341669839533483,0,0.01238858694292433,0.006769645606388004,0.0045499786180359676,0.008998796421139918,0.0015821280488898663,0.007795705300698447,0,0,0.00032601330665746743,0.000810234902096112,0,0,0.0004698558564967364,0.0002828618965380295,0.0008533637316765025,0.005211588412122586,0.0031210821071212475,0.009995953025469786,0.002536200984717438,0,0.0029293281695313643,0.028060835874110648,0.00022533119915325517,0.010782207395097639,0.0014814188447387971,0.0006472485712525209,0.00026368650277904123,0,0.0004746429307747693,0.0018458235838845266,0.010811110485078214,0.014119159455511312,0], 36 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] 37 | ] 38 | -------------------------------------------------------------------------------- /graph/data/cities.csv: -------------------------------------------------------------------------------- 1 | name,latitude,longitude,population,color 2 | Excelsior,37.7244,-122.421,0.083884,#E41A1C 3 | Crocker Amazon,37.7107,-122.4372,0.058439,#FFFF33 4 | Marina,37.8021,-122.4369,0.40364,#FF7F00 5 | Nob Hill,37.793,-122.416,0.33259,#FF7F00 6 | North Beach,37.8045,-122.4076,0.28784,#FF7F00 7 | Pacific Heights,37.7924,-122.4352,0.33382,#FF7F00 8 | Presidio Heights,37.7868,-122.4538,0.19965,#FF7F00 9 | Russian Hill,37.8014,-122.4182,0.34165,#FF7F00 10 | Visitacion Valley,37.7144,-122.4113,0.064774,#999999 11 | Diamond Heights,37.7423,-122.4423,0.074148,#984EA3 12 | Glen Park,37.7378,-122.4316,0.1226,#984EA3 13 | Inner Sunset,37.7584,-122.4654,0.18995,#984EA3 14 | Lakeshore,37.7225,-122.4885,0.092602,#984EA3 15 | Outer Mission,37.7239,-122.4439,0.10242,#984EA3 16 | West Of Twin Peaks,37.7373,-122.4589,0.12473,#984EA3 17 | Bayview,37.73,-122.3855,0.10309,#377EB8 18 | Bernal Heights,37.7399,-122.4169,0.20043,#377EB8 19 | Castro,37.7624,-122.4348,0.34092,#377EB8 20 | Haight,37.7692,-122.4463,0.29559,#377EB8 21 | Mission,37.7589,-122.4153,0.42583,#377EB8 22 | Noe,37.7493,-122.433,0.25158,#377EB8 23 | Parkside,37.7411,-122.4892,0.092271,#377EB8 24 | Potrero,37.7583,-122.393,0.26353,#377EB8 25 | South Of Market,37.7764,-122.3994,0.55316,#377EB8 26 | Twin Peaks,37.752,-122.45,0.13105,#377EB8 27 | Western Addition,37.7804,-122.4332,0.41192,#377EB8 28 | Inner Richmond,37.7802,-122.4652,0.21194,#4DAF4A 29 | Outer Richmond,37.778,-122.4928,0.18916,#4DAF4A 30 | Outer Sunset,37.7553,-122.4938,0.12363,#4DAF4A 31 | Seacliff,37.7841,-122.5009,0.091937,#4DAF4A 32 | Golden Gate Park,37.7689,-122.4828,0.15941,#4DAF4A 33 | Chinatown,37.7941,-122.407,0.24273,#F781BF 34 | Downtown,37.7835,-122.4158,0.44235,#F781BF 35 | Financial District,37.7915,-122.3988,0.49915,#F781BF 36 | Ocean View,37.7178,-122.4622,0.076638,#A65628 37 | -------------------------------------------------------------------------------- /graph/data/miserables.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { "name": "Myriel", "group": 1 }, 4 | { "name": "Napoleon", "group": 1 }, 5 | { "name": "Mlle.Baptistine", "group": 1 }, 6 | { "name": "Mme.Magloire", "group": 1 }, 7 | { "name": "CountessdeLo", "group": 1 }, 8 | { "name": "Geborand", "group": 1 }, 9 | { "name": "Champtercier", "group": 1 }, 10 | { "name": "Cravatte", "group": 1 }, 11 | { "name": "Count", "group": 1 }, 12 | { "name": "OldMan", "group": 1 }, 13 | { "name": "Labarre", "group": 2 }, 14 | { "name": "Valjean", "group": 2 }, 15 | { "name": "Marguerite", "group": 3 }, 16 | { "name": "Mme.deR", "group": 2 }, 17 | { "name": "Isabeau", "group": 2 }, 18 | { "name": "Gervais", "group": 2 }, 19 | { "name": "Tholomyes", "group": 3 }, 20 | { "name": "Listolier", "group": 3 }, 21 | { "name": "Fameuil", "group": 3 }, 22 | { "name": "Blacheville", "group": 3 }, 23 | { "name": "Favourite", "group": 3 }, 24 | { "name": "Dahlia", "group": 3 }, 25 | { "name": "Zephine", "group": 3 }, 26 | { "name": "Fantine", "group": 3 }, 27 | { "name": "Mme.Thenardier", "group": 4 }, 28 | { "name": "Thenardier", "group": 4 }, 29 | { "name": "Cosette", "group": 5 }, 30 | { "name": "Javert", "group": 4 }, 31 | { "name": "Fauchelevent", "group": 0 }, 32 | { "name": "Bamatabois", "group": 2 }, 33 | { "name": "Perpetue", "group": 3 }, 34 | { "name": "Simplice", "group": 2 }, 35 | { "name": "Scaufflaire", "group": 2 }, 36 | { "name": "Woman1", "group": 2 }, 37 | { "name": "Judge", "group": 2 }, 38 | { "name": "Champmathieu", "group": 2 }, 39 | { "name": "Brevet", "group": 2 }, 40 | { "name": "Chenildieu", "group": 2 }, 41 | { "name": "Cochepaille", "group": 2 }, 42 | { "name": "Pontmercy", "group": 4 }, 43 | { "name": "Boulatruelle", "group": 6 }, 44 | { "name": "Eponine", "group": 4 }, 45 | { "name": "Anzelma", "group": 4 }, 46 | { "name": "Woman2", "group": 5 }, 47 | { "name": "MotherInnocent", "group": 0 }, 48 | { "name": "Gribier", "group": 0 }, 49 | { "name": "Jondrette", "group": 7 }, 50 | { "name": "Mme.Burgon", "group": 7 }, 51 | { "name": "Gavroche", "group": 8 }, 52 | { "name": "Gillenormand", "group": 5 }, 53 | { "name": "Magnon", "group": 5 }, 54 | { "name": "Mlle.Gillenormand", "group": 5 }, 55 | { "name": "Mme.Pontmercy", "group": 5 }, 56 | { "name": "Mlle.Vaubois", "group": 5 }, 57 | { "name": "Lt.Gillenormand", "group": 5 }, 58 | { "name": "Marius", "group": 8 }, 59 | { "name": "BaronessT", "group": 5 }, 60 | { "name": "Mabeuf", "group": 8 }, 61 | { "name": "Enjolras", "group": 8 }, 62 | { "name": "Combeferre", "group": 8 }, 63 | { "name": "Prouvaire", "group": 8 }, 64 | { "name": "Feuilly", "group": 8 }, 65 | { "name": "Courfeyrac", "group": 8 }, 66 | { "name": "Bahorel", "group": 8 }, 67 | { "name": "Bossuet", "group": 8 }, 68 | { "name": "Joly", "group": 8 }, 69 | { "name": "Grantaire", "group": 8 }, 70 | { "name": "MotherPlutarch", "group": 0 }, 71 | { "name": "Gueulemer", "group": 4 }, 72 | { "name": "Babet", "group": 4 }, 73 | { "name": "Claquesous", "group": 4 }, 74 | { "name": "Montparnasse", "group": 4 }, 75 | { "name": "Toussaint", "group": 5 }, 76 | { "name": "Child1", "group": 10 }, 77 | { "name": "Child2", "group": 10 }, 78 | { "name": "Brujon", "group": 4 }, 79 | { "name": "Mme.Hucheloup", "group": 8 } 80 | ], 81 | 82 | "links": [ 83 | { "source": 1, "target": 0, "value": 1 }, 84 | { "source": 2, "target": 0, "value": 8 }, 85 | { "source": 3, "target": 0, "value": 10 }, 86 | { "source": 3, "target": 2, "value": 6 }, 87 | { "source": 4, "target": 0, "value": 1 }, 88 | { "source": 5, "target": 0, "value": 1 }, 89 | { "source": 6, "target": 0, "value": 1 }, 90 | { "source": 7, "target": 0, "value": 1 }, 91 | { "source": 8, "target": 0, "value": 2 }, 92 | { "source": 0, "target": 0, "value": 1 }, 93 | { "source": 11, "target": 10, "value": 1 }, 94 | { "source": 11, "target": 3, "value": 3 }, 95 | { "source": 11, "target": 2, "value": 3 }, 96 | { "source": 11, "target": 0, "value": 5 }, 97 | { "source": 12, "target": 11, "value": 1 }, 98 | { "source": 13, "target": 11, "value": 1 }, 99 | { "source": 14, "target": 11, "value": 1 }, 100 | { "source": 15, "target": 11, "value": 1 }, 101 | { "source": 17, "target": 16, "value": 4 }, 102 | { "source": 18, "target": 16, "value": 4 }, 103 | { "source": 18, "target": 17, "value": 4 }, 104 | { "source": 19, "target": 16, "value": 4 }, 105 | { "source": 19, "target": 17, "value": 4 }, 106 | { "source": 19, "target": 18, "value": 4 }, 107 | { "source": 20, "target": 16, "value": 3 }, 108 | { "source": 20, "target": 17, "value": 3 }, 109 | { "source": 20, "target": 18, "value": 3 }, 110 | { "source": 20, "target": 19, "value": 4 }, 111 | { "source": 21, "target": 16, "value": 3 }, 112 | { "source": 21, "target": 17, "value": 3 }, 113 | { "source": 21, "target": 18, "value": 3 }, 114 | { "source": 21, "target": 19, "value": 3 }, 115 | { "source": 21, "target": 20, "value": 5 }, 116 | { "source": 22, "target": 16, "value": 3 }, 117 | { "source": 22, "target": 17, "value": 3 }, 118 | { "source": 22, "target": 18, "value": 3 }, 119 | { "source": 22, "target": 19, "value": 3 }, 120 | { "source": 22, "target": 20, "value": 4 }, 121 | { "source": 22, "target": 21, "value": 4 }, 122 | { "source": 23, "target": 16, "value": 3 }, 123 | { "source": 23, "target": 17, "value": 3 }, 124 | { "source": 23, "target": 18, "value": 3 }, 125 | { "source": 23, "target": 19, "value": 3 }, 126 | { "source": 23, "target": 20, "value": 4 }, 127 | { "source": 23, "target": 21, "value": 4 }, 128 | { "source": 23, "target": 22, "value": 4 }, 129 | { "source": 23, "target": 12, "value": 2 }, 130 | { "source": 23, "target": 11, "value": 0 }, 131 | { "source": 24, "target": 23, "value": 2 }, 132 | { "source": 24, "target": 11, "value": 7 }, 133 | { "source": 25, "target": 24, "value": 13 }, 134 | { "source": 25, "target": 23, "value": 1 }, 135 | { "source": 25, "target": 11, "value": 12 }, 136 | { "source": 26, "target": 24, "value": 4 }, 137 | { "source": 26, "target": 11, "value": 31 }, 138 | { "source": 26, "target": 16, "value": 1 }, 139 | { "source": 26, "target": 25, "value": 1 }, 140 | { "source": 27, "target": 11, "value": 17 }, 141 | { "source": 27, "target": 23, "value": 5 }, 142 | { "source": 27, "target": 25, "value": 5 }, 143 | { "source": 27, "target": 24, "value": 1 }, 144 | { "source": 27, "target": 26, "value": 1 }, 145 | { "source": 28, "target": 11, "value": 8 }, 146 | { "source": 28, "target": 27, "value": 1 }, 147 | { "source": 29, "target": 23, "value": 1 }, 148 | { "source": 29, "target": 27, "value": 1 }, 149 | { "source": 29, "target": 11, "value": 2 }, 150 | { "source": 30, "target": 23, "value": 1 }, 151 | { "source": 31, "target": 30, "value": 2 }, 152 | { "source": 31, "target": 11, "value": 3 }, 153 | { "source": 31, "target": 23, "value": 2 }, 154 | { "source": 31, "target": 27, "value": 1 }, 155 | { "source": 32, "target": 11, "value": 1 }, 156 | { "source": 33, "target": 11, "value": 2 }, 157 | { "source": 33, "target": 27, "value": 1 }, 158 | { "source": 34, "target": 11, "value": 3 }, 159 | { "source": 34, "target": 29, "value": 2 }, 160 | { "source": 35, "target": 11, "value": 3 }, 161 | { "source": 35, "target": 34, "value": 3 }, 162 | { "source": 35, "target": 29, "value": 2 }, 163 | { "source": 36, "target": 34, "value": 2 }, 164 | { "source": 36, "target": 35, "value": 2 }, 165 | { "source": 36, "target": 11, "value": 2 }, 166 | { "source": 36, "target": 29, "value": 1 }, 167 | { "source": 37, "target": 34, "value": 2 }, 168 | { "source": 37, "target": 35, "value": 2 }, 169 | { "source": 37, "target": 36, "value": 2 }, 170 | { "source": 37, "target": 11, "value": 2 }, 171 | { "source": 37, "target": 29, "value": 1 }, 172 | { "source": 38, "target": 34, "value": 2 }, 173 | { "source": 38, "target": 35, "value": 2 }, 174 | { "source": 38, "target": 36, "value": 2 }, 175 | { "source": 38, "target": 37, "value": 2 }, 176 | { "source": 38, "target": 11, "value": 2 }, 177 | { "source": 38, "target": 29, "value": 1 }, 178 | { "source": 39, "target": 25, "value": 1 }, 179 | { "source": 40, "target": 25, "value": 1 }, 180 | { "source": 41, "target": 24, "value": 2 }, 181 | { "source": 41, "target": 25, "value": 3 }, 182 | { "source": 42, "target": 41, "value": 2 }, 183 | { "source": 42, "target": 25, "value": 2 }, 184 | { "source": 42, "target": 24, "value": 1 }, 185 | { "source": 43, "target": 11, "value": 3 }, 186 | { "source": 43, "target": 26, "value": 1 }, 187 | { "source": 43, "target": 27, "value": 1 }, 188 | { "source": 44, "target": 28, "value": 3 }, 189 | { "source": 44, "target": 11, "value": 1 }, 190 | { "source": 45, "target": 28, "value": 2 }, 191 | { "source": 47, "target": 46, "value": 1 }, 192 | { "source": 48, "target": 47, "value": 2 }, 193 | { "source": 48, "target": 25, "value": 1 }, 194 | { "source": 48, "target": 27, "value": 1 }, 195 | { "source": 48, "target": 11, "value": 1 }, 196 | { "source": 49, "target": 26, "value": 3 }, 197 | { "source": 49, "target": 11, "value": 2 }, 198 | { "source": 50, "target": 49, "value": 1 }, 199 | { "source": 50, "target": 24, "value": 1 }, 200 | { "source": 51, "target": 49, "value": 0 }, 201 | { "source": 51, "target": 26, "value": 2 }, 202 | { "source": 51, "target": 11, "value": 2 }, 203 | { "source": 52, "target": 51, "value": 1 }, 204 | { "source": 52, "target": 39, "value": 1 }, 205 | { "source": 53, "target": 51, "value": 1 }, 206 | { "source": 54, "target": 51, "value": 2 }, 207 | { "source": 54, "target": 49, "value": 1 }, 208 | { "source": 54, "target": 26, "value": 1 }, 209 | { "source": 55, "target": 51, "value": 6 }, 210 | { "source": 55, "target": 49, "value": 12 }, 211 | { "source": 55, "target": 39, "value": 1 }, 212 | { "source": 55, "target": 54, "value": 1 }, 213 | { "source": 55, "target": 26, "value": 21 }, 214 | { "source": 55, "target": 11, "value": 19 }, 215 | { "source": 55, "target": 16, "value": 1 }, 216 | { "source": 55, "target": 25, "value": 2 }, 217 | { "source": 55, "target": 41, "value": 5 }, 218 | { "source": 55, "target": 48, "value": 4 }, 219 | { "source": 56, "target": 49, "value": 1 }, 220 | { "source": 56, "target": 55, "value": 1 }, 221 | { "source": 57, "target": 55, "value": 1 }, 222 | { "source": 57, "target": 41, "value": 1 }, 223 | { "source": 57, "target": 48, "value": 1 }, 224 | { "source": 58, "target": 55, "value": 7 }, 225 | { "source": 58, "target": 48, "value": 7 }, 226 | { "source": 58, "target": 27, "value": 6 }, 227 | { "source": 58, "target": 57, "value": 1 }, 228 | { "source": 58, "target": 11, "value": 4 }, 229 | { "source": 59, "target": 58, "value": 15 }, 230 | { "source": 59, "target": 55, "value": 5 }, 231 | { "source": 59, "target": 48, "value": 6 }, 232 | { "source": 59, "target": 57, "value": 2 }, 233 | { "source": 60, "target": 48, "value": 1 }, 234 | { "source": 60, "target": 58, "value": 4 }, 235 | { "source": 60, "target": 59, "value": 2 }, 236 | { "source": 61, "target": 48, "value": 2 }, 237 | { "source": 61, "target": 58, "value": 6 }, 238 | { "source": 61, "target": 60, "value": 2 }, 239 | { "source": 61, "target": 59, "value": 5 }, 240 | { "source": 61, "target": 57, "value": 1 }, 241 | { "source": 61, "target": 55, "value": 1 }, 242 | { "source": 62, "target": 55, "value": 0 }, 243 | { "source": 62, "target": 58, "value": 17 }, 244 | { "source": 62, "target": 59, "value": 13 }, 245 | { "source": 62, "target": 48, "value": 7 }, 246 | { "source": 62, "target": 57, "value": 2 }, 247 | { "source": 62, "target": 41, "value": 1 }, 248 | { "source": 62, "target": 61, "value": 6 }, 249 | { "source": 62, "target": 60, "value": 3 }, 250 | { "source": 63, "target": 59, "value": 5 }, 251 | { "source": 63, "target": 48, "value": 5 }, 252 | { "source": 63, "target": 62, "value": 6 }, 253 | { "source": 63, "target": 57, "value": 2 }, 254 | { "source": 63, "target": 58, "value": 4 }, 255 | { "source": 63, "target": 61, "value": 3 }, 256 | { "source": 63, "target": 60, "value": 2 }, 257 | { "source": 63, "target": 55, "value": 1 }, 258 | { "source": 64, "target": 55, "value": 5 }, 259 | { "source": 64, "target": 62, "value": 12 }, 260 | { "source": 64, "target": 48, "value": 5 }, 261 | { "source": 64, "target": 63, "value": 4 }, 262 | { "source": 64, "target": 58, "value": 10 }, 263 | { "source": 64, "target": 61, "value": 6 }, 264 | { "source": 64, "target": 60, "value": 2 }, 265 | { "source": 64, "target": 59, "value": 0 }, 266 | { "source": 64, "target": 57, "value": 1 }, 267 | { "source": 64, "target": 11, "value": 1 }, 268 | { "source": 65, "target": 63, "value": 5 }, 269 | { "source": 65, "target": 64, "value": 7 }, 270 | { "source": 65, "target": 48, "value": 3 }, 271 | { "source": 65, "target": 62, "value": 5 }, 272 | { "source": 65, "target": 58, "value": 5 }, 273 | { "source": 65, "target": 61, "value": 5 }, 274 | { "source": 65, "target": 60, "value": 2 }, 275 | { "source": 65, "target": 59, "value": 5 }, 276 | { "source": 65, "target": 57, "value": 1 }, 277 | { "source": 65, "target": 55, "value": 2 }, 278 | { "source": 66, "target": 64, "value": 3 }, 279 | { "source": 66, "target": 58, "value": 3 }, 280 | { "source": 66, "target": 59, "value": 1 }, 281 | { "source": 66, "target": 62, "value": 2 }, 282 | { "source": 66, "target": 65, "value": 2 }, 283 | { "source": 66, "target": 48, "value": 1 }, 284 | { "source": 66, "target": 63, "value": 1 }, 285 | { "source": 66, "target": 61, "value": 1 }, 286 | { "source": 66, "target": 60, "value": 1 }, 287 | { "source": 67, "target": 57, "value": 3 }, 288 | { "source": 68, "target": 25, "value": 5 }, 289 | { "source": 68, "target": 11, "value": 1 }, 290 | { "source": 68, "target": 24, "value": 1 }, 291 | { "source": 68, "target": 27, "value": 1 }, 292 | { "source": 68, "target": 48, "value": 1 }, 293 | { "source": 68, "target": 41, "value": 1 }, 294 | { "source": 69, "target": 25, "value": 6 }, 295 | { "source": 69, "target": 68, "value": 6 }, 296 | { "source": 69, "target": 11, "value": 1 }, 297 | { "source": 69, "target": 24, "value": 1 }, 298 | { "source": 69, "target": 27, "value": 2 }, 299 | { "source": 69, "target": 48, "value": 1 }, 300 | { "source": 69, "target": 41, "value": 1 }, 301 | { "source": 70, "target": 25, "value": 4 }, 302 | { "source": 70, "target": 69, "value": 4 }, 303 | { "source": 70, "target": 68, "value": 4 }, 304 | { "source": 70, "target": 11, "value": 1 }, 305 | { "source": 70, "target": 24, "value": 1 }, 306 | { "source": 70, "target": 27, "value": 1 }, 307 | { "source": 70, "target": 41, "value": 1 }, 308 | { "source": 70, "target": 58, "value": 1 }, 309 | { "source": 71, "target": 27, "value": 1 }, 310 | { "source": 71, "target": 69, "value": 2 }, 311 | { "source": 71, "target": 68, "value": 2 }, 312 | { "source": 71, "target": 70, "value": 2 }, 313 | { "source": 71, "target": 11, "value": 1 }, 314 | { "source": 71, "target": 48, "value": 1 }, 315 | { "source": 71, "target": 41, "value": 1 }, 316 | { "source": 71, "target": 25, "value": 1 }, 317 | { "source": 72, "target": 26, "value": 2 }, 318 | { "source": 72, "target": 27, "value": 1 }, 319 | { "source": 72, "target": 11, "value": 1 }, 320 | { "source": 73, "target": 48, "value": 2 }, 321 | { "source": 74, "target": 48, "value": 2 }, 322 | { "source": 74, "target": 73, "value": 3 }, 323 | { "source": 75, "target": 69, "value": 3 }, 324 | { "source": 75, "target": 68, "value": 3 }, 325 | { "source": 75, "target": 25, "value": 3 }, 326 | { "source": 75, "target": 48, "value": 1 }, 327 | { "source": 75, "target": 41, "value": 1 }, 328 | { "source": 75, "target": 70, "value": 1 }, 329 | { "source": 75, "target": 71, "value": 1 }, 330 | { "source": 76, "target": 64, "value": 1 }, 331 | { "source": 76, "target": 65, "value": 1 }, 332 | { "source": 76, "target": 66, "value": 1 }, 333 | { "source": 76, "target": 63, "value": 1 }, 334 | { "source": 76, "target": 62, "value": 1 }, 335 | { "source": 76, "target": 48, "value": 1 }, 336 | { "source": 76, "target": 58, "value": 1 } 337 | ] 338 | } 339 | -------------------------------------------------------------------------------- /graph/graph.js: -------------------------------------------------------------------------------- 1 | (function(d3) { 2 | d3.graph = function (graph) { 3 | var graph = graph ? graph : {}, 4 | nodes = [], 5 | links = []; 6 | 7 | graph.description = function() { 8 | return "d3.graph with " + nodes.length + " nodes and " + links.length + " links"; 9 | }; 10 | 11 | graph.nodes = function(x) { 12 | if (!arguments.length) return nodes; 13 | nodes = x; 14 | return this; 15 | }; 16 | 17 | graph.links = function(x) { 18 | if (!arguments.length) return links; 19 | links = x; 20 | return this; 21 | }; 22 | 23 | graph.matrix = function(x) { 24 | if (!arguments.length) return d3.graph.listToMatrix(links); 25 | links = d3.graph.matrixToList(x); 26 | // TODO nodes 27 | return this; 28 | }; 29 | 30 | return graph; 31 | }; 32 | 33 | // http://opendatastructures.org/ods-cpp/12_1_Representing_Graph_Mat.html 34 | d3.graph.matrix = function(matrix) { 35 | var matrix = matrix ? matrix : []; 36 | 37 | var matrixObj = function(i,j) { 38 | return matrix[i][j]; 39 | }; 40 | 41 | matrixObj.description = function() { 42 | return "A " + matrix.length + 43 | " by " + matrix.length + 44 | " adjacency matrix"; 45 | }; 46 | 47 | matrixObj.data = matrixObj.matrix = function(x) { 48 | if (!arguments.length) return matrix; 49 | matrix = x; 50 | return this; 51 | }; 52 | 53 | matrixObj.set = matrixObj.addEdge = function(i,j,value) { 54 | matrix[i][j] = value ? value : 1; 55 | return this; 56 | }; 57 | 58 | matrixObj.remove = matrixObj.removeEdge = function(i,j) { 59 | matrix[i][j] = 0; 60 | return this; 61 | }; 62 | 63 | matrixObj.has = matrixObj.hasEdge = function(i,j) { 64 | return !!matrix[i][j]; 65 | }; 66 | 67 | matrixObj.outE = matrixObj.outEdges = function(i) { 68 | var edges = [], 69 | n = matrix.length; 70 | var j = -1; while (++j < n) { 71 | if (matrix[i][j]) edges.push(j); 72 | } 73 | return edges; 74 | }; 75 | 76 | matrixObj.inE = matrixObj.inEdges = function(i) { 77 | var edges = [], 78 | n = matrix.length; 79 | var j = -1; while (++j < n) { 80 | if (matrix[j][i]) edges.push(j); 81 | } 82 | return edges; 83 | }; 84 | 85 | return matrixObj; 86 | }; 87 | 88 | d3.graph.listToMatrix = function(links) { 89 | var matrix = [], 90 | n = links.length, 91 | max = d3.max(links, function(d) { 92 | return d3.max([d.source, d.target]); 93 | }); 94 | 95 | // zero matrix 96 | var i = -1; while (++i <= max) { 97 | matrix[i] = []; 98 | var j = -1; while (++j <= max) { 99 | matrix[i][j] = 0; 100 | } 101 | } 102 | 103 | i = -1; while (++i < n) { 104 | matrix[ links[i].source ][ links[i].target ] = links[i].value; 105 | } 106 | 107 | return matrix; 108 | }; 109 | 110 | d3.graph.matrixToList = function(matrix) { 111 | var links = [], 112 | n = matrix.length; 113 | 114 | var i = -1; while (++i < n) { 115 | var j = -1; while (++j < n) { 116 | links.push({ 117 | source: i, 118 | target: j, 119 | value: matrix[i][j] 120 | }); 121 | } 122 | } 123 | 124 | return links; 125 | }; 126 | 127 | 128 | })(d3); 129 | -------------------------------------------------------------------------------- /graph/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 37 | -------------------------------------------------------------------------------- /hexbin/README.md: -------------------------------------------------------------------------------- 1 | # Hexagonal Binning 2 | 3 | Examples: 4 | 5 | * Color encoding: 6 | * Area encoding: 7 | 8 | The **d3.hexbin** plugin implements **hexagonal binning**, which is useful for aggregating data into a more coarse representation suitable for display. Rather than displaying a scatterplot with tens of thousands of points, you can bin points into gridded hexagons, and then display the distribution using color or area. This plugin was inspired by earlier work by [Zachary Forest Johnson](http://indiemaps.com/blog/2011/10/hexbins/). 9 | 10 | # d3.hexbin() 11 | 12 | Constructs a new default hexbin layout. 13 | 14 | # hexbin(points) 15 | 16 | Evaluates the hexbin layout on the specified array of *points*, returning an array of hexagonal *bins*. Each bin is an array containing the bin’s points, as well as some additional properties: 17 | 18 | * x - the x-coordinate of the center of the associated bin’s hexagon 19 | * y - the y-coordinate of the center of the associated bin’s hexagon 20 | 21 | Bins that are empty are not omitted. The origin bin at ⟨0,0⟩ is in the top-left. The returned bins are designed to work with the layouts point and hexagon methods. 22 | 23 | # hexbin.size([size]) 24 | 25 | If *size* is specified, sets the available layout size to the specified two-element array of numbers representing *x* and *y*. If *size* is not specified, returns the current size, which defaults to 1×1. 26 | 27 | # hexbin.radius([radius]) 28 | 29 | If *radius* is specified, sets the hexagon radius to the specified numeric value. If *radius* is not specified, returns the current radius, which defaults to 1. 30 | 31 | # hexbin.x([accessor]) 32 | 33 | Sets or gets the *x*-accessor function for the hexbin layout. If *accessor* is specified, sets the *x*-accessor function and returns the hexbin layout; if *accessor* is not specified, returns the current *x*-accessor function, which defaults to `function(d) { return d[0]; }`. 34 | 35 | # hexbin.y([accessor]) 36 | 37 | Sets or gets the *y*-accessor function for the hexbin layout. If *accessor* is specified, sets the *y*-accessor function and returns the hexbin layout; if *accessor* is not specified, returns the current *y*-accessor function, which defaults to `function(d) { return d[1]; }`. 38 | 39 | # hexbin.hexagon([radius]) 40 | 41 | Returns the SVG path string for the hexagon centered at the origin ⟨0,0⟩. The path string is defined with relative coordinates such that you can easily translate the hexagon to the desired position. For example: 42 | 43 | ```js 44 | path.attr("d", function(d) { return "M" + d.x + "," + d.y + hexbin.hexagon(); }); 45 | ``` 46 | 47 | Alternatively, use a transform attribute: 48 | 49 | ```js 50 | path.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }) 51 | .attr("d", hexbin.hexagon()); 52 | ``` 53 | 54 | If *radius* is not specified, the hexbin’s current radius is used. If *radius* is specified, a hexagon with the specified radius is returned, which is useful for area-encoded bivariate hexbins. 55 | 56 | # hexbin.centers() 57 | 58 | Returns an array of [*x*, *y*] points representing the centers of each hexagon. Each point also has properties *i* and *j* representing the grid column and row, respectively, of the hexagon. 59 | 60 | # hexbin.mesh() 61 | 62 | Returns the SVG path string for a hexagonal mesh that covers the area of the layout (as determined by the layout size). The returned mesh is designed to be stroked. The mesh may extend slightly beyond the layout’s defined area, and thus may need to be clipped. 63 | -------------------------------------------------------------------------------- /hexbin/hexbin.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | d3.hexbin = function() { 4 | var width = 1, 5 | height = 1, 6 | r, 7 | x = d3_hexbinX, 8 | y = d3_hexbinY, 9 | dx, 10 | dy; 11 | 12 | function hexbin(points) { 13 | var binsById = {}; 14 | 15 | points.forEach(function(point, i) { 16 | var py = y.call(hexbin, point, i) / dy, pj = Math.round(py), 17 | px = x.call(hexbin, point, i) / dx - (pj & 1 ? .5 : 0), pi = Math.round(px), 18 | py1 = py - pj; 19 | 20 | if (Math.abs(py1) * 3 > 1) { 21 | var px1 = px - pi, 22 | pi2 = pi + (px < pi ? -1 : 1) / 2, 23 | pj2 = pj + (py < pj ? -1 : 1), 24 | px2 = px - pi2, 25 | py2 = py - pj2; 26 | if (px1 * px1 + py1 * py1 > px2 * px2 + py2 * py2) pi = pi2 + (pj & 1 ? 1 : -1) / 2, pj = pj2; 27 | } 28 | 29 | var id = pi + "-" + pj, bin = binsById[id]; 30 | if (bin) bin.push(point); else { 31 | bin = binsById[id] = [point]; 32 | bin.i = pi; 33 | bin.j = pj; 34 | bin.x = (pi + (pj & 1 ? 1 / 2 : 0)) * dx; 35 | bin.y = pj * dy; 36 | } 37 | }); 38 | 39 | return d3.values(binsById); 40 | } 41 | 42 | function hexagon(radius) { 43 | var x0 = 0, y0 = 0; 44 | return d3_hexbinAngles.map(function(angle) { 45 | var x1 = Math.sin(angle) * radius, 46 | y1 = -Math.cos(angle) * radius, 47 | dx = x1 - x0, 48 | dy = y1 - y0; 49 | x0 = x1, y0 = y1; 50 | return [dx, dy]; 51 | }); 52 | } 53 | 54 | hexbin.x = function(_) { 55 | if (!arguments.length) return x; 56 | x = _; 57 | return hexbin; 58 | }; 59 | 60 | hexbin.y = function(_) { 61 | if (!arguments.length) return y; 62 | y = _; 63 | return hexbin; 64 | }; 65 | 66 | hexbin.hexagon = function(radius) { 67 | if (arguments.length < 1) radius = r; 68 | return "m" + hexagon(radius).join("l") + "z"; 69 | }; 70 | 71 | hexbin.centers = function() { 72 | var centers = []; 73 | for (var y = 0, odd = false, j = 0; y < height + r; y += dy, odd = !odd, ++j) { 74 | for (var x = odd ? dx / 2 : 0, i = 0; x < width + dx / 2; x += dx, ++i) { 75 | var center = [x, y]; 76 | center.i = i; 77 | center.j = j; 78 | centers.push(center); 79 | } 80 | } 81 | return centers; 82 | }; 83 | 84 | hexbin.mesh = function() { 85 | var fragment = hexagon(r).slice(0, 4).join("l"); 86 | return hexbin.centers().map(function(p) { return "M" + p + "m" + fragment; }).join(""); 87 | }; 88 | 89 | hexbin.size = function(_) { 90 | if (!arguments.length) return [width, height]; 91 | width = +_[0], height = +_[1]; 92 | return hexbin; 93 | }; 94 | 95 | hexbin.radius = function(_) { 96 | if (!arguments.length) return r; 97 | r = +_; 98 | dx = r * 2 * Math.sin(Math.PI / 3); 99 | dy = r * 1.5; 100 | return hexbin; 101 | }; 102 | 103 | return hexbin.radius(1); 104 | }; 105 | 106 | var d3_hexbinAngles = d3.range(0, 2 * Math.PI, Math.PI / 3), 107 | d3_hexbinX = function(d) { return d[0]; }, 108 | d3_hexbinY = function(d) { return d[1]; }; 109 | 110 | })(); 111 | -------------------------------------------------------------------------------- /hive/README.md: -------------------------------------------------------------------------------- 1 | # d3.hive 2 | 3 | A plugin for rendering Hive Plots. Example: 4 | 5 | 6 | -------------------------------------------------------------------------------- /hive/hive.js: -------------------------------------------------------------------------------- 1 | d3.hive = {}; 2 | 3 | d3.hive.link = function() { 4 | var source = function(d) { return d.source; }, 5 | target = function(d) { return d.target; }, 6 | angle = function(d) { return d.angle; }, 7 | startRadius = function(d) { return d.radius; }, 8 | endRadius = startRadius, 9 | arcOffset = -Math.PI / 2; 10 | 11 | function link(d, i) { 12 | var s = node(source, this, d, i), 13 | t = node(target, this, d, i), 14 | x; 15 | if (t.a < s.a) x = t, t = s, s = x; 16 | if (t.a - s.a > Math.PI) s.a += 2 * Math.PI; 17 | var a1 = s.a + (t.a - s.a) / 3, 18 | a2 = t.a - (t.a - s.a) / 3; 19 | return s.r0 - s.r1 || t.r0 - t.r1 20 | ? "M" + Math.cos(s.a) * s.r0 + "," + Math.sin(s.a) * s.r0 21 | + "L" + Math.cos(s.a) * s.r1 + "," + Math.sin(s.a) * s.r1 22 | + "C" + Math.cos(a1) * s.r1 + "," + Math.sin(a1) * s.r1 23 | + " " + Math.cos(a2) * t.r1 + "," + Math.sin(a2) * t.r1 24 | + " " + Math.cos(t.a) * t.r1 + "," + Math.sin(t.a) * t.r1 25 | + "L" + Math.cos(t.a) * t.r0 + "," + Math.sin(t.a) * t.r0 26 | + "C" + Math.cos(a2) * t.r0 + "," + Math.sin(a2) * t.r0 27 | + " " + Math.cos(a1) * s.r0 + "," + Math.sin(a1) * s.r0 28 | + " " + Math.cos(s.a) * s.r0 + "," + Math.sin(s.a) * s.r0 29 | : "M" + Math.cos(s.a) * s.r0 + "," + Math.sin(s.a) * s.r0 30 | + "C" + Math.cos(a1) * s.r1 + "," + Math.sin(a1) * s.r1 31 | + " " + Math.cos(a2) * t.r1 + "," + Math.sin(a2) * t.r1 32 | + " " + Math.cos(t.a) * t.r1 + "," + Math.sin(t.a) * t.r1; 33 | } 34 | 35 | function node(method, thiz, d, i) { 36 | var node = method.call(thiz, d, i), 37 | a = +(typeof angle === "function" ? angle.call(thiz, node, i) : angle) + arcOffset, 38 | r0 = +(typeof startRadius === "function" ? startRadius.call(thiz, node, i) : startRadius), 39 | r1 = (startRadius === endRadius ? r0 : +(typeof endRadius === "function" ? endRadius.call(thiz, node, i) : endRadius)); 40 | return {r0: r0, r1: r1, a: a}; 41 | } 42 | 43 | link.source = function(_) { 44 | if (!arguments.length) return source; 45 | source = _; 46 | return link; 47 | }; 48 | 49 | link.target = function(_) { 50 | if (!arguments.length) return target; 51 | target = _; 52 | return link; 53 | }; 54 | 55 | link.angle = function(_) { 56 | if (!arguments.length) return angle; 57 | angle = _; 58 | return link; 59 | }; 60 | 61 | link.radius = function(_) { 62 | if (!arguments.length) return startRadius; 63 | startRadius = endRadius = _; 64 | return link; 65 | }; 66 | 67 | link.startRadius = function(_) { 68 | if (!arguments.length) return startRadius; 69 | startRadius = _; 70 | return link; 71 | }; 72 | 73 | link.endRadius = function(_) { 74 | if (!arguments.length) return endRadius; 75 | endRadius = _; 76 | return link; 77 | }; 78 | 79 | return link; 80 | }; 81 | -------------------------------------------------------------------------------- /horizon/README.md: -------------------------------------------------------------------------------- 1 | # d3.horizon 2 | 3 | Demo: 4 | 5 | ![Horizon Chart](http://vis.berkeley.edu/papers/horizon/construction.png) 6 | 7 | A horizon chart is a variation of a single-series area chart where the area is folded into multiple bands. Color is used to encode bands, allowing the size of the chart to be reduced significantly without impeding readability. 8 | 9 | This layout algorithm is based on the work of J. Heer, N. Kong and M. Agrawala in "Sizing the Horizon: The Effects of Chart Size and Layering on the Graphical Perception of Time Series Visualizations", CHI 2009. 10 | 11 | 12 | -------------------------------------------------------------------------------- /horizon/horizon.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | d3.horizon = function() { 3 | var bands = 1, // between 1 and 5, typically 4 | mode = "offset", // or mirror 5 | area = d3.svg.area(), 6 | defined, 7 | x = d3_horizonX, 8 | y = d3_horizonY, 9 | width = 960, 10 | height = 40; 11 | 12 | var color = d3.scale.linear() 13 | .domain([-1, 0, 1]) 14 | .range(["#d62728", "#fff", "#1f77b4"]); 15 | 16 | // For each small multiple… 17 | function horizon(g) { 18 | g.each(function(d) { 19 | var g = d3.select(this), 20 | xMin = Infinity, 21 | xMax = -Infinity, 22 | yMax = -Infinity, 23 | x0, // old x-scale 24 | y0, // old y-scale 25 | t0, 26 | id; // unique id for paths 27 | 28 | // Compute x- and y-values along with extents. 29 | var data = d.map(function(d, i) { 30 | var xv = x.call(this, d, i), 31 | yv = y.call(this, d, i); 32 | if (xv < xMin) xMin = xv; 33 | if (xv > xMax) xMax = xv; 34 | if (-yv > yMax) yMax = -yv; 35 | if (yv > yMax) yMax = yv; 36 | return [xv, yv]; 37 | }); 38 | 39 | // Compute the new x- and y-scales, and transform. 40 | var x1 = d3.scale.linear().domain([xMin, xMax]).range([0, width]), 41 | y1 = d3.scale.linear().domain([0, yMax]).range([0, height * bands]), 42 | t1 = d3_horizonTransform(bands, height, mode); 43 | 44 | // Retrieve the old scales, if this is an update. 45 | if (this.__chart__) { 46 | x0 = this.__chart__.x; 47 | y0 = this.__chart__.y; 48 | t0 = this.__chart__.t; 49 | id = this.__chart__.id; 50 | } else { 51 | x0 = x1.copy(); 52 | y0 = y1.copy(); 53 | t0 = t1; 54 | id = ++d3_horizonId; 55 | } 56 | 57 | // We'll use a defs to store the area path and the clip path. 58 | var defs = g.selectAll("defs") 59 | .data([null]); 60 | 61 | // The clip path is a simple rect. 62 | defs.enter().append("defs").append("clipPath") 63 | .attr("id", "d3_horizon_clip" + id) 64 | .append("rect") 65 | .attr("width", width) 66 | .attr("height", height); 67 | 68 | d3.transition(defs.select("rect")) 69 | .attr("width", width) 70 | .attr("height", height); 71 | 72 | // We'll use a container to clip all horizon layers at once. 73 | g.selectAll("g") 74 | .data([null]) 75 | .enter().append("g") 76 | .attr("clip-path", "url(#d3_horizon_clip" + id + ")"); 77 | 78 | // Instantiate each copy of the path with different transforms. 79 | var path = g.select("g").selectAll("path") 80 | .data(d3.range(-1, -bands - 1, -1).concat(d3.range(1, bands + 1)), Number); 81 | 82 | if (defined) area.defined(function(_, i) { return defined.call(this, d[i], i); }); 83 | 84 | var d0 = area 85 | .x(function(d) { return x0(d[0]); }) 86 | .y0(height * bands) 87 | .y1(function(d) { return height * bands - y0(d[1]); }) 88 | (data); 89 | 90 | var d1 = area 91 | .x(function(d) { return x1(d[0]); }) 92 | .y1(function(d) { return height * bands - y1(d[1]); }) 93 | (data); 94 | 95 | path.enter().append("path") 96 | .style("fill", color) 97 | .attr("transform", t0) 98 | .attr("d", d0); 99 | 100 | d3.transition(path) 101 | .style("fill", color) 102 | .attr("transform", t1) 103 | .attr("d", d1); 104 | 105 | d3.transition(path.exit()) 106 | .attr("transform", t1) 107 | .attr("d", d1) 108 | .remove(); 109 | 110 | // Stash the new scales. 111 | this.__chart__ = {x: x1, y: y1, t: t1, id: id}; 112 | }); 113 | } 114 | 115 | horizon.bands = function(_) { 116 | if (!arguments.length) return bands; 117 | bands = +_; 118 | color.domain([-bands, 0, bands]); 119 | return horizon; 120 | }; 121 | 122 | horizon.mode = function(_) { 123 | if (!arguments.length) return mode; 124 | mode = _ + ""; 125 | return horizon; 126 | }; 127 | 128 | horizon.colors = function(_) { 129 | if (!arguments.length) return color.range(); 130 | color.range(_); 131 | return horizon; 132 | }; 133 | 134 | horizon.x = function(_) { 135 | if (!arguments.length) return x; 136 | x = _; 137 | return horizon; 138 | }; 139 | 140 | horizon.y = function(_) { 141 | if (!arguments.length) return y; 142 | y = _; 143 | return horizon; 144 | }; 145 | 146 | horizon.width = function(_) { 147 | if (!arguments.length) return width; 148 | width = +_; 149 | return horizon; 150 | }; 151 | 152 | horizon.height = function(_) { 153 | if (!arguments.length) return height; 154 | height = +_; 155 | return horizon; 156 | }; 157 | 158 | horizon.defined = function(_) { 159 | if (!arguments.length) return defined; 160 | defined = _; 161 | return horizon; 162 | }; 163 | 164 | return d3.rebind(horizon, area, "interpolate", "tension"); 165 | }; 166 | 167 | var d3_horizonId = 0; 168 | 169 | function d3_horizonX(d) { return d[0]; } 170 | function d3_horizonY(d) { return d[1]; } 171 | 172 | function d3_horizonTransform(bands, h, mode) { 173 | return mode == "offset" 174 | ? function(d) { return "translate(0," + (d + (d < 0) - bands) * h + ")"; } 175 | : function(d) { return (d < 0 ? "scale(1,-1)" : "") + "translate(0," + (d - bands) * h + ")"; }; 176 | } 177 | })(); 178 | -------------------------------------------------------------------------------- /interpolate-zoom/README.md: -------------------------------------------------------------------------------- 1 | # Interpolate Zoom 2 | 3 | **As of [D3 3.3](https://github.com/mbostock/d3/releases/tag/v3.3.0), the zoom interpolator is now part of D3 core and this plugin is deprecated.** 4 | 5 | An interpolator for zooming and panning between two views of a two-dimensional plane, based on [“Smooth and efficient zooming and panning”](https://www.google.com/search?q=Smooth+and+efficient+zooming+and+panning) by Jarke J. van Wijk and Wim A.A. Nuij. 6 | 7 | Demo: 8 | 9 | To use this plugin: 10 | 11 | ```html 12 | 13 | 14 | ``` 15 | 16 | Note that since this script uses UTF-8 characters, the `charset` attribute is required if the page does not have a `` in the head. There’s also a [d3.interpolate-zoom.v0.js](http://d3js.org/d3.interpolate-zoom.v0.js) for development, or you can clone this repository. 17 | 18 | # d3.interpolateZoom(start, end) 19 | 20 | Returns an [interpolator](https://github.com/mbostock/d3/wiki/Transitions#wiki-_interpolate) between the two points *start* and *end*. Each point should be defined as an array of three numbers in world coordinates: *ux*, *uy* and *w*. The first two coordinates *ux*, *uy* are the center of the viewport. The last coordinate *w* is the size of the viewport. 21 | 22 | The returned interpolator also has a *duration* property which encodes the recommended transition duration in milliseconds. This duration is based on the path length of the curved trajectory through *u,w* space. If you want to a slower or faster transition, feel free to multiply this by an arbitrary scale factor (V as described in the original paper). 23 | -------------------------------------------------------------------------------- /interpolate-zoom/interpolate-zoom-test.js: -------------------------------------------------------------------------------- 1 | var vows = require("vows"), 2 | assert = require("assert"); 3 | 4 | global.d3 = require("d3"); 5 | require("./interpolate-zoom"); 6 | 7 | var suite = vows.describe("d3.interpolateZoom"); 8 | 9 | suite.addBatch({ 10 | "interpolateZoom": { 11 | topic: function() { return d3.interpolateZoom; }, 12 | "identity": function(interpolate) { 13 | var i = interpolate([1, 1, 1], [1, 1, 1]); 14 | assert.deepEqual(i(0), [1, 1, 1]); 15 | assert.deepEqual(i(.5), [1, 1, 1]); 16 | assert.deepEqual(i(1), [1, 1, 1]); 17 | }, 18 | "ux0 almost equal to ux1": function(interpolate) { 19 | var i = interpolate([6317.937500000001, 9242.5, 2], [6317.9375, 9242.5, 5]); 20 | assert.deepEqual(i(0), [6317.937500000001, 9242.5, 2]); 21 | assert.deepEqual(i(.5), [6317.9375, 9242.5, 3.162277660168379]); 22 | assert.deepEqual(i(1), [6317.9375, 9242.5, 5]); 23 | } 24 | } 25 | }); 26 | 27 | suite.export(module); 28 | -------------------------------------------------------------------------------- /interpolate-zoom/interpolate-zoom.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | var ρ = Math.SQRT2, 4 | ρ2 = 2, 5 | ρ4 = 4; 6 | 7 | // p0 = [ux0, uy0, w0] 8 | // p1 = [ux1, uy1, w1] 9 | d3.interpolateZoom = function(p0, p1) { 10 | var ux0 = p0[0], uy0 = p0[1], w0 = p0[2], 11 | ux1 = p1[0], uy1 = p1[1], w1 = p1[2]; 12 | 13 | var dx = ux1 - ux0, 14 | dy = uy1 - uy0, 15 | d2 = dx * dx + dy * dy, 16 | d1 = Math.sqrt(d2), 17 | b0 = (w1 * w1 - w0 * w0 + ρ4 * d2) / (2 * w0 * ρ2 * d1), 18 | b1 = (w1 * w1 - w0 * w0 - ρ4 * d2) / (2 * w1 * ρ2 * d1), 19 | r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0), 20 | r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1), 21 | dr = r1 - r0, 22 | S = (dr || Math.log(w1 / w0)) / ρ; 23 | 24 | function interpolate(t) { 25 | var s = t * S; 26 | if (dr) { 27 | // General case. 28 | var coshr0 = cosh(r0), 29 | u = w0 / (ρ2 * d1) * (coshr0 * tanh(ρ * s + r0) - sinh(r0)); 30 | return [ 31 | ux0 + u * dx, 32 | uy0 + u * dy, 33 | w0 * coshr0 / cosh(ρ * s + r0) 34 | ]; 35 | } 36 | // Special case for u0 ~= u1. 37 | return [ 38 | ux0 + t * dx, 39 | uy0 + t * dy, 40 | w0 * Math.exp(ρ * s) 41 | ]; 42 | } 43 | 44 | interpolate.duration = S * 1000; 45 | 46 | return interpolate; 47 | }; 48 | 49 | function cosh(x) { 50 | return (Math.exp(x) + Math.exp(-x)) / 2; 51 | } 52 | 53 | function sinh(x) { 54 | return (Math.exp(x) - Math.exp(-x)) / 2; 55 | } 56 | 57 | function tanh(x) { 58 | return sinh(x) / cosh(x); 59 | } 60 | 61 | })(); 62 | -------------------------------------------------------------------------------- /jsonp/README.md: -------------------------------------------------------------------------------- 1 | # d3.jsonp 2 | 3 | Demo: 4 | 5 | A plugin for supporting the [JSONP](http://json-p.org/) technique of requesting 6 | cross-domain JSON data. This plugin is unstable and in development. 7 | 8 | Whenever possible, _use [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing) 9 | instead of JSONP_. CORS handles errors properly, has better security, and 10 | supports formats besides JSON. See [enable-cors.org](http://enable-cors.org/) for more 11 | information. 12 | 13 | ```js 14 | // autogenerate a callback in the form d3.jsonp.foo 15 | d3.jsonp('foo.jsonp?callback={callback}', function() { 16 | console.log(arguments); 17 | }); 18 | 19 | // specify a callback 20 | d3.jsonp('foo.jsonp?callback=d3.jsonp.foo', function() { 21 | console.log(arguments); 22 | }); 23 | ``` 24 | 25 | This plugin requires servers to accept callbacks with numeric and `.` 26 | characters. Unlike `d3.xhr` functions, there is no error handling. 27 | -------------------------------------------------------------------------------- /jsonp/jsonp.js: -------------------------------------------------------------------------------- 1 | d3.jsonp = function (url, callback) { 2 | function rand() { 3 | var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', 4 | c = '', i = -1; 5 | while (++i < 15) c += chars.charAt(Math.floor(Math.random() * 52)); 6 | return c; 7 | } 8 | 9 | function create(url) { 10 | var e = url.match(/callback=d3.jsonp.(\w+)/), 11 | c = e ? e[1] : rand(); 12 | d3.jsonp[c] = function(data) { 13 | callback(data); 14 | delete d3.jsonp[c]; 15 | script.remove(); 16 | }; 17 | return 'd3.jsonp.' + c; 18 | } 19 | 20 | var cb = create(url), 21 | script = d3.select('head') 22 | .append('script') 23 | .attr('type', 'text/javascript') 24 | .attr('src', url.replace(/(\{|%7B)callback(\}|%7D)/, cb)); 25 | }; 26 | -------------------------------------------------------------------------------- /keybinding/README.md: -------------------------------------------------------------------------------- 1 | # keybinding 2 | 3 | Keybindings for d3 4 | 5 | Demo: 6 | 7 | # d3.**keybinding**() 8 | 9 | Returns a keybinding object that can be used with `.call` to provide 10 | bindings on selections. 11 | 12 | The keybinding object exposes `.on()` for common keycodes and dispatches events 13 | with arguments `[d3.event, modifiers]` in which modifiers is a string which 14 | can include `⇧`, `⌃`, `⌥`, `⌘'`. 15 | 16 | Keystroke events on input areas (``, `