abstract-chart 3.1.3 → 3.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -15
- package/LICENSE +21 -21
- package/README.md +106 -106
- package/lib/chart.d.ts.map +1 -1
- package/lib/chart.js +30 -30
- package/lib/chart.js.map +1 -1
- package/package.json +3 -3
- package/src/axis.ts +234 -234
- package/src/chart.ts +758 -1020
- package/src/index.ts +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "abstract-chart",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.7",
|
|
4
4
|
"description": "Drawing charts using multiple unit of measure axes as coordinate system",
|
|
5
5
|
"repository": "https://github.com/dividab/abstract-visuals/tree/master/packages/abstract-chart",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -15,12 +15,12 @@
|
|
|
15
15
|
"README.md"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"abstract-image": "^3.
|
|
18
|
+
"abstract-image": "^3.2.3",
|
|
19
19
|
"ramda": "^0.22.1",
|
|
20
20
|
"ts-exhaustive-check": "^1.0.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@types/ramda": "^0.25.29"
|
|
24
24
|
},
|
|
25
|
-
"gitHead": "
|
|
25
|
+
"gitHead": "81109d0f47e5d8c0539e47ff83372f6c4ab9182a"
|
|
26
26
|
}
|
package/src/axis.ts
CHANGED
|
@@ -1,234 +1,234 @@
|
|
|
1
|
-
import * as R from "ramda";
|
|
2
|
-
import * as AbstractImage from "abstract-image";
|
|
3
|
-
|
|
4
|
-
export type Axis = LinearAxis | LogarithmicAxis;
|
|
5
|
-
|
|
6
|
-
export interface LinearAxis {
|
|
7
|
-
readonly type: "linear";
|
|
8
|
-
readonly min: number;
|
|
9
|
-
readonly max: number;
|
|
10
|
-
readonly label: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function createLinearAxis(
|
|
14
|
-
min: number,
|
|
15
|
-
max: number,
|
|
16
|
-
label: string
|
|
17
|
-
): LinearAxis {
|
|
18
|
-
return {
|
|
19
|
-
type: "linear",
|
|
20
|
-
min: min,
|
|
21
|
-
max: max,
|
|
22
|
-
label: label
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface LogarithmicAxis {
|
|
27
|
-
readonly type: "logarithmic";
|
|
28
|
-
readonly min: number;
|
|
29
|
-
readonly max: number;
|
|
30
|
-
readonly label: string;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function createLogarithmicAxis(
|
|
34
|
-
min: number,
|
|
35
|
-
max: number,
|
|
36
|
-
label: string
|
|
37
|
-
): LogarithmicAxis {
|
|
38
|
-
return {
|
|
39
|
-
type: "logarithmic",
|
|
40
|
-
min: min,
|
|
41
|
-
max: max,
|
|
42
|
-
label: label
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const linearMultiples = [1, 2, 5];
|
|
47
|
-
const linearPowers = [-2, -1, 0, 1, 2, 3, 4, 5, 6];
|
|
48
|
-
|
|
49
|
-
export function getTicks(desiredTicks: number, axis: Axis): Array<number> {
|
|
50
|
-
switch (axis.type) {
|
|
51
|
-
case "linear":
|
|
52
|
-
return getLinearTicks(desiredTicks, axis.min, axis.max);
|
|
53
|
-
case "logarithmic":
|
|
54
|
-
return getLogarithmicTicks(desiredTicks, axis.min, axis.max);
|
|
55
|
-
default:
|
|
56
|
-
return [];
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
interface Alternative {
|
|
61
|
-
readonly min: number;
|
|
62
|
-
readonly step: number;
|
|
63
|
-
readonly ticks: number;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export function getLinearTicks(
|
|
67
|
-
desiredTicks: number,
|
|
68
|
-
min: number,
|
|
69
|
-
max: number
|
|
70
|
-
): Array<number> {
|
|
71
|
-
let best: Alternative | undefined;
|
|
72
|
-
for (const power of linearPowers) {
|
|
73
|
-
const base = Math.pow(10, power);
|
|
74
|
-
for (const multiple of linearMultiples) {
|
|
75
|
-
const step = base * multiple;
|
|
76
|
-
const cMin = Math.ceil(min / step);
|
|
77
|
-
const cMax = Math.floor(max / step);
|
|
78
|
-
const ticks = cMax - cMin + 1;
|
|
79
|
-
|
|
80
|
-
if (
|
|
81
|
-
!best ||
|
|
82
|
-
Math.abs(best.ticks - desiredTicks) > Math.abs(ticks - desiredTicks)
|
|
83
|
-
) {
|
|
84
|
-
best = {
|
|
85
|
-
min: cMin * step,
|
|
86
|
-
step: step,
|
|
87
|
-
ticks: ticks
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (!best) {
|
|
94
|
-
return [];
|
|
95
|
-
}
|
|
96
|
-
const b = best;
|
|
97
|
-
return R.range(0, b.ticks).map(l => b.min + b.step * l);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const logarithmicAlternatives = [
|
|
101
|
-
[0],
|
|
102
|
-
[0, 5],
|
|
103
|
-
[0, 1, 2, 5],
|
|
104
|
-
[0, 1, 2, 3, 5],
|
|
105
|
-
[0, 1, 2, 3, 5, 8],
|
|
106
|
-
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
107
|
-
];
|
|
108
|
-
|
|
109
|
-
export function getLogarithmicTicks(
|
|
110
|
-
desiredTicks: number,
|
|
111
|
-
min: number,
|
|
112
|
-
max: number
|
|
113
|
-
): Array<number> {
|
|
114
|
-
const minPow = Math.floor(Math.log10(min)) - 1;
|
|
115
|
-
const maxPow = Math.ceil(Math.log10(max)) + 1;
|
|
116
|
-
const powers = R.range(0, maxPow - minPow + 1).map(p => minPow + p);
|
|
117
|
-
const alternatives = logarithmicAlternatives.map(stepAlt => {
|
|
118
|
-
const altLines = powers.reduce((lines: Array<number>, power: number) => {
|
|
119
|
-
const base = Math.pow(10, power);
|
|
120
|
-
const powerLines = stepAlt.map(i => i * base);
|
|
121
|
-
return lines.concat(powerLines);
|
|
122
|
-
}, []);
|
|
123
|
-
return altLines.filter(l => l >= min && l <= max);
|
|
124
|
-
});
|
|
125
|
-
const bestLines = alternatives.reduce(
|
|
126
|
-
(prev, alt) =>
|
|
127
|
-
Math.abs(alt.length - desiredTicks) < Math.abs(prev.length - desiredTicks)
|
|
128
|
-
? alt
|
|
129
|
-
: prev
|
|
130
|
-
);
|
|
131
|
-
return bestLines;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
export function transformPoint(
|
|
135
|
-
point: AbstractImage.Point,
|
|
136
|
-
xMin: number,
|
|
137
|
-
xMax: number,
|
|
138
|
-
yMin: number,
|
|
139
|
-
yMax: number,
|
|
140
|
-
xAxis: Axis | undefined,
|
|
141
|
-
yAxis: Axis | undefined
|
|
142
|
-
): AbstractImage.Point {
|
|
143
|
-
const x = transformValue(point.x, xMin, xMax, xAxis);
|
|
144
|
-
const y = transformValue(point.y, yMin, yMax, yAxis);
|
|
145
|
-
return AbstractImage.createPoint(x, y);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
export function transformValue(
|
|
149
|
-
value: number,
|
|
150
|
-
min: number,
|
|
151
|
-
max: number,
|
|
152
|
-
axis: Axis | undefined
|
|
153
|
-
): number {
|
|
154
|
-
if (!axis) {
|
|
155
|
-
return value;
|
|
156
|
-
}
|
|
157
|
-
const range = max - min;
|
|
158
|
-
switch (axis.type) {
|
|
159
|
-
case "linear":
|
|
160
|
-
return min + range * linearTransform(value, axis.min, axis.max);
|
|
161
|
-
case "logarithmic":
|
|
162
|
-
return min + range * logarithmicTransform(value, axis.min, axis.max);
|
|
163
|
-
default:
|
|
164
|
-
return 0;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
export function inverseTransformValue(
|
|
169
|
-
value: number,
|
|
170
|
-
min: number,
|
|
171
|
-
max: number,
|
|
172
|
-
axis: Axis | undefined
|
|
173
|
-
): number {
|
|
174
|
-
if (!axis) {
|
|
175
|
-
return value;
|
|
176
|
-
}
|
|
177
|
-
const range = max - min;
|
|
178
|
-
switch (axis.type) {
|
|
179
|
-
case "linear":
|
|
180
|
-
return inverseLinearTransform((value - min) / range, axis.min, axis.max);
|
|
181
|
-
case "logarithmic":
|
|
182
|
-
return inverseLogarithmicTransform(
|
|
183
|
-
(value - min) / range,
|
|
184
|
-
axis.min,
|
|
185
|
-
axis.max
|
|
186
|
-
);
|
|
187
|
-
default:
|
|
188
|
-
return 0;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
export function linearTransform(
|
|
193
|
-
value: number,
|
|
194
|
-
min: number,
|
|
195
|
-
max: number
|
|
196
|
-
): number {
|
|
197
|
-
return (value - min) / (max - min);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
export function logarithmicTransform(
|
|
201
|
-
value: number,
|
|
202
|
-
min: number,
|
|
203
|
-
max: number
|
|
204
|
-
): number {
|
|
205
|
-
if (value > 0) {
|
|
206
|
-
return (
|
|
207
|
-
(Math.log10(value) - Math.log10(min)) /
|
|
208
|
-
(Math.log10(max) - Math.log10(min))
|
|
209
|
-
);
|
|
210
|
-
} else if (value < 0) {
|
|
211
|
-
return 0.0;
|
|
212
|
-
} else {
|
|
213
|
-
return 0.0;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
export function inverseLinearTransform(
|
|
218
|
-
value: number,
|
|
219
|
-
min: number,
|
|
220
|
-
max: number
|
|
221
|
-
): number {
|
|
222
|
-
return min + value * (max - min);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
export function inverseLogarithmicTransform(
|
|
226
|
-
value: number,
|
|
227
|
-
min: number,
|
|
228
|
-
max: number
|
|
229
|
-
): number {
|
|
230
|
-
return Math.pow(
|
|
231
|
-
10,
|
|
232
|
-
value * (Math.log10(max) - Math.log10(min)) + Math.log10(min)
|
|
233
|
-
);
|
|
234
|
-
}
|
|
1
|
+
import * as R from "ramda";
|
|
2
|
+
import * as AbstractImage from "abstract-image";
|
|
3
|
+
|
|
4
|
+
export type Axis = LinearAxis | LogarithmicAxis;
|
|
5
|
+
|
|
6
|
+
export interface LinearAxis {
|
|
7
|
+
readonly type: "linear";
|
|
8
|
+
readonly min: number;
|
|
9
|
+
readonly max: number;
|
|
10
|
+
readonly label: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function createLinearAxis(
|
|
14
|
+
min: number,
|
|
15
|
+
max: number,
|
|
16
|
+
label: string
|
|
17
|
+
): LinearAxis {
|
|
18
|
+
return {
|
|
19
|
+
type: "linear",
|
|
20
|
+
min: min,
|
|
21
|
+
max: max,
|
|
22
|
+
label: label
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface LogarithmicAxis {
|
|
27
|
+
readonly type: "logarithmic";
|
|
28
|
+
readonly min: number;
|
|
29
|
+
readonly max: number;
|
|
30
|
+
readonly label: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function createLogarithmicAxis(
|
|
34
|
+
min: number,
|
|
35
|
+
max: number,
|
|
36
|
+
label: string
|
|
37
|
+
): LogarithmicAxis {
|
|
38
|
+
return {
|
|
39
|
+
type: "logarithmic",
|
|
40
|
+
min: min,
|
|
41
|
+
max: max,
|
|
42
|
+
label: label
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const linearMultiples = [1, 2, 5];
|
|
47
|
+
const linearPowers = [-2, -1, 0, 1, 2, 3, 4, 5, 6];
|
|
48
|
+
|
|
49
|
+
export function getTicks(desiredTicks: number, axis: Axis): Array<number> {
|
|
50
|
+
switch (axis.type) {
|
|
51
|
+
case "linear":
|
|
52
|
+
return getLinearTicks(desiredTicks, axis.min, axis.max);
|
|
53
|
+
case "logarithmic":
|
|
54
|
+
return getLogarithmicTicks(desiredTicks, axis.min, axis.max);
|
|
55
|
+
default:
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface Alternative {
|
|
61
|
+
readonly min: number;
|
|
62
|
+
readonly step: number;
|
|
63
|
+
readonly ticks: number;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function getLinearTicks(
|
|
67
|
+
desiredTicks: number,
|
|
68
|
+
min: number,
|
|
69
|
+
max: number
|
|
70
|
+
): Array<number> {
|
|
71
|
+
let best: Alternative | undefined;
|
|
72
|
+
for (const power of linearPowers) {
|
|
73
|
+
const base = Math.pow(10, power);
|
|
74
|
+
for (const multiple of linearMultiples) {
|
|
75
|
+
const step = base * multiple;
|
|
76
|
+
const cMin = Math.ceil(min / step);
|
|
77
|
+
const cMax = Math.floor(max / step);
|
|
78
|
+
const ticks = cMax - cMin + 1;
|
|
79
|
+
|
|
80
|
+
if (
|
|
81
|
+
!best ||
|
|
82
|
+
Math.abs(best.ticks - desiredTicks) > Math.abs(ticks - desiredTicks)
|
|
83
|
+
) {
|
|
84
|
+
best = {
|
|
85
|
+
min: cMin * step,
|
|
86
|
+
step: step,
|
|
87
|
+
ticks: ticks
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (!best) {
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
const b = best;
|
|
97
|
+
return R.range(0, b.ticks).map(l => b.min + b.step * l);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const logarithmicAlternatives = [
|
|
101
|
+
[0],
|
|
102
|
+
[0, 5],
|
|
103
|
+
[0, 1, 2, 5],
|
|
104
|
+
[0, 1, 2, 3, 5],
|
|
105
|
+
[0, 1, 2, 3, 5, 8],
|
|
106
|
+
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
107
|
+
];
|
|
108
|
+
|
|
109
|
+
export function getLogarithmicTicks(
|
|
110
|
+
desiredTicks: number,
|
|
111
|
+
min: number,
|
|
112
|
+
max: number
|
|
113
|
+
): Array<number> {
|
|
114
|
+
const minPow = Math.floor(Math.log10(min)) - 1;
|
|
115
|
+
const maxPow = Math.ceil(Math.log10(max)) + 1;
|
|
116
|
+
const powers = R.range(0, maxPow - minPow + 1).map(p => minPow + p);
|
|
117
|
+
const alternatives = logarithmicAlternatives.map(stepAlt => {
|
|
118
|
+
const altLines = powers.reduce((lines: Array<number>, power: number) => {
|
|
119
|
+
const base = Math.pow(10, power);
|
|
120
|
+
const powerLines = stepAlt.map(i => i * base);
|
|
121
|
+
return lines.concat(powerLines);
|
|
122
|
+
}, []);
|
|
123
|
+
return altLines.filter(l => l >= min && l <= max);
|
|
124
|
+
});
|
|
125
|
+
const bestLines = alternatives.reduce(
|
|
126
|
+
(prev, alt) =>
|
|
127
|
+
Math.abs(alt.length - desiredTicks) < Math.abs(prev.length - desiredTicks)
|
|
128
|
+
? alt
|
|
129
|
+
: prev
|
|
130
|
+
);
|
|
131
|
+
return bestLines;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function transformPoint(
|
|
135
|
+
point: AbstractImage.Point,
|
|
136
|
+
xMin: number,
|
|
137
|
+
xMax: number,
|
|
138
|
+
yMin: number,
|
|
139
|
+
yMax: number,
|
|
140
|
+
xAxis: Axis | undefined,
|
|
141
|
+
yAxis: Axis | undefined
|
|
142
|
+
): AbstractImage.Point {
|
|
143
|
+
const x = transformValue(point.x, xMin, xMax, xAxis);
|
|
144
|
+
const y = transformValue(point.y, yMin, yMax, yAxis);
|
|
145
|
+
return AbstractImage.createPoint(x, y);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function transformValue(
|
|
149
|
+
value: number,
|
|
150
|
+
min: number,
|
|
151
|
+
max: number,
|
|
152
|
+
axis: Axis | undefined
|
|
153
|
+
): number {
|
|
154
|
+
if (!axis) {
|
|
155
|
+
return value;
|
|
156
|
+
}
|
|
157
|
+
const range = max - min;
|
|
158
|
+
switch (axis.type) {
|
|
159
|
+
case "linear":
|
|
160
|
+
return min + range * linearTransform(value, axis.min, axis.max);
|
|
161
|
+
case "logarithmic":
|
|
162
|
+
return min + range * logarithmicTransform(value, axis.min, axis.max);
|
|
163
|
+
default:
|
|
164
|
+
return 0;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function inverseTransformValue(
|
|
169
|
+
value: number,
|
|
170
|
+
min: number,
|
|
171
|
+
max: number,
|
|
172
|
+
axis: Axis | undefined
|
|
173
|
+
): number {
|
|
174
|
+
if (!axis) {
|
|
175
|
+
return value;
|
|
176
|
+
}
|
|
177
|
+
const range = max - min;
|
|
178
|
+
switch (axis.type) {
|
|
179
|
+
case "linear":
|
|
180
|
+
return inverseLinearTransform((value - min) / range, axis.min, axis.max);
|
|
181
|
+
case "logarithmic":
|
|
182
|
+
return inverseLogarithmicTransform(
|
|
183
|
+
(value - min) / range,
|
|
184
|
+
axis.min,
|
|
185
|
+
axis.max
|
|
186
|
+
);
|
|
187
|
+
default:
|
|
188
|
+
return 0;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function linearTransform(
|
|
193
|
+
value: number,
|
|
194
|
+
min: number,
|
|
195
|
+
max: number
|
|
196
|
+
): number {
|
|
197
|
+
return (value - min) / (max - min);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export function logarithmicTransform(
|
|
201
|
+
value: number,
|
|
202
|
+
min: number,
|
|
203
|
+
max: number
|
|
204
|
+
): number {
|
|
205
|
+
if (value > 0) {
|
|
206
|
+
return (
|
|
207
|
+
(Math.log10(value) - Math.log10(min)) /
|
|
208
|
+
(Math.log10(max) - Math.log10(min))
|
|
209
|
+
);
|
|
210
|
+
} else if (value < 0) {
|
|
211
|
+
return 0.0;
|
|
212
|
+
} else {
|
|
213
|
+
return 0.0;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function inverseLinearTransform(
|
|
218
|
+
value: number,
|
|
219
|
+
min: number,
|
|
220
|
+
max: number
|
|
221
|
+
): number {
|
|
222
|
+
return min + value * (max - min);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export function inverseLogarithmicTransform(
|
|
226
|
+
value: number,
|
|
227
|
+
min: number,
|
|
228
|
+
max: number
|
|
229
|
+
): number {
|
|
230
|
+
return Math.pow(
|
|
231
|
+
10,
|
|
232
|
+
value * (Math.log10(max) - Math.log10(min)) + Math.log10(min)
|
|
233
|
+
);
|
|
234
|
+
}
|