Fix streaming polygon clipping.

This commit is contained in:
Jason Davies 2012-12-12 22:58:01 +00:00
Родитель 604c57df60
Коммит 37f64b74f2
13 изменённых файлов: 262 добавлений и 155 удалений

Просмотреть файл

@ -181,9 +181,6 @@ d3.layout.js: \
d3.geo.js: \
src/geo/geo.js \
src/geo/stream.js \
src/geo/stream-radians.js \
src/geo/stream-rotate.js \
src/geo/stream-transform.js \
src/geo/spherical.js \
src/geo/cartesian.js \
src/geo/type.js \

172
d3.js поставляемый
Просмотреть файл

@ -5397,32 +5397,11 @@
while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1);
listener.polygonEnd();
}
function d3_geo_streamRadians(stream) {
return d3_geo_streamTransform(stream, function(x, y) {
stream.point(x * d3_radians, y * d3_radians);
});
}
function d3_geo_streamRotate(rotate, stream) {
return d3_geo_streamTransform(stream, function(x, y) {
x = rotate(x, y);
stream.point(x[0], x[1]);
});
}
function d3_geo_streamTransform(stream, point) {
return {
point: point,
sphere: stream.sphere,
lineStart: stream.lineStart,
lineEnd: stream.lineEnd,
polygonStart: stream.polygonStart,
polygonEnd: stream.polygonEnd
};
}
function d3_geo_spherical(cartesian) {
return [ Math.atan2(cartesian[1], cartesian[0]), Math.asin(Math.max(-1, Math.min(1, cartesian[2]))) ];
}
function d3_geo_sphericalEqual(a, b) {
return !a || Math.abs(a[0] - b[0]) < ε && Math.abs(a[1] - b[1]) < ε;
return Math.abs(a[0] - b[0]) < ε && Math.abs(a[1] - b[1]) < ε;
}
function d3_geo_cartesian(spherical) {
var λ = spherical[0], φ = spherical[1], cosφ = Math.cos(φ);
@ -5528,7 +5507,7 @@
};
function d3_geo_clip(pointVisible, clipLine, interpolate) {
return function(listener) {
var line = clipLine(listener);
var line = clipLine(listener), polygon = [], ring;
var clip = {
point: point,
lineStart: lineStart,
@ -5539,7 +5518,6 @@
clip.lineEnd = d3_noop;
},
polygonEnd: function() {
return;
clip.point = point;
clip.lineStart = lineStart;
clip.lineEnd = lineEnd;
@ -5577,28 +5555,38 @@
polygon.push(ring = []);
}
};
var subject = [], clip = [], segments = [], visibleArea = 0, invisibleArea = 0, invisible = false;
}
function d3_geo_clipPolygon(polygon, clipRing, interpolate, listener) {
var subject = [], clip = [], segments = [], visibleArea = 0, invisibleArea = 0, invisible = false, buffer = d3_geo_clipBufferListener(), ringListener = clipRing(buffer);
listener.polygonStart();
var segments = d3.merge(polygon.map(function(ring) {
var x = clipRing(ring, bufferListener), ringSegments = x[1], segment, n = ringSegments.length;
ringListener.lineStart();
ring.forEach(function(point) {
ringListener.point(point[0], point[1]);
});
ringListener.lineEnd();
var clean = ringListener.clean(), ringSegments = buffer.buffer(), segment, n = ringSegments.length;
if (!n) {
invisible = true;
invisibleArea += d3_geo_areaRing(ring.map(d3_geo_clipRotationInvisible));
invisibleArea += d3_geo_clipAreaRing(ring, d3_geo_clipRotationInvisible);
return [];
}
if (x[0] & 1) {
if (clean & 1) {
segment = ringSegments[0];
visibleArea += d3_geo_areaRing(segment.map(d3_geo_clipRotation));
var n = segment.length - 1, i = -1, point;
visibleArea += d3_geo_clipAreaRing(segment, d3_geo_clipRotation);
var n = segment.length, i = -1, point;
listener.lineStart();
while (++i < n) listener.point((point = segment[i])[0], point[1]);
listener.lineEnd();
return [];
}
if (n > 1 && x[0] & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
return ringSegments.filter(d3_geo_clipSegmentLength1);
}));
if (!segments.length && (visibleArea < -ε2 || invisible && invisibleArea < -ε2)) {
listener.lineStart();
interpolate(null, null, 1, listener);
listener.lineEnd();
}
segments.forEach(function(segment) {
var n = segment.length;
@ -5644,8 +5632,8 @@
clip.sort(d3_geo_clipSort);
d3_geo_clipLinkCircular(subject);
d3_geo_clipLinkCircular(clip);
if (!subject.length) return;
var start = subject[0], current, points;
if (!subject.length) return listener.polygonEnd();
var start = subject[0], current, points, point;
while (1) {
current = start;
while (current.visited) if ((current = current.next) === start) return listener.polygonEnd();
@ -5676,15 +5664,6 @@
}
listener.polygonEnd();
}
function d3_geo_clipPolygon(polygon, clipRing, interpolate, listener) {}
function d3_geo_clipRotation(point) {
var λ = point[0], φ = point[1], cosφ = Math.cos(φ);
return [ Math.atan2(Math.sin(λ) * cosφ, Math.sin(φ)), Math.asin(Math.max(-1, Math.min(1, -Math.cos(λ) * cosφ))) ];
}
function d3_geo_clipRotationInvisible(point) {
var λ = point[0] + π, φ = point[1], cosφ = Math.cos(φ);
return [ Math.atan2(Math.sin(λ) * cosφ, Math.sin(φ)), Math.asin(Math.max(-1, Math.min(1, -Math.cos(λ) * cosφ))) ];
}
function d3_geo_clipLinkCircular(array) {
if (!(n = array.length)) return;
var n, i = 0, a = array[0], b;
@ -5702,12 +5681,50 @@
function d3_geo_clipSegmentLength1(segment) {
return segment.length > 1;
}
var d3_geo_cut = d3_geo_clip(d3_identity, d3_geo_cutLine, d3_geo_cutInterpolate);
function d3_geo_clipBufferListener() {
var lines = [], line;
return {
lineStart: function() {
lines.push(line = []);
},
point: function(λ, φ) {
line.push([ λ, φ ]);
},
lineEnd: d3_noop,
buffer: function() {
var buffer = lines;
lines = [];
line = null;
return buffer;
}
};
}
function d3_geo_clipAreaRing(ring, rotate) {
d3_geo_area.polygonStart();
d3_geo_area.lineStart();
for (var i = 0, n = ring.length, p; i < n; ++i) {
p = rotate(ring[i]);
d3_geo_area.point(p[0] * d3_degrees, p[1] * d3_degrees);
}
d3_geo_area.lineEnd();
d3_geo_area.polygonEnd();
return d3_geo_areaRing;
}
function d3_geo_clipRotation(point) {
var λ = point[0], φ = point[1], cosφ = Math.cos(φ);
return [ Math.atan2(Math.sin(λ) * cosφ, Math.sin(φ)), Math.asin(Math.max(-1, Math.min(1, -Math.cos(λ) * cosφ))) ];
}
function d3_geo_clipRotationInvisible(point) {
var λ = point[0] + π, φ = point[1], cosφ = Math.cos(φ);
return [ Math.atan2(Math.sin(λ) * cosφ, Math.sin(φ)), Math.asin(Math.max(-1, Math.min(1, -Math.cos(λ) * cosφ))) ];
}
var d3_geo_cut = d3_geo_clip(d3_true, d3_geo_cutLine, d3_geo_cutInterpolate);
function d3_geo_cutLine(listener) {
var λ0 = NaN, φ0 = NaN, sλ0 = NaN, clean = 1;
var λ0 = NaN, φ0 = NaN, sλ0 = NaN, clean;
return {
lineStart: function() {
listener.lineStart();
clean = 1;
},
point: function(λ1, φ1) {
var sλ1 = λ1 > 0 ? π : -π, = Math.abs(λ1 - λ0);
@ -5733,8 +5750,11 @@
sλ0 = sλ1;
},
lineEnd: function() {
λ0 = φ0 = NaN;
listener.lineEnd();
λ0 = φ0 = NaN;
},
clean: function() {
return 2 - clean;
}
};
}
@ -5779,8 +5799,12 @@
resample.point = resamplePoint;
listener.lineEnd();
},
polygonStart: d3_noop,
polygonEnd: d3_noop
polygonStart: function() {
listener.polygonStart();
},
polygonEnd: function() {
listener.polygonEnd();
}
};
function resamplePoint(λ, φ) {
var point = projectPoint(λ, φ);
@ -6032,22 +6056,25 @@
function d3_geo_circleClip(degrees) {
var radians = degrees * d3_radians, cr = Math.cos(radians), interpolate = d3_geo_circleInterpolate(radians, 6 * d3_radians);
return d3_geo_clip(visible, clipLine, interpolate);
function visible(point) {
return Math.cos(point[1]) * Math.cos(point[0]) > cr;
function visible(λ, φ) {
return Math.cos(λ) * Math.cos(φ) > cr;
}
function clipLine(listener) {
var point0, v0 = false, v00 = false, clean = 1;
var point0, v0, v00, clean;
return {
lineStart: d3_noop,
lineStart: function() {
v00 = v0 = false;
clean = 1;
},
point: function(λ, φ) {
var point1 = [ λ, φ ], point2, v = visible(point1);
var point1 = [ λ, φ ], point2, v = visible(λ, φ);
if (!point0 && (v00 = v0 = v)) listener.lineStart();
if (v !== v0) {
point2 = intersect(point0, point1);
if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) {
point1[0] += ε;
point1[1] += ε;
v = visible(point1);
v = visible(point1[0], point1[1]);
}
}
if (v !== v0) {
@ -6063,14 +6090,15 @@
}
point0 = point2;
}
if (v && !d3_geo_sphericalEqual(point0, point1)) listener.point(point1[0], point1[1]);
if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) listener.point(point1[0], point1[1]);
point0 = point1;
},
lineEnd: function() {
if (v0) listener.lineEnd();
v0 = v00 = false;
point0 = null;
clean = 1;
},
clean: function() {
return clean | (v00 && v0) << 1;
}
};
}
@ -6260,6 +6288,7 @@
d3.geo.path = function() {
var pointRadius = 4.5, pointCircle = d3_geo_pathCircle(pointRadius), projection = d3.geo.albersUsa(), context, buffer = [];
function path(object) {
if (object == null) return;
var radius = typeof pointRadius === "function" ? pointRadius.apply(this, arguments) : pointRadius;
d3.geo.stream(object, projection.stream(context == null ? new d3_geo_pathBuffer(buffer, radius) : new d3_geo_pathContext(context, radius)));
if (buffer.length) {
@ -6439,7 +6468,7 @@
x00 = x0 = x, y00 = y0 = y;
};
function nextPoint(x, y) {
var dx = x - x0, dy = y - y0, z = y0 * x - x0 * y;
var z = y0 * x - x0 * y;
d3_geo_centroidX += z * (x0 + x);
d3_geo_centroidY += z * (y0 + y);
d3_geo_centroidZ += z * 3;
@ -6472,7 +6501,7 @@
}
};
function d3_geo_areaRingStart() {
var λ00, φ00, λ1, λ0, φ0, cosφ0, sinφ0;
var λ00, φ00, λ1, φ1, λ0, φ0, cosφ0, sinφ0;
d3_geo_area.point = function(λ, φ) {
d3_geo_area.point = nextPoint;
λ1 = λ0 = (λ00 = λ) * d3_radians, φ0 = (φ00 = φ) * d3_radians, cosφ0 = Math.cos(φ0),
@ -6500,7 +6529,7 @@
})();
}
function d3_geo_projectionMutator(projectAt) {
var project, projectRotate, k = 150, x = 480, y = 250, λ = 0, φ = 0, δλ = 0, δφ = 0, δγ = 0, δx, δy, clip = d3_geo_cut, clipAngle = null, projectResample = d3_geo_resample(projectPoint);
var project, projectRotate, rotate, k = 150, x = 480, y = 250, λ = 0, φ = 0, δλ = 0, δφ = 0, δγ = 0, δx, δy, clip = d3_geo_cut, clipAngle = null, projectResample = d3_geo_resample(projectPoint);
function projection(coordinates) {
coordinates = projectRotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
return [ coordinates[0] * k + δx, δy - coordinates[1] * k ];
@ -6510,7 +6539,7 @@
return [ coordinates[0] * d3_degrees, coordinates[1] * d3_degrees ];
}
projection.stream = function(listener) {
return d3_geo_streamRadians(d3_geo_streamRotate(rotate, clip(projectResample(listener))));
return d3_geo_projectionRadiansRotate(rotate, clip(projectResample(listener)));
};
projection.clipAngle = function(_) {
if (!arguments.length) return clipAngle;
@ -6559,6 +6588,29 @@
return reset();
};
}
function d3_geo_projectionRadiansRotate(rotate, stream) {
return {
point: function(λ, φ) {
var p = rotate(λ * d3_radians, φ * d3_radians);
stream.point(p[0], p[1]);
},
sphere: function() {
stream.sphere();
},
lineStart: function() {
stream.lineStart();
},
lineEnd: function() {
stream.lineEnd();
},
polygonStart: function() {
stream.polygonStart();
},
polygonEnd: function() {
stream.polygonEnd();
}
};
}
function d3_geo_rotation(δλ, δφ, δγ) {
return δλ ? δφ || δγ ? d3_geo_compose(d3_geo_rotationλ(δλ), d3_geo_rotationφγ(δφ, δγ)) : d3_geo_rotationλ(δλ) : δφ || δγ ? d3_geo_rotationφγ(δφ, δγ) : d3_geo_identityRotation;
}

8
d3.min.js поставляемый

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -25,7 +25,7 @@ var d3_geo_area = {
};
function d3_geo_areaRingStart() {
var λ00, φ00, λ1, λ0, φ0, cosφ0, sinφ0; // start point and two previous points
var λ00, φ00, λ1, φ1, λ0, φ0, cosφ0, sinφ0; // start point and two previous points
// For the first point, …
d3_geo_area.point = function(λ, φ) {
@ -41,7 +41,7 @@ function d3_geo_areaRingStart() {
if (Math.abs(Math.abs(φ0) - π / 2) < ε && Math.abs(Math.abs(φ) - π / 2) < ε) return;
var cosφ = Math.cos(φ), sinφ = Math.sin(φ);
// If the previous point is at the south pole, something involving lunes…
// If the previous point is at the north pole, then compute lune area.
if (Math.abs(φ0 - π / 2) < ε) d3_geo_areaRing += (λ - λ1) * 2;
// TODO Explain this wonderous mathematics.

Просмотреть файл

@ -50,8 +50,8 @@ function d3_geo_circleClip(degrees) {
return d3_geo_clip(visible, clipLine, interpolate);
function visible(point) {
return Math.cos(point[1]) * Math.cos(point[0]) > cr;
function visible(λ, φ) {
return Math.cos(λ) * Math.cos(φ) > cr;
}
// TODO handle two invisible endpoints with visible intermediate segment.
@ -63,15 +63,18 @@ function d3_geo_circleClip(degrees) {
// rejoined.
function clipLine(listener) {
var point0,
v0 = false,
v00 = false,
clean = 1; // no intersections
v0,
v00,
clean; // no intersections
return {
lineStart: d3_noop,
lineStart: function() {
v00 = v0 = false;
clean = 1;
},
point: function(λ, φ) {
var point1 = [λ, φ],
point2,
v = visible(point1);
v = visible(λ, φ);
if (!point0 && (v00 = v0 = v)) listener.lineStart();
// handle degeneracies
if (v !== v0) {
@ -79,7 +82,7 @@ function d3_geo_circleClip(degrees) {
if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) {
point1[0] += ε;
point1[1] += ε;
v = visible(point1);
v = visible(point1[0], point1[1]);
}
}
if (v !== v0) {
@ -97,20 +100,17 @@ function d3_geo_circleClip(degrees) {
}
point0 = point2;
}
if (v && !d3_geo_sphericalEqual(point0, point1)) listener.point(point1[0], point1[1]);
if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) listener.point(point1[0], point1[1]);
point0 = point1;
},
lineEnd: function() {
if (v0) listener.lineEnd();
// TODO should these be in lineStart?
v0 = v00 = false;
point0 = null;
clean = 1;
}
},
// Rejoin first and last segments if there were intersections and the first
// and last points were visible.
clean: function() { return clean | ((v00 && v0) << 1); }
};
// Rejoin first and last segments if there were intersections and the first
// and last points were visible.
//return clean + ((v00 && v) << 1);
}
// Intersects the great circle between a and b with the clip circle.

Просмотреть файл

@ -1,20 +1,19 @@
function d3_geo_clip(pointVisible, clipLine, interpolate) {
return function(listener) {
var line = clipLine(listener);
var line = clipLine(listener),
polygon = [],
ring;
var clip = {
point: point,
lineStart: lineStart,
lineEnd: lineEnd,
polygonStart: function() {
// TODO
clip.point = pointPolygon;
clip.lineStart = linePolygon;
clip.lineEnd = d3_noop;
},
polygonEnd: function() {
return;
// TODO
clip.point = point;
clip.lineStart = lineStart;
clip.lineEnd = lineEnd;
@ -40,32 +39,50 @@ function d3_geo_clip(pointVisible, clipLine, interpolate) {
function lineEnd() { clip.point = point; line.lineEnd(); }
function linePolygon() { polygon.push(ring = []); }
};
}
// For each polygon…
// General spherical polygon clipping algorithm: takes a polygon, cuts it into
// visible line segments and rejoins the segments by interpolating along the
// clip edge. If there are no intersections with the clip edge, the whole clip
// edge is inserted if appropriate.
//
// This is used by both d3_geo_circleClip and d3_geo_cut, each providing
// different functions for clipRing and interpolate.
function d3_geo_clipPolygon(polygon, clipRing, interpolate, listener) {
var subject = [],
clip = [],
segments = [],
visibleArea = 0,
invisibleArea = 0,
invisible = false;
invisible = false,
buffer = d3_geo_clipBufferListener(),
ringListener = clipRing(buffer);
listener.polygonStart();
var segments = d3.merge(polygon.map(function(ring) {
var x = clipRing(ring, bufferListener),
ringSegments = x[1],
ringListener.lineStart();
ring.forEach(function(point) {
ringListener.point(point[0], point[1]);
});
ringListener.lineEnd();
var clean = ringListener.clean(),
ringSegments = buffer.buffer(),
segment,
n = ringSegments.length;
if (!n) {
invisible = true;
invisibleArea += d3_geo_areaRing(ring.map(d3_geo_clipRotationInvisible));
invisibleArea += d3_geo_clipAreaRing(ring, d3_geo_clipRotationInvisible);
return [];
}
// No intersections.
if (x[0] & 1) {
if (clean & 1) {
segment = ringSegments[0];
visibleArea += d3_geo_areaRing(segment.map(d3_geo_clipRotation));
var n = segment.length - 1,
visibleArea += d3_geo_clipAreaRing(segment, d3_geo_clipRotation);
var n = segment.length,
i = -1,
point;
listener.lineStart();
@ -75,14 +92,17 @@ function d3_geo_clip(pointVisible, clipLine, interpolate) {
}
// Rejoin connected segments.
if (n > 1 && x[0] & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
return ringSegments.filter(d3_geo_clipSegmentLength1);
}));
if (!segments.length && (visibleArea < -ε2 || invisible && invisibleArea < -ε2)) {
listener.lineStart();
interpolate(null, null, 1, listener);
listener.lineEnd();
}
segments.forEach(function(segment) {
var n = segment.length;
if (n <= 1) return;
@ -102,10 +122,11 @@ function d3_geo_clip(pointVisible, clipLine, interpolate) {
clip.sort(d3_geo_clipSort);
d3_geo_clipLinkCircular(subject);
d3_geo_clipLinkCircular(clip);
if (!subject.length) return;
if (!subject.length) return listener.polygonEnd();
var start = subject[0],
current,
points;
points,
point;
while (1) {
// Find first unvisited intersection.
current = start;
@ -138,37 +159,6 @@ function d3_geo_clip(pointVisible, clipLine, interpolate) {
listener.polygonEnd();
}
// General spherical polygon clipping algorithm: takes a polygon, cuts it into
// visible line segments and rejoins the segments by interpolating along the
// clip edge. If there are no intersections with the clip edge, the whole clip
// edge is inserted if appropriate.
//
// This is used by both d3_geo_circleClip and d3_geo_cut, each providing
// different functions for clipRing and interpolate.
function d3_geo_clipPolygon(polygon, clipRing, interpolate, listener) {
}
function d3_geo_clipRotation(point) {
var λ = point[0],
φ = point[1],
cosφ = Math.cos(φ);
return [
Math.atan2(Math.sin(λ) * cosφ, Math.sin(φ)),
Math.asin(Math.max(-1, Math.min(1, -Math.cos(λ) * cosφ)))
];
}
// For an invisible polygon ring, we first rotate longitudinally by 180°.
function d3_geo_clipRotationInvisible(point) {
var λ = point[0] + π,
φ = point[1],
cosφ = Math.cos(φ);
return [
Math.atan2(Math.sin(λ) * cosφ, Math.sin(φ)),
Math.asin(Math.max(-1, Math.min(1, -Math.cos(λ) * cosφ)))
];
}
function d3_geo_clipLinkCircular(array) {
if (!(n = array.length)) return;
var n,
@ -194,3 +184,52 @@ function d3_geo_clipSort(a, b) {
function d3_geo_clipSegmentLength1(segment) {
return segment.length > 1;
}
function d3_geo_clipBufferListener() {
var lines = [],
line;
return {
lineStart: function() { lines.push(line = []); },
point: function(λ, φ) { line.push([λ, φ]); },
lineEnd: d3_noop,
buffer: function() {
var buffer = lines;
lines = [];
line = null;
return buffer;
}
};
}
function d3_geo_clipAreaRing(ring, rotate) {
d3_geo_area.polygonStart();
d3_geo_area.lineStart();
for (var i = 0, n = ring.length, p; i < n; ++i) {
p = rotate(ring[i]);
d3_geo_area.point(p[0] * d3_degrees, p[1] * d3_degrees);
}
d3_geo_area.lineEnd();
d3_geo_area.polygonEnd();
return d3_geo_areaRing;
}
function d3_geo_clipRotation(point) {
var λ = point[0],
φ = point[1],
cosφ = Math.cos(φ);
return [
Math.atan2(Math.sin(λ) * cosφ, Math.sin(φ)),
Math.asin(Math.max(-1, Math.min(1, -Math.cos(λ) * cosφ)))
];
}
// For an invisible polygon ring, we first rotate longitudinally by 180°.
function d3_geo_clipRotationInvisible(point) {
var λ = point[0] + π,
φ = point[1],
cosφ = Math.cos(φ);
return [
Math.atan2(Math.sin(λ) * cosφ, Math.sin(φ)),
Math.asin(Math.max(-1, Math.min(1, -Math.cos(λ) * cosφ)))
];
}

Просмотреть файл

@ -1,5 +1,5 @@
// Cut features along the antimeridian.
var d3_geo_cut = d3_geo_clip(d3_identity, d3_geo_cutLine, d3_geo_cutInterpolate);
var d3_geo_cut = d3_geo_clip(d3_true, d3_geo_cutLine, d3_geo_cutInterpolate);
// Takes a line and cuts into visible segments. Return values:
// 0: there were intersections or the line was empty.
@ -10,13 +10,12 @@ function d3_geo_cutLine(listener) {
var λ0 = NaN,
φ0 = NaN,
sλ0 = NaN,
clean = 1; // no intersections
// if there are intersections, we always rejoin the first and last segments.
//return 2 - clean;
clean; // no intersections
return {
lineStart: function() {
listener.lineStart();
clean = 1;
},
point: function(λ1, φ1) {
var sλ1 = λ1 > 0 ? π : -π,
@ -44,9 +43,11 @@ function d3_geo_cutLine(listener) {
sλ0 = sλ1;
},
lineEnd: function() {
λ0 = φ0 = NaN;
listener.lineEnd();
}
λ0 = φ0 = NaN;
},
// if there are intersections, we always rejoin the first and last segments.
clean: function() { return 2 - clean; }
};
}

Просмотреть файл

@ -57,7 +57,7 @@ function d3_geo_pathCentroidRingStart() {
// For subsequent points, …
function nextPoint(x, y) {
var dx = x - x0, dy = y - y0, z = y0 * x - x0 * y;
var z = y0 * x - x0 * y;
d3_geo_centroidX += z * (x0 + x);
d3_geo_centroidY += z * (y0 + y);
d3_geo_centroidZ += z * 3;

Просмотреть файл

@ -8,6 +8,7 @@ d3.geo.path = function() {
buffer = [];
function path(object) {
if (object == null) return;
var radius = typeof pointRadius === "function" ? pointRadius.apply(this, arguments) : pointRadius;
d3.geo.stream(object, projection.stream(context == null
? new d3_geo_pathBuffer(buffer, radius)

Просмотреть файл

@ -8,6 +8,7 @@ function d3_geo_projection(project) {
function d3_geo_projectionMutator(projectAt) {
var project,
projectRotate,
rotate,
k = 150, // scale
x = 480, // translate
y = 250,
@ -35,7 +36,7 @@ function d3_geo_projectionMutator(projectAt) {
}
projection.stream = function(listener) {
return d3_geo_streamRadians(d3_geo_streamRotate(rotate, clip(projectResample(listener))));
return d3_geo_projectionRadiansRotate(rotate, clip(projectResample(listener)));
};
projection.clipAngle = function(_) {
@ -98,3 +99,17 @@ function d3_geo_projectionMutator(projectAt) {
return reset();
};
}
function d3_geo_projectionRadiansRotate(rotate, stream) {
return {
point: function(λ, φ) {
var p = rotate(λ * d3_radians, φ * d3_radians);
stream.point(p[0], p[1]);
},
sphere: function() { stream.sphere(); },
lineStart: function() { stream.lineStart(); },
lineEnd: function() { stream.lineEnd(); },
polygonStart: function() { stream.polygonStart(); },
polygonEnd: function() { stream.polygonEnd(); }
};
}

Просмотреть файл

@ -8,8 +8,9 @@ function d3_geo_resample(projectPoint) {
point: resamplePoint,
lineStart: function() { x0 = NaN; resample.point = resamplePointLine; listener.lineStart(); },
lineEnd: function() { resample.point = resamplePoint; listener.lineEnd(); },
polygonStart: d3_noop,
polygonEnd: d3_noop
// TODO resample last point to first point for polygon rings.
polygonStart: function() { listener.polygonStart(); },
polygonEnd: function() { listener.polygonEnd(); }
};
// TODO rename: this is not just resampling, it also projects and transforms!

Просмотреть файл

@ -6,5 +6,5 @@ function d3_geo_spherical(cartesian) {
}
function d3_geo_sphericalEqual(a, b) {
return !a || Math.abs(a[0] - b[0]) < ε && Math.abs(a[1] - b[1]) < ε;
return Math.abs(a[0] - b[0]) < ε && Math.abs(a[1] - b[1]) < ε;
}

Просмотреть файл

@ -21,7 +21,8 @@ suite.addBatch({
coordinates: [-63, 18]
});
assert.deepEqual(testContext.buffer(), [
{type: "point", x: 165, y: 160},
{type: "moveTo", x: 165, y: 160},
{type: "arc", x: 165, y: 160}
]);
},
@ -31,9 +32,9 @@ suite.addBatch({
coordinates: [[-63, 18], [-62, 18], [-62, 17]]
});
assert.deepEqual(testContext.buffer(), [
{type: "point", x: 165, y: 160},
{type: "point", x: 170, y: 160},
{type: "point", x: 170, y: 165}
{type: "moveTo", x: 165, y: 160}, {type: "arc", x: 165, y: 160},
{type: "moveTo", x: 170, y: 160}, {type: "arc", x: 170, y: 160},
{type: "moveTo", x: 170, y: 165}, {type: "arc", x: 170, y: 165}
]);
},
@ -74,7 +75,7 @@ suite.addBatch({
geometries: [{type: "Point", coordinates: [0, 0]}]
});
assert.deepEqual(testContext.buffer(), [
{type: "point", x: 480, y: 250}
{type: "moveTo", x: 480, y: 250}, {type: "arc", x: 480, y: 250}
]);
},
@ -84,7 +85,7 @@ suite.addBatch({
features: [{type: "Feature", geometry: {type: "Point", coordinates: [0, 0]}}]
});
assert.deepEqual(testContext.buffer(), [
{type: "point", x: 480, y: 250}
{type: "moveTo", x: 480, y: 250}, {type: "arc", x: 480, y: 250}
]);
},
@ -260,7 +261,7 @@ suite.addBatch({
coordinates: [-63, 18]
});
assert.deepEqual(testContext.buffer(), [
{type: "point", x: 165, y: 160},
{type: "moveTo", x: 165, y: 160}, {type: "arc", x: 165, y: 160}
]);
},
"MultiPoint": function(path) {
@ -269,9 +270,9 @@ suite.addBatch({
coordinates: [[-63, 18], [-62, 18], [-62, 17]]
});
assert.deepEqual(testContext.buffer(), [
{type: "point", x: 165, y: 160},
{type: "point", x: 170, y: 160},
{type: "point", x: 170, y: 165}
{type: "moveTo", x: 165, y: 160}, {type: "arc", x: 165, y: 160},
{type: "moveTo", x: 170, y: 160}, {type: "arc", x: 170, y: 160},
{type: "moveTo", x: 170, y: 165}, {type: "arc", x: 170, y: 165}
]);
},
"Polygon": {
@ -334,7 +335,7 @@ suite.addBatch({
"Point": {
"visible": function(path) {
path({type: "Point", coordinates: [0, 0]});
assert.deepEqual(testContext.buffer(), [{type: "point", x: 859, y: 187}]);
assert.deepEqual(testContext.buffer(), [{type: "moveTo", x: 859, y: 187}, {type: "arc", x: 859, y: 187}]);
},
"invisible": function(path) {
path({type: "Point", coordinates: [-180, 0]});
@ -343,7 +344,7 @@ suite.addBatch({
},
"MultiPoint": function(path) {
path({type: "MultiPoint", coordinates: [[0, 0], [-180, 0]]});
assert.deepEqual(testContext.buffer(), [{type: "point", x: 859, y: 187}]);
assert.deepEqual(testContext.buffer(), [{type: "moveTo", x: 859, y: 187}, {type: "arc", x: 859, y: 187}]);
}
},
"rotate(-24, -175.5])": {
@ -600,7 +601,7 @@ suite.addBatch({
"rotate([0, 0, 0])": {
"longitudes wrap at ±180°": function(path) {
path({type: "Point", coordinates: [180 + 1e-6, 0]});
assert.deepEqual(testContext.buffer(), [{type: "point", x: -420, y: 250}]);
assert.deepEqual(testContext.buffer(), [{type: "moveTo", x: -420, y: 250}, {type: "arc", x: -420, y: 250}]);
}
}
}