Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Rename for clarity.

d3_geo_cut is now d3_geo_clipAntimeridian.
d3_geo_circleClip is now d3_geo_clipCircle.
  • Loading branch information...
commit 75be0ab2727a1600534a2988b4377ee24178f152 1 parent a2753ee
Mike Bostock authored December 15, 2012
5  Makefile
@@ -183,8 +183,6 @@ d3.geo.js: \
183 183
 	src/geo/stream.js \
184 184
 	src/geo/spherical.js \
185 185
 	src/geo/cartesian.js \
186  
-	src/geo/clip.js \
187  
-	src/geo/cut.js \
188 186
 	src/geo/resample.js \
189 187
 	src/geo/albers-usa.js \
190 188
 	src/geo/albers.js \
@@ -193,6 +191,9 @@ d3.geo.js: \
193 191
 	src/geo/bounds.js \
194 192
 	src/geo/centroid.js \
195 193
 	src/geo/circle.js \
  194
+	src/geo/clip.js \
  195
+	src/geo/clip-antimeridian.js \
  196
+	src/geo/clip-circle.js \
196 197
 	src/geo/compose.js \
197 198
 	src/geo/equirectangular.js \
198 199
 	src/geo/gnomonic.js \
983  d3.js
@@ -5427,373 +5427,86 @@
5427 5427
     d[1] /= l;
5428 5428
     d[2] /= l;
5429 5429
   }
5430  
-  function d3_geo_clip(pointVisible, clipLine, interpolate) {
5431  
-    return function(listener) {
5432  
-      var line = clipLine(listener);
5433  
-      var clip = {
5434  
-        point: point,
  5430
+  function d3_geo_resample(projectPoint) {
  5431
+    var δ2 = .5, maxDepth = 16;
  5432
+    function resample(listener) {
  5433
+      var resample = {
  5434
+        point: resamplePoint,
5435 5435
         lineStart: lineStart,
5436 5436
         lineEnd: lineEnd,
5437 5437
         polygonStart: function() {
5438  
-          clip.point = pointRing;
5439  
-          clip.lineStart = ringStart;
5440  
-          clip.lineEnd = ringEnd;
5441  
-          invisible = false;
5442  
-          invisibleArea = visibleArea = 0;
5443  
-          segments = [];
5444 5438
           listener.polygonStart();
  5439
+          resample.lineStart = ringStart;
5445 5440
         },
5446 5441
         polygonEnd: function() {
5447  
-          clip.point = point;
5448  
-          clip.lineStart = lineStart;
5449  
-          clip.lineEnd = lineEnd;
5450  
-          segments = d3.merge(segments);
5451  
-          if (segments.length) {
5452  
-            d3_geo_clipPolygon(segments, interpolate, listener);
5453  
-          } else if (visibleArea < -ε2 || invisible && invisibleArea < -ε2) {
5454  
-            listener.lineStart();
5455  
-            interpolate(null, null, 1, listener);
5456  
-            listener.lineEnd();
5457  
-          }
5458 5442
           listener.polygonEnd();
5459  
-          segments = null;
5460 5443
         },
5461  
-        sphere: function() {
5462  
-          listener.polygonStart();
5463  
-          listener.lineStart();
5464  
-          interpolate(null, null, 1, listener);
5465  
-          listener.lineEnd();
5466  
-          listener.polygonEnd();
5467  
-        }
  5444
+        sphere: d3_noop
5468 5445
       };
5469  
-      function point(λ, φ) {
5470  
-        if (pointVisible(λ, φ)) listener.point(λ, φ);
5471  
-      }
5472  
-      function pointLine(λ, φ) {
5473  
-        line.point(λ, φ);
5474  
-      }
5475 5446
       function lineStart() {
5476  
-        clip.point = pointLine;
5477  
-        line.lineStart();
  5447
+        x0 = NaN;
  5448
+        resample.point = resamplePointLine;
  5449
+        listener.lineStart();
5478 5450
       }
5479 5451
       function lineEnd() {
5480  
-        clip.point = point;
5481  
-        line.lineEnd();
5482  
-      }
5483  
-      var segments, visibleArea, invisibleArea, invisible;
5484  
-      var buffer = d3_geo_clipBufferListener(), ringListener = clipLine(buffer), ring;
5485  
-      function pointRing(λ, φ) {
5486  
-        ringListener.point(λ, φ);
5487  
-        ring.push([ λ, φ ]);
  5452
+        resample.point = resamplePoint;
  5453
+        listener.lineEnd();
5488 5454
       }
5489 5455
       function ringStart() {
5490  
-        ringListener.lineStart();
5491  
-        ring = [];
  5456
+        var λ00, φ00;
  5457
+        lineStart();
  5458
+        resample.point = function(λ, φ) {
  5459
+          resamplePointLine(λ00 = λ, φ00 = φ);
  5460
+          resample.point = resamplePointLine;
  5461
+        };
  5462
+        resample.lineEnd = function() {
  5463
+          var cartesian = d3_geo_cartesian([ λ00, φ00 ]), p = projectPoint(λ00, φ00);
  5464
+          resampleLineTo(x0, y0, λ0, a0, b0, c0, p[0], p[1], λ00, cartesian[0], cartesian[1], cartesian[2], maxDepth, listener);
  5465
+          resample.lineEnd = lineEnd;
  5466
+          lineEnd();
  5467
+        };
5492 5468
       }
5493  
-      function ringEnd() {
5494  
-        pointRing(ring[0][0], ring[0][1]);
5495  
-        ringListener.lineEnd();
5496  
-        var clean = ringListener.clean(), ringSegments = buffer.buffer(), segment, n = ringSegments.length;
5497  
-        if (!n) {
5498  
-          invisible = true;
5499  
-          invisibleArea += d3_geo_clipAreaRing(ring, d3_geo_clipRotationInvisible);
5500  
-          ring = null;
5501  
-          return;
5502  
-        }
5503  
-        ring = null;
5504  
-        if (clean & 1) {
5505  
-          segment = ringSegments[0];
5506  
-          visibleArea += d3_geo_clipAreaRing(segment, d3_geo_clipRotation);
5507  
-          var n = segment.length - 1, i = -1, point;
5508  
-          listener.lineStart();
5509  
-          while (++i < n) listener.point((point = segment[i])[0], point[1]);
5510  
-          listener.lineEnd();
5511  
-          return;
5512  
-        }
5513  
-        if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
5514  
-        segments.push(ringSegments.filter(d3_geo_clipSegmentLength1));
  5469
+      function resamplePoint(λ, φ) {
  5470
+        var point = projectPoint(λ, φ);
  5471
+        listener.point(point[0], point[1]);
5515 5472
       }
5516  
-      return clip;
  5473
+      var λ0, x0, y0, a0, b0, c0;
  5474
+      function resamplePointLine(λ, φ) {
  5475
+        var cartesian = d3_geo_cartesian([ λ, φ ]), p = projectPoint(λ, φ);
  5476
+        resampleLineTo(x0, y0, λ0, a0, b0, c0, x0 = p[0], y0 = p[1], λ0 = λ, a0 = cartesian[0], b0 = cartesian[1], c0 = cartesian[2], maxDepth, listener);
  5477
+        listener.point(x0, y0);
  5478
+      }
  5479
+      return resample;
  5480
+    }
  5481
+    resample.precision = function(_) {
  5482
+      if (!arguments.length) return Math.sqrt(δ2);
  5483
+      maxDepth = (δ2 = _ * _) > 0 && 16;
  5484
+      return resample;
5517 5485
     };
5518  
-  }
5519  
-  function d3_geo_clipPolygon(segments, interpolate, listener) {
5520  
-    var subject = [], clip = [];
5521  
-    segments.forEach(function(segment) {
5522  
-      var n = segment.length;
5523  
-      if (n <= 1) return;
5524  
-      var p0 = segment[0], p1 = segment[n - 1], a = {
5525  
-        point: p0,
5526  
-        points: segment,
5527  
-        other: null,
5528  
-        visited: false,
5529  
-        entry: true,
5530  
-        subject: true
5531  
-      }, b = {
5532  
-        point: p0,
5533  
-        points: [ p0 ],
5534  
-        other: a,
5535  
-        visited: false,
5536  
-        entry: false,
5537  
-        subject: false
5538  
-      };
5539  
-      a.other = b;
5540  
-      subject.push(a);
5541  
-      clip.push(b);
5542  
-      a = {
5543  
-        point: p1,
5544  
-        points: [ p1 ],
5545  
-        other: null,
5546  
-        visited: false,
5547  
-        entry: false,
5548  
-        subject: true
5549  
-      };
5550  
-      b = {
5551  
-        point: p1,
5552  
-        points: [ p1 ],
5553  
-        other: a,
5554  
-        visited: false,
5555  
-        entry: true,
5556  
-        subject: false
5557  
-      };
5558  
-      a.other = b;
5559  
-      subject.push(a);
5560  
-      clip.push(b);
5561  
-    });
5562  
-    clip.sort(d3_geo_clipSort);
5563  
-    d3_geo_clipLinkCircular(subject);
5564  
-    d3_geo_clipLinkCircular(clip);
5565  
-    if (!subject.length) return;
5566  
-    var start = subject[0], current, points, point;
5567  
-    while (1) {
5568  
-      current = start;
5569  
-      while (current.visited) if ((current = current.next) === start) return;
5570  
-      points = current.points;
5571  
-      listener.lineStart();
5572  
-      do {
5573  
-        current.visited = current.other.visited = true;
5574  
-        if (current.entry) {
5575  
-          if (current.subject) {
5576  
-            for (var i = 0; i < points.length; i++) listener.point((point = points[i])[0], point[1]);
5577  
-          } else {
5578  
-            interpolate(current.point, current.next.point, 1, listener);
5579  
-          }
5580  
-          current = current.next;
5581  
-        } else {
5582  
-          if (current.subject) {
5583  
-            points = current.prev.points;
5584  
-            for (var i = points.length; --i >= 0; ) listener.point((point = points[i])[0], point[1]);
5585  
-          } else {
5586  
-            interpolate(current.point, current.prev.point, -1, listener);
5587  
-          }
5588  
-          current = current.prev;
  5486
+    return resample;
  5487
+    function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, listener) {
  5488
+      var dx = x1 - x0, dy = y1 - y0, distance2 = dx * dx + dy * dy;
  5489
+      if (distance2 > 4 * δ2 && depth--) {
  5490
+        var a = a0 + a1, b = b0 + b1, c = c0 + c1, m = Math.sqrt(a * a + b * b + c * c), φ2 = Math.asin(c /= m), λ2 = Math.abs(Math.abs(c) - 1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a), p = projectPoint(λ2, φ2), x2 = p[0], y2 = p[1], dx2 = x2 - x0, dy2 = y2 - y0, dz = dy * dx2 - dx * dy2;
  5491
+        if (dz * dz / distance2 > δ2 || Math.abs((dx * dx2 + dy * dy2) / distance2 - .5) > .3) {
  5492
+          resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, listener);
  5493
+          listener.point(x2, y2);
  5494
+          resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, listener);
5589 5495
         }
5590  
-        current = current.other;
5591  
-        points = current.points;
5592  
-      } while (!current.visited);
5593  
-      listener.lineEnd();
  5496
+      }
5594 5497
     }
5595 5498
   }
5596  
-  function d3_geo_clipLinkCircular(array) {
5597  
-    if (!(n = array.length)) return;
5598  
-    var n, i = 0, a = array[0], b;
5599  
-    while (++i < n) {
5600  
-      a.next = b = array[i];
5601  
-      b.prev = a;
5602  
-      a = b;
  5499
+  d3.geo.albersUsa = function() {
  5500
+    var lower48 = d3.geo.albers();
  5501
+    var alaska = d3.geo.albers().rotate([ 160, 0 ]).center([ 0, 60 ]).parallels([ 55, 65 ]);
  5502
+    var hawaii = d3.geo.albers().rotate([ 160, 0 ]).center([ 0, 20 ]).parallels([ 8, 18 ]);
  5503
+    var puertoRico = d3.geo.albers().rotate([ 60, 0 ]).center([ 0, 10 ]).parallels([ 8, 18 ]);
  5504
+    function albersUsa(coordinates) {
  5505
+      return projection(coordinates)(coordinates);
5603 5506
     }
5604  
-    a.next = b = array[0];
5605  
-    b.prev = a;
5606  
-  }
5607  
-  function d3_geo_clipSort(a, b) {
5608  
-    return ((a = a.point)[0] < 0 ? a[1] - π / 2 - ε : π / 2 - a[1]) - ((b = b.point)[0] < 0 ? b[1] - π / 2 - ε : π / 2 - b[1]);
5609  
-  }
5610  
-  function d3_geo_clipSegmentLength1(segment) {
5611  
-    return segment.length > 1;
5612  
-  }
5613  
-  function d3_geo_clipBufferListener() {
5614  
-    var lines = [], line;
5615  
-    return {
5616  
-      lineStart: function() {
5617  
-        lines.push(line = []);
5618  
-      },
5619  
-      point: function(λ, φ) {
5620  
-        line.push([ λ, φ ]);
5621  
-      },
5622  
-      lineEnd: d3_noop,
5623  
-      buffer: function() {
5624  
-        var buffer = lines;
5625  
-        lines = [];
5626  
-        line = null;
5627  
-        return buffer;
5628  
-      }
5629  
-    };
5630  
-  }
5631  
-  function d3_geo_clipAreaRing(ring, rotate) {
5632  
-    d3_geo_area.polygonStart();
5633  
-    d3_geo_area.lineStart();
5634  
-    for (var i = 0, n = ring.length, p; i < n; ++i) {
5635  
-      p = rotate(ring[i]);
5636  
-      d3_geo_area.point(p[0] * d3_degrees, p[1] * d3_degrees);
5637  
-    }
5638  
-    d3_geo_area.lineEnd();
5639  
-    d3_geo_area.polygonEnd();
5640  
-    return d3_geo_areaRing;
5641  
-  }
5642  
-  function d3_geo_clipRotation(point) {
5643  
-    var λ = point[0], φ = point[1], cosφ = Math.cos(φ);
5644  
-    return [ Math.atan2(Math.sin(λ) * cosφ, Math.sin(φ)), Math.asin(Math.max(-1, Math.min(1, -Math.cos(λ) * cosφ))) ];
5645  
-  }
5646  
-  function d3_geo_clipRotationInvisible(point) {
5647  
-    var λ = point[0] + π, φ = point[1], cosφ = Math.cos(φ);
5648  
-    return [ Math.atan2(Math.sin(λ) * cosφ, Math.sin(φ)), Math.asin(Math.max(-1, Math.min(1, -Math.cos(λ) * cosφ))) ];
5649  
-  }
5650  
-  var d3_geo_cut = d3_geo_clip(d3_true, d3_geo_cutLine, d3_geo_cutInterpolate);
5651  
-  function d3_geo_cutLine(listener) {
5652  
-    var λ0 = NaN, φ0 = NaN, sλ0 = NaN, clean;
5653  
-    return {
5654  
-      lineStart: function() {
5655  
-        listener.lineStart();
5656  
-        clean = 1;
5657  
-      },
5658  
-      point: function(λ1, φ1) {
5659  
-        var sλ1 = λ1 > 0 ? π : -π, dλ = Math.abs(λ1 - λ0);
5660  
-        if (Math.abs(dλ - π) < ε) {
5661  
-          listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? π / 2 : -π / 2);
5662  
-          listener.point(sλ0, φ0);
5663  
-          listener.lineEnd();
5664  
-          listener.lineStart();
5665  
-          listener.point(sλ1, φ0);
5666  
-          listener.point(λ1, φ0);
5667  
-          clean = 0;
5668  
-        } else if (sλ0 !== sλ1 && dλ >= π) {
5669  
-          if (Math.abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε;
5670  
-          if (Math.abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε;
5671  
-          φ0 = d3_geo_cutIntersect(λ0, φ0, λ1, φ1);
5672  
-          listener.point(sλ0, φ0);
5673  
-          listener.lineEnd();
5674  
-          listener.lineStart();
5675  
-          listener.point(sλ1, φ0);
5676  
-          clean = 0;
5677  
-        }
5678  
-        listener.point(λ0 = λ1, φ0 = φ1);
5679  
-        sλ0 = sλ1;
5680  
-      },
5681  
-      lineEnd: function() {
5682  
-        listener.lineEnd();
5683  
-        λ0 = φ0 = NaN;
5684  
-      },
5685  
-      clean: function() {
5686  
-        return 2 - clean;
5687  
-      }
5688  
-    };
5689  
-  }
5690  
-  function d3_geo_cutIntersect(λ0, φ0, λ1, φ1) {
5691  
-    var cosφ0, cosφ1, sinλ0_λ1 = Math.sin(λ0 - λ1);
5692  
-    return Math.abs(sinλ0_λ1) > ε ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1) - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0)) / (cosφ0 * cosφ1 * sinλ0_λ1)) : (φ0 + φ1) / 2;
5693  
-  }
5694  
-  function d3_geo_cutInterpolate(from, to, direction, listener) {
5695  
-    var φ;
5696  
-    if (from == null) {
5697  
-      φ = direction * π / 2;
5698  
-      listener.point(-π, φ);
5699  
-      listener.point(0, φ);
5700  
-      listener.point(π, φ);
5701  
-      listener.point(π, 0);
5702  
-      listener.point(π, -φ);
5703  
-      listener.point(0, -φ);
5704  
-      listener.point(-π, -φ);
5705  
-      listener.point(-π, 0);
5706  
-      listener.point(-π, φ);
5707  
-    } else if (Math.abs(from[0] - to[0]) > ε) {
5708  
-      var s = (from[0] < to[0] ? 1 : -1) * π;
5709  
-      φ = direction * s / 2;
5710  
-      listener.point(-s, φ);
5711  
-      listener.point(0, φ);
5712  
-      listener.point(s, φ);
5713  
-    } else {
5714  
-      listener.point(to[0], to[1]);
5715  
-    }
5716  
-  }
5717  
-  function d3_geo_resample(projectPoint) {
5718  
-    var δ2 = .5, maxDepth = 16;
5719  
-    function resample(listener) {
5720  
-      var resample = {
5721  
-        point: resamplePoint,
5722  
-        lineStart: lineStart,
5723  
-        lineEnd: lineEnd,
5724  
-        polygonStart: function() {
5725  
-          listener.polygonStart();
5726  
-          resample.lineStart = ringStart;
5727  
-        },
5728  
-        polygonEnd: function() {
5729  
-          listener.polygonEnd();
5730  
-        },
5731  
-        sphere: d3_noop
5732  
-      };
5733  
-      function lineStart() {
5734  
-        x0 = NaN;
5735  
-        resample.point = resamplePointLine;
5736  
-        listener.lineStart();
5737  
-      }
5738  
-      function lineEnd() {
5739  
-        resample.point = resamplePoint;
5740  
-        listener.lineEnd();
5741  
-      }
5742  
-      function ringStart() {
5743  
-        var λ00, φ00;
5744  
-        lineStart();
5745  
-        resample.point = function(λ, φ) {
5746  
-          resamplePointLine(λ00 = λ, φ00 = φ);
5747  
-          resample.point = resamplePointLine;
5748  
-        };
5749  
-        resample.lineEnd = function() {
5750  
-          var cartesian = d3_geo_cartesian([ λ00, φ00 ]), p = projectPoint(λ00, φ00);
5751  
-          resampleLineTo(x0, y0, λ0, a0, b0, c0, p[0], p[1], λ00, cartesian[0], cartesian[1], cartesian[2], maxDepth, listener);
5752  
-          resample.lineEnd = lineEnd;
5753  
-          lineEnd();
5754  
-        };
5755  
-      }
5756  
-      function resamplePoint(λ, φ) {
5757  
-        var point = projectPoint(λ, φ);
5758  
-        listener.point(point[0], point[1]);
5759  
-      }
5760  
-      var λ0, x0, y0, a0, b0, c0;
5761  
-      function resamplePointLine(λ, φ) {
5762  
-        var cartesian = d3_geo_cartesian([ λ, φ ]), p = projectPoint(λ, φ);
5763  
-        resampleLineTo(x0, y0, λ0, a0, b0, c0, x0 = p[0], y0 = p[1], λ0 = λ, a0 = cartesian[0], b0 = cartesian[1], c0 = cartesian[2], maxDepth, listener);
5764  
-        listener.point(x0, y0);
5765  
-      }
5766  
-      return resample;
5767  
-    }
5768  
-    resample.precision = function(_) {
5769  
-      if (!arguments.length) return Math.sqrt(δ2);
5770  
-      maxDepth = (δ2 = _ * _) > 0 && 16;
5771  
-      return resample;
5772  
-    };
5773  
-    return resample;
5774  
-    function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, listener) {
5775  
-      var dx = x1 - x0, dy = y1 - y0, distance2 = dx * dx + dy * dy;
5776  
-      if (distance2 > 4 * δ2 && depth--) {
5777  
-        var a = a0 + a1, b = b0 + b1, c = c0 + c1, m = Math.sqrt(a * a + b * b + c * c), φ2 = Math.asin(c /= m), λ2 = Math.abs(Math.abs(c) - 1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a), p = projectPoint(λ2, φ2), x2 = p[0], y2 = p[1], dx2 = x2 - x0, dy2 = y2 - y0, dz = dy * dx2 - dx * dy2;
5778  
-        if (dz * dz / distance2 > δ2 || Math.abs((dx * dx2 + dy * dy2) / distance2 - .5) > .3) {
5779  
-          resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, listener);
5780  
-          listener.point(x2, y2);
5781  
-          resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, listener);
5782  
-        }
5783  
-      }
5784  
-    }
5785  
-  }
5786  
-  d3.geo.albersUsa = function() {
5787  
-    var lower48 = d3.geo.albers();
5788  
-    var alaska = d3.geo.albers().rotate([ 160, 0 ]).center([ 0, 60 ]).parallels([ 55, 65 ]);
5789  
-    var hawaii = d3.geo.albers().rotate([ 160, 0 ]).center([ 0, 20 ]).parallels([ 8, 18 ]);
5790  
-    var puertoRico = d3.geo.albers().rotate([ 60, 0 ]).center([ 0, 10 ]).parallels([ 8, 18 ]);
5791  
-    function albersUsa(coordinates) {
5792  
-      return projection(coordinates)(coordinates);
5793  
-    }
5794  
-    function projection(point) {
5795  
-      var lon = point[0], lat = point[1];
5796  
-      return lat > 50 ? alaska : lon < -140 ? hawaii : lat < 21 ? puertoRico : lower48;
  5507
+    function projection(point) {
  5508
+      var lon = point[0], lat = point[1];
  5509
+      return lat > 50 ? alaska : lon < -140 ? hawaii : lat < 21 ? puertoRico : lower48;
5797 5510
     }
5798 5511
     albersUsa.scale = function(x) {
5799 5512
       if (!arguments.length) return lower48.scale();
@@ -5873,127 +5586,438 @@
5873 5586
     function boundPolygonLineEnd() {
5874 5587
       bound.point = bound.lineEnd = d3_noop;
5875 5588
     }
5876  
-    return function(feature) {
5877  
-      y1 = x1 = -(x0 = y0 = Infinity);
5878  
-      d3.geo.stream(feature, projectBound);
5879  
-      return [ [ x0, y0 ], [ x1, y1 ] ];
5880  
-    };
  5589
+    return function(feature) {
  5590
+      y1 = x1 = -(x0 = y0 = Infinity);
  5591
+      d3.geo.stream(feature, projectBound);
  5592
+      return [ [ x0, y0 ], [ x1, y1 ] ];
  5593
+    };
  5594
+  }
  5595
+  d3.geo.centroid = function(object) {
  5596
+    d3_geo_centroidDimension = d3_geo_centroidW = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0;
  5597
+    d3.geo.stream(object, d3_geo_centroid);
  5598
+    var m;
  5599
+    if (d3_geo_centroidW && Math.abs(m = Math.sqrt(d3_geo_centroidX * d3_geo_centroidX + d3_geo_centroidY * d3_geo_centroidY + d3_geo_centroidZ * d3_geo_centroidZ)) > ε) {
  5600
+      return [ Math.atan2(d3_geo_centroidY, d3_geo_centroidX) * d3_degrees, Math.asin(Math.max(-1, Math.min(1, d3_geo_centroidZ / m))) * d3_degrees ];
  5601
+    }
  5602
+  };
  5603
+  var d3_geo_centroidDimension, d3_geo_centroidW, d3_geo_centroidX, d3_geo_centroidY, d3_geo_centroidZ;
  5604
+  var d3_geo_centroid = {
  5605
+    sphere: d3_noop,
  5606
+    point: d3_geo_centroidPoint,
  5607
+    lineStart: d3_geo_centroidLineStart,
  5608
+    lineEnd: d3_geo_centroidLineEnd,
  5609
+    polygonStart: function() {
  5610
+      d3_geo_centroidDimension = 2;
  5611
+      d3_geo_centroid.lineStart = d3_geo_centroidRingStart;
  5612
+    },
  5613
+    polygonEnd: function() {
  5614
+      d3_geo_centroid.lineStart = d3_geo_centroidLineStart;
  5615
+    }
  5616
+  };
  5617
+  function d3_geo_centroidPoint(λ, φ) {
  5618
+    if (d3_geo_centroidDimension) return;
  5619
+    ++d3_geo_centroidW;
  5620
+    λ *= d3_radians;
  5621
+    var cosφ = Math.cos(φ *= d3_radians);
  5622
+    d3_geo_centroidX += (cosφ * Math.cos(λ) - d3_geo_centroidX) / d3_geo_centroidW;
  5623
+    d3_geo_centroidY += (cosφ * Math.sin(λ) - d3_geo_centroidY) / d3_geo_centroidW;
  5624
+    d3_geo_centroidZ += (Math.sin(φ) - d3_geo_centroidZ) / d3_geo_centroidW;
  5625
+  }
  5626
+  function d3_geo_centroidRingStart() {
  5627
+    var λ00, φ00;
  5628
+    if (d3_geo_centroidDimension < 2) {
  5629
+      d3_geo_centroidDimension = 2;
  5630
+      d3_geo_centroidW = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0;
  5631
+    }
  5632
+    d3_geo_centroidDimension = 1;
  5633
+    d3_geo_centroidLineStart();
  5634
+    d3_geo_centroidDimension = 2;
  5635
+    var linePoint = d3_geo_centroid.point;
  5636
+    d3_geo_centroid.point = function(λ, φ) {
  5637
+      linePoint(λ00 = λ, φ00 = φ);
  5638
+    };
  5639
+    d3_geo_centroid.lineEnd = function() {
  5640
+      d3_geo_centroid.point(λ00, φ00);
  5641
+      d3_geo_centroidLineEnd();
  5642
+      d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd;
  5643
+    };
  5644
+  }
  5645
+  function d3_geo_centroidLineStart() {
  5646
+    var x0, y0, z0;
  5647
+    if (d3_geo_centroidDimension !== 1) {
  5648
+      if (d3_geo_centroidDimension < 1) {
  5649
+        d3_geo_centroidDimension = 1;
  5650
+        d3_geo_centroidW = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0;
  5651
+      } else return;
  5652
+    }
  5653
+    d3_geo_centroid.point = function(λ, φ) {
  5654
+      λ *= d3_radians;
  5655
+      var cosφ = Math.cos(φ *= d3_radians);
  5656
+      x0 = cosφ * Math.cos(λ);
  5657
+      y0 = cosφ * Math.sin(λ);
  5658
+      z0 = Math.sin(φ);
  5659
+      d3_geo_centroid.point = nextPoint;
  5660
+    };
  5661
+    function nextPoint(λ, φ) {
  5662
+      λ *= d3_radians;
  5663
+      var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), w = Math.atan2(Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z);
  5664
+      d3_geo_centroidW += w;
  5665
+      d3_geo_centroidX += w * (x0 + (x0 = x));
  5666
+      d3_geo_centroidY += w * (y0 + (y0 = y));
  5667
+      d3_geo_centroidZ += w * (z0 + (z0 = z));
  5668
+    }
  5669
+  }
  5670
+  function d3_geo_centroidLineEnd() {
  5671
+    d3_geo_centroid.point = d3_geo_centroidPoint;
  5672
+  }
  5673
+  d3.geo.circle = function() {
  5674
+    var origin = [ 0, 0 ], angle, precision = 6, rotate, interpolate;
  5675
+    function circle() {
  5676
+      var o = typeof origin === "function" ? origin.apply(this, arguments) : origin;
  5677
+      rotate = d3_geo_rotation(-o[0] * d3_radians, -o[1] * d3_radians, 0);
  5678
+      var ring = [];
  5679
+      interpolate(null, null, 1, {
  5680
+        point: function(λ, φ) {
  5681
+          var point = rotate.invert(λ, φ);
  5682
+          point[0] *= d3_degrees;
  5683
+          point[1] *= d3_degrees;
  5684
+          ring.push(point);
  5685
+        }
  5686
+      });
  5687
+      return {
  5688
+        type: "Polygon",
  5689
+        coordinates: [ ring ]
  5690
+      };
  5691
+    }
  5692
+    circle.origin = function(x) {
  5693
+      if (!arguments.length) return origin;
  5694
+      origin = x;
  5695
+      return circle;
  5696
+    };
  5697
+    circle.angle = function(x) {
  5698
+      if (!arguments.length) return angle;
  5699
+      interpolate = d3_geo_circleInterpolate((angle = +x) * d3_radians, precision * d3_radians);
  5700
+      return circle;
  5701
+    };
  5702
+    circle.precision = function(_) {
  5703
+      if (!arguments.length) return precision;
  5704
+      interpolate = d3_geo_circleInterpolate(angle * d3_radians, (precision = +_) * d3_radians);
  5705
+      return circle;
  5706
+    };
  5707
+    return circle.angle(90);
  5708
+  };
  5709
+  function d3_geo_circleInterpolate(radians, precision) {
  5710
+    var cr = Math.cos(radians), sr = Math.sin(radians);
  5711
+    return function(from, to, direction, listener) {
  5712
+      if (from != null) {
  5713
+        from = d3_geo_circleAngle(cr, from);
  5714
+        to = d3_geo_circleAngle(cr, to);
  5715
+        if (direction > 0 ? from < to : from > to) from += direction * 2 * π;
  5716
+      } else {
  5717
+        from = radians + direction * 2 * π;
  5718
+        to = radians;
  5719
+      }
  5720
+      var point;
  5721
+      for (var step = direction * precision, t = from; direction > 0 ? t > to : t < to; t -= step) {
  5722
+        listener.point((point = d3_geo_spherical([ cr, -sr * Math.cos(t), -sr * Math.sin(t) ]))[0], point[1]);
  5723
+      }
  5724
+    };
  5725
+  }
  5726
+  function d3_geo_circleAngle(cr, point) {
  5727
+    var a = d3_geo_cartesian(point);
  5728
+    a[0] -= cr;
  5729
+    d3_geo_cartesianNormalize(a);
  5730
+    var angle = Math.acos(Math.max(-1, Math.min(1, -a[1])));
  5731
+    return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI);
  5732
+  }
  5733
