getSVG1() - Campionamento adattivo delle curve BéziergetSVG1()
di CostyCNC per ottenere un percorso G-code che segue davvero la curva,
invece di collegare linearmente i punti di controllo.
La funzione getSVG1() di CostyCNC converte i contorni generati
da Potrace in G-code. Tuttavia, la versione attuale ha un limite:
prende i 3 punti di controllo della Bézier e li collega con 3 comandi
G01 lineari.
Geometricamente, questo non è la curva: è una spezzata che passa per i punti di controllo. La curva reale passa accanto a quei punti, non attraverso di essi.
Invece di usare un numero fisso di punti (es. 8), si decide in base alla lunghezza del segmento: più il segmento è lungo, più punti servono.
La formula è:
N = clamp( ceil(lunghezza / tolleranza), N_min, N_max )
Dove:
lunghezza = lunghezza approssimata del segmento (poligono di controllo)tolleranza = errore massimo accettabile in mm (es. 0.2 mm)N_min = numero minimo di punti (es. 4)N_max = numero massimo di punti (es. 24)
Invece di calcolare la formula di de Casteljau a ogni punto
(con potenze e moltiplicazioni), si possono usare
coefficienti precalcolati per ogni valore di t.
| Approccio | Calcoli per punto | Velocità | Precisione |
|---|---|---|---|
| Formula di de Casteljau | 4 moltiplicazioni + 3 somme + 3 potenze | Lenta | Alta |
| Tabella precalcolata | 4 moltiplicazioni + 3 somme | Veloce | Alta |
mt*mt*mt,
t*t*t), che sono la parte più costosa. Con la tabella fai solo:
x = c0*P0x + c1*P1x + c2*P2x + c3*P3x
c0, c1, c2, c3 sono già pronti.
La lunghezza approssimata della Bézier si stima con la distanza tra i punti di controllo (poligono di controllo):
L ≈ |P0-P1| + |P1-P2| + |P2-P3|
Questa è una sovrastima della lunghezza reale, ma è veloce da calcolare e va benissimo per decidere N.
Sostituisci la funzione bezier(i) dentro getSVG1()
con questa versione, che usa la tabella precalcolata:
// Tabella dei coefficienti per t = 1/8, 2/8, ..., 7/8
// (valori arrotondati a 4 decimali)
var coeffs8 = [
[0.6699, 0.2871, 0.0410, 0.0020],
[0.4219, 0.4219, 0.1406, 0.0156],
[0.2441, 0.4395, 0.2637, 0.0527],
[0.1250, 0.3750, 0.3750, 0.1250],
[0.0527, 0.2637, 0.4395, 0.2441],
[0.0156, 0.1406, 0.4219, 0.4219],
[0.0020, 0.0410, 0.2871, 0.6699]
];
function bezier(i) {
var b = "";
// P0 = punto finale del segmento precedente
var P0x, P0y;
if (i === 0) {
P0x = curve.c[(curve.n - 1) * 3 + 2].x * sizex;
P0y = curve.c[(curve.n - 1) * 3 + 2].y * sizey;
} else {
P0x = curve.c[(i - 1) * 3 + 2].x * sizex;
P0y = curve.c[(i - 1) * 3 + 2].y * sizey;
}
var P1x = curve.c[i * 3 + 0].x * sizex;
var P1y = curve.c[i * 3 + 0].y * sizey;
var P2x = curve.c[i * 3 + 1].x * sizex;
var P2y = curve.c[i * 3 + 1].y * sizey;
var P3x = curve.c[i * 3 + 2].x * sizex;
var P3y = curve.c[i * 3 + 2].y * sizey;
// --- Lunghezza approssimata (poligono di controllo) ---
var d01 = Math.sqrt((P1x-P0x)*(P1x-P0x) + (P1y-P0y)*(P1y-P0y));
var d12 = Math.sqrt((P2x-P1x)*(P2x-P1x) + (P2y-P1y)*(P2y-P1y));
var d23 = Math.sqrt((P3x-P2x)*(P3x-P2x) + (P3y-P2y)*(P3y-P2y));
var lunghezza = d01 + d12 + d23;
// --- Configurazione ---
var N_base = 8; // valore di riferimento
var N_min = 4; // minimo assoluto
var N_max = 24; // MASSIMO ASSOLUTO
var tolleranza = 0.2; // mm (regola in base alla tua macchina)
// --- Calcola N in base alla lunghezza ---
var N;
if (lunghezza < 1.0) {
N = N_min; // curve molto corte: 4 punti
} else if (lunghezza < 5.0) {
N = N_base; // curve medie: 8 punti
} else {
N = Math.ceil(lunghezza / tolleranza);
if (N < N_base) N = N_base;
if (N > N_max) N = N_max;
}
// --- Se N non è 8, usa la formula di de Casteljau ---
// (per N=8 usiamo la tabella precalcolata)
if (N === 8) {
// Tabella precalcolata (più veloce)
for (var k = 0; k < coeffs8.length; k++) {
var c = coeffs8[k];
var x = c[0]*P0x + c[1]*P1x + c[2]*P2x + c[3]*P3x;
var y = c[0]*P0y + c[1]*P1y + c[2]*P2y + c[3]*P3y;
b += 'G01 X' + x.toFixed(3) + ' Y' + y.toFixed(3) + '\n';
}
} else {
// Formula di de Casteljau (per N diverso da 8)
for (var j = 1; j <= N; j++) {
var t = j / N;
var mt = 1 - t;
var x = mt*mt*mt * P0x + 3*mt*mt*t * P1x + 3*mt*t*t * P2x + t*t*t * P3x;
var y = mt*mt*mt * P0y + 3*mt*mt*t * P1y + 3*mt*t*t * P2y + t*t*t * P3y;
b += 'G01 X' + x.toFixed(3) + ' Y' + y.toFixed(3) + '\n';
}
}
// Punto finale
b += 'G01 X' + P3x.toFixed(3) + ' Y' + P3y.toFixed(3) + '\n';
return b;
}
| Tolleranza | Effetto | Quando usarla |
|---|---|---|
0.05 mm | Altissima fedeltà, file grande | Lavori di precisione |
0.1 mm | Ottima fedeltà | Lavori normali |
0.2 mm | Buona fedeltà, file compatto | Filo caldo (consigliato) |
0.5 mm | Fedeltà media, file piccolo | Bozze, lavori grezzi |
1.0 mm | Bassa fedeltà | Solo per test |
0.2 mm è il valore ideale:
la curva è fluida, il file non esplode, e la macchina non ha problemi a
seguire i punti.
| Lunghezza curva | N punti | Note |
|---|---|---|
| < 1 mm | 4 | Minimo (curve molto corte) |
| 1-5 mm | 8 | Base (curve medie) - usa tabella |
| 5-10 mm | 10-24 | Adattivo (formula) |
| > 10 mm | 24 | Massimo (curve lunghe) |
| Approccio | Punti per curva | Adattivo? | Velocità |
|---|---|---|---|
| 8 punti fissi | 8 sempre | ❌ No | Media |
| Tabella + adattivo | 4-24 | ✅ Sì | Alta |
| Formula pura | 4-24 | ✅ Sì | Bassa |
Se vuoi il massimo, puoi combinare lunghezza e curvatura:
// Stima della curvatura: quanto si discosta il punto medio della curva
// dalla corda P0-P3
var mx = 0.125*P0x + 0.375*P1x + 0.375*P2x + 0.125*P3x;
var my = 0.125*P0y + 0.375*P1y + 0.375*P2y + 0.125*P3y;
var cx = (P0x + P3x) / 2;
var cy = (P0y + P3y) / 2;
var curvatura = Math.sqrt((mx-cx)*(mx-cx) + (my-cy)*(my-cy));
// Punti in base a lunghezza E curvatura (sempre max 24)
var N = Math.ceil((lunghezza + curvatura * 4) / tolleranza);
if (N < 4) N = 4;
if (N > 24) N = 24;
Questo aggiunge punti extra solo dove la curva è più "curva", ottimizzando ancora di più, ma senza mai superare i 24 punti.
bezier(i) in getSVG1() con la versione
basata su tabella precalcolata (per N=8) e
formula di de Casteljau (per N≠8).
tolleranza in base alle tue
esigenze (consigliato 0.2 mm per il filo caldo).