From: APTX Date: Thu, 15 Dec 2011 20:19:55 +0000 (+0100) Subject: Done X-Git-Url: https://gitweb.aptx.org/?a=commitdiff_plain;h=8936e25493ab647bc5fe57b45eee35b85aac861d;p=graph.git Done --- diff --git a/edge.cpp b/edge.cpp index beb35b5..f8bdd21 100644 --- a/edge.cpp +++ b/edge.cpp @@ -14,16 +14,57 @@ void Edge::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWid { Q_UNUSED(option); Q_UNUSED(widget); - painter->setPen(m_color); - painter->drawLine(m_startNode->pos(), m_endNode->pos()); + + static const double Pi = 3.14159265358979323846264338327950288419717; + static double TwoPi = 2.0 * Pi; + + QPointF sourcePoint = m_startNode->pos(); + QPointF destPoint = m_endNode->pos(); + + { + QLineF line(sourcePoint, destPoint); + qreal length = line.length(); + + if (length <= Node::size) + return; + + prepareGeometryChange(); + + QPointF edgeOffset((line.dx() * Node::size / 2) / length, (line.dy() * Node::size / 2) / length); + sourcePoint = line.p1() + edgeOffset; + destPoint = line.p2() - edgeOffset; + } + QLineF line(sourcePoint, destPoint); + + // Draw the line itself + painter->setPen(QPen(m_color, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); + painter->drawLine(line); + + // Draw the arrows + double angle = ::acos(line.dx() / line.length()); + if (line.dy() >= 0) + angle = TwoPi - angle; + QPointF destArrowP1 = destPoint + QPointF(sin(angle - Pi / 3) * arrowSize, + cos(angle - Pi / 3) * arrowSize); + QPointF destArrowP2 = destPoint + QPointF(sin(angle - Pi + Pi / 3) * arrowSize, + cos(angle - Pi + Pi / 3) * arrowSize); + + painter->setBrush(m_color); + painter->drawPolygon(QPolygonF() << line.p1()); + painter->drawPolygon(QPolygonF() << line.p2() << destArrowP1 << destArrowP2); + // painter->drawText(boundingRect(), Qt::AlignCenter, QString::number(m_weight)); } QRectF Edge::boundingRect() const { - QPointF tl(qMin(m_startNode->x(), m_endNode->x()), qMin(m_startNode->y(), m_endNode->y())); - QPointF br(qMax(m_startNode->x(), m_endNode->x()), qMax(m_startNode->y(), m_endNode->y())); - return QRectF(tl, br); + qreal penWidth = 1; + qreal extra = (penWidth + arrowSize) / 2.0; + + return QRectF(m_startNode->pos(), QSizeF(m_endNode->pos().x() - m_startNode->pos().x(), + m_endNode->pos().y() - m_startNode->pos().y())) + .normalized() + .adjusted(-extra, -extra, extra, extra); } QPainterPath Edge::shape() const diff --git a/edge.h b/edge.h index 90b8cee..64b94f8 100644 --- a/edge.h +++ b/edge.h @@ -63,6 +63,8 @@ private: QColor m_color; Node *m_startNode; Node *m_endNode; + + static const int arrowSize = 10; }; QDataStream &operator<<(QDataStream &s, const Edge &edge); diff --git a/graph.cpp b/graph.cpp index 8c63202..b3af7ef 100644 --- a/graph.cpp +++ b/graph.cpp @@ -10,7 +10,7 @@ Graph::Graph(QObject *parent) : QGraphicsScene(parent), m_nodeModel(new NodeModel(this, this)), - m_edgeModel(new EdgeModel(this, this)), m_mode(EditMode), lastNode(1), startNode(0) + m_edgeModel(new EdgeModel(this, this)), m_mode(EditMode), m_addBothEdges(false), lastNode(1), startNode(0) { } @@ -74,6 +74,7 @@ Node *Graph::addNode(const QPointF &pos, const QString &label, const QColor &col m_nodeList.append(node); addItem(node); m_nodeModel->endInsertRows(); + emit graphChanged(); return node; } @@ -94,6 +95,7 @@ Edge *Graph::addEdge(Node *start, Node *end, int weight, const QColor &color) m_edgeList.append(edge); addItem(edge); m_edgeModel->endInsertRows(); + emit graphChanged(); return edge; } @@ -113,6 +115,7 @@ void Graph::removeNode(Node *node) m_nodeList.removeAt(i); m_nodeModel->endRemoveRows(); node->deleteLater(); + emit graphChanged(); } void Graph::removeEdge(Edge *edge) @@ -129,6 +132,7 @@ void Graph::removeEdge(Edge *edge) m_edgeList.removeAt(i); m_edgeModel->endRemoveRows(); edge->deleteLater(); + emit graphChanged(); } void Graph::clear() @@ -141,6 +145,7 @@ void Graph::clear() lastNode = 1; startNode = 0; + emit graphChanged(); } void Graph::save(QIODevice *dev) const @@ -204,7 +209,13 @@ bool Graph::load(QIODevice *dev) m_nodeModel->reset(); m_edgeModel->reset(); - return false; + return true; +} + +void Graph::update(const QRectF &rect) +{ + nodeColorChanged(); + QGraphicsScene::update(rect); } void Graph::mousePressEvent(QGraphicsSceneMouseEvent *event) @@ -253,7 +264,8 @@ void Graph::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) if (startNode != node) { addEdge(startNode, node); - addEdge(node, startNode); + if (m_addBothEdges) + addEdge(node, startNode); } startNode = 0; } @@ -282,6 +294,12 @@ void Graph::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) event->ignore(); } +void Graph::nodeColorChanged() const +{ + m_nodeModel->dataChanged(m_nodeModel->index(0, 1), m_nodeModel->index(m_nodeList.count() - 1, 1)); + labelChanged(); +} + void Graph::labelChanged() const { m_edgeModel->dataChanged(m_edgeModel->index(0, 0), m_edgeModel->index(m_edgeList.count() - 1, 1)); diff --git a/graph.h b/graph.h index 5e2ec64..9df696a 100644 --- a/graph.h +++ b/graph.h @@ -14,6 +14,7 @@ class Graph : public QGraphicsScene friend EdgeModel; Q_OBJECT Q_PROPERTY(Mode mode READ mode WRITE setMode) + Q_PROPERTY(bool addBothEdges READ addBothEdges WRITE setAddBothEdges) public: enum Mode { @@ -35,7 +36,13 @@ public: return m_mode; } + bool addBothEdges() const + { + return m_addBothEdges; + } + signals: + void graphChanged(); public slots: void complete(); @@ -50,17 +57,28 @@ public slots: void setMode(Mode mode) { m_mode = mode; + startNode = 0; } + void setAddBothEdges(bool arg) + { + m_addBothEdges = arg; + } + + void save(QIODevice *dev) const; bool load(QIODevice *dev); + void update(const QRectF &rect = QRectF()); + + protected: void mousePressEvent(QGraphicsSceneMouseEvent *event); void mouseMoveEvent(QGraphicsSceneMouseEvent *event); void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); private: + void nodeColorChanged() const; void labelChanged() const; NodeList m_nodeList; @@ -75,6 +93,7 @@ private: // For adding edges Node *startNode; + bool m_addBothEdges; }; #else class Graph; diff --git a/mainwindow.cpp b/mainwindow.cpp index ab5f00a..e920570 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -25,6 +25,7 @@ MainWindow::MainWindow(QWidget *parent) : modes->addAction(ui->actionEditMode); modes->addAction(ui->actionAddNodeMode); modes->addAction(ui->actionAddEdgeMode); + modes->addAction(ui->actionAddBothEdgeMode); modes->addAction(ui->actionRemoveMode); ui->actionEditMode->setChecked(true); ui->modeToolBar->addActions(modes->actions()); @@ -38,7 +39,11 @@ MainWindow::MainWindow(QWidget *parent) : pmc = new PMC(graph, this); connect(modes, SIGNAL(triggered(QAction*)), this, SLOT(modeChanged(QAction*))); + connect(graph, SIGNAL(graphChanged()), this, SLOT(graphChanged())); + connect(pmc, SIGNAL(log(QString)), this, SLOT(pmcLog(QString))); + graph->setSceneRect(QRectF(-500, -500, 1000, 1000)); +/* Node *n1, *n2; Edge *e; n1 = graph->addNode(); @@ -49,7 +54,7 @@ MainWindow::MainWindow(QWidget *parent) : e = graph->addEdge(n1, n2); e->setColor(QColor(0,0,255)); - +*/ nodeProxy = new QSortFilterProxyModel(this); edgeProxy = new QSortFilterProxyModel(this); @@ -74,13 +79,35 @@ void MainWindow::modeChanged(QAction *action) if (action == ui->actionAddNodeMode) graph->setMode(Graph::AddNodeMode); else if (action == ui->actionAddEdgeMode) + { + graph->setMode(Graph::AddEdgeMode); + graph->setAddBothEdges(false); + } + else if (action == ui->actionAddBothEdgeMode) + { graph->setMode(Graph::AddEdgeMode); + graph->setAddBothEdges(true); + } else if (action == ui->actionRemoveMode) graph->setMode(Graph::RemoveNode); else graph->setMode(Graph::EditMode); } +void MainWindow::graphChanged() +{ + if (pmc->finished()) + ui->pmcLog->clear(); + pmc->reset(); + updateLabels(); +} + +void MainWindow::pmcLog(const QString &log) +{ + ui->pmcLog->append(log); +// qDebug() << log; +} + void MainWindow::on_actionNew_triggered() { graph->clear(); @@ -177,12 +204,35 @@ void MainWindow::on_actionClearAll_triggered() graph->clear(); } +void MainWindow::on_m_valueChanged(int m) +{ + pmc->setM(m); +} +void MainWindow::on_pmcRun_clicked() +{ + pmc->setM(ui->m->value()); + pmc->run(); + ui->statusBar->showMessage(tr("PMC run complete")); + updateLabels(); + graph->update(); +} void MainWindow::on_pmcStep_clicked() { + pmc->setM(ui->m->value()); if (pmc->step()) ui->statusBar->showMessage(tr("Last Step complete")); else ui->statusBar->showMessage(tr("Step complete")); + updateLabels(); + graph->update(); +} + + +void MainWindow::updateLabels() +{ + ui->mRange->setText(QString("0..%1").arg(pmc->maxM())); + ui->pmcFailed->setText(pmc->failed() ? "Yes" : "No"); + ui->pmcFinished->setText(pmc->finished() ? "Yes" : "No"); } diff --git a/mainwindow.h b/mainwindow.h index 67c0983..fd2f0e4 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -23,6 +23,8 @@ public: private slots: void modeChanged(QAction *action); + void graphChanged(); + void pmcLog(const QString &log); void on_actionNew_triggered(); void on_actionOpen_triggered(); @@ -35,9 +37,13 @@ private slots: void on_actionGenerateGraph_triggered(); void on_actionClearAll_triggered(); + void on_m_valueChanged(int m); + void on_pmcRun_clicked(); void on_pmcStep_clicked(); private: + void updateLabels(); + Ui::MainWindow *ui; QSortFilterProxyModel *nodeProxy; diff --git a/mainwindow.ui b/mainwindow.ui index d60cdba..f25c528 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -113,7 +113,7 @@ - 80 + 365 195 @@ -124,19 +124,150 @@ 8 - - - - 70 - 0 - 75 - 23 - - - - Step - - + + + + + + + + + Run + + + + + + + Step + + + + + + + + + Input + + + + + + m range: + + + + + + + 0..1 + + + + + + + m: + + + + + + + 2 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Log + + + + + + true + + + + + + + + + + + + Status: + + + + + + Finished: + + + + + + + No + + + + + + + Failed: + + + + + + + No + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + @@ -240,6 +371,17 @@ Ctrl+Shift+S + + + true + + + Add Both Edges Mode + + + Add Both Edges Mode + + diff --git a/node.cpp b/node.cpp index ab17af3..28cc07c 100644 --- a/node.cpp +++ b/node.cpp @@ -15,8 +15,10 @@ void Node::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWid Q_UNUSED(option); Q_UNUSED(widget); - QBrush b(m_color); - painter->setBrush(b); + QRadialGradient gradient(-3, -3, 10); + gradient.setColorAt(0, Qt::white); + gradient.setColorAt(1, m_color); + painter->setBrush(gradient); painter->drawEllipse(QRectF(-size/2, -size/2, size, size)); painter->drawText(QPointF(size/2, size), m_label); } diff --git a/node.h b/node.h index 7b1be17..7e8f3ab 100644 --- a/node.h +++ b/node.h @@ -63,6 +63,9 @@ public slots: m_color = color; } +public: + static const qreal size; + private: QString m_label; QColor m_color; @@ -70,7 +73,6 @@ private: EdgeList m_outgoingEdges; EdgeList m_incomingEdges; - static const qreal size; }; QDataStream &operator<<(QDataStream &s, const Node &node); diff --git a/nodemodel.cpp b/nodemodel.cpp index 10a7d77..20bde30 100644 --- a/nodemodel.cpp +++ b/nodemodel.cpp @@ -13,7 +13,9 @@ NodeModel::NodeModel(Graph *graph, QObject *parent) : Qt::ItemFlags NodeModel::flags(const QModelIndex &index) const { - return QAbstractTableModel::flags(index) | Qt::ItemIsEditable; + if (index.column() < 4) + return QAbstractTableModel::flags(index) | Qt::ItemIsEditable; + return QAbstractTableModel::flags(index); } QVariant NodeModel::headerData(int section, Qt::Orientation orientation, int role) const @@ -33,6 +35,10 @@ QVariant NodeModel::headerData(int section, Qt::Orientation orientation, int rol return "X"; case 3: return "Y"; + case 4: + return "In Degree"; + case 5: + return "Out Degree"; default: break; } @@ -46,7 +52,7 @@ int NodeModel::rowCount(const QModelIndex &) const int NodeModel::columnCount(const QModelIndex &) const { - return 4; + return 6; } QVariant NodeModel::data(const QModelIndex &index, int role) const @@ -66,6 +72,10 @@ QVariant NodeModel::data(const QModelIndex &index, int role) const return n->x(); case 3: return n->y(); + case 4: + return n->incomingEdges().count(); + case 5: + return n->outgoingEdges().count(); default: break; } diff --git a/pmc.cpp b/pmc.cpp index 8505781..1d4d65d 100644 --- a/pmc.cpp +++ b/pmc.cpp @@ -4,59 +4,146 @@ #include "node.h" #include "edge.h" +#include "combination.h" + +#include +#include + PMC::PMC(Graph *g, QObject *parent) : - QObject(parent), m_graph(g) + QObject(parent), m_graph(g), m_m(0) +{ +} + +bool cmp(Node *a, Node *b) { + return a->incomingEdges().count() > b->incomingEdges().count(); } +void PMC::run() +{ + while(!step()) + QApplication::processEvents(); +} + + bool PMC::step() { - Node *next = findHighestDegree(); + QVector nodes = m_graph->nodes().toVector(); + NodeList nodesByIncomingCount = m_graph->nodes(); - if (!next) - return true; + qSort(nodesByIncomingCount.begin(), nodesByIncomingCount.end(), cmp); - Edge *e = next->incomingEdges().first(); -// Node *otherNode = e->startNode(); - m_graph->removeEdge(e); + // Check if possible + bool can = true; + bool allAtM = true; + for (int i = 0; i < nodesByIncomingCount.count(); ++i) + { + Node *n = nodesByIncomingCount[i]; + if (n->incomingEdges().count() != m()) + allAtM = false; + if (n->incomingEdges().count() == m()) + { + n->setColor(Qt::green); + continue; + } + if (n->incomingEdges().count() > m()) + continue; + n->setColor(Qt::red); + can = false; + } + if (!can || allAtM) + { + if (!can) + emit log(tr("Node with less than m incoming edges found. END")); + if (allAtM) + emit log(tr("Done")); + m_failed = !allAtM; + m_finished = true; + return true; + } - if (next->incomingEdges().count() == m()) - next->setColor(Qt::green); - else - next->setColor(Qt::yellow); - next->update(); - return false; -} + qSort(nodes.begin(), nodes.end()); + QVector combination(nodes.count() - 2 * m() + m() - 1); -Node *PMC::findHighestDegree() const -{ - Node *ret = 0; - foreach(Node *n, m_graph->nodes()) + for (int i = 0; i < nodesByIncomingCount.count(); ++i) { - if (n->incomingEdges().count() <= m()) + Node *n = nodesByIncomingCount[i]; + + if (n->incomingEdges().count() < m()) + { + n->setColor(Qt::red); + continue; + } + n->setColor(Qt::yellow); + + for (int j = 0; j < n->incomingEdges().count(); ++j) { - if (n->incomingEdges().count() < m()) + Edge *e = n->incomingEdges()[j]; + + Node *otherNode = e->startNode(); + + bool removable = true; + // HAKIMI + for (int p = 0; p < m() - 1; ++p) { - n->setColor(Qt::red); - n->update(); + int Ep = nodes.count() - 2 * m() + p; + qCopy(nodes.begin(), nodes.begin() + Ep, combination.begin()); + do { + int sum = 0; + for (int k = 0; k < Ep; ++k) + sum += combination.at(k)->outgoingEdges().count(); + if (combination.contains(otherNode)) + --sum; + + if (sum <= p) + { + removable = false; + break; + } + + } while(stdcomb::next_combination(nodes.begin(), nodes.end(), combination.begin(), combination.begin() + Ep)); + if (!removable) + break; } - continue; + + if (removable) + { + emit log(tr("Removing edge %1->%2 from %3").arg(e->startNode()->label(), e->endNode()->label(), n->label())); + m_graph->removeEdge(e); + + if (n->incomingEdges().count() == m()) + { + emit log(tr("%1 is now at m incoming edges").arg(n->label())); + n->setColor(Qt::green); + } + return false; + } + else + emit log(tr("Cannot remove edge %1->%2 from %3").arg(e->startNode()->label(), e->endNode()->label(), n->label())); + } - if (ret == 0) - ret = n; - if (n->incomingEdges().count() > n->outgoingEdges().count()) - ret = n; + emit log(tr("No edges to remove from %1").arg(n->label())); } - return ret; + emit log(tr("Could not remove any edge and requirements not met. END")); + m_failed = true; + m_finished = true; + return true; +} + +void PMC::reset() +{ + m_failed = false; + m_finished = false; } int PMC::m() const { - return (m_graph->nodes().count() - 1) / 2; + return m_m; } -int PMC::p() const + +int PMC::maxM() const { - return m_graph->nodes().count() - 2 * m(); + return (m_graph->nodes().count() - 1) / 2; } diff --git a/pmc.h b/pmc.h index 85b7844..f888d42 100644 --- a/pmc.h +++ b/pmc.h @@ -9,20 +9,44 @@ class Node; class PMC : public QObject { Q_OBJECT + Q_PROPERTY(int m READ m WRITE setM) + Q_PROPERTY(bool failed READ failed) + Q_PROPERTY(bool finished READ finished) + public: explicit PMC(Graph *g, QObject *parent = 0); - + + int m() const; + int maxM() const; + + bool failed() const + { + return m_failed; + } + + bool finished() const + { + return m_finished; + } + signals: + void log(const QString &log); public slots: + void run(); bool step(); + void reset(); -private: - Node *findHighestDegree() const; - int m() const; - int p() const; + void setM(int m) + { + m_m = m; + } +private: Graph *m_graph; + int m_m; + bool m_failed; + bool m_finished; }; #endif // PMC_H