From 51bf40de012ef3c747b37566ac9659efd46b1ded Mon Sep 17 00:00:00 2001 From: Jeremy Hu Date: Fri, 29 Jun 2018 14:55:32 +0800 Subject: [PATCH] Add rotate along single node. If multiple nodes or end effector node been selected and rotate, the behaves remain the same as before, however, if single node in the middle been selected and rotated, all the children of this node will be rotated. This could be useful to rotate the tail or leg from joint. --- src/skeletondocument.cpp | 25 +++++++++++++ src/skeletondocument.h | 1 + src/skeletongraphicswidget.cpp | 68 ++++++++++++++++++++++++++++++++-- 3 files changed, 91 insertions(+), 3 deletions(-) diff --git a/src/skeletondocument.cpp b/src/skeletondocument.cpp index 69538c51..b4fba1ee 100644 --- a/src/skeletondocument.cpp +++ b/src/skeletondocument.cpp @@ -1655,3 +1655,28 @@ const std::map &SkeletonDocument::animationClipCo return m_animationClipContexts; } +void SkeletonDocument::findAllNeighbors(QUuid nodeId, std::set &neighbors) const +{ + const auto &node = findNode(nodeId); + if (nullptr == node) { + qDebug() << "findNode:" << nodeId << "failed"; + return; + } + for (const auto &edgeId: node->edgeIds) { + const auto &edge = findEdge(edgeId); + if (nullptr == edge) { + qDebug() << "findEdge:" << edgeId << "failed"; + continue; + } + const auto &neighborNodeId = edge->neighborOf(nodeId); + if (neighborNodeId.isNull()) { + qDebug() << "neighborOf:" << nodeId << "is null from edge:" << edgeId; + continue; + } + if (neighbors.find(neighborNodeId) != neighbors.end()) { + continue; + } + neighbors.insert(neighborNodeId); + findAllNeighbors(neighborNodeId, neighbors); + } +} diff --git a/src/skeletondocument.h b/src/skeletondocument.h index bdd4c3d4..1bc78850 100644 --- a/src/skeletondocument.h +++ b/src/skeletondocument.h @@ -281,6 +281,7 @@ public: bool allAnimationClipsReady() const; bool postProcessResultIsObsolete() const; const std::map &animationClipContexts(); + void findAllNeighbors(QUuid nodeId, std::set &neighbors) const; public slots: void removeNode(QUuid nodeId); void removeEdge(QUuid edgeId); diff --git a/src/skeletongraphicswidget.cpp b/src/skeletongraphicswidget.cpp index 3f27d105..3a8b4bef 100644 --- a/src/skeletongraphicswidget.cpp +++ b/src/skeletongraphicswidget.cpp @@ -750,6 +750,7 @@ bool SkeletonGraphicsWidget::mouseMove(QMouseEvent *event) if (QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier)) { std::set nodeItems; readMergedSkeletonNodeSetFromRangeSelection(&nodeItems); + bool ikMoved = false; if (nodeItems.size() == 1) { auto &nodeItem = *nodeItems.begin(); const SkeletonNode *node = m_document->findNode(nodeItem->id()); @@ -768,10 +769,11 @@ bool SkeletonGraphicsWidget::mouseMove(QMouseEvent *event) target.setZ(target.z() + byX); } emit ikMove(nodeItem->id(), target); + ikMoved = true; } - } else { - rotateSelected(byX); } + if (!ikMoved) + rotateSelected(byX); } else { moveSelected(byX, byY); } @@ -793,8 +795,68 @@ void SkeletonGraphicsWidget::rotateSelected(int degree) std::set nodeItems; readMergedSkeletonNodeSetFromRangeSelection(&nodeItems); - QVector2D center = centerOfNodeItemSet(nodeItems); + QVector2D center; qNormalizeAngle(degree); + + if (1 == nodeItems.size() && m_document->originSettled()) { + // Rotate all children nodes around this node + // 1. Pick who is the parent from neighbors + // 2. Rotate all the remaining neighbor nodes and their children exlucde the picked parent + QVector3D worldOrigin = QVector3D(m_document->originX, m_document->originY, m_document->originZ); + SkeletonGraphicsNodeItem *centerNodeItem = *nodeItems.begin(); + const auto ¢erNode = m_document->findNode(centerNodeItem->id()); + if (nullptr == centerNode) { + qDebug() << "findNode:" << centerNodeItem->id() << " failed while rotate"; + return; + } + std::vector> directNeighborDists; + for (const auto &neighborId: centerNode->edgeIds) { + const auto &neighborEdge = m_document->findEdge(neighborId); + if (nullptr == neighborEdge) { + qDebug() << "findEdge:" << neighborId << " failed while rotate"; + continue; + } + const auto &neighborNodeId = neighborEdge->neighborOf(centerNodeItem->id()); + const auto &neighborNode = m_document->findNode(neighborNodeId); + if (nullptr == centerNode) { + qDebug() << "findNode:" << neighborNodeId << " failed while rotate"; + continue; + } + QVector3D nodeOrigin(neighborNode->x, neighborNode->y, neighborNode->z); + directNeighborDists.push_back(std::make_pair(neighborNodeId, (nodeOrigin - worldOrigin).lengthSquared())); + } + std::sort(directNeighborDists.begin(), directNeighborDists.end(), [](const std::pair &a, const std::pair &b) { + return a.second < b.second; + }); + int skippedNum = 1; + if (directNeighborDists.size() == 1) { + skippedNum = 0; + } + std::set neighborIds; + // Adding self to force the neighbor finding not reverse + neighborIds.insert(centerNodeItem->id()); + for (int i = skippedNum; i < (int)directNeighborDists.size(); i++) { + neighborIds.insert(directNeighborDists[i].first); + m_document->findAllNeighbors(directNeighborDists[i].first, neighborIds); + } + center = QVector2D(centerNodeItem->origin().x(), centerNodeItem->origin().y()); + nodeItems.clear(); + for (const auto &neighborId: neighborIds) { + const auto &findItemResult = nodeItemMap.find(neighborId); + if (findItemResult == nodeItemMap.end()) { + qDebug() << "find node item:" << neighborId << "failed"; + continue; + } + if (SkeletonProfile::Main == centerNodeItem->profile()) { + nodeItems.insert(findItemResult->second.first); + } else { + nodeItems.insert(findItemResult->second.second); + } + } + } else { + center = centerOfNodeItemSet(nodeItems); + } + for (const auto &it: nodeItems) { SkeletonGraphicsNodeItem *nodeItem = it; QMatrix4x4 mat;