Skip to content

Commit b6aeb01

Browse files
committed
Fix log scale tick generation
Add limit to major tick generation to stop at base extents. If log base 10 range is from 4 to 200, previously ticks overflowed from top and bottom to 1-1000. Create minor ticks within decade linearly spaced as before but if the extent is not within even log steps, stop generating at extent end. When major ticks are more than one decade apart, generate a minor tick for each decade up to the split number times.
1 parent c4e4c52 commit b6aeb01

File tree

2 files changed

+81
-0
lines changed

2 files changed

+81
-0
lines changed

src/scale/Log.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,15 @@ class LogScale extends IntervalScale<LogScaleSetting> {
7575

7676
const ticks = super.getTicks(expandToNicedExtent);
7777

78+
79+
// Ticks are created using the nice extent, but that can cause the first and last tick to be well outside the extent
80+
if (ticks[0].value < this._extent[0]) {
81+
ticks[0].value = this._extent[0];
82+
}
83+
if (ticks[ticks.length - 1].value > this._extent[1]) {
84+
ticks[ticks.length - 1].value = this._extent[1];
85+
}
86+
7887
return zrUtil.map(ticks, function (tick) {
7988
const val = tick.value;
8089
let powVal = mathPow(this.base, val);
@@ -93,6 +102,62 @@ class LogScale extends IntervalScale<LogScaleSetting> {
93102
}, this);
94103
}
95104

105+
/**
106+
* Get minor ticks for log scale. Ticks are generated based on a decade so that 5 splits
107+
* between 1 and 10 would be 2, 4, 6, 8 and
108+
* between 5 and 10 would be 6, 8.
109+
* @param splitNumber Get minor ticks number.
110+
* @returns Minor ticks.
111+
*/
112+
getMinorTicks(splitNumber: number): number[][] {
113+
const ticks = this.getTicks(true);
114+
const minorTicks = [];
115+
const negativeMultiplier = this._isNegative ? -1 : 1;
116+
117+
for (let i = 1; i < ticks.length; i++) {
118+
const nextTick = ticks[i];
119+
const prevTick = ticks[i - 1];
120+
const logNextTick = Math.ceil(scaleHelper.absMathLog(nextTick.value, this.base));
121+
const logPrevTick = Math.round(scaleHelper.absMathLog(prevTick.value, this.base));
122+
123+
const minorTicksGroup: number[] = [];
124+
const tickDiff = logNextTick - logPrevTick;
125+
const overDecade = tickDiff > 1;
126+
127+
if (overDecade) {
128+
// For spans over a decade, generate evenly spaced ticks in log space
129+
// For example, between 1 and 100, generate a tick at 10
130+
const step = Math.ceil(tickDiff / splitNumber);
131+
132+
let minorTickValue = Math.pow(this.base, logPrevTick + step);
133+
let j = 1;
134+
while (minorTickValue < nextTick.value) {
135+
minorTicksGroup.push(minorTickValue);
136+
137+
j++;
138+
minorTickValue = Math.pow(this.base, logPrevTick + j * step) * negativeMultiplier;
139+
}
140+
}
141+
else {
142+
// For spans within a decade, generate linear subdivisions
143+
// For example, between 1 and 10 with splitNumber=5, generate ticks at 2, 4, 6, 8
144+
const maxValue = Math.pow(this.base, logNextTick);
145+
146+
// Divide the space linearly between min and max
147+
const step = maxValue / splitNumber;
148+
let minorTickValue = step;
149+
while (minorTickValue < nextTick.value) {
150+
minorTicksGroup.push(minorTickValue * negativeMultiplier);
151+
minorTickValue += step;
152+
}
153+
}
154+
155+
minorTicks.push(minorTicksGroup);
156+
}
157+
158+
return minorTicks;
159+
}
160+
96161
setExtent(start: number, end: number): void {
97162
// Assume the start and end can be infinity
98163
// log(-Infinity) is NaN, so safe guard here

test/logScale.html

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)