Add undo/redo

master
Jeremy Hu 2018-04-09 07:34:46 +08:00
parent 7b3540f0c1
commit 35a62960ca
5 changed files with 84 additions and 13 deletions

View File

@ -5,6 +5,8 @@
#include "skeletondocument.h" #include "skeletondocument.h"
#include "util.h" #include "util.h"
unsigned long SkeletonDocument::m_maxSnapshot = 1000;
SkeletonDocument::SkeletonDocument() : SkeletonDocument::SkeletonDocument() :
m_resultMeshIsObsolete(false), m_resultMeshIsObsolete(false),
m_resultMesh(nullptr), m_resultMesh(nullptr),
@ -33,7 +35,7 @@ void SkeletonDocument::removeEdge(QUuid edgeId)
qDebug() << "Find edge failed:" << edgeId; qDebug() << "Find edge failed:" << edgeId;
return; return;
} }
if (isPartLocked(edge->partId)) if (isPartReadonly(edge->partId))
return; return;
const SkeletonPart *oldPart = findPart(edge->partId); const SkeletonPart *oldPart = findPart(edge->partId);
if (nullptr == oldPart) { if (nullptr == oldPart) {
@ -94,7 +96,7 @@ void SkeletonDocument::removeNode(QUuid nodeId)
qDebug() << "Find node failed:" << nodeId; qDebug() << "Find node failed:" << nodeId;
return; return;
} }
if (isPartLocked(node->partId)) if (isPartReadonly(node->partId))
return; return;
const SkeletonPart *oldPart = findPart(node->partId); const SkeletonPart *oldPart = findPart(node->partId);
if (nullptr == oldPart) { if (nullptr == oldPart) {
@ -174,7 +176,7 @@ void SkeletonDocument::addNode(float x, float y, float z, float radius, QUuid fr
return; return;
} }
partId = fromNode->partId; partId = fromNode->partId;
if (isPartLocked(partId)) if (isPartReadonly(partId))
return; return;
} }
SkeletonNode node; SkeletonNode node;
@ -243,7 +245,7 @@ void SkeletonDocument::addEdge(QUuid fromNodeId, QUuid toNodeId)
return; return;
} }
if (isPartLocked(fromNode->partId)) if (isPartReadonly(fromNode->partId))
return; return;
toNode = findNode(toNodeId); toNode = findNode(toNodeId);
@ -252,7 +254,7 @@ void SkeletonDocument::addEdge(QUuid fromNodeId, QUuid toNodeId)
return; return;
} }
if (isPartLocked(toNode->partId)) if (isPartReadonly(toNode->partId))
return; return;
QUuid toPartId = toNode->partId; QUuid toPartId = toNode->partId;
@ -339,21 +341,21 @@ void SkeletonDocument::scaleNodeByAddRadius(QUuid nodeId, float amount)
qDebug() << "Find node failed:" << nodeId; qDebug() << "Find node failed:" << nodeId;
return; return;
} }
if (isPartLocked(it->second.partId)) if (isPartReadonly(it->second.partId))
return; return;
it->second.radius += amount; it->second.radius += amount;
emit nodeRadiusChanged(nodeId); emit nodeRadiusChanged(nodeId);
emit skeletonChanged(); emit skeletonChanged();
} }
bool SkeletonDocument::isPartLocked(QUuid partId) bool SkeletonDocument::isPartReadonly(QUuid partId)
{ {
const SkeletonPart *part = findPart(partId); const SkeletonPart *part = findPart(partId);
if (nullptr == part) { if (nullptr == part) {
qDebug() << "Find part failed:" << partId; qDebug() << "Find part failed:" << partId;
return true; return true;
} }
return part->locked; return part->locked || !part->visible;
} }
void SkeletonDocument::moveNodeBy(QUuid nodeId, float x, float y, float z) void SkeletonDocument::moveNodeBy(QUuid nodeId, float x, float y, float z)
@ -363,7 +365,7 @@ void SkeletonDocument::moveNodeBy(QUuid nodeId, float x, float y, float z)
qDebug() << "Find node failed:" << nodeId; qDebug() << "Find node failed:" << nodeId;
return; return;
} }
if (isPartLocked(it->second.partId)) if (isPartReadonly(it->second.partId))
return; return;
it->second.x += x; it->second.x += x;
it->second.y += y; it->second.y += y;
@ -379,7 +381,7 @@ void SkeletonDocument::setNodeOrigin(QUuid nodeId, float x, float y, float z)
qDebug() << "Find node failed:" << nodeId; qDebug() << "Find node failed:" << nodeId;
return; return;
} }
if (isPartLocked(it->second.partId)) if (isPartReadonly(it->second.partId))
return; return;
it->second.x = x; it->second.x = x;
it->second.y = y; it->second.y = y;
@ -395,7 +397,7 @@ void SkeletonDocument::setNodeRadius(QUuid nodeId, float radius)
qDebug() << "Find node failed:" << nodeId; qDebug() << "Find node failed:" << nodeId;
return; return;
} }
if (isPartLocked(it->second.partId)) if (isPartReadonly(it->second.partId))
return; return;
it->second.radius = radius; it->second.radius = radius;
emit nodeRadiusChanged(nodeId); emit nodeRadiusChanged(nodeId);
@ -555,6 +557,8 @@ void SkeletonDocument::fromSnapshot(const SkeletonSnapshot &snapshot)
nodeMap.clear(); nodeMap.clear();
edgeMap.clear(); edgeMap.clear();
partMap.clear(); partMap.clear();
partIds.clear();
emit partListChanged();
for (const auto &nodeKv : snapshot.nodes) { for (const auto &nodeKv : snapshot.nodes) {
SkeletonNode node(QUuid(nodeKv.first)); SkeletonNode node(QUuid(nodeKv.first));
@ -603,6 +607,7 @@ void SkeletonDocument::fromSnapshot(const SkeletonSnapshot &snapshot)
} }
emit partListChanged(); emit partListChanged();
emit skeletonChanged();
} }
const char *SkeletonNodeRootMarkModeToString(SkeletonNodeRootMarkMode mode) const char *SkeletonNodeRootMarkModeToString(SkeletonNodeRootMarkMode mode)
@ -772,3 +777,33 @@ void SkeletonDocument::setPartSubdivState(QUuid partId, bool subdived)
emit skeletonChanged(); emit skeletonChanged();
} }
void SkeletonDocument::saveSnapshot()
{
if (m_undoItems.size() + 1 > m_maxSnapshot)
m_undoItems.pop_front();
SkeletonHistoryItem item;
toSnapshot(&item.snapshot);
m_undoItems.push_back(item);
qDebug() << "Undo items:" << m_undoItems.size();
}
void SkeletonDocument::undo()
{
if (m_undoItems.empty())
return;
m_redoItems.push_back(m_undoItems.back());
fromSnapshot(m_undoItems.back().snapshot);
m_undoItems.pop_back();
qDebug() << "Undo/Redo items:" << m_undoItems.size() << m_redoItems.size();
}
void SkeletonDocument::redo()
{
if (m_redoItems.empty())
return;
m_undoItems.push_back(m_redoItems.back());
fromSnapshot(m_redoItems.back().snapshot);
m_redoItems.pop_back();
qDebug() << "Undo/Redo items:" << m_undoItems.size() << m_redoItems.size();
}

