d3js-plugins-rails 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (152) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE +22 -0
  4. data/README.md +28 -0
  5. data/Rakefile +1 -0
  6. data/d3js-plugins-rails.gemspec +21 -0
  7. data/lib/d3js-plugins-rails/version.rb +7 -0
  8. data/lib/d3js-plugins-rails.rb +10 -0
  9. data/vendor/assets/javascripts/d3/plugins/.gitignore +1 -0
  10. data/vendor/assets/javascripts/d3/plugins/LICENSE +26 -0
  11. data/vendor/assets/javascripts/d3/plugins/Makefile +87 -0
  12. data/vendor/assets/javascripts/d3/plugins/README.md +19 -0
  13. data/vendor/assets/javascripts/d3/plugins/box/box.js +301 -0
  14. data/vendor/assets/javascripts/d3/plugins/bullet/README.md +14 -0
  15. data/vendor/assets/javascripts/d3/plugins/bullet/bullet.js +241 -0
  16. data/vendor/assets/javascripts/d3/plugins/chernoff/README.md +15 -0
  17. data/vendor/assets/javascripts/d3/plugins/chernoff/chernoff.js +176 -0
  18. data/vendor/assets/javascripts/d3/plugins/cie/README.md +54 -0
  19. data/vendor/assets/javascripts/d3/plugins/cie/cie.js +155 -0
  20. data/vendor/assets/javascripts/d3/plugins/fisheye/README.md +43 -0
  21. data/vendor/assets/javascripts/d3/plugins/fisheye/fisheye.js +85 -0
  22. data/vendor/assets/javascripts/d3/plugins/force_labels/README.md +29 -0
  23. data/vendor/assets/javascripts/d3/plugins/force_labels/force_labels.js +56 -0
  24. data/vendor/assets/javascripts/d3/plugins/geo/polyhedron/README.md +7 -0
  25. data/vendor/assets/javascripts/d3/plugins/geo/polyhedron/polyhedron.js +436 -0
  26. data/vendor/assets/javascripts/d3/plugins/geo/projection/README.md +107 -0
  27. data/vendor/assets/javascripts/d3/plugins/geo/projection/aitoff.js +40 -0
  28. data/vendor/assets/javascripts/d3/plugins/geo/projection/armadillo.js +79 -0
  29. data/vendor/assets/javascripts/d3/plugins/geo/projection/august.js +15 -0
  30. data/vendor/assets/javascripts/d3/plugins/geo/projection/baker.js +28 -0
  31. data/vendor/assets/javascripts/d3/plugins/geo/projection/berghaus.js +60 -0
  32. data/vendor/assets/javascripts/d3/plugins/geo/projection/boggs.js +27 -0
  33. data/vendor/assets/javascripts/d3/plugins/geo/projection/bonne.js +29 -0
  34. data/vendor/assets/javascripts/d3/plugins/geo/projection/bromley.js +5 -0
  35. data/vendor/assets/javascripts/d3/plugins/geo/projection/collignon.js +17 -0
  36. data/vendor/assets/javascripts/d3/plugins/geo/projection/conic-conformal.js +29 -0
  37. data/vendor/assets/javascripts/d3/plugins/geo/projection/conic-equidistant.js +27 -0
  38. data/vendor/assets/javascripts/d3/plugins/geo/projection/craig.js +24 -0
  39. data/vendor/assets/javascripts/d3/plugins/geo/projection/craster.js +18 -0
  40. data/vendor/assets/javascripts/d3/plugins/geo/projection/cylindrical-equal-area.js +23 -0
  41. data/vendor/assets/javascripts/d3/plugins/geo/projection/eckert1.js +18 -0
  42. data/vendor/assets/javascripts/d3/plugins/geo/projection/eckert2.js +17 -0
  43. data/vendor/assets/javascripts/d3/plugins/geo/projection/eckert3.js +17 -0
  44. data/vendor/assets/javascripts/d3/plugins/geo/projection/eckert4.js +24 -0
  45. data/vendor/assets/javascripts/d3/plugins/geo/projection/eckert5.js +17 -0
  46. data/vendor/assets/javascripts/d3/plugins/geo/projection/eckert6.js +22 -0
  47. data/vendor/assets/javascripts/d3/plugins/geo/projection/eisenlohr.js +16 -0
  48. data/vendor/assets/javascripts/d3/plugins/geo/projection/end.js +1 -0
  49. data/vendor/assets/javascripts/d3/plugins/geo/projection/gringorten.js +111 -0
  50. data/vendor/assets/javascripts/d3/plugins/geo/projection/guyou.js +46 -0
  51. data/vendor/assets/javascripts/d3/plugins/geo/projection/hammer-retroazimuthal.js +75 -0
  52. data/vendor/assets/javascripts/d3/plugins/geo/projection/hammer.js +51 -0
  53. data/vendor/assets/javascripts/d3/plugins/geo/projection/hatano.js +25 -0
  54. data/vendor/assets/javascripts/d3/plugins/geo/projection/healpix.js +64 -0
  55. data/vendor/assets/javascripts/d3/plugins/geo/projection/hill.js +64 -0
  56. data/vendor/assets/javascripts/d3/plugins/geo/projection/homolosine.js +17 -0
  57. data/vendor/assets/javascripts/d3/plugins/geo/projection/interrupt.js +111 -0
  58. data/vendor/assets/javascripts/d3/plugins/geo/projection/kavrayskiy7.js +15 -0
  59. data/vendor/assets/javascripts/d3/plugins/geo/projection/lagrange.js +27 -0
  60. data/vendor/assets/javascripts/d3/plugins/geo/projection/larrivee.js +39 -0
  61. data/vendor/assets/javascripts/d3/plugins/geo/projection/laskowski.js +31 -0
  62. data/vendor/assets/javascripts/d3/plugins/geo/projection/littrow.js +21 -0
  63. data/vendor/assets/javascripts/d3/plugins/geo/projection/loximuthal.js +29 -0
  64. data/vendor/assets/javascripts/d3/plugins/geo/projection/miller.js +15 -0
  65. data/vendor/assets/javascripts/d3/plugins/geo/projection/mollweide.js +35 -0
  66. data/vendor/assets/javascripts/d3/plugins/geo/projection/mt-flat-polar-parabolic.js +21 -0
  67. data/vendor/assets/javascripts/d3/plugins/geo/projection/mt-flat-polar-quartic.js +23 -0
  68. data/vendor/assets/javascripts/d3/plugins/geo/projection/mt-flat-polar-sinusoidal.js +25 -0
  69. data/vendor/assets/javascripts/d3/plugins/geo/projection/natural-earth.js +22 -0
  70. data/vendor/assets/javascripts/d3/plugins/geo/projection/nell-hammer.js +20 -0
  71. data/vendor/assets/javascripts/d3/plugins/geo/projection/parallel1.js +12 -0
  72. data/vendor/assets/javascripts/d3/plugins/geo/projection/parallel2.js +13 -0
  73. data/vendor/assets/javascripts/d3/plugins/geo/projection/peirce-quincuncial.js +14 -0
  74. data/vendor/assets/javascripts/d3/plugins/geo/projection/polyconic.js +25 -0
  75. data/vendor/assets/javascripts/d3/plugins/geo/projection/projection.js +29 -0
  76. data/vendor/assets/javascripts/d3/plugins/geo/projection/robinson.js +83 -0
  77. data/vendor/assets/javascripts/d3/plugins/geo/projection/satellite.js +68 -0
  78. data/vendor/assets/javascripts/d3/plugins/geo/projection/sinu-mollweide.js +19 -0
  79. data/vendor/assets/javascripts/d3/plugins/geo/projection/sinusoidal.js +15 -0
  80. data/vendor/assets/javascripts/d3/plugins/geo/projection/start.js +1 -0
  81. data/vendor/assets/javascripts/d3/plugins/geo/projection/test/aitoff-test.js +26 -0
  82. data/vendor/assets/javascripts/d3/plugins/geo/projection/test/armadillo-test.js +28 -0
  83. data/vendor/assets/javascripts/d3/plugins/geo/projection/test/baker-test.js +26 -0
  84. data/vendor/assets/javascripts/d3/plugins/geo/projection/test/boggs-test.js +26 -0
  85. data/vendor/assets/javascripts/d3/plugins/geo/projection/test/bonne-test.js +45 -0
  86. data/vendor/assets/javascripts/d3/plugins/geo/projection/test/craig-test.js +25 -0
  87. data/vendor/assets/javascripts/d3/plugins/geo/projection/test/craster-test.js +25 -0
  88. data/vendor/assets/javascripts/d3/plugins/geo/projection/test/env.js +32 -0
  89. data/vendor/assets/javascripts/d3/plugins/geo/projection/test/equirectangular-test.js +97 -0
  90. data/vendor/assets/javascripts/d3/plugins/geo/projection/test/hammer-test.js +29 -0
  91. data/vendor/assets/javascripts/d3/plugins/geo/projection/test/hatano-test.js +25 -0
  92. data/vendor/assets/javascripts/d3/plugins/geo/projection/test/hill-test.js +26 -0
  93. data/vendor/assets/javascripts/d3/plugins/geo/projection/test/larrivee-test.js +25 -0
  94. data/vendor/assets/javascripts/d3/plugins/geo/projection/test/laskowski-test.js +26 -0
  95. data/vendor/assets/javascripts/d3/plugins/geo/projection/test/littrow-test.js +24 -0
  96. data/vendor/assets/javascripts/d3/plugins/geo/projection/test/loximuthal-test.js +25 -0
  97. data/vendor/assets/javascripts/d3/plugins/geo/projection/test/mollweide-test.js +26 -0
  98. data/vendor/assets/javascripts/d3/plugins/geo/projection/test/mt-flat-polar-parabolic-test.js +25 -0
  99. data/vendor/assets/javascripts/d3/plugins/geo/projection/test/mt-flat-polar-quartic-test.js +25 -0
  100. data/vendor/assets/javascripts/d3/plugins/geo/projection/test/mt-flat-polar-sinusoidal-test.js +25 -0
  101. data/vendor/assets/javascripts/d3/plugins/geo/projection/test/natural-earth-test.js +26 -0
  102. data/vendor/assets/javascripts/d3/plugins/geo/projection/test/quartic-authalic-test.js +0 -0
  103. data/vendor/assets/javascripts/d3/plugins/geo/projection/test/robinson-test.js +26 -0
  104. data/vendor/assets/javascripts/d3/plugins/geo/projection/test/wagner6-test.js +25 -0
  105. data/vendor/assets/javascripts/d3/plugins/geo/projection/test/winkel3-test.js +26 -0
  106. data/vendor/assets/javascripts/d3/plugins/geo/projection/van-der-grinten.js +41 -0
  107. data/vendor/assets/javascripts/d3/plugins/geo/projection/van-der-grinten4.js +22 -0
  108. data/vendor/assets/javascripts/d3/plugins/geo/projection/wagner4.js +9 -0
  109. data/vendor/assets/javascripts/d3/plugins/geo/projection/wagner6.js +15 -0
  110. data/vendor/assets/javascripts/d3/plugins/geo/projection/wagner7.js +22 -0
  111. data/vendor/assets/javascripts/d3/plugins/geo/projection/wiechel.js +14 -0
  112. data/vendor/assets/javascripts/d3/plugins/geo/projection/winkel3.js +40 -0
  113. data/vendor/assets/javascripts/d3/plugins/geo/tile/README.md +6 -0
  114. data/vendor/assets/javascripts/d3/plugins/geo/tile/tile.js +53 -0
  115. data/vendor/assets/javascripts/d3/plugins/geodesic/README.md +3 -0
  116. data/vendor/assets/javascripts/d3/plugins/geodesic/geodesic.js +130 -0
  117. data/vendor/assets/javascripts/d3/plugins/geom/contour/README.md +5 -0
  118. data/vendor/assets/javascripts/d3/plugins/geom/contour/contour.js +72 -0
  119. data/vendor/assets/javascripts/d3/plugins/graph/README.md +148 -0
  120. data/vendor/assets/javascripts/d3/plugins/graph/data/cities-matrix.json +37 -0
  121. data/vendor/assets/javascripts/d3/plugins/graph/data/cities.csv +36 -0
  122. data/vendor/assets/javascripts/d3/plugins/graph/data/miserables.json +338 -0
  123. data/vendor/assets/javascripts/d3/plugins/graph/graph.js +161 -0
  124. data/vendor/assets/javascripts/d3/plugins/graph/index.html +37 -0
  125. data/vendor/assets/javascripts/d3/plugins/hexbin/README.md +58 -0
  126. data/vendor/assets/javascripts/d3/plugins/hexbin/hexbin.js +96 -0
  127. data/vendor/assets/javascripts/d3/plugins/hive/README.md +5 -0
  128. data/vendor/assets/javascripts/d3/plugins/hive/hive.js +80 -0
  129. data/vendor/assets/javascripts/d3/plugins/horizon/README.md +11 -0
  130. data/vendor/assets/javascripts/d3/plugins/horizon/horizon.js +192 -0
  131. data/vendor/assets/javascripts/d3/plugins/interpolate-zoom/README.md +11 -0
  132. data/vendor/assets/javascripts/d3/plugins/interpolate-zoom/interpolate-zoom.js +75 -0
  133. data/vendor/assets/javascripts/d3/plugins/jsonp/README.md +26 -0
  134. data/vendor/assets/javascripts/d3/plugins/jsonp/jsonp.js +25 -0
  135. data/vendor/assets/javascripts/d3/plugins/keybinding/README.md +17 -0
  136. data/vendor/assets/javascripts/d3/plugins/keybinding/keybinding.js +120 -0
  137. data/vendor/assets/javascripts/d3/plugins/longscroll/README.md +10 -0
  138. data/vendor/assets/javascripts/d3/plugins/longscroll/longscroll.js +75 -0
  139. data/vendor/assets/javascripts/d3/plugins/package.json +20 -0
  140. data/vendor/assets/javascripts/d3/plugins/qq/README.md +3 -0
  141. data/vendor/assets/javascripts/d3/plugins/qq/qq.js +249 -0
  142. data/vendor/assets/javascripts/d3/plugins/rollup/README.md +5 -0
  143. data/vendor/assets/javascripts/d3/plugins/rollup/rollup.js +131 -0
  144. data/vendor/assets/javascripts/d3/plugins/sankey/README.md +17 -0
  145. data/vendor/assets/javascripts/d3/plugins/sankey/sankey.js +292 -0
  146. data/vendor/assets/javascripts/d3/plugins/simplify/README.md +6 -0
  147. data/vendor/assets/javascripts/d3/plugins/simplify/simplify-test.js +193 -0
  148. data/vendor/assets/javascripts/d3/plugins/simplify/simplify.js +446 -0
  149. data/vendor/assets/javascripts/d3/plugins/superformula/superformula.js +98 -0
  150. data/vendor/assets/javascripts/d3/plugins/urlencode/urlencode-test.js +68 -0
  151. data/vendor/assets/javascripts/d3/plugins/urlencode/urlencode.js +37 -0
  152. metadata +218 -0
