Add undo/redo
parent
7b3540f0c1
commit
35a62960ca
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue