index.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import clipBuffer from "./buffer";
  2. import clipPolygon from "./polygon";
  3. import {epsilon, halfPi} from "../math";
  4. import polygonContains from "../polygonContains";
  5. import {merge} from "d3-array";
  6. export default function(pointVisible, clipLine, interpolate, start) {
  7. return function(rotate, sink) {
  8. var line = clipLine(sink),
  9. rotatedStart = rotate.invert(start[0], start[1]),
  10. ringBuffer = clipBuffer(),
  11. ringSink = clipLine(ringBuffer),
  12. polygonStarted = false,
  13. polygon,
  14. segments,
  15. ring;
  16. var clip = {
  17. point: point,
  18. lineStart: lineStart,
  19. lineEnd: lineEnd,
  20. polygonStart: function() {
  21. clip.point = pointRing;
  22. clip.lineStart = ringStart;
  23. clip.lineEnd = ringEnd;
  24. segments = [];
  25. polygon = [];
  26. },
  27. polygonEnd: function() {
  28. clip.point = point;
  29. clip.lineStart = lineStart;
  30. clip.lineEnd = lineEnd;
  31. segments = merge(segments);
  32. var startInside = polygonContains(polygon, rotatedStart);
  33. if (segments.length) {
  34. if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
  35. clipPolygon(segments, compareIntersection, startInside, interpolate, sink);
  36. } else if (startInside) {
  37. if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
  38. sink.lineStart();
  39. interpolate(null, null, 1, sink);
  40. sink.lineEnd();
  41. }
  42. if (polygonStarted) sink.polygonEnd(), polygonStarted = false;
  43. segments = polygon = null;
  44. },
  45. sphere: function() {
  46. sink.polygonStart();
  47. sink.lineStart();
  48. interpolate(null, null, 1, sink);
  49. sink.lineEnd();
  50. sink.polygonEnd();
  51. }
  52. };
  53. function point(lambda, phi) {
  54. var point = rotate(lambda, phi);
  55. if (pointVisible(lambda = point[0], phi = point[1])) sink.point(lambda, phi);
  56. }
  57. function pointLine(lambda, phi) {
  58. var point = rotate(lambda, phi);
  59. line.point(point[0], point[1]);
  60. }
  61. function lineStart() {
  62. clip.point = pointLine;
  63. line.lineStart();
  64. }
  65. function lineEnd() {
  66. clip.point = point;
  67. line.lineEnd();
  68. }
  69. function pointRing(lambda, phi) {
  70. ring.push([lambda, phi]);
  71. var point = rotate(lambda, phi);
  72. ringSink.point(point[0], point[1]);
  73. }
  74. function ringStart() {
  75. ringSink.lineStart();
  76. ring = [];
  77. }
  78. function ringEnd() {
  79. pointRing(ring[0][0], ring[0][1]);
  80. ringSink.lineEnd();
  81. var clean = ringSink.clean(),
  82. ringSegments = ringBuffer.result(),
  83. i, n = ringSegments.length, m,
  84. segment,
  85. point;
  86. ring.pop();
  87. polygon.push(ring);
  88. ring = null;
  89. if (!n) return;
  90. // No intersections.
  91. if (clean & 1) {
  92. segment = ringSegments[0];
  93. if ((m = segment.length - 1) > 0) {
  94. if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
  95. sink.lineStart();
  96. for (i = 0; i < m; ++i) sink.point((point = segment[i])[0], point[1]);
  97. sink.lineEnd();
  98. }
  99. return;
  100. }
  101. // Rejoin connected segments.
  102. // TODO reuse ringBuffer.rejoin()?
  103. if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
  104. segments.push(ringSegments.filter(validSegment));
  105. }
  106. return clip;
  107. };
  108. }
  109. function validSegment(segment) {
  110. return segment.length > 1;
  111. }
  112. // Intersections are sorted along the clip edge. For both antimeridian cutting
  113. // and circle clipping, the same comparison is used.
  114. function compareIntersection(a, b) {
  115. return ((a = a.x)[0] < 0 ? a[1] - halfPi - epsilon : halfPi - a[1])
  116. - ((b = b.x)[0] < 0 ? b[1] - halfPi - epsilon : halfPi - b[1]);
  117. }