Визуализация графов с помощью библиотеки arbor.js

Некое время назад, мне потребовалось визуализировать графы и хотелось найти уже готовое решение что бы не изобретать очередной велосипед. Мне в руки попалась библиотека arbor, которая используя jQuery предоставлет возможность отрисовывать вполне приемлемые графы в браузере.



Саму библиотеку, а так же документацию и примеры (всё на английском) можно скачать на официальном сайте
И так по порядку.
В качестве холста используется html5 canvas, а это значит что в браузерах его не поддерживающих ничего работать не будет.
Создадим простую страницу которая подгружает JSON данные о вершинах и рёбрах с помощью AJAX. За основу возьмём идущий в комплекте пример и модернизируем его (загрузка данных, отображение).

Создадим файл с данными, где перечислим все вершины и рёбра
data.json:

{
"nodes": [
{"name": "node_1"},
{"name": "node_2"},
{"name": "node_3"},
{"name": "node_4"},
{"name": "node_5"},
{"name": "node_6"},
{"name": "node_7"},
{"name": "node_8"},
{"name": "node_9"},
{"name": "node_10"}
],
"edges": [
{"src": "node_3", "dest": "node_2"},
{"src": "node_5", "dest": "node_3"},
{"src": "node_8", "dest": "node_7"},
{"src": "node_1", "dest": "node_4"},
{"src": "node_7", "dest": "node_5"},
{"src": "node_3", "dest": "node_9"},
{"src": "node_2", "dest": "node_4"},
{"src": "node_6", "dest": "node_5"},
{"src": "node_9", "dest": "node_1"},
{"src": "node_10", "dest": "node_2"},
{"src": "node_1", "dest": "node_10"}
]
}


В основном файле подключим необходимые библиотеки, а так же вынесем во внешний файл все js скрипты
<script src="js/jquery.min.js"></script>
<script src="js/arbor.js"></script>  
<script src="main.js"></script>


В теле документа создаём canvas в котором будем рисовать
<canvas id="viewport" width="800" height="600"></canvas>


Теперь по порядку как это всё будет работать.
При полной загрузке создаётся объект с настройками для arbor, который так же содержит функции которые будут выполнятся при наступлении различных событий, функции отрисовки каждой вершины и грани (этим мне и понравилась библиотека, что позволяет настроить практически всё).
Далее этот объект передаём в конструктор arbor, который и запустит всю эту карусель.
Загрузка данных также осуществляется сразу после того как страница будет полностью загружена.

Вообще кода здесь больше чем слов, поэтому я считаю будет яснее и понятнее перейти сразу к нему с подробными комментариями.
main.js:
(function($){
 var Renderer = function(canvas)
 {
  var canvas = $(canvas).get(0);
  var ctx = canvas.getContext("2d");
  var particleSystem;

  var that = {
   init:function(system){
    //начальная инициализация
    particleSystem = system;
    particleSystem.screenSize(canvas.width, canvas.height); 
    particleSystem.screenPadding(80);
    that.initMouseHandling();
   },
      
   redraw:function(){
    //действия при перересовке
    ctx.fillStyle = "white"; //белым цветом
    ctx.fillRect(0,0, canvas.width, canvas.height); //закрашиваем всю область
   
    particleSystem.eachEdge( //отрисуем каждую грань
     function(edge, pt1, pt2){ //будем работать с гранями и точками её начала и конца
      ctx.strokeStyle = "rgba(0,0,0, .333)"; //грани будут чёрным цветом с некой прозрачностью
      ctx.lineWidth = 1; //толщиной в один пиксель
      ctx.beginPath();  //начинаем рисовать
      ctx.moveTo(pt1.x, pt1.y); //от точки один
      ctx.lineTo(pt2.x, pt2.y); //до точки два
      ctx.stroke();
    });
 
    particleSystem.eachNode( //теперь каждую вершину
     function(node, pt){  //получаем вершину и точку где она
      var w = 10;   //ширина квадрата
      ctx.fillStyle = "orange"; //с его цветом понятно
      ctx.fillRect(pt.x-w/2, pt.y-w/2, w,w); //рисуем
      ctx.fillStyle = "black"; //цвет для шрифта
      ctx.font = 'italic 13px sans-serif'; //шрифт
      ctx.fillText (node.name, pt.x+8, pt.y+8); //пишем имя у каждой точки
    });       
   },
  
   initMouseHandling:function(){ //события с мышью
    var dragged = null;   //вершина которую перемещают
    var handler = {
     clicked:function(e){ //нажали
      var pos = $(canvas).offset(); //получаем позицию canvas
      _mouseP = arbor.Point(e.pageX-pos.left, e.pageY-pos.top); //и позицию нажатия кнопки относительно canvas
      dragged = particleSystem.nearest(_mouseP); //определяем ближайшую вершину к нажатию
      if (dragged && dragged.node !== null){
       dragged.node.fixed = true; //фиксируем её
      }
      $(canvas).bind('mousemove', handler.dragged); //слушаем события перемещения мыши
      $(window).bind('mouseup', handler.dropped);  //и отпускания кнопки
      return false;
     },
     dragged:function(e){ //перетаскиваем вершину
      var pos = $(canvas).offset();
      var s = arbor.Point(e.pageX-pos.left, e.pageY-pos.top);
 
      if (dragged && dragged.node !== null){
       var p = particleSystem.fromScreen(s);
       dragged.node.p = p; //тянем вершину за нажатой мышью
      }
 
      return false;
     },
     dropped:function(e){ //отпустили
      if (dragged===null || dragged.node===undefined) return; //если не перемещали, то уходим
      if (dragged.node !== null) dragged.node.fixed = false; //если перемещали - отпускаем
      dragged = null; //очищаем
      $(canvas).unbind('mousemove', handler.dragged); //перестаём слушать события
      $(window).unbind('mouseup', handler.dropped);
      _mouseP = null;
      return false;
     }
    }
    // слушаем события нажатия мыши
    $(canvas).mousedown(handler.clicked);
   },
      
  }
  return that;
 }    

 $(document).ready(function(){
  sys = arbor.ParticleSystem(1000); // создаём систему
  sys.parameters({gravity:true}); // гравитация вкл
  sys.renderer = Renderer("#viewport") //начинаем рисовать в выбраной области

  $.getJSON("data.json", //получаем с сервера файл с данными
   function(data){
    $.each(data.nodes, function(i,node){
     sys.addNode(node.name); //добавляем вершину
    });
    
    $.each(data.edges, function(i,edge){
     sys.addEdge(sys.getNode(edge.src),sys.getNode(edge.dest)); //добавляем грань
    });
  });
    
 })

})(this.jQuery)




