2011-07-08 12 views
13

Szukam rozwiązania do konwersji odręcznej, narysowanej przez użytkownika ścieżki SVG, składającej się z wielu segmentów LineTo, aby uzyskać płynniejszą.Jak wygładzić odręczną ścieżkę SVG?

Preferowany język to JavaScript, ale wszelkie porady są mile widziane.

+0

Spróbuj użyć krzywych http://www.w3.org/TR/SVG/paths.html#PathDataCurveCommands – Gerben

Odpowiedz

29

Przede wszystkim polecam używanie dobrej biblioteki graficznej, takiej jak Rafael. Uprości to proces korzystania z javascript do wykonania rysunku.

Bardzo prostą metodą wygładzania jest przekształcenie wszystkich poleceń lineto za pomocą równoważnych komend curveto i obliczenie niektórych punktów kontrolnych na podstawie kątów każdego segmentu linii. Na przykład,

<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> 

staje

<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> 

Obie te powinny wyciągnąć trójkąt równoboczny

Następnym krokiem byłoby obliczyć położenie punktów kontrolnych. Ogólnie rzecz biorąc, będziesz chciał, aby punkty kontrolne po obu stronach gładkiego rogu padały na wyimaginowaną linię przechodzącą przez wierzchołek. W przypadku wierzchołka trójkąta równobocznego byłaby to linia pozioma. Po pewnym manipulacji, można uzyskać coś takiego:

<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> 

Najtrudniejsze jest obliczenie punktów kontrolnych, ale to nie zmienia się znacznie więcej niż prostego problemu wyzwalania. Jak już wcześniej wspomniałem, celem jest umieszczenie dwóch punktów kontrolnych na linii dzielącej wierzchołki narożników na pół. Na przykład, załóżmy, że mamy dwa liniowe segmenty:

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 

znając kąt stycznej, możemy obliczyć punkt kontrolny pozycjonuje

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) 

Ostatnim problemem jest to, gdzie umieścić każdy punkt kontrolny w rzeczywistej curveTo command:

CX1 Y1 X2 Y2 X3 Y3 

X3 i Y3 określają położenie wierzchołków. X1 Y1 i X2 Y2 definiują punkty kontrolne. Możesz myśleć o X1 Y1 jako definiującym wektor jak wejść do wierzchołka i X2 Y2 jako definiując wektor jak wyjść. Teraz, że masz dwa punkty kontrolne należy zdecydować się na

CXcp1 Ycp1 Xcp2 Ycp2 0 0 

lub

CXcp2 Ycp2 Xcp1 Ycp1 0 0 

to ważna decyzja. Jeśli dostaniesz je do tyłu, kształt będzie wyglądać jak pętla. W tym momencie powinieneś być w stanie określić, jak należy podjąć decyzję ...

To jest bardzo proste rozwiązanie, ale wygląda dobrze na ręcznie rysowane ścieżki. Lepsze rozwiązanie może pójść o krok dalej i przesunąć punkt przecięcia do wewnątrz w kierunku wklęsłej części każdego przecięcia segmentu linii. To jest trochę trudniejsze.

+1

czy możesz podać przykład obliczania punktów kontrolnych? – florianguenther

+0

Dodałem trochę matematyki, która powinna ci pomóc w tej ścieżce. – jordancpaul

+0

Jest to prawdopodobnie jedna z najlepszych odpowiedzi, jakie widziałem +10 – austinbv

2

jestem z tym samym problemem, patrząc na paperjs przykładów widziałem, że mają jeden przykład dla path simplification, czai algorytmu tył to można go zobaczyć tutaj: https://github.com/paperjs/paper.js/blob/master/src/path/PathFitter.js

Jest to algorytm, który uprości czarownicę ścieżki jest wersją js (z optymalizacją) badania akademickiego o nazwie "An algorithm for automatically fitting digitized curves".

Jestem w trakcie prac nad wyodrębnieniem tego algorytmu i prawdopodobnie będę je publikował jako wtyczkę do svg.js.

+0

czy udało Ci się ją wdrożyć w svg.js? – Rohit

+0

Przepraszam, nie :( Na razie nie pracuję nad tym projektem pobocznym. – madcampos

1

Wyobraźmy sobie rysunek użytkownik jest tablicą krotek, możemy zrobić coś

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>

szczegółowe wyjaśnienia this article.

Powiązane problemy