Skip to content

Commit f69a0ed

Browse files
committed
fixes the calculation of tick values
1 parent abb75b1 commit f69a0ed

File tree

1 file changed

+125
-61
lines changed

1 file changed

+125
-61
lines changed

web-server/src/utils/array.ts

Lines changed: 125 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
secondsInDay,
44
secondsInHour,
55
secondsInMinute,
6+
secondsInMonth,
67
secondsInWeek
78
} from 'date-fns/constants';
89
import { last } from 'ramda';
@@ -25,85 +26,32 @@ export const generateArrayWithSteps = (n: number, s: number) => {
2526
}
2627
const generatedArray = Array.from(
2728
{ length: Math.floor(n / s) + 1 },
28-
(_, index) => Number((index * s).toFixed(0))
29+
(_, index) => roundDecimalPlaces(index * s)
2930
);
3031
const biggestElement = last(generatedArray);
3132
if (biggestElement < n) {
32-
generatedArray.push(Number((biggestElement + s).toFixed(0)));
33+
generatedArray.push(roundDecimalPlaces(biggestElement + s));
3334
}
3435
return generatedArray;
3536
};
3637

3738
export type TickArrayOptions = {
3839
wholeNumbers?: boolean;
3940
isTimeBased?: boolean;
41+
percentageBased?: boolean;
4042
};
4143