Сама библиотека мне понравилась тем что позволяет самому отрисовывать как будут выглядеть вершины и грани, а так же настроить действия на любые события.
Исходник получившегося можно скачать тут

Похожие публикации

AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 15

    0
    Простота использования манит использовать этот метод. Я в своё время сталкивался с подобной задачей, но мне нужно было работать с сетями Петри (Для этих целей использовал Raphaël). Если нужно рисовать только графы — хорошо, но если понадобится внести в эти графы что-то большее, мне кажется появятся трудности.
      0
      Я думаю их вполне можно связать. Arbor именно отрисовку примитивов возлагает на пользователя, а этим может занятся Raphaël.
        0
        Rephael работает вроде бы с SVG, а тут все на Canvas. Получится скрещение ужа и ежа.

        Меня во всех этих примерах поражает укладка графа. Обвес и отрисовку сделать не сложно, а вот уложить граф — это искусство. Вы не разбирались как устроен алгоритм укладки, скажем тут: HalfViz?
          +5
          Я бы не сказал, что этот алгоритм очень хорош — он просто отталкивает точки друг от дружки и они, словно мусор в мешке «утрясываются» на странице. В итоге — куча ненужных пересечений, которые осложняют чтение графа:


          Вручную его можно отсортировать намного изящнее и сразу добавляется читабельность:


          Это я к тому, что алгоритм по ссылке — не идеален и, кстати, довольно лёгок по своей идее и реализации.
            0
            Нет. Но если мне это потребуется, то это первая библиотека которую я разберу за этим. Кстати в arborjs.org/reference расписана arbor.ParticleSystem со всеми параметрами для укладки
              0
              Я вот подвигал мышкой и представляю себе уложение графа приблизительно таким — идём от центра, расходимся в разные стороны, каждая нода имеет пространство вокруг себя, которое «закрепленно» за ней. От каждой дочерной ноды идём дальше и стараемся не попасть во власть другой ноды. Ставим точку на территорию другой ноды только в крайнем случае. Алгоритм откровенно не идеальный, но получше, чем «случайный разброс», как у HalfViz.
                0
                А пересечения? На указанном графе есть циклы. Не все так просто. Когда я делал — я так и строил елочку от корня (у меня был корень), рассчитывал пересечения на уровне и старался распологать элементы по уровням с миниумом пересечений, хоть это и было реализовано только на 2 уровня. В общем там все довольно нетривиально делается. Самая фигня в том что понятие «хорошо» для укладки графа субъективно. Каоночино — иметь наименьшее число пересечений. Но есть и другие параметры субъективного качества, например средняя длинна связи, площадь, занимаемая графом, узел с акцентом (начальный). Так что… В общем я бы полапал библиотеку именно для укладки. Отрисовка это клево, но не так сложно и самому сделать.
          +2
          Вот ещё примерчик графов (в основе обёрнутый Raphaël).
            +1
            Самое красивое и удобное на мой взгляд InfoVis Toolkit
              +3
              Есть еще d3 (например: Force-Directed Graph)
                0
                Есть мощный кросс-платформенный инструмент построения графов — GraphViz. Эмбеддить картинки в веб не составляет труда.
                  0
                  Большое спасибо за пример. Как раз сегодня озадачился построением такой структуры, а тут добрый волшебник подкинул готовое ).
                    0
                    Чем в подобных задачах подход на Canvas перспективнее подхода на SVG?
                      0
                      Здравствуйте.
                      Я в поиске библиотеки для визуализации графов(интерактивной).
                      В принципе, нашел оч много всего, но у меня еще и критерий — нужно, чтоб работало в IE 7,8,9
                      Кто знает, сталкавался? подскажите!

                      Вот списочек, может кому пригодится:

                      arborjs.org/
                      flare.prefuse.org/ //flex-flash
                      www.graphviz.org/Gallery.php
                      thejit.org/demos/
                      js-graph-it.sourceforge.net/index.html
                      jsplumb.org/jquery/demo.html
                      mbostock.github.com/protovis/ex/
                      raphaeljs.com/
                      www.graphdracula.net/de/showcase/
                      github.com/mbostock/d3/wiki/Gallery
                      cytoscapeweb.cytoscape.org/demos
                      sigmajs.org/examples.html
                      javascript.open-libraries.com/utilities/drawing/canviz-javascript-library/

                      Ну понятное дело что из этого списка мне ничего не подходит, потому что нужно обязательно IE!!! :(
                      Из всего перечисленного на IE8 у меня запустилось только вот это, и то со глюками.
                      thejit.org/demos/

                      Оч нравится эта библиотека. о которой пример. но увы, головная боль интернета (IE) всю малину портит… :(

                      Спасибо за помощь!
                        0
                        Возможность добавить вес дуге и отрисовывать его имеется?

                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                        Самое читаемое