При описании алгоритмов на деревьях у меня возникла небольшая проблема визуализации деревьев, записанных в Newick-формате. Для рисования деревьев можно использовать различные десктопные инструменты (например, в LaTeX для этой цели подходит замечательный пакет TikZ). Возникает вопрос, можно ли для рисования использовать веб-технологии; результатом моих недельных усилий в этом направлении стала небольшая JavaScript-библиотека SVGTree.
Как видно из названия, для рисования в SVGTree используется один из аспектов HTML5 — масштабируемая векторная графика (scalable vector graphics, SVG). SVG — язык разметки на основе XML, предназначенный для отображения векторной графики; SVG-изображения можно встраивать в обычные HTML-страницы и динамически изменять при помощи JavaScript. На нынешнее время SVG поддерживается в достаточной степени всеми основными веб-браузерами: Firefox, Chrome и даже Internet Explorer 11+. По сравнению с альтернативными методами, которые можно применить для отображения деревьев, у SVG есть несколько преимуществ:
- Поскольку SVG — стандарт векторной графики, изображения легко масштабируются; SVG, в отличие от скалярной графики, отображаемой элементом HTML5 <canvas>, походит для огромных рисунков (порядка 10000×10000).
- Внешний вид SVG можно менять при помощи таблиц стилей CSS, совершенно аналогичных таблицам для обычных HTML-страниц. Это позволяет разделить логику создания базовых элементов рисунка и их отображения.
- К элементам SVG можно обратиться при помощи JavaScript DOM API почти так же, как к элементам HTML-страницы. При этом элементы SVG поддерживают базовые события (например, click), что упрощает реализацию интерактивности.
Библиотека доступна в виде Github-репозитория.
Несколько примеров использования библиотеки есть на этой странице, еще несколько приведено под катом.
С использованием SVGTree, приведенный в описании алгоритмов на деревьях пример выглядит так:
((,A,)B,((C,D),E),(F,G))H;Дерево сгенерировано при помощи такого кода:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css"
href="http://softandware.org.ua/svgtree/svgtree-0.1.min.css" />
<script src="http://softandware.org.ua/svgtree/svgtree-0.1.min.js">
</script>
</head>
<body>
<div id="tree-1"></div>
</body>
</html>
// Контейнер, в котором размещается SVG-графика
// обычно для этой цели можно использовать пустой DIV-элемент
var elem = document.querySelector('#tree-1');
// Newick-запись дерева
var notation = '((,A,)B,((C,D),E),(F,G))H;';
new SVGTree(notation, elem, {
'size': 'fit' // подобрать размер графики автоматически
});
Библиотека позволяет изменять настройки отображения. Например, можно сделать так, чтобы дерево рисовалось горизонтально (потомки справа от родительских вершин, а не снизу), а вершины обозначались квадратиками:
var elem = document.querySelector('#tree-2');
var notation = '((,A,)B,((C,D),E),(F,G))H;';
new SVGTree(notation, elem, {
'size': 'fit',
'nodes': 'square',
'orientation': 'h'
});
Созданные деревья поддерживают интерактивность: есть возможность настроить дерево так, чтобы можно было, например, добавлять и удалять вершины (при помощи клавиш Ins и Del, соответственно) и переименовывать их.
var elem = document.querySelector('#tree-3');
var notation = '((,A,)B,((C,D),E),(F,G))H;';
new SVGTree(notation, elem, {
// фиксированный размер изображения, чтобы верстка страницы
// не портилась при добавлении новых вершин
'size': [600, 360],
// позволить добавлять, удалять и редактировать вершины
'interaction': ['add', 'remove', 'edit']
});
Пожалуй, наиболее эффектная возможность SVGTree — перетаскивание вершин. Если среди допустимых действий с деревом указать 'drag', любой узел дерева можно будет перетащить на новое место рядом с другим узлом. Если во время перетаскивания держать нажатой клавишу Ctrl, перетаскиваемое поддерево будет скопировано, а не перемещено.
var elem = document.querySelector('#tree-4');
var notation = '((,A,)B,((C,D),E),(F,G))H;';
new SVGTree(notation, elem, {
'size': [600, 360],
// позволить удалять, редактировать и перетаскивать вершины
// (добавление вершин — частный случай перетаскивания)
'interaction': ['remove', 'edit', 'drag']
});
При этом можно перетаскивать вершины между разными деревьями, а если включить опцию dragAsText, то и между деревом и другими источниками!
Узлы дерева можно перетащить сюда: или отсюда: (A,B)C;.
var elem = document.querySelector('#tree-5');
var notation = '((,A,)B,((C,D),E),(F,G))H;';
new SVGTree(notation, elem, {
'size': [600, 360],
'interaction': ['remove', 'edit', 'drag'],
'dragAsText': true
});