4244
export const createTickArray = (
4345
data: { x?: DatumValue; y: number }[],
4446
options: TickArrayOptions = {}
4547
) => {
46-
const n: number =
47-
data.reduce((acc, cur) => (acc > cur.y ? acc : cur.y), 0) + 1;
48+
const n: number = data.reduce((acc, cur) => (acc > cur.y ? acc : cur.y), 0);
4849
let step = 1;
4950

50-
if (options.isTimeBased && n > 10) {
51-
{
52-
if (n < 20) {
53-
step = 2;
54-
} else if (n < 30) {
55-
step = 5;
56-
} else if (n < secondsInMinute) {
57-
step = 10;
58-
} else if (n < 2 * secondsInMinute) {
59-
step = 15;
60-
} else if (n < 5 * secondsInMinute) {
61-
step = 30;
62-
} else if (n < 10 * secondsInMinute) {
63-
step = secondsInMinute;
64-
} else if (n < 20 * secondsInMinute) {
65-
step = 2 * secondsInMinute;
66-
} else if (n < 30 * secondsInMinute) {
67-
step = 5 * secondsInMinute;
68-
} else if (n < secondsInHour) {
69-
step = 10 * secondsInMinute;
70-
} else if (n < 2 * secondsInHour) {
71-
step = 15 * secondsInMinute;
72-
} else if (n < 5 * secondsInHour) {
73-
step = 30 * secondsInMinute;
74-
} else if (n < 10 * secondsInHour) {
75-
step = secondsInHour;
76-
} else if (n < 20 * secondsInHour) {
77-
step = 2 * secondsInHour;
78-
} else if (n < secondsInDay) {
79-
step = 4 * secondsInHour;
80-
} else if (n < 2 * secondsInDay) {
81-
step = 6 * secondsInHour;
82-
} else if (n < 3 * secondsInDay) {
83-
step = 8 * secondsInHour;
84-
} else if (n < secondsInWeek) {
85-
step = secondsInDay;
86-
} else if (n < 2 * secondsInWeek) {
87-
step = 2 * secondsInDay;
88-
} else if (n < 3 * secondsInWeek) {
89-
step = 3 * secondsInDay;
90-
} else {
91-
step = secondsInWeek;
92-
}
93-
return generateArrayWithSteps(n, step);
94-
}
95-
}
96-
const numberOfDigits = Math.round(n).toString().length;
97-
const order = Math.pow(10, numberOfDigits - 1);
98-
99-
const twoStep = order * 2; // steps of 2, 20, 200...
100-
const fiveStep = order * 5; // steps of 5, 50, 500...
101-
const tenStep = order * 10; // steps of 10, 100, 1000...
102-
if (n < twoStep) step = twoStep / 10;
103-
else if (n < fiveStep) step = fiveStep / 10;
104-
else if (n < tenStep) step = tenStep / 10;
105-
106-
if (options.wholeNumbers) step = Math.ceil(step);
51+
if (options.isTimeBased && n > 10) step = getTimeBasedStep(n);
52+
else if (options.percentageBased && n > 67) step = getStep(n, true);
53+
else if (n > 2 || options.wholeNumbers) step = getStep(n);
54+
else step = getSmallStep(n);
10755

10856
return generateArrayWithSteps(n, step);
10957
};
@@ -126,3 +74,119 @@ export const roundDecimalPlaces = (
12674
) => {
12775
return Number(value?.toFixed(roundingDigits));
12876
};
77+
78+
const getSmallStep = (n: number) => {
79+
const ballPark = n / 3; // get roughly around 3-5 steps
80+
const readableIntervals = [0.01, 0.05, 0.1, 0.2, 0.25, 0.5];
81+
readableIntervals.sort(
82+
(x, y) => Math.abs(ballPark - x) - Math.abs(ballPark - y)
83+
);
84+
return readableIntervals[0];
85+
};
86+
87+
const getStep = (n: number, percentageBased: boolean = false) => {
88+
const ballPark = n / 3; // get roughly around 3-5 steps
89+
let a, twos;
90+
a = twos = 2;
91+
92+
let b, fives;
93+
b = fives = 5;
94+
95+
let c, tens;
96+
c = tens = 10;
97+
98+
let d, twentyFives;
99+
d = twentyFives = 5;
100+
101+
while (a <= ballPark) {
102+
twos = a;
103+
a *= 10;
104+
}
105+
while (b <= ballPark) {
106+
fives = b;
107+
b *= 10;
108+
}
109+
while (c <= ballPark) {
110+
tens = c;
111+
c *= 10;
112+
}
113+
while (d <= ballPark) {
114+
twentyFives = d;
115+
d *= 5;
116+
}
117+
const readableIntervals = [
118+
1,
119+
a,
120+
b,
121+
c,
122+
twos,
123+
fives,
124+
tens,
125+
15,
126+
twentyFives,
127+
30,
128+
250
129+
].filter((val) => {
130+
if (!percentageBased) return true;
131+
if (n > 67) return 100 % val === 0;
132+
return true;
133+
});
134+
135+
readableIntervals.sort(
136+
(x, y) => Math.abs(ballPark - x) - Math.abs(ballPark - y)
137+
);
138+
139+
return readableIntervals[0];
140+
};
141+
142+
const readableTimeIntervals = [
143+
10,
144+
15,
145+
20,
146+
30,
147+
secondsInMinute,
148+
2 * secondsInMinute,
149+
5 * secondsInMinute,
150+
10 * secondsInMinute,
151+
15 * secondsInMinute,
152+
20 * secondsInMinute,
153+
30 * secondsInMinute,
154+
45 * secondsInMinute,
155+
secondsInHour,
156+
2 * secondsInHour,
157+
3 * secondsInHour,
158+
4 * secondsInHour,
159+
6 * secondsInHour,
160+
8 * secondsInHour,
161+
12 * secondsInHour,
162+
secondsInDay,
163+
2 * secondsInDay,
164+
3 * secondsInDay,
165+
4 * secondsInDay,
166+
5 * secondsInDay,
167+
6 * secondsInDay,
168+
7 * secondsInDay,
169+
secondsInWeek,
170+
2 * secondsInWeek,
171+
3 * secondsInWeek,
172+
secondsInMonth
173+
];
174+
175+
const getTimeBasedStep = (n: number) => {
176+
const ballPark = n / 3; // get roughly around 3-5 steps
177+
178+
let i = 0;
179+
let j = 1;
180+
181+
while (readableTimeIntervals[j] <= ballPark) {
182+
i = j;
183+
j++;
184+
}
185+
186+
const [a, b] = [
187+
Math.abs(ballPark - readableTimeIntervals[i]),
188+
Math.abs(ballPark - readableTimeIntervals[j])
189+
];
190+
191+
return a < b ? readableTimeIntervals[i] : readableTimeIntervals[j];
192+
};

0 commit comments

Comments
 (0)