Commit 31ea7dc0 authored by Jason Davies's avatar Jason Davies
Browse files

Perform winding order check on unrotated polygons.

We remove the rotation step that occurred prior to clipping, so that
clipping processes unrotated polygons.  Note that clip regions are
defined relative to the rotated globe, so clipping now needs a rotation
parameter.

Rather than moving the rotation step so it occurs after clipping, the
rotation occurs as part of clipping, using the rotation parameter to
rotate streamed points.  Alternatively, points could be clipped against
a rotated clip region, and then subsequently rotated as a separate step,
but this seems slightly less efficient.

Fixes #1453.
parent 7bb322f6
......@@ -2709,7 +2709,7 @@ d3 = function() {
b.prev = a;
}
function d3_geo_clip(pointVisible, clipLine, interpolate, polygonContains) {
return function(listener) {
return function(rotate, listener) {
var line = clipLine(listener);
var clip = {
point: point,
......@@ -2730,7 +2730,7 @@ d3 = function() {
segments = d3.merge(segments);
if (segments.length) {
d3_geo_clipPolygon(segments, d3_geo_clipSort, null, interpolate, listener);
} else if (polygonContains(polygon)) {
} else if (polygonContains(rotate.invert, polygon)) {
listener.lineStart();
interpolate(null, null, 1, listener);
listener.lineEnd();
......@@ -2747,10 +2747,12 @@ d3 = function() {
}
};
function point(λ, φ) {
if (pointVisible(λ, φ)) listener.point(λ, φ);
var point = rotate(λ, φ);
if (pointVisible(λ = point[0], φ = point[1])) listener.point(λ, φ);
}
function pointLine(λ, φ) {
line.point(λ, φ);
var point = rotate(λ, φ);
line.point(point[0], point[1]);
}
function lineStart() {
clip.point = pointLine;
......@@ -2763,8 +2765,9 @@ d3 = function() {
var segments;
var buffer = d3_geo_clipBufferListener(), ringListener = clipLine(buffer), polygon, ring;
function pointRing(λ, φ) {
ringListener.point(λ, φ);
ring.push([ λ, φ ]);
var point = rotate(λ, φ);
ringListener.point(point[0], point[1]);
}
function ringStart() {
ringListener.lineStart();
......@@ -2915,9 +2918,8 @@ d3 = function() {
listener.point(to[0], to[1]);
}
}
var d3_geo_clipAntimeridianPoint = [ -π, 0 ];
function d3_geo_clipAntimeridianPolygonContains(polygon) {
return d3_geo_pointInPolygon(d3_geo_clipAntimeridianPoint, polygon);
function d3_geo_clipAntimeridianPolygonContains(rotate, polygon) {
return d3_geo_pointInPolygon(rotate(-π, 0), polygon);
}
function d3_geo_clipCircle(radius) {
var cr = Math.cos(radius), smallRadius = cr > 0, point = [ radius, 0 ], notHemisphere = Math.abs(cr) > ε, interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians);
......@@ -3014,8 +3016,8 @@ d3 = function() {
if (φ < -r) code |= 4; else if (φ > r) code |= 8;
return code;
}
function polygonContains(polygon) {
return d3_geo_pointInPolygon(point, polygon);
function polygonContains(rotate, polygon) {
return d3_geo_pointInPolygon(rotate(point[0], point[1]), polygon);
}
}
var d3_geo_clipExtentMAX = 1e9;
......@@ -3668,7 +3670,7 @@ d3 = function() {
}
projection.stream = function(output) {
if (stream) stream.valid = false;
stream = d3_geo_projectionRadiansRotate(rotate, preclip(projectResample(postclip(output))));
stream = d3_geo_projectionRadians(preclip(rotatePoint, projectResample(postclip(output))));
stream.valid = true;
return stream;
};
......@@ -3710,6 +3712,7 @@ d3 = function() {
d3.rebind(projection, projectResample, "precision");
function reset() {
projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project);
rotatePoint.invert = rotate.invert;
var center = project(λ, φ);
δx = x - center[0] * k;
δy = y + center[1] * k;
......@@ -3719,17 +3722,22 @@ d3 = function() {
if (stream) stream.valid = false, stream = null;
return projection;
}
function rotatePoint(λ, φ) {
var point = rotate(λ, φ);
λ = point[0];
point[0] = λ > π ? λ - 2 * π : λ < -π ? λ + 2 * π : λ;
return point;
}
return function() {
project = projectAt.apply(this, arguments);
projection.invert = project.invert && invert;
return reset();
};
}
function d3_geo_projectionRadiansRotate(rotate, stream) {
function d3_geo_projectionRadians(stream) {
var transform = new d3_geo_transform(stream);
transform.point = function(x, y) {
y = rotate(x * d3_radians, y * d3_radians), x = y[0];
stream.point(x > π ? x - 2 * π : x < -π ? x + 2 * π : x, y[1]);
transform.point = function(λ, φ) {
stream.point(λ * d3_radians, φ * d3_radians);
};
return transform;
}
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -94,8 +94,6 @@ function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) {
}
}
var d3_geo_clipAntimeridianPoint = [-π, 0];
function d3_geo_clipAntimeridianPolygonContains(polygon) {
return d3_geo_pointInPolygon(d3_geo_clipAntimeridianPoint, polygon);
function d3_geo_clipAntimeridianPolygonContains(rotate, polygon) {
return d3_geo_pointInPolygon(rotate(-π, 0), polygon);
}
......@@ -176,7 +176,7 @@ function d3_geo_clipCircle(radius) {
return code;
}
function polygonContains(polygon) {
return d3_geo_pointInPolygon(point, polygon);
function polygonContains(rotate, polygon) {
return d3_geo_pointInPolygon(rotate(point[0], point[1]), polygon);
}
}
......@@ -4,7 +4,7 @@ import "../math/trigonometry";
import "clip-polygon";
function d3_geo_clip(pointVisible, clipLine, interpolate, polygonContains) {
return function(listener) {
return function(rotate, listener) {
var line = clipLine(listener);
var clip = {
......@@ -27,7 +27,7 @@ function d3_geo_clip(pointVisible, clipLine, interpolate, polygonContains) {
segments = d3.merge(segments);
if (segments.length) {
d3_geo_clipPolygon(segments, d3_geo_clipSort, null, interpolate, listener);
} else if (polygonContains(polygon)) {
} else if (polygonContains(rotate.invert, polygon)) {
listener.lineStart();
interpolate(null, null, 1, listener);
listener.lineEnd();
......@@ -44,8 +44,14 @@ function d3_geo_clip(pointVisible, clipLine, interpolate, polygonContains) {
}
};
function point(λ, φ) { if (pointVisible(λ, φ)) listener.point(λ, φ); }
function pointLine(λ, φ) { line.point(λ, φ); }
function point(λ, φ) {
var point = rotate(λ, φ);
if (pointVisible(λ = point[0], φ = point[1])) listener.point(λ, φ);
}
function pointLine(λ, φ) {
var point = rotate(λ, φ);
line.point(point[0], point[1]);
}
function lineStart() { clip.point = pointLine; line.lineStart(); }
function lineEnd() { clip.point = point; line.lineEnd(); }
......@@ -57,8 +63,9 @@ function d3_geo_clip(pointVisible, clipLine, interpolate, polygonContains) {
ring;
function pointRing(λ, φ) {
ringListener.point(λ, φ);
ring.push([λ, φ]);
var point = rotate(λ, φ);
ringListener.point(point[0], point[1]);
}
function ringStart() {
......
......@@ -47,7 +47,7 @@ function d3_geo_projectionMutator(projectAt) {
projection.stream = function(output) {
if (stream) stream.valid = false;
stream = d3_geo_projectionRadiansRotate(rotate, preclip(projectResample(postclip(output))));
stream = d3_geo_projectionRadians(preclip(rotatePoint, projectResample(postclip(output))));
stream.valid = true; // allow caching by d3.geo.path
return stream;
};
......@@ -97,6 +97,7 @@ function d3_geo_projectionMutator(projectAt) {
function reset() {
projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project);
rotatePoint.invert = rotate.invert;
var center = project(λ, φ);
δx = x - center[0] * k;
δy = y + center[1] * k;
......@@ -108,6 +109,13 @@ function d3_geo_projectionMutator(projectAt) {
return projection;
}
function rotatePoint(λ, φ) {
var point = rotate(λ, φ);
λ = point[0];
point[0] = λ > π ? λ - 2 * π : λ < -π ? λ + 2 * π : λ;
return point;
}
return function() {
project = projectAt.apply(this, arguments);
projection.invert = project.invert && invert;
......@@ -115,11 +123,10 @@ function d3_geo_projectionMutator(projectAt) {
};
}
function d3_geo_projectionRadiansRotate(rotate, stream) {
function d3_geo_projectionRadians(stream) {
var transform = new d3_geo_transform(stream);
transform.point = function(x, y) {
y = rotate(x * d3_radians, y * d3_radians), x = y[0];
stream.point(x > π ? x - 2 * π : x < -π ? x + 2 * π : x, y[1]);
transform.point = function(λ, φ) {
stream.point(λ * d3_radians, φ * d3_radians);
};
return transform;
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment