svelte-plots-basic 1.1.3 → 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.
- package/README.md +54 -69
- package/package.json +28 -33
- package/src/2d/Area.svelte +61 -0
- package/src/2d/Axes.svelte +500 -0
- package/src/2d/Axis.svelte +50 -0
- package/src/2d/AxisLines.svelte +54 -0
- package/src/2d/AxisTickLabels.svelte +82 -0
- package/src/2d/Bars.svelte +65 -0
- package/src/2d/Box.svelte +50 -0
- package/src/2d/ColorLegend.svelte +36 -0
- package/src/2d/Legend.svelte +148 -0
- package/src/2d/Lines.svelte +49 -0
- package/src/2d/Multilines.svelte +67 -0
- package/src/2d/Points.svelte +79 -0
- package/src/2d/Rectangles.svelte +74 -0
- package/src/2d/Segments.svelte +65 -0
- package/src/2d/TextLabels.svelte +107 -0
- package/src/2d/TextLegend.svelte +53 -0
- package/src/2d/XAxis.svelte +99 -0
- package/src/2d/YAxis.svelte +99 -0
- package/src/{index.js → 2d/index.js} +10 -6
- package/src/3d/Axes.svelte +445 -0
- package/src/3d/Axis.svelte +64 -0
- package/src/3d/AxisLines.svelte +50 -0
- package/src/3d/AxisTickLabels.svelte +54 -0
- package/src/3d/Lines.svelte +49 -0
- package/src/3d/Mesh.svelte +188 -0
- package/src/3d/Points.svelte +61 -0
- package/src/3d/Segments.svelte +69 -0
- package/src/3d/TextLabels.svelte +79 -0
- package/src/3d/XAxis.svelte +123 -0
- package/src/3d/YAxis.svelte +123 -0
- package/src/3d/ZAxis.svelte +123 -0
- package/src/3d/index.js +13 -0
- package/src/Colors.js +36 -1
- package/src/Utils.js +205 -0
- package/dist/index.js +0 -4209
- package/dist/index.mjs +0 -4187
- package/src/AreaSeries.svelte +0 -53
- package/src/Axes.svelte +0 -538
- package/src/BarSeries.svelte +0 -78
- package/src/Box.svelte +0 -35
- package/src/LineSeries.svelte +0 -50
- package/src/Rectangles.svelte +0 -64
- package/src/ScatterSeries.svelte +0 -85
- package/src/Segments.svelte +0 -48
- package/src/TextLabels.svelte +0 -78
- package/src/TextLegend.svelte +0 -38
- package/src/XAxis.svelte +0 -69
- package/src/YAxis.svelte +0 -72
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
/****************************************************
|
|
3
|
+
* Axes *
|
|
4
|
+
* -------------------- *
|
|
5
|
+
* root item for any plot *
|
|
6
|
+
*****************************************************/
|
|
7
|
+
|
|
8
|
+
import { setContext, createEventDispatcher, onMount, onDestroy } from 'svelte';
|
|
9
|
+
import { writable } from 'svelte/store';
|
|
10
|
+
import { isvector, vector, Vector } from 'mdatools/arrays';
|
|
11
|
+
import { roundCoords, getScale } from '../Utils.js';
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
/*****************************************/
|
|
15
|
+
/* Input parameters */
|
|
16
|
+
/*****************************************/
|
|
17
|
+
|
|
18
|
+
export let limX; // array with limits for x-axis (in plot units) [min, max]
|
|
19
|
+
export let limY; // array with limits for y-axis (in plot units) [min, max]
|
|
20
|
+
export let title = ''; // title of the plot
|
|
21
|
+
export let xLabel = ''; // label for x-axis
|
|
22
|
+
export let yLabel = ''; // label for y-axis
|
|
23
|
+
export let margins = [0.034, 0.034, 0.034, 0.034]; // margins [bottom, left, top, right] )
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
/*****************************************/
|
|
27
|
+
/* Constants */
|
|
28
|
+
/*****************************************/
|
|
29
|
+
|
|
30
|
+
// event dispatcher
|
|
31
|
+
const dispatch = createEventDispatcher();
|
|
32
|
+
|
|
33
|
+
// how big are margins (number of pixels in unit margin value) between axis and plot area if axis are shown
|
|
34
|
+
const AXES_MARGIN_FACTORS = {
|
|
35
|
+
'small': 30,
|
|
36
|
+
'medium': 40,
|
|
37
|
+
'large': 50,
|
|
38
|
+
'xlarge': 60
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// number of ticks along each axis
|
|
42
|
+
const TICK_NUM = {
|
|
43
|
+
'small': 5,
|
|
44
|
+
'medium': 8,
|
|
45
|
+
'large': 12,
|
|
46
|
+
'xlarge': 15
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// size of ticks
|
|
50
|
+
const TICK_SIZE = {
|
|
51
|
+
'small': 4,
|
|
52
|
+
'medium': 6,
|
|
53
|
+
'large': 8,
|
|
54
|
+
'xlarge': 10
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// font size for legend items in pixels
|
|
58
|
+
const LEGEND_FONT_SIZE = {
|
|
59
|
+
"small": 10,
|
|
60
|
+
"medium": 12,
|
|
61
|
+
"large": 14,
|
|
62
|
+
"xlarge": 16
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// margin between plot series elements and data labels
|
|
66
|
+
const LABELS_MARGIN = {
|
|
67
|
+
'small': 10,
|
|
68
|
+
'medium': 13,
|
|
69
|
+
'large': 15,
|
|
70
|
+
'xlarge': 17
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// line styles for different scales and types
|
|
74
|
+
const LINE_STYLES = {
|
|
75
|
+
small: ['0', '3,3', '1,1', '3,1'],
|
|
76
|
+
medium: ['0', '5,5', '2,2', '5,2'],
|
|
77
|
+
large: ['0', '7,7', '3,3', '7,3'],
|
|
78
|
+
xlarge: ['0', '9,9', '4,4', '9,3'],
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// marker symbols
|
|
82
|
+
const MARKER_SYMBOLS = ["●", "◼", "▲", "▼", "⬥", "+", "*", "×"];
|
|
83
|
+
|
|
84
|
+
// constant to make clip path ID unique
|
|
85
|
+
const clipPathID = 'plottingArea' + Math.round(Math.random() * 10000);
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
/*****************************************/
|
|
89
|
+
/* Variable parameters for internal use */
|
|
90
|
+
/*****************************************/
|
|
91
|
+
|
|
92
|
+
/* parameters for internal use inside the component */
|
|
93
|
+
let plotElement;
|
|
94
|
+
let axesElement;
|
|
95
|
+
let width, height = 100;
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
/*****************************************/
|
|
99
|
+
/* Helper functions */
|
|
100
|
+
/*****************************************/
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Dispatcher for click events.
|
|
104
|
+
* @param {string} eventName - name of the event.
|
|
105
|
+
* @param {HTMLDOMElement} el - DOM element the click was registered for.
|
|
106
|
+
*
|
|
107
|
+
*/
|
|
108
|
+
function dispatchClickEvent(eventName, el) {
|
|
109
|
+
const id = Array.prototype.indexOf.call(el.parentNode.children, el)
|
|
110
|
+
dispatch(eventName, {seriesTitle: el.parentNode.getAttribute('title'), elementID: id});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Handler (router) for click events.
|
|
115
|
+
*
|
|
116
|
+
* @param {event} e - event object.
|
|
117
|
+
*
|
|
118
|
+
* @description
|
|
119
|
+
* Checks which element the click was made on and dispatch a corresponding event.
|
|
120
|
+
*
|
|
121
|
+
*/
|
|
122
|
+
function handleClick(e) {
|
|
123
|
+
|
|
124
|
+
// click on scatter plot markers
|
|
125
|
+
if (e.target.tagName === 'text' && e.target.parentNode.classList.contains('series_points')) {
|
|
126
|
+
dispatchClickEvent('markerclick', e.target);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// click on bar plot bars
|
|
131
|
+
if (e.target.tagName === 'rect' && e.target.parentNode.classList.contains('series_bar')) {
|
|
132
|
+
dispatchClickEvent('barclick', e.target);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// click outside any plot element
|
|
137
|
+
dispatch('axesclick');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Generic function to transform x or y-values from plot coordinates to screen (SVG) coordinates.
|
|
142
|
+
*
|
|
143
|
+
* @param {Array|Vector} v - vector with coordinates (or objects size) in original plot coordinates.
|
|
144
|
+
* @param {Object} tA - array with scaling and translation factors.
|
|
145
|
+
*
|
|
146
|
+
* @returns {Vector} vector with rescaled values
|
|
147
|
+
*
|
|
148
|
+
*/
|
|
149
|
+
const transform = function(v, tA) {
|
|
150
|
+
|
|
151
|
+
if (!$isOk || v === undefined || v === null) return undefined;
|
|
152
|
+
if (Array.isArray(v)) v = vector(v);
|
|
153
|
+
if (!isvector(v) || v.length < 1) return undefined;
|
|
154
|
+
|
|
155
|
+
return v.apply(a => roundCoords((a - tA[1]) * tA[0] + tA[2])).v;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Generic function to transform x or y-values from screen (SVG) coordinates to plot coordinates.
|
|
161
|
+
*
|
|
162
|
+
* @param {Array|Vector} v - vector with coordinates (or objects size) in screen (SVG) coordinates.
|
|
163
|
+
* @param {Object} tA - array with scaling and translation factors.
|
|
164
|
+
*
|
|
165
|
+
* @returns {Vector} vector plot coordinates.
|
|
166
|
+
*
|
|
167
|
+
*/
|
|
168
|
+
const invTransform = function(v, tA) {
|
|
169
|
+
|
|
170
|
+
if (!$isOk || v === undefined || v === null) return undefined;
|
|
171
|
+
if (Array.isArray(v)) v = vector(v);
|
|
172
|
+
if (!isvector(v) || v.length < 1) return undefined;
|
|
173
|
+
|
|
174
|
+
return v.apply(a => (a - tA[2]) / tA[0] + tA[1]).v;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/*****************************************/
|
|
178
|
+
/* Storage to share with children */
|
|
179
|
+
/*****************************************/
|
|
180
|
+
|
|
181
|
+
const scale = writable('medium'); // scale factor (how big the shown plot is)
|
|
182
|
+
const tX = writable({'coords': [1, 0, 0], 'objects': [1, 0, 0]}); // scaling and translation factors for x-dimension
|
|
183
|
+
const tY = writable({'coords': [1, 0, 0], 'objects': [1, 0, 0]}); // scaling and translation factors for y-dimension
|
|
184
|
+
const isOk = writable(false); // are axes ready for drawing
|
|
185
|
+
const xLim = writable(limX);
|
|
186
|
+
const yLim = writable(limY);
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
/*****************************************/
|
|
190
|
+
/* Axes context */
|
|
191
|
+
/*****************************************/
|
|
192
|
+
|
|
193
|
+
let context = {
|
|
194
|
+
|
|
195
|
+
// methods
|
|
196
|
+
transform: transform,
|
|
197
|
+
invTransform: invTransform,
|
|
198
|
+
|
|
199
|
+
// variables
|
|
200
|
+
scale: scale,
|
|
201
|
+
isOk: isOk,
|
|
202
|
+
xLim: xLim,
|
|
203
|
+
yLim: yLim,
|
|
204
|
+
tX: tX,
|
|
205
|
+
tY: tY,
|
|
206
|
+
|
|
207
|
+
// constants
|
|
208
|
+
LINE_STYLES: LINE_STYLES,
|
|
209
|
+
LABELS_MARGIN: LABELS_MARGIN,
|
|
210
|
+
TICK_NUM: TICK_NUM,
|
|
211
|
+
TICK_SIZE: TICK_SIZE,
|
|
212
|
+
MARKER_SYMBOLS: MARKER_SYMBOLS,
|
|
213
|
+
LEGEND_FONT_SIZE: LEGEND_FONT_SIZE
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
setContext('axes', context);
|
|
217
|
+
|
|
218
|
+
/*****************************************/
|
|
219
|
+
/* Reactive updates of the parameters */
|
|
220
|
+
/*****************************************/
|
|
221
|
+
|
|
222
|
+
// computes real margins in pixels based on current scale
|
|
223
|
+
$: pxMargins = margins.map(v => v * AXES_MARGIN_FACTORS[$scale]);
|
|
224
|
+
|
|
225
|
+
// update limits if necessary
|
|
226
|
+
$: xLim.update(v => limX);
|
|
227
|
+
$: yLim.update(v => limY);
|
|
228
|
+
|
|
229
|
+
// computes status which tells that axes limits look fine and it is safe to draw
|
|
230
|
+
// the status is based on the axis limits validity
|
|
231
|
+
$: isOk.update(v =>
|
|
232
|
+
Array.isArray($yLim) &&
|
|
233
|
+
Array.isArray($xLim) &&
|
|
234
|
+
$xLim.length === 2 &&
|
|
235
|
+
$yLim.length === 2 &&
|
|
236
|
+
!$yLim.some(v => v === undefined) &&
|
|
237
|
+
!$xLim.some(v => v === undefined) &&
|
|
238
|
+
!$yLim.some(v => isNaN(v)) &&
|
|
239
|
+
!$xLim.some(v => isNaN(v)) &&
|
|
240
|
+
$xLim[1] !== $xLim[0] &&
|
|
241
|
+
$yLim[1] !== $yLim[0] &&
|
|
242
|
+
width > (pxMargins[1] + pxMargins[3]) &&
|
|
243
|
+
height > (pxMargins[0] + pxMargins[2])
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
// update transformation array for x-coordinates
|
|
247
|
+
$: {
|
|
248
|
+
if ($isOk) {
|
|
249
|
+
tX.update(v => ({
|
|
250
|
+
'coords': [ (width - pxMargins[1] - pxMargins[3]) / ($xLim[1] - $xLim[0]), $xLim[0], pxMargins[1]],
|
|
251
|
+
'objects': [ (width - pxMargins[1] - pxMargins[3]) / ($xLim[1] - $xLim[0]), 0, 0]
|
|
252
|
+
}));
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
// update transformation array for y-coordinates
|
|
257
|
+
$: {
|
|
258
|
+
if ($isOk) {
|
|
259
|
+
tY.update(v => ({
|
|
260
|
+
'coords': [-(height - pxMargins[0] - pxMargins[2]) / ($yLim[1] - $yLim[0]), $yLim[1], pxMargins[2]],
|
|
261
|
+
'objects': [ (height - pxMargins[0] - pxMargins[2]) / ($yLim[1] - $yLim[0]), 0, 0]
|
|
262
|
+
}));
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
// computes coordinates for clip path box
|
|
267
|
+
$: cpx = $isOk ? transform($xLim, $tX.coords) : [0, 1];
|
|
268
|
+
$: cpy = $isOk ? transform($yLim, $tY.coords) : [1, 0];
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
/*****************************************/
|
|
272
|
+
/* Events observers */
|
|
273
|
+
/*****************************************/
|
|
274
|
+
|
|
275
|
+
// observer for the plot area size — to update the scale
|
|
276
|
+
const ro1 = new ResizeObserver(entries => {
|
|
277
|
+
for (let entry of entries) {
|
|
278
|
+
const pcr = plotElement.getBoundingClientRect();
|
|
279
|
+
scale.update(x => getScale(pcr.width, pcr.height));
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// observer for the axes area size - to update size of axes
|
|
284
|
+
const ro2 = new ResizeObserver(entries => {
|
|
285
|
+
for (let entry of entries) {
|
|
286
|
+
const acr = axesElement.getBoundingClientRect();
|
|
287
|
+
width = acr.width;
|
|
288
|
+
height = acr.height;
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
onMount(() => {
|
|
293
|
+
ro1.observe(plotElement);
|
|
294
|
+
ro2.observe(axesElement);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
onDestroy(() => {
|
|
298
|
+
ro1.unobserve(plotElement);
|
|
299
|
+
ro2.unobserve(axesElement);
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
</script>
|
|
303
|
+
|
|
304
|
+
<div class="plot {'plot_' + $scale}" bind:this={plotElement} class:plot_error={!$isOk}>
|
|
305
|
+
<!-- plot title and axis labels -->
|
|
306
|
+
{#if title !== ''}<div class="axes__title">{@html title}</div>{/if}
|
|
307
|
+
{#if yLabel !== ''}<div class="axes__ylabel"><span>{@html yLabel}</span></div>{/if}
|
|
308
|
+
{#if xLabel !== ''}<div class="axes__xlabel"><span>{@html xLabel}</span></div>{/if}
|
|
309
|
+
|
|
310
|
+
<!-- axes (coordinate system) -->
|
|
311
|
+
<div class="axes-wrapper" bind:this={axesElement} >
|
|
312
|
+
<svg on:click={handleClick} on:keydown={handleClick} preserveAspectRatio="none" class="axes">
|
|
313
|
+
|
|
314
|
+
<!-- define clipping path -->
|
|
315
|
+
<defs>
|
|
316
|
+
<clipPath id={clipPathID}>
|
|
317
|
+
<rect style="pointer-events:none" x={cpx[0]} y={cpy[1]} width={cpx[1]-cpx[0]} height={cpy[0]-cpy[1]} />
|
|
318
|
+
</clipPath>
|
|
319
|
+
</defs>
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
<!-- axis and box -->
|
|
323
|
+
<slot name="xaxis"></slot>
|
|
324
|
+
<slot name="yaxis"></slot>
|
|
325
|
+
|
|
326
|
+
<!-- main plot content -->
|
|
327
|
+
<g class="axes-content" clip-path="url(#{clipPathID})">
|
|
328
|
+
<slot></slot>
|
|
329
|
+
</g>
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
<!-- axis and box -->
|
|
333
|
+
<slot name="box"></slot>
|
|
334
|
+
|
|
335
|
+
</svg>
|
|
336
|
+
|
|
337
|
+
{#if !$isOk}
|
|
338
|
+
<p class="message_error">
|
|
339
|
+
Axes component was not properly initialized. <br />
|
|
340
|
+
Check that you defined axes limits and margins correctly.
|
|
341
|
+
</p>
|
|
342
|
+
{/if}
|
|
343
|
+
</div>
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
</div>
|
|
347
|
+
|
|
348
|
+
<style>
|
|
349
|
+
|
|
350
|
+
/* Plot (main container) */
|
|
351
|
+
.plot {
|
|
352
|
+
font-family: Arial, Helvetica, sans-serif;
|
|
353
|
+
|
|
354
|
+
display: grid;
|
|
355
|
+
grid-template-columns: min-content auto;
|
|
356
|
+
grid-template-rows: min-content auto min-content;
|
|
357
|
+
grid-template-areas:
|
|
358
|
+
". title"
|
|
359
|
+
"ylab axes"
|
|
360
|
+
". xlab";
|
|
361
|
+
|
|
362
|
+
box-sizing: border-box;
|
|
363
|
+
/* background: #fefefe; */
|
|
364
|
+
min-width: 100px;
|
|
365
|
+
min-height: 50px;
|
|
366
|
+
width: 100%;
|
|
367
|
+
height: 100%;
|
|
368
|
+
padding: 0;
|
|
369
|
+
margin: 0;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.plot_small {
|
|
373
|
+
font-size: 11px;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
.plot_medium {
|
|
377
|
+
font-size: 13px;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.plot_large {
|
|
381
|
+
font-size: 16px;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
.plot_xlarge {
|
|
385
|
+
font-size: 19px;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
.plot_error {
|
|
389
|
+
display: flex;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.plot_error > .axes-wrapper > svg {
|
|
393
|
+
display: none;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
.message_error {
|
|
397
|
+
font-size: 1.2em;
|
|
398
|
+
color: crimson;
|
|
399
|
+
padding: 1em;
|
|
400
|
+
box-sizing: border-box;
|
|
401
|
+
text-align: center;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/* Axes (coordinate system) */
|
|
405
|
+
.axes-wrapper {
|
|
406
|
+
grid-area: axes;
|
|
407
|
+
position:relative;
|
|
408
|
+
box-sizing: border-box;
|
|
409
|
+
display: flex;
|
|
410
|
+
width: 100%;
|
|
411
|
+
height: 100%;
|
|
412
|
+
padding: 0;
|
|
413
|
+
margin: 0;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
.axes {
|
|
417
|
+
display: block;
|
|
418
|
+
box-sizing: border-box;
|
|
419
|
+
position:absolute;
|
|
420
|
+
|
|
421
|
+
padding: 0;
|
|
422
|
+
margin: 0;
|
|
423
|
+
height: 100%;
|
|
424
|
+
width: 100%;
|
|
425
|
+
max-height: 100%;
|
|
426
|
+
max-width: 100%;
|
|
427
|
+
min-height: 100%;
|
|
428
|
+
min-width: 100%;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.axes-content {
|
|
432
|
+
padding: 0;
|
|
433
|
+
margin: 0;
|
|
434
|
+
height: 100%;
|
|
435
|
+
width: 100%;
|
|
436
|
+
max-height: 100%;
|
|
437
|
+
max-width: 100%;
|
|
438
|
+
min-height: 100%;
|
|
439
|
+
min-width: 100%;
|
|
440
|
+
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.axes__xlabel {
|
|
444
|
+
grid-area: xlab;
|
|
445
|
+
font-size: 1.0em;
|
|
446
|
+
font-weight: 600;
|
|
447
|
+
padding: 0.25em;
|
|
448
|
+
text-align: center;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
.axes__ylabel {
|
|
453
|
+
grid-area: ylab;
|
|
454
|
+
font-size: 1.0em;
|
|
455
|
+
font-weight: 600;
|
|
456
|
+
padding: 0.25em;
|
|
457
|
+
text-align: center;
|
|
458
|
+
vertical-align: middle;
|
|
459
|
+
display: flex;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
.axes__ylabel > span {
|
|
463
|
+
width: 1.5em;
|
|
464
|
+
line-height: 1.5em;
|
|
465
|
+
display: inline-block;
|
|
466
|
+
writing-mode: vertical-rl;
|
|
467
|
+
transform: rotate(180deg);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
.axes__title {
|
|
472
|
+
background: transparent;
|
|
473
|
+
grid-area: title;
|
|
474
|
+
|
|
475
|
+
font-size: 1.3em;
|
|
476
|
+
font-weight: bold;
|
|
477
|
+
line-height: 1.2em;
|
|
478
|
+
padding: 0.5em 0;
|
|
479
|
+
text-align: center;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/* Axis */
|
|
483
|
+
:global(.axis-labels) {
|
|
484
|
+
fill: #303030;
|
|
485
|
+
font-size: 0.95em;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/* Data labels */
|
|
489
|
+
:global(.labels) {
|
|
490
|
+
fill: #606060;
|
|
491
|
+
font-size: 0.90em;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
:global(.labels) {
|
|
495
|
+
visibility: hidden;
|
|
496
|
+
transition:visibility 0.25s linear, opacity 0.25s linear;
|
|
497
|
+
opacity: 0;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
</style>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
/****************************************************
|
|
3
|
+
* Axis *
|
|
4
|
+
* --------------------------------- *
|
|
5
|
+
* generic element for X- or Y-axis *
|
|
6
|
+
* !!! not for users !!! *
|
|
7
|
+
*****************************************************/
|
|
8
|
+
|
|
9
|
+
import { Colors } from '../Colors.js';
|
|
10
|
+
import AxisLines from './AxisLines.svelte';
|
|
11
|
+
import AxisTickLabels from './AxisTickLabels.svelte';
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
/*****************************************/
|
|
15
|
+
/* Input parameters */
|
|
16
|
+
/*****************************************/
|
|
17
|
+
|
|
18
|
+
export let tickLabels = []; // vector with labels for each tick
|
|
19
|
+
export let showGrid = false; // logical, show or not grid lines
|
|
20
|
+
export let las = 1; // rotation of tick labels
|
|
21
|
+
|
|
22
|
+
export let pos = 1; // position of tick labels
|
|
23
|
+
export let className = ""; // name of CSS class for the axis
|
|
24
|
+
export let grid = []; // array with grid coordinates
|
|
25
|
+
export let axisLine = []; // array with coordinates of main axis line
|
|
26
|
+
export let tickCoords = []; // array with axis tick coordinates
|
|
27
|
+
|
|
28
|
+
export let lineColor = Colors.DARKGRAY; // color of main axis line
|
|
29
|
+
export let gridColor = Colors.MIDDLEGRAY; // color of grid lines
|
|
30
|
+
export let textColor = Colors.DARKGRAY; // color of tick labels
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<g class="mdaplot__axis {className}">
|
|
34
|
+
<!-- grid -->
|
|
35
|
+
{#if showGrid }
|
|
36
|
+
<AxisLines lineCoords={grid} lineColor={gridColor} lineType={3} />
|
|
37
|
+
{/if}
|
|
38
|
+
|
|
39
|
+
<!-- main axis line -->
|
|
40
|
+
<AxisLines lineCoords={axisLine} lineColor={lineColor} lineType={1} />
|
|
41
|
+
|
|
42
|
+
<!-- ticks-->
|
|
43
|
+
<AxisLines lineCoords={tickCoords} lineColor={lineColor} lineType={1} />
|
|
44
|
+
|
|
45
|
+
<!-- labels -->
|
|
46
|
+
{#if tickCoords.length === 2 && tickLabels.length === tickCoords[1][0].length}
|
|
47
|
+
<AxisTickLabels {las} {pos} {tickCoords} {tickLabels} {textColor}/>
|
|
48
|
+
{/if}
|
|
49
|
+
</g>
|
|
50
|
+
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
/****************************************************
|
|
3
|
+
* Axis lines *
|
|
4
|
+
* -------------------- *
|
|
5
|
+
* generic component for axis lines (main, grid, ...)*
|
|
6
|
+
* !!! not for users !!! *
|
|
7
|
+
*****************************************************/
|
|
8
|
+
|
|
9
|
+
import { getContext } from 'svelte';
|
|
10
|
+
import { Colors } from '../Colors.js';
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
/*****************************************/
|
|
14
|
+
/* Input parameters */
|
|
15
|
+
/*****************************************/
|
|
16
|
+
|
|
17
|
+
export let lineCoords = []; // coordinates of start and end points of the lines
|
|
18
|
+
export let lineColor = Colors.DARKGRAY; // line color
|
|
19
|
+
export let lineType = 1; // line type
|
|
20
|
+
export let lineWidth = 1; // lined width (thickness)
|
|
21
|
+
export let className = ''; // CSS class name for the component
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
/*****************************************/
|
|
25
|
+
/* Component code */
|
|
26
|
+
/*****************************************/
|
|
27
|
+
|
|
28
|
+
// get axes context and adjust x margins
|
|
29
|
+
const axes = getContext('axes');
|
|
30
|
+
const scale = axes.scale;
|
|
31
|
+
const tX = axes.tX;
|
|
32
|
+
const tY = axes.tY;
|
|
33
|
+
|
|
34
|
+
// reactive variables for coordinates of axis lines
|
|
35
|
+
let x1, x2, y1, y2 = undefined;
|
|
36
|
+
$: if (lineCoords.length == 2) {
|
|
37
|
+
x1 = axes.transform(lineCoords[0][0], $tX.coords);
|
|
38
|
+
y1 = axes.transform(lineCoords[0][1], $tY.coords);
|
|
39
|
+
x2 = axes.transform(lineCoords[1][0], $tX.coords);
|
|
40
|
+
y2 = axes.transform(lineCoords[1][1], $tY.coords);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* styles for axis and grid lines */
|
|
44
|
+
$: lineStyleStr = `stroke:${lineColor};stroke-width: ${lineWidth}px;stroke-dasharray:${axes.LINE_STYLES[$scale][lineType-1]}`;
|
|
45
|
+
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
{#if x1 !== undefined && y1 !== undefined && x2 !== undefined && y2 !== undefined}
|
|
49
|
+
<g class={className}>
|
|
50
|
+
{#each x1 as v, i}
|
|
51
|
+
<line vector-effect="non-scaling-stroke" x1={x1[i]} x2={x2[i]} y1={y1[i]} y2={y2[i]} style={lineStyleStr} />
|
|
52
|
+
{/each}
|
|
53
|
+
</g>
|
|
54
|
+
{/if}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
/****************************************************
|
|
3
|
+
* AxisTickLabels *
|
|
4
|
+
* -------------------- *
|
|
5
|
+
* shows a series of tick labels along an axis *
|
|
6
|
+
* !!! not for users !!! *
|
|
7
|
+
*****************************************************/
|
|
8
|
+
|
|
9
|
+
import { getContext } from 'svelte';
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
/*****************************************/
|
|
13
|
+
/* Input parameters */
|
|
14
|
+
/*****************************************/
|
|
15
|
+
|
|
16
|
+
export let tickCoords; // array with tick coordinates
|
|
17
|
+
export let textColor; // color of tick labels
|
|
18
|
+
export let tickLabels; // array with tick labels
|
|
19
|
+
export let pos = 1; // position of tick labels
|
|
20
|
+
export let las = 1; // rotation of tick labels (1 - horizontal, 2 - vertical)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
/*****************************************/
|
|
24
|
+
/* Component code */
|
|
25
|
+
/*****************************************/
|
|
26
|
+
|
|
27
|
+
// get axes context and reactive variables needed to compute coordinates
|
|
28
|
+
const axes = getContext('axes');
|
|
29
|
+
const scale = axes.scale;
|
|
30
|
+
const tX = axes.tX;
|
|
31
|
+
const tY = axes.tY;
|
|
32
|
+
const isOk = axes.isOk;
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
let x, y = undefined;
|
|
36
|
+
let dx = 0, dy = 0, textAnchor;
|
|
37
|
+
|
|
38
|
+
// reactive calculations triggered by changes in coordinates and plot parameters
|
|
39
|
+
$: if ($isOk) {
|
|
40
|
+
x = axes.transform(tickCoords[1][0], $tX.coords);
|
|
41
|
+
y = axes.transform(tickCoords[1][1], $tY.coords);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// reactive calculations triggered by changes in scale
|
|
45
|
+
$: m = axes.LABELS_MARGIN[$scale];
|
|
46
|
+
|
|
47
|
+
// reactive calculations triggered by changes in pos
|
|
48
|
+
$: {
|
|
49
|
+
textAnchor = (['middle', 'middle', 'start', 'middle', 'end'])[pos];
|
|
50
|
+
dx = ([0, 0, m, 0, -m])[pos];
|
|
51
|
+
dy = ([0, m, 0, -m, 0])[pos];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// styles for the elements
|
|
55
|
+
$: textStyleStr = `dominant-baseline:middle;fill:${textColor};font-size:1em; text-anchor:middle;`;
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
{#if x !== undefined && y !== undefined}
|
|
59
|
+
<g class="tick_labels" style={textStyleStr} >
|
|
60
|
+
{#if las === 2 && pos === 4}
|
|
61
|
+
{#each x as v, i}
|
|
62
|
+
<text data-id={i} x={x[i]} y={y[i]} dx={0} dy={dx*1.25} transform={`rotate(-90, ${x[i]}, ${y[i]})`} text-anchor={"middle"}>{@html tickLabels[i]}</text>
|
|
63
|
+
{/each}
|
|
64
|
+
{:else if las === 2 && pos === 1}
|
|
65
|
+
{#each x as v, i}
|
|
66
|
+
<text data-id={i} x={x[i]} y={y[i]} dx={-dy/2} dy={0} transform={`rotate(-90, ${x[i]}, ${y[i]})`} text-anchor={"end"}>{@html tickLabels[i]}</text>
|
|
67
|
+
{/each}
|
|
68
|
+
{:else}
|
|
69
|
+
{#each x as v, i}
|
|
70
|
+
<text data-id={i} x={x[i]} y={y[i]} dx={dx} dy={dy} text-anchor={textAnchor}>{@html tickLabels[i]}</text>
|
|
71
|
+
{/each}
|
|
72
|
+
{/if}
|
|
73
|
+
</g>
|
|
74
|
+
{/if}
|
|
75
|
+
|
|
76
|
+
<style>
|
|
77
|
+
text, text > :global(tspan) {
|
|
78
|
+
cursor: default;
|
|
79
|
+
user-select: none;
|
|
80
|
+
dominant-baseline: middle;
|
|
81
|
+
}
|
|
82
|
+
</style>
|