View File

@ -5,6 +5,7 @@
#include <vector> #include <vector>
#include <map> #include <map>
#include <set> #include <set>
#include <deque>
#include <QImage> #include <QImage>
#include "skeletonsnapshot.h" #include "skeletonsnapshot.h"
#include "mesh.h" #include "mesh.h"
@ -148,7 +149,6 @@ public:
std::map<QUuid, SkeletonPart> partMap; std::map<QUuid, SkeletonPart> partMap;
std::map<QUuid, SkeletonNode> nodeMap; std::map<QUuid, SkeletonNode> nodeMap;
std::map<QUuid, SkeletonEdge> edgeMap; std::map<QUuid, SkeletonEdge> edgeMap;
std::vector<SkeletonHistoryItem> historyItems;
std::vector<QUuid> partIds; std::vector<QUuid> partIds;
QImage turnaround; QImage turnaround;
SkeletonDocumentEditMode editMode; SkeletonDocumentEditMode editMode;
@ -180,15 +180,21 @@ public slots:
void setPartLockState(QUuid partId, bool locked); void setPartLockState(QUuid partId, bool locked);
void setPartVisibleState(QUuid partId, bool visible); void setPartVisibleState(QUuid partId, bool visible);
void setPartSubdivState(QUuid partId, bool subdived); void setPartSubdivState(QUuid partId, bool subdived);
void saveSnapshot();
void undo();
void redo();
private: private:
void splitPartByNode(std::vector<std::vector<QUuid>> *groups, QUuid nodeId); void splitPartByNode(std::vector<std::vector<QUuid>> *groups, QUuid nodeId);
void joinNodeAndNeiborsToGroup(std::vector<QUuid> *group, QUuid nodeId, std::set<QUuid> *visitMap, QUuid noUseEdgeId=QUuid()); void joinNodeAndNeiborsToGroup(std::vector<QUuid> *group, QUuid nodeId, std::set<QUuid> *visitMap, QUuid noUseEdgeId=QUuid());
void splitPartByEdge(std::vector<std::vector<QUuid>> *groups, QUuid edgeId); void splitPartByEdge(std::vector<std::vector<QUuid>> *groups, QUuid edgeId);
bool isPartLocked(QUuid partId); bool isPartReadonly(QUuid partId);
private: private:
bool m_resultMeshIsObsolete; bool m_resultMeshIsObsolete;
MeshGenerator *m_meshGenerator; MeshGenerator *m_meshGenerator;
Mesh *m_resultMesh; Mesh *m_resultMesh;
static unsigned long m_maxSnapshot;
std::deque<SkeletonHistoryItem> m_undoItems;
std::deque<SkeletonHistoryItem> m_redoItems;
}; };
#endif #endif

