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;