+  function d3_geo_clip(pointVisible, clipLine, interpolate) {
  5734
+    return function(listener) {
  5735
+      var line = clipLine(listener);
  5736
+      var clip = {
  5737
+        point: point,
  5738
+        lineStart: lineStart,
  5739
+        lineEnd: lineEnd,
  5740
+        polygonStart: function() {
  5741
+          clip.point = pointRing;
  5742
+          clip.lineStart = ringStart;
  5743
+          clip.lineEnd = ringEnd;
  5744
+          invisible = false;
  5745
+          invisibleArea = visibleArea = 0;
  5746
+          segments = [];
  5747
+          listener.polygonStart();
  5748
+        },
  5749
+        polygonEnd: function() {
  5750
+          clip.point = point;
  5751
+          clip.lineStart = lineStart;
  5752
+          clip.lineEnd = lineEnd;
  5753
+          segments = d3.merge(segments);
  5754
+          if (segments.length) {
  5755
+            d3_geo_clipPolygon(segments, interpolate, listener);
  5756
+          } else if (visibleArea < -ε2 || invisible && invisibleArea < -ε2) {
  5757
+            listener.lineStart();
  5758
+            interpolate(null, null, 1, listener);
  5759
+            listener.lineEnd();
  5760
+          }
  5761
+          listener.polygonEnd();
  5762
+          segments = null;
  5763
+        },
  5764
+        sphere: function() {
  5765
+          listener.polygonStart();
  5766
+          listener.lineStart();
  5767
+          interpolate(null, null, 1, listener);
  5768
+          listener.lineEnd();
  5769
+          listener.polygonEnd();
  5770
+        }
  5771
+      };
  5772
+      function point(λ, φ) {
  5773
+        if (pointVisible(λ, φ)) listener.point(λ, φ);
  5774
+      }
  5775
+      function pointLine(λ, φ) {
  5776
+        line.point(λ, φ);
  5777
+      }
  5778
+      function lineStart() {
  5779
+        clip.point = pointLine;
  5780
+        line.lineStart();
  5781
+      }
  5782
+      function lineEnd() {
  5783
+        clip.point = point;
  5784
+        line.lineEnd();
  5785
+      }
  5786
+      var segments, visibleArea, invisibleArea, invisible;
  5787
+      var buffer = d3_geo_clipBufferListener(), ringListener = clipLine(buffer), ring;
  5788
+      function pointRing(λ, φ) {
  5789
+        ringListener.point(λ, φ);
  5790
+        ring.push([ λ, φ ]);
  5791
+      }
  5792
+      function ringStart() {
  5793
+        ringListener.lineStart();
  5794
+        ring = [];
  5795
+      }
  5796
+      function ringEnd() {
  5797
+        pointRing(ring[0][0], ring[0][1]);
  5798
+        ringListener.lineEnd();
  5799
+        var clean = ringListener.clean(), ringSegments = buffer.buffer(), segment, n = ringSegments.length;
  5800
+        if (!n) {
  5801
+          invisible = true;
  5802
+          invisibleArea += d3_geo_clipAreaRing(ring, d3_geo_clipRotationInvisible);
  5803
+          ring = null;
  5804
+          return;
  5805
+        }
  5806
+        ring = null;
  5807
+        if (clean & 1) {
  5808
+          segment = ringSegments[0];
  5809
+          visibleArea += d3_geo_clipAreaRing(segment, d3_geo_clipRotation);
  5810
+          var n = segment.length - 1, i = -1, point;
  5811
+          listener.lineStart();
  5812
+          while (++i < n) listener.point((point = segment[i])[0], point[1]);
  5813
+          listener.lineEnd();
  5814
+          return;
  5815
+        }
  5816
+        if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
  5817
+        segments.push(ringSegments.filter(d3_geo_clipSegmentLength1));
  5818
+      }
  5819
+      return clip;
  5820
+    };
  5821
+  }
  5822