View File

@ -157,6 +157,10 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() :
connect(changeTurnaroundButton, &QPushButton::clicked, this, &SkeletonDocumentWindow::changeTurnaround); connect(changeTurnaroundButton, &QPushButton::clicked, this, &SkeletonDocumentWindow::changeTurnaround);
connect(undoButton, &QPushButton::clicked, [=]() {
m_document->undo();
});
connect(addButton, &QPushButton::clicked, [=]() { connect(addButton, &QPushButton::clicked, [=]() {
m_document->setEditMode(SkeletonDocumentEditMode::Add); m_document->setEditMode(SkeletonDocumentEditMode::Add);
}); });
@ -186,6 +190,9 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() :
connect(graphicsWidget, &SkeletonGraphicsWidget::setEditMode, m_document, &SkeletonDocument::setEditMode); connect(graphicsWidget, &SkeletonGraphicsWidget::setEditMode, m_document, &SkeletonDocument::setEditMode);
connect(graphicsWidget, &SkeletonGraphicsWidget::removeEdge, m_document, &SkeletonDocument::removeEdge); connect(graphicsWidget, &SkeletonGraphicsWidget::removeEdge, m_document, &SkeletonDocument::removeEdge);
connect(graphicsWidget, &SkeletonGraphicsWidget::addEdge, m_document, &SkeletonDocument::addEdge); connect(graphicsWidget, &SkeletonGraphicsWidget::addEdge, m_document, &SkeletonDocument::addEdge);
connect(graphicsWidget, &SkeletonGraphicsWidget::groupOperationAdded, m_document, &SkeletonDocument::saveSnapshot);
connect(graphicsWidget, &SkeletonGraphicsWidget::undo, m_document, &SkeletonDocument::undo);
connect(graphicsWidget, &SkeletonGraphicsWidget::redo, m_document, &SkeletonDocument::redo);
connect(m_document, &SkeletonDocument::nodeAdded, graphicsWidget, &SkeletonGraphicsWidget::nodeAdded); connect(m_document, &SkeletonDocument::nodeAdded, graphicsWidget, &SkeletonGraphicsWidget::nodeAdded);
connect(m_document, &SkeletonDocument::nodeRemoved, graphicsWidget, &SkeletonGraphicsWidget::nodeRemoved); connect(m_document, &SkeletonDocument::nodeRemoved, graphicsWidget, &SkeletonGraphicsWidget::nodeRemoved);

View File

