Skip to content

Commit

Permalink
Support nice(count) and nice(interval, skip).
Browse files Browse the repository at this point in the history
Fixes d3#1475.
  • Loading branch information
mbostock committed Aug 22, 2013
1 parent 762f984 commit cc4a1d0
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 21 deletions.
26 changes: 20 additions & 6 deletions d3.js
Expand Up @@ -8855,15 +8855,29 @@ d3 = function() {
return d / 31536e6;
}), count)[2] ] : !i ? [ d3_time_scaleMilliseconds, d3_scale_linearTickRange(extent, count)[2] ] : methods[target / d3_time_scaleSteps[i - 1] < d3_time_scaleSteps[i] / target ? i - 1 : i];
}
scale.nice = function(interval) {
var domain = scale.domain(), extent = d3_scaleExtent(domain), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" ? tickMethod(extent, interval) : [ interval ];
return scale.domain(d3_scale_nice(domain, method[0]));
scale.nice = function(interval, skip) {
var domain = scale.domain(), extent = d3_scaleExtent(domain), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" && tickMethod(extent, interval);
if (method) interval = method[0], skip = method[1];
function skipped(date) {
return !interval.range(date, d3_time_scaleDate(+date + 1), skip).length;
}
return scale.domain(d3_scale_nice(domain, skip > 1 ? {
floor: function(date) {
while (skipped(date = interval.floor(date))) date = d3_time_scaleDate(date - 1);
return date;
},
ceil: function(date) {
while (skipped(date = interval.ceil(date))) date = d3_time_scaleDate(+date + 1);
return date;
}
} : interval));
};
scale.ticks = function(interval, skip) {
var extent = d3_scaleExtent(scale.domain()), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" ? tickMethod(extent, interval) : !interval.range ? [ {
var extent = d3_scaleExtent(scale.domain()), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" ? tickMethod(extent, interval) : !interval.range && [ {
range: interval
}, skip ] : [ interval, skip ];
return method[0].range(extent[0], new Date(+extent[1] + 1), method[1]);
}, skip ];
if (method) interval = method[0], skip = method[1];
return interval.range(extent[0], d3_time_scaleDate(+extent[1] + 1), skip);
};
scale.tickFormat = function() {
return format;
Expand Down
10 changes: 5 additions & 5 deletions d3.min.js

Large diffs are not rendered by default.

31 changes: 24 additions & 7 deletions src/time/scale.js
Expand Up @@ -39,22 +39,39 @@ function d3_time_scale(linear, methods, format) {
: methods[target / d3_time_scaleSteps[i - 1] < d3_time_scaleSteps[i] / target ? i - 1 : i];
}

scale.nice = function(interval) { // TODO skip
scale.nice = function(interval, skip) {
var domain = scale.domain(),
extent = d3_scaleExtent(domain),
method = interval == null ? tickMethod(extent, 10)
: typeof interval === "number" ? tickMethod(extent, interval)
: [interval];
return scale.domain(d3_scale_nice(domain, method[0]));
: typeof interval === "number" && tickMethod(extent, interval);

if (method) interval = method[0], skip = method[1];

function skipped(date) {
return !interval.range(date, d3_time_scaleDate(+date + 1), skip).length;
}

return scale.domain(d3_scale_nice(domain, skip > 1 ? {
floor: function(date) {
while (skipped(date = interval.floor(date))) date = d3_time_scaleDate(date - 1);
return date;
},
ceil: function(date) {
while (skipped(date = interval.ceil(date))) date = d3_time_scaleDate(+date + 1);
return date;
}
} : interval));
};

scale.ticks = function(interval, skip) {
var extent = d3_scaleExtent(scale.domain()),
method = interval == null ? tickMethod(extent, 10)
: typeof interval === "number" ? tickMethod(extent, interval)
: !interval.range ? [{range: interval}, skip] // assume deprecated range function
: [interval, skip];
return method[0].range(extent[0], new Date(+extent[1] + 1), method[1]); // inclusive upper bound
: !interval.range && [{range: interval}, skip]; // assume deprecated range function

if (method) interval = method[0], skip = method[1];

return interval.range(extent[0], d3_time_scaleDate(+extent[1] + 1), skip); // inclusive upper bound
};

scale.tickFormat = function() {
Expand Down
92 changes: 89 additions & 3 deletions test/time/scale-test.js
Expand Up @@ -20,6 +20,22 @@ suite.addBatch({
assert.deepEqual(x.nice(_.time.month).domain(), [local(2008, 11, 1), local(2009, 1, 1)]);
assert.deepEqual(x.nice(_.time.year).domain(), [local(2008, 0, 1), local(2010, 0, 1)]);
},
"rounds using the specified time interval and skip": function(scale) {
var x = scale().domain([local(2009, 0, 1, 0, 12), local(2009, 0, 1, 23, 48)]);
assert.deepEqual(x.nice(_.time.day, 3).domain(), [local(2009, 0, 1), local(2009, 0, 4)]);
assert.deepEqual(x.nice(_.time.week, 2).domain(), [local(2008, 11, 21), local(2009, 0, 4)]);
assert.deepEqual(x.nice(_.time.month, 3).domain(), [local(2008, 9, 1), local(2009, 3, 1)]);
assert.deepEqual(x.nice(_.time.year, 10).domain(), [local(2000, 0, 1), local(2010, 0, 1)]);
},
"rounds using the specified count": function(scale) {
var x = scale().domain([local(2009, 0, 1, 0, 17), local(2009, 0, 1, 23, 42)]);
assert.deepEqual(x.nice(100).domain(), [local(2009, 0, 1, 0, 15), local(2009, 0, 1, 23, 45)]);
assert.deepEqual(x.nice(10).domain(), [local(2009, 0, 1), local(2009, 0, 2)]);
},
"rounds with a default count of ten if no arguments": function(scale) {
var x = scale().domain([local(2009, 0, 1, 0, 17), local(2009, 0, 1, 23, 42)]);
assert.deepEqual(x.nice().domain(), [local(2009, 0, 1), local(2009, 0, 2)]);
},
"works on degenerate domains": function(scale) {
var x = scale().domain([local(2009, 0, 1, 0, 12), local(2009, 0, 1, 0, 12)]);
assert.deepEqual(x.nice(_.time.day).domain(), [local(2009, 0, 1), local(2009, 0, 2)]);
Expand Down Expand Up @@ -80,14 +96,32 @@ suite.addBatch({
"ticks": {
"observes explicit tick interval": function(scale) {
var x = scale().domain([local(2011, 0, 1, 12, 1, 0), local(2011, 0, 1, 12, 4, 4)]);
assert.deepEqual(x.ticks(_.time.minutes), [
assert.deepEqual(x.ticks(_.time.minute), [
local(2011, 0, 1, 12, 1),
local(2011, 0, 1, 12, 2),
local(2011, 0, 1, 12, 3),
local(2011, 0, 1, 12, 4)
]);
},
"observes explicit tick interval and step": function(scale) {
var x = scale().domain([local(2011, 0, 1, 12, 0, 0), local(2011, 0, 1, 12, 33, 4)]);
assert.deepEqual(x.ticks(_.time.minute, 10), [
local(2011, 0, 1, 12, 0),
local(2011, 0, 1, 12, 10),
local(2011, 0, 1, 12, 20),
local(2011, 0, 1, 12, 30)
]);
},
"(deprecated) observes explicit tick range": function(scale) {
var x = scale().domain([local(2011, 0, 1, 12, 1, 0), local(2011, 0, 1, 12, 4, 4)]);
assert.deepEqual(x.ticks(_.time.minutes), [
local(2011, 0, 1, 12, 1),
local(2011, 0, 1, 12, 2),
local(2011, 0, 1, 12, 3),
local(2011, 0, 1, 12, 4)
]);
},
"(deprecated) observes explicit tick range and step": function(scale) {
var x = scale().domain([local(2011, 0, 1, 12, 0, 0), local(2011, 0, 1, 12, 33, 4)]);
assert.deepEqual(x.ticks(_.time.minutes, 10), [
local(2011, 0, 1, 12, 0),
Expand Down Expand Up @@ -329,10 +363,44 @@ suite.addBatch({
"scale.utc": {
topic: load("time/scale-utc").expression("d3.time.scale.utc").document(),

"nice": {
"rounds using the specified time interval": function(scale) {
var x = scale().domain([utc(2009, 0, 1, 0, 12), utc(2009, 0, 1, 23, 48)]);
assert.deepEqual(x.nice(_.time.day.utc).domain(), [utc(2009, 0, 1), utc(2009, 0, 2)]);
assert.deepEqual(x.nice(_.time.week.utc).domain(), [utc(2008, 11, 28), utc(2009, 0, 4)]);
assert.deepEqual(x.nice(_.time.month.utc).domain(), [utc(2008, 11, 1), utc(2009, 1, 1)]);
assert.deepEqual(x.nice(_.time.year.utc).domain(), [utc(2008, 0, 1), utc(2010, 0, 1)]);
},
"rounds using the specified time interval and skip": function(scale) {
var x = scale().domain([utc(2009, 0, 1, 0, 12), utc(2009, 0, 1, 23, 48)]);
assert.deepEqual(x.nice(_.time.day.utc, 3).domain(), [utc(2009, 0, 1), utc(2009, 0, 4)]);
assert.deepEqual(x.nice(_.time.week.utc, 2).domain(), [utc(2008, 11, 21), utc(2009, 0, 4)]);
assert.deepEqual(x.nice(_.time.month.utc, 3).domain(), [utc(2008, 9, 1), utc(2009, 3, 1)]);
assert.deepEqual(x.nice(_.time.year.utc, 10).domain(), [utc(2000, 0, 1), utc(2010, 0, 1)]);
},
"rounds using the specified count": function(scale) {
var x = scale().domain([utc(2009, 0, 1, 0, 17), utc(2009, 0, 1, 23, 42)]);
assert.deepEqual(x.nice(100).domain(), [utc(2009, 0, 1, 0, 15), utc(2009, 0, 1, 23, 45)]);
assert.deepEqual(x.nice(10).domain(), [utc(2009, 0, 1), utc(2009, 0, 2)]);
},
"rounds with a default count of ten if no arguments": function(scale) {
var x = scale().domain([utc(2009, 0, 1, 0, 17), utc(2009, 0, 1, 23, 42)]);
assert.deepEqual(x.nice().domain(), [utc(2009, 0, 1), utc(2009, 0, 2)]);
},
"works on degenerate domains": function(scale) {
var x = scale().domain([utc(2009, 0, 1, 0, 12), utc(2009, 0, 1, 0, 12)]);
assert.deepEqual(x.nice(_.time.day.utc).domain(), [utc(2009, 0, 1), utc(2009, 0, 2)]);
},
"nicing a polylinear domain only affects the extent": function(linear) {
var x = linear().domain([utc(2009, 0, 1, 0, 12), utc(2009, 0, 1, 23, 48), utc(2009, 0, 2, 23, 48)]).nice(_.time.day.utc);
assert.deepEqual(x.domain(), [utc(2009, 0, 1), utc(2009, 0, 1, 23, 48), utc(2009, 0, 3)]);
}
},

"ticks": {
"observes explicit tick interval": function(scale) {
var x = scale().domain([utc(2011, 0, 1, 12, 1, 0), utc(2011, 0, 1, 12, 4, 4)]);
assert.deepEqual(x.ticks(_.time.minutes), [
assert.deepEqual(x.ticks(_.time.minute.utc), [
utc(2011, 0, 1, 12, 1),
utc(2011, 0, 1, 12, 2),
utc(2011, 0, 1, 12, 3),
Expand All @@ -341,7 +409,25 @@ suite.addBatch({
},
"observes explicit tick interval and step": function(scale) {
var x = scale().domain([utc(2011, 0, 1, 12, 0, 0), utc(2011, 0, 1, 12, 33, 4)]);
assert.deepEqual(x.ticks(_.time.minutes, 10), [
assert.deepEqual(x.ticks(_.time.minute.utc, 10), [
utc(2011, 0, 1, 12, 0),
utc(2011, 0, 1, 12, 10),
utc(2011, 0, 1, 12, 20),
utc(2011, 0, 1, 12, 30)
]);
},
"(deprecated) observes explicit tick range": function(scale) {
var x = scale().domain([utc(2011, 0, 1, 12, 1, 0), utc(2011, 0, 1, 12, 4, 4)]);
assert.deepEqual(x.ticks(_.time.minutes.utc), [
utc(2011, 0, 1, 12, 1),
utc(2011, 0, 1, 12, 2),
utc(2011, 0, 1, 12, 3),
utc(2011, 0, 1, 12, 4)
]);
},
"(deprecated) observes explicit tick range and step": function(scale) {
var x = scale().domain([utc(2011, 0, 1, 12, 0, 0), utc(2011, 0, 1, 12, 33, 4)]);
assert.deepEqual(x.ticks(_.time.minutes.utc, 10), [
utc(2011, 0, 1, 12, 0),
utc(2011, 0, 1, 12, 10),
utc(2011, 0, 1, 12, 20),
Expand Down

0 comments on commit cc4a1d0

Please sign in to comment.