| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- import clipBuffer from "./buffer";
- import clipPolygon from "./polygon";
- import {epsilon, halfPi} from "../math";
- import polygonContains from "../polygonContains";
- import {merge} from "d3-array";
- export default function(pointVisible, clipLine, interpolate, start) {
- return function(rotate, sink) {
- var line = clipLine(sink),
- rotatedStart = rotate.invert(start[0], start[1]),
- 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, rotatedStart);
- if (segments.length) {
- if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
- clipPolygon(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) {
- var point = rotate(lambda, phi);
- if (pointVisible(lambda = point[0], phi = point[1])) sink.point(lambda, phi);
- }
- function pointLine(lambda, phi) {
- var point = rotate(lambda, phi);
- line.point(point[0], point[1]);
- }
- function lineStart() {
- clip.point = pointLine;
- line.lineStart();
- }
- function lineEnd() {
- clip.point = point;
- line.lineEnd();
- }
- function pointRing(lambda, phi) {
- ring.push([lambda, phi]);
- var point = rotate(lambda, phi);
- ringSink.point(point[0], point[1]);
- }
- 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]);
- }
|