+  function d3_geo_clipPolygon(segments, interpolate, listener) {
  5823
+    var subject = [], clip = [];
  5824
+    segments.forEach(function(segment) {
  5825
+      var n = segment.length;
  5826
+      if (n <= 1) return;
  5827
+      var p0 = segment[0], p1 = segment[n - 1], a = {
  5828
+        point: p0,
  5829
+        points: segment,
  5830
+        other: null,
  5831
+        visited: false,
  5832
+        entry: true,
  5833
+        subject: true
  5834
+      }, b = {
  5835
+        point: p0,
  5836
+        points: [ p0 ],
  5837
+        other: a,
  5838
+        visited: false,
  5839
+        entry: false,
  5840
+        subject: false
  5841
+      };
  5842
+      a.other = b;
  5843
+      subject.push(a);
  5844
+      clip.push(b);
  5845
+      a = {
  5846
+        point: p1,
  5847
+        points: [ p1 ],
  5848
+        other: null,
  5849
+        visited: false,
  5850
+        entry: false,
  5851
+        subject: true
  5852
+      };
  5853
+      b = {
  5854
+        point: p1,
  5855
+        points: [ p1 ],
  5856
+        other: a,
  5857
+        visited: false,
  5858
+        entry: true,
  5859
+        subject: false
  5860
+      };
  5861
+      a.other = b;
  5862
+      subject.push(a);
  5863
+      clip.push(b);
  5864
+    });
  5865
+    clip.sort(d3_geo_clipSort);
  5866
+    d3_geo_clipLinkCircular(subject);
  5867
+    d3_geo_clipLinkCircular(clip);
  5868
+    if (!subject.length) return;
  5869
+    var start = subject[0], current, points, point;
  5870
+    while (1) {
  5871
+      current = start;
  5872
+      while (current.visited) if ((current = current.next) === start) return;
  5873
+      points = current.points;
  5874
+      listener.lineStart();
  5875
+      do {
  5876
+        current.visited = current.other.visited = true;
  5877
+        if (current.entry) {
  5878
+          if (current.subject) {
  5879
+            for (var i = 0; i < points.length; i++) listener.point((point = points[i])[0], point[1]);
  5880
+          } else {
  5881
+            interpolate(current.point, current.next.point, 1, listener);
  5882
+          }
  5883
+          current = current.next;
  5884
+        } else {
  5885
+          if (current.subject) {
  5886
+            points = current.prev.points;
  5887
+            for (var i = points.length; --i >= 0; ) listener.point((point = points[i])[0], point[1]);
  5888
+          } else {
  5889
+            interpolate(current.point, current.prev.point, -1, listener);
  5890
+          }
  5891
+          current = current.prev;
  5892
+        }
  5893
+        current = current.other;
  5894
+        points = current.points;
  5895
+      } while (!current.visited);
  5896
+      listener.lineEnd();
  5897
+    }
5881 5898
   }
