SVG od podstaw - ścieżki
Wcześniej zostało wprowadzone pojęcie ścieżki. W tym rozdziale omówię je szerzej. Ścieżka jest to kształt powstały z połączonych fragmentów linii, łuków i krzywych.
Do dokumentu wstawiamy ją za pomocą znacznika <path>. Znacznik ten ma atrybut d, w którym określamy za pomocą odpowiednich
liter sposób prowadzenia ścieżki. Każda ścieżka musi rozpoczynać się od komendy moveto (M).
Określa ona punkt (współrzędne x i y oddzielone spacją), w którym rozpoczyna się nasza ścieżka. Ścieżka może zawierać jedną lub więcej komend lineto (L),
która odpowiada za poprowadzenie linii prostej do określonego punktu. Komendy oddzielamy przecinkami. To przykład prostej ścieżki wykorzystującej komendy moveto i lineto.
<?xml version="1.0"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink"> <g style="stroke: green; fill: none;"> <path d="M 40 75, L 40 60, L 10 60, L 40 40, M 60 75, L 60 60, L 90 60, L 60 40" /> </g> </svg>Wyjaśnienie prowadzenia ścieżki od początku:
M 40 75 – startujemy w punkcie (40, 75).
L 40 60 – prowadzimy linię do punktu (40, 60).
L 10 60 – prowadzimy linię z poprzedniego punktu do (10, 60).
L 40 40 – prowadzimy linię do (40, 40).
M 60 75 – rozpoczynamy nową subścieżkę (kładziemy pióro w punkcie (60, 75), żadna linia nie jest rysowana).
L 60 60 – rysujemy linię do punktu (60, 60).
L 90 60 – prowadzimy linię do (90, 60).
L 60 40 – linia do (60, 40).
Teraz opiszę komendę closepath (Z). Jeśli chcemy przy użyciu <path> narysować np. Prostokąt to musimy narysować wszystkie cztery jego boki lub trzy i użyć komendy closepath (Z) czyli zamknięcię ścieżki. Poniższy przykład ilustruje użycie tego polecenia.
![Przykład prostej ścieżki](/arts/113/svg6_2.jpg)
<?xml version="1.0"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink"> <g style="stroke: blue; fill: none;"> <!-- rectangle; all four lines --> <path d="M 10 10, L 40 10, L 40 30, L 10 30, L 10 10" /> <!-- rectangle with closepath --> <path d="M 60 10, L 90 10, L 90 30, L 60 30, Z" /> <!-- two thirty-degree triangles --> <path d="M 40 60, L 10 60, L 40 42.68, Z M 60 60, L 90 60, L 60 42.68, Z" /> </g> </svg>Ostatnią ścieżkę opiszę dokładniej:
M 40 60 – startujemy w punkcie (40, 60).
L 10 60 – prowadzimy linię do (10, 60).
L 40 42.68 – prowadzimy linię do punktu (40 42.68).
Z – zamykamy ścieżkę (dorysowuje ostatnią linię do punktu (40, 60)). W ten sposób powstał pierwszy trójkąt.
M 60 60 – rozpoczynamy nową subścieżkę w punkcie (60, 60).
L 90 60 – rysujemy linię do (90, 60).
L 60 42.68 – prowadzimy linię do (60, 42.68).
Z – zamykamy ścieżkę (powstaje drugi trójkąt).
Relatywne moveto i lineto
Wszystkie komendy reprezentowane przez duże litery i występujące po nich współrzędne oznaczają współrzędne absolutne. Jeśli użyjemy małych liter komend to współrzędne będą interpretowane jako relatywne względem aktualnej pozycji pióra. Ilustruje to poniższy przykład.![Przykład prostej ścieżki](/arts/113/svg6_3.jpg)
<?xml version="1.0"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink"> <path d="M 10 10 L 20 10 L 20 30 M 40 40 L 55 35" style="stroke: black; fill: none;" /> <path d="M 10 10 l 10 0 l 0 20 m 20 10 l 15 -5" style="stroke: black; fill: none" /> </svg>Z obu ścieżek otrzymamy to samo. Aby to zobaczyć usuń najpierw pierwszą ze ścieżek zostawiając drugą, a potem odwrotnie.
Komendy horizontal lineto i vertical lineto
Horizontal lineto oznacza poziomą linię i jest reprezentowane przez komendę H dla obsolutnej współrzędnej x lub h dla relatywnej współrzędnej x. Podobnie vertical lineto (linia pionowa) jest reprezentowana przez V dla absolutnej współrzędnej y oraz v dla relatywnej. Spójrzmy na poniższą tabelkę:Skrót | Znaczenie | Efekt |
---|---|---|
H 40 | L 40 aktualne_y | Rysuje linię do absolutnego położenia (40, aktualne_y) |
h 40 | l 40 0 | Rysuje linię do (aktualne_x + 40, aktualne_y) |
V 40 | L aktualne_x 40 | Rysuje linię do absolutnego położenia (aktualne_x, 40) |
v 40 | l aktualne_x 40 | Rysuje linię do (aktualne_x, aktualne_y + 40) |
Elliptical Arc (eliptyczny łuk)
Rysowanie linii jest proste, co jednak gdy chcemy narysować krzywą łączącą dwa punkty? Takich krzywych jest nieskończenie wiele więc musimy podać określone informacje, aby można było narysować odpowiednią krzywą. Najprostszą z krzywych jest eliptyczny łuk (elliptical arc) – jest to część łuku elipsy łączący dwa jej punkty.Pierwszą częścią informacji jest określenie promieni rx i ry elipsy, na których leżą punkty początku i końca elipsy. Wskazuje to na dwie odmienne elipsy, a zarazem cztery łuki (patrz rys. a). Na rysunkach b i c łuki są mniejsze niż 180 stopni, a na d i e większe od 180 stopni. Jeśli spojrzymy na rysunki b i c zauważymy, że różnią się one kierunkiem. Podobnie jest z rysunkami d i e.
Komendy dotyczące łuku zaczynają się od A (we współrzędnych absolutnych) lub a (we współrzędnych relatywnych) i mają następujące parametry:
- rx i ry – promienie elipsy, na których leżą punkty początku i końca elipsy,
- x-axis-rotation, który wskazuje jak elipsa ma być obrócona,
- large-arc-flag – wskazuje, która część elipsy jest brana od uwagę (jeśli parameter wynosi 0 oznacza to miara kąta wycinka elipsy jest mniejsza od 180 stopni, jeśli parameter wynosi 1 to miara kąta jest większa lub równa 180 stopni),
- sweep-flag – określa kierunek kąta,
- x, y – współrzędne punktu kończącego łuk.
![Eliptyczny łuk](/arts/113/svg6_4.jpg)
![Eliptyczny łuk](/arts/113/svg6_5.jpg)
<path d="M 125,75 A100,50 0 0,0 225,125" /> <!-- b --> <path d="M 125,75 A100,50 0 0,1 225,125" /> <!-- c --> <path d="M 125,75 A100,50 0 1,0 225,125" /> <!-- d --> <path d="M 125,75 A100,50 0 1,1 225,125" /> <!-- e -->
Krzywe Beziera
Krzywa Baziera to parametryczna krzywa powszechnie stosowana w programach do projektowania inżynierskiego CAD, projektowania grafiki komputerowej (Corel Draw, Adobe Illustrator, GIMP, Inkscape), do reprezentowania obrysów znaków w fontach komputerowych (TrueType, METAFONT, Type1) i systemach przetwarzania grafiki (PostScript, MetaPost) oraz w grafice wektorowej (np. format SVG). Krzywe Béziera są krzywymi parametrycznymi - każda współrzędna punktu krzywej jest pewną funkcją liczby rzeczywistej - parametru; żeby określić krzywą na płaszczyźnie potrzebne są dwie funkcje, żeby określić krzywą w przestrzeni - trzy, itd. Ze względu na rodzaj tych funkcji mówi się o krzywych wielomianowych oraz krzywych wymiernych. Powszechnie stosuje się również krzywe złożone z kawałków gładko połączonych krzywych wielomianowych bądź wymiernych - krzywe B-sklejane (także krzywe gładkie).Niezależnie od rodzaju krzywej na jej przebieg wpływa łamana kontrolna określona za pomocą punktów kontrolnych.
Kwadratowe krzywe Beziera
Najprostszą krzywą Beziera jest kwadratowa krzywa. Trzeba dla niej określić tylko punkt początkowy, punkt końcowy i punkt kontrolny. Wyobraźmy sobie dwa ramiona namiotu umieszczone przy punktach końcowych linii. Te ramiona spotykają się w jednym punkcie nazywanym punktem kontrolnym. Linia pomiędzy środkami ramion namiotu jest ruchoma i rysuje naszą krzywą.![Kwadratowa krzywa Beziera](/arts/113/svg6_6.jpg)
- punkt Q0 zmienia się od P0 do P1 i rysuje prostą P0P1 czyli liniową krzywą Beziera,
- punkt Q1 zmienia się od P1 do P2 i rysuje prostą P1P2 czyli liniową krzywą Beziera,
- punkt B(t) zmienia się od Q0 do Q1 i rysuje kwadratową krzywą Beziera (narysowana kolorem czerwonym).
Kwadratowe krzywe Beziera definiujemy za pomocą komend Q (absolutne) i q (relatywne). Poniższy kod określa kwadratową krzywą Beziera z punktu (30, 75) do (300, 120), gdzie punkt (240, 30) jest punktem kontrolnym.
<path d="M30 75 Q 240 30, 300 120" style="stroke: black; fill: none;"/>
![Kwadratowa krzywa Beziera](/arts/113/svg6_7.jpg)
![Kwadratowa krzywa Beziera](/arts/113/svg6_8.jpg)
<path d="M30 100 Q 80 30, 100 100, 130 65, 200 80" style="stroke: black; fill: none;" />
![Kwadratowa krzywa Beziera](/arts/113/svg6_9.jpg)
<path d="M30 100 Q 80 30, 100 100 T 200 80" />
![Kwadratowa krzywa Beziera](/arts/113/svg6_10.jpg)
Sześcienne krzywe Beziera
Pojedyncza kwadratowa krzywa Beziera posiada dokładnie jeden punkt załamania (punkt, w którym krzywa zmienia kierunek). Sześcienne krzywe Beziera mogą mieć jeden lub dwa punkty załamania. Kolejną różnicą pomiędzy kwadratową a sześcienną krzywą Beziera jest to, że krzywa sześcienna posiada dwa punkty kontrolne (po jednym dla każdego punktu końcowego). Technika tworzenia sześciennej krzywej jest podobna do tworzenia kwadratowej krzywej. Przykład sześciennej krzywej Beziera przedstawia poniższy rysunek. Przykład tworzenia poniżej.![Sześcienna krzywa Beziera](/arts/113/svg6_11.jpg)
Do określenia w SVG krzywej sześciennej (cubic curve) używamy komendy C lub c. Komenda ta określa trzy zestawy współrzędnych: punkt kontrolny dla punktu startowego, punkt kontrolny dla punktu końcowego i punkt końcowy. Poniższy kod przedstawia sześciennę krzywą Beziera startującą w punkcie (20 , 80) i kończącą się w punkcie (200, 120) z punktami kontrolnymi (50, 20) i (150, 60).
<path d="M20 80 C 50 20, 150 60, 200 120" style="stroke: black; fill: none;"/>
![Sześcienna krzywa Beziera](/arts/113/svg6_12.jpg)
![Krzywe Beziera](/arts/113/svg6_13.jpg)
![Krzywa Beziera](/arts/113/svg6_14.jpg)
<path d="M30 100 C 50 50, 70 20, 100 100, 110, 130, 45, 150, 65, 100" style="stroke: black; fill: none;" />Generuje on sześcienną krzywą polibeziera. Zaczynamy od punktu (30, 100) i rysujemy krzywą sześcienną do (100, 100) z punktami kontrolnymi (50, 50) i (70, 20), następnie kontynuujemy do punktu (65, 100) z punktami kontrolnymi (110, 130) oraz (45, 150). Efekt jest pokazany na rysunku poniżej.
![Krzywa Beziera](/arts/113/svg6_15.jpg)
<path d="M30 100 C 50 30, 70 50, 100 100 S 150 40, 200 80" style="stroke: black; fill: none;" />
![Krzywa Beziera](/arts/113/svg6_16.jpg)
Ścieżki i wypełnienia
Rozważmy dwie ścieżki. Obydwie wstawiają kwadraty. W pierwszej ścieżce obydwa kwadraty są rysowane zgodnie z ruchem wskazówek zegara, natomiast w drugiej ścieżce zewnętrzny kwadrat jest rysowany zgodnie z ruchem wskazówek zegara, a wewnętrzny przeciwnie.<path d="M 0 0, 60 0, 60 60, 0 60 Z M 15 15, 45 15, 45 45, 15 45Z"/> <path d="M 0 0, 60 0, 60 60, 0 60 Z M 15 15, 15 45, 45 45, 45 15Z"/>Poniższy rysunek przedstawia różnicę w wyborze opcji wypełnienia (fill-rule).
![fill-rule](/arts/113/svg6_17.jpg)
Element <marker>
Na początek rozważmy następującą ścieżkę:<path d="M 10 20 100 20 A 20 30 0 0 1 120 50 L 120 110" style="fill: none; stroke: black;"/>Teraz dodamy do niej kółko na początku ścieżki, czarny trójkąt na końcu i strzałki wewnątrz jak na rysunku poniżej.
![Element marker](/arts/113/svg6_18.jpg)
<defs> <marker id="mCircle" markerWidth="10" markerHeight="10"> <circle cx="5" cy="5" r="4" style="fill: none; stroke: black;"/> </marker> </defs> <path d="M 10 20 100 20 A 20 30 0 0 1 120 50 L 120 110" style="marker-start: url(#mCircle); fill: none; stroke: black;"/>Jak widać na rysunku poniżej efekt odbiega od zamierzonego.
![Element marker](/arts/113/svg6_19.jpg)
<marker id="mCircle" markerWidth="10" markerHeight="10" refX="5" refY="5"> <circle cx="5" cy="5" r="4" style="fill: none; stroke: black;"/> </marker>
![Element marker](/arts/113/svg6_20.jpg)
<defs> <marker id="mCircle" markerWidth="10" markerHeight="10" refX="5" refY="5"> <circle cx="5" cy="5" r="4" style="fill: none; stroke: black;"/> </marker> <marker id="mArrow" markerWidth="6" markerHeight="10" refX="0" refY="4"> <path d="M 0 0 4 4 0 8" style="fill: none; stroke: black;"/> </marker> <marker id="mTriangle" markerWidth="5" markerHeight="10" refX="5" refY="5"> <path d="M 0 0 5 5 0 10 Z" style="fill: black;"/> </marker> </defs> <path d="M 10 20 100 20 A 20 30 0 0 1 120 50 L 120 110" style="marker-start: url(#mCircle); marker-mid: url(#mArrow); marker-end: url(#mTriangle); fill: none; stroke: black;"/>
![Element marker](/arts/113/svg6_21.jpg)
![Element marker](/arts/113/svg6_22.jpg)
<defs> <marker id="mCircle" markerWidth="10" markerHeight="10" refX="5" refY="5"> <circle cx="5" cy="5" r="4" style="fill: none; stroke: black;"/> </marker> <marker id="mArrow" markerWidth="6" markerHeight="10" refX="0" refY="4" orient="auto"> <path d="M 0 0 4 4 0 8" style="fill: none; stroke: black;"/> </marker> <marker id="mTriangle" markerWidth="5" markerHeight="10" refX="5" refY="5" orient="auto"> <path d="M 0 0 5 5 0 10 Z" style="fill: black;"/> </marker> </defs> <path d="M 10 20 100 20 A 20 30 0 0 1 120 50 L 120 110" style="marker-start: url(#mCircle); marker-mid: url(#mArrow); marker-end: url(#mTriangle); fill: none; stroke: black;"/>Powyższy kod generuje ścieżkę z markerami, taką jak na pierwszym rysunku w niniejszym paragrafie.