Skip to content

Commit 2a6c2db

Browse files
committed
Optimise d3.geo.clip.
We can interpolate the exact distance to the clipping edge, and use this to interpolate the point exactly instead of computing a full path and picking the closest point.
1 parent 8ad87e4 commit 2a6c2db

3 files changed

Lines changed: 63 additions & 59 deletions

File tree

d3.geo.js

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -738,37 +738,32 @@ d3.geo.clip = function() {
738738
o = {source: origin, target: null},
739739
n = d.length,
740740
i = -1,
741-
j,
742741
path,
743742
clipped = [],
744743
p = null,
745-
q = null;
744+
q = null,
745+
distance,
746+
oldDistance;
746747
while (++i < n) {
747748
o.target = d[i];
748749
distance = d3_geo_clipGreatCircle.distance(o);
749750
if (distance < r) {
750-
if (q) {
751-
path = d3_geo_clipGreatCircle({source: q, target: o.target});
752-
j = d3_geo_clipClosest(path, o, r);
753-
if (path.length) clipped.push(path[j]);
754-
}
751+
if (q) clipped.push(d3_geo_clipIntersect(q, o.target, (oldDistance - r) / (oldDistance - distance)));
755752
clipped.push(d[i]);
756753
p = q = null;
757754
} else {
758755
q = o.target;
759756
if (!p && clipped.length) {
760-
path = d3_geo_clipGreatCircle({source: clipped[clipped.length - 1], target: o.target});
761-
j = d3_geo_clipClosest(path, o, r);
762-
if (path.length) clipped.push(path[j]);
763-
p = o.target;
757+
clipped.push(d3_geo_clipIntersect(clipped[clipped.length - 1], q, (r - oldDistance) / (distance - oldDistance)));
758+
p = q;
764759
}
765760
}
761+
oldDistance = distance;
766762
}
767763
if (q && clipped.length) {
768764
o.target = clipped[0];
769-
path = d3_geo_clipGreatCircle({source: q, target: o.target});
770-
j = d3_geo_clipClosest(path, o, r);
771-
if (path.length) clipped.push(path[j]);
765+
distance = d3_geo_clipGreatCircle.distance(o);
766+
clipped.push(d3_geo_clipIntersect(q, o.target, (oldDistance - r) / (oldDistance - distance)));
772767
}
773768
return clipped;
774769
}
@@ -799,20 +794,27 @@ var d3_geo_clipGreatCircle = d3.geo.greatCircle().coordinates(function(d) {
799794
return [d.source, d.target];
800795
});
801796

802-
function d3_geo_clipClosest(path, o, r) {
803-
var i = -1,
804-
n = path.length,
805-
index = 0,
806-
best = Infinity;
807-
while (++i < n) {
808-
o.target = path[i];
809-
var d = Math.abs(d3_geo_clipGreatCircle.distance(o) - r);
810-
if (d < best) {
811-
best = d;
812-
index = i;
813-
}
814-
}
815-
o.target = path[index];
816-
return index;
797+
function d3_geo_clipIntersect(from, to, f) {
798+
var x0 = from[0] * d3_radians,
799+
y0 = from[1] * d3_radians,
800+
x1 = to[0] * d3_radians,
801+
y1 = to[1] * d3_radians,
802+
cx0 = Math.cos(x0), sx0 = Math.sin(x0),
803+
cy0 = Math.cos(y0), sy0 = Math.sin(y0),
804+
cx1 = Math.cos(x1), sx1 = Math.sin(x1),
805+
cy1 = Math.cos(y1), sy1 = Math.sin(y1),
806+
d = Math.acos(Math.max(-1, Math.min(1, sy0 * sy1 + cy0 * cy1 * Math.cos(x1 - x0)))),
807+
e = f * d,
808+
sd = Math.sin(d),
809+
A = Math.sin(d - e) / sd,
810+
B = Math.sin(e) / sd,
811+
x = A * cy0 * cx0 + B * cy1 * cx1,
812+
y = A * cy0 * sx0 + B * cy1 * sx1,
813+
z = A * sy0 + B * sy1;
814+
815+
return [
816+
Math.atan2(y, x) / d3_radians,
817+
Math.atan2(z, Math.sqrt(x * x + y * y)) / d3_radians
818+
];
817819
}
818820
})();

0 commit comments

Comments
 (0)