svelte-plots-basic 1.1.4 → 2.0.1

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.
Files changed (48) hide show
  1. package/README.md +53 -67
  2. package/package.json +10 -4
  3. package/src/2d/Area.svelte +61 -0
  4. package/src/2d/Axes.svelte +500 -0
  5. package/src/2d/Axis.svelte +50 -0
  6. package/src/2d/AxisLines.svelte +54 -0
  7. package/src/2d/AxisTickLabels.svelte +82 -0
  8. package/src/2d/Bars.svelte +65 -0
  9. package/src/2d/Box.svelte +50 -0
  10. package/src/2d/ColorLegend.svelte +36 -0
  11. package/src/2d/Legend.svelte +148 -0
  12. package/src/2d/Lines.svelte +49 -0
  13. package/src/2d/Multilines.svelte +67 -0
  14. package/src/2d/Points.svelte +79 -0
  15. package/src/2d/Rectangles.svelte +74 -0
  16. package/src/2d/Segments.svelte +65 -0
  17. package/src/2d/TextLabels.svelte +107 -0
  18. package/src/2d/TextLegend.svelte +53 -0
  19. package/src/2d/XAxis.svelte +99 -0
  20. package/src/2d/YAxis.svelte +99 -0
  21. package/src/{index.js → 2d/index.js} +10 -6
  22. package/src/3d/Axes.svelte +445 -0
  23. package/src/3d/Axis.svelte +64 -0
  24. package/src/3d/AxisLines.svelte +50 -0
  25. package/src/3d/AxisTickLabels.svelte +54 -0
  26. package/src/3d/Lines.svelte +49 -0
  27. package/src/3d/Mesh.svelte +188 -0
  28. package/src/3d/Points.svelte +61 -0
  29. package/src/3d/Segments.svelte +69 -0
  30. package/src/3d/TextLabels.svelte +79 -0
  31. package/src/3d/XAxis.svelte +123 -0
  32. package/src/3d/YAxis.svelte +123 -0
  33. package/src/3d/ZAxis.svelte +123 -0
  34. package/src/3d/index.js +13 -0
  35. package/src/Colors.js +36 -1
  36. package/src/Utils.js +205 -0
  37. package/src/AreaSeries.svelte +0 -53
  38. package/src/Axes.svelte +0 -550
  39. package/src/BarSeries.svelte +0 -68
  40. package/src/Box.svelte +0 -35
  41. package/src/LineSeries.svelte +0 -50
  42. package/src/Rectangles.svelte +0 -64
  43. package/src/ScatterSeries.svelte +0 -69
  44. package/src/Segments.svelte +0 -52
  45. package/src/TextLabels.svelte +0 -88
  46. package/src/TextLegend.svelte +0 -38
  47. package/src/XAxis.svelte +0 -69
  48. package/src/YAxis.svelte +0 -72
