Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linear nice(n) domain does not always match ticks(n). #9

Closed
mbostock opened this issue Oct 12, 2015 · 10 comments
Closed

Linear nice(n) domain does not always match ticks(n). #9

mbostock opened this issue Oct 12, 2015 · 10 comments
Assignees

Comments

@mbostock
Copy link
Member

A comment by @moshevds from d3/d3#2580:

Compare this sentence from https://github.com/mbostock/d3/wiki/Quantitative-Scales#linear_nice:

The optional tick count argument allows greater control over the step size used to extend the bounds, guaranteeing that the returned ticks will exactly cover the domain.

With this result from the current implementation:

var scale = d3.scale.linear().domain([-0.1, 51.1]).nice(8);
scale.domain(); // => [ -5, 55 ]
scale.ticks(8); // => [ 0, 10, 20, 30, 40, 50 ]

In most cases I do get the result I expected, for example:

var scale = d3.scale.linear().domain([6.3, 74.2]).nice(8);
scale.domain(); // => [ 0, 80 ]
scale.ticks(8); // => [ 0, 10, 20, 30, 40, 50, 60, 70, 80 ]

I looked at d3_scale_linearTickRange. The problem apparently stems from the step-size heuristic and its dependency on the values that nice is changing. I don't feel comfortable proposing a completely different heuristic, perhaps you would consider a patch to calculate the heuristic twice?

For comparison:

var scale = d3.scale.linear().domain([-0.1, 51.1]).nice(8).nice(8);
scale.domain(); // => [ -10, 60 ]
scale.ticks(8); // => [ -10, 0, 10, 20, 30, 40, 50, 60 ]
@mbostock
Copy link
Member Author

Right. The problem is that the tick step is calculated based on the domain, and then the domain is extended, which can change the tick step (for the same number of ticks). An iterative solution is one possibility, but I need to think about it a bit more…

@ollyhayes
Copy link

Any thoughts on this?

@mbostock
Copy link
Member Author

It should be fixed.

@mbostock mbostock self-assigned this Dec 16, 2015
@mbostock
Copy link
Member Author

I think computing the tick step twice should be sufficient, so I’ve done that.

@mbostock mbostock added bug and removed bug labels Dec 16, 2015
@ollyhayes
Copy link

Is this fix going to be back-ported to the old mbostock/d3 version at all? Should I open an issue there?

@mbostock
Copy link
Member Author

I suppose I could, but the more time I spend on back-porting, the longer the release of D3 4.0 is delayed. And there are lots of significant improvements in D3 4.0 that are not possible to back-port (hence the major release). Also here the workaround is trivial, since you can just call nice twice:

d3.scale.linear().domain([-0.1, 51.1]).nice(8).nice(8).domain() // [-10, 60]

Yet by that argument the fix is also trivial, and I could have fixed it in about the same time I spent writing this response. But I felt obligated to make my point about my larger goal of releasing 4.0.

mbostock added a commit to d3/d3 that referenced this issue Dec 17, 2015
@chenkun24
Copy link

d3.scaleLinear().domain([0,5]).nice(6).domain()
> [0, 5]
d3.scaleLinear().domain([0,5.1]).nice(6).domain()
> [0, 6]
d3.scaleLinear().domain([0,5.1]).nice(7).domain()
> [0, 6]

It happens to like this in D3 V4.0?

I expect:

d3.scaleLinear().domain([0,5]).nice(6).domain()
> [0, 6]
d3.scaleLinear().domain([0,5.1]).nice(6).domain()
> [0, 6]
d3.scaleLinear().domain([0,5.1]).nice(7).domain()
> [0, 7]

@mbostock
Copy link
Member Author

@chenkun24 The behavior looks correct to me: the resulting domain after calling scale.nice(count) is consistent with scale.ticks(count):

var x = d3.scaleLinear().domain([0, 5]).nice(6);
x.domain(); // [0, 5]
x.ticks(6); // [0, 1, 2, 3, 4, 5]

var x = d3.scaleLinear().domain([0, 5.1]).nice(6);
x.domain(); // [0, 6]
x.ticks(6); // [0, 1, 2, 3, 4, 5, 6]

var x = d3.scaleLinear().domain([0, 5.1]).nice(7);
x.domain(); // [0, 6]
x.ticks(7); // [0, 1, 2, 3, 4, 5, 6]

@chenkun24
Copy link

Is there any way to extend the domain and tick exactly as I want?
For example:

var x = d3.scaleLinear().domain([0,5]);
... // do something to x
x.ticks(7); // [0, 1, 2, 3, 4, 5, 6]

... // do something to x
x.ticks(10); //  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

@mbostock
Copy link
Member Author

If you mean have scale.ticks(count) return exactly count ticks, no; the count is only a hint. I’m sure it’d be possible for you to implement your own variant of scale.nice that achieved the desired result, but that’s not how d3-scale is designed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

3 participants