#include #include #include #include #include #include #include #include #include #include "skeletoneditgraphicsview.h" #include "skeletoneditnodeitem.h" #include "skeletoneditedgeitem.h" #include "theme.h" qreal SkeletonEditGraphicsView::m_initialNodeSize = 128; qreal SkeletonEditGraphicsView::m_minimalNodeSize = 8; SkeletonEditGraphicsView::SkeletonEditGraphicsView(QWidget *parent) : QGraphicsView(parent), m_pendingNodeItem(NULL), m_pendingEdgeItem(NULL), m_inAddNodeMode(true), m_nextStartNodeItem(NULL), m_lastHoverNodeItem(NULL), m_lastMousePos(0, 0), m_isMovingNodeItem(false), m_backgroundLoaded(false), m_modelWidget(NULL), m_modelWidgetProxy(NULL), m_combineEnabled(false), m_unionEnabled(true), m_subdivEnabled(false) { setScene(new QGraphicsScene()); m_modelWidget = new ModelWidget(this); m_modelWidget->setMinimumSize(128, 128); m_modelWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); m_modelWidget->move(100, 100); m_modelWidget->setGraphicsView(this); m_modelWidgetProxy = scene()->addWidget(m_modelWidget); m_backgroundItem = new QGraphicsPixmapItem(); m_backgroundItem->setOpacity(0.25); scene()->addItem(m_backgroundItem); m_pendingNodeItem = new QGraphicsEllipseItem(0, 0, m_initialNodeSize, m_initialNodeSize); m_pendingNodeItem->setVisible(false); scene()->addItem(m_pendingNodeItem); m_pendingEdgeItem = new QGraphicsLineItem(0, 0, 0, 0); m_pendingEdgeItem->setVisible(false); scene()->addItem(m_pendingEdgeItem); } ModelWidget *SkeletonEditGraphicsView::modelWidget() { return m_modelWidget; } void SkeletonEditGraphicsView::toggleAddNodeMode() { if (!m_backgroundLoaded) return; m_inAddNodeMode = !m_inAddNodeMode; applyAddNodeMode(); } void SkeletonEditGraphicsView::applyAddNodeMode() { m_pendingNodeItem->setVisible(m_inAddNodeMode); m_pendingEdgeItem->setVisible(m_inAddNodeMode && m_nextStartNodeItem); setMouseTracking(true); } void SkeletonEditGraphicsView::turnOffAddNodeMode() { if (!m_backgroundLoaded) return; m_inAddNodeMode = false; applyAddNodeMode(); } void SkeletonEditGraphicsView::turnOnAddNodeMode() { if (!m_backgroundLoaded) return; m_inAddNodeMode = true; applyAddNodeMode(); } SkeletonEditNodeItem *SkeletonEditGraphicsView::findNodeItemByPos(QPointF pos) { QList::iterator it; QList list = scene()->items(); for (it = list.begin(); it != list.end(); ++it) { if ((*it)->data(0).toString() == "node") { SkeletonEditNodeItem *nodeItem = static_cast(*it); if (nodeItem->shape().contains(pos)) { return nodeItem; } } } return NULL; } SkeletonEditEdgeItem *SkeletonEditGraphicsView::findEdgeItemByPos(QPointF pos) { QList::iterator it; QList list = scene()->items(); for (it = list.begin(); it != list.end(); ++it) { if ((*it)->data(0).toString() == "edge") { SkeletonEditEdgeItem *edgeItem = static_cast(*it); if (edgeItem->shape().contains(pos)) { return edgeItem; } } } return NULL; } SkeletonEditEdgeItem *SkeletonEditGraphicsView::findEdgeItemByNodePair(SkeletonEditNodeItem *first, SkeletonEditNodeItem *second) { QList::iterator it; QList list = scene()->items(); assert(first != second); for (it = list.begin(); it != list.end(); ++it) { if ((*it)->data(0).toString() == "edge") { SkeletonEditEdgeItem *edgeItem = static_cast(*it); if ((edgeItem->firstNode() == first || edgeItem->secondNode() == first) && (edgeItem->firstNode() == second || edgeItem->secondNode() == second)) { return edgeItem; } } } return NULL; } bool SkeletonEditGraphicsView::mousePress(QMouseEvent *event, const QPointF &scenePos) { bool processed = false; if (!m_backgroundLoaded) return false; QPointF pos = scenePos; if (event->button() == Qt::LeftButton) { if (!m_inAddNodeMode) { if (m_lastHoverNodeItem) { setNextStartNodeItem(m_lastHoverNodeItem); m_lastHoverNodeItem = NULL; processed = true; } else { if (m_nextStartNodeItem) { setNextStartNodeItem(NULL); processed = true; } } } } m_lastMousePos = pos; return processed; } bool SkeletonEditGraphicsView::mouseDoubleClick(QMouseEvent *event, const QPointF &scenePos) { bool processed = false; if (QApplication::keyboardModifiers() & Qt::ControlModifier) { processed = true; emit changeTurnaroundTriggered(); } return processed; } void SkeletonEditGraphicsView::removeSelectedItems() { if (m_nextStartNodeItem) { SkeletonEditNodeItem *nodeItem = m_nextStartNodeItem; setNextStartNodeItem(NULL); removeNodeItem(nodeItem->nextSidePair()); removeNodeItem(nodeItem); emit nodesChanged(); } } void SkeletonEditGraphicsView::removeNodeItem(SkeletonEditNodeItem *nodeItem) { scene()->removeItem(nodeItem); QList::iterator it; QList list = scene()->items(); for (it = list.begin(); it != list.end(); ++it) { if ((*it)->data(0).toString() == "edge") { SkeletonEditEdgeItem *edgeItem = static_cast(*it); if (edgeItem->firstNode() == nodeItem || edgeItem->secondNode() == nodeItem) { scene()->removeItem(edgeItem); } } } } void SkeletonEditGraphicsView::fetchNodeItemAndAllSidePairs(SkeletonEditNodeItem *nodeItem, std::vector *sidePairs) { sidePairs->push_back(nodeItem); sidePairs->push_back(nodeItem->nextSidePair()); sidePairs->push_back(nodeItem->nextSidePair()->nextSidePair()); } void SkeletonEditGraphicsView::removeNodeItemAndSidePairs(SkeletonEditNodeItem *nodeItem) { std::vector nodes; fetchNodeItemAndAllSidePairs(nodeItem, &nodes); for (size_t i = 0; i < nodes.size(); i++) { removeNodeItem(nodes[i]); } } bool SkeletonEditGraphicsView::keyPress(QKeyEvent *event, const QPointF &scenePos) { bool processed = false; if (!m_backgroundLoaded) return false; if (event->key() == Qt::Key_A) { toggleAddNodeMode(); processed = true; } else if (event->key() == Qt::Key_C) { m_combineEnabled = !m_combineEnabled; emit nodesChanged(); } else if (event->key() == Qt::Key_U) { m_unionEnabled = !m_unionEnabled; emit nodesChanged(); } else if (event->key() == Qt::Key_S) { m_subdivEnabled = !m_subdivEnabled; emit nodesChanged(); } else if (event->key() == Qt::Key_Delete || event->key() ==Qt::Key_Backspace) { removeSelectedItems(); processed = true; } return processed; } void SkeletonEditGraphicsView::resizeEvent(QResizeEvent *event) { QFrame::resizeEvent(event); emit sizeChanged(); } SkeletonEditNodeItem *SkeletonEditGraphicsView::addNodeItemAndSidePairs(QRectF area, SkeletonEditNodeItem *fromNodeItem, const QString &sideColorName) { float pairedX = 0; QRectF pairedRect = area; if (fromNodeItem) { pairedX = fromNodeItem->nextSidePair()->rect().x(); } else { if (area.center().x() < scene()->sceneRect().width() / 2) { pairedX = area.center().x() + scene()->sceneRect().width() / 4; } else { pairedX = area.center().x() - scene()->sceneRect().width() / 4; } } pairedRect.translate(pairedX - area.x(), 0); SkeletonEditNodeItem *firstNode = new SkeletonEditNodeItem(area); scene()->addItem(firstNode); firstNode->setSideColorName(fromNodeItem ? fromNodeItem->sideColorName() : sideColorName); SkeletonEditNodeItem *secondNode = new SkeletonEditNodeItem(pairedRect); scene()->addItem(secondNode); secondNode->setSideColorName(firstNode->nextSideColorName()); firstNode->setNextSidePair(secondNode); secondNode->setNextSidePair(firstNode); setNextStartNodeItem(firstNode); if (!fromNodeItem) { return firstNode; } addEdgeItem(fromNodeItem, firstNode); addEdgeItem(fromNodeItem->nextSidePair(), firstNode->nextSidePair()); return firstNode; } SkeletonEditNodeItem *SkeletonEditGraphicsView::addNodeItem(float originX, float originY, float radius) { QRectF area(originX - radius, originY - radius, radius * 2, radius * 2); SkeletonEditNodeItem *firstNode = new SkeletonEditNodeItem(area); scene()->addItem(firstNode); return firstNode; } void SkeletonEditGraphicsView::addEdgeItem(SkeletonEditNodeItem *first, SkeletonEditNodeItem *second) { SkeletonEditEdgeItem *newEdge = new SkeletonEditEdgeItem(); newEdge->setNodes(first, second); scene()->addItem(newEdge); } bool SkeletonEditGraphicsView::mouseRelease(QMouseEvent *event, const QPointF &scenePos) { bool processed = false; if (!m_backgroundLoaded) return false; if (event->button() == Qt::LeftButton) { if (m_inAddNodeMode) { if (m_lastHoverNodeItem && m_nextStartNodeItem && m_lastHoverNodeItem != m_nextStartNodeItem && m_lastHoverNodeItem->sideColor() == m_nextStartNodeItem->sideColor()) { if (!findEdgeItemByNodePair(m_lastHoverNodeItem, m_nextStartNodeItem)) { addEdgeItem(m_nextStartNodeItem, m_lastHoverNodeItem); addEdgeItem(m_nextStartNodeItem->nextSidePair(), m_lastHoverNodeItem->nextSidePair()); processed = true; emit nodesChanged(); } } else { addNodeItemAndSidePairs(m_pendingNodeItem->rect(), m_nextStartNodeItem); processed = true; emit nodesChanged(); } } m_isMovingNodeItem = false; } return processed; } bool SkeletonEditGraphicsView::canNodeItemMoveTo(SkeletonEditNodeItem *item, QPointF moveTo) { if (moveTo.x() < 0) return false; if (moveTo.y() < 0) return false; if (moveTo.x() + item->rect().width() >= scene()->sceneRect().width()) return false; if (moveTo.y() + item->rect().height() >= scene()->sceneRect().height()) return false; return true; } bool SkeletonEditGraphicsView::mouseMove(QMouseEvent *event, const QPointF &scenePos) { bool processed = false; if (!m_backgroundLoaded) return false; QPointF pos = scenePos; QPointF moveTo = QPointF(pos.x() - m_pendingNodeItem->rect().width() / 2, pos.y() - m_pendingNodeItem->rect().height() / 2); if (moveTo.x() < 0) moveTo.setX(0); if (moveTo.y() < 0) moveTo.setY(0); if (moveTo.x() + m_pendingNodeItem->rect().width() >= scene()->sceneRect().width()) moveTo.setX(scene()->sceneRect().width() - m_pendingNodeItem->rect().width()); if (moveTo.y() + m_pendingNodeItem->rect().height() >= scene()->sceneRect().height()) moveTo.setY(scene()->sceneRect().height() - m_pendingNodeItem->rect().height()); QSizeF oldSize = m_pendingNodeItem->rect().size(); m_pendingNodeItem->setRect(moveTo.x(), moveTo.y(), oldSize.width(), oldSize.height()); if (m_nextStartNodeItem) { m_pendingEdgeItem->setLine(QLineF(m_nextStartNodeItem->origin(), QPointF(moveTo.x() + m_pendingNodeItem->rect().width() / 2, moveTo.y() + m_pendingNodeItem->rect().height() / 2))); } if (!m_isMovingNodeItem) { SkeletonEditNodeItem *hoverNodeItem = findNodeItemByPos(pos); if (hoverNodeItem) { hoverNodeItem->setHovered(true); processed = true; } if (hoverNodeItem != m_lastHoverNodeItem) { if (m_lastHoverNodeItem) m_lastHoverNodeItem->setHovered(false); m_lastHoverNodeItem = hoverNodeItem; processed = true; } } QPointF curMousePos = pos; if (m_lastHoverNodeItem) { if ((event->buttons() & Qt::LeftButton) && (curMousePos != m_lastMousePos || m_isMovingNodeItem)) { m_isMovingNodeItem = true; QRectF rect = m_lastHoverNodeItem->rect(); QRectF pairedRect; rect.translate(curMousePos.x() - m_lastMousePos.x(), curMousePos.y() - m_lastMousePos.y()); pairedRect = m_lastHoverNodeItem->nextSidePair()->rect(); pairedRect.translate(0, curMousePos.y() - m_lastMousePos.y()); m_lastHoverNodeItem->setRect(rect); m_lastHoverNodeItem->nextSidePair()->setRect(pairedRect); QList::iterator it; QList list = scene()->items(); for (it = list.begin(); it != list.end(); ++it) { if ((*it)->data(0).toString() == "edge") { SkeletonEditEdgeItem *edgeItem = static_cast(*it); if (edgeItem->connects(m_lastHoverNodeItem) || edgeItem->connects(m_lastHoverNodeItem->nextSidePair())) edgeItem->updatePosition(); } } processed = true; emit nodesChanged(); } } m_lastMousePos = curMousePos; return processed; } void SkeletonEditGraphicsView::AddItemRadius(QGraphicsEllipseItem *item, float delta) { QSizeF oldSize = item->rect().size(); QPointF originPt = QPointF(item->rect().left() + oldSize.width() / 2, item->rect().top() + oldSize.height() / 2); QSizeF newSize = QSizeF(oldSize.width() + delta, oldSize.height() + delta); if (newSize.width() < m_minimalNodeSize || newSize.height() < m_minimalNodeSize) { newSize.setWidth(m_minimalNodeSize); newSize.setHeight(m_minimalNodeSize); } QPointF newLeftTop = QPointF(originPt.x() - newSize.width() / 2, originPt.y() - newSize.height() / 2); if (newLeftTop.x() < 0 || newLeftTop.x() + newSize.width() >= scene()->sceneRect().width()) return; if (newLeftTop.y() < 0 || newLeftTop.y() + newSize.height() >= scene()->sceneRect().height()) return; item->setRect(newLeftTop.x(), newLeftTop.y(), newSize.width(), newSize.height()); } bool SkeletonEditGraphicsView::canAddItemRadius(QGraphicsEllipseItem *item, float delta) { QSizeF oldSize = item->rect().size(); QPointF originPt = QPointF(item->rect().left() + oldSize.width() / 2, item->rect().top() + oldSize.height() / 2); QSizeF newSize = QSizeF(oldSize.width() + delta, oldSize.height() + delta); if (newSize.width() < m_minimalNodeSize || newSize.height() < m_minimalNodeSize) { newSize.setWidth(m_minimalNodeSize); newSize.setHeight(m_minimalNodeSize); } QPointF newLeftTop = QPointF(originPt.x() - newSize.width() / 2, originPt.y() - newSize.height() / 2); if (newLeftTop.x() < 0 || newLeftTop.x() + newSize.width() >= scene()->sceneRect().width()) return false; if (newLeftTop.y() < 0 || newLeftTop.y() + newSize.height() >= scene()->sceneRect().height()) return false; return true; } bool SkeletonEditGraphicsView::wheel(QWheelEvent *event, const QPointF &scenePos) { bool processed = false; if (!m_backgroundLoaded) return false; qreal delta = event->delta() / 10; if (QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier)) { if (delta > 0) delta = 1; else delta = -1; } else { if (fabs(delta) < 1) delta = delta < 0 ? -1.0 : 1.0; } AddItemRadius(m_pendingNodeItem, delta); if (!m_inAddNodeMode && m_lastHoverNodeItem) { AddItemRadius(m_lastHoverNodeItem, delta); processed = true; emit nodesChanged(); } if (m_inAddNodeMode) processed = true; return processed; } void SkeletonEditGraphicsView::setNextStartNodeItem(SkeletonEditNodeItem *item) { if (m_nextStartNodeItem != item) { if (m_nextStartNodeItem) m_nextStartNodeItem->setChecked(false); } m_nextStartNodeItem = item; if (m_nextStartNodeItem) m_nextStartNodeItem->setChecked(true); applyAddNodeMode(); } void SkeletonEditGraphicsView::updateBackgroundImage(const QImage &image) { QSizeF oldSceneSize = scene()->sceneRect().size(); QPixmap pixmap = QPixmap::fromImage(image); scene()->setSceneRect(pixmap.rect()); m_backgroundItem->setPixmap(pixmap); adjustItems(oldSceneSize, scene()->sceneRect().size()); if (!m_backgroundLoaded) { m_backgroundLoaded = true; applyAddNodeMode(); } } QPixmap SkeletonEditGraphicsView::backgroundImage() { return m_backgroundItem->pixmap(); } bool SkeletonEditGraphicsView::hasBackgroundImage() { return m_backgroundLoaded; } void SkeletonEditGraphicsView::adjustItems(QSizeF oldSceneSize, QSizeF newSceneSize) { if (oldSceneSize == newSceneSize) return; float radiusMul = (float)newSceneSize.height() / oldSceneSize.height(); float xMul = (float)newSceneSize.width() / oldSceneSize.width(); float yMul = radiusMul; QList::iterator it; QList list = scene()->items(); for (it = list.begin(); it != list.end(); ++it) { if ((*it)->data(0).toString() == "node") { SkeletonEditNodeItem *nodeItem = static_cast(*it); nodeItem->setRadius(nodeItem->radius() * radiusMul); QPointF oldOrigin = nodeItem->origin(); nodeItem->setOrigin(QPointF(oldOrigin.x() * xMul, oldOrigin.y() * yMul)); } } for (it = list.begin(); it != list.end(); ++it) { if ((*it)->data(0).toString() == "edge") { SkeletonEditEdgeItem *edgeItem = static_cast(*it); edgeItem->updatePosition(); } } } void SkeletonEditGraphicsView::loadFromSnapshot(SkeletonSnapshot *snapshot) { float radiusMul = 1.0; float xMul = 1.0; float yMul = radiusMul; QString canvasWidth = snapshot->canvas["width"]; QString canvasHeight = snapshot->canvas["height"]; float canvasWidthVal = canvasWidth.toFloat(); float canvasHeightVal = canvasHeight.toFloat(); if (!hasBackgroundImage()) { QPixmap emptyImage((int)canvasWidthVal, (int)canvasHeightVal); emptyImage.fill(QWidget::palette().color(QWidget::backgroundRole())); updateBackgroundImage(emptyImage.toImage()); } if (canvasHeightVal > 0) radiusMul = (float)scene()->sceneRect().height() / canvasHeightVal; if (canvasWidthVal > 0) xMul = (float)scene()->sceneRect().width() / canvasWidthVal; yMul = radiusMul; std::map nodeItemMap; std::map>::iterator nodeIterator; for (nodeIterator = snapshot->nodes.begin(); nodeIterator != snapshot->nodes.end(); nodeIterator++) { std::map *snapshotNode = &nodeIterator->second; SkeletonEditNodeItem *nodeItem = addNodeItem((*snapshotNode)["x"].toFloat() * xMul, (*snapshotNode)["y"].toFloat() * yMul, (*snapshotNode)["radius"].toFloat() * radiusMul); nodeItem->setSideColorName((*snapshotNode)["sideColorName"]); nodeItem->markAsBranch("true" == (*snapshotNode)["isBranch"]); nodeItemMap[nodeIterator->first] = nodeItem; } for (nodeIterator = snapshot->nodes.begin(); nodeIterator != snapshot->nodes.end(); nodeIterator++) { std::map *snapshotNode = &nodeIterator->second; SkeletonEditNodeItem *nodeItem = nodeItemMap[nodeIterator->first]; nodeItem->setNextSidePair(nodeItemMap[(*snapshotNode)["nextSidePair"]]); } std::map>::iterator edgeIterator; for (edgeIterator = snapshot->edges.begin(); edgeIterator != snapshot->edges.end(); edgeIterator++) { std::map *snapshotEdge = &edgeIterator->second; addEdgeItem(nodeItemMap[(*snapshotEdge)["from"]], nodeItemMap[(*snapshotEdge)["to"]]); } emit nodesChanged(); } void SkeletonEditGraphicsView::saveToSnapshot(SkeletonSnapshot *snapshot) { snapshot->canvas["width"] = QString("%1").arg(scene()->sceneRect().width()); snapshot->canvas["height"] = QString("%1").arg(scene()->sceneRect().height()); snapshot->canvas["combine"] = m_combineEnabled ? "true" : "false"; snapshot->canvas["union"] = m_unionEnabled ? "true" : "false"; snapshot->canvas["subdiv"] = m_subdivEnabled ? "true" : "false"; QList::iterator it; QList list = scene()->items(); int nextNodeId = 1; std::map nodeIdMap; for (it = list.begin(); it != list.end(); ++it) { if ((*it)->data(0).toString() == "node") { SkeletonEditNodeItem *nodeItem = static_cast(*it); QString nodeId = QString("node%1").arg(nextNodeId); std::map *snapshotNode = &snapshot->nodes[nodeId]; (*snapshotNode)["id"] = nodeId; (*snapshotNode)["sideColorName"] = nodeItem->sideColorName(); (*snapshotNode)["radius"] = QString("%1").arg(nodeItem->radius()); (*snapshotNode)["isBranch"] = QString("%1").arg(nodeItem->isBranch() ? "true" : "false"); (*snapshotNode)["isRoot"] = QString("%1").arg(nodeItem->isRoot() ? "true" : "false"); QPointF origin = nodeItem->origin(); (*snapshotNode)["x"] = QString("%1").arg(origin.x()); (*snapshotNode)["y"] = QString("%1").arg(origin.y()); nodeIdMap[nodeItem] = nodeId; nextNodeId++; } } for (it = list.begin(); it != list.end(); ++it) { if ((*it)->data(0).toString() == "node") { SkeletonEditNodeItem *nodeItem = static_cast(*it); QString nodeId = nodeIdMap[nodeItem]; std::map *snapshotNode = &snapshot->nodes[nodeId]; (*snapshotNode)["nextSidePair"] = nodeIdMap[nodeItem->nextSidePair()]; } } int nextEdgeId = 1; for (it = list.begin(); it != list.end(); ++it) { if ((*it)->data(0).toString() == "edge") { SkeletonEditEdgeItem *edgeItem = static_cast(*it); QString edgeId = QString("edge%1").arg(nextEdgeId); std::map *snapshotEdge = &snapshot->edges[edgeId]; (*snapshotEdge)["id"] = edgeId; (*snapshotEdge)["from"] = nodeIdMap[edgeItem->firstNode()]; (*snapshotEdge)["to"] = nodeIdMap[edgeItem->secondNode()]; nextEdgeId++; } } } void SkeletonEditGraphicsView::markAsBranch() { if (m_nextStartNodeItem) { m_nextStartNodeItem->markAsBranch(true); m_nextStartNodeItem->nextSidePair()->markAsBranch(true); emit nodesChanged(); } } void SkeletonEditGraphicsView::markAsTrunk() { if (m_nextStartNodeItem) { m_nextStartNodeItem->markAsBranch(false); m_nextStartNodeItem->nextSidePair()->markAsBranch(false); emit nodesChanged(); } } void SkeletonEditGraphicsView::markAsRoot() { if (m_nextStartNodeItem) { m_nextStartNodeItem->markAsRoot(true); m_nextStartNodeItem->nextSidePair()->markAsRoot(true); emit nodesChanged(); } } void SkeletonEditGraphicsView::markAsChild() { if (m_nextStartNodeItem) { m_nextStartNodeItem->markAsRoot(false); m_nextStartNodeItem->nextSidePair()->markAsRoot(false); emit nodesChanged(); } } void SkeletonEditGraphicsView::mouseMoveEvent(QMouseEvent *event) { mouseMove(event, mapToScene(event->pos())); } void SkeletonEditGraphicsView::wheelEvent(QWheelEvent *event) { wheel(event, mapToScene(event->pos())); } void SkeletonEditGraphicsView::mouseReleaseEvent(QMouseEvent *event) { mouseRelease(event, mapToScene(event->pos())); } void SkeletonEditGraphicsView::mousePressEvent(QMouseEvent *event) { mousePress(event, mapToScene(event->pos())); } void SkeletonEditGraphicsView::mouseDoubleClickEvent(QMouseEvent *event) { mouseDoubleClick(event, mapToScene(event->pos())); } void SkeletonEditGraphicsView::keyPressEvent(QKeyEvent *event) { keyPress(event, QPointF()); } bool SkeletonEditGraphicsView::combineEnabled() { return m_combineEnabled; } bool SkeletonEditGraphicsView::unionEnabled() { return m_unionEnabled; } bool SkeletonEditGraphicsView::subdivEnabled() { return m_subdivEnabled; }