<?xml version="1.0" encoding="utf-8"?> 
<rss version="2.0"
  xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
  xmlns:atom="http://www.w3.org/2005/Atom">

<channel>

<title>Математик среди биологов: заметки с тегом СВГ</title>
<link>https://antonlyakh.ru/blog/tags/svg/</link>
<description>Я немного умею складывать, но от вычитания у меня всегда кружится голова</description>
<author>Антон Лях</author>
<language>ru</language>
<generator>E2 (v3559; Aegea)</generator>

<itunes:owner>
<itunes:name>Антон Лях</itunes:name>
<itunes:email></itunes:email>
</itunes:owner>
<itunes:subtitle>Я немного умею складывать, но от вычитания у меня всегда кружится голова</itunes:subtitle>
<itunes:image href="" />
<itunes:explicit></itunes:explicit>

<item>
<title>Добавляем рисунки к листьям дендрограммы в R: используем ggtree</title>
<guid isPermaLink="false">482</guid>
<link>https://antonlyakh.ru/blog/all/dobavlyaem-risunki-k-listyam-dendrogrammy-ggtree/</link>
<pubDate>Sun, 26 Nov 2023 11:31:42 +0300</pubDate>
<author>Антон Лях</author>
<comments>https://antonlyakh.ru/blog/all/dobavlyaem-risunki-k-listyam-dendrogrammy-ggtree/</comments>
<description>
&lt;p class="loud"&gt;Дендрограмма — это результат объединения данных при помощи иерархического кластерного анализа. Обычно на листьях дендрограммы написаны имена переменных — названия объектов. Рассказываю, как вместо названий показать изображения.&lt;/p&gt;
&lt;p class="foot"&gt;В конце заметки — &lt;a href="https://antonlyakh.ru/blog/all/dobavlyaem-risunki-k-listyam-dendrogrammy-ggtree/#dend-ggtree--final-code"&gt;финальный код на R&lt;/a&gt; &amp;darr;&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://antonlyakh.ru/blog/pictures/hc_compl__da_s0__x600.png" width="500" height="560" alt="" /&gt;
&lt;div class="e2-text-caption"&gt;Эта красочная дендрограмма содержит 38 листьев и разделена на 19 классов. На листьях дендрограммы написаны названия видов плоских паразитических червей из класса моногеней. Собираюсь показать вместо них изображения прикрепительных органов этих паразитов&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;h4&gt;Для работы понадобятся&lt;/h4&gt;
&lt;p&gt;Знание языка Эр.&lt;/p&gt;
&lt;p&gt;Установленный &lt;a href="https://bioconductor.org/packages/release/bioc/html/ggtree.html"&gt;пакет &lt;kbd&gt;ggtree&lt;/kbd&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;!-- [[https://cran.r-project.org/web/packages/dendextend/vignettes/dendextend.html Пакет &lt;kbd&gt;dendextend&lt;/kbd&gt;]] для построения финальных дендрограмм. --&gt;&lt;h3&gt;Создаю изображения объектов&lt;/h3&gt;
&lt;p&gt;Каждому листу дендрограммы сопоставлю маленький рисунок объекта. Для этого буду использовать два набора изображений: один — для тестирования, другой — для финальной отрисовки.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Тестовые изображения&lt;/b&gt;&lt;br /&gt;
Тестовые изображения содержат номера, идущие по порядку. Они нужны для того, чтобы было понятно, куда именно разработанный код помещает картинки.&lt;/p&gt;
&lt;p&gt;Вот код для генерации 99-ти пнг-файлов размером 20×20 пикселей. Имя файла с изображением совпадает с нарисованым номером.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;imgs_path = &amp;quot;img_indexed/&amp;quot;

for (i in 1:99) {
  # Создаю новый пнг-файл img_{i}.png с нулевыми отступами
  png(file = paste0(imgs_path, &amp;quot;img_&amp;quot;, i, &amp;quot;.png&amp;quot;), width = 20, height = 20)
  par(mar = c(0, 0, 0, 0))
  
  # Созданию рисунок числа
  plot(0, 0, type = &amp;quot;n&amp;quot;, xlim = c(0, 1), ylim = c(0, 1), xlab = &amp;quot;&amp;quot;, ylab = &amp;quot;&amp;quot;, axes = FALSE)
  text(0.5, 0.5, i, cex = 1)
  
  # Сохраняю пнг-файл
  dev.off()
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Вот сами изображения.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://antonlyakh.ru/blog/pictures/numbered-images-1-99.png" width="520" height="130" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;Вод код для их загрузки.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;imgs_indexed = c()
imgs_path = &amp;quot;imgs_indexed/&amp;quot;

for(i in 1:75) {
  imgs_indexed = c(imgs_indexed, paste0(imgs_path, &amp;quot;img_&amp;quot;, i, &amp;quot;.png&amp;quot;))
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;b&gt;Изображения объектов&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Изображение объекта — это уменьшенный рисунок прикрепительного крючка паразитического червя (моногенеи).  Вот они все:&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://antonlyakh.ru/blog/pictures/all-thumbs--ligophorus--da.png" width="480" height="48" alt="" /&gt;
&lt;/div&gt;
&lt;p class="foot"&gt;&lt;i&gt;Общая картинка создана с помощью Имедж-меджик:&lt;/i&gt; &lt;kbd&gt;montage -tile 20x2 -geometry +0+0 *.png all-anchors.png&lt;/kbd&gt;&lt;/p&gt;
&lt;p&gt;Название файла с крючком совпадает с наванием вида. Эти названия храню списком в текстовом файле &lt;kbd&gt;labels.txt&lt;/kbd&gt;. Для загрузки изображений объектов использую следующий код.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;imgs_path = &amp;quot;img_objects/&amp;quot;
# Загружаем имена меток
img_objects &amp;lt;- readLines(&amp;quot;labels.txt&amp;quot;)
# и добавляем расширение '.png'
img_objects &amp;lt;- paste0(imgs_path, img_objects, &amp;quot;.png&amp;quot;)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Изображения готовы, перейдем к дереву.&lt;/p&gt;
&lt;h3&gt;Строю дерево с картинками, используя &lt;kbd&gt;ggtree&lt;/kbd&gt;&lt;/h3&gt;
&lt;p&gt;В книге «Data integration, manipulation and visualization of phylogenetic trees» описано, &lt;a href="https://yulab-smu.top/treedata-book/chapter8.html"&gt;как прикрутить картинки к дендрограмме с помощью &lt;kbd&gt;ggtree&lt;/kbd&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://yulab-smu.top/treedata-book/9781032233574_cover_review.png" style="width: 200px" /&gt;&lt;/p&gt;
&lt;p&gt;К сожалению, приведнный в книге код у меня не заработал. Он подразумевает, что мы загружаем филодерево из файла. А я строю дерево при помощи иерархической кластеризации и преобразую ее в объект дендрограммы.&lt;/p&gt;
&lt;p&gt;&lt;kbd&gt;data&lt;/kbd&gt; — это матрица данных: &lt;a href="https://antonlyakh.ru/blog/all/obekt-i-priznaki-obekta-v-biologicheskoy-statistike/#data-matrix"&gt;в ее строках — объекты, в столбцах — признаки&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;# Выполняю иерархический кластерный анализ
dist &amp;lt;- dist(data)
hc   &amp;lt;- hclust(dist, method = &amp;quot;complete&amp;quot;) 

library(dendextend)

# Преобразую результат в дендрограмму
dend &amp;lt;- as.dendrogram(hc)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Из дендрограммы делаю филодерево.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;library(ggtree)

phylo &amp;lt;- as.phylo(dend)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Рисую дерево.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;ggtree(phylo)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;И получаю пустой каркас.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://antonlyakh.ru/blog/pictures/ggtree.png" width="500" height="560" alt="" /&gt;
&lt;div class="e2-text-caption"&gt;R: ggtree(∙)&lt;/div&gt;
&lt;/div&gt;
&lt;h4&gt;Строю дендрограмму с текстовыми листьями&lt;/h4&gt;
&lt;p&gt;Добавлю к листьям каркаса текстовые подписи при помощи функции &lt;kbd&gt;&lt;a href="https://rdrr.io/github/GuangchuangYu/ggtree/man/geom_tiplab.html"&gt;geom_tiplab(∙)&lt;/a&gt;&lt;/kbd&gt;. Чтобы подписи поместились на канве, оставлю пустое место справа при помощи &lt;kbd&gt;xlim(∙)&lt;/kbd&gt;.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;ggtree(phylo) + xlim(0, 0.7) + geom_tiplab(geom=&amp;quot;label&amp;quot;, size=2.2)&lt;/code&gt;&lt;/pre&gt;&lt;div class="e2-text-picture"&gt;
&lt;img src="https://antonlyakh.ru/blog/pictures/ggtree-+-tiplabels.png" width="500" height="800" alt="" /&gt;
&lt;div class="e2-text-caption"&gt;R: ggtree(∙) + xlim(∙) + geom_tiplab(label)&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Получилось так себе: текст мелкий и бесит рамка вокруг него, но как от нее избавиться — не понял.&lt;/p&gt;
&lt;p style="text-decoration: line-through"&gt;Метки мелкие и иногда перекрывают друг на друга. &lt;a href="https://ggrepel.slowkow.com/articles/examples.html"&gt;От перекрытия меток избавит пакет &lt;kbd&gt;ggrepel&lt;/kbd&gt;&lt;/a&gt;. Но в результате получается каша.&lt;/p&gt;
&lt;h4&gt;Рисую картинки на дендрограмме&lt;/h4&gt;
&lt;p&gt;У функции &lt;kbd&gt;geom_tiplab(∙)&lt;/kbd&gt; есть параметр &lt;kbd&gt;geom&lt;/kbd&gt;, задающий тип метки:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;one of ’text’, ’label’, ’shadowtext’, ’image’ and ’phylopic’.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;При помощи опции ’image’, можно на месте листьев нарисовать картинки.&lt;/p&gt;
&lt;p&gt;На нашей дендрограмме 38 листьев. Значит передадим ей массив из 38 тестовых изображений: &lt;kbd&gt;imgs_indexed[1:38]&lt;/kbd&gt;.&lt;/p&gt;
&lt;p class="foot"&gt;Параметр &lt;kbd&gt;size&lt;/kbd&gt; управляет размером картинок. Пришлось с ним повозиться, пока не подобрал подходящее значение.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;ggtree(phylo) + geom_tiplab(geom=&amp;quot;image&amp;quot;, aes(image=imgs_indexed[1:38]), size=.03)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;В результате выполнения кода получил ошибку:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Error in `label_geom()`:&lt;br /&gt;
! Problem while computing aesthetics.&lt;br /&gt;
i Error occurred in the 3rd layer.&lt;br /&gt;
Caused by error in `check_aesthetics()`:&lt;br /&gt;
! Aesthetics must be either length 1 or the same as the data (75)&lt;br /&gt;
x Fix the following mappings: `image`&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;После длительного анализа определил, что массив изображений должен состоять из 75 элементов, что написано в сообщении об ошибке. (&lt;nobr&gt;75 = 38 листьев * 2 &amp;minus; 1&lt;/nobr&gt;.) Но код все равно использует только 38 первых изображений. (Это хорошо.)&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;ggtree(phylo) + geom_tiplab(geom=&amp;quot;image&amp;quot;, aes(image=imgs_indexed[1:75]), size=.03)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Вуаля.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://antonlyakh.ru/blog/pictures/ggtree-+-tip-test-images.png" width="500" height="560" alt="" /&gt;
&lt;div class="e2-text-caption"&gt;R: ggtree(∙) + xlim(∙) + geom_tiplab(image)&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Подставлю сюда изображения объектов.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;ggtree(phylo) + geom_tiplab(geom=&amp;quot;image&amp;quot;, aes(image=img_objects[1:75]), size=.03)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Цель достигнута.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://antonlyakh.ru/blog/pictures/ggtree-+-tip-ligo-images-da-24x24.png" width="400" height="560" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a id="dend-ggtree--final-code"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Финальный код&lt;/h4&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;library(ggplot2)
library(ggtree)

# Формируем массив с названиями файлов с рисунками объектов.
imgs_path = &amp;quot;img_objects/&amp;quot;
img_objects &amp;lt;- readLines(&amp;quot;labels.txt&amp;quot;)
img_objects &amp;lt;- paste0(imgs_path, img_objects, &amp;quot;.png&amp;quot;)

# Выполняю иерархический кластерный анализ
dist &amp;lt;- dist(data)
hc   &amp;lt;- hclust(dist, method = &amp;quot;complete&amp;quot;) 

# Преобразую результат в филодерево
phylo &amp;lt;- as.phylo(dend)

# Рисую дерево с картинками
ggtree(phylo) + geom_tiplab(geom=&amp;quot;image&amp;quot;, aes(image=img_objects[1:75]), size=.03)&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;Итог&lt;/h3&gt;
&lt;p&gt;Результат не нравится.&lt;/p&gt;
&lt;p&gt;Дендрограмма вверху заметки — красивая, а эта — средненькая. Поэтому в следующий раз покажу, как сделать красивую дендрограмму с картинками при помощи пакета &lt;kbd&gt;dendextend&lt;/kbd&gt;.&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Если после прочтения этой прекрасной заметки вам вдруг непреодолимо захотелось меня поблагодарить, переведите мне 200 руб. на круасан с чаем. А я пока напишу что-то новенькое.&lt;/p&gt;
&lt;iframe src="https://yoomoney.ru/quickpay/fundraise/button?billNumber=wfB4EAHx4LE.231103&amp;" width="330" height="50" frameborder="0" allowtransparency="true" scrolling="no"&gt;&lt;/iframe&gt;
&lt;p&gt;​&lt;/p&gt;
</description>
</item>

<item>
<title>Двойная дуга в СВГ пути</title>
<guid isPermaLink="false">472</guid>
<link>https://antonlyakh.ru/blog/all/dvoynaya-duga-v-svg-puti/</link>
<pubDate>Sat, 05 Aug 2023 16:42:57 +0300</pubDate>
<author>Антон Лях</author>
<comments>https://antonlyakh.ru/blog/all/dvoynaya-duga-v-svg-puti/</comments>
<description>
&lt;p&gt;Это короткая техническая заметка о декодировании &lt;abbr&gt;СВГ&lt;/abbr&gt; тега &lt;var&gt;path&lt;/var&gt;.&lt;/p&gt;
&lt;p&gt;Я — ретроград и у меня есть Дельфи-библиотека для загрузки СВГ графики. Она хорошо работала до тех пор, пока в теге &lt;var&gt;path&lt;/var&gt; не встретился такой набор команд:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;... &lt;tt&gt;a25.35,25.35,0,0,0-.32,8.58,32.42,32.42,0,0,0,1.53,6.3&lt;/tt&gt; ...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths#arcs"&gt;По спецификации&lt;/a&gt; &lt;var&gt;a&lt;/var&gt; — рисует дугу и содержит семь параметров (семь чисел). А здесь их четырнадцать.&lt;/p&gt;
&lt;p&gt;Немного подумав, понял, что вторая семерка чисел определяет следующую дугу. То есть без сокращений будет так:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;... &lt;tt&gt;a25.35,25.35,0,0,0-.32,8.58 a32.42,32.42,0,0,0,1.53,6.3&lt;/tt&gt; ...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Учту это правило в библиотеке.&lt;/p&gt;
</description>
</item>

<item>
<title>Храним данные в СВГ</title>
<guid isPermaLink="true">https://antonlyakh.ru/blog/all/hranim-svoi-dannye-v-svg/</guid>
<link>https://antonlyakh.ru/blog/all/hranim-svoi-dannye-v-svg/</link>
<pubDate>Thu, 18 Oct 2018 22:26:36 +0300</pubDate>
<author>Антон Лях</author>
<comments>https://antonlyakh.ru/blog/all/hranim-svoi-dannye-v-svg/</comments>
<description>
&lt;p&gt;В СВГ можно хранить свои данные. Это могут быть дополнительные количественные или качественные характеристики векторной модели, которые не поддерживает формат СВГ.&lt;/p&gt;
&lt;p&gt;Для хранения данных придумываем новый тег &lt;abbr&gt;&lt;b&gt;newtag&lt;/b&gt;&lt;/abbr&gt;.&lt;/p&gt;
&lt;p&gt;СВГ основан на XML. Согласно спецификации надо описать каждый новый тег в ДТД файле, выложить его на сервер и сослаться в заголовке.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;&amp;lt;svg ... xmlns:newtag=&amp;quot;http://antonlyakh.ru/new-svg-tags.dtd&amp;quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;По факту работает, если указать пространство имен xhtml.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;&amp;lt;svg ... xmlns:newtag=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;И работает даже если не упоминать новый тег в заголовке.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;&amp;lt;svg ...&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Вставляем тег в текст.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;&amp;lt;svg ...&amp;gt;
  &amp;lt;newtag&amp;gt;My own data.&amp;lt;/newtag&amp;gt;
&amp;lt;/svg&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Прописываем атрибуты тега.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;&amp;lt;newtag scale=&amp;quot;1&amp;quot; scaleunit=&amp;quot;mkm&amp;quot; scalepx=&amp;quot;254&amp;quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Комбинируем  атрибуты и содержимое.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;&amp;lt;newtag scale=&amp;quot;1&amp;quot; scaleunit=&amp;quot;mkm&amp;quot; scalepx=&amp;quot;254&amp;quot;&amp;gt;Very usefull data.&amp;lt;/newtag&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Текст на русском кодируем в УТФ-8.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;&amp;lt;newtag lang=&amp;quot;ru&amp;quot;&amp;gt;╨Я╨╛╨╝╨╜╨╕, ╤З╤В╨╛ ╤А╤Г╤Б╤Б╨║╨╕╨╣ тАФ ╨╜╨╡ ╤П╨╖╤Л╨║ ╨╝╨╡╨╢╨┤╤Г╨╜╨░╤А╨╛╨┤╨╜╨╛╨│╨╛ ╨╛╨▒╤Й╨╡╨╜╨╕╤П.&amp;lt;/newtag&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Загружаем файл в браузер — браузер правильно рисует вектор и не ругается на теги.&lt;/p&gt;
&lt;p&gt;Открываем файл в Иллюстраторе или Инкскейпе. Правим. Сохраняем. Смотрим исходник — теги с данными сохранились. Иллюстратор и Инскейп теги не портят.&lt;/p&gt;
&lt;p&gt;Мало того, можно окружить новым тегом команду рисования.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;&amp;lt;newtag scale=&amp;quot;1&amp;quot; scaleunit=&amp;quot;mkm&amp;quot;&amp;gt;&amp;lt;line ... /&amp;gt;&amp;lt;/newtag&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Это скроет векторный объект, и он не будет виден ни в браузере, ни в векторных редакторах, но будет доступен парсеру.&lt;/p&gt;
</description>
</item>

<item>
<title>Вырезаем растровую графику из СВГ</title>
<guid isPermaLink="true">https://antonlyakh.ru/blog/all/vyrezaem-pixelnuyu-grafiku-iz-vectornogo-fayla/</guid>
<link>https://antonlyakh.ru/blog/all/vyrezaem-pixelnuyu-grafiku-iz-vectornogo-fayla/</link>
<pubDate>Sun, 26 Feb 2017 11:03:37 +0300</pubDate>
<author>Антон Лях</author>
<comments>https://antonlyakh.ru/blog/all/vyrezaem-pixelnuyu-grafiku-iz-vectornogo-fayla/</comments>
<description>
&lt;p&gt;Столкнулся с задачей.&lt;/p&gt;
&lt;p&gt;Есть фотография большого размера (более 4 тыс. пикселей по ширине), на которой запечатлены части живых организмов.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://antonlyakh.ru/blog/pictures/big-photo-of-ligophorus-haptor.jpg" width="800" height="600" alt="" /&gt;
&lt;div class="e2-text-caption"&gt;Большая фотография с прикрепительными структурами плоского паразитического червя&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Организмы — это паразиты. Мизерные примитивные червячки из &lt;a href="https://ru.wikipedia.org/wiki/%D0%9C%D0%BE%D0%BD%D0%BE%D0%B3%D0%B5%D0%BD%D0%B5%D0%B8"&gt;класса Моногеней&lt;/a&gt;, живущие на жабрах рыб. К жабрам они крепятся при помощи крючков.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://antonlyakh.ru/blog/pictures/ligophorus-on-liza-gills.gif" width="800" height="185" alt="" /&gt;
&lt;div class="e2-text-caption"&gt;Рисунок головы кефали из работы &lt;a href="https://ru.wikipedia.org/wiki/%D0%A1%D0%B2%D0%B5%D1%82%D0%BE%D0%B2%D0%B8%D0%B4%D0%BE%D0%B2,_%D0%90%D0%BD%D0%B0%D1%82%D0%BE%D0%BB%D0%B8%D0%B9_%D0%9D%D0%B8%D0%BA%D0%BE%D0%BB%D0%B0%D0%B5%D0%B2%D0%B8%D1%87"&gt;Световидова&lt;/a&gt; (1964); рисунок жабр и сидящего на них паразита из работы &lt;a href="http://journals.plos.org/plosone/article?id=10.1371/journal.pone.0142365"&gt;Лопеса-Беленгуэра с сотоварищами (2015)&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Разные виды червячков внешне очень похожи. Они отличаются только формой прикрепительных крючков. У каждого таксономического рода уникальная форма крючков, но среди видов одного рода различия форм не столь очевидны. Чтобы их обнаружить, надо использовать методы морфометрического анализа.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://antonlyakh.ru/blog/pictures/diversity-of-haptoral-hooks.png" width="800" height="507" alt="" /&gt;
&lt;div class="e2-text-caption"&gt;Метод главных компонент описывает разнообразие форм прикрепительных крючков разных родов моногеней. График из &lt;a href="http://link.springer.com/article/10.1007/s11230-011-9303-1"&gt;статьи Вигнона (2011)&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Для выполнения морфометрического анализа мы оцифровываем форму крючков. Загружаем фотографию в Инскейп, обводим кривыми и сохраняем результат в СВГ.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://antonlyakh.ru/blog/pictures/digitized-hooks.gif" width="800" height="600" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;Проблема в том, что готовый файл слишком много «весит». Исходный джипег размером 800 Кб, после обводки и сохранения в СВГ, толстеет до 12 Мб. В результате мне присылают мегатонные свгешки с внедренной графикой, которые надо разместить на сервере, а он не резиновый.&lt;/p&gt;
&lt;p&gt;Я не могу выбросить фото и загрузить на сервер только векторные кривые. Потому что контуры обрисовывают несколько человек, у каждого свои особенности рисования, они ошибаются, и результаты надо перепроверять. А фотография помогает проверить правильность построения контуров.&lt;/p&gt;
&lt;p&gt;Я несколько часов пытался уменьшить размер свг-файла с графикой без потери качества. Попробовал уменьшить в Инскейпе, не получилось — размеры фотографии и свг-файла остались огромными. Потом разобрался с форматом и нашел решение.&lt;/p&gt;
&lt;p&gt;Формат СВГ поддерживает два способа хранения изображений.&lt;/p&gt;
&lt;p&gt;Первый, когда графический файл внедрен в СВГ. Это не оптимальный способ, потому что  файл не сжат. Изображение состоит из последовательности символов в &lt;a href="https://ru.wikipedia.org/wiki/Base64"&gt;позиционной системе счисления с основанием 64&lt;/a&gt; (base64). Отсюда 800 Кб превращаются в 12 Мб.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://antonlyakh.ru/blog/pictures/image-embeded-in-svg.png" width="800" height="287" alt="" /&gt;
&lt;div class="e2-text-caption"&gt;Фотография, внедренная внутрь свг-файла, занимает очень много места&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Второй, когда СВГ хранит ссылку на внешний графический файл. Тогда СВГ содержит только &lt;a href="http://antonlyakh.ru/blog/all/koordinaty-krivyh-iz-svg-fayla/"&gt;описание кривых&lt;/a&gt;, а фотография хранится отдельно. Размер свг-файла становится маленьким.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://antonlyakh.ru/blog/pictures/svg-jpg.png" width="800" height="300" alt="" /&gt;
&lt;div class="e2-text-caption"&gt;Отделил «мух» от «котлет»&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Второй вариант подошел.&lt;/p&gt;
&lt;p&gt;Если бы файлов было мало, я бы все переконвертировал вручную. Но файлов много — несколько сотен.&lt;/p&gt;
&lt;p&gt;В результате написал скрипт на ПХП. Он извлекает из СВГ фотографию, сохраняет ее в виде отдельного джипега и вставляет в СВГ ссылку на этот джипег. Джипег хранится локально и называется так же, как и свг-файл.&lt;/p&gt;
&lt;p&gt;Скрипт умный. Если в разных файлах используется одна и та же фотография, скрипт сохранит ее всего один раз и во всех файлах будет правильно на нее ссылаться. Это экономит место.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://antonlyakh.ru/done/extract-images-from-svg/"&gt;&lt;b&gt;Скрипт для всех&lt;/b&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;div style="border: 5px solid #00ab66; padding: 6px 8px; margin-left: -14px; border-radius: 2px; margin-top: 2em"&gt;&lt;p&gt;Эта заметка описывает этап создания &lt;a href="http://marineparasites.org/"&gt;виртуальной  коллекции паразитов рыб Мирового океана&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;</description>
</item>

<item>
<title>СВГ Клинер — утилита, которая очищает СВГ от мусора</title>
<guid isPermaLink="true">https://antonlyakh.ru/blog/all/svg-cleaner-utilita-kotoraya-ochischaet-svg-fayl-ot-musora/</guid>
<link>https://antonlyakh.ru/blog/all/svg-cleaner-utilita-kotoraya-ochischaet-svg-fayl-ot-musora/</link>
<pubDate>Sat, 25 Feb 2017 14:35:19 +0300</pubDate>
<author>Антон Лях</author>
<comments>https://antonlyakh.ru/blog/all/svg-cleaner-utilita-kotoraya-ochischaet-svg-fayl-ot-musora/</comments>
<description>
&lt;p&gt;&lt;a href="https://github.com/RazrFalcon/SVGCleaner"&gt;СВГ Клинер&lt;/a&gt; помогает очистить СВГ-файл от мусора и без потерь уменьшить его размер.&lt;/p&gt;
&lt;p&gt;Без потерь значит, что утилита не разрушает СВГ. Она сохраняет векторные объекты и:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;удаляет данные, которые внедряют в файл векторные редакторы, не нужные для отображения свг-файла;&lt;/li&gt;
&lt;li&gt;оптимизирует структуру СВГ;&lt;/li&gt;
&lt;li&gt;удаляет невидимые или неиспользуемые элементы.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="https://github.com/RazrFalcon/SVGCleaner"&gt;&lt;b&gt;Проект на Гитхабе&lt;/b&gt;&lt;/a&gt;. Есть &lt;a href="https://github.com/RazrFalcon/svgcleaner-gui/releases"&gt;версия с графическим интерфейсом&lt;/a&gt;.&lt;/p&gt;
</description>
</item>

<item>
<title>Координаты кривых из СВГ файла</title>
<guid isPermaLink="true">https://antonlyakh.ru/blog/all/koordinaty-krivyh-iz-svg-fayla/</guid>
<link>https://antonlyakh.ru/blog/all/koordinaty-krivyh-iz-svg-fayla/</link>
<pubDate>Wed, 30 Dec 2015 19:29:28 +0300</pubDate>
<author>Антон Лях</author>
<comments>https://antonlyakh.ru/blog/all/koordinaty-krivyh-iz-svg-fayla/</comments>
<description>
&lt;p&gt;&lt;span style="display: inline-block; font-weight: bold; padding: 6px; background-color: #7fC"&gt;&lt;a href="http://antonlyakh.ru/download/?svg2txt"&gt;Скачать конвертер&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Для работы мне необходимы контуры микроорганизмов, которые я использую для сравнения форм. Я получаю контуры из фотографий, очерчивая объект в векторном редакторе. Программа сохраняет результат в СВГ файле, а мне нужны живые координаты точек. Чтобы получить координаты, я анализирую СВГ файл.&lt;/p&gt;
&lt;p&gt;СВГ файл состоит из тегов. Я разберу только один — &lt;abbr&gt;path&lt;/abbr&gt;, — остальные мне не нужны. Он хранит описание криволинейных и замкнутых траекторий. Траектория — это и есть тот самый контур микроорганизма.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://antonlyakh.ru/blog/pictures/trajectory.jpg" width="600" height="505" alt="" /&gt;
&lt;/div&gt;
&lt;h2&gt;Учим синтаксис тега &lt;abbr&gt;path&lt;/abbr&gt;&lt;/h2&gt;
&lt;p&gt;Описание траектории вложено в тег &lt;abbr&gt;svg&lt;/abbr&gt;. Примерно так:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;Заголовок подтверждающий, что это СВГ
&amp;lt;svg&amp;gt;
  &amp;lt;path ... /&amp;gt;
&amp;lt;/svg&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Тег &lt;abbr&gt;path&lt;/abbr&gt; содержит один атрибут &lt;abbr&gt;d&lt;/abbr&gt; с алгоритмом рисования траектории.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;&amp;lt;path d=&amp;quot;алгоритм рисования&amp;quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Алгоритм состоит из &lt;i&gt;букв-команд&lt;/i&gt; и &lt;i&gt;цифр-координат,&lt;/i&gt; которые разделенны &lt;i&gt;пробелом, запятой&lt;/i&gt; или &lt;i&gt;знаком минус.&lt;/i&gt; Траекторию рисует перо, буквы-команды управляют, цифры-координаты показывают куда или как сместить перо, а разделители отделяют одно от другого.&lt;/p&gt;
&lt;h3&gt;Разделители разделяют&lt;/h3&gt;
&lt;p&gt;Пробел — это стандартный разделитель. Его можно ставить везде между командами и координатами. Спецификация СВГ разрешает не писать пробел, когда из контекста понятно, что здесь две разные сущности. Это сокращает объем СВГ-файла.&lt;/p&gt;
&lt;p&gt;Запомним:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;пробел не нужен между командой и числом;&lt;/li&gt;
&lt;li&gt;пробел не нужен после запятой;&lt;/li&gt;
&lt;li&gt;пробел не нужен перед знаком минус.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Запятая тоже разделяет два числа, но вместо нее можно написать пробел. Что именно написать — дело вкуса.&lt;/p&gt;
&lt;p&gt;Следующие три записи эквивалентны, но последняя компактнее.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;&amp;lt;path d=&amp;quot;M 15 17 L 18 -23 Z&amp;quot; /&amp;gt;
&amp;lt;path d=&amp;quot;M 15, 17 L 18, -23 Z&amp;quot; /&amp;gt;
&amp;lt;path d=&amp;quot;M15,17L18-23Z&amp;quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Теперь о цифрах.&lt;/p&gt;
&lt;h3&gt;Цифры хранят координаты&lt;/h3&gt;
&lt;p&gt;СВГ предназначен для хранения двумерной графики. Поэтому цифры задают координаты точек на плоскости. Для каждой точки используется два числа — первое для &lt;i&gt;x&lt;/i&gt;, второе для &lt;i&gt;y&lt;/i&gt;.&lt;/p&gt;
&lt;p&gt;Координаты бывают абсолютными и относительными. Абсолютные координаты отсчитываются от нуля, относительные — от конечной точки предыдущей команды. Относительные — это шаг, на который надо сдвинуть перо относительно последней позиции.&lt;/p&gt;
&lt;p&gt;Тип координат определяет регистр буквы-команды: большая буква обозначает абсолютную позицию, маленькая — относительную.&lt;/p&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Заглавная буква-команда — для абсолютных координат, прописная — для относительных&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;Точка является десятичным разделителем вещественных чисел. Запись &lt;abbr&gt;12,75&lt;/abbr&gt; — это два числа: 12 и 75, тогда как &lt;abbr&gt;12.75&lt;/abbr&gt; — это одно вещественное число.&lt;/p&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Точка — десятичный разделитель, запятая отделяет два числа.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;Цифры еще описывают дополнительные параметры команды &lt;b&gt;A&lt;/b&gt;, которая рисует дуги.&lt;/p&gt;
&lt;h3&gt;Буквы управляют пером&lt;/h3&gt;
&lt;p&gt;Буквы — это команды, которые управляют пером. За каждой командой следуют пары координат, которые показывают, куда сместить перо или как закруглить линию. Тег &lt;abbr&gt;path&lt;/abbr&gt; использует следующие команды:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;M&lt;/b&gt; — начать новую траекторию: переместить перо в заданную точку без рисования.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Z&lt;/b&gt; — замкнуть траекторию: переместить перо в начальную точку и нарисовать линию.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;С&lt;/b&gt;, &lt;b&gt;S&lt;/b&gt; — нарисовать кубическую кривую Безье.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Q&lt;/b&gt;, &lt;b&gt;T&lt;/b&gt; — нарисовать квадратную кривую Безье.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;L&lt;/b&gt;, &lt;b&gt;Н&lt;/b&gt; и &lt;b&gt;V&lt;/b&gt; — нарисовать произвольную, вертикальную или горизонтальную прямую.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;A&lt;/b&gt; — нарисовать дугу окружности или эллипса.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Команду &lt;b&gt;A&lt;/b&gt; я пропущу, но рассмотрю остальные.&lt;/p&gt;
&lt;h5&gt;&lt;i&gt;M x y&lt;/i&gt;&lt;/h5&gt;
&lt;p&gt;&lt;b&gt;М&lt;/b&gt; или &lt;b&gt;m&lt;/b&gt; начинает новую траекторию. Команда перемещает перо в заданную точку и ничего не рисует. Большое &lt;b&gt;М&lt;/b&gt; — первая обязательная команда тега &lt;abbr&gt;path&lt;/abbr&gt;.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;&amp;lt;path d=&amp;quot;M20 50...&amp;quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;&lt;i&gt;&lt;b&gt;Z&lt;/b&gt;&lt;/i&gt;&lt;/h5&gt;
&lt;p&gt;&lt;b&gt;Z&lt;/b&gt; или &lt;b&gt;z&lt;/b&gt; замыкает траекторию — проводит прямую линию от последней позиции пера к первой точке траектории. Регистр буквы не важен. Обычно это последняя команда тега. Если нет, тогда после &lt;b&gt;z&lt;/b&gt; опять стоит большое &lt;b&gt;M&lt;/b&gt;, которое начинает новую траекторию. Если &lt;b&gt;z&lt;/b&gt; не написано, траектория незамкнута.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;&amp;lt;path d=&amp;quot;M20 50 L 10 10, 140 10, 110 50z&amp;quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="e2-text-picture"&gt;
&lt;img src="https://antonlyakh.ru/blog/pictures/open-path.gif" width="384" height="132" alt="" /&gt;
&lt;/div&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;&amp;lt;path d=&amp;quot;M20 50 L 10 10, 140 10, 110 50z&amp;quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="e2-text-picture"&gt;
&lt;img src="https://antonlyakh.ru/blog/pictures/closed-path.gif" width="374" height="132" alt="" /&gt;
&lt;/div&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;&amp;lt;path d=&amp;quot;M20 50 L 10 10, 140 10z M110 50, 80 60, 95 90z&amp;quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="e2-text-picture"&gt;
&lt;img src="https://antonlyakh.ru/blog/pictures/two-closed-path.gif" width="374" height="234" alt="" /&gt;
&lt;/div&gt;
&lt;h5&gt;&lt;i&gt;&lt;b&gt;L&lt;/b&gt; x y&lt;/i&gt;&lt;/h5&gt;
&lt;p&gt;&lt;b&gt;L&lt;/b&gt; или &lt;b&gt;l&lt;/b&gt; рисует прямую линию от последней позиции пера до заданной точки. Несколько следующих друг за другом команд описывают ломаную. В такой записи, для краткости, все &lt;b&gt;L&lt;/b&gt;, кроме первой, не пишут. Если &lt;b&gt;L&lt;/b&gt; стоит после &lt;b&gt;M&lt;/b&gt;, ее тоже не пишут. Вот так:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;&amp;lt;path d=&amp;quot;M 70,12 L 24,30 L 60,70 L 120,50&amp;quot; /&amp;gt;
&amp;lt;path d=&amp;quot;M 70,12 L 24,30 60,70 120,50&amp;quot; /&amp;gt;
&amp;lt;path d=&amp;quot;M 70,12 24,30 60,70 120,50&amp;quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="e2-text-picture"&gt;
&lt;img src="https://antonlyakh.ru/blog/pictures/polyline.gif" width="266" height="172" alt="" /&gt;
&lt;/div&gt;
&lt;h5&gt;&lt;i&gt;&lt;b&gt;H&lt;/b&gt; x&lt;/i&gt;&lt;/h5&gt;
&lt;p&gt;&lt;b&gt;H&lt;/b&gt; или &lt;b&gt;h&lt;/b&gt; рисует горизонтальную линию от последней позиции пера до указанной точки. Координата &lt;i&gt;у&lt;/i&gt; не меняется.&lt;/p&gt;
&lt;h5&gt;&lt;i&gt;&lt;b&gt;V&lt;/b&gt; x&lt;/i&gt;&lt;/h5&gt;
&lt;p&gt;&lt;b&gt;V&lt;/b&gt; или &lt;b&gt;v&lt;/b&gt; рисует вертикальную линию от последней позиции пера до указанной точки. Координата &lt;i&gt;x&lt;/i&gt; не меняется.&lt;/p&gt;
&lt;h5&gt;&lt;i&gt;&lt;b&gt;C&lt;/b&gt; cx&lt;sub&gt;1&lt;/sub&gt; cy&lt;sub&gt;1&lt;/sub&gt; cx&lt;sub&gt;2&lt;/sub&gt; cy&lt;sub&gt;2&lt;/sub&gt; x&lt;sub&gt;2&lt;/sub&gt; y&lt;sub&gt;2&lt;/sub&gt;&lt;/i&gt;&lt;/h5&gt;
&lt;p&gt;&lt;b&gt;С&lt;/b&gt; или &lt;b&gt;с&lt;/b&gt; строит кубическую кривую Безье. Она состоит из четырех точек: первая и последняя задают начало и конец кривой, две промежуточные управляют формой. Так как первая точка совпадает с текущим положением пера, то после команды &lt;b&gt;C&lt;/b&gt; указывают только три точки: две управляющие вершины (&lt;i&gt;cx&lt;sub&gt;1&lt;/sub&gt; cy&lt;sub&gt;1&lt;/sub&gt;&lt;/i&gt;), (&lt;i&gt;cx&lt;sub&gt;2&lt;/sub&gt; cy&lt;sub&gt;2&lt;/sub&gt;&lt;/i&gt;) и последнюю точку кривой (&lt;i&gt;x&lt;sub&gt;2&lt;/sub&gt; y&lt;sub&gt;2&lt;/sub&gt;&lt;/i&gt;).&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;&amp;lt;path d=&amp;quot;M40 50 C 60 10, 90 90, 110 50&amp;quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="e2-text-picture"&gt;
&lt;img src="https://antonlyakh.ru/blog/pictures/bezier.gif" width="314" height="118" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;&lt;i&gt;Поликривая Безье&lt;/i&gt; — это несколько соприкасающихся кривых Безье, где конец одной кривой становится началом следующей. Последовательные команды &lt;b&gt;C&lt;/b&gt; описывают поликривую. Для экономии байтов все команды, кроме первой &lt;b&gt;C&lt;/b&gt;, не пишут.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;&amp;lt;path d=&amp;quot;M30 100C50 50,70 20,100 100,110 130,45 150,65 100&amp;quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="e2-text-picture"&gt;
&lt;img src="https://antonlyakh.ru/blog/pictures/bezier-polyline.gif" width="270" height="302" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;Обычно в месте соприкосновения кривых Безье гладкость поликривой нарушается. Чтобы поликривая оставалась гладкой, используют команду &lt;b&gt;S&lt;/b&gt;.&lt;/p&gt;
&lt;h5&gt;&lt;i&gt;&lt;b&gt;S&lt;/b&gt; cx&lt;sub&gt;2&lt;/sub&gt; cy&lt;sub&gt;2&lt;/sub&gt; x&lt;sub&gt;2&lt;/sub&gt; y&lt;sub&gt;2&lt;/sub&gt;&lt;/i&gt;&lt;/h5&gt;
&lt;p&gt;&lt;b&gt;S&lt;/b&gt; или &lt;b&gt;s&lt;/b&gt; строит гладкую кубическую кривую Безье так, что ее первая управляющая вершина является зеркальным отражением второй управляющей вершины предыдущей кривой Безье. Отражение относительно начальной точки данной кривой.&lt;/p&gt;
&lt;h5&gt;&lt;i&gt;&lt;b&gt;Q&lt;/b&gt; cx&lt;sub&gt;1&lt;/sub&gt; cy&lt;sub&gt;1&lt;/sub&gt; x&lt;sub&gt;2&lt;/sub&gt; y&lt;sub&gt;2&lt;/sub&gt;&lt;/i&gt;&lt;/h5&gt;
&lt;p&gt;&lt;b&gt;Q&lt;/b&gt; или &lt;b&gt;q&lt;/b&gt; строит квадратную кривую Безье. Синтаксис команды аналогичен &lt;b&gt;C&lt;/b&gt;, только здесь используется одна управляющая вершина.&lt;/p&gt;
&lt;p&gt;Несколько команд &lt;b&gt;Q&lt;/b&gt; описывают квадратную поликривую Безье. Для краткости все команды, кроме первой &lt;b&gt;Q&lt;/b&gt;, не пишут. Чтобы квадратная поликривая получилась гладкой, используют команду &lt;b&gt;T&lt;/b&gt;.&lt;/p&gt;
&lt;h5&gt;&lt;i&gt;&lt;b&gt;T&lt;/b&gt; x&lt;sub&gt;2&lt;/sub&gt; y&lt;sub&gt;2&lt;/sub&gt;&lt;/i&gt;&lt;/h5&gt;
&lt;p&gt;&lt;b&gt;T&lt;/b&gt; или &lt;b&gt;t&lt;/b&gt; строит гладкую квадратную кривую Безье так, что ее единственная управляющая вершина является зеркальным отражением управляющей вершины предыдущей кривой Безье.&lt;/p&gt;
&lt;p&gt;Со структурой файла разобрались.&lt;/p&gt;
&lt;p&gt;&lt;a name="svg-converter"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Пишем конвертер СВГ в текст&lt;/h3&gt;
&lt;p&gt;Конвертер готов. Это экзешник. Инсталировать не нужно. Сохраните на диск и запустите.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://antonlyakh.ru/blog/pictures/svg-converter.gif" width="645" height="446" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;&lt;span style="display: inline-block; font-weight: bold; padding: 6px; background-color: #7fC"&gt;&lt;a href="http://antonlyakh.ru/download/?svg2txt"&gt;Скачать конвертер&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Для описания любого контура достаточно трех команд: &lt;abbr&gt;M&lt;/abbr&gt;, &lt;abbr&gt;C&lt;/abbr&gt; и &lt;abbr&gt;Z&lt;/abbr&gt;. Конвертер понимает еще и четвертую: &lt;abbr&gt;L&lt;/abbr&gt;, остальные — нет.&lt;/p&gt;
&lt;p&gt;Конвертер выбирает из СВГ-файла теги &lt;abbr&gt;path&lt;/abbr&gt;, вычисляет координаты точек траектории и сохраняет их в текстовом файле.&lt;/p&gt;
&lt;p&gt;Конвертер не проверяет валидность СВГ-файла. Ему все равно, какая информация находится вокруг тега &lt;abbr&gt;path&lt;/abbr&gt;. На вход конвертера можно подать любой мусор с корректными данными только в теге &lt;abbr&gt;path&lt;/abbr&gt;.&lt;/p&gt;
&lt;p&gt;Конвертер поддерживает пропорциональное и произвольное масштабирование координат: перед сохранением он спрашивает размер области, в которую надо вписать траекторию.&lt;/p&gt;
&lt;p&gt;Конвертер позволяет задать позицию начала координат: в одном из углов, на середине стороны или в центре прямоугольника, ограничивающего контур.&lt;/p&gt;
&lt;p&gt;Конвертер сохраняет в текстовом файле пары чисел — двумерные координаты точек контура. Десятичный разделитель — точка. Числа разделены пробелом.&lt;/p&gt;
&lt;p&gt;&lt;abbr&gt;12.3 15.6 78.4 24.1 15 123&lt;/abbr&gt;&lt;/p&gt;
&lt;h3&gt;Детали&lt;/h3&gt;
&lt;p&gt;Конвертер написан на Дельфи 7.&lt;/p&gt;
&lt;p&gt;Пары чисел вырезаю из СВГ-файла с помощью регулярного выражения. Понимает числа разделенные пробелом, запятой или минусом.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;/(-?\d+(\.\d+)?)[,|\s]?(-?\d+(\.\d+)?)\s?/&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Для построения кубической кривой Безье применяю &lt;a href="https://rsdn.ru/article/multimedia/Bezier.xml"&gt;адаптивное разбиение&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Полученные данные анализирую методами геометрической морфометрии. Но это уже другая история.&lt;/p&gt;
&lt;p&gt;С Новым годом!&lt;/p&gt;
</description>
</item>


</channel>
</rss>