| import clipBuffer from "./buffer.js"; |
| import clipRejoin from "./rejoin.js"; |
| import {epsilon, halfPi} from "../math.js"; |
| import polygonContains from "../polygonContains.js"; |
| import {merge} from "d3-array"; |
|
|
| export default function(pointVisible, clipLine, interpolate, start) { |
| return function(sink) { |
| var line = clipLine(sink), |
| ringBuffer = clipBuffer(), |
| ringSink = clipLine(ringBuffer), |
| polygonStarted = false, |
| polygon, |
| segments, |
| ring; |
|
|
| var clip = { |
| point: point, |
| lineStart: lineStart, |
| lineEnd: lineEnd, |
| polygonStart: function() { |
| clip.point = pointRing; |
| clip.lineStart = ringStart; |
| clip.lineEnd = ringEnd; |
| segments = []; |
| polygon = []; |
| }, |
| polygonEnd: function() { |
| clip.point = point; |
| clip.lineStart = lineStart; |
| clip.lineEnd = lineEnd; |
| segments = merge(segments); |
| var startInside = polygonContains(polygon, start); |
| if (segments.length) { |
| if (!polygonStarted) sink.polygonStart(), polygonStarted = true; |
| clipRejoin(segments, compareIntersection, startInside, interpolate, sink); |
| } else if (startInside) { |
| if (!polygonStarted) sink.polygonStart(), polygonStarted = true; |
| sink.lineStart(); |
| interpolate(null, null, 1, sink); |
| sink.lineEnd(); |
| } |
| if (polygonStarted) sink.polygonEnd(), polygonStarted = false; |
| segments = polygon = null; |
| }, |
| sphere: function() { |
| sink.polygonStart(); |
| sink.lineStart(); |
| interpolate(null, null, 1, sink); |
| sink.lineEnd(); |
| sink.polygonEnd(); |
| } |
| }; |
|
|
| function point(lambda, phi) { |
| if (pointVisible(lambda, phi)) sink.point(lambda, phi); |
| } |
|
|
| function pointLine(lambda, phi) { |
| line.point(lambda, phi); |
| } |
|
|
| function lineStart() { |
| clip.point = pointLine; |
| line.lineStart(); |
| } |
|
|
| function lineEnd() { |
| clip.point = point; |
| line.lineEnd(); |
| } |
|
|
| function pointRing(lambda, phi) { |
| ring.push([lambda, phi]); |
| ringSink.point(lambda, phi); |
| } |
|
|
| function ringStart() { |
| ringSink.lineStart(); |
| ring = []; |
| } |
|
|
| function ringEnd() { |
| pointRing(ring[0][0], ring[0][1]); |
| ringSink.lineEnd(); |
|
|
| var clean = ringSink.clean(), |
| ringSegments = ringBuffer.result(), |
| i, n = ringSegments.length, m, |
| segment, |
| point; |
|
|
| ring.pop(); |
| polygon.push(ring); |
| ring = null; |
|
|
| if (!n) return; |
|
|
| |
| if (clean & 1) { |
| segment = ringSegments[0]; |
| if ((m = segment.length - 1) > 0) { |
| if (!polygonStarted) sink.polygonStart(), polygonStarted = true; |
| sink.lineStart(); |
| for (i = 0; i < m; ++i) sink.point((point = segment[i])[0], point[1]); |
| sink.lineEnd(); |
| } |
| return; |
| } |
|
|
| |
| |
| if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift())); |
|
|
| segments.push(ringSegments.filter(validSegment)); |
| } |
|
|
| return clip; |
| }; |
| } |
|
|
| function validSegment(segment) { |
| return segment.length > 1; |
| } |
|
|
| |
| |
| function compareIntersection(a, b) { |
| return ((a = a.x)[0] < 0 ? a[1] - halfPi - epsilon : halfPi - a[1]) |
| - ((b = b.x)[0] < 0 ? b[1] - halfPi - epsilon : halfPi - b[1]); |
| } |
|
|