Skip to content

Commit ec6ee5d

Browse files
committed
Expose d3.geo.clipCircle.
1 parent 0008bca commit ec6ee5d

File tree

8 files changed

+419
-300
lines changed

8 files changed

+419
-300
lines changed

d3.js

Lines changed: 230 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -3047,94 +3047,92 @@
30473047
}
30483048
};
30493049
}
3050-
function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) {
3051-
return function(sink) {
3052-
var line = clipLine(sink);
3053-
var clip = {
3054-
point: point,
3055-
lineStart: lineStart,
3056-
lineEnd: lineEnd,
3057-
polygonStart: function() {
3058-
clip.point = pointRing;
3059-
clip.lineStart = ringStart;
3060-
clip.lineEnd = ringEnd;
3061-
segments = [];
3062-
polygon = [];
3063-
},
3064-
polygonEnd: function() {
3065-
clip.point = point;
3066-
clip.lineStart = lineStart;
3067-
clip.lineEnd = lineEnd;
3068-
segments = d3.merge(segments);
3069-
var clipStartInside = d3_geo_pointInPolygon(clipStart, polygon);
3070-
if (segments.length) {
3071-
if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
3072-
d3_geo_clipPolygon(segments, d3_geo_clipSort, clipStartInside, interpolate, sink);
3073-
} else if (clipStartInside) {
3074-
if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
3075-
sink.lineStart();
3076-
interpolate(null, null, 1, sink);
3077-
sink.lineEnd();
3078-
}
3079-
if (polygonStarted) sink.polygonEnd(), polygonStarted = false;
3080-
segments = polygon = null;
3081-
},
3082-
sphere: function() {
3083-
sink.polygonStart();
3050+
function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart, sink) {
3051+
var line = clipLine(sink);
3052+
var clip = {
3053+
point: point,
3054+
lineStart: lineStart,
3055+
lineEnd: lineEnd,
3056+
polygonStart: function() {
3057+
clip.point = pointRing;
3058+
clip.lineStart = ringStart;
3059+
clip.lineEnd = ringEnd;
3060+
segments = [];
3061+
polygon = [];
3062+
},
3063+
polygonEnd: function() {
3064+
clip.point = point;
3065+
clip.lineStart = lineStart;
3066+
clip.lineEnd = lineEnd;
3067+
segments = d3.merge(segments);
3068+
var clipStartInside = d3_geo_pointInPolygon(clipStart, polygon);
3069+
if (segments.length) {
3070+
if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
3071+
d3_geo_clipPolygon(segments, d3_geo_clipSort, clipStartInside, interpolate, sink);
3072+
} else if (clipStartInside) {
3073+
if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
30843074
sink.lineStart();
30853075
interpolate(null, null, 1, sink);
30863076
sink.lineEnd();
3087-
sink.polygonEnd();
30883077
}
3089-
};
3090-
function point(λ, φ) {
3091-
if (pointVisible(λ, φ)) sink.point(λ, φ);
3092-
}
3093-
function pointLine(λ, φ) {
3094-
line.point(λ, φ);
3095-
}
3096-
function lineStart() {
3097-
clip.point = pointLine;
3098-
line.lineStart();
3078+
if (polygonStarted) sink.polygonEnd(), polygonStarted = false;
3079+
segments = polygon = null;
3080+
},
3081+
sphere: function() {
3082+
sink.polygonStart();
3083+
sink.lineStart();
3084+
interpolate(null, null, 1, sink);
3085+
sink.lineEnd();
3086+
sink.polygonEnd();
30993087
}
3100-
function lineEnd() {
3101-
clip.point = point;
3102-
line.lineEnd();
3103-
}
3104-
var segments;
3105-
var buffer = d3_geo_clipBufferSink(), ringSink = clipLine(buffer), polygonStarted = false, polygon, ring;
3106-
function pointRing(λ, φ) {
3107-
ring.push([ λ, φ ]);
3108-
ringSink.point(λ, φ);
3109-
}
3110-
function ringStart() {
3111-
ringSink.lineStart();
3112-
ring = [];
3113-
}
3114-
function ringEnd() {
3115-
pointRing(ring[0][0], ring[0][1]);
3116-
ringSink.lineEnd();
3117-
var clean = ringSink.clean(), ringSegments = buffer.buffer(), segment, n = ringSegments.length;
3118-
ring.pop();
3119-
polygon.push(ring);
3120-
ring = null;
3121-
if (!n) return;
3122-
if (clean & 1) {
3123-
segment = ringSegments[0];
3124-
var n = segment.length - 1, i = -1, point;
3125-
if (n > 0) {
3126-
if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
3127-
sink.lineStart();
3128-
while (++i < n) sink.point((point = segment[i])[0], point[1]);
3129-
sink.lineEnd();
3130-
}
3131-
return;
3088+
};
3089+
function point(λ, φ) {
3090+
if (pointVisible(λ, φ)) sink.point(λ, φ);
3091+
}
3092+
function pointLine(λ, φ) {
3093+
line.point(λ, φ);
3094+
}
3095+
function lineStart() {
3096+
clip.point = pointLine;
3097+
line.lineStart();
3098+
}
3099+
function lineEnd() {
3100+
clip.point = point;
3101+
line.lineEnd();
3102+
}
3103+
var segments;
3104+
var buffer = d3_geo_clipBufferSink(), ringSink = clipLine(buffer), polygonStarted = false, polygon, ring;
3105+
function pointRing(λ, φ) {
3106+
ring.push([ λ, φ ]);
3107+
ringSink.point(λ, φ);
3108+
}
3109+
function ringStart() {
3110+
ringSink.lineStart();
3111+
ring = [];
3112+
}
3113+
function ringEnd() {
3114+
pointRing(ring[0][0], ring[0][1]);
3115+
ringSink.lineEnd();
3116+
var clean = ringSink.clean(), ringSegments = buffer.buffer(), segment, n = ringSegments.length;
3117+
ring.pop();
3118+
polygon.push(ring);
3119+
ring = null;
3120+
if (!n) return;
3121+
if (clean & 1) {
3122+
segment = ringSegments[0];
3123+
var n = segment.length - 1, i = -1, point;
3124+
if (n > 0) {
3125+
if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
3126+
sink.lineStart();
3127+
while (++i < n) sink.point((point = segment[i])[0], point[1]);
3128+
sink.lineEnd();
31323129
}
3133-
if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
3134-
segments.push(ringSegments.filter(d3_geo_clipSegmentLength1));
3130+
return;
31353131
}
3136-
return clip;
3137-
};
3132+
if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
3133+
segments.push(ringSegments.filter(d3_geo_clipSegmentLength1));
3134+
}
3135+
return clip;
31383136
}
31393137
function d3_geo_clipSegmentLength1(segment) {
31403138
return segment.length > 1;
@@ -3228,39 +3226,41 @@
32283226
}
32293227
return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < 0) ^ winding & 1;
32303228
}
3231-
d3.geo.clipAntimeridian = d3_geo_clip(d3_true, d3_geo_clipAntimeridianLine, d3_geo_clipAntimeridianInterpolate, [ -π, -π / 2 ]);
3232-
function d3_geo_clipAntimeridianLine(listener) {
3229+
d3.geo.clipAntimeridian = function(sink) {
3230+
return d3_geo_clip(d3_true, d3_geo_clipAntimeridianLine, d3_geo_clipAntimeridianInterpolate, [ -π, -π / 2 ], sink);
3231+
};
3232+
function d3_geo_clipAntimeridianLine(sink) {
32333233
var λ0 = NaN, φ0 = NaN, sλ0 = NaN, clean;
32343234
return {
32353235
lineStart: function() {
3236-
listener.lineStart();
3236+
sink.lineStart();
32373237
clean = 1;
32383238
},
32393239
point: function(λ1, φ1) {
32403240
var sλ1 = λ1 > 0 ? π : -π, = abs(λ1 - λ0);
32413241
if (abs( - π) < ε) {
3242-
listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? halfπ : -halfπ);
3243-
listener.point(sλ0, φ0);
3244-
listener.lineEnd();
3245-
listener.lineStart();
3246-
listener.point(sλ1, φ0);
3247-
listener.point(λ1, φ0);
3242+
sink.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? halfπ : -halfπ);
3243+
sink.point(sλ0, φ0);
3244+
sink.lineEnd();
3245+
sink.lineStart();
3246+
sink.point(sλ1, φ0);
3247+
sink.point(λ1, φ0);
32483248
clean = 0;
32493249
} else if (sλ0 !== sλ1 && >= π) {
32503250
if (abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε;
32513251
if (abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε;
32523252
φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1);
3253-
listener.point(sλ0, φ0);
3254-
listener.lineEnd();
3255-
listener.lineStart();
3256-
listener.point(sλ1, φ0);
3253+
sink.point(sλ0, φ0);
3254+
sink.lineEnd();
3255+
sink.lineStart();
3256+
sink.point(sλ1, φ0);
32573257
clean = 0;
32583258
}
3259-
listener.point(λ0 = λ1, φ0 = φ1);
3259+
sink.point(λ0 = λ1, φ0 = φ1);
32603260
sλ0 = sλ1;
32613261
},
32623262
lineEnd: function() {
3263-
listener.lineEnd();
3263+
sink.lineEnd();
32643264
λ0 = φ0 = NaN;
32653265
},
32663266
clean: function() {
@@ -3272,29 +3272,149 @@
32723272
var cosφ0, cosφ1, sinλ0_λ1 = Math.sin(λ0 - λ1);
32733273
return abs(sinλ0_λ1) > ε ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1) - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0)) / (cosφ0 * cosφ1 * sinλ0_λ1)) : (φ0 + φ1) / 2;
32743274
}
3275-
function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) {
3275+
function d3_geo_clipAntimeridianInterpolate(from, to, direction, sink) {
32763276
var φ;
32773277
if (from == null) {
32783278
φ = direction * halfπ;
3279-
listener.point(-π, φ);
3280-
listener.point(0, φ);
3281-
listener.point(π, φ);
3282-
listener.point(π, 0);
3283-
listener.point(π, -φ);
3284-
listener.point(0, -φ);
3285-
listener.point(-π, -φ);
3286-
listener.point(-π, 0);
3287-
listener.point(-π, φ);
3279+
sink.point(-π, φ);
3280+
sink.point(0, φ);
3281+
sink.point(π, φ);
3282+
sink.point(π, 0);
3283+
sink.point(π, -φ);
3284+
sink.point(0, -φ);
3285+
sink.point(-π, -φ);
3286+
sink.point(-π, 0);
3287+
sink.point(-π, φ);
32883288
} else if (abs(from[0] - to[0]) > ε) {
32893289
var s = from[0] < to[0] ? π : -π;
32903290
φ = direction * s / 2;
3291-
listener.point(-s, φ);
3292-
listener.point(0, φ);
3293-
listener.point(s, φ);
3291+
sink.point(-s, φ);
3292+
sink.point(0, φ);
3293+
sink.point(s, φ);
32943294
} else {
3295-
listener.point(to[0], to[1]);
3295+
sink.point(to[0], to[1]);
32963296
}
32973297
}
3298+
function d3_geo_circleInterpolate(radius, precision) {
3299+
var cr = Math.cos(radius), sr = Math.sin(radius);
3300+
return function(from, to, direction, sink) {
3301+
var step = direction * precision;
3302+
if (from != null) {
3303+
from = d3_geo_circleInterpolateAngle(cr, from);
3304+
to = d3_geo_circleInterpolateAngle(cr, to);
3305+
if (direction > 0 ? from < to : from > to) from += direction * τ;
3306+
} else {
3307+
from = radius + direction * τ;
3308+
to = radius - .5 * step;
3309+
}
3310+
for (var point, t = from; direction > 0 ? t > to : t < to; t -= step) {
3311+
sink.point((point = d3_geo_spherical([ cr, -sr * Math.cos(t), -sr * Math.sin(t) ]))[0], point[1]);
3312+
}
3313+
};
3314+
}
3315+
function d3_geo_circleInterpolateAngle(cr, point) {
3316+
var a = d3_geo_cartesian(point);
3317+
a[0] -= cr;
3318+
d3_geo_cartesianNormalize(a);
3319+
var angle = d3_acos(-a[1]);
3320+
return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI);
3321+
}
3322+
d3.geo.clipCircle = function(radius, sink) {
3323+
var cr = Math.cos(radius), smallRadius = cr > 0, notHemisphere = abs(cr) > ε, interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians);
3324+
return d3_geo_clip(visible, clipLine, interpolate, smallRadius ? [ 0, -radius ] : [ -π, radius - π ], sink);
3325+
function visible(λ, φ) {
3326+
return Math.cos(λ) * Math.cos(φ) > cr;
3327+
}
3328+
function clipLine(sink) {
3329+
var point0, c0, v0, v00, clean;
3330+
return {
3331+
lineStart: function() {
3332+
v00 = v0 = false;
3333+
clean = 1;
3334+
},
3335+
point: function(λ, φ) {
3336+
var point1 = [ λ, φ ], point2, v = visible(λ, φ), c = smallRadius ? v ? 0 : code(λ, φ) : v ? code(λ + (λ < 0 ? π : -π), φ) : 0;
3337+
if (!point0 && (v00 = v0 = v)) sink.lineStart();
3338+
if (v !== v0) {
3339+
point2 = intersect(point0, point1);
3340+
if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) {
3341+
point1[0] += ε;
3342+
point1[1] += ε;
3343+
v = visible(point1[0], point1[1]);
3344+
}
3345+
}
3346+
if (v !== v0) {
3347+
clean = 0;
3348+
if (v) {
3349+
sink.lineStart();
3350+
point2 = intersect(point1, point0);
3351+
sink.point(point2[0], point2[1]);
3352+
} else {
3353+
point2 = intersect(point0, point1);
3354+
sink.point(point2[0], point2[1]);
3355+
sink.lineEnd();
3356+
}
3357+
point0 = point2;
3358+
} else if (notHemisphere && point0 && smallRadius ^ v) {
3359+
var t;
3360+
if (!(c & c0) && (t = intersect(point1, point0, true))) {
3361+
clean = 0;
3362+
if (smallRadius) {
3363+
sink.lineStart();
3364+
sink.point(t[0][0], t[0][1]);
3365+
sink.point(t[1][0], t[1][1]);
3366+
sink.lineEnd();
3367+
} else {
3368+
sink.point(t[1][0], t[1][1]);
3369+
sink.lineEnd();
3370+
sink.lineStart();
3371+
sink.point(t[0][0], t[0][1]);
3372+
}
3373+
}
3374+
}
3375+
if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) {
3376+
sink.point(point1[0], point1[1]);
3377+
}
3378+
point0 = point1, v0 = v, c0 = c;
3379+
},
3380+
lineEnd: function() {
3381+
if (v0) sink.lineEnd();
3382+
point0 = null;
3383+
},
3384+
clean: function() {
3385+
return clean | (v00 && v0) << 1;
3386+
}
3387+
};
3388+
}
3389+
function intersect(a, b, two) {
3390+
var pa = d3_geo_cartesian(a), pb = d3_geo_cartesian(b);
3391+
var n1 = [ 1, 0, 0 ], n2 = d3_geo_cartesianCross(pa, pb), n2n2 = d3_geo_cartesianDot(n2, n2), n1n2 = n2[0], determinant = n2n2 - n1n2 * n1n2;
3392+
if (!determinant) return !two && a;
3393+
var c1 = cr * n2n2 / determinant, c2 = -cr * n1n2 / determinant, n1xn2 = d3_geo_cartesianCross(n1, n2), A = d3_geo_cartesianScale(n1, c1), B = d3_geo_cartesianScale(n2, c2);
3394+
d3_geo_cartesianAdd(A, B);
3395+
var u = n1xn2, w = d3_geo_cartesianDot(A, u), uu = d3_geo_cartesianDot(u, u), t2 = w * w - uu * (d3_geo_cartesianDot(A, A) - 1);
3396+
if (t2 < 0) return;
3397+
var t = Math.sqrt(t2), q = d3_geo_cartesianScale(u, (-w - t) / uu);
3398+
d3_geo_cartesianAdd(q, A);
3399+
q = d3_geo_spherical(q);
3400+
if (!two) return q;
3401+
var λ0 = a[0], λ1 = b[0], φ0 = a[1], φ1 = b[1], z;
3402+
if (λ1 < λ0) z = λ0, λ0 = λ1, λ1 = z;
3403+
var δλ = λ1 - λ0, polar = abs(δλ - π) < ε, meridian = polar || δλ < ε;
3404+
if (!polar && φ1 < φ0) z = φ0, φ0 = φ1, φ1 = z;
3405+
if (meridian ? polar ? φ0 + φ1 > 0 ^ q[1] < (abs(q[0] - λ0) < ε ? φ0 : φ1) : φ0 <= q[1] && q[1] <= φ1 : δλ > π ^ (λ0 <= q[0] && q[0] <= λ1)) {
3406+
var q1 = d3_geo_cartesianScale(u, (-w + t) / uu);
3407+
d3_geo_cartesianAdd(q1, A);
3408+
return [ q, d3_geo_spherical(q1) ];
3409+
}
3410+
}
3411+
function code(λ, φ) {
3412+
var r = smallRadius ? radius : π - radius, code = 0;
3413+
if (λ < -r) code |= 1; else if (λ > r) code |= 2;
3414+
if (φ < -r) code |= 4; else if (φ > r) code |= 8;
3415+
return code;
3416+
}
3417+
};
32983418
d3.geo.graticule = function() {
32993419
var x1, x0, X1, X0, y1, y0, Y1, Y0, dx = 10, dy = dx, DX = 90, DY = 360, x, y, X, Y, precision = 2.5;
33003420
function graticule() {

d3.min.js

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)