#include #include "skeleton_document.h" const SkeletonNode *SkeletonDocument::findNode(dust3d::Uuid nodeId) const { auto it = nodeMap.find(nodeId); if (it == nodeMap.end()) return nullptr; return &it->second; } const SkeletonEdge *SkeletonDocument::findEdge(dust3d::Uuid edgeId) const { auto it = edgeMap.find(edgeId); if (it == edgeMap.end()) return nullptr; return &it->second; } const SkeletonPart *SkeletonDocument::findPart(dust3d::Uuid partId) const { auto it = partMap.find(partId); if (it == partMap.end()) return nullptr; return &it->second; } const SkeletonEdge *SkeletonDocument::findEdgeByNodes(dust3d::Uuid firstNodeId, dust3d::Uuid secondNodeId) const { const SkeletonNode *firstNode = nullptr; firstNode = findNode(firstNodeId); if (nullptr == firstNode) { return nullptr; } for (auto edgeIdIt = firstNode->edgeIds.begin(); edgeIdIt != firstNode->edgeIds.end(); edgeIdIt++) { auto edgeIt = edgeMap.find(*edgeIdIt); if (edgeIt == edgeMap.end()) { continue; } if (std::find(edgeIt->second.nodeIds.begin(), edgeIt->second.nodeIds.end(), secondNodeId) != edgeIt->second.nodeIds.end()) return &edgeIt->second; } return nullptr; } void SkeletonDocument::findAllNeighbors(dust3d::Uuid nodeId, std::set &neighbors) const { const auto &node = findNode(nodeId); if (nullptr == node) { return; } for (const auto &edgeId: node->edgeIds) { const auto &edge = findEdge(edgeId); if (nullptr == edge) { continue; } const auto &neighborNodeId = edge->neighborOf(nodeId); if (neighborNodeId.isNull()) { continue; } if (neighbors.find(neighborNodeId) != neighbors.end()) { continue; } neighbors.insert(neighborNodeId); findAllNeighbors(neighborNodeId, neighbors); } } bool SkeletonDocument::isNodeConnectable(dust3d::Uuid nodeId) const { const auto &node = findNode(nodeId); if (nullptr == node) return false; if (node->edgeIds.size() < 2) return true; return false; } void SkeletonDocument::reduceNode(dust3d::Uuid nodeId) { const SkeletonNode *node = findNode(nodeId); if (nullptr == node) { return; } if (node->edgeIds.size() != 2) { return; } dust3d::Uuid firstEdgeId = node->edgeIds[0]; dust3d::Uuid secondEdgeId = node->edgeIds[1]; const SkeletonEdge *firstEdge = findEdge(firstEdgeId); if (nullptr == firstEdge) { return; } const SkeletonEdge *secondEdge = findEdge(secondEdgeId); if (nullptr == secondEdge) { return; } dust3d::Uuid firstNeighborNodeId = firstEdge->neighborOf(nodeId); dust3d::Uuid secondNeighborNodeId = secondEdge->neighborOf(nodeId); removeNode(nodeId); addEdge(firstNeighborNodeId, secondNeighborNodeId); } void SkeletonDocument::breakEdge(dust3d::Uuid edgeId) { const SkeletonEdge *edge = findEdge(edgeId); if (nullptr == edge) { return; } if (edge->nodeIds.size() != 2) { return; } dust3d::Uuid firstNodeId = edge->nodeIds[0]; dust3d::Uuid secondNodeId = edge->nodeIds[1]; const SkeletonNode *firstNode = findNode(firstNodeId); if (nullptr == firstNode) { return; } const SkeletonNode *secondNode = findNode(secondNodeId); if (nullptr == secondNode) { return; } QVector3D firstOrigin(firstNode->getX(), firstNode->getY(), firstNode->getZ()); QVector3D secondOrigin(secondNode->getX(), secondNode->getY(), secondNode->getZ()); QVector3D middleOrigin = (firstOrigin + secondOrigin) / 2; float middleRadius = (firstNode->radius + secondNode->radius) / 2; removeEdge(edgeId); dust3d::Uuid middleNodeId = createNode(dust3d::Uuid::createUuid(), middleOrigin.x(), middleOrigin.y(), middleOrigin.z(), middleRadius, firstNodeId); if (middleNodeId.isNull()) { return; } addEdge(middleNodeId, secondNodeId); } void SkeletonDocument::reverseEdge(dust3d::Uuid edgeId) { SkeletonEdge *edge = (SkeletonEdge *)findEdge(edgeId); if (nullptr == edge) { return; } if (edge->nodeIds.size() != 2) { return; } std::swap(edge->nodeIds[0], edge->nodeIds[1]); auto part = partMap.find(edge->partId); if (part != partMap.end()) part->second.dirty = true; emit edgeReversed(edgeId); emit skeletonChanged(); } void SkeletonDocument::removeEdge(dust3d::Uuid edgeId) { const SkeletonEdge *edge = findEdge(edgeId); if (nullptr == edge) { return; } if (isPartReadonly(edge->partId)) return; const SkeletonPart *oldPart = findPart(edge->partId); if (nullptr == oldPart) { return; } QString nextPartName = oldPart->name; dust3d::Uuid oldPartId = oldPart->id; std::vector> groups; splitPartByEdge(&groups, edgeId); std::vector> newPartNodeNumMap; std::vector newPartIds; for (auto groupIt = groups.begin(); groupIt != groups.end(); groupIt++) { const auto newUuid = dust3d::Uuid::createUuid(); SkeletonPart &part = partMap[newUuid]; part.id = newUuid; part.copyAttributes(*oldPart); part.name = nextPartName; for (auto nodeIdIt = (*groupIt).begin(); nodeIdIt != (*groupIt).end(); nodeIdIt++) { auto nodeIt = nodeMap.find(*nodeIdIt); if (nodeIt == nodeMap.end()) { continue; } nodeIt->second.partId = part.id; part.nodeIds.push_back(nodeIt->first); for (auto edgeIdIt = nodeIt->second.edgeIds.begin(); edgeIdIt != nodeIt->second.edgeIds.end(); edgeIdIt++) { auto edgeIt = edgeMap.find(*edgeIdIt); if (edgeIt == edgeMap.end()) { continue; } edgeIt->second.partId = part.id; } } addPartToComponent(part.id, findComponentParentId(part.componentId)); newPartNodeNumMap.push_back({part.id, part.nodeIds.size()}); newPartIds.push_back(part.id); emit partAdded(part.id); } for (auto nodeIdIt = edge->nodeIds.begin(); nodeIdIt != edge->nodeIds.end(); nodeIdIt++) { auto nodeIt = nodeMap.find(*nodeIdIt); if (nodeIt == nodeMap.end()) { continue; } nodeIt->second.edgeIds.erase(std::remove(nodeIt->second.edgeIds.begin(), nodeIt->second.edgeIds.end(), edgeId), nodeIt->second.edgeIds.end()); emit nodeOriginChanged(nodeIt->first); } edgeMap.erase(edgeId); emit edgeRemoved(edgeId); removePart(oldPartId); if (!newPartNodeNumMap.empty()) { std::sort(newPartNodeNumMap.begin(), newPartNodeNumMap.end(), [&]( const std::pair &first, const std::pair &second) { return first.second > second.second; }); updateLinkedPart(oldPartId, newPartNodeNumMap[0].first); } emit skeletonChanged(); } void SkeletonDocument::removeNode(dust3d::Uuid nodeId) { const SkeletonNode *node = findNode(nodeId); if (nullptr == node) { return; } if (isPartReadonly(node->partId)) return; const SkeletonPart *oldPart = findPart(node->partId); if (nullptr == oldPart) { return; } QString nextPartName = oldPart->name; dust3d::Uuid oldPartId = oldPart->id; std::vector> groups; splitPartByNode(&groups, nodeId); std::vector> newPartNodeNumMap; std::vector newPartIds; for (auto groupIt = groups.begin(); groupIt != groups.end(); groupIt++) { const auto newUuid = dust3d::Uuid::createUuid(); SkeletonPart &part = partMap[newUuid]; part.id = newUuid; part.copyAttributes(*oldPart); part.name = nextPartName; for (auto nodeIdIt = (*groupIt).begin(); nodeIdIt != (*groupIt).end(); nodeIdIt++) { auto nodeIt = nodeMap.find(*nodeIdIt); if (nodeIt == nodeMap.end()) { continue; } nodeIt->second.partId = part.id; part.nodeIds.push_back(nodeIt->first); for (auto edgeIdIt = nodeIt->second.edgeIds.begin(); edgeIdIt != nodeIt->second.edgeIds.end(); edgeIdIt++) { auto edgeIt = edgeMap.find(*edgeIdIt); if (edgeIt == edgeMap.end()) { continue; } edgeIt->second.partId = part.id; } } addPartToComponent(part.id, findComponentParentId(part.componentId)); newPartNodeNumMap.push_back({part.id, part.nodeIds.size()}); newPartIds.push_back(part.id); emit partAdded(part.id); } for (auto edgeIdIt = node->edgeIds.begin(); edgeIdIt != node->edgeIds.end(); edgeIdIt++) { auto edgeIt = edgeMap.find(*edgeIdIt); if (edgeIt == edgeMap.end()) { continue; } dust3d::Uuid neighborId = edgeIt->second.neighborOf(nodeId); auto nodeIt = nodeMap.find(neighborId); if (nodeIt == nodeMap.end()) { continue; } nodeIt->second.edgeIds.erase(std::remove(nodeIt->second.edgeIds.begin(), nodeIt->second.edgeIds.end(), *edgeIdIt), nodeIt->second.edgeIds.end()); edgeMap.erase(*edgeIdIt); emit edgeRemoved(*edgeIdIt); } nodeMap.erase(nodeId); emit nodeRemoved(nodeId); removePart(oldPartId); if (!newPartNodeNumMap.empty()) { std::sort(newPartNodeNumMap.begin(), newPartNodeNumMap.end(), [&]( const std::pair &first, const std::pair &second) { return first.second > second.second; }); updateLinkedPart(oldPartId, newPartNodeNumMap[0].first); } emit skeletonChanged(); } void SkeletonDocument::addNode(float x, float y, float z, float radius, dust3d::Uuid fromNodeId) { createNode(dust3d::Uuid::createUuid(), x, y, z, radius, fromNodeId); } void SkeletonDocument::addNodeWithId(dust3d::Uuid nodeId, float x, float y, float z, float radius, dust3d::Uuid fromNodeId) { createNode(nodeId, x, y, z, radius, fromNodeId); } dust3d::Uuid SkeletonDocument::createNode(dust3d::Uuid nodeId, float x, float y, float z, float radius, dust3d::Uuid fromNodeId) { dust3d::Uuid partId; const SkeletonNode *fromNode = nullptr; bool newPartAdded = false; if (fromNodeId.isNull()) { const auto newUuid = dust3d::Uuid::createUuid(); SkeletonPart &part = partMap[newUuid]; part.id = newUuid; partId = part.id; emit partAdded(partId); newPartAdded = true; } else { fromNode = findNode(fromNodeId); if (nullptr == fromNode) { return dust3d::Uuid(); } partId = fromNode->partId; if (isPartReadonly(partId)) return dust3d::Uuid(); auto part = partMap.find(partId); if (part != partMap.end()) part->second.dirty = true; } SkeletonNode node(nodeId); node.partId = partId; node.setRadius(radius); node.setX(x); node.setY(y); node.setZ(z); nodeMap[node.id] = node; partMap[partId].nodeIds.push_back(node.id); emit nodeAdded(node.id); if (nullptr != fromNode) { SkeletonEdge edge; edge.partId = partId; edge.nodeIds.push_back(fromNode->id); edge.nodeIds.push_back(node.id); edgeMap[edge.id] = edge; nodeMap[node.id].edgeIds.push_back(edge.id); nodeMap[fromNode->id].edgeIds.push_back(edge.id); emit edgeAdded(edge.id); } if (newPartAdded) addPartToComponent(partId, m_currentCanvasComponentId); emit skeletonChanged(); return node.id; } void SkeletonDocument::joinNodeAndNeiborsToGroup(std::vector *group, dust3d::Uuid nodeId, std::set *visitMap, dust3d::Uuid noUseEdgeId) { if (nodeId.isNull() || visitMap->find(nodeId) != visitMap->end()) return; const SkeletonNode *node = findNode(nodeId); if (nullptr == node) { return; } visitMap->insert(nodeId); group->push_back(nodeId); for (auto edgeIt = node->edgeIds.begin(); edgeIt != node->edgeIds.end(); edgeIt++) { if (noUseEdgeId == *edgeIt) continue; const SkeletonEdge *edge = findEdge(*edgeIt); if (nullptr == edge) { continue; } for (auto nodeIt = edge->nodeIds.begin(); nodeIt != edge->nodeIds.end(); nodeIt++) { joinNodeAndNeiborsToGroup(group, *nodeIt, visitMap, noUseEdgeId); } } } void SkeletonDocument::splitPartByNode(std::vector> *groups, dust3d::Uuid nodeId) { const SkeletonNode *node = findNode(nodeId); std::set visitMap; visitMap.insert(nodeId); for (auto edgeIt = node->edgeIds.begin(); edgeIt != node->edgeIds.end(); edgeIt++) { std::vector group; const SkeletonEdge *edge = findEdge(*edgeIt); if (nullptr == edge) { continue; } joinNodeAndNeiborsToGroup(&group, edge->neighborOf(nodeId), &visitMap, *edgeIt); if (!group.empty()) groups->push_back(group); } } void SkeletonDocument::splitPartByEdge(std::vector> *groups, dust3d::Uuid edgeId) { const SkeletonEdge *edge = findEdge(edgeId); if (nullptr == edge) { return; } std::set visitMap; for (auto nodeIt = edge->nodeIds.begin(); nodeIt != edge->nodeIds.end(); nodeIt++) { std::vector group; joinNodeAndNeiborsToGroup(&group, *nodeIt, &visitMap, edgeId); if (!group.empty()) groups->push_back(group); } } const SkeletonComponent *SkeletonDocument::findComponentParent(dust3d::Uuid componentId) const { auto component = componentMap.find(componentId); if (component == componentMap.end()) { return nullptr; } if (component->second.parentId.isNull()) return &rootComponent; return (SkeletonComponent *)findComponent(component->second.parentId); } dust3d::Uuid SkeletonDocument::findComponentParentId(dust3d::Uuid componentId) const { auto component = componentMap.find(componentId); if (component == componentMap.end()) { return dust3d::Uuid(); } return component->second.parentId; } void SkeletonDocument::moveComponentUp(dust3d::Uuid componentId) { SkeletonComponent *parent = (SkeletonComponent *)findComponentParent(componentId); if (nullptr == parent) return; dust3d::Uuid parentId = findComponentParentId(componentId); parent->moveChildUp(componentId); parent->dirty = true; emit componentChildrenChanged(parentId); emit skeletonChanged(); } void SkeletonDocument::moveComponentDown(dust3d::Uuid componentId) { SkeletonComponent *parent = (SkeletonComponent *)findComponentParent(componentId); if (nullptr == parent) return; dust3d::Uuid parentId = findComponentParentId(componentId); parent->moveChildDown(componentId); parent->dirty = true; emit componentChildrenChanged(parentId); emit skeletonChanged(); } void SkeletonDocument::moveComponentToTop(dust3d::Uuid componentId) { SkeletonComponent *parent = (SkeletonComponent *)findComponentParent(componentId); if (nullptr == parent) return; dust3d::Uuid parentId = findComponentParentId(componentId); parent->moveChildToTop(componentId); parent->dirty = true; emit componentChildrenChanged(parentId); emit skeletonChanged(); } void SkeletonDocument::moveComponentToBottom(dust3d::Uuid componentId) { SkeletonComponent *parent = (SkeletonComponent *)findComponentParent(componentId); if (nullptr == parent) return; dust3d::Uuid parentId = findComponentParentId(componentId); parent->moveChildToBottom(componentId); parent->dirty = true; emit componentChildrenChanged(parentId); emit skeletonChanged(); } void SkeletonDocument::renameComponent(dust3d::Uuid componentId, QString name) { auto component = componentMap.find(componentId); if (component == componentMap.end()) { return; } if (component->second.name == name) return; if (!name.trimmed().isEmpty()) component->second.name = name; emit componentNameChanged(componentId); emit optionsChanged(); } void SkeletonDocument::setComponentExpandState(dust3d::Uuid componentId, bool expanded) { auto component = componentMap.find(componentId); if (component == componentMap.end()) { return; } if (component->second.expanded == expanded) return; component->second.expanded = expanded; emit componentExpandStateChanged(componentId); emit optionsChanged(); } void SkeletonDocument::ungroupComponent(const dust3d::Uuid &componentId) { if (componentId.isNull()) return; SkeletonComponent *component = (SkeletonComponent *)findComponent(componentId); if (nullptr == component) { dust3dDebug << "Component not found:" << componentId.toString(); return; } if (component->childrenIds.empty()) return; auto childrenIds = component->childrenIds; SkeletonComponent *newParent = (SkeletonComponent *)findComponentParent(componentId); if (nullptr == newParent) { dust3dDebug << "Expected parent component to be found, component:" << componentId.toString(); return; } auto newParentId = newParent->id; newParent->replaceChildWithOthers(componentId, childrenIds); for (const auto &childId: childrenIds) { SkeletonComponent *child = (SkeletonComponent *)findComponent(childId); if (nullptr == child) continue; child->parentId = newParentId; } componentMap.erase(componentId); emit componentRemoved(componentId); emit componentChildrenChanged(newParentId); emit skeletonChanged(); } void SkeletonDocument::groupComponents(const std::vector &componentIds) { if (componentIds.empty()) return; dust3d::Uuid newParentId; SkeletonComponent newParent(dust3d::Uuid::createUuid()); newParentId = newParent.id; auto it = componentIds.begin(); SkeletonComponent *oldParent = (SkeletonComponent *)findComponentParent(*it); if (nullptr == oldParent) { dust3dDebug << "Expected parent component to be found, component:" << it->toString(); return; } auto oldParentId = oldParent->id; oldParent->replaceChild(*it, newParentId); for (++it; it != componentIds.end(); ++it) { oldParent->removeChild(*it); } for (const auto &componentId: componentIds) { auto component = componentMap.find(componentId); if (component == componentMap.end()) { continue; } component->second.parentId = newParentId; newParent.addChild(componentId); } newParent.parentId = oldParentId; newParent.name = tr("Group") + " " + QString::number(componentMap.size() - partMap.size() + 1); componentMap.emplace(newParentId, std::move(newParent)); emit componentChildrenChanged(oldParentId); emit componentAdded(newParentId); emit skeletonChanged(); } void SkeletonDocument::createNewChildComponent(dust3d::Uuid parentComponentId) { SkeletonComponent *parentComponent = (SkeletonComponent *)findComponent(parentComponentId); if (!parentComponent->linkToPartId.isNull()) { parentComponentId = parentComponent->parentId; parentComponent = (SkeletonComponent *)findComponent(parentComponentId); } SkeletonComponent newComponent(dust3d::Uuid::createUuid()); newComponent.name = tr("Group") + " " + QString::number(componentMap.size() - partMap.size() + 1); parentComponent->addChild(newComponent.id); newComponent.parentId = parentComponentId; auto newComponentId = newComponent.id; componentMap.emplace(newComponentId, std::move(newComponent)); emit componentChildrenChanged(parentComponentId); emit componentAdded(newComponentId); emit optionsChanged(); } void SkeletonDocument::removePart(dust3d::Uuid partId) { auto part = partMap.find(partId); if (part == partMap.end()) { return; } if (!part->second.componentId.isNull()) { removeComponent(part->second.componentId); return; } removePartDontCareComponent(partId); } void SkeletonDocument::removePartDontCareComponent(dust3d::Uuid partId) { auto part = partMap.find(partId); if (part == partMap.end()) { return; } std::vector removedNodeIds; std::vector removedEdgeIds; for (auto nodeIt = nodeMap.begin(); nodeIt != nodeMap.end();) { if (nodeIt->second.partId != partId) { nodeIt++; continue; } removedNodeIds.push_back(nodeIt->second.id); nodeIt = nodeMap.erase(nodeIt); } for (auto edgeIt = edgeMap.begin(); edgeIt != edgeMap.end();) { if (edgeIt->second.partId != partId) { edgeIt++; continue; } removedEdgeIds.push_back(edgeIt->second.id); edgeIt = edgeMap.erase(edgeIt); } partMap.erase(part); for (const auto &nodeId: removedNodeIds) { emit nodeRemoved(nodeId); } for (const auto &edgeId: removedEdgeIds) { emit edgeRemoved(edgeId); } emit partRemoved(partId); } void SkeletonDocument::addPartToComponent(dust3d::Uuid partId, dust3d::Uuid componentId) { SkeletonComponent child(dust3d::Uuid::createUuid()); if (!componentId.isNull()) { auto parentComponent = componentMap.find(componentId); if (parentComponent == componentMap.end()) { componentId = dust3d::Uuid(); rootComponent.addChild(child.id); } else { parentComponent->second.addChild(child.id); } } else { rootComponent.addChild(child.id); } partMap[partId].componentId = child.id; child.linkToPartId = partId; child.parentId = componentId; auto childId = child.id; componentMap.emplace(childId, std::move(child)); emit componentChildrenChanged(componentId); emit componentAdded(childId); } void SkeletonDocument::removeComponent(dust3d::Uuid componentId) { removeComponentRecursively(componentId); emit skeletonChanged(); } void SkeletonDocument::removeComponentRecursively(dust3d::Uuid componentId) { auto component = componentMap.find(componentId); if (component == componentMap.end()) { return; } if (!component->second.linkToPartId.isNull()) { removePartDontCareComponent(component->second.linkToPartId); } auto childrenIds = component->second.childrenIds; for (const auto &childId: childrenIds) { removeComponentRecursively(childId); } dust3d::Uuid parentId = component->second.parentId; if (!parentId.isNull()) { auto parentComponent = componentMap.find(parentId); if (parentComponent != componentMap.end()) { parentComponent->second.dirty = true; parentComponent->second.removeChild(componentId); } } else { rootComponent.removeChild(componentId); } componentMap.erase(component); emit componentRemoved(componentId); emit componentChildrenChanged(parentId); } void SkeletonDocument::setCurrentCanvasComponentId(dust3d::Uuid componentId) { m_currentCanvasComponentId = componentId; const SkeletonComponent *component = findComponent(m_currentCanvasComponentId); if (nullptr == component) { m_currentCanvasComponentId = dust3d::Uuid(); } else { if (!component->linkToPartId.isNull()) { m_currentCanvasComponentId = component->parentId; component = findComponent(m_currentCanvasComponentId); } } } void SkeletonDocument::addComponent(dust3d::Uuid parentId) { SkeletonComponent component(dust3d::Uuid::createUuid()); if (!parentId.isNull()) { auto parentComponent = componentMap.find(parentId); if (parentComponent == componentMap.end()) { return; } parentComponent->second.addChild(component.id); } else { rootComponent.addChild(component.id); } component.parentId = parentId; auto componentId = component.id; componentMap.emplace(componentId, std::move(component)); emit componentChildrenChanged(parentId); emit componentAdded(componentId); } bool SkeletonDocument::isDescendantComponent(dust3d::Uuid componentId, dust3d::Uuid suspiciousId) { const SkeletonComponent *loopComponent = findComponentParent(suspiciousId); while (nullptr != loopComponent) { if (loopComponent->id == componentId) return true; loopComponent = findComponentParent(loopComponent->parentId); } return false; } void SkeletonDocument::moveComponent(dust3d::Uuid componentId, dust3d::Uuid toParentId) { if (componentId == toParentId) return; auto component = componentMap.find(componentId); if (component == componentMap.end()) { return; } if (component->second.parentId == toParentId) return; if (isDescendantComponent(componentId, toParentId)) return; if (component->second.parentId.isNull()) { rootComponent.removeChild(componentId); emit componentChildrenChanged(rootComponent.id); } else { auto oldParent = componentMap.find(component->second.parentId); if (oldParent != componentMap.end()) { oldParent->second.dirty = true; oldParent->second.removeChild(componentId); emit componentChildrenChanged(oldParent->second.id); } } component->second.parentId = toParentId; if (toParentId.isNull()) { rootComponent.addChild(componentId); emit componentChildrenChanged(rootComponent.id); } else { auto newParent = componentMap.find(toParentId); if (newParent != componentMap.end()) { newParent->second.dirty = true; newParent->second.addChild(componentId); emit componentChildrenChanged(newParent->second.id); } } emit skeletonChanged(); } void SkeletonDocument::setPartLockState(dust3d::Uuid partId, bool locked) { auto part = partMap.find(partId); if (part == partMap.end()) { return; } if (part->second.locked == locked) return; part->second.locked = locked; emit partLockStateChanged(partId); emit optionsChanged(); } void SkeletonDocument::setPartVisibleState(dust3d::Uuid partId, bool visible) { auto part = partMap.find(partId); if (part == partMap.end()) { return; } if (part->second.visible == visible) return; part->second.visible = visible; emit partVisibleStateChanged(partId); emit optionsChanged(); } void SkeletonDocument::setPartDisableState(dust3d::Uuid partId, bool disabled) { auto part = partMap.find(partId); if (part == partMap.end()) { return; } if (part->second.disabled == disabled) return; part->second.disabled = disabled; part->second.dirty = true; emit partDisableStateChanged(partId); emit skeletonChanged(); } void SkeletonDocument::collectComponentDescendantParts(dust3d::Uuid componentId, std::vector &partIds) const { const SkeletonComponent *component = findComponent(componentId); if (nullptr == component) return; if (!component->linkToPartId.isNull()) { partIds.push_back(component->linkToPartId); return; } for (const auto &childId: component->childrenIds) { collectComponentDescendantParts(childId, partIds); } } void SkeletonDocument::collectComponentDescendantComponents(dust3d::Uuid componentId, std::vector &componentIds) const { const SkeletonComponent *component = findComponent(componentId); if (nullptr == component) return; if (!component->linkToPartId.isNull()) { return; } for (const auto &childId: component->childrenIds) { componentIds.push_back(childId); collectComponentDescendantComponents(childId, componentIds); } } void SkeletonDocument::hideOtherComponents(dust3d::Uuid componentId) { std::vector partIds; collectComponentDescendantParts(componentId, partIds); std::set partIdSet; for (const auto &partId: partIds) { partIdSet.insert(partId); } for (const auto &part: partMap) { if (partIdSet.find(part.first) != partIdSet.end()) continue; setPartVisibleState(part.first, false); } } void SkeletonDocument::lockOtherComponents(dust3d::Uuid componentId) { std::vector partIds; collectComponentDescendantParts(componentId, partIds); std::set partIdSet; for (const auto &partId: partIds) { partIdSet.insert(partId); } for (const auto &part: partMap) { if (partIdSet.find(part.first) != partIdSet.end()) continue; setPartLockState(part.first, true); } } void SkeletonDocument::hideAllComponents() { for (const auto &part: partMap) { setPartVisibleState(part.first, false); } } void SkeletonDocument::showAllComponents() { for (const auto &part: partMap) { setPartVisibleState(part.first, true); } } void SkeletonDocument::showOrHideAllComponents() { bool foundVisiblePart = false; for (const auto &part: partMap) { if (part.second.visible) { foundVisiblePart = true; } } if (foundVisiblePart) hideAllComponents(); else showAllComponents(); } void SkeletonDocument::collapseAllComponents() { for (const auto &component: componentMap) { if (!component.second.linkToPartId.isNull()) continue; setComponentExpandState(component.first, false); } } void SkeletonDocument::expandAllComponents() { for (const auto &component: componentMap) { if (!component.second.linkToPartId.isNull()) continue; setComponentExpandState(component.first, true); } } void SkeletonDocument::lockAllComponents() { for (const auto &part: partMap) { setPartLockState(part.first, true); } } void SkeletonDocument::unlockAllComponents() { for (const auto &part: partMap) { setPartLockState(part.first, false); } } void SkeletonDocument::hideDescendantComponents(dust3d::Uuid componentId) { std::vector partIds; collectComponentDescendantParts(componentId, partIds); for (const auto &partId: partIds) { setPartVisibleState(partId, false); } } void SkeletonDocument::showDescendantComponents(dust3d::Uuid componentId) { std::vector partIds; collectComponentDescendantParts(componentId, partIds); for (const auto &partId: partIds) { setPartVisibleState(partId, true); } } void SkeletonDocument::lockDescendantComponents(dust3d::Uuid componentId) { std::vector partIds; collectComponentDescendantParts(componentId, partIds); for (const auto &partId: partIds) { setPartLockState(partId, true); } } void SkeletonDocument::unlockDescendantComponents(dust3d::Uuid componentId) { std::vector partIds; collectComponentDescendantParts(componentId, partIds); for (const auto &partId: partIds) { setPartLockState(partId, false); } } void SkeletonDocument::scaleNodeByAddRadius(dust3d::Uuid nodeId, float amount) { auto it = nodeMap.find(nodeId); if (it == nodeMap.end()) { return; } if (isPartReadonly(it->second.partId)) return; if (radiusLocked) return; it->second.setRadius(it->second.radius + amount); auto part = partMap.find(it->second.partId); if (part != partMap.end()) part->second.dirty = true; emit nodeRadiusChanged(nodeId); emit skeletonChanged(); } bool SkeletonDocument::isPartReadonly(dust3d::Uuid partId) const { const SkeletonPart *part = findPart(partId); if (nullptr == part) { return true; } return part->locked || !part->visible; } void SkeletonDocument::moveNodeBy(dust3d::Uuid nodeId, float x, float y, float z) { auto it = nodeMap.find(nodeId); if (it == nodeMap.end()) { return; } if (m_allPositionRelatedLocksEnabled && isPartReadonly(it->second.partId)) return; bool changed = false; if (!(m_allPositionRelatedLocksEnabled && xlocked)) { it->second.addX(x); changed = true; } if (!(m_allPositionRelatedLocksEnabled && ylocked)) { it->second.addY(y); changed = true; } if (!(m_allPositionRelatedLocksEnabled && zlocked)) { it->second.addZ(z); changed = true; } if (!changed) return; auto part = partMap.find(it->second.partId); if (part != partMap.end()) part->second.dirty = true; emit nodeOriginChanged(nodeId); emit skeletonChanged(); } void SkeletonDocument::moveOriginBy(float x, float y, float z) { if (!(m_allPositionRelatedLocksEnabled && xlocked)) addOriginX(x); if (!(m_allPositionRelatedLocksEnabled && ylocked)) addOriginY(y); if (!(m_allPositionRelatedLocksEnabled && zlocked)) addOriginZ(z); markAllDirty(); emit originChanged(); emit skeletonChanged(); } void SkeletonDocument::setNodeOrigin(dust3d::Uuid nodeId, float x, float y, float z) { auto it = nodeMap.find(nodeId); if (it == nodeMap.end()) { return; } if ((m_allPositionRelatedLocksEnabled && isPartReadonly(it->second.partId))) return; if (!(m_allPositionRelatedLocksEnabled && xlocked)) it->second.setX(x); if (!(m_allPositionRelatedLocksEnabled && ylocked)) it->second.setY(y); if (!(m_allPositionRelatedLocksEnabled && zlocked)) it->second.setZ(z); auto part = partMap.find(it->second.partId); if (part != partMap.end()) part->second.dirty = true; emit nodeOriginChanged(nodeId); emit skeletonChanged(); } void SkeletonDocument::setNodeRadius(dust3d::Uuid nodeId, float radius) { auto it = nodeMap.find(nodeId); if (it == nodeMap.end()) { return; } if (isPartReadonly(it->second.partId)) return; if (!radiusLocked) it->second.setRadius(radius); auto part = partMap.find(it->second.partId); if (part != partMap.end()) part->second.dirty = true; emit nodeRadiusChanged(nodeId); emit skeletonChanged(); } void SkeletonDocument::switchNodeXZ(dust3d::Uuid nodeId) { auto it = nodeMap.find(nodeId); if (it == nodeMap.end()) { return; } if (isPartReadonly(it->second.partId)) return; { float oldX = it->second.getX(); float oldZ = it->second.getZ(); it->second.setX(oldZ); it->second.setZ(oldX); } auto part = partMap.find(it->second.partId); if (part != partMap.end()) part->second.dirty = true; emit nodeOriginChanged(nodeId); emit skeletonChanged(); } const SkeletonComponent *SkeletonDocument::findComponent(dust3d::Uuid componentId) const { if (componentId.isNull()) return &rootComponent; auto it = componentMap.find(componentId); if (it == componentMap.end()) return nullptr; return &it->second; } void SkeletonDocument::addEdge(dust3d::Uuid fromNodeId, dust3d::Uuid toNodeId) { if (findEdgeByNodes(fromNodeId, toNodeId)) { return; } const SkeletonNode *fromNode = nullptr; const SkeletonNode *toNode = nullptr; bool toPartRemoved = false; fromNode = findNode(fromNodeId); if (nullptr == fromNode) { return; } if (isPartReadonly(fromNode->partId)) return; toNode = findNode(toNodeId); if (nullptr == toNode) { return; } if (isPartReadonly(toNode->partId)) return; dust3d::Uuid toPartId = toNode->partId; auto fromPart = partMap.find(fromNode->partId); if (fromPart == partMap.end()) { return; } fromPart->second.dirty = true; if (fromNode->partId != toNode->partId) { toPartRemoved = true; std::vector toGroup; std::set visitMap; joinNodeAndNeiborsToGroup(&toGroup, toNodeId, &visitMap); for (auto nodeIdIt = toGroup.begin(); nodeIdIt != toGroup.end(); nodeIdIt++) { auto nodeIt = nodeMap.find(*nodeIdIt); if (nodeIt == nodeMap.end()) { continue; } nodeIt->second.partId = fromNode->partId; fromPart->second.nodeIds.push_back(nodeIt->first); for (auto edgeIdIt = nodeIt->second.edgeIds.begin(); edgeIdIt != nodeIt->second.edgeIds.end(); edgeIdIt++) { auto edgeIt = edgeMap.find(*edgeIdIt); if (edgeIt == edgeMap.end()) { continue; } edgeIt->second.partId = fromNode->partId; } } } SkeletonEdge edge; edge.partId = fromNode->partId; edge.nodeIds.push_back(fromNode->id); edge.nodeIds.push_back(toNodeId); edgeMap[edge.id] = edge; nodeMap[toNodeId].edgeIds.push_back(edge.id); nodeMap[fromNode->id].edgeIds.push_back(edge.id); emit edgeAdded(edge.id); if (toPartRemoved) { updateLinkedPart(toPartId, fromNode->partId); removePart(toPartId); } emit skeletonChanged(); } void SkeletonDocument::updateLinkedPart(dust3d::Uuid oldPartId, dust3d::Uuid newPartId) { for (auto &partIt: partMap) { if (partIt.second.cutFaceLinkedId == oldPartId) { partIt.second.dirty = true; partIt.second.setCutFaceLinkedId(newPartId); } } std::set dirtyPartIds; for (auto &nodeIt: nodeMap) { if (nodeIt.second.cutFaceLinkedId == oldPartId) { dirtyPartIds.insert(nodeIt.second.partId); nodeIt.second.setCutFaceLinkedId(newPartId); } } for (const auto &partId: dirtyPartIds) { SkeletonPart *part = (SkeletonPart *)findPart(partId); if (nullptr == part) continue; part->dirty = true; } } void SkeletonDocument::enableAllPositionRelatedLocks() { m_allPositionRelatedLocksEnabled = true; } void SkeletonDocument::disableAllPositionRelatedLocks() { m_allPositionRelatedLocksEnabled = false; } void SkeletonDocument::resetDirtyFlags() { for (auto &part: partMap) { part.second.dirty = false; } for (auto &component: componentMap) { component.second.dirty = false; } } void SkeletonDocument::markAllDirty() { for (auto &part: partMap) { part.second.dirty = true; } } void SkeletonDocument::setXlockState(bool locked) { if (xlocked == locked) return; xlocked = locked; emit xlockStateChanged(); } void SkeletonDocument::setYlockState(bool locked) { if (ylocked == locked) return; ylocked = locked; emit ylockStateChanged(); } void SkeletonDocument::setZlockState(bool locked) { if (zlocked == locked) return; zlocked = locked; emit zlockStateChanged(); } void SkeletonDocument::setRadiusLockState(bool locked) { if (radiusLocked == locked) return; radiusLocked = locked; emit radiusLockStateChanged(); } void SkeletonDocument::setComponentPreviewImage(const dust3d::Uuid &componentId, std::unique_ptr image) { SkeletonComponent *component = (SkeletonComponent *)findComponent(componentId); if (nullptr == component) return; component->isPreviewImageDecorationObsolete = true; component->previewImage = std::move(image); } void SkeletonDocument::setComponentPreviewPixmap(const dust3d::Uuid &componentId, const QPixmap &pixmap) { SkeletonComponent *component = (SkeletonComponent *)findComponent(componentId); if (nullptr == component) return; component->previewPixmap = pixmap; emit componentPreviewPixmapChanged(componentId); } void SkeletonDocument::setComponentPreviewMesh(const dust3d::Uuid &componentId, std::unique_ptr mesh) { SkeletonComponent *component = (SkeletonComponent *)findComponent(componentId); if (nullptr == component) return; component->updatePreviewMesh(std::move(mesh)); emit componentPreviewMeshChanged(componentId); }