Contoh penggunaan ImageMagick -- menggambar
Menggambar di IM adalah cara untuk menambahkan elemen baru ke gambar yang sudah ada. Banyak hal tentang penggambaran teks sudah dibahas di halaman contoh Efek font gabungan dan Anotasi gambar, jadi halaman ini menangani sisi lain yang lebih umum dari operator "-draw". Perintah draw bermula sebagai sarana untuk membuat gambar sederhana. Namun seiring waktu ia berkembang menjadi antarmuka untuk mengonversi grafik vektor menjadi gambar raster.
Perintah draw ImageMagick Gambar di komputer umumnya disimpan dengan dua cara yang berbeda. Cara pertama dan yang paling umum, yang sudah Anda lihat di sepanjang halaman contoh ini, dikenal sebagai grafik raster. Pada pendekatan ini gambar disimpan sebagai larik piksel berbentuk persegi panjang. Cara yang lain kurang umum dan kurang mudah diubah, tetapi dalam arti lain lebih serbaguna, yaitu grafik vektor berbasis objek. Dalam bentuk ini gambar diuraikan sebagai garis, busur, isian warna, dan kadang kedalaman. Ini berguna karena gambar semacam itu dapat diskalakan ke hampir semua ukuran dan tetap tampil sempurna. Gambar yang sangat besar dan rumit pun dapat diuraikan dalam ruang yang sangat kecil dibandingkan padanannya dalam format raster. Contoh gambar grafik vektor antara lain postscript dan SVG -- Scalable Vector Graphics yang lebih baru. Font True-Type juga termasuk grafik vektor, karena bentuk ini membuat uraian tiap karakter dapat dipakai pada skala berapa pun. Operator gambar "-draw" adalah jendela ke fungsi penggambaran vektor ImageMagick, dan membentuk sekumpulan perintah yang cukup terpisah dari operator gambar baris perintah IM yang biasa. | _Hanya ada sedikit format file grafik vektor yang umum dipakai karena tiap format semacam itu biasanya sangat berbeda dari format lainnya. Akibatnya, hampir tidak ada kemungkinan berbagi kode.
Karena itu, ImageMagick lebih berfokus pada penggunaan grafik vektor untuk menggambar gambar berformat SVG. Grafik postscript dan font true-type diserahkan ke pustaka dan aplikasi 'delegate' eksternal lain yang jauh lebih cocok untuk menggambar format grafik vektor semacam itu.
Bukan berarti delegate untuk SVG tidak tersedia. Salah satu contohnya adalah pustaka RSVG atau pustaka GTK SVG yang tersedia saat kompilasi. IM akan menautkan diri ke pustaka tersebut untuk mengonversi SVG, alih-alih mencoba melakukannya sendiri._
Perintah draw primitif
Mari kita mulai dengan primitif penggambaran yang paling tua, paling sederhana, dan paling umum dari operator gambar "-draw" di antara perintah MVG. Perhatikan bahwa semua argumen diperlakukan sebagai bilangan floating point dan tidak harus bilangan bulat, seperti yang biasa saya pakai pada contoh-contoh ini. |
# Single Pixel Draw (two ways -- these have been enlarged)
# Point 'paints' the color pixel
magick -size 10x6 xc:skyblue -fill black \
-draw 'point 3,2' -scale 100x60 draw_point.gif
# Color Point 'replaces' the color pixel
magick -size 10x6 xc:skyblue -fill black \
-draw 'color 6,3 point' -scale 100x60 draw_color_point.gif
![[IM Output]](../static/img/draw/draw_point.gif)
![[IM Output]](../static/img/draw/draw_color_point.gif)
Kedua metode point ini memberi hasil yang berbeda bila warna semitransparan terlibat, sesuai komentar yang diberikan. Lihat Primitif isian warna di bawah untuk rinciannya. |
# Rectangle / Rounded Rectangle / Rectangular Arc
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "rectangle 20,10 80,50" draw_rect.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "roundrectangle 20,10 80,50 20,15" draw_rrect.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "arc 20,10 80,50 0,360" draw_arc.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "arc 20,10 80,50 45,270" draw_arc_partial.gif
![[IM Output]](../static/img/draw/draw_rect.gif)
![[IM Output]](../static/img/draw/draw_rrect.gif)
![[IM Output]](../static/img/draw/draw_arc.gif)
![[IM Output]](../static/img/draw/draw_arc_partial.gif)
Primitif draw 'arc' didaftarkan bersama persegi panjang karena sebenarnya ia hanyalah 'ellipse' yang dipaskan ke dalam 'rectangle' yang ditentukan oleh dua koordinat itu. Busur parsial jarang dipakai karena titik ujungnya sulit ditentukan kecuali sudutnya dibatasi pada kelipatan sembilan puluh derajat.
Primitif 'circle' dan 'ellipse' melibatkan koordinat 'center' beserta koordinat 'edge', atau nilai 'size' dan 'angle', masing-masing. |
# Circle / Ellipse (centered on a point)
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "circle 50,30 40,10" draw_circle.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "ellipse 50,30 40,20 0,360" draw_ellipse.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "ellipse 50,30 40,20 45,270" draw_ellipse_partial.gif
![[IM Output]](../static/img/draw/draw_circle.gif)
![[IM Output]](../static/img/draw/draw_ellipse.gif)
![[IM Output]](../static/img/draw/draw_ellipse_partial.gif)
Anda mungkin juga ingin melihat Push/Pop konteks untuk contoh cara membuat elips yang diputar. |
# Line / Polyline / Polygon / Bezier
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "line 20,50 90,10" draw_line.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "polyline 40,10 20,50 90,10 70,40" draw_polyline.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "polygon 40,10 20,50 90,10 70,40" draw_polygon.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "bezier 40,10 20,50 90,10 70,40" draw_bezier.gif
![[IM Output]](../static/img/draw/draw_line.gif)
![[IM Output]](../static/img/draw/draw_polyline.gif)
![[IM Output]](../static/img/draw/draw_polygon.gif)
![[IM Output]](../static/img/draw/draw_bezier.gif)
Metode yang lebih baik untuk menggambar garis dan kurva adalah menggambar path SVG, yang jauh lebih serbaguna dan bahkan mendukung 'penggambaran garis relatif'. |
# text drawing / image
magick -size 100x60 xc:skyblue -fill white -stroke black \
-font Candice -pointsize 40 -gravity center \
-draw "text 0,0 'Hello'" draw_text.gif
magick -size 100x60 xc:skyblue -gravity center \
-draw "image over 0,0 0,0 'terminal.gif'" draw_image.gif
![[IM Output]](../static/img/draw/draw_text.gif)
![[IM Output]](../static/img/draw/draw_image.gif)
Dua operasi bertipe isian terakhir ini untuk saat ini adalah satu-satunya operasi draw yang dipengaruhi oleh "-gravity". Pengubah lain untuk operasi ini antara lain: "-fill", "-tile", "-origin", "-stroke", "-strokewidth", "-font", "-pointsize", "-box", Ada pengubah lain lagi, tetapi semuanya berkaitan dengan bahasa Magick Vector Graphics yang lebih lanjut.
Primitif bezier
Primitif 'bezier' dipakai untuk menggambar kurva. Tiap perintah hanya menggambar satu segmen kurva. Biasanya diberikan 4 titik (8 angka): satu titik awal 'knot', dua titik kendali, dan satu titik akhir 'knot'. Kedua titik kendali menentukan arah dan seberapa cepat kurva menyimpang dari titik 'knot' ujung yang menempel padanya. Agar dua kurva menyambung mulus, titik kendali dari ujung harus dicerminkan melalui 'knot' untuk membentuk titik kendali pada kurva Bezier berikutnya. Sebagai contoh, di sini saya menggambar dua kurva bezier yang menyambung mulus. Perhatikan bagaimana garis dan titik kendali (yang juga digambar) tercermin lurus melalui koordinat sambungan, baik dari sudut maupun panjangnya. Ini penting; kalau tidak, kurvanya tidak akan mulus. |
points="10,10 30,90 25,10 50,50 50,50 75,90 70,10 90,40"
clines=`echo "$points" | sed 's/ /\n/g' |\
while read line; do echo "line $line"; done`
symbols=`echo path "'"; for point in $points; do
echo "M $point l -2,-2 +4,+4 -2,-2 l -2,+2 +4,-4 -2,+2"
done; echo "'"`
magick -size 100x100 xc:skyblue -fill none \
-draw "stroke gray $clines stroke blue $symbols " \
-draw "stroke red bezier 10,10 30,90 25,10 50,50 " \
-draw "stroke red bezier 50,50 75,90 70,10 90,40 " \
draw_bezier_joined.gif
![[IM Output]](../static/img/draw/draw_bezier_joined.gif)
Jika saya memindahkan salah satu titik kendali sehingga ia TIDAK 'dicerminkan' melalui 'knot' yang menempel dari titik kendali lain pada 'knot' yang sama, kurvanya menjadi tidak kontinu. |
points="10,10 30,90 25,10 50,50 50,50 80,50 70,10 90,40"
clines=`echo "$points" | sed 's/ /\n/g' |\
while read line; do echo "line $line"; done`
symbols=`echo path "'"; for point in $points; do
echo "M $point l -2,-2 +4,+4 -2,-2 l -2,+2 +4,-4 -2,+2"
done; echo "'"`
magick -size 100x100 xc:skyblue -fill none \
-draw "stroke gray $clines stroke blue $symbols " \
-draw "stroke red bezier 10,10 30,90 25,10 50,50 " \
-draw "stroke red bezier 50,50 80,50 70,10 90,40 " \
draw_bezier_disjoint.gif
![[IM Output]](../static/img/draw/draw_bezier_disjoint.gif)
Jika titik kendali digeser lagi sampai berimpit dengan titik 'knot' terkait, garisnya akan langsung berangkat dari titik itu tanpa 'kurva' sama sekali. |
points="10,10 30,90 25,10 50,50 50,50 50,50 70,10 90,40"
clines=`echo "$points" | sed 's/ /\n/g' |\
while read line; do echo "line $line"; done`
symbols=`echo path "'"; for point in $points; do
echo "M $point l -2,-2 +4,+4 -2,-2 l -2,+2 +4,-4 -2,+2"
done; echo "'"`
magick -size 100x100 xc:skyblue -fill none \
-draw "stroke gray $clines stroke blue $symbols " \
-draw "stroke red bezier 10,10 30,90 25,10 50,50 " \
-draw "stroke red bezier 50,50 50,50 70,10 90,40 " \
draw_bezier_no_curve.gif
![[IM Output]](../static/img/draw/draw_bezier_no_curve.gif)
Jika kedua titik kendali disetel ke 'knot' masing-masing, yang dihasilkan adalah garis lurus. |
points="10,10 10,10 50,50 50,50 50,50 50,50 90,40 90,40"
clines=`echo "$points" | sed 's/ /\n/g' |\
while read line; do echo "line $line"; done`
symbols=`echo path "'"; for point in $points; do
echo "M $point l -2,-2 +4,+4 -2,-2 l -2,+2 +4,-4 -2,+2"
done; echo "'"`
magick -size 100x100 xc:skyblue -fill none \
-draw "stroke gray $clines stroke blue $symbols " \
-draw "stroke red bezier 10,10 10,10 50,50 50,50 " \
-draw "stroke red bezier 50,50 50,50 90,40 90,40 " \
draw_bezier_lines.gif
![[IM Output]](../static/img/draw/draw_bezier_lines.gif)
Primitif 'bezier' tidak begitu berguna tanpa menentukan keempat titiknya. Hanya titik pertama dan terakhir yang tergolong 'knot', yaitu titik yang dilalui (atau tempat berakhirnya) kurva. Semua titik lain di antaranya dianggap murni sebagai titik kendali, yang memengaruhi kurva sesuai urutan pemberiannya; makin jauh titik kendali itu, makin besar pengaruhnya pada segmen kurva tersebut. |
points="10,10 30,90 25,10 75,90 70,10 90,40"
symbols=`for point in $points; do
echo "M $point l -2,-2 +4,+4 -2,-2 l -2,+2 +4,-4 -2,+2"
done`
magick -size 100x100 xc:skyblue -fill none \
-draw "stroke gray polyline $points " \
-draw "stroke red bezier $points " \
-draw "stroke blue path '$symbols' " \
draw_bezier_multi.gif
![[IM Output]](../static/img/draw/draw_bezier_multi.gif)
Sebaiknya jangan memakai lebih atau kurang dari 4 titik per segmen kurva 'bezier', supaya semuanya tetap sederhana. Sebenarnya saya menyarankan agar primitif 'bezier' tidak dipakai sama sekali; untuk menghasilkan kurva, pakailah Cubic Bezier path SVG. Cara itu punya fungsi penerus kurva 'S' khusus yang secara otomatis melakukan 'pencerminan' titik kendali yang tepat sehingga segmen kurva menyambung mulus, sekaligus mengurangi jumlah titik kendali yang perlu dipakai. Titik juga dapat didefinisikan relatif terhadap titik akhir sebelumnya di dalam path.
Primitif isian warna
Selain primitif 'sederhana' di atas, "-draw" juga menyediakan sekumpulan primitif untuk mengisi atau mengubah warna. Primitif ini mengubah warna di dalam gambar mulai dari titik yang ditentukan, sesuai metode yang dipilih. Metode isian ini sebenarnya bukan perintah 'draw' yang sesungguhnya, melainkan fungsi penggantian warna. Semuanya ditambahkan ke draw karena di situlah tempat termudah untuk menyisipkan operasinya ke ImageMagick pada versi program yang sangat awal. Sama seperti di atas, warna yang dipakai ditentukan lewat pengaturan warna "-fill", tetapi bila disetel, gambar "-tile" yang akan dipakai sebagai gantinya. Opsi pengaturan lain di atas tidak dipakai dan tidak berpengaruh pada operasi ini. Ada dua pengaturan tambahan yang juga berlaku untuk primitif ini, yaitu "-bordercolor" dan faktor "-fuzz". Namun pengaturan tersebut TIDAK dapat didefinisikan di dalam bahasa 'MVG', sehingga hanya bisa disetel sebelum operator "-draw" dipakai. Yang pertama, 'color point', sudah Anda lihat sebagai alternatif primitif draw 'point' pada contoh di atas. Kalau diperhatikan baik-baik, akan tampak satu piksel putih yang kita setel pada gambar uji kita.
|
magick color_test.png -fill white \
-draw 'color 30,20 point' color_point.png
![[IM Output]](../static/img/draw/color_point.png)
Namun ketika menggambar warna transparan dan semitransparan, kedua fungsi ini tidak sama. Di sini kita punya gambar merah tiga piksel (diperbesar); pada piksel kedua atau tengah kita memakai fungsi 'point' untuk melukis di atas piksel merah dengan warna biru semitransparan, sehingga hasilnya ungu. Namun jika kita memakai fungsi 'color point' (piksel terakhir atau kanan), warna merahnya diganti sepenuhnya oleh piksel biru semitransparan. Warna itu tidak ditimpakan. |
magick -size 3x1 xc:red -alpha on -fill '#00F8' \
-draw 'point 1,0' \
-draw 'color 2,0 point' -scale 33x33 draw_points.png
![[IM Output]](../static/img/draw/draw_points.png)
Semua fungsi 'color' melakukan penggantian warna secara penuh, sedangkan semua primitif warna lain 'melukis' warna di atas gambar. Karena itu 'color' dapat dipakai untuk menggambar warna transparan. Fungsi draw 'color replace' akan mengganti semua kemunculan warna yang persis sama dengan warna di lokasi yang ditentukan. Dan seperti terlihat, areanya tidak harus tersambung. |
magick color_test.png -fill white \
-draw 'color 30,20 replace' color_replace.png
magick color_test.png -fill white -fuzz 13%\
-draw 'color 30,20 replace' color_replace_fuzz.png
![[IM Output]](../static/img/draw/color_replace_fuzz.png)
Namun seperti terlihat pada hasil pertama, sebagian piksel di sepanjang tepi tidak ikut diganti. Piksel-piksel itu warnanya tidak persis sama dengan piksel yang dipilih, jadi diabaikan. Menambahkan faktor fuzz kecil akan menyertakan pula warna-warna yang mirip dengan warna aslinya, seperti pada contoh kedua di atas. Tentu saja 'faktor fuzz' bukan solusi yang bagus, karena tidak semua piksel tepi semacam itu tertangkap. Ini masalah yang berulang pada semua metode 'isian warna' ini, dan belum ada solusi umumnya. Bila yang ingin diganti adalah warna tertentu yang sudah diketahui, bukan warna yang diambil dari gambar itu sendiri, operator gambar "-opaque" bisa dipakai sebagai gantinya. Fungsi ini juga memakai pengaturan faktor "-fuzz" untuk memperlebar rentang warna yang cocok dengan warna yang diberikan. Metode 'floodfill' juga cukup sederhana karena ia hanya mengisi seluruh area di sekitar titik yang dipilih dan tidak memilih area lain berwarna mirip yang tidak tersambung. Area yang diisi juga dapat diperluas dengan "-fuzz" agar warna-warna mirip ikut tercakup. Dalam hal ini kita memilih nilai yang cukup tinggi sehingga batas garis silangnya pun ikut tercakup, sehingga flood fill 'bocor' ke sisi lain gambar. |
magick color_test.png -fill white \
-draw 'color 30,20 floodfill' color_floodfill.png
magick color_test.png -fill white -fuzz 15% \
-draw 'color 30,20 floodfill' color_floodfill_fuzz.png
![[IM Output]](../static/img/draw/color_floodfill_fuzz.png)
Mengisi area dengan warna secara flood fill bukannya tanpa masalah. Warnanya bisa bocor melewati batas yang tipis, masuk ke area yang tidak diinginkan (lihat GIF di atas pola latar belakang sebagai peragaannya). Atau area yang dipilih bisa saja tidak terisi sampai ke tepinya (lihat Masalah anti-aliasing dan flood fill). Meski begitu, cara ini bekerja. 'filltoborder' mirip 'floodfill', bedanya yang ditentukan adalah warna yang membatasi area yang akan diisi, bukan warna yang akan diganti oleh proses pengisian. Tentu saja faktor fuzz juga disarankan agar 'warna-warna mirip' ikut terpilih sebagai warna batas, sehingga floodfill lebih terbatas. |
magick color_test.png -fill white -bordercolor royalblue \
-draw 'color 30,20 filltoborder' color_filltoborder.png
magick color_test.png -fill white -bordercolor blue \
-draw 'color 30,20 filltoborder' color_filltoborder2.png
magick color_test.png -fill white -bordercolor blue -fuzz 30% \
-draw 'color 30,20 filltoborder' color_filltoborder_fuzz.png
![[IM Output]](../static/img/draw/color_filltoborder.png)
![[IM Output]](../static/img/draw/color_filltoborder2.png)
![[IM Output]](../static/img/draw/color_filltoborder_fuzz.png)
Metode warna draw yang terakhir adalah 'reset', yang cuma mengganti atau mereset seluruh gambar menjadi warna fill. Dalam hal ini piksel yang dipilih sama sekali tidak memengaruhi hasilnya. |
magick color_test.png -fill white \
-draw 'color 30,20 reset' color_reset.png
![[IM Output]](../static/img/draw/color_reset.png)
Ini sebenarnya sangat berguna karena memberi satu cara sederhana untuk menghasilkan kanvas berwarna polos (atau gambar yang di-tile) dari gambar yang sudah ada. (Lihat Membuat gambar berukuran sama) untuk cara ini dan cara lain melakukan hal yang sama. FUTURE: Using a "-tile" pattern to fill the area.
Primitif isian alpha
Primitif draw 'alpha' bekerja persis seperti primitif 'color' yang dijelaskan di atas, bedanya ia tidak mengganti warna area yang dipilih, melainkan hanya kanal 'alpha' dari area tersebut. (Artinya, fungsi isian ini hanya menyetel kanal 'alpha'.) Sama seperti fungsi isian 'color', nilai 'alpha' memakai warna fill (kecuali bila "-tile" dipakai sebagai sumber 'nilai alpha'). Di sini kita memakai contoh 'color floodfill' yang sama seperti di atas, tetapi kali ini hanya menyetel kanal alpha agar bagian yang terisi menjadi transparan sepenuhnya. Artinya, warna aslinya masih ada, hanya saja transparan! |
magick color_test.png -fill none \
-draw 'alpha 30,20 floodfill' matte_floodfill.png
magick color_test.png -fill none -fuzz 15% \
-draw 'alpha 30,20 floodfill' matte_floodfill_fuzz.png
![[IM Output]](../static/img/draw/matte_floodfill_fuzz.png)
Fungsi 'alpha reset' juga dapat dipakai untuk membuat seluruh gambar menjadi semitransparan. Tentu saja dalam hal ini kita harus menghasilkan PNG, yang dapat menerima warna semitransparan di dalam gambar. |
magick color_test.png -fill '#00000080' \
-draw 'alpha 30,20 reset' matte_reset.png
![[IM Output]](../static/img/draw/matte_reset.png)
Perhatikan bahwa komponen warna 'black' tidak dipakai dalam operasi itu, hanya komponen alpha dari warnanya. Warna asli gambar dibiarkan apa adanya. FUTURE: Using "-tile" pattern for in interesting alpha effect. Baik 'color' maupun 'alpha' adalah fungsi penggantian warna secara penuh, yang selalu menghasilkan penggantian warna bertipe Boolean (ada atau tidak sama sekali). Karena itu tepi area semacam itu akan selalu memperlihatkan efek aliasing. Karena itu pula, keduanya umumnya bukan operator gambar yang baik untuk pengembangan gambar secara umum, kecuali untuk menyetel area transparan gambar GIF (yang juga Boolean). Meski begitu, tidak semuanya sia-sia, seperti terlihat pada contoh Penghapusan latar belakang.
Rincian tentang perintah draw
Koordinat piksel
Perintah "-draw" (dan banyak perintah lain di IM) memakai apa yang disebut "koordinat piksel". Artinya, koordinat '10,10 adalah pusat piksel yang berjarak 10 piksel ke bawah dan ke kanan dari sudut kiri atas. Dalam sistem koordinat ini 0,0 adalah pusat piksel kiri atas, dan w-1,h-1 adalah pusat sudut kanan bawah. Tepi yang sebenarnya terletak di -0.5,-0.5 dan w-0.5,h-0.5, sedangkan piksel tengah (bila ukuran gambarnya ganjil) terletak di '(w-1)/2,(h-1)/2'. Namun ketika gambar diproses secara matematis (misalnya saat memakai distort), piksel yang sebenarnya tidak punya arti, sehingga yang dipakai adalah "koordinat gambar". Dalam sistem ini tepi gambar yang sebenarnya ada di '0,0' dan 'w,h'. Dan pusat gambar (yang belum tentu merupakan pusat sebuah piksel) ada di 'w/2,h/2'. Untuk mengonversi 'koordinat piksel' menjadi koordinat gambar, tambahkan ½. Jadi pusat piksel kiri atas adalah '0.5,0.5' dan piksel kanan bawah adalah 'w-0.5,h-0.5'. Contoh: pusat lingkaran pada gambar kecil
Menggambar dengan koreksi gamma dan ruang warna
Seperti hampir semua operasi ImageMagick, "-draw" adalah operator linear, dan karenanya bekerja di ruang warna RGB linear. Artinya, untuk mendapatkan tepi yang mulus, gambar mungkin perlu dikoreksi gamma-nya sebelum disimpan, supaya tersimpan memakai ruang warna sRGB yang nonlinear (terkoreksi gamma). Misalnya, kalau kita menggambar lingkaran besar lalu langsung menyimpannya... |
magick -size 81x81 xc:black -fill white -draw 'circle 40,40 40,3' \
circle_raw.png
![[IM Output]](../static/img/draw/circle_raw.png)
Lihat tepi lingkarannya: sebenarnya tidak terlihat mulus. Efek tangganya cukup kentara. Itu karena lingkarannya digambar di ruang warna RGB linear, tetapi gambarnya lalu disimpan seolah-olah benar-benar berada di ruang warna sRGB! Untuk memperbaikinya, kita perlu menambahkan koreksi gamma pada gambar sebelum menyimpannya. |
magick -size 81x81 xc:black -fill white -draw 'circle 40,40 40,3' \
-gamma 2.2 circle_gamma.png
![[IM Output]](../static/img/draw/circle_gamma.png)
Sekarang tepi lingkarannya benar-benar tampak mulus dan membulat sebagaimana mestinya. Kalau ingin melakukannya dengan benar, koreksinya sebenarnya harus dibuat lewat ruang warna. Namun karena IM menganggap RGB sebagai ruang warna bawaan saat menyimpan, diperlukan penanganan yang agak berliku supaya hasilnya benar. |
magick -size 81x81 xc:black -set colorspace RGB \
-fill white -draw 'circle 40,40 40,3' \
-colorspace sRGB circle_sRGB.png
![[IM Output]](../static/img/draw/circle_sRGB.png)
| Perhatikan bahwa ruang warna sRGB (cara yang benar untuk menyimpan gambar) tidak persis sama dengan sekadar menerapkan koreksi gamma 2.2. Namun perbedaan hasil keduanya kecil, dan hanya terlihat pada gambar yang sangat sangat gelap.
| Sebelum IM v6.7.5-1 nama ruang warna 'sRGB' dan 'RGB' (RGB linear) sebenarnya terbalik. Karena itu, pada IM versi lama kedua label di atas harus ditukar.
Untuk menggambar dengan benar (atau melakukan pemrosesan gambar 'linear' apa pun) pada gambar sungguhan (di IMv7), gamma yang ada harus dihilangkan lebih dulu, gambarnya diproses, lalu koreksi gamma itu dikembalikan. Lihat Resize dengan koreksi ruang warna untuk rinciannya. Berikut contoh menggambar pada gambar sungguhan... Pertama tanpa koreksi warna sama sekali (mentah), lalu dengan koreksi gamma dan koreksi ruang warna. |
magick rose: -fill none -stroke white -draw 'line 5,40 65,5' rose_raw.png
magick rose: -gamma .454545 \
-fill none -stroke white -draw 'line 5,40 65,5' \
-gamma 2.2 rose_gamma.png
magick rose: -colorspace RGB \
-fill none -stroke white -draw 'line 5,40 65,5' \
-colorspace sRGB rose_sRGB.png
![[IM Output]](../static/img/draw/rose_sRGB.png)
Seperti terlihat, dengan koreksi gamma atau ruang warna garisnya tampak sangat mulus, tanpa efek aliasing 'tangga' yang bergerigi seperti yang muncul kalau digambar langsung. (Perlu monitor yang sangat bagus untuk melihatnya) | Garis di atas digambar memakai warna "-stroke". Garis itu bisa juga digambar memakai "-fill" dengan hasil yang sama, tetapi ketebalan garisnya tidak dapat dikendalikan lewat "-strokewidth". Lihat Interaksi stroke, strokewidth, dan fill di bawah untuk keterangan lebih lanjut.
| _Nama warna sebenarnya didefinisikan memakai nilai untuk ruang warna 'sRGB', TETAPI diterapkan oleh draw seolah-olah gambarnya berada di ruang warna RGB linear. Karena itu, memakai koreksi gamma di atas bersama warna bernama (selain 'white' atau 'black') akan membuat warna tersebut terdistorsi. Dalam kasus semacam itu mungkin lebih baik tidak memakai koreksi gamma atau ruang warna, supaya warna bernama terpetakan dengan benar.
Pemetaan yang benar dari warna 'sRGB' bernama ke ruang warna gambar yang digambari akan diperbaiki sebagai bagian dari pengembangan IMv7.
Interaksi stroke, strokewidth, dan fill
Opsi "-stroke" dan "-strokewidth" dipakai saat menggambar garis luar di sekeliling tepi font. Opsi ini biasa dipakai bersama "-fill" untuk membuat teks lebih menarik dengan usaha yang sangat kecil.
magick -size 380x70 xc:lightblue -pointsize 50 -font Chisel \
-fill green -stroke black -draw 'text 10,55 "Black Border"' \
stroke_font.jpg
Pengaturan bawaannya adalah "-strokewidth 1" dan "-stroke None". Namun ini membuat garis luar stroke tidak terlihat, hanya menyisakan warna "-fill", jadi strokenya tidak akan tampak. Satu-satunya pengaruh "-strokewidth" ketika "-stroke" 'tak terlihat' adalah pada atribut ukuran font, yang berarti ia tetap dapat memengaruhi penempatan font dan ukuran gambar Label dan caption yang dihasilkan. Selebihnya, lebar itu tidak berpengaruh secara kasatmata sampai strokenya dibuat terlihat. Untuk melihat bagaimana "-strokewidth" sebenarnya memengaruhi tampilan font (ketika dibuat terlihat), di sini saya menggambar beberapa teks dengan berbagai lebar, dari 'dimatikan' lalu makin besar.
magick -size 320x420 xc:lightblue -pointsize 70 -font Vademecum \
-fill red -stroke none -draw 'text 30,80 "Stroke -"' \
-fill red -stroke black -strokewidth 0 -draw 'text 30,160 "Stroke 0"' \
-fill red -stroke black -strokewidth 1 -draw 'text 30,240 "Stroke 1"' \
-fill red -stroke black -strokewidth 2 -draw 'text 30,320 "Stroke 2"' \
-fill red -stroke black -strokewidth 3 -draw 'text 30,400 "Stroke 3"' \
stroke_table.jpg
Perhatikan dari contoh di atas bahwa menyetel "-strokewidth" ke '0' TIDAK sama dengan menyetel warna "-stroke" ke 'none' (bawaannya). Yang pertama menghasilkan garis luar stroke yang sangat sangat tipis, sedangkan yang kedua praktis mematikannya. Pada kedua kasus, strokenya tetap digambar. Namun perlu juga dicatat bahwa bahkan dengan "-strokewidth" '0', garis luar gambar akan melebar sangat sangat sedikit dibanding gambar yang sekadar 'diisi' (memakai warna "-stroke" 'none'. Pada dasarnya lebar berapa pun yang lebih kecil dari '1.0' tidak bekerja sebagaimana mestinya. Berhati-hatilah dalam kasus yang bisa terpengaruh oleh hal ini. Ingat pula bahwa "-strokewidth" adalah pengaturan floating point. Artinya, lebar stroke '0.5' juga sah. Namun biasanya hal ini baru penting ketika kita mencoba menggambar lingkaran bitmap tipis dengan anti-aliasing dimatikan.
Berikut contoh memakai lebar stroke yang teramat besar.
magick -size 320x100 xc:lightblue -font Candice -pointsize 72 -fill white \
-stroke black -strokewidth 15 -draw "text 25,65 'Anthony'" \
stroke_thick.jpg
Perhatikan bahwa "-strokewidth" melebarkan garis ke dalam sekaligus ke luar. Berikut contoh yang sama, tetapi fontnya digambar ulang tanpa garis luar stroke, untuk menghilangkan bagian dalam dari stroke yang sangat tebal itu.
magick -size 320x100 xc:lightblue -font Candice -pointsize 72 -fill white \
-stroke black -strokewidth 15 -draw "text 25,65 'Anthony'" \
-stroke none -draw "text 25,65 'Anthony'" \
stroke_outline.jpg
Untuk contoh lain penggunaan stroke, lihat Efek font gabungan. Perhatikan khususnya "Efek balon".
Menggambar garis (stroke)
Penggambaran garis bawaan di IM punya beberapa perilaku aneh yang layak diketahui. Berikut penggambaran garis bawaannya... |
magick -size 100x40 xc:lightblue \
-draw "line 5,35 95,5" \
line_default.jpg
![[IM Output]](../static/img/draw/line_default.jpg)
Warna garis dapat disetel dengan opsi "-fill". |
magick -size 100x40 xc:lightblue \
-fill white -draw "line 5,35 95,5" \
line.jpg
![[IM Output]](../static/img/draw/line.jpg)
Garis juga bisa dibuat sedikit lebih tebal dengan menyetel warna "-stroke". |
magick -size 100x40 xc:lightblue \
-fill white -stroke black -draw "line 5,35 95,5" \
line_stroke.jpg
![[IM Output]](../static/img/draw/line_stroke.jpg)
Tetapi ke mana perginya warna putih yang kita tentukan lewat opsi "-fill" tadi? Inilah sisi yang membingungkan dari penggambaran garis di ImageMagick. Yang dilakukan program sebenarnya adalah memperlakukan garis sebagai objek terisi selebar sekitar 1 piksel. Ini wajar, karena biasanya banyak garis dipakai untuk menyapu suatu area yang akan diisi. Jadi sama seperti waktu kita memakai stroke pada font di bagian sebelumnya, IM menggambar garis (atau objek) itu memakai warna fill, lalu menggambar di sekelilingnya dengan warna stroke. Hasilnya, garis berwarna stroke di atas kini sedikit lebih tebal, dengan warna fill tersembunyi sepenuhnya di bawahnya. Kalau warna strokenya dibuat semitransparan, warna fill itu bisa terlihat lagi. Ringkasnya, garis akan tampak digambar dengan warna "-fill", tetapi opsi itu tidak lagi berarti begitu warna "-stroke" ditetapkan selain warna bawaan "none" atau "transparent". | Opsi "-linewidth" sebenarnya hanya alias untuk "-strokewidth", dan sebaiknya tidak dipakai.
Misalnya, Anda mungkin mengira perintah ini akan menghasilkan garis yang sangat tebal. Memang begitu, tetapi karena warna "-stroke"-nya tidak terlihat, hasilnya tak tampak. Yang terlihat hanya 'fill' di bagian dalam area garis selebar satu piksel itu. |
magick -size 100x40 xc:lightblue \
-fill white -strokewidth 3 -draw "line 5,35 95,5" \
line_fill_3.jpg
![[IM Output]](../static/img/draw/line_fill_3.jpg)
| Hasil di atas sebenarnya saya anggap bug. Seharusnya tidak ada yang tergambar, karena tidak ada 'area' yang perlu diisi dan tidak ada 'warna stroke' garis yang disetel. Alasan IM saat ini berlaku begitu adalah untuk menghindari kebingungan pengguna baru, tetapi sebenarnya hal ini justru menyusahkan pengguna tingkat lanjut. LihatBatas isian draw untuk rinciannya.
Tetapi kalau warna strokenya juga ditetapkan, garis tebal yang diminta akan muncul... |
magick -size 100x40 xc:lightblue \
-stroke black -strokewidth 3 -draw "line 5,35 95,5" \
line_stroke_3.jpg
![[IM Output]](../static/img/draw/line_stroke_3.jpg)
Kalau pengaturan "-strokewidth" disetel ke satu, garis di atas akan tertutup sepenuhnya. |
magick -size 100x40 xc:lightblue \
-stroke black -strokewidth 1 -draw "line 5,35 95,5" \
line_stroke_1.jpg
![[IM Output]](../static/img/draw/line_stroke_1.jpg)
Tentu saja dengan berbekal pengetahuan ini, kita bisa memakainya secara kreatif, sama seperti pada penggambaran font. |
magick -size 100x40 xc:lightblue \
-stroke black -strokewidth 5 -draw "line 5,35 95,5" \
-stroke white -strokewidth 2 -draw "line 5,35 95,5" \
line_multi.jpg
![[IM Output]](../static/img/draw/line_multi.jpg)
Di sini saya memakai pengaturan "-strokewidth" paling tipis, yaitu '0', sama seperti yang saya lakukan pada font di atas. |
magick -size 100x40 xc:lightblue \
-fill white -stroke black -strokewidth 0 -draw "line 5,35 95,5" \
line_stroke_0.jpg
![[IM Output]](../static/img/draw/line_stroke_0.jpg)
Hasilnya sangat aneh: garis putus-putus yang terdiri atas titik hitam dan segmen abu-abu. Ini akibat "frekuensi pelayangan warna" yang ganjil antara warna stroke, fill, dan latar belakang. Berikut tampilan garis itu diperbesar... |
magick -size 25x10 xc:lightblue \
-fill white -stroke black -strokewidth 0 -draw "line 2,8 22,1" \
-scale 400% line_stroke_0_white.jpg
![[IM Output]](../static/img/draw/line_stroke_0_white.jpg)
| Efek "frekuensi pelayangan warna" tidak jauh berbeda dari "pelayangan bunyi" yang terdengar bila ada dua gitar yang sedikit sekali sumbang. Dalam hal ini titik hitam muncul di tempat warna stroke sepenuhnya menimpa warna fill di bawahnya, dan titik abu-abu muncul di tempat warna stroke bercampur dengan KEDUA warna fill dan latar belakang. Percampuran warna ini konsekuensi wajar dari proses anti-aliasing yang dipakai IM untuk memperbaiki tampilan garis dan objek draw lain. Untuk keterangan lebih lanjut, lihat halaman pembahasan dan contoh saya,Anti-aliasing di IM.
Perhatikan bahwa efek ini hanya muncul pada garis miring, bukan pada garis yang murni horizontal atau vertikal, karena di situ aliasing tidak berpengaruh sehingga tidak ada efek "frekuensi pelayangan warna". |
magick -size 100x40 xc:lightblue \
-fill white -stroke black -strokewidth 0 -draw "line 5,20 95,20" \
line_stroke_horz.jpg
![[IM Output]](../static/img/draw/line_stroke_horz.jpg)
Di sini saya memakai warna fill dasar yang berbeda-beda pada tampilan yang diperbesar, supaya terlihat bagaimana warna itu mengubah pelayangan yang dihasilkan. |
magick -size 25x10 xc:lightblue \
-fill none -stroke black -strokewidth 0 -draw "line 2,8 22,1" \
-scale 400% line_stroke_0_none.jpg
magick -size 25x10 xc:lightblue \
-fill red -stroke black -strokewidth 0 -draw "line 2,8 22,1" \
-scale 400% line_stroke_0_red.jpg
magick -size 25x10 xc:lightblue \
-fill black -stroke black -strokewidth 0 -draw "line 2,8 22,1" \
-scale 400% line_stroke_0_black.jpg
![[IM Output]](../static/img/draw/line_stroke_0_black.jpg)
Mari kita bandingkan dengan stroke bernilai none... |
magick -size 25x10 xc:lightblue \
-fill black -stroke none -draw "line 2,8 22,1" \
-scale 400% line_stroke_-_black.jpg
![[IM Output]](../static/img/draw/line_stroke_-_black.jpg)
Seperti terlihat, saat menggambar garis yang sangat tipis, 'pelayangan' itu dapat dikurangi dengan memakai warna fill dan stroke yang sama, ATAU dengan menyetel salah satu warnanya ke none untuk mematikannya. Cara kedua yang paling baik, tetapi cara pertama mungkin lebih praktis untuk kebutuhan pemrograman Anda. Perhatikan bahwa ketebalan garis fill adalah '0'. Namun garis stroke bisa punya ketebalan lebih besar, dan nilainya pun floating point! Garis selebar 2.5 piksel sepenuhnya sah. | Hasil ini disebabkan bukan hanya oleh lebar stroke 0 yang bermasalah sehingga menimbulkan pelayangan warna, tetapi juga oleh 'warna fill' yang digambar dengan tambahan ketebalan berdiameter 1.0 padahal tidak ada area yang perlu diisi. Ini pun saya anggap bug. LihatBatas isian draw.
Batas isian draw
Ada beberapa hal lain yang perlu dicatat tentang berbagai primitif draw. Stroke-width bekerja baik untuk nilai floating point di atas 1.0, tetapi tampaknya kacau untuk nilai di bawah 1.0. Ini karena algoritme implementasi yang dipakai, bukan semata karena salah, sebab untuk garis yang lebih tebal ia bekerja baik-baik saja. Pada dasarnya, kalau stroke-width nol dipakai, kita berharap tidak ada warna stroke yang ditambahkan. Yang terjadi malah semacam pola pelayangan: warna stroke muncul dengan kekuatan penuh ketika garisnya melewati 'pusat' piksel yang sesungguhnya.. Yang seharusnya terjadi adalah banyaknya warna yang ditambahkan ke sebuah piksel mencerminkan luas garis yang digambar, bukan jarak piksel itu dari garis tersebut. Dengan begitu, garis berlebar nol semestinya tidak menambahkan warna apa pun ke gambar, sedangkan garis dengan ketebalan di bawah 1.0 semestinya hanya menambahkan sedikit warna. Lihat contoh Menggambar garis (stroke) di atas. Masalah yang lain, warna fill tidak diterapkan tepat sampai ke tepi bentuk (poligon) yang digambar, melainkan ½ piksel lebih ke luar. Ini termasuk situasi ketika tidak ada 'stroke' yang diterapkan dan tepinya semestinya persis. Termasuk juga penggambaran 'line', yang sebenarnya punya ketebalan fill 'nol'. Pada dasarnya, kalau kita menggambar garis tanpa mengaktifkan stroke, secara teknis semestinya tidak ada garis yang terlihat karena ia tidak punya ketebalan 'fill'. Yang terjadi, garis tetap digambar dengan warna 'fill' selebar minimal 1 piksel. Ini karena alasan historis, dan umumnya menghindarkan kebingungan pengguna baru IM. Sayangnya hal itu TIDAK benar bagi pengguna tingkat lanjut. Artinya, kalau kita menggambar dua poligon hanya dengan warna fill dan keduanya berbagi satu tepi, tepi itu akan bertumpang tindih sejauh 1 piksel karena tiap poligon ½ piksel lebih besar di sepanjang semua tepinya. Dengan kata lain, poligon dan bentuk lain tidak saling pas, melainkan bertumpang tindih! Sebagai contoh, di sini saya mencoba memakai draw untuk membagi sebuah gambar menjadi dua bagian (menggambar hitam di atas putih). Untuk itu saya menggambar dua poligon yang berbagi satu tepi, persis tanpa tumpang tindih. Gambar 'mungil' yang dihasilkan sudah diperbesar untuk ditampilkan.
magick -size 10x10 xc: -draw 'polygon 2,-1 7,10 10,10 10,-1' bound_left.gif
magick -size 10x10 xc: -draw 'polygon 2,-1 7,10 -1,10 -1,-1' bound_right.gif
magick bound_left.gif bound_right.gif -compose Plus -composite bound_add.gif
Kedua bagian hitam (yang memang benar-benar digambar) ternyata saling bertumpang tindih! Dengan kata lain, meskipun kita mencoba menggambar kedua area itu secara terpisah dengan poligon, area yang terisi sedikit lebih besar daripada yang diminta. Saya juga menjumlahkan (komposisi Plus) kedua gambar itu supaya tumpang tindih area hitam yang digambar benar-benar terlihat. Kalau kedua poligon itu pas sempurna, gambar hasil 'penjumlahan' akan berwarna putih polos. Besarnya tumpang tindih setara dengan pengaturan bawaan "-strokewidth 1.0". Jadi biasanya area tambahan ini diharapkan tertutup oleh lebar stroke yang normal. Meski begitu, hal ini bisa menimbulkan masalah nyata. ASIDE: Untuk menguji sambungan itu secara lengkap, buatlah area abu-abu 50% di atas latar belakang hitam lalu jumlahkan keduanya. Dengan begitu terlihat apakah areanya tidak hanya 'bertumpang tindih' (seperti di atas), tetapi juga apakah keduanya 'kurang bertemu' (menyisakan celah antararea terisi) ketika dijumlahkan. Gambar hasilnya semestinya berupa warna abu-abu 50% yang mulus sempurna tanpa variasi warna di sepanjang sambungan. Pemeriksaan transparansi dilakukan dengan memakai warna abu-abu 50% yang transparan 50% di atas latar belakang yang sepenuhnya transparan. Untuk melihat contoh pemotongan dan penggabungan ulang yang sempurna berdasarkan satu gambar mask, lihat contoh metode komposisi, Dst_Out. FUTURE BUG FIX: The area filled should be exact, but to compensate for this when drawing shapes, the default 'stroke color' should be set to the fill color (unless it is itself specifically set).
MVG - Magick Vector Graphics
Primitif yang ditunjukkan di atas menjadi dasar semua operasi "-draw" yang tersedia. Bersama-sama, semuanya merupakan titik awal sebuah bahasa internal khusus di ImageMagick yang disebut bahasa Magick Vector Graphics. Untuk rincian bahasa ini, lihat Ringkasan primitif dan sintaksis MVG di situs web IM. Bahasa "MVG" ini dirancang dengan tujuan agar ImageMagick dapat menangani bahasa SVG (Scalable Vector Graphics) yang jauh lebih rumit. Caranya adalah dengan mencoba mengonversi gambar yang diberikan dalam format SVG ke format MVG internal yang lebih sederhana. Untuk rinciannya lihat penanganan SVG di bawah. Akibatnya, apa yang kita lihat di atas hanyalah sebagian kecil dari kemampuan operator "-draw". Meski begitu, kalau yang ingin digambar adalah objek yang rumit, saya menyarankan agar gambar objek itu dibuat terpisah dalam format SVG memakai editor SVG seperti "Sodipodi". (Lihat editor grafik vektor non-IM di bawah). Tidak seperti SVG, MVG tidak punya bentuk 'kontainer' atau himpunan perintah gambar apa pun. Semuanya dibuang selama proses konversi sehingga yang dihasilkan adalah urutan perintah penggambaran MVG yang disederhanakan. Sebagai gantinya, MVG memakai konsep konteks grafik untuk menyimpan dan memulihkan berbagai pengaturan penggambaran, dan itulah yang akan kita lihat sekarang.
Pengaturan baris perintah vs pengaturan MVG
Pertama-tama, hampir semua pengaturan yang disetel lewat opsi baris perintah dan dipakai oleh primitif draw punya padanan langsung di dalam perintah penggambaran MVG. Perbedaan utama antara menyetelnya lewat opsi baris perintah (misalnya "-strokewidth") dan memakai pengaturan di dalam string penggambaran MVG (misalnya 'stroke-width) adalah bahwa pengaturan MVG hanya bertahan selama string perintah MVG itu berlangsung.
**Ringkasan pengaturan penggambaran umum**
__cmd_option__ __draw_MVG__ __Argumen__
-fill fill warna/tile untuk bagian dalam bentuk
-tile fill tile gambar, menggantikan warna fill
-stroke stroke warna/tile garis di sekeliling bentuk
-strokewidth stroke-width lebar dalam piksel
+antialias stroke-antialias 0/1 aliasing tepi garis
-font font font_name / font_file
-family font-family ?
-weight ? ?
-stretch ? ?
-pointsize font-size tinggi dalam poin
-kerning - spasi antarkarakter tambahan
+antialias text-antialias 0/1 aliasing penggambaran teks
-box text-undercolor warna fill untuk kotak batas font
- decorate (None, Underline, LineThrough atau Overline)
-gravity gravity (None, North, South-East,...)
-fuzz - selisih warna / persentase
-bordercolor - warna
Catatan:
- tidak ada opsi tersebut ? tidak diketahui
| Pengaturan ini biasanya sudah cukup dipahami karena kerap dipakai dan diperagakan di atas. | font, stretch, style, dan weight dipakai untuk mengenali sebuah font dari daftar font ImageMagick. Namun kebanyakan orang cukup memilih font dan pointsize tertentu untuk dipakai. Karena itu keempatnya jarang dipakai di IM. |
|---|---|
Seperti terlihat, pengaturan khusus untuk primitif 'isian warna' tidak punya padanan langsung di MVG, yaitu "-bordercolor" dan pengaturan faktor "-fuzz". Keduanya harus ditentukan dari baris perintah sebelum operator "-draw" dipakai. Beberapa pengaturan MVG mungkin akan lebih berguna sebagai pengaturan baris perintah global, misalnya pengaturan 'decorate' untuk penggambaran font. PERINGATAN: "-gravity" bukan bagian dari spesifikasi SVG. Di dalam MVG, ia hanya dipakai untuk penempatan teks dan gambar serta perataan. Saat ini belum ada pengaturan perataan yang terpisah dari efek 'gravitasi' bawaan. Namun karena perataan adalah bagian dari penanganan teks SVG, hal itu kemungkinan akan berubah suatu saat nanti. Nah, pengaturan baris perintah global (di luar string draw MVG) dipakai untuk menginisialisasi pengaturan tiap operasi "-draw" yang diterapkan, dan karena itulah kita bisa menyetel warna "-fill" lalu memakainya untuk menggambar lingkaran berwarna itu. |
|
| --- | |
magick -size 100x60 xc:skyblue -fill red \
-draw "circle 50,30 40,10" draw_circle_global.gif
![[IM Output]](../static/img/draw/draw_circle_global.gif)
Pengaturan global itu dapat ditimpa secara lokal di dalam argumen MVG "-draw"...
|
magick -size 100x60 xc:skyblue -fill red \
-draw "fill green circle 50,30 40,10" draw_circle_override.gif
![[IM Output]](../static/img/draw/draw_circle_override.gif)
Namun pengaturan yang disetel di dalam satu argumen MVG "-draw" hanya berlaku selama operasi "-draw" itu. Artinya, pengaturan di dalam sebuah "-draw" bersifat lokal untuk draw tersebut saja dan tidak terbawa ke argumen "-draw" terpisah berikutnya.
|
magick -size 100x60 xc:skyblue -fill red -draw 'fill green' \
-draw "circle 50,30 40,10" draw_circle_local.gif
![[IM Output]](../static/img/draw/draw_circle_local.gif)
Kalau berencana melakukan banyak operasi, mungkin lebih baik semuanya dikerjakan dalam satu string MVG saja, ketimbang lewat banyak operasi "-draw".
|
magick -size 100x60 xc:skyblue \
-draw "fill green circle 41,39 44,57
fill blue circle 59,39 56,57
fill red circle 50,21 50,3 " draw_circle_multi.gif
![[IM Output]](../static/img/draw/draw_circle_multi.gif)
Pengaturan khusus MVG Pengaturan MVG lain yang mengendalikan cara garis dan objek digambar juga berguna untuk diketahui, bahkan ketika memakai operasi primitif. Di antaranya..
__draw_MVG__ __Deskripsi/Argumen__
fill-opacity transparansi fill, dari 0.0 sampai 1.0
clip-rule gaya isian untuk garis bersilangan (evenodd, nonzero)
stroke-opacity transparansi garis, angka dari 0.0 sampai 1.0
stroke-dasharray daftar panjang 'on' dan 'off' untuk garis
stroke-dash
stroke-linecap Tampilan ujung garis: butt round square
stroke-linejoin Sambungan garis: butt miter round square
stroke-miterlimit Sudut saat sambungan 'miter' menjadi 'bevel' (atau 'butt')
Ingat, daftar lengkap semua pengaturan MVG dan operator penggambaran dapat dilihat di Ringkasan primitif dan sintaksis MVG pada situs web IM. Mari kita lihat efek beberapa pengaturan yang lebih sederhana... |
# Stroke Opacity
magick -size 100x60 xc:skyblue -fill none -stroke black \
-draw " path 'M 10,10 L 90,10'" \
-draw "stroke-opacity 0.8 path 'M 10,20 L 90,20'" \
-draw "stroke-opacity 0.6 path 'M 10,30 L 90,30'" \
-draw "stroke-opacity 0.4 path 'M 10,40 L 90,40'" \
-draw "stroke-opacity 0.2 path 'M 10,50 L 90,50'" \
set_stroke_opacity.gif
# Fill Opacity
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw " rectangle 5,10 15,50 " \
-draw "fill-opacity 0.8 rectangle 20,10 30,50 " \
-draw "fill-opacity 0.6 rectangle 35,10 45,50 " \
-draw "fill-opacity 0.4 rectangle 50,10 60,50 " \
-draw "fill-opacity 0.2 rectangle 65,10 75,50 " \
-draw "fill-opacity 0 rectangle 80,10 90,50 " \
set_fill_opacity.gif
# Plain and Dashed Lines
magick -size 100x60 xc:skyblue -fill none -stroke black \
-draw " path 'M 10,10 L 90,10'" \
-draw "stroke-dasharray 5 3 path 'M 10,20 L 90,20'" \
-draw "stroke-dasharray 5 5 path 'M 10,30 L 90,30'" \
-draw "stroke-dasharray 10 3 3 3 path 'M 10,40 L 90,40'" \
-draw "stroke-dasharray 1 6 path 'M 10,50 L 90,50'" \
set_lines.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw " path 'M 10,10 L 90,10'" \
-draw "stroke-dasharray 5 3 path 'M 10,20 L 90,20'" \
-draw "stroke-dasharray 5 5 path 'M 10,30 L 90,30'" \
-draw "stroke-dasharray 10 3 3 3 path 'M 10,40 L 90,40'" \
-draw "stroke-dasharray 1 6 path 'M 10,50 L 90,50'" \
set_lines_fill.gif
# Note: Technically the second image should be the same as the first
# as the 'filled' lines contain no area. This I regard as a BUG.
# Stroke Ends and Joins
magick -size 100x60 xc:skyblue -fill white -stroke black -strokewidth 8 \
-draw " path 'M 20,20 L 20,70'" \
-draw "stroke-linecap butt path 'M 40,20 L 40,70'" \
-draw "stroke-linecap round path 'M 60,20 L 60,70'" \
-draw "stroke-linecap square path 'M 80,20 L 80,70'" \
set_endcaps.gif
magick -size 100x60 xc:skyblue -fill white -stroke black -strokewidth 5 \
-draw " path 'M 5,70 L 20,20 35,70'" \
-draw "stroke-linejoin miter path 'M 35,70 L 50,20 65,70'" \
-draw "stroke-linejoin bevel path 'M 55,70 L 70,20 85,70'" \
-draw "stroke-linejoin round path 'M 75,70 L 90,20 105,70'" \
set_linejoin.gif
magick -size 100x60 xc:skyblue -fill white -stroke black -strokewidth 5 \
-draw " path 'M 5,70 L 20,20 35,70'" \
-draw "stroke-miterlimit 7 path 'M 35,70 L 50,20 65,70'" \
-draw "stroke-miterlimit 6 path 'M 65,70 L 80,20 95,70'" \
set_miterlimit.gif
![[IM Output]](../static/img/draw/set_endcaps.gif)
![[IM Output]](../static/img/draw/set_linejoin.gif)
![[IM Output]](../static/img/draw/set_miterlimit.gif)
Pengaturan 'stroke-miterlimit' agak sulit diperagakan. Properti ini menentukan sudut saat sambungan 'miter' berubah menjadi sambungan 'bevel'. Pada dasarnya, untuk sudut yang sangat lancip, miter bisa menjulur jauh dari sambungan kedua garis yang sebenarnya. Pengaturan ini menetapkan batas maksimum ketajaman itu, dan menumpulkan titik sudutnya ketika terlalu panjang. Perhatikan bahwa nilainya mewakili nilai trigonometri dari suatu sudut, bukan panjang atau jarak. Nilainya harus lebih besar dari 1.0. Contoh di atas memperlihatkan bahwa, untuk sudut sambungan yang saya tampilkan, miter tiba-tiba berubah menjadi bevel di antara nilai 6 dan 7. Misalnya, 'stroke-miterlimit' 1.414 mengubah 'miter' menjadi 'bevel' untuk setiap sudut kurang dari 90 derajat. Nilai 4.0 (bawaannya) mengubah sambungan untuk sudut kurang dari kira-kira 29 derajat. Sedangkan nilai 10.0 mengubahnya untuk sudut kurang dari kira-kira 11.5 derajat. Menggambar path SVG Path SVG adalah primitif penggambaran dasar pada SVG. Path dipakai untuk menggambar garis, bentuk, lingkaran, kurva, busur, dan seterusnya. Spesifikasi lengkap SVG Path dapat ditemukan di dokumen Spesifikasi SVG Path. Namun dokumen itu tidak mudah dibaca karena memang ditujukan untuk pemrogram, bukan pengguna, jadi saya akan menyederhanakan dan meringkas spesifikasi path itu...
- Huruf adalah perintah, sedangkan semua angka (floating point) adalah argumen.
- Koma atau spasi boleh dipakai sebagai pemisah argumen; selain itu keduanya diabaikan sepenuhnya.
- Dua argumen terakhir (x,y) pada tiap komponen path akan menjadi titik akhir (atau 'knot') komponen path tersebut.
- Huruf kapital menyatakan koordinat absolut titik akhir.
Huruf kecil bersifat relatif terhadap titik akhir komponen sebelumnya.
Sebagai contoh: "M 1,2 l 3,4 l 2,-4" sama dengan "M 1,2 L 4,6 L 6,2".
Artinya, 3,4 ditambahkan ke 1,2 untuk menggambar garis ke 4,6.
Lalu 2,-4 ditambahkan untuk menggambar garis ke koordinat akhir 6,2. - Argumen tiap elemen boleh diulang tanpa mengeluarkan lagi huruf path yang sama, yaitu dengan menambahkan kelompok argumen angka berikutnya. Namun untuk kurva, saya menyarankan huruf fungsinya tetap ditulis supaya mudah dibaca.
- Argumen "
M" atau "m" yang berulang diperlakukan berturut-turut sebagai "L" atau "l".
Sebagai contoh: "M 1,2 3,4 5,6" sama dengan "M 1,2 L 3,4 L 5,6"
Dan : "m 1,2 3,4 2,-4" sama dengan "m 1,2 l 3,4 l 2,-4" - Untuk cubic bezier, semua titik (titik kendali maupun titik knot akhir) diberikan relatif terhadap titik akhir komponen path sebelumnya.
Perhatikan bahwa segalanya dapat dinyatakan dalam koordinat absolut maupun relatif. Jadi sebuah objek dapat didefinisikan dalam koordinat relatif, lalu cukup diberi satu koordinat 'move' absolut di awal untuk menempatkan seluruh path. Di sisi lain, perintah 'graphic-content' lain juga bisa dipakai untuk memindahkan seluruh gambar di dalam 'viewbox' atau 'translation' (lihat di bawah). Jadi sebenarnya tidak jadi soal apakah koordinat absolut atau relatif yang dipakai di dalam path SVG. Move, garis, dan penutupan path adalah titik awal untuk mempelajari path objek SVG. |
# Open, Completed and Closed Paths (same points)
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 40,10 L 20,50 90,10 70,40'" path_open.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 40,10 L 20,50 90,10 70,40 40,10'" path_complete.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 40,10 20,50 90,10 70,40 Z'" path_closed.gif
![[IM Output]](../static/img/draw/path_complete.gif)
![[IM Output]](../static/img/draw/path_closed.gif)
Namun perhatikan bahwa 'Z' hanya menutup loop. Ia TIDAK membuat objek terpisah. Karena itu dua path 'tertutup' tetap tergolong satu objek gambar, baik keduanya bertumpang tindih maupun sama sekali terpisah. Di sini kita menampilkan dua loop tertutup yang bertumpang tindih, digambar dengan arah yang sama. Karena hanya satu path yang dipakai, objeknya tunggal, dan pengaturan 'fill-rule ' mengendalikan bagaimana daerah tumpang tindih itu diisi. |
# Overlapping Paths and Fill Rule
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "fill-rule evenodd \
path 'M 40,10 20,20 70,50 Z
M 20,40 70,40 90,10 Z' " path_evenodd.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "fill-rule nonzero \
path 'M 40,10 20,20 70,50 Z
M 20,40 70,40 90,10 Z' " path_nonzero.gif
![[IM Output]](../static/img/draw/path_evenodd.gif)
![[IM Output]](../static/img/draw/path_nonzero.gif)
Karena kedua objek digambar dengan arah sudut yang sama mengelilingi pusatnya, kedua loop tertutup itu melingkupi area dengan nilai siklus 2. Karena itu aturan 'evenodd' membiarkan area tersebut tidak terisi, sedangkan aturan 'nonzero' mengisinya. Namun perhatikan bahwa semua path tetap terlihat, karena sebenarnya semuanya objek yang sama. Arah penggambaran path sangat penting, dan secara umum semua path harus digambar dengan arah yang persis sama relatif terhadap 'bagian dalam' objek. Sebagai contoh, di sini saya menggambar objek kedua dengan arah yang berlawanan dari objek pertama. Akibatnya, ketika kedua objek bertumpang tindih, area itu terlingkari 'nol ' kali. Artinya, area tersebut tidak akan terisi apa pun 'fill-rule' yang dipakai, sehingga terbentuk 'lubang'. |
# Overlapping Closed Objects, Second object drawn in reverse
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "fill-rule evenodd \
path 'M 40,10 20,20 70,50 Z
M 20,40 90,10 70,40 Z' " path_rvs_evenodd.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "fill-rule nonzero \
path 'M 40,10 20,20 70,50 Z
M 20,40 90,10 70,40 Z' " path_rvs_nonzero.gif
![[IM Output]](../static/img/draw/path_rvs_nonzero.gif)
![[IM Output]](../static/img/draw/path_rvs_evenodd.gif)
Artinya, 'lubang' pada sebuah objek dapat dihasilkan dengan membalik arahnya, sehingga 'bagian dalam' objek tetap berada di sisi yang sama terhadap arah gerak. |
# An object with a reversed drawn hole!
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,10 20,55 70,50 80,5 Z
M 50,20 60,40 40,30 Z' " path_with_hole.gif
![[IM Output]](../static/img/draw/path_with_hole.gif)
Hasilnya sama apa pun pengaturan 'fill-rule'-nya, karena lubang itu sekaligus 'genap' dan 'nol' sehingga tidak terisi. Tentu saja, kalau elemen 'path' yang benar-benar terpisah dipakai, yang dihasilkan adalah objek yang benar-benar terpisah pula. Dalam hal itu 'fill-rule' tidak berlaku dan objek-objeknya sekadar digambar bertindihan sesuai urutan yang diberikan. |
# Separate paths are separate objects
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 40,10 20,20 70,50 Z'
path 'M 20,40 70,40 90,10 Z' " path_separate.gif
FUTURE: coordinate aligned paths "H" and "V"
Busur elips adalah fungsi penggambaran lingkaran pada path SVG...
Parameter 'large' dan 'sweep' sangat penting karena keduanya menentukan mana di antara empat cara 'membusur' dari titik awal ke titik akhir komponen path tersebut yang dipakai. Kedua flag 'large' dan 'sweep' menentukan mana di antara empat busur berjari-jari itu yang akan menghubungkan kedua titik. |
# Elliptical Arcs : A radius_x,y angle large,sweep x,y
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 30,15 0 0,0 70,20'" path_arc.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 30,15 0 0,1 70,20'" path_arc2.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 30,15 0 1,0 70,20'" path_arc3.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 30,15 0 1,1 70,20'" path_arc4.gif
![[IM Output]](../static/img/draw/path_arc.gif)
![[IM Output]](../static/img/draw/path_arc2.gif)
![[IM Output]](../static/img/draw/path_arc3.gif)
![[IM Output]](../static/img/draw/path_arc4.gif)
flag kedua, 'sweep', sekadar menentukan di sisi mana dari arah garis lurus path itu busurnya digambar. Flag 'large' dipakai untuk memilih lintasan yang lebih panjang, yang melingkari pusat elips. Artinya, sudut busurnya akan lebih besar dari 180 derajat. Bila dimatikan, yang didapat adalah 'busur' yang lebih kecil, tidak mencakup pusat elips, dan membusur pada sudut kurang dari 180 derajat. Menutup busur dengan 'Z' hanya menggambar satu segmen garis lurus terakhir. Untuk membuat elips atau lingkaran penuh diperlukan sedikitnya dua segmen 'arc', dari titik pertama ke titik kedua, lalu kembali ke titik pertama. Kedua busur harus punya pengaturan 'sweep' yang sama, sehingga busurnya berada di sisi yang berbeda karena arah geraknya berbeda. Salah satu busur harus memakai pengaturan 'large'. |
# Closed and angled elliptical arcs (defined by two edge points)
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 30,20 20 0,0 70,20 Z '" path_arc5.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 30,20 20 1,1 70,20 Z '" path_arc6.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 30,20 20 0,0 70,20 \
A 30,20 20 1,0 30,40 Z '" path_arc7.gif
![[IM Output]](../static/img/draw/path_arc5.gif)
![[IM Output]](../static/img/draw/path_arc6.gif)
![[IM Output]](../static/img/draw/path_arc7.gif)
Perhatikan bahwa kalau garisnya terlalu panjang untuk muat pada ukuran elips yang diberikan pada sudut tersebut, ukuran elipsnya akan diperbesar agar muat, dengan elips berpusat pada garis itu. Artinya, dengan memakai angka kecil untuk jari-jari sumbunya, kita cukup menentukan rasio panjang sumbu dan terjamin bahwa lintasan garis langsungnya melewati titik pusat elips. Dengan kata lain, path itu membentuk diameter elips dari satu sisi ke sisi lainnya. Ini belum tentu sumbu mayor atau minor elips, sekadar sebuah diameter elips. |
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 3,2 45 0,0 70,20'" path_arc_x.gif
![[IM Output]](../static/img/draw/path_arc_x.gif)
Tentu saja memakai panjang "1,1" menghasilkan setengah lingkaran yang sempurna, dari satu titik ke titik berikutnya. Sudut elipsnya dalam hal ini tidak berpengaruh. |
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 1,1 0 0,0 70,20'" path_hcircle.gif
![[IM Output]](../static/img/draw/path_hcircle.gif)
Untuk lingkaran penuh yang berpusat di antara kedua titik, pakailah... |
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 1,1 0 0,0 70,20
A 1,1 0 1,0 30,40 Z'" path_circle.gif
![[IM Output]](../static/img/draw/path_circle.gif)
Definisi 'arc' pada SVG juga menyatakan bahwa bila salah satu dari kedua jari-jarinya nol, yang digambar adalah garis lurus. Jadi busur mana pun dengan jari-jari "0,0" hanyalah busur berupa garis lurus sederhana... |
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 0,0 0 0,0 70,20'" path_arc_line.gif
![[IM Output]](../static/img/draw/path_arc_line.gif)
Kalau jari-jari busurnya ditentukan sangat besar dan lintasan baliknya tidak diberi 'large sweep', bentuk lensa berjari-jari itu dapat dibuat di antara kedua titik. |
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 50,50 0 0,0 70,20
A 50,50 0 0,0 30,40 Z'" path_lens.gif
![[IM Output]](../static/img/draw/path_lens.gif)
Busur jenis ini adalah fitur penting. Dengan mudah ia dapat memberi lengkung kecil tetapi jelas pada garis yang sebenarnya lurus. Misalnya, alih-alih segitiga sederhana seperti ini... |
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 20,55 L 25,10 L 70,5 L 20,55 Z' " triangle.gif
![[IM Output]](../static/img/draw/triangle.gif)
Tiap garis dapat diganti dengan busur berjari-jari besar untuk memberinya lengkung yang tipis saja. |
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 20,55 A 100,100 0 0,0 25,10
A 100,100 0 0,0 70,5
A 100,100 0 0,0 20,55 Z' " triangle_curved.gif
![[IM Output]](../static/img/draw/triangle_curved.gif)
Titik ujung garis-garisnya tidak berubah; yang terjadi hanyalah tiap 'L' diganti oleh segmen busur. Namun ukuran busurnya semestinya sebanding dengan panjang garisnya. Karena saya tidak melakukan itu, garis diagonal yang lebih panjang punya lengkung yang jauh lebih kuat dan dalam daripada dua garis lainnya. Ingat, saat mengubah ukuran atau menskalakan objek yang digambar, jari-jarinya harus diskalakan sebesar perubahan panjang garis itu juga supaya lengkungnya ikut berubah ukuran dan busurnya ikut terskalakan dengan benar. Perhatikan bahwa flag 'sweep' mengendalikan apakah lengkungnya menggembung ke luar atau ke dalam, sesuai arah penggambaran tiap segmen path (lihat di atas). |
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 20,55 A 100,100 0 0,0 25,10
A 100,100 0 0,1 70,5
A 100,100 0 0,1 20,55 Z' " triangle_bulge.gif
![[IM Output]](../static/img/draw/triangle_bulge.gif)
Segitiga bertepi lurus yang tadinya tampak 'kaku' kini agak mirip layar yang terisi angin. Kalau garisnya benar-benar ingin dibuat lurus sempurna tanpa mengubahnya kembali menjadi segmen garis sungguhan, lengkungnya dapat dimatikan dengan memakai jari-jari busur nol. Jadi busur tidak hanya bagus untuk menghasilkan elips dan lingkaran, tetapi juga berguna untuk menggambar segmen garis yang lurus maupun yang sedikit melengkung. Ia adalah path draw titik-ke-titik yang sangat serbaguna. Satu alternatif sederhana selain busur elips untuk menghasilkan segmen garis melengkung yang terpisah adalah segmen Quadratic Bezier. Bedanya yang utama, yang dipakai adalah satu titik kendali, bukan jari-jari lingkaran, untuk mendefinisikan busurnya. Cara itu juga membuat busurnya bisa dicondongkan ke salah satu ujung segmen garis, dengan konsekuensi busur simetris jadi lebih sulit dibuat. Tentu saja keduanya bisa dicampur sesuai kebutuhan. Contoh diagram lingkaran Sebagai penutup pembahasan busur, mari kita beri contoh pemakaiannya untuk menghasilkan juring lingkaran. Tentu saja mungkin diperlukan sedikit matematika trigonometri dari luar (seberapa bagus nilai matematika SMA Anda?) untuk menentukan titik akhir path yang dibutuhkan. |
magick -size 140x130 xc:white -stroke black \
-fill red -draw "path 'M 60,70 L 60,20 A 50,50 0 0,1 68.7,20.8 Z'" \
-fill green -draw "path 'M 60,70 L 68.7,20.8 A 50,50 0 0,1 77.1,23.0 Z'" \
-fill blue -draw "path 'M 68,65 L 85.1,18.0 A 50,50 0 0,1 118,65 Z'" \
-fill gold -draw "path 'M 60,70 L 110,70 A 50,50 0 1,1 60,20 Z'" \
-fill black -stroke none -pointsize 10 \
-draw "text 57,19 '10' text 70,20 '10' text 90,19 '70' text 113,78 '270'" \
piechart.jpg
![[IM Output]](../static/img/draw/piechart.jpg)
Perhatikan bahwa semua busur digambar di sebelah kiri 'lintasan garis' dan diberi flag yang sesuai (memakai flag 'sweep'). Tetapi kalau busurnya mencakup sudut lebih dari 180 derajat, flag 'large' perlu disetel. Lihat komponen 'gold' terakhir pada contoh di atas. Perhatikan juga bahwa tiap juring harus digambar utuh, meskipun itu berarti garis pembatasnya mungkin harus digambar dua kali. Kalau tidak, kemungkinan besar juring itu tidak terisi warna sepenuhnya, atau warna fill-nya menimpa garis luar juring yang digambar sebelumnya. Satu-satunya cara menghindari penggandaan garis adalah menggambar semua area terisi lebih dulu, lalu mengulanginya untuk menggambar garis luarnya. Artinya, semuanya harus digambar dua kali dan harus dipastikan cocok satu sama lain. Jadi, menggandakan garis luar mungkin memang solusi yang paling sederhana. Kurva Cubic Bezier dapat didefinisikan memakai fungsi 'C' yang menentukan dua titik kendali dan titik akhir. Untuk melanjutkan kurva Cubic Bezier yang memakai cerminan titik kendali terakhir (agar kurvanya kontinu), pakailah fungsi 'S'. Berikut contohnya. Karena fungsi ini rumit, saya sudah menyiapkan kanvas yang menunjukkan letak titik-titik kendali sekaligus 'cerminan yang diandaikan' dari titik kendali terakhir. |
# Cubic Bezier: C control_1_x,y control_2_x,y x,y
# Smooth " : S control_2_x,y x,y
magick path_cubic_canvas.gif -fill white -stroke black \
-draw "path 'M 10,30 C 10,4 50,4 50,30 S 90,55 90,30' " \
path_cubic.gif
![[IM Output]](../static/img/draw/path_cubic.gif)
Garis yang menghubungkan titik kendali ke titik akhir path pada segmen tersebut (garis kendali) pada dasarnya menentukan arah kurva saat melewati titik itu. Garis kendali yang panjang menghasilkan kurva yang lebih mulus di titik tersebut, sedangkan garis yang pendek menghasilkan kurva yang lebih tajam. Kalau titik kendalinya berimpit dengan titik kurva (panjang garis kendalinya nol), kurvanya akan patah tajam di titik itu, seolah-olah hanya segmen garis lurus yang dipakai. Sebagai contoh yang lebih praktis, potongan kode berikut diambil dari Skrip pembuat logo IM Examples yang membuat area cipratan melengkung pada logo IM Examples Bagian yang berliku dari contoh ini adalah bahwa saya mengubah string path Cubic Bezier yang saya pakai menjadi path lain yang menunjukkan garis kendali yang dipakai untuk menghasilkan kurva bezier itu. Dengan begitu saya bisa melihat sudut dan panjang garis kendali kurvanya, sehingga hasilnya jauh lebih mudah disesuaikan. Hanya satu himpunan titik yang perlu disesuaikan untuk menampilkan kurva sekaligus kendalinya, sehingga kesalahan dapat ditekan seminimal mungkin. |
curve="M 12,27 C 7,37 18,50 18,60 S 0,80 10,94
S 40,74 50,78 S 60,99 76,95 S 72,70 75,65
S 95,55 95,42 S 69,37 66,32 S 67,2 53,7
S 43,17 35,22 S 17,17 12,27 Z"
c_ctrls=`echo $curve | \
sed '1s/\([0-9]\) *\([0-9]\)/\1 M \2/;
s/S/M/g; s/C/ /;' -`
magick -size 100x100 xc:white \
-draw "stroke None fill Green path '$curve'" \
-draw "stroke Red fill None path '$c_ctrls'" \
curvy_splash.gif
![[IM Output]](../static/img/draw/curvy_splash.gif)
Kalau gambarnya diperhatikan baik-baik, akan terlihat bahwa awal dan akhir kurva punya dua garis kendali yang menghadap ke arah berlawanan. Untuk path tertutup yang kontinu, garis kendali awal dan akhir harus punya sudut yang sama (hanya berlawanan arah cermin) dan tentu saja panjang yang sama pula. Ini penting diingat, karena mudah sekali keliru. Semua titik lain di sepanjang kurva hanya punya satu titik/garis kendali yang mengarah ke arah berlawanan dengan arah penggambaran kurva. Makin panjang segmen garis itu, makin tidak 'tajam' kurvanya di titik kendali tersebut, dan panjang nol menghasilkan 'sudut runcing'. Fungsi 'S' secara internal menghasilkan titik/garis kendali cerminan untuk segmen berikutnya dari data segmen sebelumnya, sehingga kurvanya berlanjut dengan mulus. Untuk contoh lain fungsi path ini, lihat SVG: perintah kurva Cubic Bezier. Membuat kurva Bezier secara manual relatif mudah tanpa perlu perkakas GUI yang canggih.
- Pertama, tentukan semua titik koordinat yang ingin dilalui kurva, lalu ulangi koordinat awalnya di akhir daftar.
- Sekarang perluas daftar itu dengan menggandakan semua titik koordinat x,y menjadi pasangan, lalu tambahkan fungsi '
S' (Smooth Cubic) di depan tiap pasangan. Angka pertama pada tiap pasangan adalah titik kendali yang terhubung ke angka kedua yang mewakili titik pada kurva. Namun pada pasangan titik pertama urutannya terbalik: titik pertama adalah awal kurva dan titik kedua mewakili titik kendali terbalik yang pertama dan satu-satunya. - Ubah huruf fungsi pada pasangan koordinat pertama dari '
S' menjadi 'M', lalu tambahkan 'C' di antara pasangan koordinat itu. Terakhir, hapus 'S' dari pasangan koordinat kedua untuk melengkapi fungsi Cubic ('C') yang pertama. - Lengkapi path dengan menambahkan '
Z' di akhir untuk menutup kurvanya.
Lihat contoh urutan di atas untuk melihat bentuk seharusnya. - Pada tahap ini path Anda sudah bisa dicoba digambar. Path itu hanya akan terdiri dari segmen garis lurus, karena semua garis kendalinya berpanjang nol.
- Yang perlu dilakukan sekarang hanyalah menyetel posisi segmen garis kendali (koordinat pertama dari tiap pasangan '
S') pelan-pelan dan hati-hati sampai kurvanya sesuai keinginan. Jangan membuat garis kendali terlalu panjang, atau mengarah ke arah yang keliru, karena kurvanya akan tampak sangat aneh. - Untuk membantu melihat perubahan dan menemukan kesalahan, gunakan perintah konversi "
sed" di atas guna menggambar garis kontrol antara titik kontrol path dan titik kontrol kurva. Perhatikan bahwa garis kontrol berpanjang nol tidak terlihat, tetapi karena garisnya menghasilkan sudut yang tajam, posisinya tetap jelas. - Terakhir, pastikan titik/garis kontrol pertama sesudah '
C' benar-benar berlawanan arah dengan garis kontrol penutupnya, pada posisi yang sama.
Pembuatan kurva secara interaktif juga dimungkinkan dengan sejumlah editor grafik vektor. Sebagai contoh, Luis Guerra melaporkan bahwa kurva bezier yang dihasilkan "Inkscape" bisa diakses lewat fungsi "Edit -> XML Editor", lalu memilih path atau bentuk yang titik kontrolnya ingin dilihat.
Tahukah Anda cara lain untuk mengekstrak kurva bezier (yang memberi dua atau satu titik kontrol per titik pada kurva) dengan perkakas GUI? Atau mungkin teknik lain untuk menghasilkan kurva semacam itu? Kirimi saya email! Saya senang sekali mendengarnya. Nama Anda akan dicantumkan, seperti yang sudah-sudah.
Quadratic Bezier adalah penyederhanaan dari fungsi Cubic Bezier, yaitu ketika dua titik kendalinya dilebur menjadi satu titik kendali saja. Sekali lagi, kurvanya bisa dimulai dengan fungsi 'Q', lalu dilanjutkan dengan fungsi 'T' yang mencerminkan titik kontrol terakhir. |
# Quadratic Bezier: Q control_x,y x,y
# Smooth " : T x,y
magick path_quad_canvas.gif -fill white -stroke black \
-draw "path 'M 10,30 Q 20,4 50,30 T 90,30' " \
path_quad.gif
![[IM Output]](../static/img/draw/path_quad.gif)
Perlu saya ingatkan, fungsi penerus 'T' sebenarnya hanya bekerja untuk path yang menghubungkan titik-titik berjarak sama. Saya tidak menyarankan pemakaiannya. Keunggulan Quadratic Curve adalah sebagai pengganti Busur elips, karena yang dipakai posisi sebenarnya, bukan radius busur. Busurnya juga bisa dicondongkan ke salah satu ujung, hal yang tidak praktis bila memakai Busur elips. |
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 20,55 Q 30,32 25,10
Q 50,1 70,5
Q 50,45 20,55 Z' " triangle_bulge_2.gif
![[IM Output]](../static/img/draw/triangle_bulge_2.gif)
Dalam kasus ini busurnya tidak begitu seragam, dan hasilnya lebih mirip sirip hiu terbalik ketimbang layar perahu. Ingat bahwa Quadratic Arc berupa parabola, sedangkan busur elips pada dasarnya menghasilkan segmen lingkaran. Inilah yang mungkin menjadi kunci untuk menentukan jenis segmen garis melengkung mana yang sebaiknya dipakai. Untuk contoh lain fungsi path ini lihat SVG: perintah kurva Quadratic Bezier.
Pelengkungan permukaan gambar
Selain kemampuan-kemampuan itu, permukaan gambar tempat objek digambar juga bisa dilengkungkan dengan berbagai cara sehingga hal-hal menakjubkan bisa dikerjakan. Pertama, beberapa modifikasi umum atas permukaan gambar dapat diterapkan, seperti... 'translate', 'rotate', 'scale', 'skewX', 'skewY', dan 'affine'. Sebagai contoh, dari sebuah 'path' berisi garis kita bisa me-"translate" titik asal atau titik 0,0 permukaan gambar ke lokasi lain. |
magick -size 100x60 xc:skyblue \
-draw "translate 50,30
image over 3,3 0,0 'terminal.gif'
fill white stroke black
path 'M 0,20 -45,20 20,-25 -25,-25'
fill none stroke red
path 'M 0,10 0,-10 M 10,0 -10,0' " transform_translate.gif
![[IM Output]](../static/img/draw/transform_translate.gif)
Perhatikan bahwa '0,0' atau titik asal area gambar kini berada di tengah gambar, meski sumbu Y tetap bernilai negatif di bagian atas dan positif di bagian bawah gambar. Operasi "rotate" memutar permukaan gambar sehingga apa pun yang digambar sesudahnya pada permukaan itu ikut terputar. Tentu saja perputarannya mengelilingi titik asal hasil translate, jadi sebaiknya kedua operator transformasi ini dipakai bersamaan. |
magick -size 100x60 xc:skyblue \
-draw "translate 50,30 rotate -30
image over 4,4 0,0 'terminal.gif'
fill white stroke black
path 'M 0,20 -45,20 20,-25 -25,-25'
fill none stroke red
path 'M 0,10 0,-10 M 10,0 -10,0' " transform_rotate.gif
![[IM Output]](../static/img/draw/transform_rotate.gif)
"scale" memperbesar dan memperkecil permukaan gambar di sekitar titik asal. |
magick -size 100x60 xc:skyblue \
-draw "translate 50,30 scale 1.5,1.5
image over 4,4 0,0 'terminal.gif'
fill white stroke black
path 'M 0,20 -45,20 20,-25 -25,-25'
fill none stroke red
path 'M 0,10 0,-10 M 10,0 -10,0' " transform_scale.gif
![[IM Output]](../static/img/draw/transform_scale.gif)
Salah satu pemakaian umum "scale" adalah membalik sumbu Y supaya nilai Y positif mengarah ke atas. Tentu titik asalnya juga perlu dipindahkan ke tengah atau ke sudut kiri bawah agar semuanya tetap rapi. |
magick -size 100x60 xc:skyblue \
-draw "translate 50,30 scale 1,-1
image over 4,4 0,0 'terminal.gif'
fill white stroke black
path 'M 0,20 -45,20 20,-25 -25,-25'
fill none stroke red
path 'M 0,10 0,-10 M 10,0 -10,0' " transform_flip.gif
![[IM Output]](../static/img/draw/transform_flip.gif)
Dan terakhir, "skewX" dan "skewY" memiringkan gambar pada arah X dan Y. Sebagai contoh, di sini kita memakai "skewX" untuk memiringkan sumbu Y vertikal gambar. |
magick -size 100x60 xc:skyblue \
-draw "translate 50,30 skewX 20
image over 4,4 0,0 'terminal.gif'
fill white stroke black
path 'M 0,20 -45,20 20,-25 -25,-25'
fill none stroke red
path 'M 0,10 0,-10 M 10,0 -10,0' " transform_skewY.gif
![[IM Output]](../static/img/draw/transform_skewY.gif)
Operator-operator ini punya padanan di luar string MVG "-draw" untuk pemakaian umum. Namun versi baris perintahnya adalah operator dan langsung diterapkan pada gambar yang sudah ada di memori, bukan pada permukaan gambar yang objek vektornya belum digambar. Selengkapnya lihat Mendistorsi gambar.
Pelengkungan affine pada permukaan gambar
Kelima transformasi kanvas di atas dapat digabungkan menjadi satu Affine Matrix Operator umum, baik lewat primitif MVG 'affine' maupun dengan menetapkan transformasi affine memakai "-affine" sebelum memanggil "-draw". Transformasi affine memakai sekumpulan 'Matrix Coefficients ' yang menentukan bagaimana koordinat yang Anda berikan diubah menjadi koordinat gambar yang sebenarnya. Untuk keterangan lebih rinci tentang cara kerja 'coefficients' ini, lihat Affine Matrix Transforms. Sebagai contoh... untuk sekadar menetapkan titik asal di tengah sebagai acuan objek yang digambar... |
magick -size 100x60 xc:skyblue \
-draw "affine 1,0,0,1,50,30
image over 4,4 0,0 'terminal.gif'
fill white stroke black
path 'M 0,20 -45,20 20,-25 -25,-25'
fill none stroke red
path 'M 0,10 0,-10 M 10,0 -10,0' " affine_null.gif
magick -size 100x60 xc:skyblue \
-draw "affine 1,0,0,-1,50,30
image over 4,4 0,0 'terminal.gif'
fill white stroke black
path 'M 0,20 -45,20 20,-25 -25,-25'
fill none stroke red
path 'M 0,10 0,-10 M 10,0 -10,0' " affine_flip.gif
![[IM Output]](../static/img/draw/affine_flip.gif)
Memutar 30 derajat mengelilingi titik asal... |
magick -size 100x60 xc:skyblue \
-draw "affine .866,-.5,.5,.866,50,30
image over 4,4 0,0 'terminal.gif'
fill white stroke black
path 'M 0,20 -45,20 20,-25 -25,-25'
fill none stroke red
path 'M 0,10 0,-10 M 10,0 -10,0' " affine_rot.gif
![[IM Output]](../static/img/draw/affine_rot.gif)
Untuk transformasi affine yang lebih rumit, tersedia Affine Helper Scripts yang memang dibuat untuk keperluan itu. Skrip-skrip ini mengubah hal seperti sudut putar dan titik pusat menjadi koordinat affine yang langsung bisa dipakai pada "-draw affine" atau pengaturan "-affine".
Push/Pop konteks
Sebagian primitif MVG sebenarnya bergantung pada transformasi ini agar bisa dipakai dengan benar. Sebagai contoh, primitif ellipse hanya bisa ditentukan langsung dengan sumbu yang sejajar ortogonal. |
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "ellipse 50,30 30,15 0,360" ellipse_orthogonal.gif
![[IM Output]](../static/img/draw/ellipse_orthogonal.gif)
Namun dengan transformasi permukaan gambar kita bisa dengan mudah menambahkan 'sudut putar' pada elips itu. |
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "push graphic-context
translate 50,30 rotate 30
fill white stroke black
ellipse 0,0 30,15 0,360
pop graphic-context" ellipse_rotated.gif
![[IM Output]](../static/img/draw/ellipse_rotated.gif)
Perhatikan bahwa 'pusat' ellipse (titik putarnya) di-translate lebih dulu, baru kemudian rotasi diterapkan. 'ellipse' lalu digambar pada posisi hasil translate itu, yaitu '0,0'. Contoh di atas juga memperlihatkan dua primitif gambar MVG yang baru: 'push graphic-context' dan 'pop graphic-context'. Keduanya sebenarnya tidak wajib pada contoh di atas, tetapi disarankan bila transformasi gambarnya besar-besaran. Yang dikerjakan primitif 'push' dan 'pop' adalah menyimpan keadaan gambar saat itu atau 'graphic-context', lalu memulihkannya kembali sesudahnya. Semua pengaturan gambar yang diubah di antara kedua primitif itu akan dilupakan. Termasuk di dalamnya pelengkungan permukaan seperti 'translate' dan 'rotate', pengaturan warna 'fill' dan 'stroke', serta apa pun yang mengubah 'state' penggambaran. Primitif ini memudahkan penggambaran objek yang sangat rumit dengan banyak transformasi, lalu mengembalikan semuanya ke keadaan yang lebih 'normal' untuk operasi gambar berikutnya. Peragaan yang lebih praktis bisa dilihat di Menggambar panah di bawah.
Push/Pop objek khusus
Sedang dikerjakan
Pengaturan tambahan yang khusus dipakai MVG untuk menangani format SVG.
font-family font-stretch font-style font-weight
encoding 'UTF-8'
push defs
push gradient 'def_name' linear X1,Y1 X2,Y2
stop-color 'color' where
stop-color 'color' where
# where is a point between the two pixels given (0 = X1,Y1 1= X2,Y2)
gradient-units 'objectBoundingBox|userSpaceOnUse'
affine ....
pop gradient
push gradient 'def_name' radial CX,CY FX,FY R
# Here CX,CY is the center of the radial gradient of radius R
# the FX,FY is the focal, and is usually the same a CX,CY
# unless you are trying to warp the gradient in a specific direction
stop-color 'color' where
...
pop gradient
pop defs
push graphic-context
fill 'url(#def_name)'
... draw things here ...
pop graphic-context
Untuk contohnya lihat situs pengembangan _Florent Monnier_...
<http://www.linux-nantes.fr.eu.org/~fmonnier/OCaml/MVG/>
Membaca file MVG Seperti terlihat pada contoh-contoh di atas, argumen MVG untuk "-draw" bisa menjadi sangat panjang. Konversi SVG ke MVG bahkan dapat menghasilkan argumen gambar MVG yang teramat panjang (lihat di bawah). Untungnya antarmuka baris perintah IM secara umum memperbolehkan pembacaan argumen string mana pun dari sebuah file lewat argumen "@filename". Ini praktis, sebab artinya perintah gambar MVG yang panjang dan rumit bisa dibaca dari file terpisah. Sebagai contoh, kalau saya menaruh operasi MVG ke dalam file bernama "draw_circles.mvg", saya bisa menggambarnya seperti ini... |
magick -size 100x60 xc:skyblue -draw @mvg_circles.mvg mvg_draw.gif
| ![[IM Output]](../static/img/draw/mvg_draw.gif)
Bukan itu saja, ImageMagick juga mengerti pembacaan format file gambar "MVG:" secara langsung, sehingga perintah semacam itu bisa digambar dengan lebih langsung pula. Namun, kecuali file MVG-nya mendefinisikan kanvas sendiri, kanvas awalnya mungkin perlu ditentukan ("-size" dan "-background") sebagai tempat menggambar. |
magick -size 100x60 -background limegreen mvg_circles.mvg mvg_file.gif
| ![[IM Output]](../static/img/draw/mvg_file.gif)
Pengaturan kanvas awal bisa dipindahkan ke dalam gambar MVG dengan menambahkan 'viewbox' pada file MVG, disertai penggambaran isian warna latar belakang yang sesuai. Dengan begitu file gambar MVG menjadi definisi gambar yang lengkap. |
magick mvg_circles2.mvg mvg_image.gif
| ![[IM Output]](../static/img/draw/mvg_image.gif)
| _Saat ini hanya ada satu cara membaca file MVG eksternal dari dalam string argumen MVG, yaitu memakai primitif gambar 'image'. Sayangnya cara ini mengonversi MVG yang disertakan menjadi gambar raster SEBELUM gambar itu ditimpakan ke permukaan gambar.
Dengan kata lain, saat ini belum ada fungsi 'include' MVG._ :-(
Sedang dikerjakan
Operasi gambar tingkat rendah IM bisa dihasilkan dengan "[+render](https://imagemagick.org/command-line-options/#render)" untuk merekamnya.
Ketika kemudian pengaturan/operator "[-render](https://imagemagick.org/command-line-options/#render)" diberikan, IM langsung menggambar
operasi yang tersimpan itu.
Anehnya, sekadar mengeluarkan hasil ke file "MVG" pun tampaknya melakukan hal yang sama...
magick ... -draw '....' draw_commands.mvg
CATATAN: bila kurva digambar sambil mengeluarkan file berformat MVG, file itu
mencatat kurvanya sebagai deretan segmen garis pendek, bukan kurva aslinya.
Tentu saja langkahnya bisa diteruskan sepenuhnya dengan memakai format SVG yang lebih universal.
Lihat "[Penanganan format SVG](#svg)" di bawah.
Komposisi alpha MVG
Sedang dikerjakan
Saya belum pernah melihat komposisi alpha dipakai (selain algoritma 'painters'
yang pada dasarnya komposisi alpha 'over') untuk penggambaran objek.
Meski begitu, bukan berarti hal itu tidak bisa dilakukan.
Kalau rectangle, ellipse, circle, atau apa pun ingin dikomposisikan dengan
komposisi alpha yang lain (misalnya 'DstOver' yang mirip komposisi Under),
gambarlah bangun itu pada kanvas transparan kosong berukuran sama dengan
aslinya, lalu komposisikan ke gambar Anda.
Namun karena SVG memungkinkan komposisi alpha dipakai untuk menggambar teks dan
benda lain ke atas gambar, saya membayangkan hal itu akan ditambahkan kelak.
Nantikan saja!
Menggambar simbol
Kadang ada sekumpulan titik pada sebuah gambar yang ingin ditandai dengan simbol acuan, seperti tanda silang, lingkaran, dan sebagainya... Sayangnya sampai saat ini IM belum punya perintah untuk menggambar simbol semacam itu dengan mudah, tetapi dengan sedikit kerja tambahan simbol seperti itu tetap bisa digambar.
Teknik menggambar simbol
Kiat menggambar banyak simbol pada sederet lokasi tertentu adalah membangkitkan perintah gambar MVG lewat skrip shell, atau lewat API apa pun yang Anda pakai, sehingga sekumpulan titik yang diberikan berubah menjadi kumpulan perintah gambar yang sesuai. Sebagai contoh, di sini saya mengubah sederet titik menjadi tanda 'plus' pada masing-masing titik itu... |
# Define a string of X and Y coordinates
# comma between values, space between coordinates.
points="6.6,7.7 25.0,75.0 42.2,85.4 75.8,94.7 51.5,39.3 92.5,66.6"
# magick each point into a draw command for a cross (using 'awk')
# the 'tr' converts spaces into 'newlines' (one point per line).
crosses=`echo $points | tr -s ' ' '\012' |\
awk -F, '{ print "line " $1-3 "," $2 " " $1+3 "," $2 ;
print "line " $1 "," $2-3 " " $1 "," $2+3 ; }' -`
# draw a red line between the points, and blue crosses on the points.
magick -size 100x100 xc:white \
-draw "fill none stroke red polyline $points " \
-draw "fill none stroke blue $crosses " \
points_plus.gif
![[IM Output]](../static/img/draw/points_plus.gif)
Contoh di atas memakai "tr" untuk memisahkan tiap titik (dua angka) menjadi satu titik per baris, lalu memakai "awk" untuk semua perhitungan matematis yang diperlukan guna menggambar 'plus' di atas titik tersebut. Silakan pakai apa saja yang Anda suka di sini, karena yang saya lakukan hanyalah semacam ekspansi makro teks atas daftar titik masukan. Hampir semua bahasa pemrograman bisa melakukannya. Untuk kasus skrip shell di atas, "awk" saya rasa cara paling sederhana dan paling cepat. Sebenarnya ImageMagick sendiri pun bisa dipakai untuk ekspansi 'makro' itu lewat opsi format "magick"... Sebagai contoh, di sini saya memakainya untuk menghitung sebuah titik pada keliling lingkaran, untuk 'simbol titik' ini. |
# Define a string of X and Y coordinates
# comma between values, space between coordinates.
points="6.6,7.7 25.0,75.0 42.2,85.4 75.8,94.7 51.5,39.3 92.5,66.6"
# circle radius (or symbol size) to draw around each point.
radius=3.5
# magick each point into a draw command for a cross
# In this case, points are space separated by the shell
circles=$(for point in $points; do
x=$(echo "$point" | cut -d, -f1)
y=$(echo "$point" | cut -d, -f2)
# use IM to do some floating point math, EG: y2=$y+$radius
y2=$(magick xc: -format '%[fx:'"$y"'+'"$radius"']' info:)
echo "circle $x,$y $x,$y2"
done)
# Draw a red line between the points, and blue circles on the points.
magick -size 100x100 xc:white \
-draw "fill none stroke red polyline $points " \
-draw "fill none stroke blue $circles " \
points_circle.gif
![[IM Output]](../static/img/draw/points_circle.gif)
String draw yang dihasilkan bisa menjadi cukup panjang, dan bisa mulai menimbulkan masalah pada panjang perintah akhir Anda. Jadi, alih-alih mengubah titik-titik itu menjadi string panjang yang lalu diteruskan ke IM lewat baris perintah, perintah gambar bisa disalurkan (pipe) ke IM sebagai sebuah file. Kali ini saya juga memakai metode gambar Path SVG, bukan metode primitif draw. Simbol yang saya hasilkan pun berupa segitiga di sekeliling tiap titik. |
# Define a string of X and Y coordinates
# comma between values, space between coordinates.
points="6.6,7.7 25.0,75.0 42.2,85.4 75.8,94.7 51.5,39.3 92.5,66.6"
# magick each point into a draw commands to draw a triangle
for point in $points; do
echo "path 'M $point m 0,-5 -4,+8 +8,0 -4,-8'"
done |\
magick -size 100x100 xc:white \
-fill none -stroke red -draw "path 'M $points' " \
-fill none -stroke blue -draw '@-' \
points_tri.gif
![[IM Output]](../static/img/draw/points_tri.gif)
Path SVG sebenarnya membuat hal ini lebih mudah, karena mengizinkan gerak piksel relatif, sehingga simbolnya bisa dirancang agar hanya perlu satu gerak absolut awal 'M' sebelum deretan 'move' dan 'line' untuk menggambar simbol itu. Karena itu perhitungan floating point sama sekali tidak diperlukan, sebab draw IM yang akan mengerjakan matematika penempatannya. | Item gerak relatif 'm' pada Path SVG rusak sebelum IM v6.4.3-5. Bila IM yang Anda pasang lebih lama dari itu, contoh di atas (dan berikutnya) mungkin tidak menggambar apa pun. Untuk versi lama, ini bisa diperbaiki dengan mengganti gerak relatif 'm' di atas dengan deretan garis relatif 'l' yang sepadan.
Langkah ini bahkan bisa dibawa satu tahap lebih jauh: umpankan sebuah file MVG yang utuh, lengkap dengan spesifikasi kanvas gambar, langsung ke IM sebagai pipeline perintah gambar. Kali ini mari kita buat 'silang' yang mirip contoh 'plus' pertama di atas, yang tadi butuh banyak perhitungan. |
# Define a string of X and Y coordinates
# comma between values, space between coordinates.
points="6.6,7.7 25.0,75.0 42.2,85.4 75.8,94.7 51.5,39.3 92.5,66.6"
# Generate a MVG file for IM to draw all components
( echo "viewbox 0 0 100 100 fill white rectangle 0,0 100 100"
echo "fill none stroke red path 'M $points'"
echo "fill none stroke blue path '"
for point in $points; do
echo " M $point m -2,-2 +4,+4 m -4,0 +4,-4"
done
echo "'"
) | magick mvg:- points_cross.gif
![[IM Output]](../static/img/draw/points_cross.gif)
Ini memakai teknik pemrograman shell khusus, yaitu segala sesuatu yang di-'echo' di dalam tanda kurung shell akan diumpankan ke perintah "magick" terakhir sebagai file MVG. 'echo' pertama mendefinisikan sekaligus mengisi kanvas gambar, sedangkan perulangan 'while' mengubah tiap 'point' yang diberikan menjadi lingkaran dengan radius yang ditentukan. Keunggulan metode ini adalah tidak adanya batasan panjang string seperti pada dua metode lainnya. Simbol lain yang bisa dihasilkan antara lain kotak, belah ketupat, error-bar, dan sebagainya... Lihat juga 'Menggambar lingkaran di bawah untuk metode lingkaran yang lain, termasuk penggambaran lingkaran 'path' relatif tanpa perhitungan.
Alternatif selain menggambar simbol
Ada cara lain menambahkan simbol ke gambar selain menggambarnya secara langsung.
Font simbol
Simbol dapat diekstrak dari font simbol lalu disimpan sebagai bitmap kecil. Gambar berwarna kecil yang sudah disiapkan pun bisa dipakai untuk keperluan ini. Namun cara ini mungkin sulit menempatkan fontnya tepat relatif terhadap piksel tertentu. Artinya, tekniknya tidak terlalu presisi. Tetapi gambar apa pun bisa dikomposisikan di lokasi piksel mana pun. Sebagai contoh, simbol-simbol ini diekstrak dari sejumlah font, khusus untuk dipakai di halaman-halaman contoh ini.
Contoh mengomposisikan gambar ke latar belakang yang lebih besar diberikan pada bagian Melapiskan gambar. Namun metode berulang mungkin lebih berguna, seperti yang diberikan di Penempatan terprogram gambar berlapis. FUTURE: example of layering images using coordinates
Morfologi
Alternatif lain adalah memakai Morfologi untuk men-'Dilate' satu piksel, dengan kernel 'shape' khusus seperti 'Disk', 'Ring', dan 'Plus', atau bahkan kernel buatan pengguna Anda sendiri. Sebagai contoh...
magick -size 80x80 xc:black -fill white \
-draw 'point 20,15 point 55,30 point 40,60' points_pixels.gif
magick points_pixels.gif -morphology Dilate Ring points_rings.gif
magick points_pixels.gif -morphology Dilate Plus:4 points_pluses.gif
magick points_pixels.gif -morphology Dilate Cross:3 points_crosses.gif
Hasilnya lalu bisa langsung diubah menjadi overlay berwarna dengan operator Alpha Shape. Keunggulan besarnya, lokasi tiap simbol satu per satu tidak perlu diketahui. Begitu pula jumlah simbolnya. Tapi itu sekaligus bisa menjadi kelemahan. Kelemahan utamanya, posisinya hanya berada di lokasi bilangan bulat. Penggambaran dengan penempatan 'sub-piksel' floating point tidak bisa dilakukan.
Konvolusi
Teknik yang hampir sama adalah memakai 'Convolve' dengan kernel yang dirancang khusus, sehingga berbagai gradasi abu-abu bisa ditetapkan, bukan sekadar hasil hidup/mati seperti di atas. Dengan memakai kernel buatan pengguna yang berbeda untuk tiap kanal gambar (merah, hijau, biru, dan alpha), simbol beraneka warna bahkan bisa dibuat dari tiap koordinat piksel.
Untuk ini saya memakai skrip khusus "image2kernel" yang saya tulis untuk mengubah gambar berwarna (lihat kanan) menjadi kernel konvolusi floating point terpisah bagi masing-masing kanal.
image2kernel -q marker.png marker.dat
Ini menghasilkan empat file, misalnya "marker_R.dat", satu untuk tiap kanal gambar masukan yang sangat kecil itu, yang merupakan representasi buatan pengguna dari gambar tersebut (dengan titik asal di tengah gambar). Dengan file data kernel itu, kini kita bisa mem-Convolve titik-titik tunggal tadi menjadi gambar penanda berwarna di atas latar belakang transparan.
magick points_pixels.gif -alpha off \
\( -clone 0 -morphology Convolve @marker_R.dat \) \
\( -clone 0 -morphology Convolve @marker_G.dat \) \
\( -clone 0 -morphology Convolve @marker_B.dat \) \
\( -clone 0 -morphology Convolve @marker_A.dat \) \
-delete 0 -channel RGBA -combine point_markers.png
| Sebelum IM v6.7.6-9, operator Combine menuntut kanal transparansi gambar diberikan sebagai nilai 'opacity', bukan nilai alpha, sehingga kanal alpha yang dihasilkan perlu dinegasikan. Contoh: |
... "`cat marker_A.dat`" -negate \) \
Sebaiknya hanya gambar kecil yang dipakai, dengan titik piksel yang cukup berjauhan supaya simbolnya tidak saling tumpang tindih. Sebab Convolve akan menjumlahkan area yang tumpang tindih, sehingga area itu menjadi lebih terang daripada yang diharapkan. Contoh di atas sudah dijadikan skrip shell UNIX "convolve_image" agar lebih mudah dipakai.
convolve_image points_pixels.gif marker.png point_markers.png
Teknik ini lahir dari diskusi di Forum IM A Fun Experience with IM. Penanyanya ingin menempatkan orang-orang mungil di atas gambar latar lapangan sepak bola sehingga posisi mereka mengeja nama seseorang di dalam gambar itu.
Berlapis
Teknik lain seperti Lapisan gambar, yang ditempatkan memakai daftar piksel hasil ekstraksi dari gambar sumber, mungkin pendekatan yang lebih baik. Gambar simbol yang lebih jauh bisa ditimpakan lebih dulu sebelum gambar latar depan, dan pemilihan simbol untuk tiap titik bisa dilakukan secara terprogram atau diacak. Untuk contohnya lihat Pin pada peta.
Menggambar lingkaran
Opsi draw menyediakan beberapa cara untuk mengerjakan hal yang sangat mendasar... menggambar lingkaran. Sebagai contoh, lingkaran bisa digambar melalui titik mana pun pada kelilingnya. Jadi Anda perlu menghitung titik pusat dan titik kedua yang berjarak sebesar jari-jari (katakanlah 25 piksel) dari titik pertama. |
magick -size 100x60 xc: -stroke Firebrick -fill tomato -strokewidth 2 \
-draw 'circle 50,30 50,55' circle_circle.gif
![[IM Output]](../static/img/draw/circle_circle.gif)
Fred Weinhaus mencatat bahwa dengan memakai translate, kebutuhan menghitung koordinat tepi lingkaran bisa dihilangkan, dan radiusnya cukup diberikan langsung. |
magick -size 100x60 xc: -stroke SeaGreen -fill PaleGreen -strokewidth 2 \
-draw 'translate 50,30 circle 0,0 25,0' circle_circle_trans.gif
![[IM Output]](../static/img/draw/circle_circle_trans.gif)
Namun untuk menggambar banyak lingkaran, cara di atas memerlukan operasi "-draw" terpisah bagi tiap lingkaran, atau memakai push konteks. Dengan ellipse, jari-jarinya bisa ditentukan langsung sebagai panjang sumbu |
magick -size 100x60 xc: -stroke Sienna -fill Wheat -strokewidth 2 \
-draw 'ellipse 50,30 25,25 0,360' circle_ellipse.gif
![[IM Output]](../static/img/draw/circle_ellipse.gif)
Lingkaran juga bisa dihasilkan dengan menggambar garis yang sangat sangat pendek memakai 'stroke-linecap round'. Lebar stroke lalu menentukan diameter lingkarannya. CATATAN: garis itu harus punya panjang (sekecil apa pun), kalau tidak draw tidak akan menggambar apa-apa. |
magick -size 100x60 xc: -stroke Blue -strokewidth 50 \
-draw 'stroke-linecap round line 50,30 50,30.0001' \
circle_line.gif
![[IM Output]](../static/img/draw/circle_line.gif)
Sayangnya teknik ini tidak bisa memberi garis tepi pada lingkaran yang dihasilkan, tetapi untuk menutup area luas, lebar stroke yang besar bisa berguna. Lihat Beberapa contoh sederhana di bawah. Metode ini memanfaatkan cara gambar Path SVG sehingga lingkarannya bisa digambar tanpa perlu menghitung koordinat tambahan. |
magick -size 100x60 xc: -stroke Blue -fill DodgerBlue -strokewidth 2 \
-draw "path 'M 50,30 m 0,25 a 1,1 0 0,0 0,-50 a 1,1 0 1,0 0,50'" \
circle_path.gif
![[IM Output]](../static/img/draw/circle_path.gif)
Hanya gerak absolut awal 'M' yang diperlukan untuk menentukan pusatnya; '25' dan '50' pada komponen path selanjutnya menentukan radius dan diameter lingkaran relatif terhadap pusat itu. | Item gerak relatif 'm' pada Path SVG rusak sebelum IM v6.4.3-5. Bila IM yang Anda pasang lebih lama dari itu, lingkarannya mungkin hanya muncul sebagai satu piksel. Untuk versi lama, ini bisa diperbaiki dengan mengganti 'm' di atas dengan 'l'.
Fred Weinhaus menambahkan metode lingkaran bezier berikut. Hasilnya sangat mendekati lingkaran sungguhan (walau tidak persis), dan memerlukan perhitungan floating point. |
r=25; cx=50; cy=30;
x1=25; x2=75; # = cx ± radius
y1=-3.25; y2=63.25; # = cy ± radius*1.275
magick -size 100x60 xc: -stroke Purple -fill Violet -strokewidth 2 \
-draw "bezier $x1,$cy $x1,$y1 $x2,$y1 $x2,$cy" \
-draw "bezier $x1,$cy $x1,$y2 $x2,$y2 $x2,$cy" \
circle_bezier.gif
![[IM Output]](../static/img/draw/circle_bezier.gif)
Kalau menggambar lingkaran yang persis tidak penting, path SVG dengan 4 segmen Bezier ini bisa dipakai; perhitungannya hanya memakai batas X dan Y lingkaran. |
r=25; cx=50; cy=30;
x1=25; x2=75; # X bounds = cx ± radius
y1=5; y2=55; # Y bounds = cy ± radius
magick -size 100x60 xc: -stroke Tomato -fill Gold -strokewidth 2 \
-draw "path 'M $cx,$y1 Q $x1,$y1 $x1,$cy T $cx,$y2 $x2,$cy $cx,$y1 z'" \
circle_bezier_path.gif
![[IM Output]](../static/img/draw/circle_bezier_path.gif)
Kalau Anda menginginkan lingkaran yang digambar sepenuhnya relatif terhadap titik awal di pusat, teknik ini bisa dipakai. Hanya nilai radius yang dipakai, sehingga mudah dibangkitkan hanya dengan fungsi string di sebuah API. |
magick -size 100x60 xc: -stroke Orange -fill LemonChiffon -strokewidth 2 \
-draw "path 'M 50,30 m 0,25 q 25,0 25,-25 t -25,-25 -25,25 25,25 z'"\
circle_bezier_path_rel.gif
![[IM Output]](../static/img/draw/circle_bezier_path_rel.gif)
Terpikir cara lain untuk menggambar lingkaran?
Menggambar panah -- menempatkan, memutar, dan menskalakan simbol
Dengan teknik di atas, simbol khusus seperti kepala panah bisa dibuat, lalu ditempatkan sehingga ujungnya persis di penghujung sebuah garis, dan digambar menimpanya.. Kalau panahnya digambar sesudah garis (situasi yang lazim), panah itu akan tergambar di atas garis. Namun ada tiga jenis panah yang bisa didefinisikan, dan tiap jenis didefinisikan dengan cara berbeda bergantung pada kegunaannya.
- Pengukuran, yaitu saat Anda sekadar ingin menandai ujung-ujung sebuah garis dengan kepala panah untuk menunjukkan batas suatu pengukuran pada diagram teknik. Sangat sederhana.
- Vektor , yang menunjukkan arah sekaligus intensitas suatu nilai.
Misalnya pada peta angin cuaca. Ekornya diperlukan dan titik 0,0 berada di ujung ekor itu. Sering kali dibuat kisi besar berisi vektor semacam ini. - Penunjuk , yang menunjukkan suatu detail.
Untuk yang ini titik 0,0 sebaiknya berada di ujung panah, atau agak di depan panahnya.
Panah pengukuran
Sekadar menambahkan kepala panah di ujung garis relatif mudah dilakukan. Pada dasarnya Anda membuat simbol 'kepala panah', lalu menggambarnya di posisi yang tepat. Sebagai contoh... |
arrow_head="l -15,-5 +5,+5 -5,+5 +15,-5 z"
magick -size 100x60 xc: -draw 'line 10,30 80,30' \
-draw "stroke blue fill skyblue
path 'M 80,30 $arrow_head' " \
arrow_horizontal.gif
![[IM Output]](../static/img/draw/arrow_horizontal.gif)
Perhatikan bahwa saya menggambar simbolnya sedemikian rupa sehingga titik awalnya persis di penghujung garis. Dengan begitu simbol itu bisa digambar mundur menimpa garis yang tadi digambar, menghasilkan simbol yang rapi dan enak dilihat. Namun panah punya arah. Anda bisa saja membuat sangat banyak definisi panah pada bermacam sudut, dan banyak program memang begitu. Tapi karena panah itu sebuah vektor, kenapa tidak memutarnya sebagai vektor saja. Perintah draw IM sudah punya rotasi gambar bawaan (pelengkungan kanvas), jadi mari kita pakai. Cara ini juga menguntungkan karena posisinya keluar dari definisi 'path' kepala panah, sehingga seluruh path bisa ditetapkan sebagai 'konstanta'... |
arrow_head="path 'M 0,0 l -15,-5 +5,+5 -5,+5 +15,-5 z'"
magick -size 100x60 xc: -draw 'line 25,55 70,10' \
-draw "stroke blue fill skyblue
translate 70,10 rotate -45
$arrow_head
" \
arrow_rotate.gif
![[IM Output]](../static/img/draw/arrow_rotate.gif)
Kalau ukuran panahnya ingin diubah, tambahkan opsi draw "scale" sesudah rotate. |
arrow_head="path 'M 0,0 l -15,-5 +5,+5 -5,+5 +15,-5 z'"
magick -size 100x60 xc: -draw 'line 25,55 70,10' \
-draw "stroke blue fill skyblue
translate 70,10 rotate -45 scale 2,2
$arrow_head
" \
arrow_scale.gif
![[IM Output]](../static/img/draw/arrow_scale.gif)
Perhatikan bagaimana panah itu membesar sambil membiarkan 'ujung'-nya tetap di tempat yang Anda tentukan. Ini aspek yang sangat penting dalam menangani panah, sebab yang berpengaruh hanyalah titik akhir dan sudut garis yang diberi panah. Urutan 'transform'-nya penting, dan sebenarnya kebalikan dari urutan pelaksanaannya. Artinya, scale diterapkan lebih dulu pada koordinat, lalu rotate, baru translate. Kalau transformasi koordinatnya tidak dikerjakan dalam urutan itu, penempatan akhir panahnya ikut terskalakan dan tidak akan berada di tempat yang kita harapkan. Selain itu, karena scale punya dua angka dan simbol kepala panah aslinya dirancang mendatar (sudut nol), lebar panah bisa diskalakan terpisah dari tingginya. Perhatikan juga bagaimana lebar stroke ikut terskalakan bersama ukuran kepala panah, sehingga semuanya tetap serasi. |
arrow_head="path 'M 0,0 l -15,-5 +5,+5 -5,+5 +15,-5 z'"
magick -size 100x60 xc: -draw 'line 25,55 70,10' \
-draw "stroke blue fill skyblue
translate 70,10 rotate -45 scale 2,1
$arrow_head
" \
arrow_scale_aspect.gif
![[IM Output]](../static/img/draw/arrow_scale_aspect.gif)
Nah, karena kanvasnya dilengkungkan untuk menggambar tiap panah, mungkin bersama banyak operasi gambar lain, Anda mungkin ingin mengerjakan semuanya dalam satu operasi "-draw". Katakanlah menggambar garisnya lalu menambahkan panah di KEDUA ujungnya, yang menuntut set warna, posisi, rotasi, dan bahkan mungkin skala yang berbeda. Artinya cakupan pelengkungan kanvas perlu dibatasi hanya pada penggambaran tiap kepala panah. Bila cakupannya tidak dibatasi, operasi gambar berikutnya bisa ikut terpengaruh dan Anda tidak akan pernah benar-benar yakin apa yang sedang dihasilkan. Untuk membatasi cakupan pelengkungan (dan semua atribut gambar lain), bagian yang bersangkutan dibungkus dalam sebuah "graphic-context" ... |
arrow_head="path 'M 0,0 l -15,-5 +5,+5 -5,+5 +15,-5 z'"
magick -size 100x60 xc: \
-draw "stroke black fill none
path 'M 10,40 A 50,50 0 0,1 90,40'
push graphic-context
stroke blue fill skyblue
translate 10,40 rotate 135
$arrow_head
pop graphic-context
push graphic-context
stroke firebrick fill tomato
translate 90,40 rotate 45
$arrow_head
pop graphic-context
" \
arrow_context.gif
![[IM Output]](../static/img/draw/arrow_context.gif)
Pada dasarnya 'push' menyimpan semua atribut gambar saat itu untuk dipakai nanti, sedangkan 'pop' memulihkan atribut tersebut, mengganti pengaturan apa pun (warna, pelengkungan, posisi, dan sebagainya) dengan pengaturan yang tadi disimpan. Artinya, sesudah 'pop', 'pelengkungan kanvas' dibatalkan dan draw kembali ke keadaan sebelum apa pun diubah. Teknik di atas hanyalah satu cara menghasilkan panah, dan cara yang bagus ketika panah digambar sebagai bagian dari pengukuran jarak, seperti pada gambar teknik.
Panah vektor
Seperti disebutkan, vektor menunjukkan arah sekaligus intensitas suatu nilai. Artinya panjang panahnya berubah-ubah, dan kepala panahnya bisa berada di posisi mana pun sejauh apa pun dari titik awal vektor. Anda bisa saja melakukan perhitungan berat untuk menentukan di mana kepala panah harus ditempatkan berdasarkan panjang dan sudut vektornya, tetapi ada cara yang jauh lebih baik, yaitu membiarkan ImageMagick yang menghitungnya. Solusinya adalah menggambar panjang vektor itu sebagai garis mendatar dengan panjang yang tepat di ruang kanvas yang dilengkungkan. Setelah garis itu tergambar, translate saja ruang gambarnya lagi ke ujung garis selagi kanvasnya masih 'melengkung'. Posisinya kini sudah benar, dengan rotasi yang tepat untuk menggambar 'kepala panah' vektornya seperti biasa. Sebagai contoh, di sini saya menghasilkan vektor sepanjang 70 piksel pada sudut -35 derajat. |
vector_head="path 'M 0,0 l -15,-5 +5,+5 -5,+5 +15,-5 z'"
indicator="path 'M 10,0 l +15,+5 -5,-5 +5,-5 -15,+5 m +10,0 +20,0 '"
magick -size 100x100 xc: \
-draw "stroke black fill none circle 20,50 23,50
push graphic-context
stroke blue fill skyblue
translate 20,50 rotate -35
line 0,0 70,0
translate 70,0
$vector_head
pop graphic-context
push graphic-context
stroke firebrick fill tomato
translate 20,50 rotate 40
$indicator
translate 40,0 rotate -40
stroke none fill firebrick
text 3,6 'Center'
pop graphic-context
" \
arrow_with_tails.gif
Panah penunjuk
Pada contoh di atas saya juga memperagakan panah penunjuk yang mengarah ke titik awal panah vektor sebelumnya. Namun alih-alih menggambar panahnya seperti tadi, saya membuatnya sebagai simbol panah terbalik yang dimulai 10 piksel dari titik asal (atau titik awal). Maksudnya, ada simbol di posisi yang ingin saya tunjuk, jadi saya justru tidak ingin panahnya persis menimpa posisi itu, melainkan sedikit menjauh darinya. Nah, meski penunjuk lebih sederhana ditangani daripada vektor dan umumnya tidak perlu panjang yang berubah-ubah, biasanya Anda ingin menambahkan teks di ujung jauh penunjuk itu untuk menjelaskan apa yang ditunjuk. Seperti sebelumnya, menghitung posisi itu bisa merepotkan, jadi untuk apa repot. Solusi penempatan teksnya pun sama seperti pada vektor. Pertahankan ruang lengkung yang tadi dipakai menggambar panah penunjuk, lalu translate titik asalnya ke pangkal ekor panah itu (40 piksel mendatar dalam ruang lengkung). Setelah posisinya berpindah, pelengkungannya bisa diputar balik di sekitar posisi baru itu, sehingga teksnya bisa digambar seperti biasa (dengan sedikit offset). Sayangnya, walaupun perataan teks bawaan 'left' berhasil pada contoh di atas, saat ini perataan teks belum bisa ditetapkan di MVG sebagai pengaturan yang terpisah dari gravity. Kalau ini menjadi masalah, ajukan permintaan di forum bug IM, dan mudah-mudahan perataan teks (yang terpisah dari penempatan gravity) menjadi kenyataan, apalagi hal itu sebenarnya bagian dari spesifikasi SVG.
Menggambar objek
Stroke warna yang lebar
Anda tidak harus menutup rapat sebuah area isian dengan path atau garis tepi untuk membuat berbagai bentuk. Dengan stroke yang sangat besar dan lebar, area serta bidang warna yang luas bisa dihasilkan pada sebuah kanvas. Sebagai contoh, busur eliptis berstroke lebar bisa menghasilkan bidang warna yang bagus, yang pernah benar-benar saya lihat dipakai untuk membuat poster. |
magick -size 100x100 xc: -fill none -stroke powderblue \
-draw 'stroke-width 70 ellipse -30,0 90,90 10,50' \
-rotate 180 arc_background.gif
![[IM Output]](../static/img/draw/arc_background.gif)
Atau Anda bisa menghasilkan senyum badut yang cukup rumit. |
magick -size 100x100 xc: \
-draw 'fill none stroke-linecap round
stroke-width 40 stroke tomato ellipse 50,0 70,70 65,115
stroke-width 2 stroke black ellipse 50,0 70,70 60,120
stroke-width 40 stroke palegreen line 50,40 50,40.01' clown.gif
![[IM Output]](../static/img/draw/clown.gif)
Apa yang bisa Anda buat? Beri tahu kami.
Silinder
Dalam sebuah diskusi di Forum IM ada pembahasan panjang tentang menggambar silinder, khususnya silinder bergradasi, dengan perintah draw ImageMagick. Kiat menggambar silinder adalah menggambar primitif 'roundrectangle' sedemikian rupa sehingga ujung-ujungnya membentuk oval. Artinya, kalau silindernya selebar 50 piksel, sudut-sudut persegi panjangnya dibulatkan sebesar 25 dan 12 piksel. Yaitu setengah lebar persegi panjang, lalu setengahnya lagi. Dengan begitu silinder hanyalah dua persegi panjang bersudut bulat yang digambar bertumpuk. 'Oval ujung' kedua yang isian warnanya lebih terang dibuat berukuran tepat dua kali lipat kedua dimensi sudut tadi. Sebagai contoh... |
magick -size 60x100 xc:white -stroke snow4 \
-fill chartreuse3 -draw 'roundrectangle 5,5 55,95 25,12' \
-fill chartreuse2 -draw 'roundrectangle 5,5 55,29 25,12' \
cylinder.gif
![[IM Output]](../static/img/draw/cylinder.gif)
Dengan mengganti warna fill pertama dengan warna gradien (memakai teknik tiling di memori), silindernya bisa dibuat tampak sedikit lebih 3d... |
magick -size 60x100 xc:white -stroke snow4 \
\( -size 1x60 gradient:chartreuse1-chartreuse4 -rotate -90 \
-write mpr:shading +delete \) \
-tile mpr:shading -draw 'roundrectangle 5,5 55,95 25,12' +tile \
-fill chartreuse2 -draw 'roundrectangle 5,5 55,29 25,12' \
cylinder_shade.gif
![[IM Output]](../static/img/draw/cylinder_shade.gif)
Dengan memperhalus gambar silindernya sedikit demi sedikit (seperti yang dibahas di forum IM), silinder yang sangat rumit dan enak dipandang bisa dihasilkan. Ini termasuk penambahan silinder kaca semitransparan di sekelilingnya, efek bayangan, dan pelabelan. Hasil akhir diskusi itu adalah skrip "cylinder_bar" yang menghasilkan bilah persentase berbentuk silinder...
cylinder_bar 95 cylinder_95.png
Skrip ini bisa menghasilkan gambar berukuran berapa pun, menyesuaikan semua parameter dengan tepat berdasarkan ukuran itu dan pengaturan lain yang didefinisikan di bagian atas skrip. Di dalamnya juga ada konsep 'ketebalan kaca', untuk membuat celah antara silinder kaca semitransparan di luar dan silinder berwarna di dalamnya. Perhatikan gradasi silinder yang begitu halus, terutama saat ujung silinder hijau bertumpang tindih dengan ujung silinder kacanya! Menakjubkan apa yang bisa dicapai dengan sedikit perencanaan di awal.
Menggambar karakter khusus di dalam string teks
Pakai tanda kutip atau backslash?
Salah satu masalah terbesar yang dihadapi orang dengan -draw adalah menggambar karakter yang juga punya arti khusus bagi shell UNIX dan baris perintah DOS, atau bahkan bagi bahasa lain seperti C, Perl, PHP, R, atau Visual Basic. Biang keroknya adalah dua jenis karakter kutip, ditambah karakter substitusi variabel seperti dolar '$' serta karakter escape shell dan ImageMagick, yaitu backslash '\'. Pada dasarnya argumen MVG untuk "-draw" perlu diapit tanda kutip, dan argumen string 'text' di dalamnya pun mungkin perlu tanda kutip tambahan. Untuk mengatasinya, pengguna biasanya memakai dua karakter kutip yang berbeda: satu untuk shell dan satu lagi untuk string teks MVG.
`**-draw '... text 0,0 "string" ...'**`
Perhatikan bahwa inilah satu-satunya pilihan nyata bagi pengguna Windows, yang punya masalah dan metode pengutipannya sendiri. Sebagai alternatif, tanda kutipnya bisa ditukar, dan dipakai...
**-draw "... text 0,0 'string' ..."**
Bentuk ini memungkinkan penyertaan substitusi variabel shell (memakai '$' tanpa escape. Memilih bentuk yang tepat akan menyelesaikan sebagian besar masalah, tetapi beberapa karakter tetap menyulitkan, dan tiap solusinya bergantung pada persis set tanda kutip mana yang dipakai, karena itu pula yang menentukan bagaimana karakter khusus harus di-escape. Berikut keempat kasus pengutipan dan penanganan karakter khususnya...
- Memakai tanda kutip tunggal untuk argumen shell,
dengan tanda kutip ganda mengapit string teks MVG.Teknik paling sederhana untuk menangani string teks draw adalah memakai kutip tunggal sebagai pembungkus argumen shell. Namun ini berarti, untuk menyertakan apostrof di dalam string yang digambar, Anda perlu keluar dari 'mode kutip tunggal' shell dan memberikan apostrof itu di luar kutip tunggalnya. Sebagai contoh, beginilah cara menangani keempat karakter khusus yang saya sebut tadi. |magick -size 250x50 xc:none -box white -pointsize 20 -gravity center \ -draw 'text 0,0 " '\'' \" $ \\ " ' \ -trim +repage text_special_sd.gif
![[IM Output]](../static/img/draw/text_special_sd.gif)
Perhatikan bahwa karena tanda dolar tidak perlu di-escape, tanda itu juga tidak bisa dipakai untuk menyisipkan isi sebuah variabel shell. Penting diingat bahwa backslash adalah satu-satunya karakter khusus yang ditangani string draw IM. Alasan keberadaannya pun semata-mata agar 'tanda kutip string draw IM' bisa di-escape, seperti yang kita lakukan di atas untuk kutip ganda. Selebihnya, semua keanehan lain disebabkan oleh shell baris perintah UNIX, bukan oleh IM. PC-DOS punya keanehannya sendiri, dan saya akan berterima kasih atas masukan tentang cara meng-escape karakter khusus ketika memakai IM dari lingkungan tersebut.
* Memakai tanda kutip ganda untuk argumen shell,
dengan tanda kutip tunggal mengapit string teks MVG.Kalau Anda memang ingin menyisipkan 'variabel shell' ke dalam string yang digambar, argumen shell terluarnya harus memakai kutip ganda. Ini membuat seluruh urusannya jauh lebih rumit, karena perlindungan dari shell hilang dan kini bukan hanya tanda dolar '$' yang harus di-escape, tetapi backslash '\' juga. Di sisi lain, shell jadi tidak perlu memakai karakter kutip tunggal sebagai pembatas akhir argumen, jadi bagian itu menjadi lebih sederhana. Mari kita rangkum hasilnya untuk daftar pendek karakter khusus kita. |
magick -size 250x50 xc:none -box white -pointsize 20 -gravity center \
-draw "text 0,0 ' \\' \" \$ \\\\ ' " \
-trim +repage text_special_ds.gif
![[IM Output]](../static/img/draw/text_special_ds.gif)
Perhatikan bahwa kalau backslash itu sendiri yang ingin digambar, string teks MVG menuntut backslash-nya digandakan (seperti pada contoh sebelumnya), tetapi shell-nya sendiri juga menuntut tiap backslash itu digandakan lagi, sehingga total ada empat backslash hanya untuk menghasilkan satu karakter tersebut. Penggandaan ini bisa dengan cepat menjadi membingungkan dan melelahkan, karena butuh banyak sekali backslash untuk mencapai yang diinginkan. Pelan-pelan saja, dan bentuk yang cocok untuk situasi Anda pasti ketemu.
* Memakai tanda kutip tunggal untuk argumen shell,
dengan tanda kutip tunggal mengapit string teks MVG. Mari kita tutup dengan rangkuman dua kombinasi pengutipan terakhir. Bagaimana keduanya diurai oleh shell dan MVG, saya serahkan kepada Anda. |
magick -size 250x50 xc:none -box white -pointsize 20 -gravity center \
-draw 'text 0,0 '\'' \'\'' " $ \\ '\'' ' \
-trim +repage text_special_ss.gif
![[IM Output]](../static/img/draw/text_special_ss.gif)
* Memakai tanda kutip ganda untuk argumen shell,
dengan tanda kutip ganda mengapit string teks MVG. |
magick -size 250x50 xc:none -box white -pointsize 20 -gravity center \
-draw "text 0,0 \" ' \\\" \$ \\\\ \"" \
-trim +repage text_special_dd.gif
Seperti terlihat, argumen "-draw" dari baris perintah harus berurusan dengan shell baris perintah sekaligus dengan escape backslash dan tanda kutip di dalam string teks MVG. Hasilnya bisa membingungkan dan merepotkan. Ingat saja bahwa shell memperlakukan kedua jenis tanda kutip secara berbeda, sedangkan string teks MVG tidak. Tentu saja pada skrip yang rumit, cara yang lebih baik mungkin menghindari shell dan segala masalah skripnya sama sekali. Caranya, baca argumen "-draw" dari sebuah file gambar MVG.
`**-draw @drawfile.mvg**`
Tentu saja karakter kutip apa pun yang dipakai tetap perlu diberi backslash, begitu pula tiap backslash di dalam teksnya. Namun ini jauh lebih sederhana daripada menghadapi sistem pengutipan dan escape milik shell berbarengan dengan milik IM. |
magick -size 500x50 xc:lightblue -font Candice -pointsize 36 \
-gravity center -draw @text_quotes.mvg text_quotes.gif
![[IM Output]](../static/img/draw/text_quotes.mvg.gif)
![[IM Output]](../static/img/draw/text_quotes.gif)
Gambar pertama berasal dari salah satu file teks "MVG" yang saya pakai. Di dalamnya tidak ada escape maupun pengutipan shell. Jadi yang ada hanyalah pengutipan dan escape MVG. Perhatikan bahwa pada contoh di atas, seandainya saya memakai kutip tunggal untuk string teks MVG, satu-satunya perubahan adalah saya perlu memberi backslash pada karakter kutip tunggal, bukan pada karakter kutip ganda di dalam string itu. Tentang karakter persen Satu hal terakhir tentang karakter 'escape' khusus pada operator "-draw text". Karakter persen '%' seharusnya tergambar 'apa adanya'. Tidak perlu melakukan apa pun yang khusus untuk menggambarnya. Kalau tidak tergambar 'apa adanya', berarti versi IM Anda sudah lama dan sebaiknya segera diperbarui. | Sampai dengan IM versi 6.2.4, karakter '%' dipakai sebagai karakter escape untuk menyertakan informasi gambar tambahan di dalam string teks yang digambar. Hal itu tidak berlaku lagi karena escape semacam itu membingungkan dan keliru ketika gambar SVG juga mencoba menggambar karakter persen.
Pemakaian 'escape' persen ini (juga escape baris baru '\n') dianggap tidak cocok dengan operator "-draw" dan dengan tujuan format MVG untuk menangani format gambar SVG. Karena itu, sejak IM versi 6.2.4 escape % tidak berfungsi lagi, dan backslash hanya meng-escape dirinya sendiri serta tanda kutip yang mengapitnya. |
magick -size 250x50 xc:none -box white -pointsize 20 -gravity center \
-draw 'text 0,0 "%w\n%h"' -trim +repage text_escapes.gif
![[IM Output]](../static/img/draw/text_escapes.gif)
Untuk keterangan lebih rinci tentang 'bug persen' ini, dan cara menghindarinya saat memakai "-draw" pada ImageMagick yang lebih lama, lihat halaman Drawing a Percent Bug. Annotate alih-alih draw Cara yang lebih baik untuk menghindari masalah semacam ini adalah memakai "-annotate" ketimbang draw untuk menggambar teks. Operator ini adalah pembungkus operator draw dan memungkinkan pemakaian seluruh kemampuan draw, tetapi dalam bentuk yang lebih sederhana. Pada dasarnya operator ini hanya perlu satu set pengutipan (untuk shell). Ini membuat urusan karakter khusus menjadi jauh lebih sederhana. Sayangnya, walau tanda kutip tidak perlu lagi di-escape untuk IM, kini ada escape persen, seperti pembacaan file '@', baris baru '\n', dan ekspansi escape persen lainnya. Sebagai contoh, dengan kutip tunggal... |
magick -size 200x50 xc:none -box white -pointsize 20 -gravity center \
-annotate 0 '\@ '\'' " $ \\ %% ' \
-trim +repage annotate_s.gif
magick -size 200x50 xc:none -box white -pointsize 20 -gravity center \
-annotate 0 "\@ ' \" \$ \\\\ %% " \
-trim +repage annotate_d.gif
![[IM Output]](../static/img/draw/annotate_d.gif)
Namun semua tanda kutip dan escape anotasi diabaikan sama sekali kalau escape '@' dipakai untuk membaca string dari sebuah file. Sebagai contoh, di sini kita menyertakan informasi lebar dan tinggi sebuah gambar! |
magick -size 200x50 xc:none -box white -pointsize 20 -gravity center \
-annotate 0 '%w\n%h' -trim +repage annotate_percents.gif
![[IM Output]](../static/img/draw/annotate_percents.gif)
Namun semua escape diabaikan sama sekali ketika string anotasi dibaca dari sebuah file. |
echo -n '@ %w\n%h' |\
magick -size 200x50 xc:none -box white -pointsize 20 -gravity center \
-annotate 0 '@-' -trim +repage annotate_file.gif
![[IM Output]](../static/img/draw/annotate_file.gif)
Untuk keterangan lebih lanjut lihat Operator penggambaran teks Annotate, dan terutama Karakter escape Annotate.
Penanganan SVG oleh IM
Driver input SVG: RSVG vs MSVG
Menangani format gambar SVG yang sesungguhnya adalah urusan yang sangat rumit. Mesinnya harus menangani seluruh aspek format itu, sebagaimana didefinisikan dokumen SVG -- Scalable Vector Graphics. Sesuatu yang menuntut banyak sekali upaya pemrograman, dan waktu. Karena itu ImageMagick menyediakan dua metode dalam menangani gambar berformat SVG. Yang pertama memakai pustaka open source RSVG, untuk mengonversi format SVG menjadi gambar raster yang tidak menyulitkan IM. Mesin ini lengkap di hampir semua aspek penanganan SVG. Metode kedua, IM mencoba mengonversi SVG menjadi MVG, memakai metode bawaan bernama MSVG. MSVG berusaha mengonversi gambar SVG menjadi bahasa penggambaran "MVG" milik operator "-draw" IM. Banyak fungsi draw MVG dibuat khusus untuk keperluan ini. Sayangnya, walau penggambaran garis dan pewarnaan dasar sudah ada, ini masih jauh dari konvertor SVG yang lengkap. Pemakaian konvertor MSVG internal dapat dipaksakan dengan membaca gambar SVG lewat format input khusus "MSVG:" (ditambahkan di IM v6.3.4). Tetapi kalau pustaka RSVG tersedia, kebanyakan ImageMagick justru akan memakainya untuk merender gambar SVG. Untuk mengetahui apa yang akan dilakukan IM Anda, pakai...
magick -list format | grep SVG
Seperti terlihat dari "RSVG" di dalam kurung, IM saya sendiri akan memakai pustaka RSVG, dengan versi yang tertera, yang ada di komputer saya. Di sini saya 'menggambar' sebuah gambar SVG kecil buatan tangan, "diagonal.svg" (sumbangan pengguna forum penciledin), yang membuat persegi panjang dengan gradien diagonal sederhana, di atas latar belakang putih. |
magick diagonal.svg diagonal_rsvg.gif
![[IM Output]](../static/img/draw/diagonal_rsvg.gif)
Sempurna. Gradien diagonal yang benar berhasil dihasilkan. Namun kalau ini dirender memakai MSVG internal (default kalau pustaka RSVG tidak tersedia)... |
magick msvg:diagonal.svg diagonal_msvg.gif
![[IM Output]](../static/img/draw/diagonal_msvg.gif)
Seperti terlihat, konversi MSVG internal gagal: yang dikembalikan gradien vertikal, bukan diagonal. Perintah MVG yang sebenarnya dibangkitkan IM juga bisa dilihat dengan mengonversi SVG langsung menjadi file MVG.
magick msvg:diagonal.svg mvg:diagonal.mvg
Mungkin Anda bisa melihat bagaimana konvertor MSVG berusaha mengonversi SVG menjadi perintah penggambaran MVG. Hal-hal yang diketahui gagal ditangani MSVG internal saat ini antara lain...
- Gradien non-vertikal (tidak ada konversi ke penanganan gradien MVG yang baru)
- Teks yang mengikuti jalur melengkung
- Perataan teks (yang terpisah dari gravity)
Namun sebagian besar aksi penggambaran dasar tetap tertangani. Ingat juga bahwa bahasa MVG sebenarnya sanggup menangani hal-hal yang tidak bisa dilakukan SVG, termasuk pemakaian gravity untuk memosisikan gambar dan teks. Gravity bukan bagian dari spesifikasi SVG, walau ia bagian tak terpisahkan dari penanganan teks dan font di IM. Ingat juga bahwa MVG tidak punya mekanisme kontainer seperti yang dimiliki SVG. Konvertor MSVG internal menggantikan kontainer XML dengan push dan pop konteks grafik (lihat keluaran MVG di atas), yang efeknya sama.
Pengaturan SVG
Format gambar SVG adalah format vektor (lihat Sepatah kata tentang format gambar vektor), sehingga gambarnya biasanya tidak punya 'ukuran' default. Sebagai gantinya, gambar itu 'digambar' atau 'dirender' pada "-density" tertentu, persis seperti postscript (density default 72 dpi). Selain itu, kalau SVG-nya tidak 'mengecat' latar belakang, warna latar belakang yang dipakai bisa ditentukan lewat pengaturan "-background". Sebagai contoh, ini gambar SVG kecil lainnya "home.svg", yang sudah 'dirender' memakai 3 density berbeda, dengan 3 latar belakang berbeda, termasuk latar belakang transparan.
magick -density 36 home.svg home_1.gif
magick -background skyblue home.svg home_2.gif
magick -density 144 -background none home.svg home_3.png
Perhatikan bahwa saya memakai gambar berformat PNG untuk versi latar belakang transparan yang lebih besar pada contoh di atas. Ini menghasilkan gambar yang lebih bersih dibanding format GIF karena adanya piksel tepi yang semitransparan. PNG selalu disarankan bila transparansi terlibat pada gambar akhir. | _Saya menemukan bahwa sebagian gambar SVG tidak mau diskalakan. Artinya, gambar itu didefinisikan dalam satuan 'piksel', bukan panjang dunia nyata seperti 'point', 'inci', atau 'milimeter'. Akibatnya, walau pengaturan "-density" bisa mengubah ukuran gambar keseluruhan (dalam satuan dunia nyata), ukuran 'piksel' tidak berubah, sehingga gambarnya sendiri tidak berubah ukuran. Namun gambar SVG semacam itu cukup langka.
Lebih buruk lagi, ada beberapa gambar SVG yang memakai campuran satuan 'piksel' dan 'point', dan kecuali penulisnya sengaja melakukan itu, hasilnya bisa benar-benar berantakan bila Anda mencoba memakainya pada density yang berbeda dari yang dimaksudkan penulisnya. Untungnya yang seperti ini bahkan lebih langka lagi.
Perbaikan sederhananya biasanya cukup mengubah semua satuan 'piksel' di dalam SVG menjadi 'point', tetapi jangan dilakukan membabi buta, siapa tahu pemakaian 'piksel' itu memang disengaja._
Penanganan keluaran SVG
Sejak IM v6.4.2, IM bisa mengonversi gambar bitmap APA PUN menjadi grafik vektor SVG! Konversinya tidak selalu berhasil, tetapi gambar yang lebih besar dan/atau lebih sederhana (seperti mask bitmap) bisa dikonversi dengan sangat baik. Sebagai contoh, di sini saya mengonversi sebuah bentuk bitmap yang buruk menjadi gambar SVG, lalu mengonversinya kembali, supaya bitmap itu menjadi bentuk halus dengan anti-aliasing yang benar.
magick -pointsize 72 -font Candice label:A -threshold 50% \
-trim +repage -bordercolor white -border 5x5 A.gif
magick A.gif A.svg
magick A.svg A.png
Namun supaya ini berhasil, pustaka 'development' "AutoTrace" harus terpasang, dan IM dikonfigurasi dengan switch "--with-autotrace". Kalau pustaka "AutoTrace" tidak terpasang dan tidak dikompilasi ke dalam IM, keluaran SVG yang dihasilkan akan berupa lingkaran satu piksel dalam jumlah sangat besar, sehingga hasilnya biner, bukan gambar SVG bergaris tepi yang halus. Gambar semacam itu jauh lebih besar sebagai perbandingan, dan sering butuh waktu sangat lama untuk dirender oleh mesin render SVG. Teknik raster ke vektor default yang lebih baik sebenarnya memang dibutuhkan, mungkin dengan teknik skeleton Morphology dan MAT. Dulu ada delegate input "autotrace:', untuk 'menghaluskan gambar bitmap masukan', yang melakukan semua langkah di atas sekaligus dengan memakai perintah "autotrace" secara langsung. Tapi terakhir kali saya lihat, delegate ini sudah lenyap. Begini dulu cara memakainya... |
magick autotrace:A.gif A_traced.png
![[IM Output]](../static/img/draw/A_traced.png)
Tentu saja ini TIDAK akan memberi Anda keluaran SVG dari perintah "autotrace", melainkan hanya menyaring gambar masukan lewat SVG untuk menghaluskannya. Sebagai alternatif, perintah "autotrace" sebenarnya bisa dipakai langsung, seperti diperlihatkan pada contoh Pemberian tepi raster ke vektor dan Skeleton memakai Autotrace. Anda mungkin juga tertarik melihat hasil kerja cancerberosgx, di Generating SVG Images yang menelaah solusi untuk mengonversi foto.
Editor grafik vektor selain IM
| ImageMagick adalah pemroses larik piksel. Umumnya ia tidak akan menyimpan gambar vektor ('MVG' satu-satunya pengecualian), hanya membacanya lalu mengonversinya menjadi larik piksel. Hal yang sama berlaku untuk editor gambar piksel lain, seperti Gimp, Photoshop, dan seterusnya. Untuk menyunting dan menangani gambar berbasis vektor, pakailah program seperti Sodipodi | Editor grafik vektor berbasis SVG |
|---|---|
| Xfig | Editor objek vektor yang sederhana, tetapi sangat bagus |
| (Bagus untuk papan tanda, peta, dan menata foto di satu halaman) | |
| Dia | |
| AutoTrace | Mengonversi bentuk dalam larik bitmap menjadi garis tepi vektor |
| Sketch | Editor vektor berbasis Python dengan teks melengkung. |
| Tentu saja ini bukan daftar yang lengkap. Bahkan banyak pengolah kata, seperti OpenOffice, Word, dan TeX, umumnya punya berbagai editor objek sederhana, walau sering sulit dipakai. Namun untuk sekadar mengonversi sebuah format grafik vektor ke format vektor lain, jangan pakai ImageMagick. ImageMagick pada dasarnya adalah, dan akan selalu jadi, pengonversi dan pengubah gambar raster atau grafik bitmap. Untuk informasi lebih lanjut lihat Sepatah kata tentang format gambar vektor. |
![[IM Output]](../static/img/draw/color_replace.png)
![[IM Output]](../static/img/draw/color_floodfill.png)
![[IM Output]](../static/img/draw/matte_floodfill.png)
![[IM Output]](../static/img/draw/rose_raw.png)
![[IM Output]](../static/img/draw/rose_gamma.png)
![[IM Output]](../static/img/draw/stroke_font.jpg)
![[IM Output]](../static/img/draw/stroke_table.jpg)
![[IM Output]](../static/img/draw/stroke_thick.jpg)
![[IM Output]](../static/img/draw/stroke_outline.jpg)
![[IM Output]](../static/img/draw/line_stroke_0_none.jpg)
![[IM Output]](../static/img/draw/line_stroke_0_red.jpg)
![[IM Output]](../static/img/draw/bound_left_mag.gif)
![[IM Output]](../static/img/draw/bound_right_mag.gif)
![[IM Output]](../static/img/draw/bound_add_mag.gif)
![[IM Output]](../static/img/draw/set_stroke_opacity.gif)
![[IM Output]](../static/img/draw/set_fill_opacity.gif)
![[IM Output]](../static/img/draw/set_lines.gif)
![[IM Output]](../static/img/draw/set_lines_fill.gif)
![[IM Output]](../static/img/draw/path_separate.gif)
![[IM Output]](../static/img/draw/affine_null.gif)
![[IM Output]](../static/img/draw/points_rings.gif)
![[IM Output]](../static/img/draw/points_pluses.gif)
![[IM Output]](../static/img/draw/points_crosses.gif)
![[IM Output]](../static/img/draw/point_markers.png)
![[IM Output]](../static/img/draw/arrow_with_tails.gif)
![[IM Output]](../static/img/draw/cylinder_95.png)
![[IM Output]](../static/img/draw/text_special_dd.gif)
![[IM Output]](../static/img/draw/annotate_s.gif)
![[IM Text]](../static/img/draw/svg_handling.txt.gif)
![[IM Text]](../static/img/draw/diagonal.svg.gif)
![[IM Text]](../static/img/draw/diagonal.mvg.gif)
![[IM Output]](../static/img/draw/home_1.gif)
![[IM Output]](../static/img/draw/home_2.gif)
![[IM Output]](../static/img/draw/home_3.png)
![[IM Output]](../static/img/draw/A.gif)
![[IM Text]](../static/img/draw/A.svg.gif)
![[IM Output]](../static/img/draw/A.png)