@@ -0,0 +1,123 @@
1
+ <script>
2
+ /****************************************************
3
+ * Z-Axis for 3D plots *
4
+ * -------------------- *
5
+ * shows z-axis and its elements (ticks, grid, etc.) *
6
+ *****************************************************/
7
+
8
+ import { cbind, matrix, Vector } from 'mdatools/arrays';
9
+ import { getContext } from 'svelte';
10
+ import { Colors } from '../Colors';
11
+ import { getAxisTicks } from '../Utils.js';
12
+ import Axis from './Axis.svelte';
13
+
14
+
15
+ /*****************************************/
16
+ /* Input parameters */
17
+ /*****************************************/
18
+
19
+ export let slot = 'zaxis'; // slot the component must be placed in
20
+ export let ticks = undefined; // vector with numeric tick positions in plot units
21
+ export let tickLabels = ticks; // vector with labels for each tick
22
+ export let showGrid = false; // logical, show or not grid lines
23
+ export let title = ''; // axis title
24
+
25
+ export let lineColor = Colors.DARKGRAY;
26
+ export let gridColor = Colors.MIDDLEGRAY;
27
+ export let textColor = Colors.DARKGRAY;
28
+
29
+
30
+ /*****************************************/
31
+ /* Component code */
32
+ /*****************************************/
33
+
34
+ if (slot !== 'zaxis') {
35
+ throw('Component ZAxis must have "slot=\'zaxis\'" attribute.');
36
+ }
37
+
38
+ // get axes context
39
+ const axes = getContext('axes');
40
+
41
+ // get reactive variables needed to compute coordinates
42
+ const xLim = axes.xLim;
43
+ const yLim = axes.yLim;
44
+ const zLim = axes.zLim;
45
+ const scale = axes.scale;
46
+ const isOk = axes.isOk;
47
+
48
+ // prepare variables for coordinates
49
+ let titleCoords = [];
50
+ let grid1 = [];
51
+ let grid2 = [];
52
+ let axisLine = [];
53
+ let tickCoords = [];
54
+
55
+ // compute tick x-coordinates
56
+ if ($isOk) {
57
+ const dZ = ($zLim[1] - $zLim[0]) / 100; // 1% of axis size
58
+ const ticksZ = getAxisTicks(ticks, $zLim, axes.TICK_NUM[$scale], true);
59
+ const tickNum = ticksZ.length;
60
+
61
+ // compute tick x-coordinates (middle, up and bottom)
62
+ const dX = ($xLim[1] - $xLim[0]) / 100; // 1% of axis size
63
+ const ticksX = Vector.fill($xLim[0], tickNum);
64
+ const ticksX1 = ticksX.subtract(dX * 1.5);
65
+ const ticksX2 = ticksX.add(dX * 1.5);
66
+ const ticksX3 = ticksX.subtract(dX * 1);
67
+
68
+ // tick y-coordinates
69
+ const dY = ($yLim[1] - $yLim[0]) / 100; // 1% of axis size
70
+ const ticksY = Vector.fill($yLim[0], tickNum);
71
+ const ticksY1 = ticksY.subtract(dY * 1.5);
72
+ const ticksY2 = ticksY.add(dY * 1.5);
73
+ const ticksY3 = ticksY.subtract(dY * 5);
74
+
75
+ // coordinates for the ends of grid
76
+ const gridXEnd = Vector.fill($xLim[1], tickNum);
77
+ const gridYEnd = Vector.fill($yLim[1], tickNum);
78
+
79
+ // tick labels
80
+ tickLabels = (ticks === undefined || tickLabels === undefined) ? ticksZ.v : tickLabels;
81
+ if (tickLabels.length !== ticksZ.length) {
82
+ throw Error('ZAxis: "tickLabels" must be a array of the same size as ticks.');
83
+ }
84
+
85
+ // combine all coordinates together
86
+ grid1 = [
87
+ cbind(ticksX, ticksY, ticksZ),
88
+ cbind(gridXEnd, ticksY, ticksZ)
89
+ ];
90
+
91
+ grid2 = [
92
+ cbind(ticksX, ticksY, ticksZ),
93
+ cbind(ticksX, gridYEnd, ticksZ)
94
+ ];
95
+
96
+ axisLine = [
97
+ matrix([$xLim[0], $yLim[0], $zLim[0]], 1, 3),
98
+ matrix([$xLim[0], $yLim[0], $zLim[1]], 1, 3)
99
+ ]
100
+
101
+ tickCoords = [
102
+ cbind(ticksX, ticksY, ticksZ), // middle point
103
+ cbind(ticksX2, ticksY, ticksZ), // middle point with positive shift along X
104
+ cbind(ticksX, ticksY2, ticksZ), // middle point with positive shift along Y
105
+ cbind(ticksX1, ticksY1, ticksZ), // middle point with negative shift along X and Y
106
+ cbind(ticksX3, ticksY3, ticksZ) // middle point with negative shift along X and Y for ticks
107
+ ];
108
+
109
+ // here we do not need to make a matrix as the three values will be used as vectors
110
+ titleCoords = [
111
+ [$xLim[0] - 2 * dX], [$yLim[0] - 2 * dY], [$zLim[1] + 3 * dZ]
112
+ ];
113
+ }
114
+ </script>
115
+
116
+ {#if $isOk && axisLine.length > 0}
117
+ <Axis
118
+ className="mdaplot__zaxis"
119
+ {title} {lineColor} {gridColor} {textColor} {titleCoords}
120
+ {showGrid} {grid1} {grid2} {axisLine} {tickCoords} {tickLabels}
121
+ />
122
+ {/if}
123
+
@@ -0,0 +1,13 @@
1
+ export { default as Axes } from './Axes.svelte';
2
+
3
+ export { default as XAxis } from './XAxis.svelte';
4
+ export { default as YAxis } from './YAxis.svelte';
5
+ export { default as ZAxis } from './ZAxis.svelte';
6
+
7
+ export { default as TextLabels } from './TextLabels.svelte';
8
+ export { default as Segments } from './Segments.svelte';
9
+ export { default as Lines } from './Lines.svelte';
10
+ export { default as Mesh } from './Mesh.svelte';
11
+ export { default as Points } from './Points.svelte';
12
+
13
+ export { getcolmap, Colors } from '../Colors.js';
package/src/Colors.js CHANGED
@@ -7,9 +7,44 @@ export const Colors = {
7
7
  "MIDDLEGRAY": "#dadada",
8
8
  "LIGHTGRAY": "#f0f0f0",
9
9
  "DARKGRAY": "#606060",
10
+ "LEGEND": "#303030",
10
11
 
11
- "PRIMARY": "#2266ff",
12
+ "PRIMARY": "#2679B2",
12
13
  "PRIMARY_TEXT": "#333",
13
14
  }
14
15
 
15
16
 
17
+ /**
18
+ * Returns an array with 'n' colors.
19
+ *
20
+ * @param {number} n - number of colors to return.
21
+ *
22
+ * @returns {Array} array with colors as strings (hexadecimal).
23
+ *
24
+ */
25
+ export function getcolmap(n) {
26
+ if (n == 1) return ["#2679B2"];
27
+ if (n == 2) return ["#2679B2", "#D22C2F"];
28
+ if (n == 3) return ["#2679B2", "#92B42A", "#D22C2F"];
29
+ if (n == 4) return ["#2679B2", "#2E9658", "#F2B825", "#D22C2F"];
30
+ if (n == 5) return ["#2679B2", "#22988A", "#92B42A", "#F79426", "#D22C2F"];
31
+ if (n == 6) return ["#2679B2", "#1C9AA8", "#379531", "#EED524", "#FB7F28", "#D22C2F"];
32
+ if (n == 7) return ["#2679B2", "#1D94A9", "#2E9658", "#92B42A", "#F2B825", "#F47129", "#D22C2F"];
33
+ if (n == 8) return ["#2679B2", "#1E90AA", "#279775", "#519E2F", "#D3CB25", "#F5A326", "#EF672A", "#D22C2F"];
34
+ if (n == 9) return ["#2679B2", "#1F8DAB", "#22988A", "#33953F", "#92B42A", "#EFCA24", "#F79426", "#EB5F2A", "#D22C2F"];
35
+ if (n == 10) return ["#2679B2", "#208BAC", "#1E999A", "#2E9658", "#5FA32E", "#C5C626", "#F2B825", "#F98827", "#E85A2B", "#D22C2F"];
36
+ if (n == 11) return ["#2679B2", "#2089AD", "#1C9AA8", "#29976C", "#379531", "#92B42A", "#EED524", "#F4AA26", "#FB7F28",
37
+ "#E6552B", "#D22C2F"];
38
+ if (n == 12) return ["#2679B2", "#2188AD", "#1C97A8", "#25987C", "#329546", "#68A62D", "#BCC327", "#F0C524", "#F69E26",
39
+ "#F77728", "#E4512B", "#D22C2F"];
40
+ if (n == 13) return ["#2679B2", "#2186AD", "#1D94A9", "#22988A", "#2E9658", "#469A2F", "#92B42A", "#DECF25", "#F2B825",
41
+ "#F79426", "#F47129", "#E34E2C", "#D22C2F"];
42
+ if (n == 14) return ["#2679B2", "#2285AE", "#1E92AA", "#209995", "#2A9767", "#34953A", "#6FA82D", "#B5C128", "#EFCE24", "#F3AD25",
43
+ "#F98C27", "#F16B29", "#E14B2C", "#D22C2F"];
44
+ if (n == 15) return ["#2679B2", "#2284AE", "#1E90AA", "#1D999F", "#279775", "#31964A", "#519E2F", "#92B42A", "#D3CB25", "#F0C224",
45
+ "#F5A326", "#FA8527", "#EF672A", "#E0492C", "#D22C2F"];
46
+
47
+ // if n > 15 return 16 colors
48
+ return ["#2679B2", "#2283AE", "#1F8FAB", "#1C9AA8", "#249880", "#2E9658", "#379531", "#73AA2C", "#B0BF28", "#EDD424", "#F2B825",
49
+ "#F69B26", "#FB7F28", "#ED632A", "#DF472C", "#D22C2F"];
50
+ }
package/src/Utils.js ADDED
@@ -0,0 +1,205 @@
1
+ import { cbind, vector, isvector, Vector } from 'mdatools/arrays';
2
+
3
+ /**
4
+ * Check coordinates and convert them to vector if necessary.
5
+ *
6
+ * @param {Array|Vector} x - vector or array with coordinates.
7
+ * @param {string} source - name of component which called the method (needed for error message).
8
+ *
9
+ * @returns {Vector}
10
+ *
11
+ */
12
+ export function checkCoords(x, source) {
13
+
14
+ if (Array.isArray(x)) {
15
+ x = vector(x);
16
+ }
17
+
18
+ if (!isvector(x)) {
19
+ throw Error(source + ": coordinates must be provided as array or instance of Vector class.");
20
+ }
21
+
22
+ if (x.length < 1) {
23
+ throw Error(source + ": vector with coordinates is empty.");
24
+ }
25
+
26
+ return x;
27
+ }
28
+
29
+ /**
30
+ * Create string with coordinates of SVG polygon in 3D.
31
+ *
32
+ * @param {Array|Vector} x - vector with x-coordinates of polygon points.
33
+ * @param {Array|Vector} y - vector with y-coordinates of polygon points.
34
+ * @param {Array|Vector} z - vector with y-coordinates of polygon points.
35
+ * @param {Array} tM - transformation matrix for 3D->2D (from 'Axes').
36
+ * @param {Object} axes - JSON with Axes context.
37
+ *
38
+ * @returns {string} string with coordinates.
39
+ *
40
+ */
41
+ export function val2p3d(x, y, z, tM, axes) {
42
+
43
+ if (x === undefined || y === undefined) return undefined;
44
+
45
+ const [px, py] = axes.transform(cbind(checkCoords(x), checkCoords(y), checkCoords(z)), tM);
46
+
47
+ if (px.length !== py.length) {
48
+ throw Error('PlotSeries: parameters "xValues", "yValues" and "zValues" must be numeric vectors of the same length.');
49
+ }
50
+
51
+ let p = "";
52
+ for (let i = 0; i < px.length; i++) {
53
+ p += px[i] + "," + py[i] + " ";
54
+ }
55
+
56
+ return p;
57
+ }
58
+
59
+
60
+ /**
61
+ * Create string with coordinates of SVG polygon.
62
+ *
63
+ * @param {Array|Vector} x - vector with x-coordinates of polygon points.
64
+ * @param {Array|Vector} y - vector with y-coordinates of polygon points.
65
+ * @param {Array} tX - transformation parameters for x-coordinates (from 'Axes').
66
+ * @param {Array} tY - transformation parameters for y-coordinates (from 'Axes').
67
+ * @param {Object} axes - JSON with Axes context.
68
+ *
69
+ * @returns {string} string with coordinates.
70
+ *
71
+ */
72
+ export function val2p(x, y, tX, tY, axes) {
73
+
74
+ if (x === undefined || y === undefined) return undefined;
75
+
76
+ const px = axes.transform(checkCoords(x), tX.coords);
77
+ const py = axes.transform(checkCoords(y), tY.coords);
78
+
79
+ if (px.length != py.length) {
80
+ throw Error('PlotSeries: parameters "xValues" and "yValues" must be numeric vectors of the same length.');
81
+ }
82
+
83
+ let p = "";
84
+ for (let i = 0; i < px.length; i++) {
85
+ p += px[i] + "," + py[i] + " ";
86
+ }
87
+
88
+ return p;
89
+ }
90
+
91
+ /**
92
+ * Computes nice tick values for axis.
93
+ *
94
+ * @param {Array} ticks - vector with ticks if alredy available (if not, new will be computed).
95
+ * @param {Array} lim - vector with axis limits tickets must be computed for.
96
+ * @param {number} maxTickNum - maximum number of ticks to compute.
97
+ * @param {boolean} round - round or not the fractions when computing nice numbers for the ticks.
98
+ *
99
+ * @returns {Array} an array with computed tick positions.
100
+ *
101
+ */
102
+ export function getAxisTicks(ticks, lim, maxTickNum, round = true) {
103
+
104
+ // if ticks are already provided do not recompute them
105
+ if (ticks !== undefined) {
106
+ if (Array.isArray(ticks)) {
107
+ ticks = vector(ticks);
108
+ }
109
+
110
+ if (!isvector(ticks)) {
111
+ throw Error('getAxisTicks: axis ticks must be provided as an array or as a vector.');
112
+ }
113
+
114
+ return ticks.filter(x => x >= lim[0] & x <= lim[1]);
115
+ }
116
+
117
+ // check if limits are ok
118
+ if (typeof(lim) !== "object" || lim[0] === undefined || lim[1] === undefined) return undefined;
119
+
120
+ // get range as a nice number and compute min, max and steps for the tick sequence
121
+ const delta = (lim[1] - lim[0]) / 50;
122
+ const range = niceNum(lim[1] - lim[0] - 1 * delta, round);
123
+ const tickSpacing = niceNum(range / (maxTickNum - 1), round);
124
+ const tickMin = Math.ceil((lim[0] + delta) / tickSpacing) * tickSpacing;
125
+ const tickMax = Math.floor((lim[1] - delta) / tickSpacing) * tickSpacing;
126
+
127
+ // recompute maxTickNum
128
+ maxTickNum = Math.round((tickMax - tickMin + 1) / tickSpacing) + 1;
129
+
130
+ // create a sequence of ticks
131
+ ticks = Vector.seq(tickMin, tickMax, tickSpacing);
132
+
133
+ // if step is smaller than 1 round values to remove small decimals accidentiall added by JS
134
+ if (Math.abs(tickSpacing) < 1) {
135
+ const r = Math.pow(10, 1 + Math.round(-Math.log10(tickSpacing)));
136
+ ticks = ticks.apply(v => Math.round((v + Number.EPSILON) * r) / r)
137
+ }
138
+
139
+ // make sure the ticks are not aligned with axes limits
140
+ return ticks.filter(x => x >= lim[0] & x <= lim[1]);
141
+ }
142
+
143
+
144
+ /**
145
+ * Computes a nice spacing value for a given range.
146
+ *
147
+ * @param {Number} localRange - a range (max - min).
148
+ * @param {boolean} round - round or not the fractions when computing the number.
149
+ *
150
+ * @returns {Number} the computed spacing value.
151
+ *
152
+ */
153
+ export function niceNum( localRange, round) {
154
+
155
+ const exponent = Math.floor(Math.log10(localRange));
156
+ const fraction = localRange / Math.pow(10, exponent);
157
+ let niceFraction;
158
+
159
+ if (round) {
160
+ if (fraction < 1.5)
161
+ niceFraction = 1;
162
+ else if (fraction < 3)
163
+ niceFraction = 2;
164
+ else if (fraction < 7)
165
+ niceFraction = 5;
166
+ else
167
+ niceFraction = 10;
168
+ } else {
169
+ if (fraction <= 1)
170
+ niceFraction = 1;
171
+ else if (fraction <= 2)
172
+ niceFraction = 2;
173
+ else if (fraction <= 5)
174
+ niceFraction = 5;
175
+ else
176
+ niceFraction = 10;
177
+ }
178
+
179
+ return niceFraction * Math.pow(10, exponent);
180
+ }
181
+
182
+
183
+ /**
184
+ * Rounds coordinate values to single decimal
185
+ */
186
+ export function roundCoords(x) {
187
+ return Math.round(x * 10) / 10
188
+ }
189
+
190
+
191
+ /**
192
+ * Computes a scale level.
193
+ *
194
+ * @param {numeric} width - width of plotting area in pixels.
195
+ * @param {numeric} height - height of plotting area in pixels.
196
+ *
197
+ * @returns {text} the scale level ("small", "medium" or "large").
198
+ *
199
+ */
200
+ export function getScale(width, height) {
201
+ if (height < 300.2 || width < 300.2) return "small";
202
+ if (height < 600.2 || width < 600.2) return "medium";
203
+ if (height < 850.2 || width < 850.2) return "large";
204
+ return "xlarge";
205
+ }
@@ -1,53 +0,0 @@
1
- <script>
2
-
3
- import { getContext } from 'svelte';
4
- import { mrange } from 'mdatools/stat';
5
- import { Colors } from './Colors';
6
-
7
- // input parameters
8
- export let xValues;
9
- export let yValues;
10
- export let title = "";
11
- export let lineWidth = 1;
12
- export let lineColor = Colors.PRIMARY;
13
- export let fillColor = Colors.PRIMARY;
14
- export let opacity = 1;
15
- export let lineType = 1;
16
-
17
- /* sanity check of input parameters */
18
- if (!Array.isArray(xValues) || !Array.isArray(yValues) || xValues.length != yValues.length) {
19
- throw("AreaSeries: parameters 'xValues' and 'yValues' must be numeric vectors of the same length.");
20
- }
21
-
22
- // compute ranges for x and y values
23
- const xValuesRange = mrange(xValues, 0.05);
24
- const yValuesRange = mrange(yValues, 0.05);
25
-
26
- // get axes context and adjust axes limits
27
- const axes = getContext('axes');
28
- axes.adjustXAxisLimits(xValuesRange);
29
- axes.adjustYAxisLimits(yValuesRange);
30
-
31
- // get reactive variables needed to compute coordinates
32
- const xLim = axes.xLim;
33
- const yLim = axes.yLim;
34
- const axesWidth = axes.width;
35
- const axesHeight = axes.height;
36
- const scale = axes.scale;
37
-
38
- // reactive variables for coordinates of data points in pixels
39
- $: y0 = axes.scaleY([0], $yLim, $axesHeight);
40
- $: x = axes.scaleX(xValues, $xLim, $axesWidth);
41
- $: y = axes.scaleY(yValues, $yLim, $axesHeight);
42
- $: p = x !== undefined && y !== undefined ? x.map((v, i) => `${v},${y[i]}`).join(' ') : undefined;
43
- $: areaStyleStr = `opacity:${opacity};fill:${fillColor};stroke:${lineColor};stroke-width: ${lineWidth}px;stroke-dasharray:${axes.LINE_STYLES[$scale][lineType - 1]}`;
44
- </script>
45
-
46
- {#if p !== undefined}
47
- <g class="series lineseries" style="{areaStyleStr}" title="{title}">
48
- <polygon points="{x[0] + "," + y0 + " " + p + " " + x[x.length - 1] + "," + y0[0]}"/>
49
- </g>
50
- {/if}
51
-
52
- <style>
53
- </style>