• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdeedu API Reference
  • KDE Home
  • Contact Us
 

rocs/VisualEditor

  • sources
  • kde-4.14
  • kdeedu
  • rocs
  • VisualEditor
  • Tools
  • GenerateGraph
GenerateGraphWidget.cpp
Go to the documentation of this file.
1 /*
2  This file is part of Rocs.
3  Copyright (C) 2011-2013 Andreas Cord-Landwehr <cola@uni-paderborn.de>
4 
5  This program is free software; you can redistribute it and/or
6  modify it under the terms of the GNU General Public License as
7  published by the Free Software Foundation; either version 2 of
8  the License, or (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #include "GenerateGraphWidget.h"
20 #include "ui_GenerateGraphWidget.h"
21 
22 #include "Document.h"
23 #include "DataStructure.h"
24 #include "DocumentManager.h"
25 #include "PointerType.h"
26 #include "Modifiers/Topology.h"
27 #include "Data.h"
28 #include "Pointer.h"
29 
30 #include <cmath>
31 #include <KLocale>
32 #include <KDialog>
33 #include <KComboBox>
34 #include <KDebug>
35 
36 #include <QtCore/QList>
37 #include <QtCore/QMap>
38 #include <QtCore/QPair>
39 
40 #include <boost/graph/adjacency_list.hpp>
41 #include <boost/graph/iteration_macros.hpp>
42 #include <boost/graph/random.hpp>
43 #include <boost/graph/random_layout.hpp>
44 #include <boost/graph/topology.hpp>
45 #include <boost/graph/fruchterman_reingold.hpp>
46 #include <boost/random/mersenne_twister.hpp>
47 #include <boost/graph/erdos_renyi_generator.hpp>
48 #include <boost/math/constants/constants.hpp>
49 
50 // typedefs used for the boost graph library
51 typedef boost::adjacency_list < boost::listS, boost::vecS, boost::undirectedS,
52  boost::property<boost::vertex_name_t, std::string> >
53  Graph;
54 
55 typedef boost::rectangle_topology<> topology_type;
56 
57 typedef topology_type::point_type point_type;
58 
59 typedef std::vector<point_type> PositionVec;
60 
61 typedef boost::iterator_property_map < PositionVec::iterator,
62  boost::property_map<Graph, boost::vertex_index_t>::type >
63  PositionMap;
64 
65 
66 GenerateGraphWidget::GenerateGraphWidget(Document *document)
67 : KDialog()
68 {
69  // setup default identifiers for the created graphs
70  defaultIdentifiers.insert(MeshGraph, "MeshGraph");
71  defaultIdentifiers.insert(StarGraph, "StarGraph");
72  defaultIdentifiers.insert(CircleGraph, "CircleGraph");
73  defaultIdentifiers.insert(ErdosRenyiRandomGraph, "RandomGraph");
74  defaultIdentifiers.insert(RandomTree, "RandomTree");
75  defaultIdentifiers.insert(MeshGraph, "MeshGraph");
76 
77  graphGenerator_ = MeshGraph;
78 
79  QWidget *widget = new QWidget(this);
80  ui = new Ui::GenerateGraphWidget;
81  ui->setupUi(widget);
82  setMainWidget(widget);
83 
84  // other KDialog options
85  setCaption(i18nc("@title:window", "Generate Graph"));
86  setButtons(KDialog::Cancel | KDialog::Ok);
87  ui->buttonShowAdvanced->setIcon(KIcon("rocsadvancedsetup"));
88  KDialog::centerOnScreen(widget, -3);
89 
90  connect(this, SIGNAL(okClicked()), this, SLOT(generateGraph()));
91  connect(ui->comboGraphGenerator, SIGNAL(currentIndexChanged(int)), this, SLOT(setGraphGenerator(int)));
92  connect(ui->dataTypeSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(setDataType(int)));
93  connect(ui->pointerTypeSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(setPointerType(int)));
94 
95  // set random seeds
96  qint64 currentTime = QDateTime::currentMSecsSinceEpoch();
97  uint badRandomSeed = qHash(currentTime) % 99999;
98  badRandomSeed = (badRandomSeed == 0) ? 1 : badRandomSeed;
99  ui->randomGeneratorSeed->setValue(badRandomSeed);
100  ui->GNPGeneratorSeed->setValue(badRandomSeed);
101  ui->randomTreeGeneratorSeed->setValue(badRandomSeed);
102 
103  // set visibility for advanced options
104  // TODO move to containers for easier handling
105  ui->label_randomGeneratorSeed->setVisible(false);
106  ui->randomGeneratorSeed->setVisible(false);
107  ui->label_GNPGeneratorSeed->setVisible(false);
108  ui->GNPGeneratorSeed->setVisible(false);
109  ui->label_randomTreeGeneratorSeed->setVisible(false);
110  ui->randomTreeGeneratorSeed->setVisible(false);
111 
112  foreach (int pointerTypeID, document->pointerTypeList()) {
113  PointerTypePtr pointerType = document->pointerType(pointerTypeID);
114  QString item = i18nc(
115  "@item:inlistbox",
116  "%1 (ID %2)",
117  pointerType->name(),
118  pointerType->identifier());
119  ui->pointerTypeSelector->addItem(item, QVariant(pointerTypeID));
120  }
121  ui->pointerTypeSelector->setCurrentIndex(0);
122 
123  foreach (int dataTypeID, document->dataTypeList()) {
124  DataTypePtr dataType = document->dataType(dataTypeID);
125  QString item = i18nc(
126  "@item:inlistbox",
127  "%1 (ID %2)",
128  dataType->name(),
129  dataType->identifier());
130  ui->dataTypeSelector->addItem(item, QVariant(dataTypeID));
131  }
132  ui->dataTypeSelector->setCurrentIndex(0);
133 }
134 
135 
136 void GenerateGraphWidget::setGraphGenerator(int index)
137 {
138  graphGenerator_ = GraphGenerator(index);
139  if (defaultIdentifiers.contains(graphGenerator_)) {
140  ui->identifier->setText(defaultIdentifiers[graphGenerator_]);
141  } else {
142  ui->identifier->setText("Graph");
143  }
144 }
145 
146 void GenerateGraphWidget::setGraphIdentifier(const QString &identifier)
147 {
148  identifier_ = identifier;
149 }
150 
151 void GenerateGraphWidget::setDataType(int type)
152 {
153  if (!DocumentManager::self().activeDocument()->dataTypeList().contains(type)) {
154  kWarning() << "Data type " << type << " does not exist: aborting";
155  return;
156  }
157  dataType_ = type;
158 }
159 
160 void GenerateGraphWidget::setPointerType(int type)
161 {
162  if (!DocumentManager::self().activeDocument()->pointerTypeList().contains(type)) {
163  kWarning() << "Pointer type " << type << " does not exist: aborting";
164  return;
165  }
166  pointerType_ = type;
167 }
168 
169 void GenerateGraphWidget::setSeed(int seed)
170 {
171  seed_ = seed;
172 }
173 
174 void GenerateGraphWidget::generateGraph()
175 {
176  setGraphIdentifier(ui->identifier->text());
177 
178  switch (graphGenerator_) {
179  case MeshGraph:
180  generateMesh(ui->meshRows->value(), ui->meshColumns->value());
181  break;
182  case CircleGraph:
183  generateCircle(ui->circleNodes->value());
184  break;
185  case StarGraph:
186  generateStar(ui->starSatelliteNodes->value());
187  break;
188  case RandomEdgeGraph:
189  setSeed(ui->randomGeneratorSeed->value());
190  generateRandomGraph(
191  ui->randomNodes->value(),
192  ui->randomEdges->value(),
193  ui->randomAllowSelfedges->isTristate()
194  );
195  break;
196  case ErdosRenyiRandomGraph:
197  setSeed(ui->randomGeneratorSeed->value());
198  generateErdosRenyiRandomGraph(
199  ui->GNPNodes->value(),
200  ui->GNPEdgeProbability->value(),
201  ui->GNPAllowSelfedges->isTristate()
202  );
203  break;
204  case RandomTree:
205  setSeed(ui->randomTreeGeneratorSeed->value());
206  generateRandomTreeGraph(
207  ui->randomTreeNodes->value()
208  );
209  default:
210  break;
211  }
212 
213  close();
214  deleteLater();
215 }
216 
217 GenerateGraphWidget::~GenerateGraphWidget()
218 {
219  delete ui;
220 }
221 
222 void GenerateGraphWidget::generateMesh(int rows, int columns)
223 {
224  QPointF center = DocumentManager::self().activeDocument()->sceneRect().center();
225 
226  if (rows < 1) {
227  rows = 1;
228  }
229  if (columns < 1) {
230  columns = 1;
231  }
232 
233  // use active data structure iff empty
234  DataStructurePtr graph = DocumentManager::self().activeDocument()->activeDataStructure();
235  if (graph->dataListAll().size() > 0) {
236  graph = DocumentManager::self().activeDocument()->addDataStructure(identifier_);
237  }
238 
239  // create mesh of NxN points
240  QMap<QPair<int, int>, DataPtr > meshNodes;
241 
242  // create mesh nodes, store them in map
243  for (int i = 0; i < columns; i++) {
244  for (int j = 0; j < rows; j++) {
245  meshNodes[qMakePair(i, j)] = graph->createData(QString("%1-%2").arg(i).arg(j),
246  QPointF(i * 50, j * 50) - QPoint((int)25 * columns, (int)25 * rows) + center,
247  dataType_
248  );
249  }
250  }
251 
252  // connect mesh nodes
253  for (int i = 0; i < columns; i++) {
254  for (int j = 0; j < rows; j++) {
255  graph->createPointer(meshNodes[qMakePair(i, j)], meshNodes[qMakePair(i, j + 1)], pointerType_); // left
256  graph->createPointer(meshNodes[qMakePair(i, j)], meshNodes[qMakePair(i + 1, j)], pointerType_); // bottom.
257  }
258  }
259 }
260 
261 void GenerateGraphWidget::generateStar(int satelliteNodes)
262 {
263  QPointF center = DocumentManager::self().activeDocument()->sceneRect().center();
264 
265  // compute radius such that nodes have space ~50 between each other
266  // circle that border-length of 2*PI*radius
267  int radius = 50 * satelliteNodes / (2 * boost::math::constants::pi<double>());
268 
269  // use active data structure iff empty
270  DataStructurePtr graph = DocumentManager::self().activeDocument()->activeDataStructure();
271  if (graph->dataListAll().size() > 0) {
272  graph = DocumentManager::self().activeDocument()->addDataStructure(identifier_);
273  }
274 
275  QList< QPair<QString, QPointF> > starNodes;
276  for (int i = 1; i <= satelliteNodes; i++) {
277  QPointF position = QPointF(sin(i * 2 * boost::math::constants::pi<double>() / satelliteNodes)*radius,
278  cos(i * 2 * boost::math::constants::pi<double>() / satelliteNodes)*radius)
279  + center;
280  starNodes << qMakePair(
281  QString("%1").arg(i),
282  position
283  );
284  }
285  QList< DataPtr > nodeList = graph->addDataList(starNodes, dataType_);
286 
287  // center
288  nodeList.prepend(graph->createData(QString("center"), center, dataType_));
289 
290  // connect circle nodes
291  for (int i = 1; i <= satelliteNodes; i++) {
292  graph->createPointer(nodeList.at(0), nodeList.at(i), pointerType_);
293  }
294 }
295 
296 void GenerateGraphWidget::generateCircle(int nodes)
297 {
298  QPointF center = DocumentManager::self().activeDocument()->sceneRect().center();
299 
300  // compute radius such that nodes have space ~50 between each other
301  // circle that border-length of 2*PI*radius
302  int radius = 50 * nodes / (2 * boost::math::constants::pi<double>());
303 
304  // use active data structure iff empty
305  DataStructurePtr graph = DocumentManager::self().activeDocument()->activeDataStructure();
306  if (graph->dataListAll().size() > 0) {
307  graph = DocumentManager::self().activeDocument()->addDataStructure(identifier_);
308  }
309 
310  QList< QPair<QString, QPointF> > circleNodes;
311 
312  // create mesh nodes, store them in map
313  for (int i = 1; i <= nodes; i++) {
314  QPointF position = QPointF( sin(i * 2 * boost::math::constants::pi<double>() / nodes)*radius,
315  cos(i * 2 * boost::math::constants::pi<double>() / nodes)*radius)
316  + center;
317  circleNodes << qMakePair(
318  QString("%1").arg(i),
319  position
320  );
321  }
322  QList< DataPtr > nodeList = graph->addDataList(circleNodes, dataType_);
323 
324  // connect circle nodes
325  for (int i = 0; i < nodes - 1; i++) {
326  graph->createPointer(nodeList.at(i), nodeList.at(i + 1), pointerType_);
327  }
328  graph->createPointer(nodeList.at(nodes - 1), nodeList.at(0), pointerType_);
329 }
330 
331 void GenerateGraphWidget::generateRandomGraph(int nodes, int edges, bool selfEdges)
332 {
333  QPointF center = DocumentManager::self().activeDocument()->sceneRect().center();
334 
335  Graph randomGraph;
336  boost::mt19937 gen;
337  gen.seed(static_cast<unsigned int>(seed_));
338 
339  // generate graph
340  boost::generate_random_graph<Graph, boost::mt19937>(
341  randomGraph,
342  nodes,
343  edges,
344  gen,
345  selfEdges
346  );
347 
348  // generate distribution topology and apply
349  boost::rectangle_topology< boost::mt19937 > topology(gen, center.x() - 20 * nodes, center.y() - 20 * nodes, center.x() + 20 * nodes, center.y() + 20 * nodes);
350  PositionVec position_vec(boost::num_vertices(randomGraph));
351  PositionMap positionMap(position_vec.begin(), get(boost::vertex_index, randomGraph));
352 
353  boost::random_graph_layout(randomGraph, positionMap, topology);
354 
355  // minimize cuts by Fruchtman-Reingold layout algorithm
356  boost::fruchterman_reingold_force_directed_layout< boost::rectangle_topology< boost::mt19937 >, Graph, PositionMap >
357  (randomGraph,
358  positionMap,
359  topology,
360  boost::cooling(boost::linear_cooling<double>(100))
361  );
362 
363  // put generated random graph at whiteboard
364  // use active data structure iff empty
365  DataStructurePtr graph = DocumentManager::self().activeDocument()->activeDataStructure();
366  if (graph->dataListAll().size() > 0) {
367  graph = DocumentManager::self().activeDocument()->addDataStructure(identifier_);
368  }
369 
370  // put nodes at whiteboard as generated
371  QMap<int, DataPtr > mapNodes;
372  int index = 0;
373  boost::graph_traits<Graph>::vertex_iterator vi, vi_end;
374  for (boost::tie(vi, vi_end) = boost::vertices(randomGraph); vi != vi_end; ++vi) {
375  mapNodes[*vi] = graph->createData(
376  QString("%1").arg(index++),
377  QPointF(positionMap[*vi][0], positionMap[*vi][1]),
378  dataType_
379  );
380  }
381 
382  boost::graph_traits<Graph>::edge_iterator ei, ei_end;
383  for (boost::tie(ei, ei_end) = boost::edges(randomGraph); ei != ei_end; ++ei) {
384  graph->createPointer(mapNodes[boost::source(*ei, randomGraph)],
385  mapNodes[boost::target(*ei, randomGraph)],
386  pointerType_);
387  }
388 }
389 
390 
391 void GenerateGraphWidget::generateErdosRenyiRandomGraph(int nodes, double edgeProbability, bool selfEdges)
392 {
393  QPointF center = DocumentManager::self().activeDocument()->sceneRect().center();
394 
395  boost::mt19937 gen;
396  gen.seed(static_cast<unsigned int>(seed_));
397 
398  // generate graph
399  typedef boost::erdos_renyi_iterator<boost::mt19937, Graph> ergen;
400  Graph randomGraph(ergen(gen, nodes, edgeProbability, selfEdges), ergen(), nodes);
401 
402  // generate distribution topology and apply
403  boost::rectangle_topology< boost::mt19937 > topology(gen, center.x() - 20 * nodes, center.y() - 20 * nodes, center.x() + 20 * nodes, center.y() + 20 * nodes);
404  PositionVec position_vec(boost::num_vertices(randomGraph));
405  PositionMap positionMap(position_vec.begin(), get(boost::vertex_index, randomGraph));
406  boost::random_graph_layout(randomGraph, positionMap, topology);
407 
408  // put generated random graph at whiteboard
409  // use active data structure iff empty
410  DataStructurePtr graph = DocumentManager::self().activeDocument()->activeDataStructure();
411  if (graph->dataListAll().size() > 0) {
412  graph = DocumentManager::self().activeDocument()->addDataStructure(identifier_);
413  }
414 
415  // minimize cuts by Fruchtman-Reingold layout algorithm
416  boost::fruchterman_reingold_force_directed_layout< boost::rectangle_topology< boost::mt19937 >, Graph, PositionMap >
417  (randomGraph,
418  positionMap,
419  topology,
420  boost::cooling(boost::linear_cooling<double>(100))
421  );
422 
423  // put nodes at whiteboard as generated
424  QMap<int, DataPtr > mapNodes;
425  int index = 0;
426  boost::graph_traits<Graph>::vertex_iterator vi, vi_end;
427  for (boost::tie(vi, vi_end) = boost::vertices(randomGraph); vi != vi_end; ++vi) {
428  mapNodes[*vi] = graph->createData(
429  QString("%1").arg(index++),
430  QPointF(positionMap[*vi][0], positionMap[*vi][1]),
431  dataType_
432  );
433  }
434 
435  boost::graph_traits<Graph>::edge_iterator ei, ei_end;
436  for (boost::tie(ei, ei_end) = boost::edges(randomGraph); ei != ei_end; ++ei) {
437  graph->createPointer(mapNodes[boost::source(*ei, randomGraph)],
438  mapNodes[boost::target(*ei, randomGraph)],
439  pointerType_);
440  }
441 }
442 
443 
444 void GenerateGraphWidget::generateRandomTreeGraph(int nodes)
445 {
446  Document *activeDocument = DocumentManager::self().activeDocument();
447  QPointF center = DocumentManager::self().activeDocument()->sceneRect().center();
448 
449  DataStructurePtr graph = activeDocument->activeDataStructure();
450  if (graph->dataListAll().size() > 0) {
451  graph = DocumentManager::self().activeDocument()->addDataStructure(identifier_);
452  }
453 
454  boost::mt19937 gen;
455  gen.seed(static_cast<unsigned int>(seed_));
456 
457  DataList addedNodes;
458  addedNodes << graph->createData(QString::number(1), dataType_);
459  PointerTypePtr ptrType = activeDocument->pointerType(0);
460  for (int i = 1; i < nodes; i++) {
461  DataPtr thisNode = graph->createData(QString::number(i + 1), center, dataType_);
462  center += QPointF(30,30);
463  boost::random::uniform_int_distribution<> randomEarlierNodeGen(0, i-1);
464  int randomEarlierNode = randomEarlierNodeGen(gen);
465  graph->createPointer(thisNode, addedNodes.at(randomEarlierNode), pointerType_);
466  if (ptrType->direction() == PointerType::Unidirectional) {
467  graph->createPointer(addedNodes.at(randomEarlierNode), thisNode, pointerType_);
468  }
469  addedNodes.append(thisNode);
470  }
471 
472  Topology topology = Topology();
473  topology.directedGraphDefaultTopology(graph);
474 }
QWidget
QHash::insert
iterator insert(const Key &key, const T &value)
GenerateGraphWidget::setGraphGenerator
void setGraphGenerator(int index)
Select graph generator by its index.
Definition: GenerateGraphWidget.cpp:136
QList::at
const T & at(int i) const
QMap
QPoint
KDialog
GenerateGraphWidget::setGraphIdentifier
void setGraphIdentifier(const QString &identifier)
Set the unique graph identifier for the next generated graph.
Definition: GenerateGraphWidget.cpp:146
QPointF
QDateTime::currentMSecsSinceEpoch
qint64 currentMSecsSinceEpoch()
QString::number
QString number(int n, int base)
QPointF::x
qreal x() const
QPointF::y
qreal y() const
GenerateGraphWidget::setSeed
void setSeed(int seed)
Set seed for the internal random number generator.
Definition: GenerateGraphWidget.cpp:169
GenerateGraphWidget::GenerateGraphWidget
GenerateGraphWidget(Document *document=0)
Definition: GenerateGraphWidget.cpp:66
QString
QList
GenerateGraphWidget::setPointerType
void setPointerType(int type)
Set the type of pointers for the generator.
Definition: GenerateGraphWidget.cpp:160
topology_type
boost::rectangle_topology topology_type
Definition: GenerateGraphWidget.cpp:55
GenerateGraphWidget::generateGraph
void generateGraph()
Generate the graph with the previously set configuration.
Definition: GenerateGraphWidget.cpp:174
point_type
topology_type::point_type point_type
Definition: GenerateGraphWidget.cpp:57
GenerateGraphWidget.h
Graph
boost::adjacency_list< boost::listS, boost::vecS, boost::undirectedS, boost::property< boost::vertex_name_t, std::string > > Graph
Definition: GenerateGraphWidget.cpp:53
GenerateGraphWidget::~GenerateGraphWidget
~GenerateGraphWidget()
Definition: GenerateGraphWidget.cpp:217
GenerateGraphWidget::setDataType
void setDataType(int type)
Set the type of data elements for the generator.
Definition: GenerateGraphWidget.cpp:151
QList::prepend
void prepend(const T &value)
QHash::contains
bool contains(const Key &key) const
PositionVec
std::vector< point_type > PositionVec
Definition: GenerateGraphWidget.cpp:59
PositionMap
boost::iterator_property_map< PositionVec::iterator, boost::property_map< Graph, boost::vertex_index_t >::type > PositionMap
Definition: GenerateGraphWidget.cpp:63
QVariant
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:16:27 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

rocs/VisualEditor

Skip menu "rocs/VisualEditor"
  • Main Page
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdeedu API Reference

Skip menu "kdeedu API Reference"
  • Analitza
  •     lib
  • kalgebra
  • kalzium
  •   libscience
  • kanagram
  • kig
  •   lib
  • klettres
  • marble
  • parley
  • rocs
  •   App
  •   RocsCore
  •   VisualEditor
  •   stepcore

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal