Как я «напрограммировал» себе скилл рисования диаграмм в скетч-стиле
По работе мне часто приходится рисовать разные схемы, диаграммы процессов и графики, в том числе и те, которые потом используются в качестве иллюстраций для сайта, статей и презентаций. Диаграммы я привык рисовать в app.diagrams.net (бывший draw.io), а графики — в datawrapper.de. Всё бы ничего, но есть у диаграмм, сделанных в draw.io, lucidcharts или visio и графиков, построенных в аналитических сервисах одна беда — они выглядят как-то слишком уныло и «олдскульно», в духе »90-х».
Экспортировано из draw.io как SVG
Всю эту визуализацию и инфографику хотелось бы сделать более заметной, привлекательной и душевной (и, желательно, без привлечения дизайнера). Что-то вроде этого:
Обработано в INSTAD.IO
Сначала я пробовал перерисовывать диаграммы руками на листе бумаги и сканировать, потом пытался отрисовывать их в векторном редакторе, используя разные кисти. Наконец, экспериментировал со стилусом на планшете — во всех случаях получалась какая-то непотребщина, на которую ещё и уходило много времени. Благо, программирую я чуть лучше, чем рисую — то даёт шанс компенсировать недостаток одного навыка другим. Так у меня возникла идея создания инструмента для отрисовки диаграмм и графиков в стиле «нарисовано от руки».
Построено в GoogleSheets, обработано в INSTAD.IOПостроено в GoogleSheets, обработано в INSTAD.IO
Поскольку в стеке технологий у меня в основном javascript и php, я начал смотреть, какие есть библиотеки для рендеринга скетчей. Вариантов оказалось немного — выбор пал на крутую библиотеку roughjs, которая как раз умела генерировать графические примитивы (от прямоугольников до кривых) в нужном «рисованном» стиле и была написана на JS. Если в ней хорошенько покрутить параметры, можно было получить достойный результат. Оставалось только придумать, где брать исходный материал для отрисовки, потому что каждый график или UML-диаграмму рисовать на JS непрактично. Нужен был какой-то векторный редактор для генерирования диаграм. Изобретать diagrams.net и datawrapper.de не хотелось, поэтому решил стилизовать готовое SVG-изображение, сгенерированное в любом из этих сервисов. То есть нужно было парсить SVG с графиком или диаграммой и конвертировать каждый примитив (
В итоге придумалась следующая схема:
Загружаем готовый векторный SVG-рисунок со схемой, графиком или диаграммой. Его можно нарисовать в draw.io или даже в GoogleSheets.
Заменяем в исходном SVG все примитивы типа
, , и пр. на , который генерирует roughjs. Примеряем к надписям рисованные шрифты (мне понравились бесплатные гарнитуры XKCD, Indie, Caveat и Pangolin), после чего подгоняем размеры надписей, чтобы высота и ширина не отличалась от исходных, и при этом надписи читались.
Меняем цвета на приятные, переносим часть стилей и фильтров из оригинального SVG, чтобы диаграмма максимально сохраняла пропорции, позиции элементов и свойства фигур.
Экспортируем в PNG в высоком разрешении или сохраняем обратно в SVG.
???
Профит.
Вернее, профита с ней не получилось, так как для сервиса найти product-market fit пока не удалось. Как говорится, «все пробуют, хвалят, но замуж не берут».
Но вернёмся к технической стороне вопроса. Несмотря на очевидность и простоту подхода, пришлось решить достаточно много технических нюансов. Например, учесть то, что SVG — это достаточно вольный формат, который позволяет один и тот же прямоугольник нарисовать десятком разных способов. Иногда попадались SVG-изображения, где прямоугольник — это классический
Пришлось повозиться и с рендерингом текста. Так, например, файл, экспортированный из draw.io, сохраняет большую часть данных не в каноническом SVG, а как контейнеры Ещё оказалось непросто поддерживать правильные цвета контура и заливки для фигур, поскольку они не всегда заданы SVG-атрибутами fill / stroke внутри примитива. Иногда цвета наследуются от родительского элемента, иногда объявлены в локальном стиле через style, а иногда — в глобальном стиле или даже в фильтре. Одним словом — бардак). Отдельная беда была с производительностью и потреблением памяти. Когда конвертируется график с 30 примитивами — всё работает шустро и незаметно. А вот векторизованное растровое изображение с 10K+ объектами или дашборд из Tableau, сохранённый в PDF, страшно тормозил. Но тут, конечно, всё зависело от производительности компьютера, потому что рендерится оно в браузере на клиенте (привет пользователям новых маков на M1 Max, у которых всё быстро). Первую версию SVG-конвертера я сделал на jQuery, но потом переписал на обычном js и оно стало работать в 2.5 раз быстрее и потреблять процентов на 70 меньше памяти. Такая вот плата за удобство. После некоторого периода тестирования стало понятно, что не во всех редакторах диаграмм и генерилках графиков есть экспорт в SVG. Или есть, но с побочными эффектами. Например, при сохранении графиков из GoogleSheets в SVG текст переводится в кривые. В этом случае надпись становится картинкой и её уже не поменять. Но том же GoogleSheets можно сохранить график в PDF, где надписи остаются без изменений. Поэтому пришлось прикрутить загрузку других форматов (PDF, EPS) — cпасибо консольному inkscape за возможность конвертации векторных форматов. Постепенно сервис обрастал всякими фичами, наподобие работы с буфером обмена: можно скопировать в буфер код SVG и вставить его в сервис для рестайлинга (это особенно удобно, если копируете непосредственно со страницы с SVG-графиком или диаграммой). А результат можно скопировать в буфер и затем вставить в документ. Кроме векторных графиков и диаграмм неплохо получаются рисованные карты и схемы: В одной из последних версий я даже решил добавить цветной трейсер растровых изображений, чтобы любой JPEG или PNG можно было сконвертировать в векторный SVG и затем применить к нему «скетч-эффект». Готового векторизатора не было (potrace не в счёт), поэтому пришлось написать свой. Но как оказалось, для качественной векторизации нужно трейсить изображение в высоком разрешении, примерно 12000×12000 пикселей (предварительно изображение апскейлится с помощью нейронки). То есть нужны мощные сервера с GPU — на стандартном 8-ядерном железе даже небольшая растровая картинка 2000×2000 векторизуется минуты две (а пользователи не любят ждать). Поэтому в некотором обрезанном виде я оставил векторизатор в сервисе, но никому про него не рассказываю, чтобы не позориться. Большая часть сервиса сделана на самом обыкновенном Vanilla JS, UI — на Bootstrap + jQuery. Небольшая серверная часть — на PHP. Всё крутится в докере. Для авторизации без регистраций прикрутил Google и Facebook. Напоследок проиллюстрирую работу сервиса на Хабра-схеме модерации постов из справочного раздела про «Песочницу»: Сервис бесплатный, потестировать его можно здесь. Будет здорово, если вы тоже найдёте ему применение при решении своих повседневных графических задач. За информацию о багах также буду премного благодарен!