5882  
-  d3.geo.centroid = function(object) {
5883  
-    d3_geo_centroidDimension = d3_geo_centroidW = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0;
5884  
-    d3.geo.stream(object, d3_geo_centroid);
5885  
-    var m;
5886  
-    if (d3_geo_centroidW && Math.abs(m = Math.sqrt(d3_geo_centroidX * d3_geo_centroidX + d3_geo_centroidY * d3_geo_centroidY + d3_geo_centroidZ * d3_geo_centroidZ)) > ε) {
5887  
-      return [ Math.atan2(d3_geo_centroidY, d3_geo_centroidX) * d3_degrees, Math.asin(Math.max(-1, Math.min(1, d3_geo_centroidZ / m))) * d3_degrees ];
5888  
-    }
5889  
-  };
5890  
-  var d3_geo_centroidDimension, d3_geo_centroidW, d3_geo_centroidX, d3_geo_centroidY, d3_geo_centroidZ;
5891  
-  var d3_geo_centroid = {
5892  
-    sphere: d3_noop,
5893  
-    point: d3_geo_centroidPoint,
5894  
-    lineStart: d3_geo_centroidLineStart,
5895  
-    lineEnd: d3_geo_centroidLineEnd,
5896  
-    polygonStart: function() {
5897  
-      d3_geo_centroidDimension = 2;
5898  
-      d3_geo_centroid.lineStart = d3_geo_centroidRingStart;
5899  
-    },
5900  
-    polygonEnd: function() {
5901  
-      d3_geo_centroid.lineStart = d3_geo_centroidLineStart;
  5899
+  function d3_geo_clipLinkCircular(array) {
  5900
+    if (!(n = array.length)) return;
  5901
+    var n, i = 0, a = array[0], b;
  5902
+    while (++i < n) {
  5903
+      a.next = b = array[i];
  5904
+      b.prev = a;
  5905
+      a = b;
5902 5906
     }
5903  
-  };
5904  
-  function d3_geo_centroidPoint(λ, φ) {
5905  
-    if (d3_geo_centroidDimension) return;
5906  
-    ++d3_geo_centroidW;
5907  
-    λ *= d3_radians;
5908  
-    var cosφ = Math.cos(φ *= d3_radians);
5909  
-    d3_geo_centroidX += (cosφ * Math.cos(λ) - d3_geo_centroidX) / d3_geo_centroidW;
5910  
-    d3_geo_centroidY += (cosφ * Math.sin(λ) - d3_geo_centroidY) / d3_geo_centroidW;
5911  
-    d3_geo_centroidZ += (Math.sin(φ) - d3_geo_centroidZ) / d3_geo_centroidW;
  5907
+    a.next = b = array[0];
  5908
+    b.prev = a;
5912 5909
   }
5913  
-  function d3_geo_centroidRingStart() {
5914  
-    var λ00, φ00;
5915  
-    if (d3_geo_centroidDimension < 2) {
5916  
-      d3_geo_centroidDimension = 2;
5917  
-      d3_geo_centroidW = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0;
5918  
-    }
5919  
-    d3_geo_centroidDimension = 1;
5920  
-    d3_geo_centroidLineStart();
5921  
-    d3_geo_centroidDimension = 2;
5922  
-    var linePoint = d3_geo_centroid.point;
5923  
-    d3_geo_centroid.point = function(λ, φ) {
5924  
-      linePoint(λ00 = λ, φ00 = φ);
5925  
-    };
5926  
-    d3_geo_centroid.lineEnd = function() {
5927  
-      d3_geo_centroid.point(λ00, φ00);
5928  
-      d3_geo_centroidLineEnd();
5929  
-      d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd;
5930  
-    };
  5910
+  function d3_geo_clipSort(a, b) {
  5911
+    return ((a = a.point)[0] < 0 ? a[1] - π / 2 - ε : π / 2 - a[1]) - ((b = b.point)[0] < 0 ? b[1] - π / 2 - ε : π / 2 - b[1]);
5931 5912
   }
5932  
-  function d3_geo_centroidLineStart() {
5933  
-    var x0, y0, z0;
5934  
-    if (d3_geo_centroidDimension !== 1) {
5935  
-      if (d3_geo_centroidDimension < 1) {
5936  
-        d3_geo_centroidDimension = 1;
5937  
-        d3_geo_centroidW = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0;
5938  
-      } else return;
5939  
-    }
5940  
-    d3_geo_centroid.point = function(λ, φ) {
5941  
-      λ *= d3_radians;
5942  
-      var cosφ = Math.cos(φ *= d3_radians);
5943  
-      x0 = cosφ * Math.cos(λ);
5944  
-      y0 = cosφ * Math.sin(λ);
5945  
-      z0 = Math.sin(φ);
5946  
-      d3_geo_centroid.point = nextPoint;
  5913
+  function d3_geo_clipSegmentLength1(segment) {
  5914
+    return segment.length > 1;
  5915
+  }
  5916
+  function d3_geo_clipBufferListener() {
  5917
+    var lines = [], line;
  5918
+    return {
  5919
+      lineStart: function() {
  5920
+        lines.push(line = []);
  5921
+      },
  5922
+      point: function(λ, φ) {
  5923
+        line.push([ λ, φ ]);
  5924
+      },
  5925
+      lineEnd: d3_noop,
  5926
+      buffer: function() {
  5927
+        var buffer = lines;
  5928
+        lines = [];
  5929
+        line = null;
  5930
+        return buffer;
  5931
+      }
5947 5932
     };
5948  
-    function nextPoint(λ, φ) {
5949  
-      λ *= d3_radians;
5950  
-      var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), w = Math.atan2(Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z);
5951  
-      d3_geo_centroidW += w;
5952  
-      d3_geo_centroidX += w * (x0 + (x0 = x));
5953  
-      d3_geo_centroidY += w * (y0 + (y0 = y));
5954  
-      d3_geo_centroidZ += w * (z0 + (z0 = z));
  5933
+  }
  5934
+  function d3_geo_clipAreaRing(ring, rotate) {
  5935
+    d3_geo_area.polygonStart();
  5936
+    d3_geo_area.lineStart();
  5937
+    for (var i = 0, n = ring.length, p; i < n; ++i) {
  5938
+      p = rotate(ring[i]);
  5939
+      d3_geo_area.point(p[0] * d3_degrees, p[1] * d3_degrees);
5955 5940
     }
  5941
+    d3_geo_area.lineEnd();
  5942
+    d3_geo_area.polygonEnd();
  5943
+    return d3_geo_areaRing;
5956 5944
   }
5957  
-  function d3_geo_centroidLineEnd() {
5958  
-    d3_geo_centroid.point = d3_geo_centroidPoint;
  5945
+  function d3_geo_clipRotation(point) {
  5946
+    var λ = point[0], φ = point[1], cosφ = Math.cos(φ);
  5947
+    return [ Math.atan2(Math.sin(λ) * cosφ, Math.sin(φ)), Math.asin(Math.max(-1, Math.min(1, -Math.cos(λ) * cosφ))) ];
5959 5948
   }
5960  
-  d3.geo.circle = function() {
5961  
-    var origin = [ 0, 0 ], angle, precision = 6, rotate, interpolate;
5962  
-    function circle() {
5963  
-      var o = typeof origin === "function" ? origin.apply(this, arguments) : origin;
5964  
-      rotate = d3_geo_rotation(-o[0] * d3_radians, -o[1] * d3_radians, 0);
5965  
-      var ring = [];
5966  
-      interpolate(null, null, 1, {
5967  
-        point: function(λ, φ) {
5968  
-          var point = rotate.invert(λ, φ);
5969  
-          point[0] *= d3_degrees;
5970  
-          point[1] *= d3_degrees;
5971  
-          ring.push(point);
  5949
+  function d3_geo_clipRotationInvisible(point) {
  5950
+    var λ = point[0] + π, φ = point[1], cosφ = Math.cos(φ);
  5951
+    return [ Math.atan2(Math.sin(λ) * cosφ, Math.sin(φ)), Math.asin(Math.max(-1, Math.min(1, -Math.cos(λ) * cosφ))) ];
  5952
+  }
  5953
+  var d3_geo_clipAntimeridian = d3_geo_clip(d3_true, d3_geo_clipAntimeridianLine, d3_geo_clipAntimeridianInterpolate);
  5954
+  function d3_geo_clipAntimeridianLine(listener) {
  5955
+    var λ0 = NaN, φ0 = NaN, sλ0 = NaN, clean;
  5956
+    return {
  5957
+      lineStart: function() {
  5958
+        listener.lineStart();
  5959
+        clean = 1;
  5960
+      },
  5961
+      point: function(λ1, φ1) {
  5962
+        var sλ1 = λ1 > 0 ? π : -π, dλ = Math.abs(λ1 - λ0);
  5963
+        if (Math.abs(dλ - π) < ε) {
  5964
+          listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? π / 2 : -π / 2);
  5965
+          listener.point(sλ0, φ0);
  5966
+          listener.lineEnd();
  5967
+          listener.lineStart();
  5968
+          listener.point(sλ1, φ0);
  5969
+          listener.point(λ1, φ0);
  5970
+          clean = 0;
  5971
+        } else if (sλ0 !== sλ1 && dλ >= π) {
  5972
+          if (Math.abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε;
  5973
+          if (Math.abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε;
  5974
+          φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1);
  5975
+          listener.point(sλ0, φ0);
  5976
+          listener.lineEnd();
  5977
+          listener.lineStart();
  5978
+          listener.point(sλ1, φ0);
  5979
+          clean = 0;
5972 5980
         }
5973  
-      });
5974  
-      return {
5975  
-        type: "Polygon",
5976  
-        coordinates: [ ring ]
5977  
-      };
5978  
-    }
5979  
-    circle.origin = function(x) {
5980  
-      if (!arguments.length) return origin;
5981  
-      origin = x;
5982  
-      return circle;
5983  
-    };
5984  
-    circle.angle = function(x) {
5985  
-      if (!arguments.length) return angle;
5986  
-      interpolate = d3_geo_circleInterpolate((angle = +x) * d3_radians, precision * d3_radians);
5987  
-      return circle;
5988  
-    };
5989  
-    circle.precision = function(_) {
5990  
-      if (!arguments.length) return precision;
5991  
-      interpolate = d3_geo_circleInterpolate(angle * d3_radians, (precision = +_) * d3_radians);
5992  
-      return circle;
  5981
+        listener.point(λ0 = λ1, φ0 = φ1);
  5982
+        sλ0 = sλ1;
  5983
+      },
  5984
+      lineEnd: function() {
  5985
+        listener.lineEnd();
  5986
+        λ0 = φ0 = NaN;
  5987
+      },
  5988
+      clean: function() {
  5989
+        return 2 - clean;
  5990
+      }
5993 5991
     };
5994  
-    return circle.angle(90);
5995  
-  };
5996  
-  function d3_geo_circleClip(degrees) {
  5992
+  }
  5993
+  function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) {
  5994
+    var cosφ0, cosφ1, sinλ0_λ1 = Math.sin(λ0 - λ1);
  5995
+    return Math.abs(sinλ0_λ1) > ε ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1) - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0)) / (cosφ0 * cosφ1 * sinλ0_λ1)) : (φ0 + φ1) / 2;
  5996
+  }
  5997
+  function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) {
  5998
+    var φ;
  5999
+    if (from == null) {
  6000
+      φ = direction * π / 2;
  6001
+      listener.point(-π, φ);
  6002
+      listener.point(0, φ);
  6003
+      listener.point(π, φ);
  6004
+      listener.point(π, 0);
  6005
+      listener.point(π, -φ);
  6006
+      listener.point(0, -φ);
  6007
+      listener.point(-π, -φ);
  6008
+      listener.point(-π, 0);
  6009
+      listener.point(-π, φ);
  6010
+    } else if (Math.abs(from[0] - to[0]) > ε) {
  6011
+      var s = (from[0] < to[0] ? 1 : -1) * π;
  6012
+      φ = direction * s / 2;
  6013
+      listener.point(-s, φ);
  6014
+      listener.point(0, φ);
  6015
+      listener.point(s, φ);
  6016
+    } else {
  6017
+      listener.point(to[0], to[1]);
  6018
+    }
  6019
+  }
  6020
+  function d3_geo_clipCircle(degrees) {
5997 6021
     var radians = degrees * d3_radians, cr = Math.cos(radians), interpolate = d3_geo_circleInterpolate(radians, 6 * d3_radians);
5998 6022
     return d3_geo_clip(visible, clipLine, interpolate);
5999 6023
     function visible(λ, φ) {
@@ -6053,30 +6077,6 @@
6053 6077
       return d3_geo_spherical(q);
6054 6078
     }
6055 6079
   }
6056  
-  function d3_geo_circleInterpolate(radians, precision) {
6057  
-    var cr = Math.cos(radians), sr = Math.sin(radians);
6058  
-    return function(from, to, direction, listener) {
6059  
-      if (from != null) {
6060  
-        from = d3_geo_circleAngle(cr, from);
6061  
-        to = d3_geo_circleAngle(cr, to);
6062  
-        if (direction > 0 ? from < to : from > to) from += direction * 2 * π;
6063  
-      } else {
6064  
-        from = radians + direction * 2 * π;
6065  
-        to = radians;
6066  
-      }
6067  
-      var point;
6068  
-      for (var step = direction * precision, t = from; direction > 0 ? t > to : t < to; t -= step) {
6069  
-        listener.point((point = d3_geo_spherical([ cr, -sr * Math.cos(t), -sr * Math.sin(t) ]))[0], point[1]);
6070  
-      }
6071  
-    };
6072  
-  }
6073  
-  function d3_geo_circleAngle(cr, point) {
6074  
-    var a = d3_geo_cartesian(point);
6075  
-    a[0] -= cr;
6076  
-    d3_geo_cartesianNormalize(a);
6077  
-    var angle = Math.acos(Math.max(-1, Math.min(1, -a[1])));
6078  
-    return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI);
6079  
-  }
6080 6080
   function d3_geo_compose(a, b) {
6081 6081
     if (a === d3_geo_equirectangular) return b;
6082 6082
     if (b === d3_geo_equirectangular) return a;
@@ -6518,7 +6518,10 @@
6518 6518
     })();
6519 6519
   }
