2011-07-08 14 views
13

Sto cercando una soluzione per convertire un tracciato SVG disegnato a mano libera, composto da molti segmenti LineTo, in uno più fluido.Come lisciare un percorso SVG disegnato a mano libera?

La lingua preferita sarebbe JavaScript, ma qualsiasi consiglio è benvenuto.

+0

Prova utilizzando curve http://www.w3.org/TR/SVG/paths.html#PathDataCurveCommands – Gerben

risposta

29

Prima di tutto, consiglierei di utilizzare una buona libreria grafica, come Raphael. Semplificherebbe il processo di usare effettivamente javascript per eseguire il disegno.

Un metodo molto semplice di levigatura consiste nel convertire tutti i comandi di lineto con comandi di curveto equivalenti e calcolare alcuni punti di controllo in base agli angoli di ciascun segmento di linea. Ad esempio,

<svg width="1000" height="1000" version="1.1" 
xmlns="http://www.w3.org/2000/svg"> 

<path d=" 
M250 150 
L150 350 
L350 350 
L250 150 
" /> 

</svg> 

diventa

<svg width="1000" height="1000" version="1.1" 
xmlns="http://www.w3.org/2000/svg"> 

<path d=" 
M250 150 
C250 150 150 350 150 350 
C150 350 350 350 350 350 
C350 350 250 150 250 150 
" /> 

</svg> 

Entrambi questi dovrebbero disegnare un triangolo equilatero

Il passo successivo sarebbe quello di calcolare la posizione dei punti di controllo. In generale, si desidera che i punti di controllo su entrambi i lati di un angolo liscio cadano su una linea immaginaria che passa attraverso il vertice. Nel caso del punto superiore del triangolo equilatero, questa sarebbe una linea orizzontale. Dopo un po 'di manipolazione, si può ottenere qualcosa di simile:

<svg width="1000" height="1000" version="1.1" 
xmlns="http://www.w3.org/2000/svg"> 

<path d=" 
M250 150 
C230 150 140 333 150 350 
C160 367 340 367 350 350 
C360 333 270 150 250 150 
" /> 

</svg> 

La parte difficile sta calcolando i punti di controllo, ma che si trasforma in non molto più di un semplice problema di trigonometria. Come ho detto in precedenza, l'obiettivo qui è mettere i due punti di controllo su una linea che divide in due il vertice d'angolo. Ad esempio, supponiamo di avere due segmenti:

A. (0,0) to (3,2) 
B. (0,0) to (1,-4) 

the absolute angle of A is arctan(2/3) = 33.69 deg 
the absolute angle of B is arctan(-4/1) = -75.96 deg 
the bisection angle of AB is (33.69 + -75.96)/2 = -21.135 
the tangent angle is AB is (-21.135 + 90) = 68.865 

conoscendo l'angolo tangente, possiamo calcolare il punto di controllo posiziona

smoothness = radius = r 
tangent angle = T 
Vertex X = Xv 
Vertex Y = Yv 

Control Point 1: 
Xcp1 = cos(T)*r 
Ycp1 = sin(T)*r 

Control Point 2: 
Xcp2 = cos(T)*(-r) 
Ycp2 = sin(T)*(-r) 

L'ultimo problema è dove mettere ogni punto di controllo nella curveTo effettivo comando:

CX1 Y1 X2 Y2 X3 Y3 

X3 e Y3 definiscono la posizione del vertice. X1 Y1 e X2 Y2 definiscono i punti di controllo. Puoi pensare a X1 Y1 come a definire il vettore di come inserire il vertice e X2 Y2 come definizione del vettore di come lasciare. Ora che avete i due punti di controllo si deve decidere su

CXcp1 Ycp1 Xcp2 Ycp2 0 0 

o

CXcp2 Ycp2 Xcp1 Ycp1 0 0 

questa è una decisione importante. Se li fai tornare indietro, la forma sembrerà un loop. A questo punto dovresti essere in grado di determinare come questa decisione dovrebbe essere presa ...

Anche in questo caso, questa è una soluzione molto semplice, ma tende a guardare bene per i percorsi disegnati a mano. Una soluzione migliore potrebbe fare un ulteriore passo avanti e spostare il punto di intersezione verso l'interno verso la sezione concava di ogni intersezione del segmento di linea. Questo è un po 'più impegnativo.

+1

ti invitiamo a dare un esempio di come calcolare i punti di controllo? – florianguenther

+0

ho aggiunto un po 'di matematica che dovrebbe aiutare a questa strada – jordancpaul

+0

Questo è probabilmente uno dei migliori risposte che ho visto 10 – austinbv

2

sto con lo stesso problema, guardando paperjs esempi ho visto che hanno un esempio per path simplification, in agguato l'algoritmo dietro di esso si può vedere qui: https://github.com/paperjs/paper.js/blob/master/src/path/PathFitter.js

E 'l'algoritmo che semplificano la strega percorso è una versione js (con le ottimizzazioni) di uno studio accademico di nome "An algorithm for automatically fitting digitized curves".

Sono sulle opere di estraendo solo questo algoritmo e probabilmente publich come un plugin per svg.js.

+0

sei arrivato intorno ad attuare in svg.js? – Rohit

+0

dispiace, no :( non sto lavorando su questo progetto parte per ora. – madcampos

1

Immaginiamo il disegno utente è un array di tuple, potremmo fare qualcosa di simile

const points = [[100, 50], [50, 15], [5, 60], [10, 20], [20, 10], [30, 190], [40, 10], [50, 60], [60, 120], [70, 10], [80, 50], [90, 50], [120, 10], [150, 80], [160, 10] ] 
 

 
const lineProperties = (pointA, pointB) => { 
 
    const lengthX = pointB[0] - pointA[0] 
 
    const lengthY = pointB[1] - pointA[1] 
 
    return { 
 
    length: Math.sqrt(Math.pow(lengthX, 2) + Math.pow(lengthY, 2)), 
 
    angle: Math.atan2(lengthY, lengthX) 
 
    } 
 
} 
 

 
const controlPointCalc = (current, previous, next, reverse) => { 
 
    const c = current 
 
    const p = previous ? previous : c 
 
    const n = next ? next : c 
 
    const smoothing = 0.2 
 
    const o = lineProperties(p, n) 
 
    const rev = reverse ? Math.PI : 0 
 

 
    const x = c[0] + Math.cos(o.angle + rev) * o.length * smoothing 
 
    const y = c[1] + Math.sin(o.angle + rev) * o.length * smoothing 
 

 
    return [x, y] 
 
} 
 

 
const svgPathRender = points => {  
 
    const d = points.reduce((acc, e, i, a) => { 
 
     if (i > 0) { 
 
     const cs = controlPointCalc(a[i - 1], a[i - 2], e) 
 
     const ce = controlPointCalc(e, a[i - 1], a[i + 1], true) 
 
     return `${acc} C ${cs[0]},${cs[1]} ${ce[0]},${ce[1]} ${e[0]},${e[1]}` 
 
     } else { 
 
     return `${acc} M ${e[0]},${e[1]}` 
 
     } 
 
    },'') 
 

 
    return `<path d="${d}" fill="none" stroke="black" />` 
 
} 
 

 
const svg = document.querySelector('.svg') 
 

 
svg.innerHTML = svgPathRender(points)
<svg viewBox="0 0 200 200" version="1.1" xmlns="http://www.w3.org/2000/svg" class="svg"> 
 
</svg>

spiegazioni dettagliate in this article.

Problemi correlati