@ -359,10 +359,12 @@ bool SkeletonGraphicsWidget::wheel(QWheelEvent *event)
emit scaleNodeByAddRadius(nodeItem->id(), unifiedDelta); emit scaleNodeByAddRadius(nodeItem->id(), unifiedDelta);
} }
} }
emit groupOperationAdded();
return true; return true;
} else if (m_hoveredNodeItem) { } else if (m_hoveredNodeItem) {
float unifiedDelta = sceneRadiusToUnified(delta); float unifiedDelta = sceneRadiusToUnified(delta);
emit scaleNodeByAddRadius(m_hoveredNodeItem->id(), unifiedDelta); emit scaleNodeByAddRadius(m_hoveredNodeItem->id(), unifiedDelta);
emit groupOperationAdded();
return true; return true;
} }
} }
@ -379,6 +381,7 @@ bool SkeletonGraphicsWidget::mouseRelease(QMouseEvent *event)
} }
if (m_moveStarted) { if (m_moveStarted) {
m_moveStarted = false; m_moveStarted = false;
emit groupOperationAdded();
} }
if (m_rangeSelectionStarted) { if (m_rangeSelectionStarted) {
m_selectionItem->hide(); m_selectionItem->hide();
@ -428,6 +431,7 @@ bool SkeletonGraphicsWidget::mousePress(QMouseEvent *event)
if (m_document->findEdgeByNodes(m_addFromNodeItem->id(), m_hoveredNodeItem->id())) if (m_document->findEdgeByNodes(m_addFromNodeItem->id(), m_hoveredNodeItem->id()))
return true; return true;
emit addEdge(m_addFromNodeItem->id(), m_hoveredNodeItem->id()); emit addEdge(m_addFromNodeItem->id(), m_hoveredNodeItem->id());
emit groupOperationAdded();
return true; return true;
} }
} }
@ -452,6 +456,7 @@ bool SkeletonGraphicsWidget::mousePress(QMouseEvent *event)
m_lastAddedZ = unifiedSidePos.x(); m_lastAddedZ = unifiedSidePos.x();
qDebug() << "Emit add node " << m_lastAddedX << m_lastAddedY << m_lastAddedZ; qDebug() << "Emit add node " << m_lastAddedX << m_lastAddedY << m_lastAddedZ;
emit addNode(unifiedMainPos.x(), unifiedMainPos.y(), unifiedSidePos.x(), sceneRadiusToUnified(m_cursorNodeItem->radius()), nullptr == m_addFromNodeItem ? QUuid() : m_addFromNodeItem->id()); emit addNode(unifiedMainPos.x(), unifiedMainPos.y(), unifiedSidePos.x(), sceneRadiusToUnified(m_cursorNodeItem->radius()), nullptr == m_addFromNodeItem ? QUuid() : m_addFromNodeItem->id());
emit groupOperationAdded();
return true; return true;
} }
} else if (SkeletonDocumentEditMode::Select == m_document->editMode) { } else if (SkeletonDocumentEditMode::Select == m_document->editMode) {
@ -533,6 +538,7 @@ bool SkeletonGraphicsWidget::keyPress(QKeyEvent *event)
} }
} }
if (processed) { if (processed) {
emit groupOperationAdded();
return true; return true;
} }
} else if (event->key() == Qt::Key_A) { } else if (event->key() == Qt::Key_A) {
@ -542,6 +548,20 @@ bool SkeletonGraphicsWidget::keyPress(QKeyEvent *event)
emit setEditMode(SkeletonDocumentEditMode::Add); emit setEditMode(SkeletonDocumentEditMode::Add);
} }
return true; return true;
} else if (event->key() == Qt::Key_Z) {
if (QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ControlModifier)) {
if (QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier)) {
emit redo();
} else {
emit undo();
}
}
} else if (event->key() == Qt::Key_Y) {
if (QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ControlModifier)) {
if (!QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier)) {
emit redo();
}
}
} }
return false; return false;
} }

View File

@ -259,6 +259,9 @@ signals:
void removeEdge(QUuid edgeId); void removeEdge(QUuid edgeId);
void addEdge(QUuid fromNodeId, QUuid toNodeId); void addEdge(QUuid fromNodeId, QUuid toNodeId);
void cursorChanged(); void cursorChanged();
void groupOperationAdded();
void undo();
void redo();
public: public:
SkeletonGraphicsWidget(const SkeletonDocument *document); SkeletonGraphicsWidget(const SkeletonDocument *document);
std::map<QUuid, std::pair<SkeletonGraphicsNodeItem *, SkeletonGraphicsNodeItem *>> nodeItemMap; std::map<QUuid, std::pair<SkeletonGraphicsNodeItem *, SkeletonGraphicsNodeItem *>> nodeItemMap;