@massimo-cassandro/minimo 1.2.0 → 1.3.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/charts/README.md +42 -0
- package/charts/TODO.md +2 -0
- package/charts/index.js +21 -0
- package/charts/src/bars.js +377 -0
- package/charts/src/goal.js +311 -0
- package/charts/src/helpers/cartesian-axis.js +254 -0
- package/charts/src/helpers/chart-utils.js +174 -0
- package/charts/src/helpers/core.js +25 -0
- package/charts/src/helpers/create-svg-canvas.js +25 -0
- package/charts/src/helpers/create-text-element.js +54 -0
- package/charts/src/helpers/default-chart-colors.js +17 -0
- package/charts/src/helpers/legenda.js +96 -0
- package/charts/src/helpers/parse-std-axis-opts.js +95 -0
- package/charts/src/helpers/svg-text-to-path.js +45 -0
- package/charts/src/lines.js +383 -0
- package/charts/src/rating-display/debug-tools.js +124 -0
- package/charts/src/rating-display/index.js +849 -0
- package/charts/src/rating-display/ratindDisplayAnimation.js +56 -0
- package/charts/src/rating-display/ratingDisplayUtils.js +102 -0
- package/package.json +15 -11
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raccolta di utilità condivise per la gestione dei grafici.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Tronca un numero a un numero specifico di decimali.
|
|
7
|
+
* @param {number} number - Il numero da troncare.
|
|
8
|
+
* @param {number} [decimals=4] - Il numero di decimali da mantenere.
|
|
9
|
+
* @returns {number} Il numero troncato.
|
|
10
|
+
*/
|
|
11
|
+
export function truncateDecimal(number, decimals = 4) {
|
|
12
|
+
return +parseFloat((number).toFixed(decimals));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Converte un angolo da gradi in radianti.
|
|
17
|
+
* @param {number} degrees_angle - L'angolo in gradi.
|
|
18
|
+
* @returns {number} L'angolo in radianti.
|
|
19
|
+
*/
|
|
20
|
+
export const toRadians = degrees_angle => (degrees_angle * Math.PI) / 180.0;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Trasforma le coordinate polari (centro + raggio) in coordinate cartesiane (x, y).
|
|
24
|
+
* @param {number} degrees_angle - L'angolo in gradi.
|
|
25
|
+
* @param {number} cx - Coordinata X del centro.
|
|
26
|
+
* @param {number} cy - Coordinata Y del centro.
|
|
27
|
+
* @param {number} circleRadius - Il raggio del cerchio.
|
|
28
|
+
* @returns {{x: number, y: number}} Le coordinate cartesiane risultanti.
|
|
29
|
+
*/
|
|
30
|
+
export function polarToCartesian(degrees_angle, cx, cy, circleRadius) {
|
|
31
|
+
const radians = toRadians(degrees_angle);
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
x: truncateDecimal(Math.abs(cx + circleRadius * Math.cos(radians))),
|
|
35
|
+
y: truncateDecimal(Math.abs(cy - circleRadius * Math.sin(radians)))
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// come il precedente, ma restituisce una stringa
|
|
41
|
+
export function polarToCartesianString(degrees_angle, cx, cy, circleRadius) {
|
|
42
|
+
const {x,y} = polarToCartesian(degrees_angle, cx, cy, circleRadius);
|
|
43
|
+
|
|
44
|
+
return `${x},${y}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Concatena i nomi delle classi filtrando i valori falsy.
|
|
49
|
+
* @param {...any} args - I nomi delle classi o condizioni.
|
|
50
|
+
* @returns {string} Una stringa di nomi di classi separati da spazio.
|
|
51
|
+
*/
|
|
52
|
+
export function classnames(...args) {
|
|
53
|
+
return args.filter(Boolean).join(' ');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Concatena i nomi delle classi filtrando i valori falsy; restituisce null se il risultato è una stringa vuota.
|
|
58
|
+
* @param {...any} args - I nomi delle classi o condizioni.
|
|
59
|
+
* @returns {string|null} Una stringa di nomi di classi o null.
|
|
60
|
+
*/
|
|
61
|
+
export function classnamesNull(...args) {
|
|
62
|
+
return args.filter(Boolean).join(' ') || null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Calcola le coordinate per un elemento `path` di tipo arco (arc).
|
|
67
|
+
* Lo `start_angle` è il punto di inizio e `percentage` è la lunghezza percentuale dell'arco rispetto a `max_angle`.
|
|
68
|
+
*
|
|
69
|
+
* @param {Object} options - Opzioni di configurazione.
|
|
70
|
+
* @param {number} [options.start_angle=0] - Angolo di partenza in gradi.
|
|
71
|
+
* @param {number|null} options.percentage - Lunghezza dell'arco in percentuale (da 0 a 1).
|
|
72
|
+
* @param {number} [options.max_arc=360] - L'estensione massima dell'arco in gradi.
|
|
73
|
+
* @param {Object} options.center - Coordinate del centro.
|
|
74
|
+
* @param {number|null} options.center.x - Coordinata X del centro.
|
|
75
|
+
* @param {number|null} options.center.y - Coordinata Y del centro.
|
|
76
|
+
* @param {number|null} options.radius - Raggio dell'arco.
|
|
77
|
+
* @param {boolean} [options.clockwise=true] - Direzione oraria se true.
|
|
78
|
+
* @returns {{arc_length: number, start: {x: number, y: number}, end: {x: number, y: number}}} Coordinate di inizio, fine e lunghezza.
|
|
79
|
+
*/
|
|
80
|
+
export function calcArcCoords(options) {
|
|
81
|
+
const default_options = {
|
|
82
|
+
start_angle: 0,
|
|
83
|
+
percentage: null,
|
|
84
|
+
max_arc: 360,
|
|
85
|
+
center: { x: null, y: null },
|
|
86
|
+
radius: null,
|
|
87
|
+
clockwise: true
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
options = { ...default_options, ...options };
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
if (options.percentage == null ||
|
|
94
|
+
options.center?.x == null ||
|
|
95
|
+
options.center?.y == null ||
|
|
96
|
+
options.start_angle == null ||
|
|
97
|
+
options.radius == null ||
|
|
98
|
+
options.max_arc == null
|
|
99
|
+
) {
|
|
100
|
+
throw 'Errore parametri in `calcArcCoords`';
|
|
101
|
+
}
|
|
102
|
+
} catch (e) {
|
|
103
|
+
// eslint-disable-next-line no-console
|
|
104
|
+
console.error(e);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Angolo dell'arco corrispondente alla percentuale data
|
|
108
|
+
let arc_length = options.max_arc * options.percentage,
|
|
109
|
+
end_angle = options.start_angle + (options.clockwise ? -arc_length : arc_length);
|
|
110
|
+
|
|
111
|
+
if (options.percentage === 1) {
|
|
112
|
+
end_angle -= .001; // Per evitare errori di arrotondamento nel disegno dell'arco completo
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
arc_length: arc_length,
|
|
117
|
+
start: polarToCartesian(options.start_angle, options.center, options.radius),
|
|
118
|
+
end: polarToCartesian(end_angle, options.center, options.radius),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Calcola l'attributo `d` per un elemento SVG path che rappresenta un arco.
|
|
124
|
+
* Accetta le stesse opzioni di `calcArcCoords`.
|
|
125
|
+
* * @param {Object} options - Opzioni per il calcolo dell'arco.
|
|
126
|
+
* @returns {string} La stringa da utilizzare nell'attributo `d` del path SVG.
|
|
127
|
+
*/
|
|
128
|
+
export function calcAttrD(options) {
|
|
129
|
+
const points = calcArcCoords(options);
|
|
130
|
+
|
|
131
|
+
return [
|
|
132
|
+
`M${points.start.x},${points.start.y}`,
|
|
133
|
+
`A${options.radius},${options.radius}`,
|
|
134
|
+
0, // xAxisRotation
|
|
135
|
+
(points.arc_length <= 180 ? 0 : 1), // largeArcFlag
|
|
136
|
+
options.clockwise && points.arc_length < 360 ? 1 : 0, // sweepFlag
|
|
137
|
+
`${points.end.x},${points.end.y}`
|
|
138
|
+
].join(' ');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Recupera un elemento DOM da un selettore stringa o lo restituisce se è già un elemento.
|
|
143
|
+
* @param {string|Element|null} container - Il selettore CSS o l'elemento DOM.
|
|
144
|
+
* @returns {Element|null} L'elemento DOM o null.
|
|
145
|
+
*/
|
|
146
|
+
export function getElementFromContainer(container) {
|
|
147
|
+
if (!container) {
|
|
148
|
+
return null;
|
|
149
|
+
} else if (typeof container === 'string') {
|
|
150
|
+
return document.querySelector(container);
|
|
151
|
+
} else if (container instanceof Element) {
|
|
152
|
+
return container;
|
|
153
|
+
} else {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Analizza il contenitore e restituisce informazioni sulle sue dimensioni, eseguendo alcune azioni di base.
|
|
160
|
+
* @param {Object} params - Parametri di input.
|
|
161
|
+
* @param {string|Element|null} [params.container=null] - Il contenitore del grafico.
|
|
162
|
+
* @param {number|null} [params.width=null] - Larghezza predefinita (opzionale).
|
|
163
|
+
* @param {number|null} [params.height=null] - Altezza predefinita (opzionale).
|
|
164
|
+
* @returns {[Element|null, number|null, number|null]} Un array contenente l'elemento, la larghezza e l'altezza.
|
|
165
|
+
*/
|
|
166
|
+
export function parseContainer({ container = null, width = null, height = null }) {
|
|
167
|
+
const containerElement = getElementFromContainer(container);
|
|
168
|
+
|
|
169
|
+
// Calcolo delle dimensioni del grafico
|
|
170
|
+
width = width || (containerElement ? containerElement.clientWidth : null);
|
|
171
|
+
height = height || (containerElement ? containerElement.clientHeight : null);
|
|
172
|
+
|
|
173
|
+
return [containerElement, width, height];
|
|
174
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { default_colors } from './default-chart-colors.js';
|
|
2
|
+
import { createSvgCanvas } from './create-svg-canvas.js';
|
|
3
|
+
|
|
4
|
+
export class MinimoCharts {
|
|
5
|
+
constructor(customParams = {}) {
|
|
6
|
+
this.defaults = {
|
|
7
|
+
colors: customParams.defaults?.colors ?? Object.values(default_colors),
|
|
8
|
+
font: {
|
|
9
|
+
size: 14,
|
|
10
|
+
weight: 400,
|
|
11
|
+
fill: '#999',
|
|
12
|
+
...customParams.defaults?.font
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
this.debug = customParams.debug ?? false;
|
|
18
|
+
this.createSvgCanvas = (customParams.createSvgCanvas ?? createSvgCanvas).bind(this);
|
|
19
|
+
|
|
20
|
+
this.sharedParams = {}; // per condivisione parametri tra grafici nella stessa applicazione
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
setSharedParams(key, value = null) { this.sharedParams[key] = value; }
|
|
24
|
+
getSharedParams(key) { return this.sharedParams[key]; }
|
|
25
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { SVG } from '@svgdotjs/svg.js';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
export function createSvgCanvas(container, debug = false) {
|
|
5
|
+
|
|
6
|
+
try {
|
|
7
|
+
|
|
8
|
+
const chartInstance = this;
|
|
9
|
+
|
|
10
|
+
if(!container) {
|
|
11
|
+
throw 'Missing container';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const svgCanvas = SVG().addTo(container);
|
|
15
|
+
|
|
16
|
+
if(debug || chartInstance.debug) {
|
|
17
|
+
svgCanvas.addClass('debug');
|
|
18
|
+
}
|
|
19
|
+
return svgCanvas;
|
|
20
|
+
|
|
21
|
+
} catch(e) {
|
|
22
|
+
console.error( 'Charts → createSvgCanvas', e ); // eslint-disable-line
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { textToSvgPath } from './svg-text-to-path.js';
|
|
2
|
+
import { SVG, Text } from '@svgdotjs/svg.js';
|
|
3
|
+
|
|
4
|
+
export async function createTextEl (chartInstance, {
|
|
5
|
+
text,
|
|
6
|
+
textFill = '#000',
|
|
7
|
+
fontAttr = {},
|
|
8
|
+
fontFilePath = null,
|
|
9
|
+
parentElement = null, // se null viene restituito l'elemento generato (text o path)
|
|
10
|
+
textToPath = false, // bypassa l'impostazione globale, per usi particolari
|
|
11
|
+
}) {
|
|
12
|
+
|
|
13
|
+
let textEl;
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
if(text == null) {
|
|
17
|
+
throw '`createTextEl`: missing `text`';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Utilizza i defaults dell'istanza (grazie al bind nel costruttore)
|
|
21
|
+
// Se fontAttr è nullo, usa un oggetto vuoto per evitare errori di spread
|
|
22
|
+
fontAttr = { ...chartInstance.defaults.font, ...(fontAttr ?? {}), fill: textFill };
|
|
23
|
+
|
|
24
|
+
if(chartInstance.textToPath || textToPath) {
|
|
25
|
+
|
|
26
|
+
if(!fontFilePath) {
|
|
27
|
+
throw `‘createTextEl’: missing ‘fontFilePath’ for text ‘${text}’`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Qui recuperiamo sia il tracciato che la stringa SVG (come nel tuo originale)
|
|
31
|
+
const { pathData, pathElementString } = await textToSvgPath(fontFilePath, text, fontAttr.size ?? 16);
|
|
32
|
+
|
|
33
|
+
if(parentElement) {
|
|
34
|
+
textEl = parentElement.path(pathData);
|
|
35
|
+
} else {
|
|
36
|
+
// Nota: utilizzando path con pathData direttamente su SVG() a volte dà errore in svg.js 3.x
|
|
37
|
+
textEl = SVG(pathElementString);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
textEl.attr({ fill: textFill });
|
|
41
|
+
|
|
42
|
+
} else {
|
|
43
|
+
// Modalità testo standard (non tracciato)
|
|
44
|
+
if(parentElement) {
|
|
45
|
+
textEl = parentElement.plain(text);
|
|
46
|
+
} else {
|
|
47
|
+
textEl = new Text().plain(text);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
textEl.font(fontAttr).attr({ lengthAdjust: 'spacingAndGlyphs' });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return textEl;
|
|
54
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
|
|
2
|
+
export const default_colors = {
|
|
3
|
+
azzurro : '#2387e2',
|
|
4
|
+
rosaScuro : '#fc1484',
|
|
5
|
+
giallo : '#f5a623',
|
|
6
|
+
verdeAcqua : '#6dbebf',
|
|
7
|
+
lavanda : '#9894fd',
|
|
8
|
+
carminio : '#af373e',
|
|
9
|
+
verdeElettrico : '#54f25a',
|
|
10
|
+
verdeBlu : '#167bac',
|
|
11
|
+
acqua : '#00e6fb',
|
|
12
|
+
bluElettrico : '#0b16e9',
|
|
13
|
+
oliva : '#9dbf6d',
|
|
14
|
+
violaElettrico : '#c614fc',
|
|
15
|
+
lime : '#d9f440',
|
|
16
|
+
rosaChiaro : '#fea4f2',
|
|
17
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { G } from '@svgdotjs/svg.js';
|
|
2
|
+
import { createTextEl } from './create-text-element.js';
|
|
3
|
+
|
|
4
|
+
export async function legenda(chartInstance, {
|
|
5
|
+
|
|
6
|
+
/** altezza legenda */
|
|
7
|
+
height = 20,
|
|
8
|
+
|
|
9
|
+
/** spazio tra gli elementi della legenda */
|
|
10
|
+
elements_gap = 20,
|
|
11
|
+
|
|
12
|
+
/** dimensioni delle forme della legenda */
|
|
13
|
+
lines_width = 20,
|
|
14
|
+
circles_radius = 20,
|
|
15
|
+
|
|
16
|
+
/** attributi svg.js dei font del testo della legenda (null utilizza default) */
|
|
17
|
+
fontAttrs = null,
|
|
18
|
+
|
|
19
|
+
/** percorso font file per textToPath */
|
|
20
|
+
fontFilePath = null,
|
|
21
|
+
|
|
22
|
+
/** elementi della legenda */
|
|
23
|
+
items = [],
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
}) {
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
elementi di default di items
|
|
30
|
+
*/
|
|
31
|
+
const items_defaults = {
|
|
32
|
+
|
|
33
|
+
shape: 'line', // line || circle
|
|
34
|
+
attrs: {}, // attributi svg.js di shape
|
|
35
|
+
text: '__label__', // testo della voce
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
if(!items.length) {
|
|
42
|
+
throw 'nessuna voce in `items`';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
items = items.map(i => ({...items_defaults, ...i}));
|
|
46
|
+
|
|
47
|
+
const legendaGroup = new G();
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
let x = 0; // posizionamento x
|
|
51
|
+
for await (const voce of items) {
|
|
52
|
+
|
|
53
|
+
const thisGroup = legendaGroup.group();
|
|
54
|
+
|
|
55
|
+
if(voce.shape === 'line') {
|
|
56
|
+
|
|
57
|
+
thisGroup.line(
|
|
58
|
+
x, height / 2,
|
|
59
|
+
x + lines_width, height / 2
|
|
60
|
+
)
|
|
61
|
+
.attr(voce.attrs);
|
|
62
|
+
x = x + lines_width;
|
|
63
|
+
|
|
64
|
+
} else if (voce.shape === 'circle') {
|
|
65
|
+
thisGroup.circle(circles_radius)
|
|
66
|
+
.attr(voce.attrs)
|
|
67
|
+
.move(x, (height - circles_radius) / 2);
|
|
68
|
+
|
|
69
|
+
x = x + circles_radius;
|
|
70
|
+
|
|
71
|
+
} else {
|
|
72
|
+
throw `parametro 'shape' non corretto in una delle voci (${voce.shape})`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// testo
|
|
76
|
+
const labelEl = await createTextEl(chartInstance, {
|
|
77
|
+
text: voce.text,
|
|
78
|
+
textFill: fontAttrs.fill?? '#000',
|
|
79
|
+
fontAttr: fontAttrs,
|
|
80
|
+
fontFilePath: fontFilePath,
|
|
81
|
+
parentElement: thisGroup
|
|
82
|
+
}),
|
|
83
|
+
labelBbox = labelEl.bbox();
|
|
84
|
+
|
|
85
|
+
labelEl.move(x + 5, (height - labelBbox.height) / 2);
|
|
86
|
+
|
|
87
|
+
x = legendaGroup.bbox().width + elements_gap;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return legendaGroup;
|
|
91
|
+
|
|
92
|
+
} catch(e) {
|
|
93
|
+
console.error( 'MinimoCharts → legenda', e ); // eslint-disable-line
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// prima elaborazione e controlli delle impostazioni comuni a tutti i grafici con assi
|
|
2
|
+
// NB non nel prototypoe della classe, va importato dove richiesto
|
|
3
|
+
/*
|
|
4
|
+
l'argomento params è un oggetto che deve contenere i parametri con cui vengono effettuati i vari calcoli:
|
|
5
|
+
width, height, ecc... (vedi default)
|
|
6
|
+
|
|
7
|
+
values è un array flat contentente tutti i valori da rappresentare
|
|
8
|
+
|
|
9
|
+
restituisce un offetto `parsed` con i nuovi parametri calcolati
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export function parseStdAxisOpts(params = {} ) {
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
const parsed = {};
|
|
19
|
+
|
|
20
|
+
// default
|
|
21
|
+
params = {
|
|
22
|
+
max_value: null,
|
|
23
|
+
min_value: null,
|
|
24
|
+
values: [], // array flat dei valori per il calcolo di maxValues e minValues, qualora fossero null
|
|
25
|
+
legenda: null,
|
|
26
|
+
padding: 0, // spazio intorno ai bordi del grafico
|
|
27
|
+
width: null, // width e height non possono essere null, vanno verificate prima
|
|
28
|
+
height: null,
|
|
29
|
+
eti_asseX_height: null, // non può essere null, va verificato prima
|
|
30
|
+
eti_asseY_width: null, // non può essere null, va verificato prima
|
|
31
|
+
stepY_count: null, // non può essere null, va verificato prima
|
|
32
|
+
stepX_count: null, // non può essere null, va verificato prima
|
|
33
|
+
ticksLength: 0,
|
|
34
|
+
|
|
35
|
+
...params
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
['width', 'height', 'eti_asseX_height', 'eti_asseY_width', 'stepY_count', 'stepX_count', 'ticksLength']
|
|
39
|
+
.forEach(key => {
|
|
40
|
+
if(params[key] == null) {
|
|
41
|
+
throw `‘${key}’ non può essere null`;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
// =>> PARAMETRI calcolati
|
|
48
|
+
//---------------
|
|
49
|
+
|
|
50
|
+
// calcolo valore massimo dei dati se non già impostato in precedenza
|
|
51
|
+
if(params.max_value == null) {
|
|
52
|
+
parsed.max_value = Math.max(0, ...params.values.flat());
|
|
53
|
+
} else {
|
|
54
|
+
parsed.max_value = params.max_value;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// calcolo valore minimo dei dati se non già impostato in precedenza
|
|
58
|
+
if(params.min_value == null) {
|
|
59
|
+
parsed.min_value = Math.min(parsed.max_value, ...params.values.flat().filter(v => v != null));
|
|
60
|
+
} else {
|
|
61
|
+
parsed.min_value = params.min_value;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const legenda_height = params.legenda.height?? 0;
|
|
65
|
+
const gap_legenda = legenda_height? 10 : 0; // spazio tra legenda e grafico
|
|
66
|
+
|
|
67
|
+
// =>> chart area
|
|
68
|
+
// coordinate e dimensioni del rettangolo interno all'svg entro cui viene disegnato il grafico
|
|
69
|
+
// i lati sx e inferiore corrispondono agli assi
|
|
70
|
+
parsed.chart_area = {
|
|
71
|
+
top : legenda_height + gap_legenda + params.padding, // coordinata y superiore
|
|
72
|
+
right : params.width - params.padding, // coordinata x dx
|
|
73
|
+
bottom : params.height - params.eti_asseX_height - params.ticksLength - params.padding, // coordinata y inferiore
|
|
74
|
+
left : params.eti_asseY_width + params.ticksLength + params.padding // coordinata x sx
|
|
75
|
+
};
|
|
76
|
+
parsed.chart_area.width = parsed.chart_area.right - parsed.chart_area.left;
|
|
77
|
+
parsed.chart_area.height = parsed.chart_area.bottom - parsed.chart_area.top;
|
|
78
|
+
|
|
79
|
+
// valore di ogni step asse y
|
|
80
|
+
// (usato per il calcolo delle etichette dell'asse Y)
|
|
81
|
+
parsed.stepY_value = (parsed.max_value - parsed.min_value) / params.stepY_count;
|
|
82
|
+
|
|
83
|
+
// lunghezza di ogni step asse y
|
|
84
|
+
parsed.stepY_size = parsed.chart_area.height / params.stepY_count;
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
parsed.stepX_size = parsed.chart_area.width / (params.stepX_count - 1);
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
return parsed;
|
|
91
|
+
|
|
92
|
+
} catch(e) {
|
|
93
|
+
console.error( 'MinimoCharts → parseStdOpts', e ); // eslint-disable-line
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// https://stackblitz.com/edit/vitejs-vite-acphht?file=main.js
|
|
2
|
+
|
|
3
|
+
import opentype from 'opentype.js';
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
// Example usage:
|
|
7
|
+
const fontUrl = './Rubik Moonrocks.ttf';
|
|
8
|
+
const text = 'Hello, World!';
|
|
9
|
+
const fontSize = 16; // in points
|
|
10
|
+
textToSvgPath(fontUrl, text, fontSize)
|
|
11
|
+
.then(({ svgPathData, svg }) => {
|
|
12
|
+
//console.log(svgPathData); // SVG path data string
|
|
13
|
+
console.log(svg); // SVG element as a string
|
|
14
|
+
})
|
|
15
|
+
.catch((err) => console.error(err));
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
// Function to load the font and convert text to SVG path
|
|
19
|
+
// vedi ada-frontend/ada-charts/test/textToPath-test.mjs
|
|
20
|
+
|
|
21
|
+
export async function textToSvgPath(fontUrl, text, fontSize) {
|
|
22
|
+
try {
|
|
23
|
+
const font = await opentype.load(fontUrl);
|
|
24
|
+
|
|
25
|
+
// Define the x and y coordinates where the text should start
|
|
26
|
+
const x = 0;
|
|
27
|
+
const y = 50;
|
|
28
|
+
|
|
29
|
+
// Get a path representing the text
|
|
30
|
+
const path = font.getPath(String(text), x, y, fontSize);
|
|
31
|
+
|
|
32
|
+
// Convert the path to an SVG path data string
|
|
33
|
+
const pathData = path.toPathData();
|
|
34
|
+
|
|
35
|
+
// Alternatively, convert the path to an SVG
|
|
36
|
+
const pathElementString = path.toSVG();
|
|
37
|
+
|
|
38
|
+
return { pathData, pathElementString };
|
|
39
|
+
|
|
40
|
+
} catch (err) {
|
|
41
|
+
// eslint-disable-next-line no-console
|
|
42
|
+
console.error('textToSvgPath - font loading error:', err);
|
|
43
|
+
throw err;
|
|
44
|
+
}
|
|
45
|
+
}
|