@@ -0,0 +1,54 @@
1
+ # d3.cie
2
+
3
+ A plugin for CIE Lab and LCH color spaces. For an example, see
4
+
5
+ * http://bl.ocks.org/3014589
6
+
7
+ This plugin was incorporated into D3 2.10.0 on August 9, 2012 and can now be used directly as d3.hcl and d3.lab!
8
+
9
+ ## Old Documentation
10
+
11
+ To create a Lab color, use the `d3.cie.lab` constructor. To create an LCH color, use the `d3.cie.lch` constructor. For example, here are two ugly colors:
12
+
13
+ ```js
14
+ var green = d3.cie.lab(46.23, -51.70, 49.90),
15
+ blue = d3.cie.lch(32.30, 133.81, -53.72);
16
+ ```
17
+
18
+ To convert from RGB to Lab (or LCH), simply specify an RGB color to the constructor. You can use the same technique to convert from HSV; D3 first converts the HSV color to RGB, and then then d3.cie plugin converts from RGB to Lab or LCH. The following three statements are equivalent:
19
+
20
+ ```js
21
+ var color = d3.cie.lab("#048F07");
22
+ var color = d3.cie.lab("rgb(4,143,7)");
23
+ var color = d3.cie.lab(d3.rgb(4, 143, 7));
24
+ ```
25
+
26
+ To convert from Lab (or LCH) to RGB, simply stringify the color. For example, you can pass a Lab color to [selection.attr](/mbostock/d3/wiki/Selections#wiki-attr) and [selection.style](/mbostock/d3/wiki/Selections#wiki-attr):
27
+
28
+ ```js
29
+ d3.select("body").style("background", d3.cie.lab(46.23, -51.70, 49.90));
30
+ ```
31
+
32
+ The Lab and LCH color classes support custom [brighter](/mbostock/d3/wiki/Colors#wiki-rgb_brighter) and [darker](/mbostock/d3/wiki/Colors#wiki-rgb_brighter) implementations that only modify the L (*lightness*) channel. This tends to produce better results than the RGB or HSL equivalents.
33
+
34
+ The d3.cie plugin also lets you interpolates in Lab or LCH space. For example:
35
+
36
+ ```js
37
+ var x = d3.scale.linear()
38
+ .domain([0, 100])
39
+ .range(["brown", "steelblue"])
40
+ .interpolate(d3.cie.interpolateLab);
41
+ ```
42
+
43
+ The d3.cie plugin does not support parsing CSS3-style color names for Lab and LCH, so you can't say `d3.rgb("lch(32, 133, -53")`; you must use the d3.cie.lab or d3.cie.lch constructors.
44
+
45
+ ## Thank You
46
+
47
+ Various people contributed and helped in implementing this plugin.
48
+
49
+ * [Jeffery Heer](/jheer)
50
+ * [Justin Cormack](/justincormack)
51
+ * [Alex Gaynor](/alex)
52
+ * [Jacob Rus](/jrus)
53
+
54
+ If you like this, you might also like [Gregor Aisch](/gka)’s [Chroma.js](/gka/chroma.js).
@@ -0,0 +1,155 @@
1
+ (function(d3) {
2
+ var cie = d3.cie = {};
3
+
4
+ cie.lab = function(l, a, b) {
5
+ return arguments.length === 1
6
+ ? (l instanceof Lab ? lab(l.l, l.a, l.b)
7
+ : (l instanceof Lch ? lch_lab(l.l, l.c, l.h)
8
+ : rgb_lab((l = d3.rgb(l)).r, l.g, l.b)))
9
+ : lab(+l, +a, +b);
10
+ };
11
+
12
+ cie.lch = function(l, c, h) {
13
+ return arguments.length === 1
14
+ ? (l instanceof Lch ? lch(l.l, l.c, l.h)
15
+ : (l instanceof Lab ? lab_lch(l.l, l.a, l.b)
16
+ : lab_lch((l = rgb_lab((l = d3.rgb(l)).r, l.g, l.b)).l, l.a, l.b)))
17
+ : lch(+l, +c, +h);
18
+ };
19
+
20
+ cie.interpolateLab = function(a, b) {
21
+ a = cie.lab(a);
22
+ b = cie.lab(b);
23
+ var al = a.l,
24
+ aa = a.a,
25
+ ab = a.b,
26
+ bl = b.l - al,
27
+ ba = b.a - aa,
28
+ bb = b.b - ab;
29
+ return function(t) {
30
+ return lab_rgb(al + bl * t, aa + ba * t, ab + bb * t) + "";
31
+ };
32
+ };
33
+
34
+ cie.interpolateLch = function(a, b) {
35
+ a = cie.lch(a);
36
+ b = cie.lch(b);
37
+ var al = a.l,
38
+ ac = a.c,
39
+ ah = a.h,
40
+ bl = b.l - al,
41
+ bc = b.c - ac,
42
+ bh = b.h - ah;
43
+ if (bh > 180) bh -= 360; else if (bh < -180) bh += 360; // shortest path
44
+ return function(t) {
45
+ return lch_lab(al + bl * t, ac + bc * t, ah + bh * t) + "";
46
+ };
47
+ };
48
+
49
+ function lab(l, a, b) {
50
+ return new Lab(l, a, b);
51
+ }
52
+
53
+ function Lab(l, a, b) {
54
+ this.l = l;
55
+ this.a = a;
56
+ this.b = b;
57
+ }
58
+
59
+ Lab.prototype.brighter = function(k) {
60
+ return lab(Math.min(100, this.l + K * (arguments.length ? k : 1)), this.a, this.b);
61
+ };
62
+
63
+ Lab.prototype.darker = function(k) {
64
+ return lab(Math.max(0, this.l - K * (arguments.length ? k : 1)), this.a, this.b);
65
+ };
66
+
67
+ Lab.prototype.rgb = function() {
68
+ return lab_rgb(this.l, this.a, this.b);
69
+ };
70
+
71
+ Lab.prototype.toString = function() {
72
+ return this.rgb() + "";
73
+ };
74
+
75
+ function lch(l, c, h) {
76
+ return new Lch(l, c, h);
77
+ }
78
+
79
+ function Lch(l, c, h) {
80
+ this.l = l;
81
+ this.c = c;
82
+ this.h = h;
83
+ }
84
+
85
+ Lch.prototype.brighter = function(k) {
86
+ return lch(Math.min(100, this.l + K * (arguments.length ? k : 1)), this.c, this.h);
87
+ };
88
+
89
+ Lch.prototype.darker = function(k) {
90
+ return lch(Math.max(0, this.l - K * (arguments.length ? k : 1)), this.c, this.h);
91
+ };
92
+
93
+ Lch.prototype.rgb = function() {
94
+ return lch_lab(this.l, this.c, this.h).rgb();
95
+ };
96
+
97
+ Lch.prototype.toString = function() {
98
+ return this.rgb() + "";
99
+ };
100
+
101
+ // Corresponds roughly to RGB brighter/darker
102
+ var K = 18;
103
+
104
+ // D65 standard referent
105
+ var X = 0.950470, Y = 1, Z = 1.088830;
106
+
107
+ function lab_rgb(l, a, b) {
108
+ var y = (l + 16) / 116, x = y + a / 500, z = y - b / 200;
109
+ x = lab_xyz(x) * X;
110
+ y = lab_xyz(y) * Y;
111
+ z = lab_xyz(z) * Z;
112
+ return d3.rgb(
113
+ xyz_rgb( 3.2404542 * x - 1.5371385 * y - 0.4985314 * z),
114
+ xyz_rgb(-0.9692660 * x + 1.8760108 * y + 0.0415560 * z),
115
+ xyz_rgb( 0.0556434 * x - 0.2040259 * y + 1.0572252 * z)
116
+ );
117
+ }
118
+
119
+ function rgb_lab(r, g, b) {
120
+ r = rgb_xyz(r);
121
+ g = rgb_xyz(g);
122
+ b = rgb_xyz(b);
123
+ var x = xyz_lab((0.4124564 * r + 0.3575761 * g + 0.1804375 * b) / X),
124
+ y = xyz_lab((0.2126729 * r + 0.7151522 * g + 0.0721750 * b) / Y),
125
+ z = xyz_lab((0.0193339 * r + 0.1191920 * g + 0.9503041 * b) / Z);
126
+ return lab(116 * y - 16, 500 * (x - y), 200 * (y - z));
127
+ }
128
+
129
+ function lab_lch(l, a, b) {
130
+ var c = Math.sqrt(a * a + b * b),
131
+ h = Math.atan2(b, a) / Math.PI * 180;
132
+ return lch(l, c, h);
133
+ }
134
+
135
+ function lch_lab(l, c, h) {
136
+ h = h * Math.PI / 180;
137
+ return lab(l, Math.cos(h) * c, Math.sin(h) * c);
138
+ }
139
+
140
+ function lab_xyz(x) {
141
+ return x > 0.206893034 ? x * x * x : (x - 4 / 29) / 7.787037;
142
+ }
143
+
144
+ function xyz_lab(x) {
145
+ return x > 0.008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29;
146
+ }
147
+
148
+ function xyz_rgb(r) {
149
+ return Math.round(255 * (r <= 0.00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - 0.055));
150
+ }
151
+
152
+ function rgb_xyz(r) {
153
+ return (r /= 255) <= 0.04045 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4);
154
+ }
155
+ })(d3);
@@ -0,0 +1,43 @@
1
+ # Fisheye Distortion
2
+
3
+ Demo: <http://bost.ocks.org/mike/fisheye/>
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.
@@ -0,0 +1,85 @@
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: 1};
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
+ })();
@@ -0,0 +1,29 @@
1
+ # D3 Force Labels v 0.1
2
+
3
+ Demo: <http://bl.ocks.org/1691430/>
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
+ <https://github.com/ZJONSSON>
@@ -0,0 +1,56 @@
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.stop()
49
+ .nodes(nodes)
50
+ .links(links)
51
+ updateAnchor()
52
+ labels.start()
53
+ }
54
+ return labels
55
+ }
56
+ })()
@@ -0,0 +1,7 @@
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/)
@@ -0,0 +1,436 @@
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
+ })();