WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit 2c6ca98

Browse files
committed
feat: add graphviz layout option to graph_viewer
1 parent 930ab66 commit 2c6ca98

File tree

6 files changed

+113
-2
lines changed

6 files changed

+113
-2
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
**/cmake-build-release/*
44
**/.idea/*
55
.cache/
6-
bench/
6+
bench/
7+
experiment/

dependencies.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ sudo apt install libqglviewer-dev-qt6
1212
sudo apt install libeigen3-dev
1313
sudo apt install python3-dev python3-pybind11
1414
sudo apt install cmake gcc-13 g++-13
15+
sudo apt install libgraphviz-dev
1516

1617
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 1
1718
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 1

gui/CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,16 @@ find_package(Eigen3 3.3 REQUIRED NO_MODULE)
3838
find_package(OpenSceneGraph REQUIRED COMPONENTS osg osgDB osgGA)
3939
find_package(cppitertools)
4040

41+
find_package(PkgConfig)
42+
pkg_check_modules(GRAPHVIZ libgvc libcgraph)
43+
if(GRAPHVIZ_FOUND)
44+
message(STATUS "Found Graphviz via pkg-config")
45+
#message(STATUS " Include dirs: ${GRAPHVIZ_INCLUDE_DIRS}")
46+
#message(STATUS " Libraries: ${GRAPHVIZ_LIBRARIES}")
47+
#message(STATUS " Library dirs: ${GRAPHVIZ_LIBRARY_DIRS}")
48+
endif()
49+
50+
4151
SET(CMAKE_AUTOMOC ON)
4252
SET(CMAKE_AUTOUIC ON)
4353

@@ -88,6 +98,7 @@ target_link_libraries(dsr_gui
8898
Qt6::OpenGL
8999
fastdds
90100
${qt3d_libs}
101+
${GRAPHVIZ_LIBRARIES}
91102
)
92103

93104
target_include_directories(dsr_gui
@@ -99,6 +110,7 @@ target_include_directories(dsr_gui
99110
PUBLIC
100111
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/>
101112
$<INSTALL_INTERFACE:>
113+
${GRAPHVIZ_INCLUDE_DIRS}
102114
)
103115

104116
set_target_properties(dsr_gui

gui/dsr_gui.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,15 @@
2121
#include <QScreen>
2222
#include <QStringList>
2323
#include <QStatusBar>
24+
#include <qsharedpointer.h>
2425
#include <utility>
2526
#include <dsr/gui/viewers/graph_viewer/graph_node.h>
2627
#include <dsr/gui/viewers/graph_viewer/graph_edge.h>
2728
#include <unistd.h>
2829
#include <cstdlib>
2930
#include <cstdio>
3031
#include <cstring>
32+
#include "dsr/gui/viewers/graph_viewer/graph_viewer.h"
3133
#include "sys/times.h"
3234
#include <fcntl.h>
3335

@@ -313,6 +315,19 @@ void DSRViewer::initialize_views(int options, view central)
313315
{
314316
qobject_cast<GraphViewer *>(widgets_by_type[view::graph]->widget)->toggle_animation(state);
315317
});
318+
319+
auto layout_menu = window->menuBar()->addMenu(window->tr("&Graph Layout"));
320+
321+
for (auto str : {"dot", "neato", "fdp", "sfdp", "circo", "twopi"}) {
322+
auto action = std::string("Graphviz ") + str;
323+
QAction *action_graphviz = new QAction(action.data(), this);
324+
action_graphviz->setStatusTip(tr(action.data()));
325+
layout_menu->addAction(action_graphviz);
326+
connect(action_graphviz, &QAction::triggered, this, [this, str](bool state)
327+
{
328+
qobject_cast<GraphViewer *>(widgets_by_type[view::graph]->widget)->compute_layout(str);
329+
});
330+
}
316331
}
317332

318333
// Tabification of current docks

gui/include/dsr/gui/viewers/graph_viewer/graph_viewer.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
#include <QResizeEvent>
2929
#include <QMenu>
3030

31+
#include <graphviz/gvc.h>
32+
#include <graphviz/cgraph.h>
3133

3234
class GraphNode;
3335
class GraphEdge;
@@ -54,6 +56,7 @@ namespace DSR
5456
void hide_show_node_SLOT(uint64_t id, bool visible);
5557
// Others
5658
void toggle_animation(bool state);
59+
void compute_layout(const char * alg = "dot");
5760
void reload(QWidget * widget);
5861
void remove_node_SLOT(uint64_t id); // remove node from DSR
5962

@@ -72,9 +75,13 @@ namespace DSR
7275
std::map<std::string,std::set<std::uint64_t>> type_id_map;
7376
int timerId = 0;
7477
void showContextMenu(QMouseEvent *event);
75-
78+
79+
// Graphviz layout
80+
GVC_t* graphviz_context;
81+
Agraph_t* graphviz_graph;
7682

7783
protected:
84+
7885
void createGraph();
7986
virtual void timerEvent(QTimerEvent *event);
8087
virtual void mousePressEvent(QMouseEvent *event);

gui/viewers/graph_viewer/graph_viewer.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55
#include <dsr/gui/viewers/graph_viewer/graph_edge.h>
66
#include <dsr/gui/viewers/graph_viewer/graph_viewer.h>
77
#include <QMessageBox>
8+
#include <graphviz/cgraph.h>
9+
#include <graphviz/gvc.h>
10+
#include <graphviz/types.h>
11+
#include <qglobal.h>
12+
#include <string>
813

914
using namespace DSR ;
1015

@@ -18,6 +23,9 @@ GraphViewer::GraphViewer(std::shared_ptr<DSR::DSRGraph> G_, QWidget *parent) :
1823
G = std::move(G_);
1924
own = std::shared_ptr<GraphViewer>(this);
2025

26+
graphviz_context = gvContext();
27+
graphviz_graph = agopen((char*)"G", Agdirected, nullptr);
28+
2129
contextMenu = new QMenu(this);
2230
showMenu = contextMenu->addMenu(tr("&Show:"));
2331

@@ -55,6 +63,8 @@ GraphViewer::~GraphViewer()
5563

5664
}
5765
scene.clear();
66+
agclose(graphviz_graph);
67+
gvFreeContext(graphviz_context);
5868
}
5969

6070
void GraphViewer::createGraph()
@@ -157,6 +167,12 @@ void GraphViewer::add_or_assign_node_SLOT(uint64_t id, const std::string &type)
157167
color = G->get_attrib_by_name<color_att>(n.value()).value_or(color);
158168
gnode->set_color(color);
159169
gnode->setType(type);
170+
auto id_str = std::to_string(id);
171+
Agnode_t* gvnode = agnode(graphviz_graph, id_str.data(), 1);
172+
agset(gvnode, (char*)"width", (char*)"1.5");
173+
agset(gvnode, (char*)"height", (char*)"1.5");
174+
agset(gvnode, (char*)"shape", (char*)"box");
175+
agset(gvnode, (char*)"fixedsize", (char*)"true");
160176
}
161177
else
162178
{
@@ -186,6 +202,9 @@ void GraphViewer::add_or_assign_node_SLOT(uint64_t id, const std::string &type)
186202
add_or_assign_edge_SLOT(edges.from(), edges.to(), edges.type());
187203
}
188204
}
205+
206+
this->scene.setSceneRect(scene.itemsBoundingRect());
207+
this->fitInView(scene.itemsBoundingRect(), Qt::KeepAspectRatio );
189208
}
190209

191210
GraphNode* GraphViewer::new_visual_node(uint64_t id, const std::string &type, const std::string &name, bool debug)
@@ -215,6 +234,15 @@ void GraphViewer::add_or_assign_edge_SLOT(std::uint64_t from, std::uint64_t to,
215234
{
216235
auto item = this->new_visual_edge(from, to, edge_tag);
217236
gmap_edges.insert(std::make_pair(key, item));
237+
238+
auto from_str = std::to_string(from);
239+
auto to_str = std::to_string(to);
240+
Agnode_t *gvnode_f = agfindnode(graphviz_graph, from_str.data());
241+
Agnode_t *gvnode_t = agfindnode(graphviz_graph, to_str.data());
242+
243+
Agedge_t* gvedge = agedge(graphviz_graph, gvnode_f, gvnode_t, nullptr, 1);
244+
agset(gvedge, (char *)"type", (char *)edge_tag.data());
245+
218246
}
219247
if (gmap_edges[key]) gmap_edges[key]->change_detected();
220248
}
@@ -266,6 +294,15 @@ void GraphViewer::del_edge_SLOT(std::uint64_t from, std::uint64_t to, const std:
266294
scene.removeItem(edge);
267295
delete edge;
268296
}
297+
298+
auto from_str = std::to_string(from);
299+
auto to_str = std::to_string(to);
300+
Agnode_t *gvnode_f = agfindnode(graphviz_graph, from_str.data());
301+
Agnode_t *gvnode_t = agfindnode(graphviz_graph, to_str.data());
302+
303+
if (Agedge_t *gvedge = agfindedge(graphviz_graph, gvnode_f, gvnode_t)) {
304+
agdeledge(graphviz_graph, gvedge);
305+
}
269306
}
270307
} catch(const std::exception &e) { std::cout << e.what() <<" Error "<<__FUNCTION__<<":"<<__LINE__<< std::endl;}
271308

@@ -282,6 +319,10 @@ void GraphViewer::del_node_SLOT(uint64_t id)
282319
scene.removeItem(item);
283320
delete item;
284321
gmap.erase(id);
322+
auto id_str = std::to_string(id);
323+
if (Agnode_t *gvnode = agfindnode(graphviz_graph, id_str.data())) {
324+
agdelnode(graphviz_graph, gvnode);
325+
}
285326
}
286327
} catch(const std::exception &e) { std::cout << e.what() <<" Error "<<__FUNCTION__<<":"<<__LINE__<< std::endl;}
287328

@@ -333,4 +374,38 @@ void GraphViewer::remove_node_SLOT(uint64_t node_id)
333374
{
334375
std::cout << "Remove node in graph_viewer class"<<node_id <<std::endl;
335376
G->delete_node(node_id);
377+
}
378+
379+
void GraphViewer::compute_layout(const char * alg) {
380+
381+
gvLayout(graphviz_context, graphviz_graph, alg);
382+
383+
qreal root_x = 0.0, root_y = 0.0;
384+
for (Agnode_t* n = agfstnode(graphviz_graph); n; n = agnxtnode(graphviz_graph, n)) {
385+
auto name = agnameof(n);
386+
uint64_t id = std::stoull(name);
387+
double x = ND_coord(n).x;
388+
double y = ND_coord(n).y;
389+
auto *gnode = gmap.at(id);
390+
if (gnode) gnode->setPos(x, y);
391+
if (std::string_view(name) == std::string_view("root")) {
392+
root_x = x;
393+
root_y = y;
394+
}
395+
// We don't need to process the edges to render them
396+
//Uncomment this is the layout propagation is desired
397+
/*qDebug() << __FILE__ <<":"<<__FUNCTION__<< " node id in graphnode: " << id ;
398+
std::optional<Node> g_node = G->get_node(id);
399+
if (g_node.has_value()) {
400+
G->add_or_modify_attrib_local<pos_x_att>(*g_node, (float) x);
401+
G->add_or_modify_attrib_local<pos_y_att>(*g_node, (float) y);
402+
G->update_node(*g_node);
403+
}*/
404+
}
405+
406+
centerOn(root_x, root_y);
407+
this->scene.setSceneRect(scene.itemsBoundingRect());
408+
this->fitInView(scene.itemsBoundingRect(), Qt::KeepAspectRatio );
409+
410+
gvFreeLayout(graphviz_context, graphviz_graph);
336411
}

0 commit comments

Comments
 (0)