Merge branch '3.2'
This commit is contained in:
Коммит
84f4a62c01
2
Makefile
2
Makefile
|
@ -33,7 +33,7 @@ d3.min.js: d3.js
|
|||
@rm -f $@
|
||||
bin/uglify $< > $@
|
||||
|
||||
component.json: bin/component d3.js package.json
|
||||
component.json: bin/component package.json
|
||||
@rm -f $@
|
||||
bin/component > $@
|
||||
@chmod a-w $@
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "d3",
|
||||
"version": "3.1.10",
|
||||
"version": "3.2.0",
|
||||
"main": "index-browserify.js",
|
||||
"scripts": [
|
||||
"index-browserify.js",
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -0,0 +1,23 @@
|
|||
This license applies to GeographicLib, versions 1.12 and later.
|
||||
|
||||
Copyright (c) 2008-2012, Charles Karney
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use, copy,
|
||||
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "d3",
|
||||
"version": "3.1.10",
|
||||
"version": "3.2.0",
|
||||
"description": "A small, free JavaScript library for manipulating documents based on data.",
|
||||
"keywords": [
|
||||
"dom",
|
||||
|
@ -38,7 +38,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"smash": "~0.0.8",
|
||||
"uglify-js": "git://github.com/mishoo/UglifyJS2.git#3bd7ca9961125b39dcd54d2182cb72bd1ca6006e",
|
||||
"uglify-js": "2.3.6",
|
||||
"vows": "0.7.x"
|
||||
},
|
||||
"scripts": {
|
||||
|
|
|
@ -3,6 +3,7 @@ import "../core/rebind";
|
|||
import "../event/event";
|
||||
import "../event/mouse";
|
||||
import "../event/touches";
|
||||
import "../event/user-select";
|
||||
import "behavior";
|
||||
|
||||
d3.behavior.drag = function() {
|
||||
|
@ -21,7 +22,8 @@ d3.behavior.drag = function() {
|
|||
touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null,
|
||||
offset,
|
||||
origin_ = point(),
|
||||
moved = 0;
|
||||
moved = 0,
|
||||
selectEnable = d3_event_userSelectSuppress(touchId != null ? "drag-" + touchId : "drag");
|
||||
|
||||
var w = d3.select(d3_window)
|
||||
.on(touchId != null ? "touchmove.drag-" + touchId : "mousemove.drag", dragmove)
|
||||
|
@ -34,8 +36,6 @@ d3.behavior.drag = function() {
|
|||
offset = [0, 0];
|
||||
}
|
||||
|
||||
// Only cancel mousedown; touchstart is needed for draggable links.
|
||||
if (touchId == null) d3_eventCancel();
|
||||
event_({type: "dragstart"});
|
||||
|
||||
function point() {
|
||||
|
@ -70,6 +70,7 @@ d3.behavior.drag = function() {
|
|||
|
||||
w .on(touchId != null ? "touchmove.drag-" + touchId : "mousemove.drag", null)
|
||||
.on(touchId != null ? "touchend.drag-" + touchId : "mouseup.drag", null);
|
||||
selectEnable();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import "../core/rebind";
|
|||
import "../event/event";
|
||||
import "../event/mouse";
|
||||
import "../event/touches";
|
||||
import "../event/user-select";
|
||||
import "../selection/selection";
|
||||
import "behavior";
|
||||
|
||||
|
@ -102,10 +103,8 @@ d3.behavior.zoom = function() {
|
|||
eventTarget = d3.event.target,
|
||||
moved = 0,
|
||||
w = d3.select(d3_window).on("mousemove.zoom", mousemove).on("mouseup.zoom", mouseup),
|
||||
l = location(d3.mouse(target));
|
||||
|
||||
d3_window.focus();
|
||||
d3_eventCancel();
|
||||
l = location(d3.mouse(target)),
|
||||
selectEnable = d3_event_userSelectSuppress("zoom");
|
||||
|
||||
function mousemove() {
|
||||
moved = 1;
|
||||
|
@ -116,6 +115,7 @@ d3.behavior.zoom = function() {
|
|||
function mouseup() {
|
||||
if (moved) d3_eventCancel();
|
||||
w.on("mousemove.zoom", null).on("mouseup.zoom", null);
|
||||
selectEnable();
|
||||
if (moved && d3.event.target === eventTarget) d3_eventSuppress(w, "click.zoom");
|
||||
}
|
||||
}
|
||||
|
@ -145,7 +145,6 @@ d3.behavior.zoom = function() {
|
|||
scale0 = scale;
|
||||
translate0 = {};
|
||||
touches.forEach(function(t) { translate0[t.identifier] = location(t); });
|
||||
d3_eventCancel();
|
||||
|
||||
if (touches.length === 1) {
|
||||
if (now - touchtime < 500) { // dbltap
|
||||
|
|
|
@ -13,7 +13,7 @@ function d3_arraySlice(pseudoarray) {
|
|||
}
|
||||
|
||||
try {
|
||||
d3_array(d3_document.documentElement.childNodes)[0].nodeType;
|
||||
d3_array(d3_documentElement.childNodes)[0].nodeType;
|
||||
} catch(e) {
|
||||
d3_array = d3_arrayCopy;
|
||||
}
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
var d3_document = document,
|
||||
d3_documentElement = d3_document.documentElement,
|
||||
d3_window = window;
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import "document";
|
||||
|
||||
function d3_vendorSymbol(object, name) {
|
||||
if (name in object) return name;
|
||||
name = name.charAt(0).toUpperCase() + name.substring(1);
|
||||
for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) {
|
||||
var prefixName = d3_vendorPrefixes[i] + name;
|
||||
if (prefixName in object) return prefixName;
|
||||
}
|
||||
}
|
||||
|
||||
var d3_vendorPrefixes = ["webkit", "ms", "moz", "Moz", "o", "O"];
|
|
@ -1,3 +1,3 @@
|
|||
import "dsv";
|
||||
|
||||
d3.csv = d3_dsv(",", "text/csv");
|
||||
d3.csv = d3.dsv(",", "text/csv");
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import "../arrays/set";
|
||||
import "../xhr/xhr";
|
||||
|
||||
function d3_dsv(delimiter, mimeType) {
|
||||
d3.dsv = function(delimiter, mimeType) {
|
||||
var reFormat = new RegExp("[\"" + delimiter + "\n]"),
|
||||
delimiterCode = delimiter.charCodeAt(0);
|
||||
|
||||
|
@ -133,4 +133,4 @@ function d3_dsv(delimiter, mimeType) {
|
|||
}
|
||||
|
||||
return dsv;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
import "dsv";
|
||||
|
||||
d3.tsv = d3_dsv("\t", "text/tab-separated-values");
|
||||
d3.tsv = d3.dsv("\t", "text/tab-separated-values");
|
||||
|
|
|
@ -12,10 +12,14 @@ function d3_mousePoint(container, e) {
|
|||
if (svg.createSVGPoint) {
|
||||
var point = svg.createSVGPoint();
|
||||
if (d3_mouse_bug44083 < 0 && (d3_window.scrollX || d3_window.scrollY)) {
|
||||
svg = d3.select(d3_document.body).append("svg")
|
||||
.style("position", "absolute")
|
||||
.style("top", 0)
|
||||
.style("left", 0);
|
||||
svg = d3.select("body").append("svg").style({
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
border: "none"
|
||||
}, "important");
|
||||
var ctm = svg[0][0].getScreenCTM();
|
||||
d3_mouse_bug44083 = !(ctm.f || ctm.e);
|
||||
svg.remove();
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import "../core/document";
|
||||
import "../core/vendor";
|
||||
|
||||
var d3_timer_queueHead,
|
||||
d3_timer_queueTail,
|
||||
|
@ -77,9 +78,5 @@ function d3_timer_sweep() {
|
|||
return time;
|
||||
}
|
||||
|
||||
var d3_timer_frame = d3_window.requestAnimationFrame
|
||||
|| d3_window.webkitRequestAnimationFrame
|
||||
|| d3_window.mozRequestAnimationFrame
|
||||
|| d3_window.oRequestAnimationFrame
|
||||
|| d3_window.msRequestAnimationFrame
|
||||
var d3_timer_frame = d3_window[d3_vendorSymbol(d3_window, "requestAnimationFrame")]
|
||||
|| function(callback) { setTimeout(callback, 17); };
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import "../core/document";
|
||||
import "../core/vendor";
|
||||
import "event";
|
||||
|
||||
var d3_event_userSelectProperty = d3_vendorSymbol(d3_documentElement.style, "userSelect"),
|
||||
d3_event_userSelectSuppress = d3_event_userSelectProperty
|
||||
? function() {
|
||||
var style = d3_documentElement.style,
|
||||
select = style[d3_event_userSelectProperty];
|
||||
style[d3_event_userSelectProperty] = "none";
|
||||
return function() { style[d3_event_userSelectProperty] = select; };
|
||||
}
|
||||
: function(type) {
|
||||
var w = d3.select(d3_window).on("selectstart." + type, d3_eventCancel);
|
||||
return function() { w.on("selectstart." + type, null); };
|
||||
};
|
|
@ -1,4 +1,5 @@
|
|||
import "../core/noop";
|
||||
import "../math/adder";
|
||||
import "../math/trigonometry";
|
||||
import "geo";
|
||||
import "stream";
|
||||
|
@ -10,7 +11,7 @@ d3.geo.area = function(object) {
|
|||
};
|
||||
|
||||
var d3_geo_areaSum,
|
||||
d3_geo_areaRingSum;
|
||||
d3_geo_areaRingSum = new d3_adder;
|
||||
|
||||
var d3_geo_area = {
|
||||
sphere: function() { d3_geo_areaSum += 4 * π; },
|
||||
|
@ -20,7 +21,7 @@ var d3_geo_area = {
|
|||
|
||||
// Only count area for polygon rings.
|
||||
polygonStart: function() {
|
||||
d3_geo_areaRingSum = 0;
|
||||
d3_geo_areaRingSum.reset();
|
||||
d3_geo_area.lineStart = d3_geo_areaRingStart;
|
||||
},
|
||||
polygonEnd: function() {
|
||||
|
@ -31,7 +32,7 @@ var d3_geo_area = {
|
|||
};
|
||||
|
||||
function d3_geo_areaRingStart() {
|
||||
var λ00, φ00, λ0, cosφ0, sinφ0; // start point and two previous points
|
||||
var λ00, φ00, λ0, cosφ0, sinφ0; // start point and previous point
|
||||
|
||||
// For the first point, …
|
||||
d3_geo_area.point = function(λ, φ) {
|
||||
|
@ -53,7 +54,7 @@ function d3_geo_areaRingStart() {
|
|||
k = sinφ0 * sinφ,
|
||||
u = cosφ0 * cosφ + k * Math.cos(dλ),
|
||||
v = k * Math.sin(dλ);
|
||||
d3_geo_areaRingSum += Math.atan2(v, u);
|
||||
d3_geo_areaRingSum.add(Math.atan2(v, u));
|
||||
|
||||
// Advance the previous points.
|
||||
λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ;
|
||||
|
|
|
@ -136,29 +136,34 @@ d3.geo.bounds = (function() {
|
|||
|
||||
d3.geo.stream(feature, bound);
|
||||
|
||||
// First, sort ranges by their minimum longitudes.
|
||||
ranges.sort(compareRanges);
|
||||
var n = ranges.length;
|
||||
if (n) {
|
||||
// First, sort ranges by their minimum longitudes.
|
||||
ranges.sort(compareRanges);
|
||||
|
||||
// Then, merge any ranges that overlap.
|
||||
for (var i = 1, n = ranges.length, a = ranges[0], b, merged = [a]; i < n; ++i) {
|
||||
b = ranges[i];
|
||||
if (withinRange(b[0], a) || withinRange(b[1], a)) {
|
||||
if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];
|
||||
if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];
|
||||
} else {
|
||||
merged.push(a = b);
|
||||
// Then, merge any ranges that overlap.
|
||||
for (var i = 1, a = ranges[0], b, merged = [a]; i < n; ++i) {
|
||||
b = ranges[i];
|
||||
if (withinRange(b[0], a) || withinRange(b[1], a)) {
|
||||
if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];
|
||||
if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];
|
||||
} else {
|
||||
merged.push(a = b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, find the largest gap between the merged ranges.
|
||||
// The final bounding box will be the inverse of this gap.
|
||||
var best = -Infinity, dλ;
|
||||
for (var n = merged.length - 1, i = 0, a = merged[n], b; i <= n; a = b, ++i) {
|
||||
b = merged[i];
|
||||
if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1];
|
||||
// Finally, find the largest gap between the merged ranges.
|
||||
// The final bounding box will be the inverse of this gap.
|
||||
var best = -Infinity, dλ;
|
||||
for (var n = merged.length - 1, i = 0, a = merged[n], b; i <= n; a = b, ++i) {
|
||||
b = merged[i];
|
||||
if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1];
|
||||
}
|
||||
}
|
||||
ranges = range = null;
|
||||
|
||||
return [[λ0, φ0], [λ1, φ1]];
|
||||
return λ0 === Infinity || φ0 === Infinity
|
||||
? [[NaN, NaN], [NaN, NaN]]
|
||||
: [[λ0, φ0], [λ1, φ1]];
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -1,41 +1,51 @@
|
|||
import "../core/noop";
|
||||
import "../math/trigonometry";
|
||||
import "geo";
|
||||
import "stream";
|
||||
|
||||
d3.geo.centroid = function(object) {
|
||||
d3_geo_centroidDimension = d3_geo_centroidW = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0;
|
||||
d3_geo_centroidW0 = d3_geo_centroidW1 =
|
||||
d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 =
|
||||
d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 =
|
||||
d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0;
|
||||
d3.geo.stream(object, d3_geo_centroid);
|
||||
var m;
|
||||
if (d3_geo_centroidW &&
|
||||
Math.abs(m = Math.sqrt(d3_geo_centroidX * d3_geo_centroidX + d3_geo_centroidY * d3_geo_centroidY + d3_geo_centroidZ * d3_geo_centroidZ)) > ε) {
|
||||
return [
|
||||
Math.atan2(d3_geo_centroidY, d3_geo_centroidX) * d3_degrees,
|
||||
Math.asin(Math.max(-1, Math.min(1, d3_geo_centroidZ / m))) * d3_degrees
|
||||
];
|
||||
|
||||
var x = d3_geo_centroidX2,
|
||||
y = d3_geo_centroidY2,
|
||||
z = d3_geo_centroidZ2,
|
||||
m = x * x + y * y + z * z;
|
||||
|
||||
// If the area-weighted centroid is undefined, fall back to length-weighted centroid.
|
||||
if (m < ε2) {
|
||||
x = d3_geo_centroidX1, y = d3_geo_centroidY1, z = d3_geo_centroidZ1;
|
||||
// If the feature has zero length, fall back to arithmetic mean of point vectors.
|
||||
if (d3_geo_centroidW1 < ε) x = d3_geo_centroidX0, y = d3_geo_centroidY0, z = d3_geo_centroidZ0;
|
||||
m = x * x + y * y + z * z;
|
||||
// If the feature still has an undefined centroid, then return.
|
||||
if (m < ε2) return [NaN, NaN];
|
||||
}
|
||||
|
||||
return [Math.atan2(y, x) * d3_degrees, d3_asin(z / Math.sqrt(m)) * d3_degrees];
|
||||
};
|
||||
|
||||
var d3_geo_centroidDimension,
|
||||
d3_geo_centroidW,
|
||||
d3_geo_centroidX,
|
||||
d3_geo_centroidY,
|
||||
d3_geo_centroidZ;
|
||||
var d3_geo_centroidW0,
|
||||
d3_geo_centroidW1,
|
||||
d3_geo_centroidX0,
|
||||
d3_geo_centroidY0,
|
||||
d3_geo_centroidZ0,
|
||||
d3_geo_centroidX1,
|
||||
d3_geo_centroidY1,
|
||||
d3_geo_centroidZ1,
|
||||
d3_geo_centroidX2,
|
||||
d3_geo_centroidY2,
|
||||
d3_geo_centroidZ2;
|
||||
|
||||
var d3_geo_centroid = {
|
||||
sphere: function() {
|
||||
if (d3_geo_centroidDimension < 2) {
|
||||
d3_geo_centroidDimension = 2;
|
||||
d3_geo_centroidW = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0;
|
||||
}
|
||||
},
|
||||
sphere: d3_noop,
|
||||
point: d3_geo_centroidPoint,
|
||||
lineStart: d3_geo_centroidLineStart,
|
||||
lineEnd: d3_geo_centroidLineEnd,
|
||||
polygonStart: function() {
|
||||
if (d3_geo_centroidDimension < 2) {
|
||||
d3_geo_centroidDimension = 2;
|
||||
d3_geo_centroidW = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0;
|
||||
}
|
||||
d3_geo_centroid.lineStart = d3_geo_centroidRingStart;
|
||||
},
|
||||
polygonEnd: function() {
|
||||
|
@ -45,42 +55,21 @@ var d3_geo_centroid = {
|
|||
|
||||
// Arithmetic mean of Cartesian vectors.
|
||||
function d3_geo_centroidPoint(λ, φ) {
|
||||
if (d3_geo_centroidDimension) return;
|
||||
++d3_geo_centroidW;
|
||||
λ *= d3_radians;
|
||||
var cosφ = Math.cos(φ *= d3_radians);
|
||||
d3_geo_centroidX += (cosφ * Math.cos(λ) - d3_geo_centroidX) / d3_geo_centroidW;
|
||||
d3_geo_centroidY += (cosφ * Math.sin(λ) - d3_geo_centroidY) / d3_geo_centroidW;
|
||||
d3_geo_centroidZ += (Math.sin(φ) - d3_geo_centroidZ) / d3_geo_centroidW;
|
||||
d3_geo_centroidPointXYZ(cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ));
|
||||
}
|
||||
|
||||
function d3_geo_centroidRingStart() {
|
||||
var λ00, φ00; // first point
|
||||
|
||||
d3_geo_centroidDimension = 1;
|
||||
d3_geo_centroidLineStart();
|
||||
d3_geo_centroidDimension = 2;
|
||||
|
||||
var linePoint = d3_geo_centroid.point;
|
||||
d3_geo_centroid.point = function(λ, φ) {
|
||||
linePoint(λ00 = λ, φ00 = φ);
|
||||
};
|
||||
d3_geo_centroid.lineEnd = function() {
|
||||
d3_geo_centroid.point(λ00, φ00);
|
||||
d3_geo_centroidLineEnd();
|
||||
d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd;
|
||||
};
|
||||
function d3_geo_centroidPointXYZ(x, y, z) {
|
||||
++d3_geo_centroidW0;
|
||||
d3_geo_centroidX0 += (x - d3_geo_centroidX0) / d3_geo_centroidW0;
|
||||
d3_geo_centroidY0 += (y - d3_geo_centroidY0) / d3_geo_centroidW0;
|
||||
d3_geo_centroidZ0 += (z - d3_geo_centroidZ0) / d3_geo_centroidW0;
|
||||
}
|
||||
|
||||
function d3_geo_centroidLineStart() {
|
||||
var x0, y0, z0; // previous point
|
||||
|
||||
if (d3_geo_centroidDimension > 1) return;
|
||||
if (d3_geo_centroidDimension < 1) {
|
||||
d3_geo_centroidDimension = 1;
|
||||
d3_geo_centroidW = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0;
|
||||
}
|
||||
|
||||
d3_geo_centroid.point = function(λ, φ) {
|
||||
λ *= d3_radians;
|
||||
var cosφ = Math.cos(φ *= d3_radians);
|
||||
|
@ -88,6 +77,7 @@ function d3_geo_centroidLineStart() {
|
|||
y0 = cosφ * Math.sin(λ);
|
||||
z0 = Math.sin(φ);
|
||||
d3_geo_centroid.point = nextPoint;
|
||||
d3_geo_centroidPointXYZ(x0, y0, z0);
|
||||
};
|
||||
|
||||
function nextPoint(λ, φ) {
|
||||
|
@ -99,13 +89,61 @@ function d3_geo_centroidLineStart() {
|
|||
w = Math.atan2(
|
||||
Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w),
|
||||
x0 * x + y0 * y + z0 * z);
|
||||
d3_geo_centroidW += w;
|
||||
d3_geo_centroidX += w * (x0 + (x0 = x));
|
||||
d3_geo_centroidY += w * (y0 + (y0 = y));
|
||||
d3_geo_centroidZ += w * (z0 + (z0 = z));
|
||||
d3_geo_centroidW1 += w;
|
||||
d3_geo_centroidX1 += w * (x0 + (x0 = x));
|
||||
d3_geo_centroidY1 += w * (y0 + (y0 = y));
|
||||
d3_geo_centroidZ1 += w * (z0 + (z0 = z));
|
||||
d3_geo_centroidPointXYZ(x0, y0, z0);
|
||||
}
|
||||
}
|
||||
|
||||
function d3_geo_centroidLineEnd() {
|
||||
d3_geo_centroid.point = d3_geo_centroidPoint;
|
||||
}
|
||||
|
||||
// See J. E. Brock, The Inertia Tensor for a Spherical Triangle,
|
||||
// J. Applied Mechanics 42, 239 (1975).
|
||||
function d3_geo_centroidRingStart() {
|
||||
var λ00, φ00, // first point
|
||||
x0, y0, z0; // previous point
|
||||
|
||||
d3_geo_centroid.point = function(λ, φ) {
|
||||
λ00 = λ, φ00 = φ;
|
||||
d3_geo_centroid.point = nextPoint;
|
||||
λ *= d3_radians;
|
||||
var cosφ = Math.cos(φ *= d3_radians);
|
||||
x0 = cosφ * Math.cos(λ);
|
||||
y0 = cosφ * Math.sin(λ);
|
||||
z0 = Math.sin(φ);
|
||||
d3_geo_centroidPointXYZ(x0, y0, z0);
|
||||
};
|
||||
|
||||
d3_geo_centroid.lineEnd = function() {
|
||||
nextPoint(λ00, φ00);
|
||||
d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd;
|
||||
d3_geo_centroid.point = d3_geo_centroidPoint;
|
||||
};
|
||||
|
||||
function nextPoint(λ, φ) {
|
||||
λ *= d3_radians;
|
||||
var cosφ = Math.cos(φ *= d3_radians),
|
||||
x = cosφ * Math.cos(λ),
|
||||
y = cosφ * Math.sin(λ),
|
||||
z = Math.sin(φ),
|
||||
cx = y0 * z - z0 * y,
|
||||
cy = z0 * x - x0 * z,
|
||||
cz = x0 * y - y0 * x,
|
||||
m = Math.sqrt(cx * cx + cy * cy + cz * cz),
|
||||
u = x0 * x + y0 * y + z0 * z,
|
||||
v = m && -d3_acos(u) / m, // area weight
|
||||
w = Math.atan2(m, u); // line weight
|
||||
d3_geo_centroidX2 += v * cx;
|
||||
d3_geo_centroidY2 += v * cy;
|
||||
d3_geo_centroidZ2 += v * cz;
|
||||
d3_geo_centroidW1 += w;
|
||||
d3_geo_centroidX1 += w * (x0 + (x0 = x));
|
||||
d3_geo_centroidY1 += w * (y0 + (y0 = y));
|
||||
d3_geo_centroidZ1 += w * (z0 + (z0 = z));
|
||||
d3_geo_centroidPointXYZ(x0, y0, z0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
import "../core/true";
|
||||
import "../math/trigonometry";
|
||||
import "clip";
|
||||
import "point-in-polygon";
|
||||
|
||||
var d3_geo_clipAntimeridian = d3_geo_clip(d3_true, d3_geo_clipAntimeridianLine, d3_geo_clipAntimeridianInterpolate);
|
||||
var d3_geo_clipAntimeridian = d3_geo_clip(
|
||||
d3_true,
|
||||
d3_geo_clipAntimeridianLine,
|
||||
d3_geo_clipAntimeridianInterpolate,
|
||||
d3_geo_clipAntimeridianPolygonContains);
|
||||
|
||||
// Takes a line and cuts into visible segments. Return values:
|
||||
// 0: there were intersections or the line was empty.
|
||||
|
@ -88,3 +93,9 @@ function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) {
|
|||
listener.point(to[0], to[1]);
|
||||
}
|
||||
}
|
||||
|
||||
var d3_geo_clipAntimeridianPoint = [-π, 0];
|
||||
|
||||
function d3_geo_clipAntimeridianPolygonContains(polygon) {
|
||||
return d3_geo_pointInPolygon(d3_geo_clipAntimeridianPoint, polygon);
|
||||
}
|
||||
|
|
|
@ -3,15 +3,17 @@ import "cartesian";
|
|||
import "clip";
|
||||
import "circle";
|
||||
import "spherical";
|
||||
import "point-in-polygon";
|
||||
|
||||
// Clip features against a small circle centered at [0°, 0°].
|
||||
function d3_geo_clipCircle(radius) {
|
||||
var cr = Math.cos(radius),
|
||||
smallRadius = cr > 0,
|
||||
point = [radius, 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);
|
||||
return d3_geo_clip(visible, clipLine, interpolate, polygonContains);
|
||||
|
||||
function visible(λ, φ) {
|
||||
return Math.cos(λ) * Math.cos(φ) > cr;
|
||||
|
@ -173,4 +175,8 @@ function d3_geo_clipCircle(radius) {
|
|||
else if (φ > r) code |= 8; // above
|
||||
return code;
|
||||
}
|
||||
|
||||
function polygonContains(polygon) {
|
||||
return d3_geo_pointInPolygon(point, polygon);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import "../core/noop";
|
|||
import "../math/trigonometry";
|
||||
import "clip-polygon";
|
||||
|
||||
function d3_geo_clip(pointVisible, clipLine, interpolate) {
|
||||
function d3_geo_clip(pointVisible, clipLine, interpolate, polygonContains) {
|
||||
return function(listener) {
|
||||
var line = clipLine(listener);
|
||||
|
||||
|
@ -15,9 +15,8 @@ function d3_geo_clip(pointVisible, clipLine, interpolate) {
|
|||
clip.point = pointRing;
|
||||
clip.lineStart = ringStart;
|
||||
clip.lineEnd = ringEnd;
|
||||
invisible = false;
|
||||
invisibleArea = visibleArea = 0;
|
||||
segments = [];
|
||||
polygon = [];
|
||||
listener.polygonStart();
|
||||
},
|
||||
polygonEnd: function() {
|
||||
|
@ -28,13 +27,13 @@ function d3_geo_clip(pointVisible, clipLine, interpolate) {
|
|||
segments = d3.merge(segments);
|
||||
if (segments.length) {
|
||||
d3_geo_clipPolygon(segments, d3_geo_clipSort, null, interpolate, listener);
|
||||
} else if (visibleArea < -ε || invisible && invisibleArea < -ε) {
|
||||
} else if (polygonContains(polygon)) {
|
||||
listener.lineStart();
|
||||
interpolate(null, null, 1, listener);
|
||||
listener.lineEnd();
|
||||
}
|
||||
listener.polygonEnd();
|
||||
segments = null;
|
||||
segments = polygon = null;
|
||||
},
|
||||
sphere: function() {
|
||||
listener.polygonStart();
|
||||
|
@ -50,13 +49,11 @@ function d3_geo_clip(pointVisible, clipLine, interpolate) {
|
|||
function lineStart() { clip.point = pointLine; line.lineStart(); }
|
||||
function lineEnd() { clip.point = point; line.lineEnd(); }
|
||||
|
||||
var segments,
|
||||
visibleArea,
|
||||
invisibleArea,
|
||||
invisible;
|
||||
var segments;
|
||||
|
||||
var buffer = d3_geo_clipBufferListener(),
|
||||
ringListener = clipLine(buffer),
|
||||
polygon,
|
||||
ring;
|
||||
|
||||
function pointRing(λ, φ) {
|
||||
|
@ -78,20 +75,15 @@ function d3_geo_clip(pointVisible, clipLine, interpolate) {
|
|||
segment,
|
||||
n = ringSegments.length;
|
||||
|
||||
// TODO compute on-the-fly?
|
||||
if (!n) {
|
||||
invisible = true;
|
||||
invisibleArea += d3_geo_clipAreaRing(ring, -1);
|
||||
ring = null;
|
||||
return;
|
||||
}
|
||||
ring.pop();
|
||||
polygon.push(ring);
|
||||
ring = null;
|
||||
|
||||
if (!n) return;
|
||||
|
||||
// No intersections.
|
||||
// TODO compute on-the-fly?
|
||||
if (clean & 1) {
|
||||
segment = ringSegments[0];
|
||||
visibleArea += d3_geo_clipAreaRing(segment, 1);
|
||||
var n = segment.length - 1,
|
||||
i = -1,
|
||||
point;
|
||||
|
@ -135,59 +127,6 @@ function d3_geo_clipBufferListener() {
|
|||
};
|
||||
}
|
||||
|
||||
// Approximate polygon ring area (×2, since we only need the sign).
|
||||
// For an invisible polygon ring, we rotate longitudinally by 180°.
|
||||
// The invisible parameter should be 1, or -1 to rotate longitudinally.
|
||||
// Based on Robert. G. Chamberlain and William H. Duquette,
|
||||
// “Some Algorithms for Polygons on a Sphere”,
|
||||
// http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
|
||||
function d3_geo_clipAreaRing(ring, invisible) {
|
||||
if (!(n = ring.length)) return 0;
|
||||
var n,
|
||||
i = 0,
|
||||
area = 0,
|
||||
p = ring[0],
|
||||
λ = p[0],
|
||||
φ = p[1],
|
||||
cosφ = Math.cos(φ),
|
||||
x0 = Math.atan2(invisible * Math.sin(λ) * cosφ, Math.sin(φ)),
|
||||
y0 = 1 - invisible * Math.cos(λ) * cosφ,
|
||||
x1 = x0,
|
||||
x, // λ'; λ rotated to south pole.
|
||||
y; // φ' = 1 + sin(φ); φ rotated to south pole.
|
||||
while (++i < n) {
|
||||
p = ring[i];
|
||||
cosφ = Math.cos(φ = p[1]);
|
||||
x = Math.atan2(invisible * Math.sin(λ = p[0]) * cosφ, Math.sin(φ));
|
||||
y = 1 - invisible * Math.cos(λ) * cosφ;
|
||||
|
||||
// If both the current point and the previous point are at the north pole,
|
||||
// skip this point.
|
||||
if (Math.abs(y0 - 2) < ε && Math.abs(y - 2) < ε) continue;
|
||||
|
||||
// If this or the previous point is at the south pole, or if this segment
|
||||
// goes through the south pole, the area is 0.
|
||||
if (Math.abs(y) < ε || Math.abs(y0) < ε) {}
|
||||
|
||||
// If this segment goes through either pole…
|
||||
else if (Math.abs(Math.abs(x - x0) - π) < ε) {
|
||||
// For the north pole, compute lune area.
|
||||
if (y + y0 > 2) area += 4 * (x - x0);
|
||||
// For the south pole, the area is zero.
|
||||
}
|
||||
|
||||
// If the previous point is at the north pole, then compute lune area.
|
||||
else if (Math.abs(y0 - 2) < ε) area += 4 * (x - x1);
|
||||
|
||||
// Otherwise, the spherical triangle area is approximately
|
||||
// δλ * (1 + sinφ0 + 1 + sinφ) / 2.
|
||||
else area += ((3 * π + x - x0) % (2 * π) - π) * (y0 + y);
|
||||
|
||||
x1 = x0, x0 = x, y0 = y;
|
||||
}
|
||||
return area;
|
||||
}
|
||||
|
||||
// Intersection points are sorted along the clip edge. For both antimeridian
|
||||
// cutting and circle clipping, the same comparison is used.
|
||||
function d3_geo_clipSort(a, b) {
|
||||
|
|
|
@ -22,33 +22,25 @@ var d3_geo_pathCentroid = {
|
|||
};
|
||||
|
||||
function d3_geo_pathCentroidPoint(x, y) {
|
||||
if (d3_geo_centroidDimension) return;
|
||||
d3_geo_centroidX += x;
|
||||
d3_geo_centroidY += y;
|
||||
++d3_geo_centroidZ;
|
||||
d3_geo_centroidX0 += x;
|
||||
d3_geo_centroidY0 += y;
|
||||
++d3_geo_centroidZ0;
|
||||
}
|
||||
|
||||
function d3_geo_pathCentroidLineStart() {
|
||||
var x0, y0;
|
||||
|
||||
if (d3_geo_centroidDimension !== 1) {
|
||||
if (d3_geo_centroidDimension < 1) {
|
||||
d3_geo_centroidDimension = 1;
|
||||
d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0;
|
||||
} else return;
|
||||
}
|
||||
|
||||
d3_geo_pathCentroid.point = function(x, y) {
|
||||
d3_geo_pathCentroid.point = nextPoint;
|
||||
x0 = x, y0 = y;
|
||||
d3_geo_pathCentroidPoint(x0 = x, y0 = y);
|
||||
};
|
||||
|
||||
function nextPoint(x, y) {
|
||||
var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
|
||||
d3_geo_centroidX += z * (x0 + x) / 2;
|
||||
d3_geo_centroidY += z * (y0 + y) / 2;
|
||||
d3_geo_centroidZ += z;
|
||||
x0 = x, y0 = y;
|
||||
d3_geo_centroidX1 += z * (x0 + x) / 2;
|
||||
d3_geo_centroidY1 += z * (y0 + y) / 2;
|
||||
d3_geo_centroidZ1 += z;
|
||||
d3_geo_pathCentroidPoint(x0 = x, y0 = y);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,24 +51,24 @@ function d3_geo_pathCentroidLineEnd() {
|
|||
function d3_geo_pathCentroidRingStart() {
|
||||
var x00, y00, x0, y0;
|
||||
|
||||
if (d3_geo_centroidDimension < 2) {
|
||||
d3_geo_centroidDimension = 2;
|
||||
d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0;
|
||||
}
|
||||
|
||||
// For the first point, …
|
||||
d3_geo_pathCentroid.point = function(x, y) {
|
||||
d3_geo_pathCentroid.point = nextPoint;
|
||||
x00 = x0 = x, y00 = y0 = y;
|
||||
d3_geo_pathCentroidPoint(x00 = x0 = x, y00 = y0 = y);
|
||||
};
|
||||
|
||||
// For subsequent points, …
|
||||
function nextPoint(x, y) {
|
||||
var z = y0 * x - x0 * y;
|
||||
d3_geo_centroidX += z * (x0 + x);
|
||||
d3_geo_centroidY += z * (y0 + y);
|
||||
d3_geo_centroidZ += z * 3;
|
||||
x0 = x, y0 = y;
|
||||
var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
|
||||
d3_geo_centroidX1 += z * (x0 + x) / 2;
|
||||
d3_geo_centroidY1 += z * (y0 + y) / 2;
|
||||
d3_geo_centroidZ1 += z;
|
||||
|
||||
z = y0 * x - x0 * y;
|
||||
d3_geo_centroidX2 += z * (x0 + x);
|
||||
d3_geo_centroidY2 += z * (y0 + y);
|
||||
d3_geo_centroidZ2 += z * 3;
|
||||
d3_geo_pathCentroidPoint(x0 = x, y0 = y);
|
||||
}
|
||||
|
||||
// For the last point, return to the start.
|
||||
|
|
|
@ -19,13 +19,15 @@ d3.geo.path = function() {
|
|||
projection,
|
||||
context,
|
||||
projectStream,
|
||||
contextStream;
|
||||
contextStream,
|
||||
cacheStream;
|
||||
|
||||
function path(object) {
|
||||
if (object) d3.geo.stream(object, projectStream(
|
||||
contextStream.pointRadius(typeof pointRadius === "function"
|
||||
? +pointRadius.apply(this, arguments)
|
||||
: pointRadius)));
|
||||
if (object) {
|
||||
if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments));
|
||||
if (!cacheStream || !cacheStream.valid) cacheStream = projectStream(contextStream);
|
||||
d3.geo.stream(object, cacheStream);
|
||||
}
|
||||
return contextStream.result();
|
||||
}
|
||||
|
||||
|
@ -36,9 +38,14 @@ d3.geo.path = function() {
|
|||
};
|
||||
|
||||
path.centroid = function(object) {
|
||||
d3_geo_centroidDimension = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0;
|
||||
d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 =
|
||||
d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 =
|
||||
d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0;
|
||||
d3.geo.stream(object, projectStream(d3_geo_pathCentroid));
|
||||
return d3_geo_centroidZ ? [d3_geo_centroidX / d3_geo_centroidZ, d3_geo_centroidY / d3_geo_centroidZ] : undefined;
|
||||
return d3_geo_centroidZ2 ? [d3_geo_centroidX2 / d3_geo_centroidZ2, d3_geo_centroidY2 / d3_geo_centroidZ2]
|
||||
: d3_geo_centroidZ1 ? [d3_geo_centroidX1 / d3_geo_centroidZ1, d3_geo_centroidY1 / d3_geo_centroidZ1]
|
||||
: d3_geo_centroidZ0 ? [d3_geo_centroidX0 / d3_geo_centroidZ0, d3_geo_centroidY0 / d3_geo_centroidZ0]
|
||||
: [NaN, NaN];
|
||||
};
|
||||
|
||||
path.bounds = function(object) {
|
||||
|
@ -50,21 +57,27 @@ d3.geo.path = function() {
|
|||
path.projection = function(_) {
|
||||
if (!arguments.length) return projection;
|
||||
projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity;
|
||||
return path;
|
||||
return reset();
|
||||
};
|
||||
|
||||
path.context = function(_) {
|
||||
if (!arguments.length) return context;
|
||||
contextStream = (context = _) == null ? new d3_geo_pathBuffer : new d3_geo_pathContext(_);
|
||||
return path;
|
||||
if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius);
|
||||
return reset();
|
||||
};
|
||||
|
||||
path.pointRadius = function(_) {
|
||||
if (!arguments.length) return pointRadius;
|
||||
pointRadius = typeof _ === "function" ? _ : +_;
|
||||
pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
|
||||
return path;
|
||||
};
|
||||
|
||||
function reset() {
|
||||
cacheStream = null;
|
||||
return path;
|
||||
}
|
||||
|
||||
return path.projection(d3.geo.albersUsa()).context(null);
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
import "geo";
|
||||
import "area";
|
||||
import "cartesian";
|
||||
import "../math/trigonometry";
|
||||
|
||||
function d3_geo_pointInPolygon(point, polygon) {
|
||||
var meridian = point[0],
|
||||
parallel = point[1],
|
||||
meridianNormal = [Math.sin(meridian), -Math.cos(meridian), 0],
|
||||
polarAngle = 0,
|
||||
polar = false,
|
||||
southPole = false,
|
||||
winding = 0;
|
||||
d3_geo_areaRingSum.reset();
|
||||
|
||||
for (var i = 0, n = polygon.length; i < n; ++i) {
|
||||
var ring = polygon[i],
|
||||
m = ring.length;
|
||||
if (!m) continue;
|
||||
var point0 = ring[0],
|
||||
λ0 = point0[0],
|
||||
φ0 = point0[1] / 2 + π / 4,
|
||||
sinφ0 = Math.sin(φ0),
|
||||
cosφ0 = Math.cos(φ0),
|
||||
j = 1;
|
||||
|
||||
while (true) {
|
||||
if (j === m) j = 0;
|
||||
point = ring[j];
|
||||
var λ = point[0],
|
||||
φ = point[1] / 2 + π / 4,
|
||||
sinφ = Math.sin(φ),
|
||||
cosφ = Math.cos(φ),
|
||||
dλ = λ - λ0,
|
||||
antimeridian = Math.abs(dλ) > π,
|
||||
k = sinφ0 * sinφ;
|
||||
d3_geo_areaRingSum.add(Math.atan2(k * Math.sin(dλ), cosφ0 * cosφ + k * Math.cos(dλ)));
|
||||
|
||||
if (Math.abs(φ) < ε) southPole = true;
|
||||
polarAngle += antimeridian ? dλ + (dλ >= 0 ? 2 : -2) * π : dλ;
|
||||
|
||||
// Are the longitudes either side of the point's meridian, and are the
|
||||
// latitudes smaller than the parallel?
|
||||
if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) {
|
||||
var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point));
|
||||
d3_geo_cartesianNormalize(arc);
|
||||
var intersection = d3_geo_cartesianCross(meridianNormal, arc);
|
||||
d3_geo_cartesianNormalize(intersection);
|
||||
var φarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]);
|
||||
if (parallel > φarc) {
|
||||
winding += antimeridian ^ dλ >= 0 ? 1 : -1;
|
||||
}
|
||||
}
|
||||
if (!j++) break;
|
||||
λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point;
|
||||
}
|
||||
if (Math.abs(polarAngle) > ε) polar = true;
|
||||
}
|
||||
|
||||
// First, determine whether the South pole is inside or outside:
|
||||
//
|
||||
// It is inside if:
|
||||
// * the polygon doesn't wind around it, and its area is negative (counter-clockwise).
|
||||
// * otherwise, if the polygon winds around it in a clockwise direction.
|
||||
//
|
||||
// Second, count the (signed) number of times a segment crosses a meridian
|
||||
// from the point to the South pole. If it is zero, then the point is the
|
||||
// same side as the South pole.
|
||||
|
||||
return (!southPole && !polar && d3_geo_areaRingSum < 0 || polarAngle < -ε) ^ (winding & 1);
|
||||
}
|
|
@ -6,6 +6,7 @@ import "clip-circle";
|
|||
import "clip-view";
|
||||
import "compose";
|
||||
import "geo";
|
||||
import "path";
|
||||
import "resample";
|
||||
import "rotation";
|
||||
import "stream";
|
||||
|
@ -30,7 +31,8 @@ function d3_geo_projectionMutator(projectAt) {
|
|||
preclip = d3_geo_clipAntimeridian,
|
||||
postclip = d3_identity,
|
||||
clipAngle = null,
|
||||
clipExtent = null;
|
||||
clipExtent = null,
|
||||
stream;
|
||||
|
||||
function projection(point) {
|
||||
point = projectRotate(point[0] * d3_radians, point[1] * d3_radians);
|
||||
|
@ -42,21 +44,24 @@ function d3_geo_projectionMutator(projectAt) {
|
|||
return point && [point[0] * d3_degrees, point[1] * d3_degrees];
|
||||
}
|
||||
|
||||
projection.stream = function(stream) {
|
||||
return d3_geo_projectionRadiansRotate(rotate, preclip(projectResample(postclip(stream))));
|
||||
projection.stream = function(output) {
|
||||
if (stream) stream.valid = false;
|
||||
stream = d3_geo_projectionRadiansRotate(rotate, preclip(projectResample(postclip(output))));
|
||||
stream.valid = true; // allow caching by d3.geo.path
|
||||
return stream;
|
||||
};
|
||||
|
||||
projection.clipAngle = function(_) {
|
||||
if (!arguments.length) return clipAngle;
|
||||
preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle((clipAngle = +_) * d3_radians);
|
||||
return projection;
|
||||
return invalidate();
|
||||
};
|
||||
|
||||
projection.clipExtent = function(_) {
|
||||
if (!arguments.length) return clipExtent;
|
||||
clipExtent = _;
|
||||
postclip = _ == null ? d3_identity : d3_geo_clipView(_[0][0], _[0][1], _[1][0], _[1][1]);
|
||||
return projection;
|
||||
return invalidate();
|
||||
};
|
||||
|
||||
projection.scale = function(_) {
|
||||
|
@ -94,6 +99,14 @@ function d3_geo_projectionMutator(projectAt) {
|
|||
var center = project(λ, φ);
|
||||
δx = x - center[0] * k;
|
||||
δy = y + center[1] * k;
|
||||
return invalidate();
|
||||
}
|
||||
|
||||
function invalidate() {
|
||||
if (stream) {
|
||||
stream.valid = false;
|
||||
stream = null;
|
||||
}
|
||||
return projection;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,13 +6,14 @@ function d3_geo_resample(project) {
|
|||
maxDepth = 16;
|
||||
|
||||
function resample(stream) {
|
||||
var λ0, x0, y0, a0, b0, c0; // previous point
|
||||
var λ00, φ00, x00, y00, a00, b00, c00, // first point
|
||||
λ0, x0, y0, a0, b0, c0; // previous point
|
||||
|
||||
var resample = {
|
||||
point: point,
|
||||
lineStart: lineStart,
|
||||
lineEnd: lineEnd,
|
||||
polygonStart: function() { stream.polygonStart(); resample.lineStart = polygonLineStart; },
|
||||
polygonStart: function() { stream.polygonStart(); resample.lineStart = ringStart; },
|
||||
polygonEnd: function() { stream.polygonEnd(); resample.lineStart = lineStart; }
|
||||
};
|
||||
|
||||
|
@ -28,8 +29,9 @@ function d3_geo_resample(project) {
|
|||
}
|
||||
|
||||
function linePoint(λ, φ) {
|
||||
var c = d3_geo_cartesian([λ, φ]), p = project(λ, φ);
|
||||
resampleLineTo(x0, y0, λ0, a0, b0, c0, x0 = p[0], y0 = p[1], λ0 = λ, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream);
|
||||
var c = d3_geo_cartesian([λ, φ]), p = project(λ, φ), buffer = [];
|
||||
resampleLineTo(x0, y0, λ0, a0, b0, c0, x0 = p[0], y0 = p[1], λ0 = λ, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, buffer);
|
||||
streamLine(buffer, stream);
|
||||
stream.point(x0, y0);
|
||||
}
|
||||
|
||||
|
@ -38,27 +40,35 @@ function d3_geo_resample(project) {
|
|||
stream.lineEnd();
|
||||
}
|
||||
|
||||
function polygonLineStart() {
|
||||
var λ00, φ00, x00, y00, a00, b00, c00; // first point
|
||||
|
||||
function ringStart() {
|
||||
lineStart();
|
||||
resample.point = ringPoint;
|
||||
resample.lineEnd = ringEnd;
|
||||
}
|
||||
|
||||
resample.point = function(λ, φ) {
|
||||
linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
|
||||
resample.point = linePoint;
|
||||
};
|
||||
function ringPoint(λ, φ) {
|
||||
linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
|
||||
resample.point = linePoint;
|
||||
}
|
||||
|
||||
resample.lineEnd = function() {
|
||||
resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream);
|
||||
resample.lineEnd = lineEnd;
|
||||
lineEnd();
|
||||
};
|
||||
function ringEnd() {
|
||||
var buffer = [];
|
||||
resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, buffer);
|
||||
streamLine(buffer, stream);
|
||||
resample.lineEnd = lineEnd;
|
||||
lineEnd();
|
||||
}
|
||||
|
||||
function streamLine(line, stream) {
|
||||
for (var i = 0, n = line.length, point; i < n; ++i) {
|
||||
stream.point((point = line[i])[0], point[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return resample;
|
||||
}
|
||||
|
||||
function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) {
|
||||
function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, buffer) {
|
||||
var dx = x1 - x0,
|
||||
dy = y1 - y0,
|
||||
d2 = dx * dx + dy * dy;
|
||||
|
@ -74,11 +84,13 @@ function d3_geo_resample(project) {
|
|||
y2 = p[1],
|
||||
dx2 = x2 - x0,
|
||||
dy2 = y2 - y0,
|
||||
dz = dy * dx2 - dx * dy2;
|
||||
if (dz * dz / d2 > δ2 || Math.abs((dx * dx2 + dy * dy2) / d2 - .5) > .3) {
|
||||
resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream);
|
||||
stream.point(x2, y2);
|
||||
resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream);
|
||||
dz = dy * dx2 - dx * dy2,
|
||||
tooFar = false;
|
||||
if (dz * dz / d2 > δ2 || Math.abs((dx * dx2 + dy * dy2) / d2 - .5) > .3 || (tooFar = dx2 * dx2 + dy2 * dy2 > 256 * δ2)) {
|
||||
var s0 = resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, buffer);
|
||||
buffer.push(p);
|
||||
var s1 = resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, buffer);
|
||||
return !tooFar || s0 || s1 || (buffer.pop(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ function d3_geo_rotationφγ(δφ, δγ) {
|
|||
k = z * cosδφ + x * sinδφ;
|
||||
return [
|
||||
Math.atan2(y * cosδγ - k * sinδγ, x * cosδφ - z * sinδφ),
|
||||
Math.asin(Math.max(-1, Math.min(1, k * cosδγ + y * sinδγ)))
|
||||
d3_asin(k * cosδγ + y * sinδγ)
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,7 @@ function d3_geo_rotationφγ(δφ, δγ) {
|
|||
k = z * cosδγ - y * sinδγ;
|
||||
return [
|
||||
Math.atan2(y * cosδγ + z * sinδγ, x * cosδφ + k * sinδφ),
|
||||
Math.asin(Math.max(-1, Math.min(1, k * cosδφ - x * sinδφ)))
|
||||
d3_asin(k * cosδφ - x * sinδφ)
|
||||
];
|
||||
};
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import "../math/trigonometry";
|
|||
function d3_geo_spherical(cartesian) {
|
||||
return [
|
||||
Math.atan2(cartesian[1], cartesian[0]),
|
||||
Math.asin(Math.max(-1, Math.min(1, cartesian[2])))
|
||||
d3_asin(cartesian[2])
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -147,14 +147,17 @@ d3.geom.quadtree = function(points, x1, y1, x2, y2) {
|
|||
return arguments.length ? (y = _, quadtree) : y;
|
||||
};
|
||||
|
||||
quadtree.extent = function(_) {
|
||||
if (!arguments.length) return x1 == null ? null : [[x1, y1], [x2, y2]];
|
||||
if (_ == null) x1 = y1 = x2 = y2 = null;
|
||||
else x1 = +_[0][0], y1 = +_[0][1], x2 = +_[1][0], y2 = +_[1][1];
|
||||
return quadtree;
|
||||
};
|
||||
|
||||
quadtree.size = function(_) {
|
||||
if (!arguments.length) return x1 == null ? null : [x2, y2];
|
||||
if (_ == null) {
|
||||
x1 = y1 = x2 = y2 = null;
|
||||
} else {
|
||||
x1 = y1 = 0;
|
||||
x2 = +_[0], y2 = +_[1];
|
||||
}
|
||||
if (!arguments.length) return x1 == null ? null : [x2 - x1, y2 - y1];
|
||||
if (_ == null) x1 = y1 = x2 = y2 = null;
|
||||
else x1 = y1 = 0, x2 = +_[0], y2 = +_[1];
|
||||
return quadtree;
|
||||
};
|
||||
|
||||
|
|
|
@ -27,10 +27,9 @@ import "polygon";
|
|||
* @returns polygons [[[x1, y1], [x2, y2], …], …]
|
||||
*/
|
||||
d3.geom.voronoi = function(points) {
|
||||
var size = null,
|
||||
x = d3_svg_lineX,
|
||||
var x = d3_svg_lineX,
|
||||
y = d3_svg_lineY,
|
||||
clip;
|
||||
clipPolygon = null;
|
||||
|
||||
// For backwards-compatibility.
|
||||
if (arguments.length) return voronoi(points);
|
||||
|
@ -46,8 +45,8 @@ d3.geom.voronoi = function(points) {
|
|||
Z = 1e6;
|
||||
|
||||
if (fx === d3_svg_lineX && fy === d3_svg_lineY) points = data;
|
||||
else for (points = [], i = 0; i < n; ++i) {
|
||||
points.push([+fx.call(this, d = data[i], i), +fy.call(this, d, i)]);
|
||||
else for (points = new Array(n), i = 0; i < n; ++i) {
|
||||
points[i] = [+fx.call(this, d = data[i], i), +fy.call(this, d, i)];
|
||||
}
|
||||
|
||||
d3_geom_voronoiTessellate(points, function(e) {
|
||||
|
@ -124,7 +123,7 @@ d3.geom.voronoi = function(points) {
|
|||
}
|
||||
});
|
||||
|
||||
if (clip) for (i = 0; i < n; ++i) clip(polygons[i]);
|
||||
if (clipPolygon) for (i = 0; i < n; ++i) clipPolygon.clip(polygons[i]);
|
||||
for (i = 0; i < n; ++i) polygons[i].point = data[i];
|
||||
|
||||
return polygons;
|
||||
|
@ -138,17 +137,22 @@ d3.geom.voronoi = function(points) {
|
|||
return arguments.length ? (y = _, voronoi) : y;
|
||||
};
|
||||
|
||||
voronoi.size = function(_) {
|
||||
if (!arguments.length) return size;
|
||||
if (_ == null) {
|
||||
clip = null;
|
||||
} else {
|
||||
size = [+_[0], +_[1]];
|
||||
clip = d3.geom.polygon([[0, 0], [0, size[1]], size, [size[0], 0]]).clip;
|
||||
voronoi.clipExtent = function(_) {
|
||||
if (!arguments.length) return clipPolygon && [clipPolygon[0], clipPolygon[2]];
|
||||
if (_ == null) clipPolygon = null;
|
||||
else {
|
||||
var x1 = +_[0][0], y1 = +_[0][1], x2 = +_[1][0], y2 = +_[1][1];
|
||||
clipPolygon = d3.geom.polygon([[x1, y1], [x1, y2], [x2, y2], [x2, y1]]);
|
||||
}
|
||||
return voronoi;
|
||||
};
|
||||
|
||||
// @deprecated; use clipExtent instead
|
||||
voronoi.size = function(_) {
|
||||
if (!arguments.length) return clipPolygon && clipPolygon[2];
|
||||
return voronoi.clipExtent(_ && [[0, 0], _]);
|
||||
};
|
||||
|
||||
voronoi.links = function(data) {
|
||||
var points,
|
||||
graph = data.map(function() { return []; }),
|
||||
|
@ -160,8 +164,8 @@ d3.geom.voronoi = function(points) {
|
|||
n = data.length;
|
||||
|
||||
if (fx === d3_svg_lineX && fy === d3_svg_lineY) points = data;
|
||||
else for (i = 0; i < n; ++i) {
|
||||
points.push([+fx.call(this, d = data[i], i), +fy.call(this, d, i)]);
|
||||
else for (points = new Array(n), i = 0; i < n; ++i) {
|
||||
points[i] = [+fx.call(this, d = data[i], i), +fy.call(this, d, i)];
|
||||
}
|
||||
|
||||
d3_geom_voronoiTessellate(points, function(e) {
|
||||
|
@ -178,18 +182,15 @@ d3.geom.voronoi = function(points) {
|
|||
voronoi.triangles = function(data) {
|
||||
if (x === d3_svg_lineX && y === d3_svg_lineY) return d3.geom.delaunay(data);
|
||||
|
||||
var points,
|
||||
point,
|
||||
var points = new Array(n),
|
||||
fx = d3_functor(x),
|
||||
fy = d3_functor(y),
|
||||
d,
|
||||
i,
|
||||
n;
|
||||
i = -1,
|
||||
n = data.length;
|
||||
|
||||
for (i = 0, points = [], n = data.length; i < n; ++i) {
|
||||
point = [+fx.call(this, d = data[i], i), +fy.call(this, d, i)];
|
||||
point.data = d;
|
||||
points.push(point);
|
||||
while (++i < n) {
|
||||
(points[i] = [+fx.call(this, d = data[i], i), +fy.call(this, d, i)]).data = d;
|
||||
}
|
||||
|
||||
return d3.geom.delaunay(points).map(function(triangle) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import "../arrays/range";
|
||||
import "../math/trigonometry";
|
||||
import "layout";
|
||||
|
||||
d3.layout.chord = function() {
|
||||
var chord = {},
|
||||
|
|
|
@ -8,7 +8,8 @@ import "tree";
|
|||
d3.layout.cluster = function() {
|
||||
var hierarchy = d3.layout.hierarchy().sort(null).value(null),
|
||||
separation = d3_layout_treeSeparation,
|
||||
size = [1, 1]; // width, height
|
||||
size = [1, 1], // width, height
|
||||
nodeSize = false;
|
||||
|
||||
function cluster(d, i) {
|
||||
var nodes = hierarchy.call(this, d, i),
|
||||
|
@ -36,7 +37,10 @@ d3.layout.cluster = function() {
|
|||
x1 = right.x + separation(right, left) / 2;
|
||||
|
||||
// Second walk, normalizing x & y to the desired size.
|
||||
d3_layout_treeVisitAfter(root, function(node) {
|
||||
d3_layout_treeVisitAfter(root, nodeSize ? function(node) {
|
||||
node.x = (node.x - root.x) * size[0];
|
||||
node.y = (root.y - node.y) * size[1];
|
||||
} : function(node) {
|
||||
node.x = (node.x - x0) / (x1 - x0) * size[0];
|
||||
node.y = (1 - (root.y ? node.y / root.y : 1)) * size[1];
|
||||
});
|
||||
|
@ -51,8 +55,14 @@ d3.layout.cluster = function() {
|
|||
};
|
||||
|
||||
cluster.size = function(x) {
|
||||
if (!arguments.length) return size;
|
||||
size = x;
|
||||
if (!arguments.length) return nodeSize ? null : size;
|
||||
nodeSize = (size = x) == null;
|
||||
return cluster;
|
||||
};
|
||||
|
||||
cluster.nodeSize = function(x) {
|
||||
if (!arguments.length) return nodeSize ? size : null;
|
||||
nodeSize = (size = x) != null;
|
||||
return cluster;
|
||||
};
|
||||
|
||||
|
|
|
@ -5,41 +5,44 @@ import "tree";
|
|||
d3.layout.pack = function() {
|
||||
var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort),
|
||||
padding = 0,
|
||||
size = [1, 1];
|
||||
size = [1, 1],
|
||||
radius;
|
||||
|
||||
function pack(d, i) {
|
||||
var nodes = hierarchy.call(this, d, i),
|
||||
root = nodes[0];
|
||||
root = nodes[0],
|
||||
w = size[0],
|
||||
h = size[1],
|
||||
r = radius || Math.sqrt;
|
||||
|
||||
// Recursively compute the layout.
|
||||
root.x = 0;
|
||||
root.y = 0;
|
||||
d3_layout_treeVisitAfter(root, function(d) { d.r = Math.sqrt(d.value); });
|
||||
root.x = root.y = 0;
|
||||
d3_layout_treeVisitAfter(root, function(d) { d.r = r(d.value); });
|
||||
d3_layout_treeVisitAfter(root, d3_layout_packSiblings);
|
||||
|
||||
// Compute the scale factor the initial layout.
|
||||
var w = size[0],
|
||||
h = size[1],
|
||||
k = Math.max(2 * root.r / w, 2 * root.r / h);
|
||||
|
||||
// When padding, recompute the layout using scaled padding.
|
||||
if (padding > 0) {
|
||||
var dr = padding * k / 2;
|
||||
if (padding) {
|
||||
var dr = padding * (radius ? 1 : Math.max(2 * root.r / w, 2 * root.r / h)) / 2;
|
||||
d3_layout_treeVisitAfter(root, function(d) { d.r += dr; });
|
||||
d3_layout_treeVisitAfter(root, d3_layout_packSiblings);
|
||||
d3_layout_treeVisitAfter(root, function(d) { d.r -= dr; });
|
||||
k = Math.max(2 * root.r / w, 2 * root.r / h);
|
||||
}
|
||||
|
||||
// Scale the layout to fit the requested size.
|
||||
d3_layout_packTransform(root, w / 2, h / 2, 1 / k);
|
||||
// Translate and scale the layout to fit the requested size.
|
||||
d3_layout_packTransform(root, w / 2, h / 2, radius ? 1 : 1 / Math.max(2 * root.r / w, 2 * root.r / h));
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
pack.size = function(x) {
|
||||
pack.size = function(_) {
|
||||
if (!arguments.length) return size;
|
||||
size = x;
|
||||
size = _;
|
||||
return pack;
|
||||
};
|
||||
|
||||
pack.radius = function(_) {
|
||||
if (!arguments.length) return radius;
|
||||
radius = _;
|
||||
return pack;
|
||||
};
|
||||
|
||||
|
@ -73,7 +76,7 @@ function d3_layout_packIntersects(a, b) {
|
|||
var dx = b.x - a.x,
|
||||
dy = b.y - a.y,
|
||||
dr = a.r + b.r;
|
||||
return dr * dr - dx * dx - dy * dy > .001; // within epsilon
|
||||
return .999 * dr * dr > dx * dx + dy * dy; // relative error within epsilon
|
||||
}
|
||||
|
||||
function d3_layout_packSiblings(node) {
|
||||
|
|
|
@ -5,7 +5,8 @@ import "hierarchy";
|
|||
d3.layout.tree = function() {
|
||||
var hierarchy = d3.layout.hierarchy().sort(null).value(null),
|
||||
separation = d3_layout_treeSeparation,
|
||||
size = [1, 1]; // width, height
|
||||
size = [1, 1], // width, height
|
||||
nodeSize = false;
|
||||
|
||||
function tree(d, i) {
|
||||
var nodes = hierarchy.call(this, d, i),
|
||||
|
@ -119,7 +120,11 @@ d3.layout.tree = function() {
|
|||
y1 = deep.depth || 1;
|
||||
|
||||
// Clear temporary layout variables; transform x and y.
|
||||
d3_layout_treeVisitAfter(root, function(node) {
|
||||
d3_layout_treeVisitAfter(root, nodeSize ? function(node) {
|
||||
node.x *= size[0];
|
||||
node.y = node.depth * size[1];
|
||||
delete node._tree;
|
||||
} : function(node) {
|
||||
node.x = (node.x - x0) / (x1 - x0) * size[0];
|
||||
node.y = node.depth / y1 * size[1];
|
||||
delete node._tree;
|
||||
|
@ -135,8 +140,14 @@ d3.layout.tree = function() {
|
|||
};
|
||||
|
||||
tree.size = function(x) {
|
||||
if (!arguments.length) return size;
|
||||
size = x;
|
||||
if (!arguments.length) return nodeSize ? null : size;
|
||||
nodeSize = (size = x) == null;
|
||||
return tree;
|
||||
};
|
||||
|
||||
tree.nodeSize = function(x) {
|
||||
if (!arguments.length) return nodeSize ? size : null;
|
||||
nodeSize = (size = x) != null;
|
||||
return tree;
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
// Adds floating point numbers with twice the normal precision.
|
||||
// Reference: J. R. Shewchuk, Adaptive Precision Floating-Point Arithmetic and
|
||||
// Fast Robust Geometric Predicates, Discrete & Computational Geometry 18(3)
|
||||
// 305–363 (1997).
|
||||
// Code adapted from GeographicLib by Charles F. F. Karney,
|
||||
// http://geographiclib.sourceforge.net/
|
||||
// See lib/geographiclib/LICENSE for details.
|
||||
|
||||
function d3_adder() {}
|
||||
|
||||
d3_adder.prototype = {
|
||||
s: 0, // rounded value
|
||||
t: 0, // exact error
|
||||
add: function(y) {
|
||||
d3_adderSum(y, this.t, d3_adderTemp);
|
||||
d3_adderSum(d3_adderTemp.s, this.s, this);
|
||||
if (this.s) this.t += d3_adderTemp.t;
|
||||
else this.s = d3_adderTemp.t;
|
||||
},
|
||||
reset: function() {
|
||||
this.s = this.t = 0;
|
||||
},
|
||||
valueOf: function() {
|
||||
return this.s;
|
||||
}
|
||||
};
|
||||
|
||||
var d3_adderTemp = new d3_adder;
|
||||
|
||||
function d3_adderSum(a, b, o) {
|
||||
var x = o.s = a + b, // a + b
|
||||
bv = x - a, av = x - bv; // b_virtual & a_virtual
|
||||
o.t = (a - av) + (b - bv); // a_roundoff + b_roundoff
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
var π = Math.PI,
|
||||
ε = 1e-6,
|
||||
ε2 = ε * ε,
|
||||
d3_radians = π / 180,
|
||||
d3_degrees = 180 / π;
|
||||
|
||||
|
@ -8,7 +9,7 @@ function d3_sgn(x) {
|
|||
}
|
||||
|
||||
function d3_acos(x) {
|
||||
return Math.acos(Math.max(-1, Math.min(1, x)));
|
||||
return x > 1 ? 0 : x < -1 ? π : Math.acos(x);
|
||||
}
|
||||
|
||||
function d3_asin(x) {
|
||||
|
|
|
@ -70,8 +70,8 @@ function d3_scale_linear(domain, range, interpolate, clamp) {
|
|||
return d3_scale_linearTickFormat(domain, m, format);
|
||||
};
|
||||
|
||||
scale.nice = function() {
|
||||
d3_scale_nice(domain, d3_scale_linearNice);
|
||||
scale.nice = function(m) {
|
||||
d3_scale_linearNice(domain, m);
|
||||
return rescale();
|
||||
};
|
||||
|
||||
|
@ -86,12 +86,16 @@ function d3_scale_linearRebind(scale, linear) {
|
|||
return d3.rebind(scale, linear, "range", "rangeRound", "interpolate", "clamp");
|
||||
}
|
||||
|
||||
function d3_scale_linearNice(dx) {
|
||||
dx = Math.pow(10, Math.round(Math.log(dx) / Math.LN10) - 1);
|
||||
return dx && {
|
||||
floor: function(x) { return Math.floor(x / dx) * dx; },
|
||||
ceil: function(x) { return Math.ceil(x / dx) * dx; }
|
||||
};
|
||||
function d3_scale_linearNice(domain, m) {
|
||||
return d3_scale_nice(domain, d3_scale_niceStep(m
|
||||
? d3_scale_linearTickRange(domain, m)[2]
|
||||
: d3_scale_linearNiceStep(domain)));
|
||||
}
|
||||
|
||||
function d3_scale_linearNiceStep(domain) {
|
||||
var extent = d3_scaleExtent(domain),
|
||||
span = extent[1] - extent[0];
|
||||
return Math.pow(10, Math.round(Math.log(span) / Math.LN10) - 1);
|
||||
}
|
||||
|
||||
function d3_scale_linearTickRange(domain, m) {
|
||||
|
|
|
@ -32,7 +32,19 @@ function d3_scale_log(linear, base, log, pow, domain) {
|
|||
};
|
||||
|
||||
scale.nice = function() {
|
||||
linear.domain(d3_scale_nice(domain, nice).map(log));
|
||||
|
||||
function floor(x) {
|
||||
return Math.pow(base, Math.floor(Math.log(x) / Math.log(base)));
|
||||
}
|
||||
|
||||
function ceil(x) {
|
||||
return Math.pow(base, Math.ceil(Math.log(x) / Math.log(base)));
|
||||
}
|
||||
|
||||
linear.domain(d3_scale_nice(domain, log === d3_scale_logp
|
||||
? {floor: floor, ceil: ceil}
|
||||
: {floor: function(x) { return -ceil(-x); }, ceil: function(x) { return -floor(-x); }}).map(log));
|
||||
|
||||
return scale;
|
||||
};
|
||||
|
||||
|
@ -61,8 +73,9 @@ function d3_scale_log(linear, base, log, pow, domain) {
|
|||
};
|
||||
|
||||
scale.tickFormat = function(n, format) {
|
||||
if (!arguments.length) return d3_scale_logFormat;
|
||||
if (arguments.length < 2) format = d3_scale_logFormat;
|
||||
if (!arguments.length) return format;
|
||||
else if (typeof format !== "function") format = d3.format(format);
|
||||
var b = Math.log(base),
|
||||
k = Math.max(.1, n / scale.ticks().length),
|
||||
f = log === d3_scale_logn ? (e = -1e-12, Math.floor) : (e = 1e-12, Math.ceil),
|
||||
|
@ -76,20 +89,6 @@ function d3_scale_log(linear, base, log, pow, domain) {
|
|||
return d3_scale_log(linear.copy(), base, log, pow, domain);
|
||||
};
|
||||
|
||||
function nice() {
|
||||
return log === d3_scale_logp
|
||||
? {floor: floor, ceil: ceil}
|
||||
: {floor: function(x) { return -ceil(-x); }, ceil: function(x) { return -floor(-x); }};
|
||||
}
|
||||
|
||||
function floor(x) {
|
||||
return Math.pow(base, Math.floor(Math.log(x) / Math.log(base)));
|
||||
}
|
||||
|
||||
function ceil(x) {
|
||||
return Math.pow(base, Math.ceil(Math.log(x) / Math.log(base)));
|
||||
}
|
||||
|
||||
return d3_scale_linearRebind(scale, linear);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,10 +10,19 @@ function d3_scale_nice(domain, nice) {
|
|||
dx = x0, x0 = x1, x1 = dx;
|
||||
}
|
||||
|
||||
if (nice = nice(x1 - x0)) {
|
||||
domain[i0] = nice.floor(x0);
|
||||
domain[i1] = nice.ceil(x1);
|
||||
}
|
||||
|
||||
domain[i0] = nice.floor(x0);
|
||||
domain[i1] = nice.ceil(x1);
|
||||
return domain;
|
||||
}
|
||||
|
||||
function d3_scale_niceStep(step) {
|
||||
return step ? {
|
||||
floor: function(x) { return Math.floor(x / step) * step; },
|
||||
ceil: function(x) { return Math.ceil(x / step) * step; }
|
||||
} : d3_scale_niceIdentity;
|
||||
}
|
||||
|
||||
var d3_scale_niceIdentity = {
|
||||
floor: d3_identity,
|
||||
ceil: d3_identity
|
||||
};
|
||||
|
|
|
@ -32,8 +32,8 @@ function d3_scale_pow(linear, exponent, domain) {
|
|||
return d3_scale_linearTickFormat(domain, m, format);
|
||||
};
|
||||
|
||||
scale.nice = function() {
|
||||
return scale.domain(d3_scale_nice(domain, d3_scale_linearNice));
|
||||
scale.nice = function(m) {
|
||||
return scale.domain(d3_scale_linearNice(domain, m));
|
||||
};
|
||||
|
||||
scale.exponent = function(x) {
|
||||
|
|
|
@ -34,5 +34,11 @@ function d3_scale_quantize(x0, x1, range) {
|
|||
return d3_scale_quantize(x0, x1, range); // copy on write
|
||||
};
|
||||
|
||||
scale.invertExtent = function(y) {
|
||||
y = range.indexOf(y);
|
||||
y = y < 0 ? NaN : y / kx + x0;
|
||||
return [y, y + 1 / kx];
|
||||
};
|
||||
|
||||
return rescale();
|
||||
}
|
||||
|
|
|
@ -23,6 +23,11 @@ function d3_scale_threshold(domain, range) {
|
|||
return scale;
|
||||
};
|
||||
|
||||
scale.invertExtent = function(y) {
|
||||
y = range.indexOf(y);
|
||||
return [domain[y - 1], domain[y]];
|
||||
};
|
||||
|
||||
scale.copy = function() {
|
||||
return d3_scale_threshold(domain, range);
|
||||
};
|
||||
|
|
|
@ -15,5 +15,7 @@ d3_selection_enterPrototype.append = d3_selectionPrototype.append;
|
|||
d3_selection_enterPrototype.insert = d3_selectionPrototype.insert;
|
||||
d3_selection_enterPrototype.empty = d3_selectionPrototype.empty;
|
||||
d3_selection_enterPrototype.node = d3_selectionPrototype.node;
|
||||
d3_selection_enterPrototype.call = d3_selectionPrototype.call;
|
||||
d3_selection_enterPrototype.size = d3_selectionPrototype.size;
|
||||
|
||||
import "enter-select";
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import "../core/array";
|
||||
import "../core/document";
|
||||
import "../core/vendor";
|
||||
|
||||
function d3_selection(groups) {
|
||||
d3_arraySubclass(groups, d3_selectionPrototype);
|
||||
|
@ -8,8 +9,7 @@ function d3_selection(groups) {
|
|||
|
||||
var d3_select = function(s, n) { return n.querySelector(s); },
|
||||
d3_selectAll = function(s, n) { return n.querySelectorAll(s); },
|
||||
d3_selectRoot = d3_document.documentElement,
|
||||
d3_selectMatcher = d3_selectRoot.matchesSelector || d3_selectRoot.webkitMatchesSelector || d3_selectRoot.mozMatchesSelector || d3_selectRoot.msMatchesSelector || d3_selectRoot.oMatchesSelector,
|
||||
d3_selectMatcher = d3_documentElement[d3_vendorSymbol(d3_documentElement, "matchesSelector")],
|
||||
d3_selectMatches = function(n, s) { return d3_selectMatcher.call(n, s); };
|
||||
|
||||
// Prefer Sizzle, if available.
|
||||
|
@ -46,20 +46,21 @@ import "each";
|
|||
import "call";
|
||||
import "empty";
|
||||
import "node";
|
||||
import "size";
|
||||
import "enter";
|
||||
import "transition";
|
||||
|
||||
// TODO fast singleton implementation?
|
||||
d3.select = function(node) {
|
||||
var group = [typeof node === "string" ? d3_select(node, d3_document) : node];
|
||||
group.parentNode = d3_selectRoot;
|
||||
group.parentNode = d3_documentElement;
|
||||
return d3_selection([group]);
|
||||
};
|
||||
|
||||
d3.selectAll = function(nodes) {
|
||||
var group = d3_array(typeof nodes === "string" ? d3_selectAll(nodes, d3_document) : nodes);
|
||||
group.parentNode = d3_selectRoot;
|
||||
group.parentNode = d3_documentElement;
|
||||
return d3_selection([group]);
|
||||
};
|
||||
|
||||
var d3_selectionRoot = d3.select(d3_selectRoot);
|
||||
var d3_selectionRoot = d3.select(d3_documentElement);
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
import "selection";
|
||||
|
||||
d3_selectionPrototype.size = function() {
|
||||
var n = 0;
|
||||
this.each(function() { ++n; });
|
||||
return n;
|
||||
};
|
|
@ -1,2 +1,2 @@
|
|||
d3 = (function(){
|
||||
var d3 = {version: "3.1.10"}; // semver
|
||||
var d3 = {version: "3.2.0"}; // semver
|
||||
|
|
|
@ -33,7 +33,7 @@ d3.svg.axis = function() {
|
|||
|
||||
// Major ticks.
|
||||
var tick = g.selectAll(".tick.major").data(ticks, String),
|
||||
tickEnter = tick.enter().insert("g", "path").attr("class", "tick major").style("opacity", 1e-6),
|
||||
tickEnter = tick.enter().insert("g", ".domain").attr("class", "tick major").style("opacity", 1e-6),
|
||||
tickExit = d3.transition(tick.exit()).style("opacity", 1e-6).remove(),
|
||||
tickUpdate = d3.transition(tick).style("opacity", 1),
|
||||
tickTransform;
|
||||
|
|
|
@ -14,6 +14,7 @@ d3.svg.brush = function() {
|
|||
y = null, // y-scale, optional
|
||||
resizes = d3_svg_brushResizes[0],
|
||||
extent = [[0, 0], [0, 0]], // [x0, y0], [x1, y1], in pixels (integers)
|
||||
clamp = [true, true], // whether or not to clamp the extent to the range
|
||||
extentDomain; // the extent in data space, lazily created
|
||||
|
||||
function brush(g) {
|
||||
|
@ -223,8 +224,8 @@ d3.svg.brush = function() {
|
|||
r1 -= size + position;
|
||||
}
|
||||
|
||||
// Clamp the point so that the extent fits within the range extent.
|
||||
min = Math.max(r0, Math.min(r1, point[i]));
|
||||
// Clamp the point (unless clamp set to false) so that the extent fits within the range extent.
|
||||
min = clamp[i] ? Math.max(r0, Math.min(r1, point[i])) : point[i];
|
||||
|
||||
// Compute the new extent bounds.
|
||||
if (dragging) {
|
||||
|
@ -285,6 +286,13 @@ d3.svg.brush = function() {
|
|||
return brush;
|
||||
};
|
||||
|
||||
brush.clamp = function(z) {
|
||||
if (!arguments.length) return x && y ? clamp : clamp[+!x];
|
||||
if (x && y) clamp = [!!z[0], !!z[1]];
|
||||
else clamp[+!x] = !!z;
|
||||
return brush;
|
||||
};
|
||||
|
||||
brush.extent = function(z) {
|
||||
var x0, x1, y0, y1, t;
|
||||
|
||||
|
|
|
@ -91,6 +91,7 @@ function d3_svg_lineY(d) {
|
|||
var d3_svg_lineInterpolators = d3.map({
|
||||
"linear": d3_svg_lineLinear,
|
||||
"linear-closed": d3_svg_lineLinearClosed,
|
||||
"step": d3_svg_lineStep,
|
||||
"step-before": d3_svg_lineStepBefore,
|
||||
"step-after": d3_svg_lineStepAfter,
|
||||
"basis": d3_svg_lineBasis,
|
||||
|
@ -117,6 +118,17 @@ function d3_svg_lineLinearClosed(points) {
|
|||
return d3_svg_lineLinear(points) + "Z";
|
||||
}
|
||||
|
||||
// Step interpolation; generates "H" and "V" commands.
|
||||
function d3_svg_lineStep(points) {
|
||||
var i = 0,
|
||||
n = points.length,
|
||||
p = points[0],
|
||||
path = [p[0], ",", p[1]];
|
||||
while (++i < n) path.push("H", (p[0] + (p = points[i])[0]) / 2, "V", p[1]);
|
||||
if (n > 1) path.push("H", p[0]);
|
||||
return path.join("");
|
||||
}
|
||||
|
||||
// Step interpolation; generates "H" and "V" commands.
|
||||
function d3_svg_lineStepBefore(points) {
|
||||
var i = 0,
|
||||
|
|
|
@ -3,7 +3,7 @@ import "time";
|
|||
import "year";
|
||||
|
||||
d3.time.day = d3_time_interval(function(date) {
|
||||
var day = new d3_time(1970, 0);
|
||||
var day = new d3_time(2000, 0);
|
||||
day.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
|
||||
return day;
|
||||
}, function(date, offset) {
|
||||
|
|
|
@ -36,8 +36,14 @@ d3.time.format = function(template) {
|
|||
// The am-pm flag is 0 for AM, and 1 for PM.
|
||||
if ("p" in d) d.H = d.H % 12 + d.p * 12;
|
||||
|
||||
var date = new d3_time();
|
||||
date.setFullYear(d.y, d.m, d.d);
|
||||
var date = new d3_time;
|
||||
if ("j" in d) date.setFullYear(d.y, 0, d.j);
|
||||
else if ("w" in d && ("W" in d || "U" in d)) {
|
||||
date.setFullYear(d.y, 0, 1);
|
||||
date.setFullYear(d.y, 0, "W" in d
|
||||
? (d.w + 6) % 7 + d.W * 7 - (date.getDay() + 5) % 7
|
||||
: d.w + d.U * 7 - (date.getDay() + 6) % 7);
|
||||
} else date.setFullYear(d.y, d.m, d.d);
|
||||
date.setHours(d.H, d.M, d.S, d.L);
|
||||
return date;
|
||||
};
|
||||
|
@ -79,17 +85,21 @@ function d3_time_formatLookup(names) {
|
|||
}
|
||||
|
||||
function d3_time_formatPad(value, fill, width) {
|
||||
value += "";
|
||||
var length = value.length;
|
||||
return length < width ? new Array(width - length + 1).join(fill) + value : value;
|
||||
var sign = value < 0 ? "-" : "",
|
||||
string = (sign ? -value : value) + "",
|
||||
length = string.length;
|
||||
return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string);
|
||||
}
|
||||
|
||||
var d3_time_dayRe = d3_time_formatRe(d3_time_days),
|
||||
d3_time_dayLookup = d3_time_formatLookup(d3_time_days),
|
||||
d3_time_dayAbbrevRe = d3_time_formatRe(d3_time_dayAbbreviations),
|
||||
d3_time_dayAbbrevLookup = d3_time_formatLookup(d3_time_dayAbbreviations),
|
||||
d3_time_monthRe = d3_time_formatRe(d3_time_months),
|
||||
d3_time_monthLookup = d3_time_formatLookup(d3_time_months),
|
||||
d3_time_monthAbbrevRe = d3_time_formatRe(d3_time_monthAbbreviations),
|
||||
d3_time_monthAbbrevLookup = d3_time_formatLookup(d3_time_monthAbbreviations);
|
||||
d3_time_monthAbbrevLookup = d3_time_formatLookup(d3_time_monthAbbreviations),
|
||||
d3_time_percentRe = /^%/;
|
||||
|
||||
var d3_time_formatPads = {
|
||||
"-": "",
|
||||
|
@ -134,48 +144,63 @@ var d3_time_parsers = {
|
|||
e: d3_time_parseDay,
|
||||
H: d3_time_parseHour24,
|
||||
I: d3_time_parseHour24,
|
||||
// j: function(d, s, i) { /*TODO day of year [001,366] */ return i; },
|
||||
j: d3_time_parseDayOfYear,
|
||||
L: d3_time_parseMilliseconds,
|
||||
m: d3_time_parseMonthNumber,
|
||||
M: d3_time_parseMinutes,
|
||||
p: d3_time_parseAmPm,
|
||||
S: d3_time_parseSeconds,
|
||||
// U: function(d, s, i) { /*TODO week number (sunday) [00,53] */ return i; },
|
||||
// w: function(d, s, i) { /*TODO weekday [0,6] */ return i; },
|
||||
// W: function(d, s, i) { /*TODO week number (monday) [00,53] */ return i; },
|
||||
U: d3_time_parseWeekNumberSunday,
|
||||
w: d3_time_parseWeekdayNumber,
|
||||
W: d3_time_parseWeekNumberMonday,
|
||||
x: d3_time_parseLocaleDate,
|
||||
X: d3_time_parseLocaleTime,
|
||||
y: d3_time_parseYear,
|
||||
Y: d3_time_parseFullYear
|
||||
// ,
|
||||
Y: d3_time_parseFullYear,
|
||||
// Z: function(d, s, i) { /*TODO time zone */ return i; },
|
||||
// "%": function(d, s, i) { /*TODO literal % */ return i; }
|
||||
"%": d3_time_parseLiteralPercent
|
||||
};
|
||||
|
||||
// Note: weekday is validated, but does not set the date.
|
||||
function d3_time_parseWeekdayAbbrev(date, string, i) {
|
||||
d3_time_dayAbbrevRe.lastIndex = 0;
|
||||
var n = d3_time_dayAbbrevRe.exec(string.substring(i));
|
||||
return n ? i += n[0].length : -1;
|
||||
return n ? (date.w = d3_time_dayAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
|
||||
}
|
||||
|
||||
// Note: weekday is validated, but does not set the date.
|
||||
function d3_time_parseWeekday(date, string, i) {
|
||||
d3_time_dayRe.lastIndex = 0;
|
||||
var n = d3_time_dayRe.exec(string.substring(i));
|
||||
return n ? i += n[0].length : -1;
|
||||
return n ? (date.w = d3_time_dayLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
|
||||
}
|
||||
|
||||
function d3_time_parseWeekdayNumber(date, string, i) {
|
||||
d3_time_numberRe.lastIndex = 0;
|
||||
var n = d3_time_numberRe.exec(string.substring(i, i + 1));
|
||||
return n ? (date.w = +n[0], i + n[0].length) : -1;
|
||||
}
|
||||
|
||||
function d3_time_parseWeekNumberSunday(date, string, i) {
|
||||
d3_time_numberRe.lastIndex = 0;
|
||||
var n = d3_time_numberRe.exec(string.substring(i));
|
||||
return n ? (date.U = +n[0], i + n[0].length) : -1;
|
||||
}
|
||||
|
||||
function d3_time_parseWeekNumberMonday(date, string, i) {
|
||||
d3_time_numberRe.lastIndex = 0;
|
||||
var n = d3_time_numberRe.exec(string.substring(i));
|
||||
return n ? (date.W = +n[0], i + n[0].length) : -1;
|
||||
}
|
||||
|
||||
function d3_time_parseMonthAbbrev(date, string, i) {
|
||||
d3_time_monthAbbrevRe.lastIndex = 0;
|
||||
var n = d3_time_monthAbbrevRe.exec(string.substring(i));
|
||||
return n ? (date.m = d3_time_monthAbbrevLookup.get(n[0].toLowerCase()), i += n[0].length) : -1;
|
||||
return n ? (date.m = d3_time_monthAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
|
||||
}
|
||||
|
||||
function d3_time_parseMonth(date, string, i) {
|
||||
d3_time_monthRe.lastIndex = 0;
|
||||
var n = d3_time_monthRe.exec(string.substring(i));
|
||||
return n ? (date.m = d3_time_monthLookup.get(n[0].toLowerCase()), i += n[0].length) : -1;
|
||||
return n ? (date.m = d3_time_monthLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
|
||||
}
|
||||
|
||||
function d3_time_parseLocaleFull(date, string, i) {
|
||||
|
@ -193,13 +218,13 @@ function d3_time_parseLocaleTime(date, string, i) {
|
|||
function d3_time_parseFullYear(date, string, i) {
|
||||
d3_time_numberRe.lastIndex = 0;
|
||||
var n = d3_time_numberRe.exec(string.substring(i, i + 4));
|
||||
return n ? (date.y = +n[0], i += n[0].length) : -1;
|
||||
return n ? (date.y = +n[0], i + n[0].length) : -1;
|
||||
}
|
||||
|
||||
function d3_time_parseYear(date, string, i) {
|
||||
d3_time_numberRe.lastIndex = 0;
|
||||
var n = d3_time_numberRe.exec(string.substring(i, i + 2));
|
||||
return n ? (date.y = d3_time_expandYear(+n[0]), i += n[0].length) : -1;
|
||||
return n ? (date.y = d3_time_expandYear(+n[0]), i + n[0].length) : -1;
|
||||
}
|
||||
|
||||
function d3_time_expandYear(d) {
|
||||
|
@ -209,38 +234,44 @@ function d3_time_expandYear(d) {
|
|||
function d3_time_parseMonthNumber(date, string, i) {
|
||||
d3_time_numberRe.lastIndex = 0;
|
||||
var n = d3_time_numberRe.exec(string.substring(i, i + 2));
|
||||
return n ? (date.m = n[0] - 1, i += n[0].length) : -1;
|
||||
return n ? (date.m = n[0] - 1, i + n[0].length) : -1;
|
||||
}
|
||||
|
||||
function d3_time_parseDay(date, string, i) {
|
||||
d3_time_numberRe.lastIndex = 0;
|
||||
var n = d3_time_numberRe.exec(string.substring(i, i + 2));
|
||||
return n ? (date.d = +n[0], i += n[0].length) : -1;
|
||||
return n ? (date.d = +n[0], i + n[0].length) : -1;
|
||||
}
|
||||
|
||||
function d3_time_parseDayOfYear(date, string, i) {
|
||||
d3_time_numberRe.lastIndex = 0;
|
||||
var n = d3_time_numberRe.exec(string.substring(i, i + 3));
|
||||
return n ? (date.j = +n[0], i + n[0].length) : -1;
|
||||
}
|
||||
|
||||
// Note: we don't validate that the hour is in the range [0,23] or [1,12].
|
||||
function d3_time_parseHour24(date, string, i) {
|
||||
d3_time_numberRe.lastIndex = 0;
|
||||
var n = d3_time_numberRe.exec(string.substring(i, i + 2));
|
||||
return n ? (date.H = +n[0], i += n[0].length) : -1;
|
||||
return n ? (date.H = +n[0], i + n[0].length) : -1;
|
||||
}
|
||||
|
||||
function d3_time_parseMinutes(date, string, i) {
|
||||
d3_time_numberRe.lastIndex = 0;
|
||||
var n = d3_time_numberRe.exec(string.substring(i, i + 2));
|
||||
return n ? (date.M = +n[0], i += n[0].length) : -1;
|
||||
return n ? (date.M = +n[0], i + n[0].length) : -1;
|
||||
}
|
||||
|
||||
function d3_time_parseSeconds(date, string, i) {
|
||||
d3_time_numberRe.lastIndex = 0;
|
||||
var n = d3_time_numberRe.exec(string.substring(i, i + 2));
|
||||
return n ? (date.S = +n[0], i += n[0].length) : -1;
|
||||
return n ? (date.S = +n[0], i + n[0].length) : -1;
|
||||
}
|
||||
|
||||
function d3_time_parseMilliseconds(date, string, i) {
|
||||
d3_time_numberRe.lastIndex = 0;
|
||||
var n = d3_time_numberRe.exec(string.substring(i, i + 3));
|
||||
return n ? (date.L = +n[0], i += n[0].length) : -1;
|
||||
return n ? (date.L = +n[0], i + n[0].length) : -1;
|
||||
}
|
||||
|
||||
// Note: we don't look at the next directive.
|
||||
|
@ -264,3 +295,9 @@ function d3_time_zone(d) {
|
|||
zm = Math.abs(z) % 60;
|
||||
return zs + d3_time_formatPad(zh, "0", 2) + d3_time_formatPad(zm, "0", 2);
|
||||
}
|
||||
|
||||
function d3_time_parseLiteralPercent(date, string, i) {
|
||||
d3_time_percentRe.lastIndex = 0;
|
||||
var n = d3_time_percentRe.exec(string.substring(i, i + 1));
|
||||
return n ? i + n[0].length : -1;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ function d3_time_scale(linear, methods, format) {
|
|||
};
|
||||
|
||||
scale.nice = function(m) {
|
||||
return scale.domain(d3_scale_nice(scale.domain(), function() { return m; }));
|
||||
return scale.domain(d3_scale_nice(scale.domain(), m));
|
||||
};
|
||||
|
||||
scale.ticks = function(m, k) {
|
||||
|
|
|
@ -17,7 +17,13 @@ function d3_xhr(url, mimeType, response, callback) {
|
|||
var xhr = {},
|
||||
dispatch = d3.dispatch("progress", "load", "error"),
|
||||
headers = {},
|
||||
request = new (d3_window.XDomainRequest && /^(http(s)?:)?\/\//.test(url) ? XDomainRequest : XMLHttpRequest);
|
||||
request = new XMLHttpRequest,
|
||||
responseType = null;
|
||||
|
||||
// If IE does not support CORS, use XDomainRequest.
|
||||
if (d3_window.XDomainRequest
|
||||
&& !("withCredentials" in request)
|
||||
&& /^(http(s)?:)?\/\//.test(url)) request = new XDomainRequest;
|
||||
|
||||
"onload" in request
|
||||
? request.onload = request.onerror = respond
|
||||
|
@ -60,6 +66,14 @@ function d3_xhr(url, mimeType, response, callback) {
|
|||
return xhr;
|
||||
};
|
||||
|
||||
// Specifies what type the response value should take;
|
||||
// for instance, arraybuffer, blob, document, or text.
|
||||
xhr.responseType = function(value) {
|
||||
if (!arguments.length) return responseType;
|
||||
responseType = value;
|
||||
return xhr;
|
||||
};
|
||||
|
||||
// Specify how to convert the response content to a specific type;
|
||||
// changes the callback value on "load" events.
|
||||
xhr.response = function(value) {
|
||||
|
@ -81,6 +95,7 @@ function d3_xhr(url, mimeType, response, callback) {
|
|||
if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*";
|
||||
if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]);
|
||||
if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType);
|
||||
if (responseType != null) request.responseType = responseType;
|
||||
if (callback != null) xhr.on("error", callback).on("load", function(request) { callback(null, request); });
|
||||
request.send(data == null ? null : data);
|
||||
return xhr;
|
||||
|
|
|
@ -31,6 +31,18 @@ suite.addBatch({
|
|||
[-64.66070178517852, 18.33986913231323]
|
||||
]]}), 4.890516e-13, 1e-13);
|
||||
},
|
||||
"zero area": function(area) {
|
||||
assert.equal(area({
|
||||
"type": "Polygon",
|
||||
"coordinates": [[
|
||||
[96.79142432523281, 5.262704519048153],
|
||||
[96.81065389253769, 5.272455576551362],
|
||||
[96.82988345984256, 5.272455576551362],
|
||||
[96.81065389253769, 5.272455576551362],
|
||||
[96.79142432523281, 5.262704519048153]
|
||||
]]
|
||||
}), 0);
|
||||
},
|
||||
"semilune": function(area) {
|
||||
assert.inDelta(area({type: "Polygon", coordinates: [[[0, 0], [0, 90], [90, 0], [0, 0]]]}), π / 2, 1e-6);
|
||||
},
|
||||
|
|
|
@ -10,6 +10,26 @@ var formatNumber = d3.format(",.02r"),
|
|||
o,
|
||||
then;
|
||||
|
||||
o = circle();
|
||||
then = Date.now();
|
||||
|
||||
for (var i = 0, k = 0; i < n * 1000; i++, k++) {
|
||||
path(o);
|
||||
}
|
||||
|
||||
console.log("Single circle: " + formatNumber((Date.now() - then) / k) + "ms/op.");
|
||||
|
||||
o = JSON.parse(fs.readFileSync("./test/data/us-counties.json")).features;
|
||||
then = Date.now();
|
||||
|
||||
for (var i = 0, k = 0; i < n; i++, k++) {
|
||||
for (var j = 0, m = o.length; j < m; ++j) {
|
||||
path(o[j]);
|
||||
}
|
||||
}
|
||||
|
||||
console.log("U.S. counties (separate): " + formatNumber((Date.now() - then) / k) + "ms/op.");
|
||||
|
||||
o = JSON.parse(fs.readFileSync("./test/data/us-counties.json"));
|
||||
then = Date.now();
|
||||
|
||||
|
|
|
@ -228,6 +228,36 @@ suite.addBatch({
|
|||
}
|
||||
]
|
||||
}), [[-120,46], [-119,47]]);
|
||||
},
|
||||
"null geometries": {
|
||||
"Feature": function(bounds) {
|
||||
var b = bounds({type: "Feature", geometry: null});
|
||||
assert.isNaN(b[0][0]);
|
||||
assert.isNaN(b[0][1]);
|
||||
assert.isNaN(b[1][0]);
|
||||
assert.isNaN(b[1][1]);
|
||||
},
|
||||
"MultiPoint": function(bounds) {
|
||||
var b = bounds({type: "MultiPoint", coordinates: []});
|
||||
assert.isNaN(b[0][0]);
|
||||
assert.isNaN(b[0][1]);
|
||||
assert.isNaN(b[1][0]);
|
||||
assert.isNaN(b[1][1]);
|
||||
},
|
||||
"MultiLineString": function(bounds) {
|
||||
var b = bounds({type: "MultiLineString", coordinates: []});
|
||||
assert.isNaN(b[0][0]);
|
||||
assert.isNaN(b[0][1]);
|
||||
assert.isNaN(b[1][0]);
|
||||
assert.isNaN(b[1][1]);
|
||||
},
|
||||
"MultiPolygon": function(bounds) {
|
||||
var b = bounds({type: "MultiPolygon", coordinates: []});
|
||||
assert.isNaN(b[0][0]);
|
||||
assert.isNaN(b[0][1]);
|
||||
assert.isNaN(b[1][0]);
|
||||
assert.isNaN(b[1][1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -8,26 +8,31 @@ var suite = vows.describe("d3.geo.centroid");
|
|||
suite.addBatch({
|
||||
"centroid": {
|
||||
topic: load("geo/centroid").expression("d3.geo.centroid"),
|
||||
"Point": function(centroid) {
|
||||
assert.deepEqual(centroid({type: "Point", coordinates: [0, 0]}), [0, 0]);
|
||||
|
||||
"the centroid of a point is itself": function(centroid) {
|
||||
assert.inDelta(centroid({type: "Point", coordinates: [0, 0]}), [0, 0], 1e-6);
|
||||
assert.inDelta(centroid({type: "Point", coordinates: [1, 1]}), [1, 1], 1e-6);
|
||||
assert.inDelta(centroid({type: "Point", coordinates: [2, 3]}), [2, 3], 1e-6);
|
||||
assert.inDelta(centroid({type: "Point", coordinates: [-4, -5]}), [-4, -5], 1e-6);
|
||||
},
|
||||
"MultiPoint": {
|
||||
"": function(centroid) {
|
||||
assert.inDelta(centroid({type: "MultiPoint", coordinates: [[0, 0], [1, 2]]}), [0.499847, 1.000038], 1e-6);
|
||||
},
|
||||
"antimeridian": function(centroid) {
|
||||
assert.deepEqual(centroid({type: "MultiPoint", coordinates: [[179, 0], [-179, 0]]}), [180, 0]);
|
||||
},
|
||||
"rings": {
|
||||
"equator": function(centroid) {
|
||||
assert.isUndefined(centroid({type: "MultiPoint", coordinates: [[0, 0], [90, 0], [180, 0], [-90, 0]]}));
|
||||
},
|
||||
"polar": function(centroid) {
|
||||
assert.isUndefined(centroid({type: "MultiPoint", coordinates: [[0, 0], [0, 90], [180, 0], [0, -90]]}));
|
||||
}
|
||||
}
|
||||
|
||||
"the centroid of a set of points is the (spherical) average of its constituent members": function(centroid) {
|
||||
assert.inDelta(centroid({type: "GeometryCollection", geometries: [{type: "Point", coordinates: [0, 0]}, {type: "Point", coordinates: [1, 2]}]}), [0.499847, 1.000038], 1e-6);
|
||||
assert.inDelta(centroid({type: "MultiPoint", coordinates: [[0, 0], [1, 2]]}), [0.499847, 1.000038], 1e-6);
|
||||
assert.inDelta(centroid({type: "MultiPoint", coordinates: [[179, 0], [-179, 0]]}), [180, 0], 1e-6);
|
||||
},
|
||||
"LineString": function(centroid) {
|
||||
|
||||
"the centroid of a set of points and their antipodes is ambiguous": function(centroid) {
|
||||
assert.ok(centroid({type: "MultiPoint", coordinates: [[0, 0], [180, 0]]}).every(isNaN));
|
||||
assert.ok(centroid({type: "MultiPoint", coordinates: [[0, 0], [90, 0], [180, 0], [-90, 0]]}).every(isNaN));
|
||||
assert.ok(centroid({type: "MultiPoint", coordinates: [[0, 0], [0, 90], [180, 0], [0, -90]]}).every(isNaN));
|
||||
},
|
||||
|
||||
"the centroid of the empty set of points is ambiguous": function(centroid) {
|
||||
assert.ok(centroid({type: "MultiPoint", coordinates: []}).every(isNaN));
|
||||
},
|
||||
|
||||
"the centroid of a line string is the (spherical) average of its constituent great arc segments": function(centroid) {
|
||||
assert.inDelta(centroid({type: "LineString", coordinates: [[0, 0], [1, 0]]}), [.5, 0], 1e-6);
|
||||
assert.inDelta(centroid({type: "LineString", coordinates: [[0, 0], [0, 90]]}), [0, 45], 1e-6);
|
||||
assert.inDelta(centroid({type: "LineString", coordinates: [[0, 0], [0, 45], [0, 90]]}), [0, 45], 1e-6);
|
||||
|
@ -36,65 +41,145 @@ suite.addBatch({
|
|||
assert.inDelta(centroid({type: "LineString", coordinates: [[179, -1], [-179, 1]]}), [180, 0], 1e-6);
|
||||
assert.inDelta(centroid({type: "LineString", coordinates: [[-179, 0], [0, 0], [179, 0]]}), [0, 0], 1e-6);
|
||||
assert.inDelta(centroid({type: "LineString", coordinates: [[-180, -90], [0, 0], [0, 90]]}), [0, 0], 1e-6);
|
||||
assert.isUndefined(centroid({type: "LineString", coordinates: [[0, -90], [0, 90]]}));
|
||||
},
|
||||
"MultiLineString": function(centroid) {
|
||||
|
||||
"the centroid of a great arc from a point to its antipode is ambiguous": function(centroid) {
|
||||
assert.ok(centroid({type: "LineString", coordinates: [[180, 0], [0, 0]]}).every(isNaN));
|
||||
assert.ok(centroid({type: "MultiLineString", coordinates: [[[0, -90], [0, 90]]]}).every(isNaN));
|
||||
},
|
||||
|
||||
"the centroid of a set of line strings is the (spherical) average of its constituent great arc segments": function(centroid) {
|
||||
assert.inDelta(centroid({type: "MultiLineString", coordinates: [[[0, 0], [0, 2]]]}), [0, 1], 1e-6);
|
||||
},
|
||||
"Polygon": function(centroid) {
|
||||
|
||||
"a line of zero length is treated as points": function(centroid) {
|
||||
assert.inDelta(centroid({type: "LineString", coordinates: [[1, 1], [1, 1]]}), [1, 1], 1e-6);
|
||||
assert.inDelta(centroid({type: "GeometryCollection", geometries: [{type: "Point", coordinates: [0, 0]}, {type: "LineString", coordinates: [[1, 2], [1, 2]]}]}), [0.666534, 1.333408], 1e-6);
|
||||
},
|
||||
|
||||
"an empty polygon with non-zero extent is treated as a line": function(centroid) {
|
||||
assert.inDelta(centroid({type: "Polygon", coordinates: [[[1, 1], [2, 1], [3, 1], [2, 1], [1, 1]]]}), [2, 1.000076], 1e-6);
|
||||
assert.inDelta(centroid({type: "GeometryCollection", geometries: [{type: "Point", coordinates: [0, 0]}, {type: "Polygon", coordinates: [[[1, 2], [1, 2], [1, 2], [1, 2]]]}]}), [0.799907, 1.600077], 1e-6);
|
||||
},
|
||||
|
||||
"an empty polygon with zero extent is treated as a point": function(centroid) {
|
||||
assert.inDelta(centroid({type: "Polygon", coordinates: [[[1, 1], [1, 1], [1, 1], [1, 1]]]}), [1, 1], 1e-6);
|
||||
assert.inDelta(centroid({type: "GeometryCollection", geometries: [{type: "Point", coordinates: [0, 0]}, {type: "Polygon", coordinates: [[[1, 2], [1, 2], [1, 2], [1, 2]]]}]}), [0.799907, 1.600077], 1e-6);
|
||||
},
|
||||
|
||||
"the centroid of the equator is ambiguous": function(centroid) {
|
||||
assert.ok(centroid({type: "LineString", coordinates: [[0, 0], [120, 0], [-120, 0], [0, 0]]}).every(isNaN));
|
||||
},
|
||||
|
||||
"the centroid of a polygon is the (spherical) average of its surface": function(centroid) {
|
||||
assert.inDelta(centroid({type: "Polygon", coordinates: [[[0, -90], [0, 0], [0, 90], [1, 0], [0, -90]]]}), [.5, 0], 1e-6);
|
||||
assert.inDelta(centroid(_.geo.circle().angle(5).origin([0, 45])()), [0, 45], 1e-6);
|
||||
assert.equal(centroid({type: "Polygon", coordinates: [_.range(-180, 180 + 1 / 2, 1).map(function(x) { return [x, -60]; })]})[1], -90);
|
||||
assert.inDelta(centroid({type: "Polygon", coordinates: [_.range(-180, 180 + 1 / 2, 1).map(function(x) { return [x, -60]; })]})[1], -90, 1e-6);
|
||||
assert.inDelta(centroid({type: "Polygon", coordinates: [[[0, -10], [0, 10], [10, 10], [10, -10], [0, -10]]]}), [5, 0], 1e-6);
|
||||
},
|
||||
"MultiPolygon": function(centroid) {
|
||||
assert.inDelta(centroid({type: "MultiPolygon", coordinates: [[[[0, -90], [0, 0], [0, 90], [1, 0], [0, -90]]]]}), [.5, 0], 1e-6);
|
||||
|
||||
"the centroid of a set of polygons is the (spherical) average of its surface": function(centroid) {
|
||||
var circle = _.geo.circle();
|
||||
assert.inDelta(centroid({
|
||||
type: "MultiPolygon",
|
||||
coordinates: [
|
||||
circle.angle(45).origin([0, 0])().coordinates,
|
||||
circle.angle(60).origin([180, 0])().coordinates
|
||||
]
|
||||
}), [180, 0], 1e-6);
|
||||
},
|
||||
"Sphere": function(centroid) {
|
||||
assert.isUndefined(centroid({type: "Sphere"}));
|
||||
|
||||
"the centroid of a lune is the (spherical) average of its surface": function(centroid) {
|
||||
assert.inDelta(centroid({type: "Polygon", coordinates: [[[0, -90], [0, 0], [0, 90], [1, 0], [0, -90]]]}), [.5, 0], 1e-6);
|
||||
},
|
||||
"Feature": function(centroid) {
|
||||
assert.deepEqual(centroid({type: "Feature", geometry: {type: "Point", coordinates: [0, 0]}}), [0, 0]);
|
||||
|
||||
"the centroid of a small circle is its origin": {
|
||||
"5°": function(centroid) {
|
||||
assert.inDelta(centroid(_.geo.circle().angle(5).origin([30, 45])()), [30, 45], 1e-6);
|
||||
},
|
||||
"135°": function(centroid) {
|
||||
assert.inDelta(centroid(_.geo.circle().angle(135).origin([30, 45])()), [30, 45], 1e-6);
|
||||
},
|
||||
"South Pole": function(centroid) {
|
||||
assert.equal(centroid({type: "Polygon", coordinates: [_.range(-180, 180 + 1 / 2, 1).map(function(x) { return [x, -60]; })]})[1], -90);
|
||||
},
|
||||
"equator": function(centroid) {
|
||||
assert.inDelta(centroid({type: "Polygon", coordinates: [[[0, -10], [0, 10], [10, 10], [10, -10], [0, -10]]]}), [5, 0], 1e-6);
|
||||
},
|
||||
"equator with coincident points": function(centroid) {
|
||||
assert.inDelta(centroid({type: "Polygon", coordinates: [[[0, -10], [0, 10], [0, 10], [10, 10], [10, -10], [0, -10]]]}), [5, 0], 1e-6);
|
||||
},
|
||||
"other": function(centroid) {
|
||||
assert.inDelta(centroid({type: "Polygon", coordinates: [[[-180, 0], [-180, 10], [-179, 10], [-179, 0], [-180, 0]]]}), [-179.5, 4.987448], 1e-6);
|
||||
},
|
||||
"concentric rings": function(centroid) {
|
||||
var circle = _.geo.circle().origin([0, 45]),
|
||||
coordinates = circle.angle(60)().coordinates;
|
||||
coordinates.push(circle.angle(45)().coordinates[0].reverse());
|
||||
assert.inDelta(centroid({type: "Polygon", coordinates: coordinates}), [0, 45], 1e-6);
|
||||
}
|
||||
},
|
||||
"FeatureCollection": function(centroid) {
|
||||
|
||||
"the centroid of a spherical square on the equator": function(centroid) {
|
||||
assert.inDelta(centroid({type: "Polygon", coordinates: [[[0, -10], [0, 10], [10, 10], [10, -10], [0, -10]]]}), [5, 0], 1e-6);
|
||||
},
|
||||
|
||||
"the centroid of a spherical square touching the antimeridian": function(centroid) {
|
||||
assert.inDelta(centroid({type: "Polygon", coordinates: [[[-180, 0], [-180, 10], [-179, 10], [-179, 0], [-180, 0]]]}), [-179.5, 4.987448], 1e-6);
|
||||
},
|
||||
|
||||
"concentric rings": function(centroid) {
|
||||
var circle = _.geo.circle().origin([0, 45]),
|
||||
coordinates = circle.angle(60)().coordinates;
|
||||
coordinates.push(circle.angle(45)().coordinates[0].reverse());
|
||||
assert.inDelta(centroid({type: "Polygon", coordinates: coordinates}), [0, 45], 1e-6);
|
||||
},
|
||||
|
||||
"the centroid of a sphere is ambiguous": function(centroid) {
|
||||
assert.ok(centroid({type: "Sphere"}).every(isNaN));
|
||||
},
|
||||
|
||||
"the centroid of a feature is the centroid of its constituent geometry": function(centroid) {
|
||||
assert.inDelta(centroid({type: "Feature", geometry: {type: "LineString", coordinates: [[1, 1], [1, 1]]}}), [1, 1], 1e-6);
|
||||
assert.inDelta(centroid({type: "Feature", geometry: {type: "Point", coordinates: [1, 1]}}), [1, 1], 1e-6);
|
||||
assert.inDelta(centroid({type: "Feature", geometry: {type: "Polygon", coordinates: [[[0, -90], [0, 0], [0, 90], [1, 0], [0, -90]]]}}), [.5, 0], 1e-6);
|
||||
},
|
||||
|
||||
"the centroid of a feature collection is the centroid of its constituent geometry": function(centroid) {
|
||||
assert.inDelta(centroid({type: "FeatureCollection", features: [
|
||||
{type: "Feature", geometry: {type: "LineString", coordinates: [[179, 0], [180, 0]]}},
|
||||
{type: "Feature", geometry: {type: "Point", coordinates: [0, 0]}}
|
||||
]}), [179.5, 0], 1e-6);
|
||||
},
|
||||
"GeometryCollection": {
|
||||
"LineString, Point": function(centroid) {
|
||||
assert.inDelta(centroid({type: "GeometryCollection", geometries: [
|
||||
{type: "LineString", coordinates: [[179, 0], [180, 0]]},
|
||||
{type: "Point", coordinates: [0, 0]}
|
||||
]}), [179.5, 0], 1e-6);
|
||||
},
|
||||
"Polygon, LineString, Point": function(centroid) {
|
||||
assert.inDelta(centroid({type: "GeometryCollection", geometries: [
|
||||
{type: "Polygon", coordinates: [[[-180, 0], [-180, 1], [-179, 1], [-179, 0], [-180, 0]]]},
|
||||
{type: "LineString", coordinates: [[179, 0], [180, 0]]},
|
||||
{type: "Point", coordinates: [0, 0]}
|
||||
]}), [-179.5, 0.5], 1e-6);
|
||||
},
|
||||
"Point, LineString, Polygon": function(centroid) {
|
||||
assert.inDelta(centroid({type: "GeometryCollection", geometries: [
|
||||
{type: "Point", coordinates: [0, 0]},
|
||||
{type: "LineString", coordinates: [[179, 0], [180, 0]]},
|
||||
{type: "Polygon", coordinates: [[[-180, 0], [-180, 1], [-179, 1], [-179, 0], [-180, 0]]]}
|
||||
]}), [-179.5, 0.5], 1e-6);
|
||||
},
|
||||
"Sphere, Point": function(centroid) {
|
||||
assert.isUndefined(centroid({type: "GeometryCollection", geometries: [
|
||||
{type: "Sphere"},
|
||||
{type: "Point", coordinates: [0, 0]}
|
||||
]}));
|
||||
},
|
||||
"Point, Sphere": function(centroid) {
|
||||
assert.isUndefined(centroid({type: "GeometryCollection", geometries: [
|
||||
{type: "Point", coordinates: [0, 0]},
|
||||
{type: "Sphere"}
|
||||
]}));
|
||||
}
|
||||
|
||||
"the centroid of a non-empty line string and a point only considers the line string": function(centroid) {
|
||||
assert.inDelta(centroid({type: "GeometryCollection", geometries: [
|
||||
{type: "LineString", coordinates: [[179, 0], [180, 0]]},
|
||||
{type: "Point", coordinates: [0, 0]}
|
||||
]}), [179.5, 0], 1e-6);
|
||||
},
|
||||
|
||||
"the centroid of a non-empty polygon, a non-empty line string and a point only considers the polygon": function(centroid) {
|
||||
assert.inDelta(centroid({type: "GeometryCollection", geometries: [
|
||||
{type: "Polygon", coordinates: [[[-180, 0], [-180, 1], [-179, 1], [-179, 0], [-180, 0]]]},
|
||||
{type: "LineString", coordinates: [[179, 0], [180, 0]]},
|
||||
{type: "Point", coordinates: [0, 0]}
|
||||
]}), [-179.5, 0.500006], 1e-6);
|
||||
assert.inDelta(centroid({type: "GeometryCollection", geometries: [
|
||||
{type: "Point", coordinates: [0, 0]},
|
||||
{type: "LineString", coordinates: [[179, 0], [180, 0]]},
|
||||
{type: "Polygon", coordinates: [[[-180, 0], [-180, 1], [-179, 1], [-179, 0], [-180, 0]]]}
|
||||
]}), [-179.5, 0.500006], 1e-6);
|
||||
},
|
||||
|
||||
"the centroid of the sphere and a point is the point": function(centroid) {
|
||||
assert.deepEqual(centroid({type: "GeometryCollection", geometries: [
|
||||
{type: "Sphere"},
|
||||
{type: "Point", coordinates: [0, 0]}
|
||||
]}), [0, 0]);
|
||||
assert.deepEqual(centroid({type: "GeometryCollection", geometries: [
|
||||
{type: "Point", coordinates: [0, 0]},
|
||||
{type: "Sphere"}
|
||||
]}), [0, 0]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -130,7 +130,7 @@ suite.addBatch({
|
|||
assert.deepEqual(centroid({type: "Point", coordinates: [0, 0]}), [480, 250]);
|
||||
},
|
||||
"of an empty multipoint": function(centroid) {
|
||||
assert.isUndefined(centroid({type: "MultiPoint", coordinates: []}));
|
||||
assert.ok(centroid({type: "MultiPoint", coordinates: []}).every(isNaN));
|
||||
},
|
||||
"of a singleton multipoint": function(centroid) {
|
||||
assert.deepEqual(centroid({type: "MultiPoint", coordinates: [[0, 0]]}), [480, 250]);
|
||||
|
@ -139,15 +139,15 @@ suite.addBatch({
|
|||
assert.deepEqual(centroid({type: "MultiPoint", coordinates: [[-122, 37], [-74, 40]]}), [-10, 57.5]);
|
||||
},
|
||||
"of an empty linestring": function(centroid) {
|
||||
assert.isUndefined(centroid({type: "LineString", coordinates: []}));
|
||||
assert.ok(centroid({type: "LineString", coordinates: []}).every(isNaN));
|
||||
},
|
||||
"of a linestring with two points": function(centroid) {
|
||||
assert.deepEqual(centroid({type: "LineString", coordinates: [[100, 0], [0, 0]]}), [730, 250]);
|
||||
assert.deepEqual(centroid({type: "LineString", coordinates: [[0, 0], [100, 0], [101, 0]]}), [732.5, 250]);
|
||||
},
|
||||
"of a linestring with two points, one unique": function(centroid) {
|
||||
assert.isUndefined(centroid({type: "LineString", coordinates: [[-122, 37], [-122, 37]]}));
|
||||
assert.isUndefined(centroid({type: "LineString", coordinates: [[ -74, 40], [ -74, 40]]}));
|
||||
assert.deepEqual(centroid({type: "LineString", coordinates: [[-122, 37], [-122, 37]]}), [-130, 65]);
|
||||
assert.deepEqual(centroid({type: "LineString", coordinates: [[-74, 40], [-74, 40]]}), [110, 50]);
|
||||
},
|
||||
"of a linestring with three points; two unique": function(centroid) {
|
||||
assert.deepEqual(centroid({type: "LineString", coordinates: [[-122, 37], [-74, 40], [-74, 40]]}), [-10, 57.5]);
|
||||
|
@ -162,7 +162,7 @@ suite.addBatch({
|
|||
assert.deepEqual(centroid({type: "Polygon", coordinates: [[[100, 0], [100, 1], [101, 1], [101, 0], [100, 0]]]}), [982.5, 247.5]);
|
||||
},
|
||||
"of a zero-area polygon": function(centroid) {
|
||||
assert.isUndefined(centroid({type: "Polygon", coordinates: [[[1, 0], [2, 0], [3, 0], [1, 0]]]}));
|
||||
assert.deepEqual(centroid({type: "Polygon", coordinates: [[[1, 0], [2, 0], [3, 0], [1, 0]]]}), [490, 250]);
|
||||
},
|
||||
"of a polygon with two rings, one with zero area": function(centroid) {
|
||||
assert.deepEqual(centroid({type: "Polygon", coordinates: [
|
||||
|
@ -180,7 +180,7 @@ suite.addBatch({
|
|||
}), [479.642857, 250], 1e-6);
|
||||
},
|
||||
"of an empty multipolygon": function(centroid) {
|
||||
assert.isUndefined(centroid({type: "MultiPolygon", coordinates: []}));
|
||||
assert.ok(centroid({type: "MultiPolygon", coordinates: []}).every(isNaN));
|
||||
},
|
||||
"of a singleton multipolygon": function(centroid) {
|
||||
assert.deepEqual(centroid({type: "MultiPolygon", coordinates: [[[[100, 0], [100, 1], [101, 1], [101, 0], [100, 0]]]]}), [982.5, 247.5]);
|
||||
|
@ -294,7 +294,7 @@ suite.addBatch({
|
|||
[[109.378, 189.584], [797.758, 504.660]], 1e-3);
|
||||
},
|
||||
"centroid of a line string": function(p) {
|
||||
assert.inDelta(p.centroid({type: "LineString", coordinates: [[-122, 37], [-74, 40], [-100, 0]]}), [545.130, 253.859], 1e-3);
|
||||
assert.inDelta(p.centroid({type: "LineString", coordinates: [[-122, 37], [-74, 40], [-100, 0]]}), [545.131, 253.860], 1e-3);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -757,19 +757,38 @@ suite.addBatch({
|
|||
},
|
||||
|
||||
"with an Albers projection and adaptive resampling": {
|
||||
topic: function(path) {
|
||||
return path()
|
||||
"correctly resamples near the poles": function(path) {
|
||||
var p = path()
|
||||
.context(testContext)
|
||||
.projection(_.geo.albers()
|
||||
.scale(140)
|
||||
.rotate([0, 0])
|
||||
.precision(1));
|
||||
},
|
||||
"correctly resamples near the poles": function(p) {
|
||||
p({type: "LineString", coordinates: [[0, 88], [180, 89]]});
|
||||
assert.isTrue(testContext.buffer().filter(function(d) { return d.type === "lineTo"; }).length > 1);
|
||||
p({type: "LineString", coordinates: [[180, 90], [1, 89.5]]});
|
||||
assert.isTrue(testContext.buffer().filter(function(d) { return d.type === "lineTo"; }).length > 1);
|
||||
},
|
||||
"rotate([11.5, 285])": function(path) {
|
||||
var p = path()
|
||||
.context(testContext)
|
||||
.projection(_.geo.albers()
|
||||
.scale(140)
|
||||
.rotate([11.5, 285])
|
||||
.precision(1));
|
||||
p({type: "LineString", coordinates: [[170, 20], [170, 0]]});
|
||||
assert.isTrue(testContext.buffer().filter(function(d) { return d.type === "lineTo"; }).length > 1);
|
||||
},
|
||||
"wavy projection": function(path) {
|
||||
var p = path()
|
||||
.context(testContext)
|
||||
.projection(_.geo.projection(function(λ, φ) {
|
||||
return [λ, Math.sin(λ * 4)];
|
||||
})
|
||||
.scale(140)
|
||||
.precision(1));
|
||||
p({type: "LineString", coordinates: [[-45, 0], [45, 0]]});
|
||||
assert.isTrue(testContext.buffer().filter(function(d) { return d.type === "lineTo"; }).length > 1);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
import "../../src/geo/point-in-polygon";
|
||||
import "../../src/math/trigonometry";
|
||||
|
||||
d3.geo.pointInPolygon = function(polygon) {
|
||||
polygon = polygon.map(function(ring) {
|
||||
ring = ring.map(pointRadians);
|
||||
ring.pop();
|
||||
return ring;
|
||||
});
|
||||
|
||||
return function(point) {
|
||||
return d3_geo_pointInPolygon(pointRadians(point), polygon);
|
||||
};
|
||||
|
||||
function pointRadians(point) {
|
||||
return [point[0] * d3_radians, point[1] * d3_radians];
|
||||
}
|
||||
};
|
|
@ -0,0 +1,352 @@
|
|||
var vows = require("vows"),
|
||||
_ = require("../../"),
|
||||
load = require("../load"),
|
||||
assert = require("../assert");
|
||||
|
||||
var suite = vows.describe("d3.geo.pointInPolygon");
|
||||
|
||||
suite.addBatch({
|
||||
"d3.geo.pointInPolygon": {
|
||||
topic: load("../test/geo/point-in-polygon-mock").expression("d3.geo.pointInPolygon"),
|
||||
"empty": function(pointInPolygon) {
|
||||
assert.ok(!pointInPolygon([])([0, 0]));
|
||||
},
|
||||
"simple": {
|
||||
topic: function(pointInPolygon) {
|
||||
return pointInPolygon([[[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]]);
|
||||
},
|
||||
"outside": function(pointInPolygon) {
|
||||
assert.ok(!pointInPolygon([.1, 2]));
|
||||
},
|
||||
"inside": function(pointInPolygon) {
|
||||
assert.ok(pointInPolygon([.1, .1]));
|
||||
}
|
||||
},
|
||||
"small circle": {
|
||||
topic: function(pointInPolygon) {
|
||||
return pointInPolygon(_.geo.circle().angle(60)().coordinates);
|
||||
},
|
||||
"outside": function(pointInPolygon) {
|
||||
assert.ok(!pointInPolygon([-180, 0]));
|
||||
},
|
||||
"inside": function(pointInPolygon) {
|
||||
assert.ok(pointInPolygon([1, 1]));
|
||||
}
|
||||
},
|
||||
"South pole": {
|
||||
topic: function(pointInPolygon) {
|
||||
return pointInPolygon([
|
||||
[[-60, -80], [60, -80], [180, -80], [-60, -80]]
|
||||
]);
|
||||
},
|
||||
"outside": function(pointInPolygon) {
|
||||
assert.ok(!pointInPolygon([0, 0]));
|
||||
},
|
||||
"inside": function(pointInPolygon) {
|
||||
assert.ok(pointInPolygon([0, -85]));
|
||||
}
|
||||
},
|
||||
"North pole": {
|
||||
topic: function(pointInPolygon) {
|
||||
return pointInPolygon([
|
||||
[[60, 80], [-60, 80], [-180, 80], [60, 80]]
|
||||
]);
|
||||
},
|
||||
"outside": function(pointInPolygon) {
|
||||
assert.ok(!pointInPolygon([0, 0]));
|
||||
},
|
||||
"inside": function(pointInPolygon) {
|
||||
assert.ok(pointInPolygon([0, 85]));
|
||||
}
|
||||
},
|
||||
"larger than hemisphere": {
|
||||
"near [0°, 0°]": {
|
||||
topic: function(pointInPolygon) {
|
||||
return pointInPolygon([
|
||||
[[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]
|
||||
]);
|
||||
},
|
||||
"outside": function(pointInPolygon) {
|
||||
assert.ok(!pointInPolygon([.1, .1]));
|
||||
},
|
||||
"inside": function(pointInPolygon) {
|
||||
assert.ok(pointInPolygon([2, .1]));
|
||||
}
|
||||
},
|
||||
"South pole": {
|
||||
topic: function(pointInPolygon) {
|
||||
return pointInPolygon([
|
||||
[[-60, 80], [60, 80], [180, 80], [-60, 80]]
|
||||
]);
|
||||
},
|
||||
"outside": function(pointInPolygon) {
|
||||
assert.ok(!pointInPolygon([0, 85]));
|
||||
},
|
||||
"inside": function(pointInPolygon) {
|
||||
assert.ok(pointInPolygon([0, 0]));
|
||||
}
|
||||
},
|
||||
"North pole": {
|
||||
topic: function(pointInPolygon) {
|
||||
return pointInPolygon([
|
||||
[[60, -80], [-60, -80], [-180, -80], [60, -80]]
|
||||
]);
|
||||
},
|
||||
"outside": function(pointInPolygon) {
|
||||
assert.ok(!pointInPolygon([0, -85]));
|
||||
},
|
||||
"inside": function(pointInPolygon) {
|
||||
assert.ok(pointInPolygon([0, 0]));
|
||||
}
|
||||
},
|
||||
"circle with radius 120°": {
|
||||
topic: function(pointInPolygon) {
|
||||
return pointInPolygon(_.geo.circle().angle(120)().coordinates);
|
||||
},
|
||||
"outside": function(pointInPolygon) {
|
||||
assert.ok(!pointInPolygon([-180, 0]));
|
||||
},
|
||||
"inside": function(pointInPolygon) {
|
||||
assert.ok(pointInPolygon([-90, 0]));
|
||||
}
|
||||
},
|
||||
"narrow strip hole, length 340°": {
|
||||
topic: function(pointInPolygon) {
|
||||
return pointInPolygon([
|
||||
[[-170, -1], [0, -1], [170, -1], [170, 1], [0, 1], [-170, 1], [-170, -1]]
|
||||
]);
|
||||
},
|
||||
"outside": function(pointInPolygon) {
|
||||
assert.ok(!pointInPolygon([0, 0]));
|
||||
},
|
||||
"inside": function(pointInPolygon) {
|
||||
assert.ok(pointInPolygon([0, 20]));
|
||||
}
|
||||
}
|
||||
},
|
||||
"ring": {
|
||||
"near [0°, 0°]": {
|
||||
topic: function(pointInPolygon) {
|
||||
return pointInPolygon([
|
||||
[[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]],
|
||||
[[.4, .4], [.6, .4], [.6, .6], [.4, .6], [.4, .4]]
|
||||
]);
|
||||
},
|
||||
"outside": function(pointInPolygon) {
|
||||
assert.ok(!pointInPolygon([.5, .5]));
|
||||
},
|
||||
"inside": function(pointInPolygon) {
|
||||
assert.ok(pointInPolygon([.1, .5]));
|
||||
}
|
||||
},
|
||||
"equatorial": {
|
||||
topic: function(pointInPolygon) {
|
||||
return pointInPolygon([
|
||||
[[0, -10], [-120, -10], [120, -10], [0, -10]],
|
||||
[[0, 10], [120, 10], [-120, 10], [0, 10]]
|
||||
]);
|
||||
},
|
||||
"outside": function(pointInPolygon) {
|
||||
assert.ok(!pointInPolygon([0, 20]));
|
||||
},
|
||||
"inside": function(pointInPolygon) {
|
||||
assert.ok(pointInPolygon([0, 0]));
|
||||
}
|
||||
},
|
||||
"excluding both poles": {
|
||||
topic: function(pointInPolygon) {
|
||||
return pointInPolygon([
|
||||
[[10, 10], [-10, 10], [-10, -10], [10, -10], [10, 10]].reverse(),
|
||||
[[170, 10], [170, -10], [-170, -10], [-170, 10], [170, 10]].reverse()
|
||||
]);
|
||||
},
|
||||
"outside": function(pointInPolygon) {
|
||||
assert.ok(!pointInPolygon([0, 90]));
|
||||
},
|
||||
"inside": function(pointInPolygon) {
|
||||
assert.ok(pointInPolygon([0, 0]));
|
||||
}
|
||||
},
|
||||
"containing both poles": {
|
||||
topic: function(pointInPolygon) {
|
||||
return pointInPolygon([
|
||||
[[10, 10], [-10, 10], [-10, -10], [10, -10], [10, 10]],
|
||||
[[170, 10], [170, -10], [-170, -10], [-170, 10], [170, 10]]
|
||||
]);
|
||||
},
|
||||
"outside": function(pointInPolygon) {
|
||||
assert.ok(!pointInPolygon([0, 0]));
|
||||
},
|
||||
"inside": function(pointInPolygon) {
|
||||
assert.ok(pointInPolygon([0, 20]));
|
||||
}
|
||||
},
|
||||
"containing the South pole": {
|
||||
topic: function(pointInPolygon) {
|
||||
return pointInPolygon([
|
||||
[[10, 10], [-10, 10], [-10, -10], [10, -10], [10, 10]],
|
||||
[[0, 80], [120, 80], [-120, 80], [0, 80]]
|
||||
]);
|
||||
},
|
||||
"outside": function(pointInPolygon) {
|
||||
assert.ok(!pointInPolygon([0, 90]));
|
||||
},
|
||||
"inside": function(pointInPolygon) {
|
||||
assert.ok(pointInPolygon([0, -90]));
|
||||
}
|
||||
},
|
||||
"containing the North pole": {
|
||||
topic: function(pointInPolygon) {
|
||||
return pointInPolygon([
|
||||
[[10, 10], [-10, 10], [-10, -10], [10, -10], [10, 10]].reverse(),
|
||||
[[0, 80], [120, 80], [-120, 80], [0, 80]].reverse()
|
||||
]);
|
||||
},
|
||||
"inside": function(pointInPolygon) {
|
||||
assert.ok(pointInPolygon([0, 90]));
|
||||
},
|
||||
"outside": function(pointInPolygon) {
|
||||
assert.ok(!pointInPolygon([0, -90]));
|
||||
}
|
||||
}
|
||||
},
|
||||
"self-intersecting": {
|
||||
"near [0°, 0°]": {
|
||||
topic: function(pointInPolygon) {
|
||||
return pointInPolygon([
|
||||
[[0, 0], [1, 0], [1, 3], [3, 3], [3, 1], [0, 1], [0, 0]]
|
||||
]);
|
||||
},
|
||||
"inside": {
|
||||
"counter-clockwise region": function(pointInPolygon) {
|
||||
assert.ok(pointInPolygon([.5, .5]));
|
||||
},
|
||||
"clockwise region": function(pointInPolygon) {
|
||||
assert.ok(pointInPolygon([2, 2]));
|
||||
}
|
||||
},
|
||||
"outside": {
|
||||
"counter-clockwise region": function(pointInPolygon) {
|
||||
assert.ok(!pointInPolygon([15, .5]));
|
||||
},
|
||||
"clockwise region": function(pointInPolygon) {
|
||||
assert.ok(!pointInPolygon([12, 2]));
|
||||
}
|
||||
}
|
||||
},
|
||||
"near the South pole": {
|
||||
topic: function(pointInPolygon) {
|
||||
return pointInPolygon([
|
||||
[[-10, -80], [120, -80], [-120, -80], [10, -85], [10, -75], [-10, -75], [-10, -80]]
|
||||
]);
|
||||
},
|
||||
"inside": {
|
||||
"counter-clockwise region": function(pointInPolygon) {
|
||||
assert.ok(pointInPolygon([0, -76]));
|
||||
},
|
||||
"clockwise region": function(pointInPolygon) {
|
||||
assert.ok(pointInPolygon([0, -89]));
|
||||
}
|
||||
},
|
||||
"outside": function(pointInPolygon) {
|
||||
assert.ok(!pointInPolygon([0, 0]));
|
||||
}
|
||||
},
|
||||
"near the North pole": {
|
||||
topic: function(pointInPolygon) {
|
||||
return pointInPolygon([
|
||||
[[-10, 80], [-10, 75], [10, 75], [10, 85], [-120, 80], [120, 80], [-10, 80]]
|
||||
]);
|
||||
},
|
||||
"inside": {
|
||||
"clockwise region": function(pointInPolygon) {
|
||||
assert.ok(pointInPolygon([0, 76]));
|
||||
},
|
||||
"counter-clockwise region": function(pointInPolygon) {
|
||||
assert.ok(pointInPolygon([0, 89]));
|
||||
}
|
||||
},
|
||||
"outside": function(pointInPolygon) {
|
||||
assert.ok(!pointInPolygon([0, 0]));
|
||||
}
|
||||
}
|
||||
},
|
||||
"touching a pole": {
|
||||
"hemisphere touching the South pole": {
|
||||
topic: function(pointInPolygon) {
|
||||
return pointInPolygon(_.geo.circle().angle(90)().coordinates);
|
||||
},
|
||||
"origin is inside": function(pointInPolygon) {
|
||||
assert.ok(pointInPolygon([0, 0]));
|
||||
}
|
||||
},
|
||||
"triangle touching the South pole": {
|
||||
topic: function(pointInPolygon) {
|
||||
return pointInPolygon([[[180, -90], [-45, 0], [45, 0], [180, -90]]]);
|
||||
},
|
||||
"inside": function(pointInPolygon) {
|
||||
assert.ok(pointInPolygon([-44, 0]));
|
||||
assert.ok(pointInPolygon([0, 0]));
|
||||
assert.ok(pointInPolygon([0, -30]));
|
||||
assert.ok(pointInPolygon([30, -80]));
|
||||
},
|
||||
"outside": function(pointInPolygon) {
|
||||
assert.ok(!pointInPolygon([-46, 0]));
|
||||
assert.ok(!pointInPolygon([0, 1]));
|
||||
assert.ok(!pointInPolygon([-90, -80]));
|
||||
}
|
||||
},
|
||||
"triangle touching the South pole (2)": {
|
||||
topic: function(pointInPolygon) {
|
||||
return pointInPolygon([[[-45, 0], [45, 0], [180, -90], [-45, 0]]]);
|
||||
},
|
||||
"inside": function(pointInPolygon) {
|
||||
assert.ok(pointInPolygon([-44, 0]));
|
||||
assert.ok(pointInPolygon([0, 0]));
|
||||
assert.ok(pointInPolygon([0, -30]));
|
||||
assert.ok(pointInPolygon([30, -80]));
|
||||
},
|
||||
"outside": function(pointInPolygon) {
|
||||
assert.ok(!pointInPolygon([-46, 0]));
|
||||
assert.ok(!pointInPolygon([0, 1]));
|
||||
assert.ok(!pointInPolygon([-90, -80]));
|
||||
}
|
||||
},
|
||||
"triangle touching the South pole (3)": {
|
||||
topic: function(pointInPolygon) {
|
||||
return pointInPolygon([[[180, -90], [-135, 0], [135, 0], [180, -90]]]);
|
||||
},
|
||||
"inside": function(pointInPolygon) {
|
||||
assert.ok(pointInPolygon([180, 0]));
|
||||
assert.ok(pointInPolygon([150, 0]));
|
||||
assert.ok(pointInPolygon([180, -30]));
|
||||
assert.ok(pointInPolygon([150, -80]));
|
||||
},
|
||||
"outside": function(pointInPolygon) {
|
||||
assert.ok(!pointInPolygon([0, 0]));
|
||||
assert.ok(!pointInPolygon([180, 1]));
|
||||
assert.ok(!pointInPolygon([-90, -80]));
|
||||
}
|
||||
},
|
||||
"triangle touching the North pole": {
|
||||
topic: function(pointInPolygon) {
|
||||
return pointInPolygon([[[180, 90], [45, 0], [-45, 0], [180, 90]]]);
|
||||
},
|
||||
"inside": function(pointInPolygon) {
|
||||
assert.ok(pointInPolygon([-44, 10]));
|
||||
assert.ok(pointInPolygon([0, 10]));
|
||||
assert.ok(pointInPolygon([30, 80]));
|
||||
},
|
||||
"outside": function(pointInPolygon) {
|
||||
assert.ok(!pointInPolygon([-90, 0]));
|
||||
assert.ok(!pointInPolygon([0, -1]));
|
||||
assert.ok(!pointInPolygon([0, -80]));
|
||||
assert.ok(!pointInPolygon([-90, 1]));
|
||||
assert.ok(!pointInPolygon([-90, 80]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
suite.export(module);
|
|
@ -14,6 +14,9 @@ suite.addBatch({
|
|||
"has no defined size": function(q) {
|
||||
assert.isNull(q.size());
|
||||
},
|
||||
"has no defined extent": function(q) {
|
||||
assert.isNull(q.extent());
|
||||
},
|
||||
"has the default x-accessor, d[0]": function(q) {
|
||||
assert.strictEqual(q.x()([42, 43]), 42);
|
||||
},
|
||||
|
|
|
@ -12,16 +12,40 @@ suite.addBatch({
|
|||
topic: function(voronoi) {
|
||||
return voronoi();
|
||||
},
|
||||
"has no defined clip extent": function(v) {
|
||||
assert.isNull(v.clipExtent());
|
||||
},
|
||||
"has no defined size": function(v) {
|
||||
assert.isNull(v.size());
|
||||
},
|
||||
"returns the configured clip extent": function(v) {
|
||||
try {
|
||||
assert.deepEqual(v.clipExtent([[1, 2], [3, 4]]).clipExtent(), [[1, 2], [3, 4]]);
|
||||
} finally {
|
||||
v.clipExtent(null);
|
||||
}
|
||||
},
|
||||
"returns the configured size": function(v) {
|
||||
try {
|
||||
assert.deepEqual(v.size([100, 100]).size(), [100, 100]);
|
||||
assert.deepEqual(v.size([1, 2]).size(), [1, 2]);
|
||||
} finally {
|
||||
v.size(null);
|
||||
}
|
||||
},
|
||||
"size implies a clip extent from [0, 0]": function(v) {
|
||||
try {
|
||||
assert.deepEqual(v.size([1, 2]).clipExtent(), [[0, 0], [1, 2]]);
|
||||
} finally {
|
||||
v.size(null);
|
||||
}
|
||||
},
|
||||
"clip extent implies a size, assuming [0, 0]": function(v) {
|
||||
try {
|
||||
assert.deepEqual(v.clipExtent([[1, 2], [3, 4]]).size(), [3, 4]);
|
||||
} finally {
|
||||
v.clipExtent(null);
|
||||
}
|
||||
},
|
||||
"has the default x-accessor, d[0]": function(v) {
|
||||
assert.strictEqual(v.x()([42, 43]), 42);
|
||||
},
|
||||
|
@ -113,15 +137,32 @@ suite.addBatch({
|
|||
[[480, -1e6], [480, 1e6], [1e6, -1e6], [1e6, 1e6]]
|
||||
], 1e-6);
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
topic: function(v) {
|
||||
return v.y(function(d) { return d.y; });
|
||||
},
|
||||
"for two points": function(v) {
|
||||
assert.deepEqual(v.links([{x: 200, y: 200}, {x: 760, y: 300}]), [
|
||||
{source: {x: 200, y: 200}, target: {x: 760, y: 300}}
|
||||
]);
|
||||
},
|
||||
"for three points": function(v) {
|
||||
assert.deepEqual(v.links([{x: 200, y: 200}, {x: 500, y: 250}, {x: 760, y: 300}]), [
|
||||
{source: {x: 200, y: 200}, target: {x: 760, y: 300}},
|
||||
{source: {x: 500, y: 250}, target: {x: 760, y: 300}},
|
||||
{source: {x: 200, y: 200}, target: {x: 500, y: 250}}
|
||||
]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"a voronoi layout with size 960x500": {
|
||||
"a voronoi layout with clip extent [[0, 0], [960, 500]]": {
|
||||
topic: function(voronoi) {
|
||||
return voronoi()
|
||||
.x(function(d) { return d.x; })
|
||||
.y(function(d) { return d.y; })
|
||||
.size([960, 500]);
|
||||
.clipExtent([[0, 0], [960, 500]]);
|
||||
},
|
||||
"of two points": {
|
||||
topic: function(v) {
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
var vows = require("vows"),
|
||||
load = require("../load"),
|
||||
assert = require("../assert");
|
||||
|
||||
var suite = vows.describe("d3.layout.chord");
|
||||
|
||||
suite.addBatch({
|
||||
"chord": {
|
||||
topic: load("layout/chord").expression("d3.layout.chord"),
|
||||
"of a simple matrix": {
|
||||
topic: function(chord) {
|
||||
return chord()
|
||||
.padding(.05)
|
||||
.sortSubgroups(function(a, b) { return b - a; })
|
||||
.matrix([
|
||||
[11975, 5871, 8916, 2868],
|
||||
[ 1951, 10048, 2060, 6171],
|
||||
[ 8010, 16145, 8090, 8045],
|
||||
[ 1013, 990, 940, 6907]
|
||||
]);
|
||||
},
|
||||
"computes chord groups": function(chord) {
|
||||
var groups = chord.groups();
|
||||
assert.equal(groups.length, 4);
|
||||
|
||||
assert.equal(groups[0].index, 0);
|
||||
assert.inDelta(groups[0].startAngle, 0.0000000000000000, 1e-6);
|
||||
assert.inDelta(groups[0].endAngle, 1.8024478065173115, 1e-6);
|
||||
assert.inDelta(groups[0].value, 29630, 1e-6);
|
||||
|
||||
assert.equal(groups[1].index, 1);
|
||||
assert.inDelta(groups[1].startAngle, 1.8524478065173116, 1e-6);
|
||||
assert.inDelta(groups[1].endAngle, 3.0830761941597418, 1e-6);
|
||||
assert.inDelta(groups[1].value, 20230, 1e-6);
|
||||
|
||||
assert.equal(groups[2].index, 2);
|
||||
assert.inDelta(groups[2].startAngle, 3.1330761941597416, 1e-6);
|
||||
assert.inDelta(groups[2].endAngle, 5.583991554422396, 1e-6);
|
||||
assert.inDelta(groups[2].value, 40290, 1e-6);
|
||||
|
||||
assert.equal(groups[3].index, 3);
|
||||
assert.inDelta(groups[3].startAngle, 5.6339915544223960, 1e-6);
|
||||
assert.inDelta(groups[3].endAngle, 6.233185307179585, 1e-6);
|
||||
assert.inDelta(groups[3].value, 9850, 1e-6);
|
||||
},
|
||||
"computes chords": function(chord) {
|
||||
var chords = chord.chords();
|
||||
assert.equal(chords.length, 10);
|
||||
|
||||
assert.equal(chords[0].source.index, 0);
|
||||
assert.equal(chords[0].source.subindex, 0);
|
||||
assert.inDelta(chords[0].source.startAngle, 0, 1e-6);
|
||||
assert.inDelta(chords[0].source.endAngle, 0.7284614405347555, 1e-6);
|
||||
assert.equal(chords[0].source.value, 11975);
|
||||
assert.equal(chords[0].target.index, 0);
|
||||
assert.equal(chords[0].target.subindex, 0);
|
||||
assert.inDelta(chords[0].target.startAngle, 0, 1e-6);
|
||||
assert.inDelta(chords[0].target.endAngle, 0.7284614405347555, 1e-6);
|
||||
assert.equal(chords[0].target.value, 11975);
|
||||
|
||||
assert.equal(chords[1].source.index, 0);
|
||||
assert.equal(chords[1].source.subindex, 1);
|
||||
assert.inDelta(chords[1].source.startAngle, 1.2708382425228875, 1e-6);
|
||||
assert.inDelta(chords[1].source.endAngle, 1.6279820519074009, 1e-6);
|
||||
assert.equal(chords[1].source.value, 5871);
|
||||
assert.equal(chords[1].target.index, 1);
|
||||
assert.equal(chords[1].target.subindex, 0);
|
||||
assert.inDelta(chords[1].target.startAngle, 2.964393248816668, 1e-6);
|
||||
assert.inDelta(chords[1].target.endAngle, 3.0830761941597418, 1e-6);
|
||||
assert.equal(chords[1].target.value, 1951);
|
||||
|
||||
assert.equal(chords[2].source.index, 0);
|
||||
assert.equal(chords[2].source.subindex, 2);
|
||||
assert.inDelta(chords[2].source.startAngle, 0.7284614405347555, 1e-6);
|
||||
assert.inDelta(chords[2].source.endAngle, 1.2708382425228875, 1e-6);
|
||||
assert.equal(chords[2].source.value, 8916);
|
||||
assert.equal(chords[2].target.index, 2);
|
||||
assert.equal(chords[2].target.subindex, 0);
|
||||
assert.inDelta(chords[2].target.startAngle, 5.0967284113173115, 1e-6);
|
||||
assert.inDelta(chords[2].target.endAngle, 5.583991554422396, 1e-6);
|
||||
assert.equal(chords[2].target.value, 8010);
|
||||
|
||||
assert.equal(chords[3].source.index, 0);
|
||||
assert.equal(chords[3].source.subindex, 3);
|
||||
assert.inDelta(chords[3].source.startAngle, 1.6279820519074009, 1e-6);
|
||||
assert.inDelta(chords[3].source.endAngle, 1.8024478065173115, 1e-6);
|
||||
assert.equal(chords[3].source.value, 2868);
|
||||
assert.equal(chords[3].target.index, 3);
|
||||
assert.equal(chords[3].target.subindex, 0);
|
||||
assert.inDelta(chords[3].target.startAngle, 6.05415716358929, 1e-6);
|
||||
assert.inDelta(chords[3].target.endAngle, 6.115779830751019, 1e-6);
|
||||
assert.equal(chords[3].target.value, 1013);
|
||||
|
||||
assert.equal(chords[4].source.index, 1);
|
||||
assert.equal(chords[4].source.subindex, 1);
|
||||
assert.inDelta(chords[4].source.startAngle, 1.8524478065173116, 1e-6);
|
||||
assert.inDelta(chords[4].source.endAngle, 2.4636862661827164, 1e-6);
|
||||
assert.equal(chords[4].source.value, 10048);
|
||||
assert.equal(chords[4].target.index, 1);
|
||||
assert.equal(chords[4].target.subindex, 1);
|
||||
assert.inDelta(chords[4].target.startAngle, 1.8524478065173116, 1e-6);
|
||||
assert.inDelta(chords[4].target.endAngle, 2.4636862661827164, 1e-6);
|
||||
assert.equal(chords[4].target.value, 10048);
|
||||
|
||||
assert.equal(chords[5].source.index, 2);
|
||||
assert.equal(chords[5].source.subindex, 1);
|
||||
assert.inDelta(chords[5].source.startAngle, 3.1330761941597416, 1e-6);
|
||||
assert.inDelta(chords[5].source.endAngle, 4.1152064620038855, 1e-6);
|
||||
assert.equal(chords[5].source.value, 16145);
|
||||
assert.equal(chords[5].target.index, 1);
|
||||
assert.equal(chords[5].target.subindex, 2);
|
||||
assert.inDelta(chords[5].target.startAngle, 2.8390796314887687, 1e-6);
|
||||
assert.inDelta(chords[5].target.endAngle, 2.964393248816668, 1e-6);
|
||||
assert.equal(chords[5].target.value, 2060);
|
||||
|
||||
assert.equal(chords[6].source.index, 1);
|
||||
assert.equal(chords[6].source.subindex, 3);
|
||||
assert.inDelta(chords[6].source.startAngle, 2.4636862661827164, 1e-6);
|
||||
assert.inDelta(chords[6].source.endAngle, 2.8390796314887687, 1e-6);
|
||||
assert.equal(chords[6].source.value, 6171);
|
||||
assert.equal(chords[6].target.index, 3);
|
||||
assert.equal(chords[6].target.subindex, 1);
|
||||
assert.inDelta(chords[6].target.startAngle, 6.115779830751019, 1e-6);
|
||||
assert.inDelta(chords[6].target.endAngle, 6.176003365292097, 1e-6);
|
||||
assert.equal(chords[6].target.value, 990);
|
||||
|
||||
assert.equal(chords[7].source.index, 2);
|
||||
assert.equal(chords[7].source.subindex, 2);
|
||||
assert.inDelta(chords[7].source.startAngle, 4.1152064620038855, 1e-6);
|
||||
assert.inDelta(chords[7].source.endAngle, 4.607336153354714, 1e-6);
|
||||
assert.equal(chords[7].source.value, 8090);
|
||||
assert.equal(chords[7].target.index, 2);
|
||||
assert.equal(chords[7].target.subindex, 2);
|
||||
assert.inDelta(chords[7].target.startAngle, 4.1152064620038855, 1e-6);
|
||||
assert.inDelta(chords[7].target.endAngle, 4.607336153354714, 1e-6);
|
||||
assert.equal(chords[7].target.value, 8090);
|
||||
|
||||
assert.equal(chords[8].source.index, 2);
|
||||
assert.equal(chords[8].source.subindex, 3);
|
||||
assert.inDelta(chords[8].source.startAngle, 4.607336153354714, 1e-6);
|
||||
assert.inDelta(chords[8].source.endAngle, 5.0967284113173115, 1e-6);
|
||||
assert.equal(chords[8].source.value, 8045);
|
||||
assert.equal(chords[8].target.index, 3);
|
||||
assert.equal(chords[8].target.subindex, 2);
|
||||
assert.inDelta(chords[8].target.startAngle, 6.176003365292097, 1e-6);
|
||||
assert.inDelta(chords[8].target.endAngle, 6.233185307179585, 1e-6);
|
||||
assert.equal(chords[8].target.value, 940);
|
||||
|
||||
assert.equal(chords[9].source.index, 3);
|
||||
assert.equal(chords[9].source.subindex, 3);
|
||||
assert.inDelta(chords[9].source.startAngle, 5.633991554422396, 1e-6);
|
||||
assert.inDelta(chords[9].source.endAngle, 6.05415716358929, 1e-6);
|
||||
assert.equal(chords[9].source.value, 6907);
|
||||
assert.equal(chords[9].target.index, 3);
|
||||
assert.equal(chords[9].target.subindex, 3);
|
||||
assert.inDelta(chords[9].target.startAngle, 5.633991554422396, 1e-6);
|
||||
assert.inDelta(chords[9].target.endAngle, 6.05415716358929, 1e-6);
|
||||
assert.equal(chords[9].target.value, 6907);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
suite.export(module);
|
|
@ -77,6 +77,16 @@ suite.addBatch({
|
|||
assert.isFalse(result.slice(1).some(function(d, i) {
|
||||
return d.x === result[i].x && d.y === result[i].y && d.value > 0;
|
||||
}));
|
||||
},
|
||||
"radius defaults to automatic scaling": function(pack) {
|
||||
assert.equal(pack().radius(), null);
|
||||
},
|
||||
"radius can be specified using a custom function of value": function(pack) {
|
||||
var p = pack().radius(function(value) { return Math.sqrt(value) * 10; });
|
||||
assert.deepEqual(p.nodes({children: [{value: 1}]}).map(layout), [
|
||||
{value: 1, depth: 0, x: 0.5, y: 0.5, r: 10},
|
||||
{value: 1, depth: 1, x: 0.5, y: 0.5, r: 10}
|
||||
]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -220,6 +220,14 @@ suite.addBatch({
|
|||
assert.deepEqual(x.domain(), [1, 1, 2, 3, 11]);
|
||||
var x = d3.scale.linear().domain([123.1, 1, 2, 3, -.9]).nice();
|
||||
assert.deepEqual(x.domain(), [130, 1, 2, 3, -10]);
|
||||
},
|
||||
"accepts a tick count to control nicing step": function(d3) {
|
||||
var x = d3.scale.linear().domain([12, 87]).nice(5);
|
||||
assert.deepEqual(x.domain(), [0, 100]);
|
||||
var x = d3.scale.linear().domain([12, 87]).nice(10);
|
||||
assert.deepEqual(x.domain(), [10, 90]);
|
||||
var x = d3.scale.linear().domain([12, 87]).nice(100);
|
||||
assert.deepEqual(x.domain(), [12, 87]);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -214,6 +214,15 @@ suite.addBatch({
|
|||
"+100", "+200", "+300", "", "", "", "", "", "",
|
||||
"+1,000"
|
||||
]);
|
||||
},
|
||||
"can override the tick format as string": function(d3) {
|
||||
var x = d3.scale.log().domain([1000.1, 1]);
|
||||
assert.deepEqual(x.ticks().map(x.tickFormat(10, ".1s")), [
|
||||
"1", "2", "3", "", "", "", "", "", "",
|
||||
"10", "20", "30", "", "", "", "", "", "",
|
||||
"100", "200", "300", "", "", "", "", "", "",
|
||||
"1k"
|
||||
]);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -193,6 +193,14 @@ suite.addBatch({
|
|||
assert.deepEqual(x.domain(), [1, 1, 2, 3, 11]);
|
||||
var x = d3.scale.pow().domain([123.1, 1, 2, 3, -.9]).nice();
|
||||
assert.deepEqual(x.domain(), [130, 1, 2, 3, -10]);
|
||||
},
|
||||
"accepts a tick count to control nicing step": function(d3) {
|
||||
var x = d3.scale.pow().domain([12, 87]).nice(5);
|
||||
assert.deepEqual(x.domain(), [0, 100]);
|
||||
var x = d3.scale.pow().domain([12, 87]).nice(10);
|
||||
assert.deepEqual(x.domain(), [10, 90]);
|
||||
var x = d3.scale.pow().domain([12, 87]).nice(100);
|
||||
assert.deepEqual(x.domain(), [12, 87]);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -59,6 +59,32 @@ suite.addBatch({
|
|||
assert.equal(x(.6), b);
|
||||
assert.equal(x(.8), c);
|
||||
assert.equal(x(1), c);
|
||||
},
|
||||
"invertExtent": {
|
||||
"maps a value in the range to a domain extent": function(quantize) {
|
||||
var x = quantize().range([0, 1, 2, 3]);
|
||||
assert.deepEqual(x.invertExtent(0), [0, .25]);
|
||||
assert.deepEqual(x.invertExtent(1), [.25, .5]);
|
||||
assert.deepEqual(x.invertExtent(2), [.5, .75]);
|
||||
assert.deepEqual(x.invertExtent(3), [.75, 1]);
|
||||
},
|
||||
"allows arbitrary range values": function(quantize) {
|
||||
var a = {}, b = {}, x = quantize().range([a, b]);
|
||||
assert.deepEqual(x.invertExtent(a), [0, .5]);
|
||||
assert.deepEqual(x.invertExtent(b), [.5, 1]);
|
||||
},
|
||||
"returns [NaN, NaN] when the given value is not in the range": function(quantize) {
|
||||
var x = quantize();
|
||||
assert.ok(x.invertExtent(-1).every(isNaN));
|
||||
assert.ok(x.invertExtent(.5).every(isNaN));
|
||||
assert.ok(x.invertExtent(2).every(isNaN));
|
||||
assert.ok(x.invertExtent('a').every(isNaN));
|
||||
},
|
||||
"returns the first match if duplicate values exist in the range": function(quantize) {
|
||||
var x = quantize().range([0, 1, 2, 0]);
|
||||
assert.deepEqual(x.invertExtent(0), [0, .25]);
|
||||
assert.deepEqual(x.invertExtent(1), [.25, .5]);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -42,6 +42,14 @@ suite.addBatch({
|
|||
assert.equal(x(.6), b);
|
||||
assert.equal(x(.8), c);
|
||||
assert.equal(x(1), c);
|
||||
},
|
||||
"invertExtent": {
|
||||
"returns the domain extent for the specified range value": function(threshold) {
|
||||
var a = {}, b = {}, c = {}, x = threshold().domain([1/3, 2/3]).range([a, b, c]);
|
||||
assert.deepEqual(x.invertExtent(a), [undefined, 1/3]);
|
||||
assert.deepEqual(x.invertExtent(b), [1/3, 2/3]);
|
||||
assert.deepEqual(x.invertExtent(c), [2/3, undefined]);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
var vows = require("vows"),
|
||||
load = require("../load"),
|
||||
assert = require("../assert");
|
||||
|
||||
var suite = vows.describe("selection.size");
|
||||
|
||||
suite.addBatch({
|
||||
"select(body)": {
|
||||
topic: load("selection/size").document(),
|
||||
"on a simple page": {
|
||||
topic: function(d3) {
|
||||
return d3.select("body");
|
||||
},
|
||||
"returns zero for empty selections": function(body) {
|
||||
assert.equal(body.select("foo").size(), 0);
|
||||
},
|
||||
"returns one for a singleton selection": function(body) {
|
||||
assert.equal(body.size(), 1);
|
||||
},
|
||||
"does not count null nodes": function(body) {
|
||||
body[0][0] = null;
|
||||
assert.equal(body.size(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
suite.addBatch({
|
||||
"selectAll(div)": {
|
||||
topic: load("selection/node").document(),
|
||||
"on a simple page": {
|
||||
topic: function(d3) {
|
||||
var body = d3.select("body");
|
||||
body.append("div").append("span");
|
||||
body.append("div");
|
||||
return body.selectAll("div");
|
||||
},
|
||||
"returns null for empty selections": function(div) {
|
||||
assert.equal(div.select("foo").size(), 0);
|
||||
},
|
||||
"returns the number of non-null nodes": function(div) {
|
||||
assert.equal(div.size(), 2);
|
||||
},
|
||||
"does not count null nodes": function(div) {
|
||||
div[0][0] = null;
|
||||
assert.equal(div.size(), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
suite.export(module);
|
|
@ -21,6 +21,35 @@ suite.addBatch({
|
|||
}
|
||||
},
|
||||
|
||||
"clamp": {
|
||||
"returns a single boolean if only x is defined": function(brush) {
|
||||
var b = brush().x(_.scale.linear());
|
||||
assert.isTrue(b.clamp());
|
||||
},
|
||||
"returns a single boolean if only y is defined": function(brush) {
|
||||
var b = brush().y(_.scale.linear());
|
||||
assert.isTrue(b.clamp());
|
||||
},
|
||||
"returns one-dimensional array if both x and y are defined": function(brush) {
|
||||
var b = brush().x(_.scale.linear()).y(_.scale.linear());
|
||||
assert.deepEqual(b.clamp(), [true, true]);
|
||||
},
|
||||
"takes a single boolean if only x is defined": function(brush) {
|
||||
var b = brush().x(_.scale.linear()).clamp(false);
|
||||
assert.isFalse(b.clamp());
|
||||
},
|
||||
"takes a single boolean if only y is defined": function(brush) {
|
||||
var b = brush().y(_.scale.linear()).clamp(false);
|
||||
assert.isFalse(b.clamp());
|
||||
},
|
||||
"takes a one-dimensional array if both x and y are defined": function(brush) {
|
||||
var b = brush().x(_.scale.linear()).y(_.scale.linear()).clamp([false, true]);
|
||||
assert.deepEqual(b.clamp(), [false, true]);
|
||||
b.clamp([true, false]);
|
||||
assert.deepEqual(b.clamp(), [true, false]);
|
||||
}
|
||||
},
|
||||
|
||||
"extent": {
|
||||
"returns null when no scales are attached": function(brush) {
|
||||
assert.isNull(brush().extent());
|
||||
|
|
|
@ -93,6 +93,12 @@ suite.addBatch({
|
|||
assert.pathEqual(l([[0, 0], [1, 1]]), "M0,0V1H1");
|
||||
assert.pathEqual(l([[0, 0], [1, 1], [2, 0]]), "M0,0V1H1V0H2");
|
||||
},
|
||||
"supports step interpolation": function(line) {
|
||||
var l = line().interpolate("step");
|
||||
assert.pathEqual(l([[0, 0]]), "M0,0");
|
||||
assert.pathEqual(l([[0, 0], [1, 1]]), "M0,0H0.5V1H1");
|
||||
assert.pathEqual(l([[0, 0], [1, 1], [2, 0]]), "M0,0H0.5V1H1.5V0H2");
|
||||
},
|
||||
"supports step-after interpolation": function(line) {
|
||||
var l = line().interpolate("step-after");
|
||||
assert.pathEqual(l([[0, 0]]), "M0,0");
|
||||
|
|
|
@ -142,6 +142,7 @@ suite.addBatch({
|
|||
var f = format("%y");
|
||||
assert.equal(f(local(1990, 0, 1)), "90");
|
||||
assert.equal(f(local(2002, 0, 1)), "02");
|
||||
assert.equal(f(local(-2, 0, 1)), "-02");
|
||||
},
|
||||
"formats zero-padded four-digit year": function(format) {
|
||||
var f = format("%Y");
|
||||
|
@ -149,6 +150,7 @@ suite.addBatch({
|
|||
assert.equal(f(local(1990, 0, 1)), "1990");
|
||||
assert.equal(f(local(2002, 0, 1)), "2002");
|
||||
assert.equal(f(local(10002, 0, 1)), "0002");
|
||||
assert.equal(f(local(-2, 0, 1)), "-0002");
|
||||
},
|
||||
"formats time zone": function(format) {
|
||||
var f = format("%Z");
|
||||
|
@ -172,6 +174,48 @@ suite.addBatch({
|
|||
assert.deepEqual(p("Wednesday 02/03/1991"), local(1991, 1, 3));
|
||||
assert.isNull(p("Caturday 03/10/2010"));
|
||||
},
|
||||
"parses abbreviated weekday, week number (Sunday) and year": function(format) {
|
||||
var p = format("%a %U %Y").parse;
|
||||
assert.deepEqual(p("Mon 00 1990"), local(1990, 0, 1));
|
||||
assert.deepEqual(p("Sun 05 1991"), local(1991, 1, 3));
|
||||
assert.deepEqual(p("Sun 01 1995"), local(1995, 0, 1));
|
||||
assert.isNull(p("XXX 03 2010"));
|
||||
},
|
||||
"parses weekday, week number (Sunday) and year": function(format) {
|
||||
var p = format("%A %U %Y").parse;
|
||||
assert.deepEqual(p("Monday 00 1990"), local(1990, 0, 1));
|
||||
assert.deepEqual(p("Sunday 05 1991"), local(1991, 1, 3));
|
||||
assert.deepEqual(p("Sunday 01 1995"), local(1995, 0, 1));
|
||||
assert.isNull(p("Caturday 03 2010"));
|
||||
},
|
||||
"parses numeric weekday, week number (Sunday) and year": function(format) {
|
||||
var p = format("%w %U %Y").parse;
|
||||
assert.deepEqual(p("1 00 1990"), local(1990, 0, 1));
|
||||
assert.deepEqual(p("0 05 1991"), local(1991, 1, 3));
|
||||
assert.deepEqual(p("0 01 1995"), local(1995, 0, 1));
|
||||
assert.isNull(p("X 03 2010"));
|
||||
},
|
||||
"parses abbreviated weekday, week number (Monday) and year": function(format) {
|
||||
var p = format("%a %W %Y").parse;
|
||||
assert.deepEqual(p("Mon 01 1990"), local(1990, 0, 1));
|
||||
assert.deepEqual(p("Sun 04 1991"), local(1991, 1, 3));
|
||||
assert.deepEqual(p("Sun 00 1995"), local(1995, 0, 1));
|
||||
assert.isNull(p("XXX 03 2010"));
|
||||
},
|
||||
"parses weekday, week number (Monday) and year": function(format) {
|
||||
var p = format("%A %W %Y").parse;
|
||||
assert.deepEqual(p("Monday 01 1990"), local(1990, 0, 1));
|
||||
assert.deepEqual(p("Sunday 04 1991"), local(1991, 1, 3));
|
||||
assert.deepEqual(p("Sunday 00 1995"), local(1995, 0, 1));
|
||||
assert.isNull(p("Caturday 03 2010"));
|
||||
},
|
||||
"parses numeric weekday, week number (Monday) and year": function(format) {
|
||||
var p = format("%w %W %Y").parse;
|
||||
assert.deepEqual(p("1 01 1990"), local(1990, 0, 1));
|
||||
assert.deepEqual(p("0 04 1991"), local(1991, 1, 3));
|
||||
assert.deepEqual(p("0 00 1995"), local(1995, 0, 1));
|
||||
assert.isNull(p("X 03 2010"));
|
||||
},
|
||||
"parses numeric date": function(format) {
|
||||
var p = format("%m/%d/%y").parse;
|
||||
assert.deepEqual(p("01/01/90"), local(1990, 0, 1));
|
||||
|
@ -196,6 +240,12 @@ suite.addBatch({
|
|||
assert.deepEqual(p("February 2, 2010"), local(2010, 1, 2));
|
||||
assert.isNull(p("jan 1, 1990"));
|
||||
},
|
||||
"parses day of year and numeric date": function(format) {
|
||||
var p = format("%j %m/%d/%Y").parse;
|
||||
assert.deepEqual(p("001 01/01/1990"), local(1990, 0, 1));
|
||||
assert.deepEqual(p("034 02/03/1991"), local(1991, 1, 3));
|
||||
assert.isNull(p("2012 03/10/2010"));
|
||||
},
|
||||
"parses locale date and time": function(format) {
|
||||
var p = format("%c").parse;
|
||||
assert.deepEqual(p("Mon Jan 1 00:00:00 1990"), local(1990, 0, 1));
|
||||
|
@ -228,6 +278,12 @@ suite.addBatch({
|
|||
assert.deepEqual(p("12:00:01 pm"), local(1900, 0, 1, 12, 0, 1));
|
||||
assert.deepEqual(p("11:59:59 PM"), local(1900, 0, 1, 23, 59, 59));
|
||||
},
|
||||
"parses literal %": function(format) {
|
||||
var p = format("%% %m/%d/%Y").parse;
|
||||
assert.deepEqual(p("% 01/01/1990"), local(1990, 0, 1));
|
||||
assert.deepEqual(p("% 02/03/1991"), local(1991, 1, 3));
|
||||
assert.isNull(p("%% 03/10/2010"));
|
||||
},
|
||||
"doesn't crash when given weird strings": function(format) {
|
||||
try {
|
||||
Object.prototype.foo = 10;
|
||||
|
|
Загрузка…
Ссылка в новой задаче