Add undo/redo
parent
7b3540f0c1
commit
35a62960ca
|
@ -5,6 +5,8 @@
|
|||
#include "skeletondocument.h"
|
||||
#include "util.h"
|
||||
|
||||
unsigned long SkeletonDocument::m_maxSnapshot = 1000;
|
||||
|
||||
SkeletonDocument::SkeletonDocument() :
|
||||
m_resultMeshIsObsolete(false),
|
||||
m_resultMesh(nullptr),
|
||||
|
@ -33,7 +35,7 @@ void SkeletonDocument::removeEdge(QUuid edgeId)
|
|||
qDebug() << "Find edge failed:" << edgeId;
|
||||
return;
|
||||
}
|
||||
if (isPartLocked(edge->partId))
|
||||
if (isPartReadonly(edge->partId))
|
||||
return;
|
||||
const SkeletonPart *oldPart = findPart(edge->partId);
|
||||
if (nullptr == oldPart) {
|
||||
|
@ -94,7 +96,7 @@ void SkeletonDocument::removeNode(QUuid nodeId)
|
|||
qDebug() << "Find node failed:" << nodeId;
|
||||
return;
|
||||
}
|
||||
if (isPartLocked(node->partId))
|
||||
if (isPartReadonly(node->partId))
|
||||
return;
|
||||
const SkeletonPart *oldPart = findPart(node->partId);
|
||||
if (nullptr == oldPart) {
|
||||
|
@ -174,7 +176,7 @@ void SkeletonDocument::addNode(float x, float y, float z, float radius, QUuid fr
|
|||
return;
|
||||
}
|
||||
partId = fromNode->partId;
|
||||
if (isPartLocked(partId))
|
||||
if (isPartReadonly(partId))
|
||||
return;
|
||||
}
|
||||
SkeletonNode node;
|
||||
|
@ -243,7 +245,7 @@ void SkeletonDocument::addEdge(QUuid fromNodeId, QUuid toNodeId)
|
|||
return;
|
||||
}
|
||||
|
||||
if (isPartLocked(fromNode->partId))
|
||||
if (isPartReadonly(fromNode->partId))
|
||||
return;
|
||||
|
||||
toNode = findNode(toNodeId);
|
||||
|
@ -252,7 +254,7 @@ void SkeletonDocument::addEdge(QUuid fromNodeId, QUuid toNodeId)
|
|||
return;
|
||||
}
|
||||
|
||||
if (isPartLocked(toNode->partId))
|
||||
if (isPartReadonly(toNode->partId))
|
||||
return;
|
||||
|
||||
QUuid toPartId = toNode->partId;
|
||||
|
@ -339,21 +341,21 @@ void SkeletonDocument::scaleNodeByAddRadius(QUuid nodeId, float amount)
|
|||
qDebug() << "Find node failed:" << nodeId;
|
||||
return;
|
||||
}
|
||||
if (isPartLocked(it->second.partId))
|
||||
if (isPartReadonly(it->second.partId))
|
||||
return;
|
||||
it->second.radius += amount;
|
||||
emit nodeRadiusChanged(nodeId);
|
||||
emit skeletonChanged();
|
||||
}
|
||||
|
||||
bool SkeletonDocument::isPartLocked(QUuid partId)
|
||||
bool SkeletonDocument::isPartReadonly(QUuid partId)
|
||||
{
|
||||
const SkeletonPart *part = findPart(partId);
|
||||
if (nullptr == part) {
|
||||
qDebug() << "Find part failed:" << partId;
|
||||
return true;
|
||||
}
|
||||
return part->locked;
|
||||
return part->locked || !part->visible;
|
||||
}
|
||||
|
||||
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;
|
||||
return;
|
||||
}
|
||||
if (isPartLocked(it->second.partId))
|
||||
if (isPartReadonly(it->second.partId))
|
||||
return;
|
||||
it->second.x += x;
|
||||
it->second.y += y;
|
||||
|
@ -379,7 +381,7 @@ void SkeletonDocument::setNodeOrigin(QUuid nodeId, float x, float y, float z)
|
|||
qDebug() << "Find node failed:" << nodeId;
|
||||
return;
|
||||
}
|
||||
if (isPartLocked(it->second.partId))
|
||||
if (isPartReadonly(it->second.partId))
|
||||
return;
|
||||
it->second.x = x;
|
||||
it->second.y = y;
|
||||
|
@ -395,7 +397,7 @@ void SkeletonDocument::setNodeRadius(QUuid nodeId, float radius)
|
|||
qDebug() << "Find node failed:" << nodeId;
|
||||
return;
|
||||
}
|
||||
if (isPartLocked(it->second.partId))
|
||||
if (isPartReadonly(it->second.partId))
|
||||
return;
|
||||
it->second.radius = radius;
|
||||
emit nodeRadiusChanged(nodeId);
|
||||
|
@ -555,6 +557,8 @@ void SkeletonDocument::fromSnapshot(const SkeletonSnapshot &snapshot)
|
|||
nodeMap.clear();
|
||||
edgeMap.clear();
|
||||
partMap.clear();
|
||||
partIds.clear();
|
||||
emit partListChanged();
|
||||
|
||||
for (const auto &nodeKv : snapshot.nodes) {
|
||||
SkeletonNode node(QUuid(nodeKv.first));
|
||||
|
@ -603,6 +607,7 @@ void SkeletonDocument::fromSnapshot(const SkeletonSnapshot &snapshot)
|
|||
}
|
||||
|
||||
emit partListChanged();
|
||||
emit skeletonChanged();
|
||||
}
|
||||
|
||||
const char *SkeletonNodeRootMarkModeToString(SkeletonNodeRootMarkMode mode)
|
||||
|
@ -772,3 +777,33 @@ void SkeletonDocument::setPartSubdivState(QUuid partId, bool subdived)
|
|||
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 <map>
|
||||
#include <set>
|
||||
#include <deque>
|
||||
#include <QImage>
|
||||
#include "skeletonsnapshot.h"
|
||||
#include "mesh.h"
|
||||
|
@ -148,7 +149,6 @@ public:
|
|||
std::map<QUuid, SkeletonPart> partMap;
|
||||
std::map<QUuid, SkeletonNode> nodeMap;
|
||||
std::map<QUuid, SkeletonEdge> edgeMap;
|
||||
std::vector<SkeletonHistoryItem> historyItems;
|
||||
std::vector<QUuid> partIds;
|
||||
QImage turnaround;
|
||||
SkeletonDocumentEditMode editMode;
|
||||
|
@ -180,15 +180,21 @@ public slots:
|
|||
void setPartLockState(QUuid partId, bool locked);
|
||||
void setPartVisibleState(QUuid partId, bool visible);
|
||||
void setPartSubdivState(QUuid partId, bool subdived);
|
||||
void saveSnapshot();
|
||||
void undo();
|
||||
void redo();
|
||||
private:
|
||||
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 splitPartByEdge(std::vector<std::vector<QUuid>> *groups, QUuid edgeId);
|
||||
bool isPartLocked(QUuid partId);
|
||||
bool isPartReadonly(QUuid partId);
|
||||
private:
|
||||
bool m_resultMeshIsObsolete;
|
||||
MeshGenerator *m_meshGenerator;
|
||||
Mesh *m_resultMesh;
|
||||
static unsigned long m_maxSnapshot;
|
||||
std::deque<SkeletonHistoryItem> m_undoItems;
|
||||
std::deque<SkeletonHistoryItem> m_redoItems;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -157,6 +157,10 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() :
|
|||
|
||||
connect(changeTurnaroundButton, &QPushButton::clicked, this, &SkeletonDocumentWindow::changeTurnaround);
|
||||
|
||||
connect(undoButton, &QPushButton::clicked, [=]() {
|
||||
m_document->undo();
|
||||
});
|
||||
|
||||
connect(addButton, &QPushButton::clicked, [=]() {
|
||||
m_document->setEditMode(SkeletonDocumentEditMode::Add);
|
||||
});
|
||||
|
@ -186,6 +190,9 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() :
|
|||
connect(graphicsWidget, &SkeletonGraphicsWidget::setEditMode, m_document, &SkeletonDocument::setEditMode);
|
||||
connect(graphicsWidget, &SkeletonGraphicsWidget::removeEdge, m_document, &SkeletonDocument::removeEdge);
|
||||
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::nodeRemoved, graphicsWidget, &SkeletonGraphicsWidget::nodeRemoved);
|
||||
|
|
|
@ -359,10 +359,12 @@ bool SkeletonGraphicsWidget::wheel(QWheelEvent *event)
|
|||
emit scaleNodeByAddRadius(nodeItem->id(), unifiedDelta);
|
||||
}
|
||||
}
|
||||
emit groupOperationAdded();
|
||||
return true;
|
||||
} else if (m_hoveredNodeItem) {
|
||||
float unifiedDelta = sceneRadiusToUnified(delta);
|
||||
emit scaleNodeByAddRadius(m_hoveredNodeItem->id(), unifiedDelta);
|
||||
emit groupOperationAdded();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -379,6 +381,7 @@ bool SkeletonGraphicsWidget::mouseRelease(QMouseEvent *event)
|
|||
}
|
||||
if (m_moveStarted) {
|
||||
m_moveStarted = false;
|
||||
emit groupOperationAdded();
|
||||
}
|
||||
if (m_rangeSelectionStarted) {
|
||||
m_selectionItem->hide();
|
||||
|
@ -428,6 +431,7 @@ bool SkeletonGraphicsWidget::mousePress(QMouseEvent *event)
|
|||
if (m_document->findEdgeByNodes(m_addFromNodeItem->id(), m_hoveredNodeItem->id()))
|
||||
return true;
|
||||
emit addEdge(m_addFromNodeItem->id(), m_hoveredNodeItem->id());
|
||||
emit groupOperationAdded();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -452,6 +456,7 @@ bool SkeletonGraphicsWidget::mousePress(QMouseEvent *event)
|
|||
m_lastAddedZ = unifiedSidePos.x();
|
||||
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 groupOperationAdded();
|
||||
return true;
|
||||
}
|
||||
} else if (SkeletonDocumentEditMode::Select == m_document->editMode) {
|
||||
|
@ -533,6 +538,7 @@ bool SkeletonGraphicsWidget::keyPress(QKeyEvent *event)
|
|||
}
|
||||
}
|
||||
if (processed) {
|
||||
emit groupOperationAdded();
|
||||
return true;
|
||||
}
|
||||
} else if (event->key() == Qt::Key_A) {
|
||||
|
@ -542,6 +548,20 @@ bool SkeletonGraphicsWidget::keyPress(QKeyEvent *event)
|
|||
emit setEditMode(SkeletonDocumentEditMode::Add);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -259,6 +259,9 @@ signals:
|
|||
void removeEdge(QUuid edgeId);
|
||||
void addEdge(QUuid fromNodeId, QUuid toNodeId);
|
||||
void cursorChanged();
|
||||
void groupOperationAdded();
|
||||
void undo();
|
||||
void redo();
|
||||
public:
|
||||
SkeletonGraphicsWidget(const SkeletonDocument *document);
|
||||
std::map<QUuid, std::pair<SkeletonGraphicsNodeItem *, SkeletonGraphicsNodeItem *>> nodeItemMap;
|
||||
|
|
Loading…
Reference in New Issue