diff --git a/src/posedocument.cpp b/src/posedocument.cpp index 76ab9a8a..1dca089d 100644 --- a/src/posedocument.cpp +++ b/src/posedocument.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "posedocument.h" #include "rigger.h" #include "util.h" @@ -516,3 +517,80 @@ float PoseDocument::toOutcomeZ(float z) return (1.0 - z) / m_outcomeScaleFactor; } +QString PoseDocument::findBoneNameByNodeId(const QUuid &nodeId) +{ + for (const auto &item: m_boneNameToIdsMap) { + if (nodeId == item.second.first || nodeId == item.second.second) + return item.first; + } + return QString(); +} + +void PoseDocument::switchChainSide(const std::set nodeIds) +{ + QRegularExpression reJoints("^(Left|Right)([a-zA-Z]+\\d*)_(Joint\\d+)$"); + + std::set baseNames; + for (const auto &nodeId: nodeIds) { + QString boneName = findBoneNameByNodeId(nodeId); + if (boneName.isEmpty()) { + //qDebug() << "Find bone name for node failed:" << nodeId; + continue; + } + + QRegularExpressionMatch match = reJoints.match(boneName); + if (!match.hasMatch()) { + //qDebug() << "Match bone name for side failed:" << boneName; + continue; + } + + QString baseName = match.captured(2); + baseNames.insert(baseName); + } + + auto switchYZ = [=](const QUuid &first, const QUuid &second) { + auto findFirstNode = nodeMap.find(first); + if (findFirstNode == nodeMap.end()) + return; + auto findSecondNode = nodeMap.find(second); + if (findSecondNode == nodeMap.end()) + return; + std::swap(findFirstNode->second.y, findSecondNode->second.y); + std::swap(findFirstNode->second.z, findSecondNode->second.z); + emit nodeOriginChanged(first); + emit nodeOriginChanged(second); + }; + + std::set> switchPairs; + for (const auto &baseName: baseNames) { + for (const auto &item: m_boneNameToIdsMap) { + QRegularExpressionMatch match = reJoints.match(item.first); + if (!match.hasMatch()) + continue; + QString itemSide = match.captured(1); + QString itemBaseName = match.captured(2); + QString itemJointName = match.captured(3); + //qDebug() << "itemSide:" << itemSide << "itemBaseName:" << itemBaseName << "itemJointName:" << itemJointName; + if (itemBaseName == baseName && "Left" == itemSide) { + QString otherSide = "Right"; + QString pairedName = otherSide + itemBaseName + "_" + itemJointName; + const auto findPaired = m_boneNameToIdsMap.find(pairedName); + if (findPaired == m_boneNameToIdsMap.end()) { + qDebug() << "Couldn't find paired name:" << pairedName; + continue; + } + //qDebug() << "Switched:" << pairedName; + switchPairs.insert({item.second.first, findPaired->second.first}); + switchPairs.insert({item.second.second, findPaired->second.second}); + } + } + } + + for (const auto &pair: switchPairs) { + switchYZ(pair.first, pair.second); + } + + //qDebug() << "switchedPairNum:" << switchPairs.size(); + if (!switchPairs.empty()) + emit parametersChanged(); +} diff --git a/src/posedocument.h b/src/posedocument.h index cb2e03bd..5e4ad536 100644 --- a/src/posedocument.h +++ b/src/posedocument.h @@ -49,6 +49,7 @@ public slots: void moveNodeBy(QUuid nodeId, float x, float y, float z); void setNodeOrigin(QUuid nodeId, float x, float y, float z); float findGroundY() const; + void switchChainSide(const std::set nodeIds); public: static const float m_nodeRadius; @@ -57,6 +58,8 @@ public: static const float m_outcomeScaleFactor; private: + QString findBoneNameByNodeId(const QUuid &nodeId); + std::map> m_boneNameToIdsMap; QUuid m_groundPartId; QUuid m_bonesPartId; diff --git a/src/poseeditwidget.cpp b/src/poseeditwidget.cpp index d4828e18..fc3afee2 100644 --- a/src/poseeditwidget.cpp +++ b/src/poseeditwidget.cpp @@ -70,6 +70,7 @@ PoseEditWidget::PoseEditWidget(const Document *document, QWidget *parent) : connect(graphicsWidget, &SkeletonGraphicsWidget::groupOperationAdded, m_poseDocument, &PoseDocument::saveHistoryItem); connect(graphicsWidget, &SkeletonGraphicsWidget::undo, m_poseDocument, &PoseDocument::undo); connect(graphicsWidget, &SkeletonGraphicsWidget::redo, m_poseDocument, &PoseDocument::redo); + connect(graphicsWidget, &SkeletonGraphicsWidget::switchChainSide, m_poseDocument, &PoseDocument::switchChainSide); connect(m_poseDocument, &PoseDocument::cleanup, graphicsWidget, &SkeletonGraphicsWidget::removeAllContent); diff --git a/src/skeletongraphicswidget.cpp b/src/skeletongraphicswidget.cpp index 37cc1055..78201d31 100644 --- a/src/skeletongraphicswidget.cpp +++ b/src/skeletongraphicswidget.cpp @@ -204,6 +204,12 @@ void SkeletonGraphicsWidget::showContextMenu(const QPoint &pos) contextMenu.addAction(&switchXzAction); } + QAction switchChainSideAction(tr("Switch Chain Side"), this); + if (m_nodePositionModifyOnly && hasNodeSelection()) { + connect(&switchChainSideAction, &QAction::triggered, this, &SkeletonGraphicsWidget::switchSelectedChainSide); + contextMenu.addAction(&switchChainSideAction); + } + QAction alignToLocalCenterAction(tr("Local Center"), this); QAction alignToLocalVerticalCenterAction(tr("Local Vertical Center"), this); QAction alignToLocalHorizontalCenterAction(tr("Local Horizontal Center"), this); @@ -977,6 +983,20 @@ void SkeletonGraphicsWidget::switchSelectedXZ() emit groupOperationAdded(); } +void SkeletonGraphicsWidget::switchSelectedChainSide() +{ + if (m_rangeSelectionSet.empty()) + return; + + std::set nodeIdSet; + readSkeletonNodeAndEdgeIdSetFromRangeSelection(&nodeIdSet); + if (nodeIdSet.empty()) + return; + + emit switchChainSide(nodeIdSet); + emit groupOperationAdded(); +} + void SkeletonGraphicsWidget::zoomSelected(float delta) { if (m_rangeSelectionSet.empty()) diff --git a/src/skeletongraphicswidget.h b/src/skeletongraphicswidget.h index 1d2c593d..38653eba 100644 --- a/src/skeletongraphicswidget.h +++ b/src/skeletongraphicswidget.h @@ -382,6 +382,7 @@ signals: void setNodeBoneMark(QUuid nodeId, BoneMark mark); void zoomRenderedModelBy(float delta); void switchNodeXZ(QUuid nodeId); + void switchChainSide(std::set nodeIds); void enableAllPositionRelatedLocks(); void disableAllPositionRelatedLocks(); public: @@ -470,6 +471,7 @@ public slots: void setSelectedNodesBoneMark(BoneMark boneMark); void timeToRemoveDeferredNodesAndEdges(); void switchSelectedXZ(); + void switchSelectedChainSide(); void shortcutDelete(); void shortcutAddMode(); void shortcutUndo();