105 lines
3.0 KiB
JavaScript
105 lines
3.0 KiB
JavaScript
import "../math/trigonometry";
|
|
import "spherical";
|
|
|
|
// General spherical polygon clipping algorithm: takes a polygon, cuts it into
|
|
// visible line segments and rejoins the segments by interpolating along the
|
|
// clip edge.
|
|
function d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener) {
|
|
var subject = [],
|
|
clip = [];
|
|
|
|
segments.forEach(function(segment) {
|
|
if ((n = segment.length - 1) <= 0) return;
|
|
var n, p0 = segment[0], p1 = segment[n];
|
|
|
|
// If the first and last points of a segment are coincident, then treat as
|
|
// a closed ring.
|
|
// TODO if all rings are closed, then the winding order of the exterior
|
|
// ring should be checked.
|
|
if (d3_geo_sphericalEqual(p0, p1)) {
|
|
listener.lineStart();
|
|
for (var i = 0; i < n; ++i) listener.point((p0 = segment[i])[0], p0[1]);
|
|
listener.lineEnd();
|
|
return;
|
|
}
|
|
|
|
var a = new d3_geo_clipPolygonIntersection(p0, segment, null, true),
|
|
b = new d3_geo_clipPolygonIntersection(p0, null, a, false);
|
|
a.o = b;
|
|
subject.push(a);
|
|
clip.push(b);
|
|
a = new d3_geo_clipPolygonIntersection(p1, segment, null, false);
|
|
b = new d3_geo_clipPolygonIntersection(p1, null, a, true);
|
|
a.o = b;
|
|
subject.push(a);
|
|
clip.push(b);
|
|
});
|
|
clip.sort(compare);
|
|
d3_geo_clipPolygonLinkCircular(subject);
|
|
d3_geo_clipPolygonLinkCircular(clip);
|
|
if (!subject.length) return;
|
|
|
|
for (var i = 0, entry = clipStartInside, n = clip.length; i < n; ++i) {
|
|
clip[i].e = entry = !entry;
|
|
}
|
|
|
|
var start = subject[0],
|
|
points,
|
|
point;
|
|
while (1) {
|
|
// Find first unvisited intersection.
|
|
var current = start,
|
|
isSubject = true;
|
|
while (current.v) if ((current = current.n) === start) return;
|
|
points = current.z;
|
|
listener.lineStart();
|
|
do {
|
|
current.v = current.o.v = true;
|
|
if (current.e) {
|
|
if (isSubject) {
|
|
for (var i = 0, n = points.length; i < n; ++i) listener.point((point = points[i])[0], point[1]);
|
|
} else {
|
|
interpolate(current.x, current.n.x, 1, listener);
|
|
}
|
|
current = current.n;
|
|
} else {
|
|
if (isSubject) {
|
|
points = current.p.z;
|
|
for (var i = points.length - 1; i >= 0; --i) listener.point((point = points[i])[0], point[1]);
|
|
} else {
|
|
interpolate(current.x, current.p.x, -1, listener);
|
|
}
|
|
current = current.p;
|
|
}
|
|
current = current.o;
|
|
points = current.z;
|
|
isSubject = !isSubject;
|
|
} while (!current.v);
|
|
listener.lineEnd();
|
|
}
|
|
}
|
|
|
|
function d3_geo_clipPolygonLinkCircular(array) {
|
|
if (!(n = array.length)) return;
|
|
var n,
|
|
i = 0,
|
|
a = array[0],
|
|
b;
|
|
while (++i < n) {
|
|
a.n = b = array[i];
|
|
b.p = a;
|
|
a = b;
|
|
}
|
|
a.n = b = array[0];
|
|
b.p = a;
|
|
}
|
|
|
|
function d3_geo_clipPolygonIntersection(point, points, other, entry) {
|
|
this.x = point;
|
|
this.z = points;
|
|
this.o = other; // another intersection
|
|
this.e = entry; // is an entry?
|
|
this.v = false; // visited
|
|
this.n = this.p = null; // next & previous
|
|
}
|