Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
MUR Drupal
d3-library
Commits
61a1651d
Commit
61a1651d
authored
Mar 13, 2013
by
Mike Bostock
Browse files
Merge branch 'clip-circle' of
git://github.com/jasondavies/d3
into 3.1.0
parents
29d60556
d93f45ee
Changes
7
Hide whitespace changes
Inline
Side-by-side
d3.js
View file @
61a1651d
...
...
@@ -2245,16 +2245,16 @@ d3 = function() {
};
return
circle
.
angle
(
90
);
};
function d3_geo_circleInterpolate(radi
an
s, precision) {
var cr = Math.cos(radi
an
s), sr = Math.sin(radi
an
s);
function
d3_geo_circleInterpolate
(
radi
u
s
,
precision
)
{
var
cr
=
Math
.
cos
(
radi
u
s
),
sr
=
Math
.
sin
(
radi
u
s
);
return
function
(
from
,
to
,
direction
,
listener
)
{
if
(
from
!=
null
)
{
from
=
d3_geo_circleAngle
(
cr
,
from
);
to
=
d3_geo_circleAngle
(
cr
,
to
);
if
(
direction
>
0
?
from
<
to
:
from
>
to
)
from
+=
direction
*
2
*
π
;
}
else
{
from = radi
an
s + direction * 2 * π;
to = radi
an
s;
from
=
radi
u
s
+
direction
*
2
*
π
;
to
=
radi
u
s
;
}
var
point
;
for
(
var
step
=
direction
*
precision
,
t
=
from
;
direction
>
0
?
t
>
to
:
t
<
to
;
t
-=
step
)
{
...
...
@@ -2737,21 +2737,21 @@ d3 = function() {
listener
.
point
(
to
[
0
],
to
[
1
]);
}
}
function d3_geo_clipCircle(
degree
s) {
var
radians = degrees * d3_radians, cr = Math.cos(radians)
, interpolate = d3_geo_circleInterpolate(radi
an
s, 6 * d3_radians);
function
d3_geo_clipCircle
(
radiu
s
)
{
var
cr
=
Math
.
cos
(
radius
),
smallRadius
=
cr
>
0
,
notHemisphere
=
Math
.
abs
(
cr
)
>
ε
,
interpolate
=
d3_geo_circleInterpolate
(
radi
u
s
,
6
*
d3_radians
);
return
d3_geo_clip
(
visible
,
clipLine
,
interpolate
);
function
visible
(
λ
,
φ
)
{
return
Math
.
cos
(
λ
)
*
Math
.
cos
(
φ
)
>
cr
;
}
function
clipLine
(
listener
)
{
var point0, v0, v00, clean;
var
point0
,
c0
,
v0
,
v00
,
clean
;
return
{
lineStart
:
function
()
{
v00
=
v0
=
false
;
clean
=
1
;
},
point
:
function
(
λ
,
φ
)
{
var point1 = [ λ, φ ], point2, v = visible(λ, φ);
var
point1
=
[
λ
,
φ
],
point2
,
v
=
visible
(
λ
,
φ
)
,
c
=
smallRadius
?
v
?
0
:
code
(
λ
,
φ
)
:
v
?
code
(
λ
+
(
λ
<
0
?
π
:
-
π
),
φ
)
:
0
;
if
(
!
point0
&&
(
v00
=
v0
=
v
))
listener
.
lineStart
();
if
(
v
!==
v0
)
{
point2
=
intersect
(
point0
,
point1
);
...
...
@@ -2763,7 +2763,7 @@ d3 = function() {
}
if
(
v
!==
v0
)
{
clean
=
0
;
if (
v0 =
v) {
if
(
v
)
{
listener
.
lineStart
();
point2
=
intersect
(
point1
,
point0
);
listener
.
point
(
point2
[
0
],
point2
[
1
]);
...
...
@@ -2773,9 +2773,27 @@ d3 = function() {
listener
.
lineEnd
();
}
point0
=
point2
;
}
else
if
(
notHemisphere
&&
point0
&&
smallRadius
^
v
)
{
var
t
;
if
(
!
(
c
&
c0
)
&&
(
t
=
intersect
(
point1
,
point0
,
true
)))
{
clean
=
0
;
if
(
smallRadius
)
{
listener
.
lineStart
();
listener
.
point
(
t
[
0
][
0
],
t
[
0
][
1
]);
listener
.
point
(
t
[
1
][
0
],
t
[
1
][
1
]);
listener
.
lineEnd
();
}
else
{
listener
.
point
(
t
[
1
][
0
],
t
[
1
][
1
]);
listener
.
lineEnd
();
listener
.
lineStart
();
listener
.
point
(
t
[
0
][
0
],
t
[
0
][
1
]);
}
}
}
if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) listener.point(point1[0], point1[1]);
point0 = point1;
if
(
v
&&
(
!
point0
||
!
d3_geo_sphericalEqual
(
point0
,
point1
)))
{
listener
.
point
(
point1
[
0
],
point1
[
1
]);
}
point0
=
point1
,
v0
=
v
,
c0
=
c
;
},
lineEnd
:
function
()
{
if
(
v0
)
listener
.
lineEnd
();
...
...
@@ -2786,15 +2804,33 @@ d3 = function() {
}
};
}
function intersect(a, b) {
function
intersect
(
a
,
b
,
two
)
{
var
pa
=
d3_geo_cartesian
(
a
),
pb
=
d3_geo_cartesian
(
b
);
var
n1
=
[
1
,
0
,
0
],
n2
=
d3_geo_cartesianCross
(
pa
,
pb
),
n2n2
=
d3_geo_cartesianDot
(
n2
,
n2
),
n1n2
=
n2
[
0
],
determinant
=
n2n2
-
n1n2
*
n1n2
;
if (!determinant) return a;
if
(
!
determinant
)
return
!
two
&&
a
;
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
);
d3_geo_cartesianAdd
(
A
,
B
);
var u = n1xn2, w = d3_geo_cartesianDot(A, u), uu = d3_geo_cartesianDot(u, u), t = Math.sqrt(w * w - uu * (d3_geo_cartesianDot(A, A) - 1)), q = d3_geo_cartesianScale(u, (-w - t) / uu);
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
);
if
(
t2
<
0
)
return
;
var
t
=
Math
.
sqrt
(
t2
),
q
=
d3_geo_cartesianScale
(
u
,
(
-
w
-
t
)
/
uu
);
d3_geo_cartesianAdd
(
q
,
A
);
return d3_geo_spherical(q);
q
=
d3_geo_spherical
(
q
);
if
(
!
two
)
return
q
;
var
λ0
=
a
[
0
],
λ1
=
b
[
0
],
φ0
=
a
[
1
],
φ1
=
b
[
1
],
z
;
if
(
λ1
<
λ0
)
z
=
λ0
,
λ0
=
λ1
,
λ1
=
z
;
var
δλ
=
λ1
-
λ0
,
polar
=
Math
.
abs
(
δλ
-
π
)
<
ε
,
meridian
=
polar
||
δλ
<
ε
;
if
(
!
polar
&&
φ1
<
φ0
)
z
=
φ0
,
φ0
=
φ1
,
φ1
=
z
;
if
(
meridian
?
polar
?
φ0
+
φ1
>
0
^
q
[
1
]
<
(
Math
.
abs
(
q
[
0
]
-
λ0
)
<
ε
?
φ0
:
φ1
)
:
φ0
<=
q
[
1
]
&&
q
[
1
]
<=
φ1
:
δλ
>
π
^
(
λ0
<=
q
[
0
]
&&
q
[
0
]
<=
λ1
))
{
var
q1
=
d3_geo_cartesianScale
(
u
,
(
-
w
+
t
)
/
uu
);
d3_geo_cartesianAdd
(
q1
,
A
);
return
[
q
,
d3_geo_spherical
(
q1
)
];
}
}
function
code
(
λ
,
φ
)
{
var
r
=
smallRadius
?
radius
:
π
-
radius
,
code
=
0
;
if
(
λ
<
-
r
)
code
|=
1
;
else
if
(
λ
>
r
)
code
|=
2
;
if
(
φ
<
-
r
)
code
|=
4
;
else
if
(
φ
>
r
)
code
|=
8
;
return
code
;
}
}
function
d3_geo_clipView
(
x0
,
y0
,
x1
,
y1
)
{
...
...
@@ -3046,7 +3082,7 @@ d3 = function() {
};
projection
.
clipAngle
=
function
(
_
)
{
if
(
!
arguments
.
length
)
return
clipAngle
;
preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle(clipAngle = +_);
preclip
=
_
==
null
?
(
clipAngle
=
_
,
d3_geo_clipAntimeridian
)
:
d3_geo_clipCircle
(
(
clipAngle
=
+
_
)
*
d3_radians
)
;
return
projection
;
};
projection
.
clipExtent
=
function
(
_
)
{
...
...
d3.min.js
View file @
61a1651d
This source diff could not be displayed because it is too large. You can
view the blob
instead.
package.json
View file @
61a1651d
...
...
@@ -31,7 +31,7 @@
"
jsdom
"
:
"
~0.5.2
"
},
"devDependencies"
:
{
"
smash
"
:
"
~0.0.
3
"
,
"
smash
"
:
"
~0.0.
4
"
,
"
uglify-js
"
:
"
2.2.x
"
,
"
vows
"
:
"
0.7.x
"
},
...
...
src/geo/circle.js
View file @
61a1651d
...
...
@@ -48,17 +48,17 @@ d3.geo.circle = function() {
// Interpolates along a circle centered at [0°, 0°], with a given radius and
// precision.
function
d3_geo_circleInterpolate
(
radi
an
s
,
precision
)
{
var
cr
=
Math
.
cos
(
radi
an
s
),
sr
=
Math
.
sin
(
radi
an
s
);
function
d3_geo_circleInterpolate
(
radi
u
s
,
precision
)
{
var
cr
=
Math
.
cos
(
radi
u
s
),
sr
=
Math
.
sin
(
radi
u
s
);
return
function
(
from
,
to
,
direction
,
listener
)
{
if
(
from
!=
null
)
{
from
=
d3_geo_circleAngle
(
cr
,
from
);
to
=
d3_geo_circleAngle
(
cr
,
to
);
if
(
direction
>
0
?
from
<
to
:
from
>
to
)
from
+=
direction
*
2
*
π
;
}
else
{
from
=
radi
an
s
+
direction
*
2
*
π
;
to
=
radi
an
s
;
from
=
radi
u
s
+
direction
*
2
*
π
;
to
=
radi
u
s
;
}
var
point
;
for
(
var
step
=
direction
*
precision
,
t
=
from
;
direction
>
0
?
t
>
to
:
t
<
to
;
t
-=
step
)
{
...
...
src/geo/clip-circle.js
View file @
61a1651d
...
...
@@ -4,11 +4,12 @@ import "clip";
import
"
circle
"
;
import
"
spherical
"
;
// Clip features against a circle centered at [0°, 0°], with a given radius.
function
d3_geo_clipCircle
(
degrees
)
{
var
radians
=
degrees
*
d3_radians
,
cr
=
Math
.
cos
(
radians
),
interpolate
=
d3_geo_circleInterpolate
(
radians
,
6
*
d3_radians
);
// Clip features against a small circle centered at [0°, 0°].
function
d3_geo_clipCircle
(
radius
)
{
var
cr
=
Math
.
cos
(
radius
),
smallRadius
=
cr
>
0
,
notHemisphere
=
Math
.
abs
(
cr
)
>
ε
,
// TODO optimise for this common case
interpolate
=
d3_geo_circleInterpolate
(
radius
,
6
*
d3_radians
);
return
d3_geo_clip
(
visible
,
clipLine
,
interpolate
);
...
...
@@ -16,7 +17,6 @@ function d3_geo_clipCircle(degrees) {
return
Math
.
cos
(
λ
)
*
Math
.
cos
(
φ
)
>
cr
;
}
// TODO handle two invisible endpoints with visible intermediate segment.
// Takes a line and cuts into visible segments. Return values used for
// polygon clipping:
// 0: there were intersections or the line was empty.
...
...
@@ -24,9 +24,10 @@ function d3_geo_clipCircle(degrees) {
// 2: there were intersections, and the first and last segments should be
// rejoined.
function
clipLine
(
listener
)
{
var
point0
,
v0
,
v00
,
var
point0
,
// previous point
c0
,
// code for previous point
v0
,
// visibility of previous point
v00
,
// visibility of first point
clean
;
// no intersections
return
{
lineStart
:
function
()
{
...
...
@@ -36,9 +37,13 @@ function d3_geo_clipCircle(degrees) {
point
:
function
(
λ
,
φ
)
{
var
point1
=
[
λ
,
φ
],
point2
,
v
=
visible
(
λ
,
φ
);
v
=
visible
(
λ
,
φ
),
c
=
smallRadius
?
v
?
0
:
code
(
λ
,
φ
)
:
v
?
code
(
λ
+
(
λ
<
0
?
π
:
-
π
),
φ
)
:
0
;
if
(
!
point0
&&
(
v00
=
v0
=
v
))
listener
.
lineStart
();
// handle degeneracies
// Handle degeneracies.
// TODO ignore if not clipping polygons.
if
(
v
!==
v0
)
{
point2
=
intersect
(
point0
,
point1
);
if
(
d3_geo_sphericalEqual
(
point0
,
point2
)
||
d3_geo_sphericalEqual
(
point1
,
point2
))
{
...
...
@@ -49,7 +54,7 @@ function d3_geo_clipCircle(degrees) {
}
if
(
v
!==
v0
)
{
clean
=
0
;
if
(
v0
=
v
)
{
if
(
v
)
{
// outside going in
listener
.
lineStart
();
point2
=
intersect
(
point1
,
point0
);
...
...
@@ -61,9 +66,29 @@ function d3_geo_clipCircle(degrees) {
listener
.
lineEnd
();
}
point0
=
point2
;
}
else
if
(
notHemisphere
&&
point0
&&
smallRadius
^
v
)
{
var
t
;
// If the codes for two points are different, or are both zero,
// and there this segment intersects with the small circle.
if
(
!
(
c
&
c0
)
&&
(
t
=
intersect
(
point1
,
point0
,
true
)))
{
clean
=
0
;
if
(
smallRadius
)
{
listener
.
lineStart
();
listener
.
point
(
t
[
0
][
0
],
t
[
0
][
1
]);
listener
.
point
(
t
[
1
][
0
],
t
[
1
][
1
]);
listener
.
lineEnd
();
}
else
{
listener
.
point
(
t
[
1
][
0
],
t
[
1
][
1
]);
listener
.
lineEnd
();
listener
.
lineStart
();
listener
.
point
(
t
[
0
][
0
],
t
[
0
][
1
]);
}
}
}
if
(
v
&&
(
!
point0
||
!
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
;
point0
=
point1
,
v0
=
v
,
c0
=
c
;
},
lineEnd
:
function
()
{
if
(
v0
)
listener
.
lineEnd
();
...
...
@@ -76,18 +101,20 @@ function d3_geo_clipCircle(degrees) {
}
// Intersects the great circle between a and b with the clip circle.
function
intersect
(
a
,
b
)
{
function
intersect
(
a
,
b
,
two
)
{
var
pa
=
d3_geo_cartesian
(
a
),
pb
=
d3_geo_cartesian
(
b
);
// We have two planes, n1.p = d1 and n2.p = d2.
// Find intersection line p(t) = c1 n1 + c2 n2 + t (n1
x
n2).
// Find intersection line p(t) = c1 n1 + c2 n2 + t (n1
⨯
n2).
var
n1
=
[
1
,
0
,
0
],
// normal
n2
=
d3_geo_cartesianCross
(
pa
,
pb
),
n2n2
=
d3_geo_cartesianDot
(
n2
,
n2
),
n1n2
=
n2
[
0
],
// d3_geo_cartesianDot(n1, n2),
determinant
=
n2n2
-
n1n2
*
n1n2
;
// Two polar points.
if
(
!
determinant
)
return
a
;
if
(
!
determinant
)
return
!
two
&&
a
;
var
c1
=
cr
*
n2n2
/
determinant
,
c2
=
-
cr
*
n1n2
/
determinant
,
...
...
@@ -95,13 +122,55 @@ function d3_geo_clipCircle(degrees) {
A
=
d3_geo_cartesianScale
(
n1
,
c1
),
B
=
d3_geo_cartesianScale
(
n2
,
c2
);
d3_geo_cartesianAdd
(
A
,
B
);
// Now solve |p(t)|^2 = 1.
// Solve |p(t)|^2 = 1.
var
u
=
n1xn2
,
w
=
d3_geo_cartesianDot
(
A
,
u
),
uu
=
d3_geo_cartesianDot
(
u
,
u
),
t
=
Math
.
sqrt
(
w
*
w
-
uu
*
(
d3_geo_cartesianDot
(
A
,
A
)
-
1
)),
t2
=
w
*
w
-
uu
*
(
d3_geo_cartesianDot
(
A
,
A
)
-
1
);
if
(
t2
<
0
)
return
;
var
t
=
Math
.
sqrt
(
t2
),
q
=
d3_geo_cartesianScale
(
u
,
(
-
w
-
t
)
/
uu
);
d3_geo_cartesianAdd
(
q
,
A
);
return
d3_geo_spherical
(
q
);
q
=
d3_geo_spherical
(
q
);
if
(
!
two
)
return
q
;
// Two intersection points.
var
λ0
=
a
[
0
],
λ1
=
b
[
0
],
φ0
=
a
[
1
],
φ1
=
b
[
1
],
z
;
if
(
λ1
<
λ0
)
z
=
λ0
,
λ0
=
λ1
,
λ1
=
z
;
var
δλ
=
λ1
-
λ0
,
polar
=
Math
.
abs
(
δλ
-
π
)
<
ε
,
meridian
=
polar
||
δλ
<
ε
;
if
(
!
polar
&&
φ1
<
φ0
)
z
=
φ0
,
φ0
=
φ1
,
φ1
=
z
;
// Check that the first point is between a and b.
if
(
meridian
?
polar
?
φ0
+
φ1
>
0
^
q
[
1
]
<
(
Math
.
abs
(
q
[
0
]
-
λ0
)
<
ε
?
φ0
:
φ1
)
:
φ0
<=
q
[
1
]
&&
q
[
1
]
<=
φ1
:
δλ
>
π
^
(
λ0
<=
q
[
0
]
&&
q
[
0
]
<=
λ1
))
{
var
q1
=
d3_geo_cartesianScale
(
u
,
(
-
w
+
t
)
/
uu
);
d3_geo_cartesianAdd
(
q1
,
A
);
return
[
q
,
d3_geo_spherical
(
q1
)];
}
}
// Generates a 4-bit vector representing the location of a point relative to
// the small circle's bounding box.
function
code
(
λ
,
φ
)
{
var
r
=
smallRadius
?
radius
:
π
-
radius
,
code
=
0
;
if
(
λ
<
-
r
)
code
|=
1
;
// left
else
if
(
λ
>
r
)
code
|=
2
;
// right
if
(
φ
<
-
r
)
code
|=
4
;
// below
else
if
(
φ
>
r
)
code
|=
8
;
// above
return
code
;
}
}
src/geo/projection.js
View file @
61a1651d
...
...
@@ -48,7 +48,7 @@ function d3_geo_projectionMutator(projectAt) {
projection
.
clipAngle
=
function
(
_
)
{
if
(
!
arguments
.
length
)
return
clipAngle
;
preclip
=
_
==
null
?
(
clipAngle
=
_
,
d3_geo_clipAntimeridian
)
:
d3_geo_clipCircle
(
clipAngle
=
+
_
);
preclip
=
_
==
null
?
(
clipAngle
=
_
,
d3_geo_clipAntimeridian
)
:
d3_geo_clipCircle
(
(
clipAngle
=
+
_
)
*
d3_radians
)
;
return
projection
;
};
...
...
test/geo/path-test.js
View file @
61a1651d
...
...
@@ -589,6 +589,42 @@ suite.addBatch({
}
}
},
"
clipAngle(30)
"
:
{
topic
:
function
()
{
return
d3
.
geo
.
path
()
.
context
(
testContext
)
.
projection
(
d3
.
geo
.
equirectangular
()
.
scale
(
900
/
Math
.
PI
)
.
precision
(
0
)
.
clipAngle
(
30
));
},
"
clips lines with two invisible endpoints and visible middle
"
:
function
(
path
)
{
path
({
type
:
"
LineString
"
,
coordinates
:
[[
-
45
,
0
],
[
45
,
0
]]});
assert
.
deepEqual
(
testContext
.
buffer
(),
[
{
type
:
"
moveTo
"
,
x
:
330
,
y
:
250
},
{
type
:
"
lineTo
"
,
x
:
630
,
y
:
250
}
]);
}
},
"
clipAngle(150)
"
:
{
topic
:
function
()
{
return
d3
.
geo
.
path
()
.
context
(
testContext
)
.
projection
(
d3
.
geo
.
equirectangular
()
.
scale
(
900
/
Math
.
PI
)
.
precision
(
0
)
.
clipAngle
(
150
));
},
"
clips lines with two visible endpoints and invisible middle
"
:
function
(
path
)
{
path
({
type
:
"
LineString
"
,
coordinates
:
[[
135
,
0
],
[
-
135
,
0
]]});
assert
.
deepEqual
(
testContext
.
buffer
(),
[
{
type
:
"
moveTo
"
,
x
:
1155
,
y
:
250
},
{
type
:
"
lineTo
"
,
x
:
1230
,
y
:
250
},
{
type
:
"
moveTo
"
,
x
:
-
270
,
y
:
250
},
{
type
:
"
lineTo
"
,
x
:
-
195
,
y
:
250
}
]);
}
},
"
antimeridian cutting
"
:
{
"
rotate([98, 0])
"
:
{
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment