+131
@@ -0,0 +1,131 @@
|
||||
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;
|
||||
|
||||
// No intersections.
|
||||
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;
|
||||
}
|
||||
|
||||
// Rejoin connected segments.
|
||||
// TODO reuse ringBuffer.rejoin()?
|
||||
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;
|
||||
}
|
||||
|
||||
// Intersections are sorted along the clip edge. For both antimeridian cutting
|
||||
// and circle clipping, the same comparison is used.
|
||||
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]);
|
||||
}
|
||||
Reference in New Issue
Block a user