Add flip
parent
5e97e3d268
commit
62fdcbdced
|
@ -32,6 +32,7 @@ int main(int argc, char ** argv)
|
||||||
darkPalette.setColor(QPalette::ToolTipBase, Theme::white);
|
darkPalette.setColor(QPalette::ToolTipBase, Theme::white);
|
||||||
darkPalette.setColor(QPalette::ToolTipText, Theme::white);
|
darkPalette.setColor(QPalette::ToolTipText, Theme::white);
|
||||||
darkPalette.setColor(QPalette::Text, Theme::white);
|
darkPalette.setColor(QPalette::Text, Theme::white);
|
||||||
|
darkPalette.setColor(QPalette::Disabled, QPalette::Text, Theme::black);
|
||||||
darkPalette.setColor(QPalette::Button, QColor(53,53,53));
|
darkPalette.setColor(QPalette::Button, QColor(53,53,53));
|
||||||
darkPalette.setColor(QPalette::ButtonText, Theme::white);
|
darkPalette.setColor(QPalette::ButtonText, Theme::white);
|
||||||
darkPalette.setColor(QPalette::BrightText, Theme::red);
|
darkPalette.setColor(QPalette::BrightText, Theme::red);
|
||||||
|
|
|
@ -14,7 +14,8 @@ unsigned long SkeletonDocument::m_maxSnapshot = 1000;
|
||||||
SkeletonDocument::SkeletonDocument() :
|
SkeletonDocument::SkeletonDocument() :
|
||||||
m_resultMeshIsObsolete(false),
|
m_resultMeshIsObsolete(false),
|
||||||
m_resultMesh(nullptr),
|
m_resultMesh(nullptr),
|
||||||
m_meshGenerator(nullptr)
|
m_meshGenerator(nullptr),
|
||||||
|
m_batchChangeRefCount(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -754,9 +755,24 @@ void SkeletonDocument::meshReady()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::batchChangeBegin()
|
||||||
|
{
|
||||||
|
m_batchChangeRefCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::batchChangeEnd()
|
||||||
|
{
|
||||||
|
m_batchChangeRefCount--;
|
||||||
|
if (0 == m_batchChangeRefCount) {
|
||||||
|
if (m_resultMeshIsObsolete) {
|
||||||
|
generateMesh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SkeletonDocument::generateMesh()
|
void SkeletonDocument::generateMesh()
|
||||||
{
|
{
|
||||||
if (nullptr != m_meshGenerator) {
|
if (nullptr != m_meshGenerator || m_batchChangeRefCount > 0) {
|
||||||
m_resultMeshIsObsolete = true;
|
m_resultMeshIsObsolete = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -867,3 +883,15 @@ void SkeletonDocument::paste()
|
||||||
addFromSnapshot(snapshot);
|
addFromSnapshot(snapshot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SkeletonDocument::hasPastableContentInClipboard() const
|
||||||
|
{
|
||||||
|
const QClipboard *clipboard = QApplication::clipboard();
|
||||||
|
const QMimeData *mimeData = clipboard->mimeData();
|
||||||
|
if (mimeData->hasText()) {
|
||||||
|
if (-1 != mimeData->text().left(1000).indexOf("partIdList"))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -106,6 +106,13 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class SkeletonProfile
|
||||||
|
{
|
||||||
|
Unknown = 0,
|
||||||
|
Main,
|
||||||
|
Side
|
||||||
|
};
|
||||||
|
|
||||||
class SkeletonHistoryItem
|
class SkeletonHistoryItem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -121,13 +128,6 @@ enum class SkeletonDocumentEditMode
|
||||||
ZoomOut
|
ZoomOut
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class SkeletonProfile
|
|
||||||
{
|
|
||||||
Unknown = 0,
|
|
||||||
Main,
|
|
||||||
Side
|
|
||||||
};
|
|
||||||
|
|
||||||
class SkeletonDocument : public QObject
|
class SkeletonDocument : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -170,6 +170,7 @@ public:
|
||||||
Mesh *takeResultMesh();
|
Mesh *takeResultMesh();
|
||||||
QImage preview;
|
QImage preview;
|
||||||
void updateTurnaround(const QImage &image);
|
void updateTurnaround(const QImage &image);
|
||||||
|
bool hasPastableContentInClipboard() const;
|
||||||
public slots:
|
public slots:
|
||||||
void removeNode(QUuid nodeId);
|
void removeNode(QUuid nodeId);
|
||||||
void removeEdge(QUuid edgeId);
|
void removeEdge(QUuid edgeId);
|
||||||
|
@ -193,6 +194,8 @@ public slots:
|
||||||
void undo();
|
void undo();
|
||||||
void redo();
|
void redo();
|
||||||
void paste();
|
void paste();
|
||||||
|
void batchChangeBegin();
|
||||||
|
void batchChangeEnd();
|
||||||
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());
|
||||||
|
@ -205,6 +208,7 @@ private:
|
||||||
static unsigned long m_maxSnapshot;
|
static unsigned long m_maxSnapshot;
|
||||||
std::deque<SkeletonHistoryItem> m_undoItems;
|
std::deque<SkeletonHistoryItem> m_undoItems;
|
||||||
std::deque<SkeletonHistoryItem> m_redoItems;
|
std::deque<SkeletonHistoryItem> m_redoItems;
|
||||||
|
int m_batchChangeRefCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -195,6 +195,8 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() :
|
||||||
connect(graphicsWidget, &SkeletonGraphicsWidget::redo, m_document, &SkeletonDocument::redo);
|
connect(graphicsWidget, &SkeletonGraphicsWidget::redo, m_document, &SkeletonDocument::redo);
|
||||||
connect(graphicsWidget, &SkeletonGraphicsWidget::paste, m_document, &SkeletonDocument::paste);
|
connect(graphicsWidget, &SkeletonGraphicsWidget::paste, m_document, &SkeletonDocument::paste);
|
||||||
connect(graphicsWidget, &SkeletonGraphicsWidget::changeTurnaround, this, &SkeletonDocumentWindow::changeTurnaround);
|
connect(graphicsWidget, &SkeletonGraphicsWidget::changeTurnaround, this, &SkeletonDocumentWindow::changeTurnaround);
|
||||||
|
connect(graphicsWidget, &SkeletonGraphicsWidget::batchChangeBegin, m_document, &SkeletonDocument::batchChangeBegin);
|
||||||
|
connect(graphicsWidget, &SkeletonGraphicsWidget::batchChangeEnd, m_document, &SkeletonDocument::batchChangeEnd);
|
||||||
|
|
||||||
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);
|
||||||
|
|
|
@ -102,10 +102,23 @@ void SkeletonGraphicsWidget::showContextMenu(const QPoint &pos)
|
||||||
|
|
||||||
QAction pasteAction("Paste", this);
|
QAction pasteAction("Paste", this);
|
||||||
connect(&pasteAction, &QAction::triggered, m_document, &SkeletonDocument::paste);
|
connect(&pasteAction, &QAction::triggered, m_document, &SkeletonDocument::paste);
|
||||||
|
pasteAction.setEnabled(m_document->hasPastableContentInClipboard());
|
||||||
contextMenu.addAction(&pasteAction);
|
contextMenu.addAction(&pasteAction);
|
||||||
|
|
||||||
contextMenu.addSeparator();
|
contextMenu.addSeparator();
|
||||||
|
|
||||||
|
QAction flipHorizontallyAction("H Flip", this);
|
||||||
|
connect(&flipHorizontallyAction, &QAction::triggered, this, &SkeletonGraphicsWidget::flipHorizontally);
|
||||||
|
flipHorizontallyAction.setEnabled(!m_rangeSelectionSet.empty());
|
||||||
|
contextMenu.addAction(&flipHorizontallyAction);
|
||||||
|
|
||||||
|
QAction flipVerticallyAction("V Flip", this);
|
||||||
|
connect(&flipVerticallyAction, &QAction::triggered, this, &SkeletonGraphicsWidget::flipVertically);
|
||||||
|
flipVerticallyAction.setEnabled(!m_rangeSelectionSet.empty());
|
||||||
|
contextMenu.addAction(&flipVerticallyAction);
|
||||||
|
|
||||||
|
contextMenu.addSeparator();
|
||||||
|
|
||||||
QAction selectAllAction("Select All", this);
|
QAction selectAllAction("Select All", this);
|
||||||
connect(&selectAllAction, &QAction::triggered, this, &SkeletonGraphicsWidget::selectAll);
|
connect(&selectAllAction, &QAction::triggered, this, &SkeletonGraphicsWidget::selectAll);
|
||||||
selectAllAction.setEnabled(!nodeItemMap.empty());
|
selectAllAction.setEnabled(!nodeItemMap.empty());
|
||||||
|
@ -123,18 +136,6 @@ void SkeletonGraphicsWidget::showContextMenu(const QPoint &pos)
|
||||||
|
|
||||||
contextMenu.addSeparator();
|
contextMenu.addSeparator();
|
||||||
|
|
||||||
QAction addMirrorAction("Mirror", this);
|
|
||||||
connect(&addMirrorAction, &QAction::triggered, this, &SkeletonGraphicsWidget::addMirror);
|
|
||||||
addMirrorAction.setEnabled(readSkeletonNodeAndAnyEdgeOfNodeFromRangeSelection(nullptr, nullptr));
|
|
||||||
contextMenu.addAction(&addMirrorAction);
|
|
||||||
|
|
||||||
QAction deleteMirrorAction("Delete Mirror", this);
|
|
||||||
connect(&deleteMirrorAction, &QAction::triggered, this, &SkeletonGraphicsWidget::deleteMirror);
|
|
||||||
deleteMirrorAction.setEnabled(readSkeletonNodeAndAnyEdgeOfNodeFromRangeSelection(nullptr, nullptr));
|
|
||||||
contextMenu.addAction(&deleteMirrorAction);
|
|
||||||
|
|
||||||
contextMenu.addSeparator();
|
|
||||||
|
|
||||||
QAction changeTurnaroundAction("Change Turnaround..", this);
|
QAction changeTurnaroundAction("Change Turnaround..", this);
|
||||||
connect(&changeTurnaroundAction, &QAction::triggered, [=]() {
|
connect(&changeTurnaroundAction, &QAction::triggered, [=]() {
|
||||||
emit changeTurnaround();
|
emit changeTurnaround();
|
||||||
|
@ -425,11 +426,7 @@ bool SkeletonGraphicsWidget::wheel(QWheelEvent *event)
|
||||||
readMergedSkeletonNodeSetFromRangeSelection(&nodeItems);
|
readMergedSkeletonNodeSetFromRangeSelection(&nodeItems);
|
||||||
float unifiedDelta = sceneRadiusToUnified(delta);
|
float unifiedDelta = sceneRadiusToUnified(delta);
|
||||||
if (QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ControlModifier)) {
|
if (QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ControlModifier)) {
|
||||||
QVector2D center;
|
QVector2D center = centerOfNodeItemSet(nodeItems);
|
||||||
for (const auto &nodeItem: nodeItems) {
|
|
||||||
center += QVector2D(nodeItem->origin());
|
|
||||||
}
|
|
||||||
center /= nodeItems.size();
|
|
||||||
for (const auto &nodeItem: nodeItems) {
|
for (const auto &nodeItem: nodeItems) {
|
||||||
QVector2D origin = QVector2D(nodeItem->origin());
|
QVector2D origin = QVector2D(nodeItem->origin());
|
||||||
QVector2D ray = (center - origin) * 0.01 * delta;
|
QVector2D ray = (center - origin) * 0.01 * delta;
|
||||||
|
@ -459,6 +456,50 @@ bool SkeletonGraphicsWidget::wheel(QWheelEvent *event)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVector2D SkeletonGraphicsWidget::centerOfNodeItemSet(const std::set<SkeletonGraphicsNodeItem *> &set)
|
||||||
|
{
|
||||||
|
QVector2D center;
|
||||||
|
for (const auto &nodeItem: set) {
|
||||||
|
center += QVector2D(nodeItem->origin());
|
||||||
|
}
|
||||||
|
center /= set.size();
|
||||||
|
return center;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonGraphicsWidget::flipHorizontally()
|
||||||
|
{
|
||||||
|
std::set<SkeletonGraphicsNodeItem *> nodeItems;
|
||||||
|
readMergedSkeletonNodeSetFromRangeSelection(&nodeItems);
|
||||||
|
if (nodeItems.empty())
|
||||||
|
return;
|
||||||
|
QVector2D center = centerOfNodeItemSet(nodeItems);
|
||||||
|
for (const auto &nodeItem: nodeItems) {
|
||||||
|
QPointF origin = nodeItem->origin();
|
||||||
|
float offset = origin.x() - center.x();
|
||||||
|
float unifiedOffset = -sceneRadiusToUnified(offset * 2);
|
||||||
|
if (SkeletonProfile::Main == nodeItem->profile()) {
|
||||||
|
emit moveNodeBy(nodeItem->id(), unifiedOffset, 0, 0);
|
||||||
|
} else {
|
||||||
|
emit moveNodeBy(nodeItem->id(), 0, 0, unifiedOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonGraphicsWidget::flipVertically()
|
||||||
|
{
|
||||||
|
std::set<SkeletonGraphicsNodeItem *> nodeItems;
|
||||||
|
readMergedSkeletonNodeSetFromRangeSelection(&nodeItems);
|
||||||
|
if (nodeItems.empty())
|
||||||
|
return;
|
||||||
|
QVector2D center = centerOfNodeItemSet(nodeItems);
|
||||||
|
for (const auto &nodeItem: nodeItems) {
|
||||||
|
QPointF origin = nodeItem->origin();
|
||||||
|
float offset = origin.y() - center.y();
|
||||||
|
float unifiedOffset = -sceneRadiusToUnified(offset * 2);
|
||||||
|
emit moveNodeBy(nodeItem->id(), 0, unifiedOffset, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool SkeletonGraphicsWidget::mouseRelease(QMouseEvent *event)
|
bool SkeletonGraphicsWidget::mouseRelease(QMouseEvent *event)
|
||||||
{
|
{
|
||||||
if (event->button() == Qt::LeftButton) {
|
if (event->button() == Qt::LeftButton) {
|
||||||
|
@ -619,6 +660,7 @@ bool SkeletonGraphicsWidget::mouseDoubleClick(QMouseEvent *event)
|
||||||
void SkeletonGraphicsWidget::deleteSelected()
|
void SkeletonGraphicsWidget::deleteSelected()
|
||||||
{
|
{
|
||||||
if (!m_rangeSelectionSet.empty()) {
|
if (!m_rangeSelectionSet.empty()) {
|
||||||
|
emit batchChangeBegin();
|
||||||
std::set<QUuid> nodeIdSet;
|
std::set<QUuid> nodeIdSet;
|
||||||
std::set<QUuid> edgeIdSet;
|
std::set<QUuid> edgeIdSet;
|
||||||
readSkeletonNodeAndEdgeIdSetFromRangeSelection(&nodeIdSet, &edgeIdSet);
|
readSkeletonNodeAndEdgeIdSetFromRangeSelection(&nodeIdSet, &edgeIdSet);
|
||||||
|
@ -628,6 +670,7 @@ void SkeletonGraphicsWidget::deleteSelected()
|
||||||
for (const auto &id: nodeIdSet) {
|
for (const auto &id: nodeIdSet) {
|
||||||
emit removeNode(id);
|
emit removeNode(id);
|
||||||
}
|
}
|
||||||
|
emit batchChangeEnd();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1094,3 +1137,6 @@ void SkeletonGraphicsWidget::copy()
|
||||||
QClipboard *clipboard = QApplication::clipboard();
|
QClipboard *clipboard = QApplication::clipboard();
|
||||||
clipboard->setText(snapshotXml);
|
clipboard->setText(snapshotXml);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -264,8 +264,8 @@ signals:
|
||||||
void redo();
|
void redo();
|
||||||
void paste();
|
void paste();
|
||||||
void changeTurnaround();
|
void changeTurnaround();
|
||||||
void addMirror();
|
void batchChangeBegin();
|
||||||
void deleteMirror();
|
void batchChangeEnd();
|
||||||
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;
|
||||||
|
@ -308,6 +308,8 @@ public slots:
|
||||||
void selectPartAll();
|
void selectPartAll();
|
||||||
void cut();
|
void cut();
|
||||||
void copy();
|
void copy();
|
||||||
|
void flipHorizontally();
|
||||||
|
void flipVertically();
|
||||||
private slots:
|
private slots:
|
||||||
void turnaroundImageReady();
|
void turnaroundImageReady();
|
||||||
private:
|
private:
|
||||||
|
@ -321,6 +323,7 @@ private:
|
||||||
void checkRangeSelection();
|
void checkRangeSelection();
|
||||||
void clearRangeSelection();
|
void clearRangeSelection();
|
||||||
void removeItem(QGraphicsItem *item);
|
void removeItem(QGraphicsItem *item);
|
||||||
|
QVector2D centerOfNodeItemSet(const std::set<SkeletonGraphicsNodeItem *> &set);
|
||||||
private:
|
private:
|
||||||
QGraphicsPixmapItem *m_backgroundItem;
|
QGraphicsPixmapItem *m_backgroundItem;
|
||||||
const SkeletonDocument *m_document;
|
const SkeletonDocument *m_document;
|
||||||
|
|
|
@ -13,6 +13,8 @@ public:
|
||||||
std::map<QString, std::map<QString, QString>> nodes;
|
std::map<QString, std::map<QString, QString>> nodes;
|
||||||
std::map<QString, std::map<QString, QString>> edges;
|
std::map<QString, std::map<QString, QString>> edges;
|
||||||
std::map<QString, std::map<QString, QString>> parts;
|
std::map<QString, std::map<QString, QString>> parts;
|
||||||
|
std::map<QString, std::map<QString, QString>> mirrors;
|
||||||
|
std::map<QString, std::map<QString, QString>> trivialMarkers;
|
||||||
std::vector<QString> partIdList;
|
std::vector<QString> partIdList;
|
||||||
public:
|
public:
|
||||||
SkeletonSnapshot();
|
SkeletonSnapshot();
|
||||||
|
|
Loading…
Reference in New Issue