d3js-plugins-rails 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ })();