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.
master
Jeremy Hu 2018-06-29 14:55:32 +08:00
parent 057d615275
commit 51bf40de01
3 changed files with 91 additions and 3 deletions

View File

@ -1655,3 +1655,28 @@ const std::map<QString, AnimationClipContext> &SkeletonDocument::animationClipCo
return m_animationClipContexts; return m_animationClipContexts;
} }
void SkeletonDocument::findAllNeighbors(QUuid nodeId, std::set<QUuid> &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);
}
}

View File

@ -281,6 +281,7 @@ public:
bool allAnimationClipsReady() const; bool allAnimationClipsReady() const;
bool postProcessResultIsObsolete() const; bool postProcessResultIsObsolete() const;
const std::map<QString, AnimationClipContext> &animationClipContexts(); const std::map<QString, AnimationClipContext> &animationClipContexts();
void findAllNeighbors(QUuid nodeId, std::set<QUuid> &neighbors) const;
public slots: public slots:
void removeNode(QUuid nodeId); void removeNode(QUuid nodeId);
void removeEdge(QUuid edgeId); void removeEdge(QUuid edgeId);

View File

@ -750,6 +750,7 @@ bool SkeletonGraphicsWidget::mouseMove(QMouseEvent *event)
if (QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier)) { if (QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier)) {
std::set<SkeletonGraphicsNodeItem *> nodeItems; std::set<SkeletonGraphicsNodeItem *> nodeItems;
readMergedSkeletonNodeSetFromRangeSelection(&nodeItems); readMergedSkeletonNodeSetFromRangeSelection(&nodeItems);
bool ikMoved = false;
if (nodeItems.size() == 1) { if (nodeItems.size() == 1) {
auto &nodeItem = *nodeItems.begin(); auto &nodeItem = *nodeItems.begin();
const SkeletonNode *node = m_document->findNode(nodeItem->id()); const SkeletonNode *node = m_document->findNode(nodeItem->id());
@ -768,10 +769,11 @@ bool SkeletonGraphicsWidget::mouseMove(QMouseEvent *event)
target.setZ(target.z() + byX); target.setZ(target.z() + byX);
} }
emit ikMove(nodeItem->id(), target); emit ikMove(nodeItem->id(), target);
ikMoved = true;
} }
} else { }
if (!ikMoved)
rotateSelected(byX); rotateSelected(byX);
}
} else { } else {
moveSelected(byX, byY); moveSelected(byX, byY);
} }
@ -793,8 +795,68 @@ void SkeletonGraphicsWidget::rotateSelected(int degree)
std::set<SkeletonGraphicsNodeItem *> nodeItems; std::set<SkeletonGraphicsNodeItem *> nodeItems;
readMergedSkeletonNodeSetFromRangeSelection(&nodeItems); readMergedSkeletonNodeSetFromRangeSelection(&nodeItems);
QVector2D center = centerOfNodeItemSet(nodeItems); QVector2D center;
qNormalizeAngle(degree); 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 &centerNode = m_document->findNode(centerNodeItem->id());
if (nullptr == centerNode) {
qDebug() << "findNode:" << centerNodeItem->id() << " failed while rotate";
return;
}
std::vector<std::pair<QUuid, float>> 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<QUuid, float> &a, const std::pair<QUuid, float> &b) {
return a.second < b.second;
});
int skippedNum = 1;
if (directNeighborDists.size() == 1) {
skippedNum = 0;
}
std::set<QUuid> 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) { for (const auto &it: nodeItems) {
SkeletonGraphicsNodeItem *nodeItem = it; SkeletonGraphicsNodeItem *nodeItem = it;
QMatrix4x4 mat; QMatrix4x4 mat;