Restrict implicit domains to explicit ranges.

When an ordinal scale’s range is explicitly defined as an array of values, we
can build the domain implicitly by progressively assigning values from the
range; this is commonly done with color palettes, for example.

However, when an ordinal scale’s range is rather implied by chopping a
continuous range into a series of points or bands, then the domain must be
specified explicitly: for the scale to be consistent, we need to know the
cardinality of the domain to compute the implied range values.

Thus, it only makes sense to extend the domain implicitly when the range is
specified explicitly. Fixes #1536 #1535.
This commit is contained in:
Mike Bostock 2013-09-21 10:31:07 -07:00
Родитель 9ca071eee7
Коммит 113032bc97
4 изменённых файлов: 61 добавлений и 28 удалений

2
d3.js поставляемый
Просмотреть файл

@ -7082,7 +7082,7 @@ d3 = function() {
function d3_scale_ordinal(domain, ranger) {
var index, range, rangeBand;
function scale(x) {
return range[((index.get(x) || index.set(x, domain.push(x))) - 1) % range.length];
return range[((index.get(x) || ranger.t === "range" && index.set(x, domain.push(x))) - 1) % range.length];
}
function steps(start, step) {
return d3.range(domain.length).map(function(i) {

2
d3.min.js поставляемый

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -12,7 +12,7 @@ function d3_scale_ordinal(domain, ranger) {
rangeBand;
function scale(x) {
return range[((index.get(x) || index.set(x, domain.push(x))) - 1) % range.length];
return range[((index.get(x) || ranger.t === "range" && index.set(x, domain.push(x))) - 1) % range.length];
}
function steps(start, step) {

Просмотреть файл

@ -12,15 +12,6 @@ suite.addBatch({
"defaults to the empty array": function(ordinal) {
assert.isEmpty(ordinal().domain());
},
"new input values are added to the domain": function(ordinal) {
var x = ordinal().range(["foo", "bar"]);
assert.equal(x(0), "foo");
assert.deepEqual(x.domain(), ["0"]);
assert.equal(x(1), "bar");
assert.deepEqual(x.domain(), ["0", "1"]);
assert.equal(x(0), "foo");
assert.deepEqual(x.domain(), ["0", "1"]);
},
"setting the domain forgets previous values": function(ordinal) {
var x = ordinal().range(["foo", "bar"]);
assert.equal(x(1), "foo");
@ -37,22 +28,6 @@ suite.addBatch({
assert.equal(x({toString: function() { return "foo"; }}), 42);
assert.equal(x({toString: function() { return "bar"; }}), 43);
},
"orders domain values by the order in which they are seen": function(ordinal) {
var x = ordinal();
x("foo");
x("bar");
x("baz");
assert.deepEqual(x.domain(), ["foo", "bar", "baz"]);
x.domain(["baz", "bar"]);
x("foo");
assert.deepEqual(x.domain(), ["baz", "bar", "foo"]);
x.domain(["baz", "foo"]);
assert.deepEqual(x.domain(), ["baz", "foo"]);
x.domain([]);
x("foo");
x("bar");
assert.deepEqual(x.domain(), ["foo", "bar"]);
},
"does not coerce domain values to strings": function(ordinal) {
var x = ordinal().domain([0, 1]);
assert.deepEqual(x.domain(), [0, 1]);
@ -73,6 +48,31 @@ suite.addBatch({
assert.isEmpty(x.range());
assert.isUndefined(x(0));
},
"new input values are added to the domain": function(ordinal) {
var x = ordinal().range(["foo", "bar"]);
assert.equal(x(0), "foo");
assert.deepEqual(x.domain(), ["0"]);
assert.equal(x(1), "bar");
assert.deepEqual(x.domain(), ["0", "1"]);
assert.equal(x(0), "foo");
assert.deepEqual(x.domain(), ["0", "1"]);
},
"orders domain values by the order in which they are seen": function(ordinal) {
var x = ordinal();
x("foo");
x("bar");
x("baz");
assert.deepEqual(x.domain(), ["foo", "bar", "baz"]);
x.domain(["baz", "bar"]);
x("foo");
assert.deepEqual(x.domain(), ["baz", "bar", "foo"]);
x.domain(["baz", "foo"]);
assert.deepEqual(x.domain(), ["baz", "foo"]);
x.domain([]);
x("foo");
x("bar");
assert.deepEqual(x.domain(), ["foo", "bar"]);
},
"setting the range remembers previous values": function(ordinal) {
var x = ordinal();
assert.isUndefined(x(0));
@ -141,6 +141,17 @@ suite.addBatch({
assert.equal(x.rangeBand(), 0);
var x = ordinal().domain(["a", "b", "c"]).rangePoints([120, 0], 2);
assert.equal(x.rangeBand(), 0);
},
"returns undefined for values outside the domain": function(ordinal) {
var x = ordinal().domain(["a", "b", "c"]).rangePoints([0, 1]);
assert.isUndefined(x("d"));
assert.isUndefined(x("e"));
assert.isUndefined(x("f"));
},
"does not implicitly add values to the domain": function(ordinal) {
var x = ordinal().domain(["a", "b", "c"]).rangePoints([0, 1]);
x("d"), x("e");
assert.deepEqual(x.domain(), ["a", "b", "c"]);
}
},
@ -176,6 +187,17 @@ suite.addBatch({
var x = ordinal().domain(["a", "b", "c"]).rangeBands([120, 0], .2, 1);
assert.deepEqual(x.range(), [75, 50, 25]);
assert.equal(x.rangeBand(), 20);
},
"returns undefined for values outside the domain": function(ordinal) {
var x = ordinal().domain(["a", "b", "c"]).rangeBands([0, 1]);
assert.isUndefined(x("d"));
assert.isUndefined(x("e"));
assert.isUndefined(x("f"));
},
"does not implicitly add values to the domain": function(ordinal) {
var x = ordinal().domain(["a", "b", "c"]).rangeBands([0, 1]);
x("d"), x("e");
assert.deepEqual(x.domain(), ["a", "b", "c"]);
}
},
@ -203,6 +225,17 @@ suite.addBatch({
var x = ordinal().domain(["a", "b", "c"]).rangeRoundBands([120, 0], .2, 1);
assert.deepEqual(x.range(), [75, 50, 25]);
assert.equal(x.rangeBand(), 20);
},
"returns undefined for values outside the domain": function(ordinal) {
var x = ordinal().domain(["a", "b", "c"]).rangeRoundBands([0, 1]);
assert.isUndefined(x("d"));
assert.isUndefined(x("e"));
assert.isUndefined(x("f"));
},
"does not implicitly add values to the domain": function(ordinal) {
var x = ordinal().domain(["a", "b", "c"]).rangeRoundBands([0, 1]);
x("d"), x("e");
assert.deepEqual(x.domain(), ["a", "b", "c"]);
}
},