6520 6520
   function d3_geo_projectionMutator(projectAt) {
6521  
-    var project, projectRotate, rotate, k = 150, x = 480, y = 250, λ = 0, φ = 0, δλ = 0, δφ = 0, δγ = 0, δx, δy, clip = d3_geo_cut, clipAngle = null, projectResample = d3_geo_resample(projectPoint);
  6521
+    var project, rotate, projectRotate, projectResample = d3_geo_resample(function(x, y) {
  6522
+      x = project(x, y);
  6523
+      return [ x[0] * k + δx, δy - x[1] * k ];
  6524
+    }), k = 150, x = 480, y = 250, λ = 0, φ = 0, δλ = 0, δφ = 0, δγ = 0, δx, δy, clip = d3_geo_clipAntimeridian, clipAngle = null;
6522 6525
     function projection(coordinates) {
6523 6526
       coordinates = projectRotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
6524 6527
       return [ coordinates[0] * k + δx, δy - coordinates[1] * k ];
@@ -6527,12 +6530,12 @@
6527 6530
       coordinates = projectRotate.invert((coordinates[0] - δx) / k, (δy - coordinates[1]) / k);
6528 6531
       return [ coordinates[0] * d3_degrees, coordinates[1] * d3_degrees ];
6529 6532
     }
6530  
-    projection.stream = function(listener) {
6531  
-      return d3_geo_projectionRadiansRotate(rotate, clip(projectResample(listener)));
  6533
+    projection.stream = function(stream) {
  6534
+      return d3_geo_projectionRadiansRotate(rotate, clip(projectResample(stream)));
6532 6535
     };
6533 6536
     projection.clipAngle = function(_) {
6534 6537
       if (!arguments.length) return clipAngle;
6535  
-      clip = _ == null ? (clipAngle = _, d3_geo_cut) : d3_geo_circleClip(clipAngle = +_);
  6538
+      clip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle(clipAngle = +_);
6536 6539
       return projection;
6537 6540
     };
6538 6541
     projection.scale = function(_) {
@@ -6567,10 +6570,6 @@
6567 6570
       δy = y + center[1] * k;
6568 6571
       return projection;
6569 6572
     }
6570  
-    function projectPoint(λ, φ) {
6571  
-      var point = project(λ, φ);
6572  
-      return [ point[0] * k + δx, δy - point[1] * k ];
6573  
-    }
6574 6573
     return function() {
6575 6574
       project = projectAt.apply(this, arguments);
6576 6575
       projection.invert = project.invert && invert;
@@ -6579,9 +6578,9 @@
6579 6578
   }
6580 6579
   function d3_geo_projectionRadiansRotate(rotate, stream) {
6581 6580
     return {
6582  
-      point: function(λ, φ) {
6583  
-        var p = rotate(λ * d3_radians, φ * d3_radians);
6584  
-        stream.point(p[0], p[1]);
  6581
+      point: function(x, y) {
  6582
+        x = rotate(x * d3_radians, y * d3_radians);
  6583
+        stream.point(x[0], x[1]);
6585 6584
       },
6586 6585
       sphere: function() {
6587 6586
         stream.sphere();
6  d3.min.js
3 additions, 3 deletions not shown
102  src/geo/circle.js
@@ -42,108 +42,6 @@ d3.geo.circle = function() {
42 42
   return circle.angle(90);
43 43
 };
44 44
 
45  
-// Clip features against a circle centered at [0°, 0°], with a given radius.
46  
-function d3_geo_circleClip(degrees) {
47  
-  var radians = degrees * d3_radians,
48  
-      cr = Math.cos(radians),
49  
-      interpolate = d3_geo_circleInterpolate(radians, 6 * d3_radians);
50  
-
51  
-  return d3_geo_clip(visible, clipLine, interpolate);
52  
-
53  
-  function visible(λ, φ) {
54  
-    return Math.cos(λ) * Math.cos(φ) > cr;
55  
-  }
56  
-
57  
-  // TODO handle two invisible endpoints with visible intermediate segment.
58  
-  // Takes a line and cuts into visible segments. Return values used for
59  
-  // polygon clipping:
60  
-  //   0: there were intersections or the line was empty.
61  
-  //   1: no intersections.
62  
-  //   2: there were intersections, and the first and last segments should be
63  
-  //      rejoined.
64  
-  function clipLine(listener) {
65  
-    var point0,
66  
-        v0,
67  
-        v00,
68  
-        clean; // no intersections
69  
-    return {
70  
-      lineStart: function() {
71  
-        v00 = v0 = false;
72  
-        clean = 1;
73  
-      },
74  
-      point: function(λ, φ) {
75  
-        var point1 = [λ, φ],
76  
-            point2,
77  
-            v = visible(λ, φ);
78  
-        if (!point0 && (v00 = v0 = v)) listener.lineStart();
79  
-        // handle degeneracies
80  
-        if (v !== v0) {
81  
-          point2 = intersect(point0, point1);
82  
-          if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) {
83  
-            point1[0] += ε;
84  
-            point1[1] += ε;
85  
-            v = visible(point1[0], point1[1]);
86  
-          }
87  
-        }
88  
-        if (v !== v0) {
89  
-          clean = 0;
90  
-          if (v0 = v) {
91  
-            // outside going in
92  
-            listener.lineStart();
93  
-            point2 = intersect(point1, point0);
94  
-            listener.point(point2[0], point2[1]);
95  
-          } else {
96  
-            // inside going out
97  
-            point2 = intersect(point0, point1);
98  
-            listener.point(point2[0], point2[1]);
99  
-            listener.lineEnd();
100  
-          }
101  
-          point0 = point2;
102  
-        }
103  
-        if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) listener.point(point1[0], point1[1]);
104  
-        point0 = point1;
105  
-      },
106  
-      lineEnd: function() {
107  
-        if (v0) listener.lineEnd();
108  
-        point0 = null;
109  
-      },
110  
-      // Rejoin first and last segments if there were intersections and the first
111  
-      // and last points were visible.
112  
-      clean: function() { return clean | ((v00 && v0) << 1); }
113  
-    };
114  
-  }
115  
-
116  
-  // Intersects the great circle between a and b with the clip circle.
117  
-  function intersect(a, b) {
118  
-    var pa = d3_geo_cartesian(a, 0),
119  
-        pb = d3_geo_cartesian(b, 0);
120  
-    // We have two planes, n1.p = d1 and n2.p = d2.
121  
-    // Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 x n2).
122  
-    var n1 = [1, 0, 0], // normal
123  
-        n2 = d3_geo_cartesianCross(pa, pb),
124  
-        n2n2 = d3_geo_cartesianDot(n2, n2),
125  
-        n1n2 = n2[0], // d3_geo_cartesianDot(n1, n2),
126  
-        determinant = n2n2 - n1n2 * n1n2;
127  
-    // Two polar points.
128  
-    if (!determinant) return a;
129  
-
130  
-    var c1 =  cr * n2n2 / determinant,
131  
-        c2 = -cr * n1n2 / determinant,
132  
-        n1xn2 = d3_geo_cartesianCross(n1, n2),
133  
-        A = d3_geo_cartesianScale(n1, c1),
134  
-        B = d3_geo_cartesianScale(n2, c2);
135  
-    d3_geo_cartesianAdd(A, B);
136  
-    // Now solve |p(t)|^2 = 1.
137  
-    var u = n1xn2,
138  
-        w = d3_geo_cartesianDot(A, u),
139  
-        uu = d3_geo_cartesianDot(u, u),
140  
-        t = Math.sqrt(w * w - uu * (d3_geo_cartesianDot(A, A) - 1)),
141  
-        q = d3_geo_cartesianScale(u, (-w - t) / uu);
142  
-    d3_geo_cartesianAdd(q, A);
143  
-    return d3_geo_spherical(q);
144  
-  }
145  
-}
146  
-
147 45
 // Interpolates along a circle centered at [0°, 0°], with a given radius and
148 46
 // precision.
149 47
 function d3_geo_circleInterpolate(radians, precision) {
13  src/geo/cut.js → src/geo/clip-antimeridian.js
... ...
@@ -1,12 +1,11 @@
1  
-// Cut features along the antimeridian.
2  
-var d3_geo_cut = d3_geo_clip(d3_true, d3_geo_cutLine, d3_geo_cutInterpolate);
  1
+var d3_geo_clipAntimeridian = d3_geo_clip(d3_true, d3_geo_clipAntimeridianLine, d3_geo_clipAntimeridianInterpolate);
3 2
 
4 3
 // Takes a line and cuts into visible segments. Return values:
5 4
 //   0: there were intersections or the line was empty.
6 5
 //   1: no intersections.
7 6
 //   2: there were intersections, and the first and last segments should be
8 7
 //      rejoined.
9  
-function d3_geo_cutLine(listener) {
  8
+function d3_geo_clipAntimeridianLine(listener) {
10 9
   var λ0 = NaN,
11 10
       φ0 = NaN,
12 11
       sλ0 = NaN,
@@ -32,7 +31,7 @@ function d3_geo_cutLine(listener) {
32 31
         // handle degeneracies
33 32
         if (Math.abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε;
34 33
         if (Math.abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε;
35  
-        φ0 = d3_geo_cutIntersect(λ0, φ0, λ1, φ1);
  34
+        φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1);
36 35
         listener.point(sλ0, φ0);
37 36
         listener.lineEnd();
38 37
         listener.lineStart();
@@ -51,8 +50,7 @@ function d3_geo_cutLine(listener) {
51 50
   };
52 51
 }
53 52
 
54  
-// Intersects a great-circle segment with the antimeridian.
55  
-function d3_geo_cutIntersect(λ0, φ0, λ1, φ1) {
  53
+function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) {
56 54
   var cosφ0,
57 55
       cosφ1,
58 56
       sinλ0_λ1 = Math.sin(λ0 - λ1);
@@ -63,8 +61,7 @@ function d3_geo_cutIntersect(λ0, φ0, λ1, φ1) {
63 61
       : (φ0 + φ1) / 2;
64