summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleks Kissinger <aleks0@gmail.com>2019-01-19 18:13:51 +0100
committerAleks Kissinger <aleks0@gmail.com>2019-01-19 18:13:51 +0100
commitbd208d8b604dd7958152fdf4db188a0bd64146f5 (patch)
tree54e841b1dfab4a121364632067b461fd057747d5
parent15d97051f0b5105a2765fc068ae980e36195434c (diff)
merge nodes
-rw-r--r--src/data/edge.cpp10
-rw-r--r--src/gui/mainmenu.cpp6
-rw-r--r--src/gui/mainmenu.h1
-rw-r--r--src/gui/mainmenu.ui9
-rw-r--r--src/gui/tikzscene.cpp63
-rw-r--r--src/gui/tikzscene.h3
-rw-r--r--src/gui/undocommands.cpp10
-rw-r--r--src/gui/undocommands.h10
8 files changed, 103 insertions, 9 deletions
diff --git a/src/data/edge.cpp b/src/data/edge.cpp
index 4803547..0bd49e8 100644
--- a/src/data/edge.cpp
+++ b/src/data/edge.cpp
@@ -57,8 +57,14 @@ Edge::Edge(Node *s, Node *t, QObject *parent) :
Edge *Edge::copy(QMap<Node*,Node*> *nodeTable)
{
Edge *e;
- if (nodeTable == nullptr) e = new Edge(_source, _target);
- else e = new Edge(nodeTable->value(_source), nodeTable->value(_target));
+ if (nodeTable == nullptr) {
+ e = new Edge(_source, _target);
+ } else {
+ Node *s = nodeTable->value(_source);
+ Node *t = nodeTable->value(_target);
+ e = new Edge((s != nullptr) ? s : _source,
+ (t != nullptr) ? t : _target);
+ }
e->setData(_data->copy());
e->setBasicBendMode(_basicBendMode);
e->setBend(_bend);
diff --git a/src/gui/mainmenu.cpp b/src/gui/mainmenu.cpp
index d291390..35ab736 100644
--- a/src/gui/mainmenu.cpp
+++ b/src/gui/mainmenu.cpp
@@ -233,6 +233,12 @@ void MainMenu::on_actionReverse_Edge_Direction_triggered()
tikzit->activeWindow()->tikzScene()->reverseSelectedEdges();
}
+void MainMenu::on_actionMerge_Nodes_triggered()
+{
+ if (tikzit->activeWindow() != 0)
+ tikzit->activeWindow()->tikzScene()->mergeNodes();
+}
+
// Tikz
void MainMenu::on_actionParse_triggered()
diff --git a/src/gui/mainmenu.h b/src/gui/mainmenu.h
index 287019c..51d7d3c 100644
--- a/src/gui/mainmenu.h
+++ b/src/gui/mainmenu.h
@@ -66,6 +66,7 @@ public slots:
void on_actionExtendLeft_triggered();
void on_actionExtendRight_triggered();
void on_actionReverse_Edge_Direction_triggered();
+ void on_actionMerge_Nodes_triggered();
// Tools
void on_actionParse_triggered();
diff --git a/src/gui/mainmenu.ui b/src/gui/mainmenu.ui
index ddba6f0..692f82e 100644
--- a/src/gui/mainmenu.ui
+++ b/src/gui/mainmenu.ui
@@ -73,6 +73,7 @@
<addaction name="menuReorder"/>
<addaction name="menuTransform"/>
<addaction name="actionReverse_Edge_Direction"/>
+ <addaction name="actionMerge_Nodes"/>
</widget>
<widget class="QMenu" name="menuTikz">
<property name="title">
@@ -374,6 +375,14 @@
<string>Ctrl+/</string>
</property>
</action>
+ <action name="actionMerge_Nodes">
+ <property name="text">
+ <string>Merge Nodes</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+M</string>
+ </property>
+ </action>
<addaction name="menuFile"/>
<addaction name="menuEdit"/>
<addaction name="menuView"/>
diff --git a/src/gui/tikzscene.cpp b/src/gui/tikzscene.cpp
index 983cf18..4577981 100644
--- a/src/gui/tikzscene.cpp
+++ b/src/gui/tikzscene.cpp
@@ -186,6 +186,66 @@ void TikzScene::extendSelectionRight()
}
}
+void TikzScene::mergeNodes()
+{
+ refreshZIndices();
+ QSet<Node*> selNodes;
+ QSet<Edge*> selEdges;
+ getSelection(selNodes, selEdges);
+
+ // build a map from locations to a chosen node at that location
+ QMap<QPair<int,int>,Node*> m;
+ foreach (Node *n, selNodes) {
+ // used fixed precision for hashing/comparing locations
+ QPair<int,int> fpPoint(
+ static_cast<int>(n->point().x() * 1000.0),
+ static_cast<int>(n->point().y() * 1000.0));
+ if (!m.contains(fpPoint) ||
+ _nodeItems[m[fpPoint]]->zValue() < _nodeItems[n]->zValue())
+ {
+ m.insert(fpPoint, n);
+ }
+ }
+
+ // build a second map from nodes to the node they will be merged with
+ QMap<Node*,Node*> m1;
+ foreach (Node *n, graph()->nodes()) {
+ QPair<int,int> fpPoint(
+ static_cast<int>(n->point().x() * 1000.0),
+ static_cast<int>(n->point().y() * 1000.0));
+ Node *n1 = m[fpPoint];
+ if (n1 != nullptr && n1 != n) m1.insert(n, n1);
+ }
+
+ _tikzDocument->undoStack()->beginMacro("Merge nodes");
+
+ // copy adjacent edges from nodes that will be deleted
+ foreach (Edge *e, graph()->edges()) {
+ if (m1.contains(e->source()) || m1.contains(e->target())) {
+ Edge *e1 = e->copy(&m1);
+ AddEdgeCommand *cmd = new AddEdgeCommand(this, e1);
+ _tikzDocument->undoStack()->push(cmd);
+ }
+ }
+
+ // delete nodes
+ QMap<int,Node*> delNodes;
+ QMap<int,Edge*> delEdges;
+ for (int i = 0; i < _tikzDocument->graph()->nodes().length(); ++i) {
+ Node *n = _tikzDocument->graph()->nodes()[i];
+ if (m1.contains(n)) delNodes.insert(i, n);
+ }
+ for (int i = 0; i < _tikzDocument->graph()->edges().length(); ++i) {
+ Edge *e = _tikzDocument->graph()->edges()[i];
+ if (m1.contains(e->source()) || m1.contains(e->target())) delEdges.insert(i, e);
+ }
+ DeleteCommand *cmd = new DeleteCommand(this, delNodes, delEdges,
+ selNodes, selEdges);
+ _tikzDocument->undoStack()->push(cmd);
+
+ _tikzDocument->undoStack()->endMacro();
+}
+
void TikzScene::reorderSelection(bool toFront)
{
QVector<Node*> nodeOrd, nodeOrd1;
@@ -911,7 +971,8 @@ void TikzScene::deleteSelectedItems()
//qDebug() << "nodes:" << deleteNodes;
//qDebug() << "edges:" << deleteEdges;
- DeleteCommand *cmd = new DeleteCommand(this, deleteNodes, deleteEdges, selEdges);
+ DeleteCommand *cmd = new DeleteCommand(this, deleteNodes, deleteEdges,
+ selNodes, selEdges);
_tikzDocument->undoStack()->push(cmd);
}
diff --git a/src/gui/tikzscene.h b/src/gui/tikzscene.h
index 0d3aadf..f8dc7ec 100644
--- a/src/gui/tikzscene.h
+++ b/src/gui/tikzscene.h
@@ -75,6 +75,8 @@ public:
void extendSelectionLeft();
void extendSelectionRight();
+ void mergeNodes();
+
void reorderSelection(bool toFront);
void reverseSelectedEdges();
@@ -86,7 +88,6 @@ public:
void refreshSceneBounds();
bool highlightHeads() const;
-
bool highlightTails() const;
public slots:
diff --git a/src/gui/undocommands.cpp b/src/gui/undocommands.cpp
index 8ad15d0..c5c26af 100644
--- a/src/gui/undocommands.cpp
+++ b/src/gui/undocommands.cpp
@@ -128,9 +128,12 @@ void EdgeBendCommand::redo()
DeleteCommand::DeleteCommand(TikzScene *scene,
QMap<int, Node *> deleteNodes,
QMap<int, Edge *> deleteEdges,
- QSet<Edge *> selEdges, QUndoCommand *parent) :
+ QSet<Node *> selNodes,
+ QSet<Edge *> selEdges,
+ QUndoCommand *parent) :
GraphUpdateCommand(scene, parent),
- _deleteNodes(deleteNodes), _deleteEdges(deleteEdges), _selEdges(selEdges)
+ _deleteNodes(deleteNodes), _deleteEdges(deleteEdges),
+ _selNodes(selNodes), _selEdges(selEdges)
{}
void DeleteCommand::undo()
@@ -142,7 +145,7 @@ void DeleteCommand::undo()
NodeItem *ni = new NodeItem(n);
_scene->nodeItems().insert(n, ni);
_scene->addItem(ni);
- ni->setSelected(true);
+ if (_selNodes.contains(n)) ni->setSelected(true);
}
for (auto it = _deleteEdges.begin(); it != _deleteEdges.end(); ++it) {
@@ -229,6 +232,7 @@ AddEdgeCommand::AddEdgeCommand(TikzScene *scene,
{
}
+
void AddEdgeCommand::undo()
{
EdgeItem *ei = _scene->edgeItems()[_edge];
diff --git a/src/gui/undocommands.h b/src/gui/undocommands.h
index 94b98be..40f0a3b 100644
--- a/src/gui/undocommands.h
+++ b/src/gui/undocommands.h
@@ -95,6 +95,7 @@ public:
explicit DeleteCommand(TikzScene *scene,
QMap<int,Node*> deleteNodes,
QMap<int,Edge*> deleteEdges,
+ QSet<Node*> selNodes,
QSet<Edge*> selEdges,
QUndoCommand *parent = nullptr);
void undo() override;
@@ -102,6 +103,7 @@ public:
private:
QMap<int,Node*> _deleteNodes;
QMap<int,Edge*> _deleteEdges;
+ QSet<Node*> _selNodes;
QSet<Edge*> _selEdges;
};
@@ -121,12 +123,16 @@ private:
class AddEdgeCommand : public GraphUpdateCommand
{
public:
- explicit AddEdgeCommand(TikzScene *scene, Edge *edge, bool selectEdge, QSet<Node *> selNodes, QSet<Edge *> selEdges, QUndoCommand *parent = nullptr);
+ explicit AddEdgeCommand(TikzScene *scene, Edge *edge,
+ bool selectEdge=false,
+ QSet<Node *> selNodes=QSet<Node*>(),
+ QSet<Edge *> selEdges=QSet<Edge*>(),
+ QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
private:
- bool _selectEdge;
Edge *_edge;
+ bool _selectEdge;
QSet<Node*> _selNodes;
QSet<Edge*> _selEdges;
};