diff --git a/src/main.cpp b/src/main.cpp index 1fceb064..b19f9b42 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -24,7 +24,7 @@ int main(int argc, char ** argv) darkPalette.setColor(QPalette::ButtonText, QColor(239,239,239)); darkPalette.setColor(QPalette::BrightText, Qt::red); darkPalette.setColor(QPalette::Link, QColor(42, 130, 218)); - darkPalette.setColor(QPalette::Highlight, QColor(42, 130, 218)); + darkPalette.setColor(QPalette::Highlight, QColor(252, 102, 33)); darkPalette.setColor(QPalette::HighlightedText, Qt::black); qApp->setPalette(darkPalette); qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #fc6621; border: 1px solid white; }"); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 5c6be9c9..bfc03811 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include "mainwindow.h" #include "skeletonwidget.h" @@ -52,6 +54,14 @@ MainWindow::MainWindow() topLayout->addLayout(topButtonsLayout); topLayout->addWidget(hrWidget); + m_edgePropertyWidget = new QWidget; + QFormLayout *formLayout = new QFormLayout; + QComboBox *edgeTypeBox = new QComboBox; + edgeTypeBox->addItem("Spine", "Spine"); + edgeTypeBox->addItem("Attach", "Attach"); + formLayout->addRow(tr("Edge Type:"), edgeTypeBox); + m_edgePropertyWidget->setLayout(formLayout); + QVBoxLayout *modelRightLayout = new QVBoxLayout; modelRightLayout->addSpacing(20); @@ -75,6 +85,10 @@ MainWindow::MainWindow() modelRightLayout->addWidget(saveModelAsButton); saveModelAsButton->hide(); + modelRightLayout->addSpacing(20); + + modelRightLayout->addWidget(m_edgePropertyWidget); + modelRightLayout->addStretch(); SkeletonWidget *skeletonWidget = new SkeletonWidget(this); diff --git a/src/mainwindow.h b/src/mainwindow.h index 2a6dbfa3..9022b198 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -22,6 +22,7 @@ private: QStackedWidget *m_stackedWidget; QString m_saveModelAs; SkeletonWidget *m_skeletonWidget; + QWidget *m_edgePropertyWidget; }; #endif diff --git a/src/skeletoneditedgeitem.cpp b/src/skeletoneditedgeitem.cpp index 5a572a2f..21a5e573 100644 --- a/src/skeletoneditedgeitem.cpp +++ b/src/skeletoneditedgeitem.cpp @@ -8,7 +8,27 @@ SkeletonEditEdgeItem::SkeletonEditEdgeItem(QGraphicsItem *parent) : m_secondNode(NULL) { setData(0, "edge"); - QPen pen(Theme::skeletonMasterNodeBorderColor); + m_checked = false; + updateAppearance(); +} + +void SkeletonEditEdgeItem::setChecked(bool checked) +{ + if (m_checked == checked) { + return; + } + m_checked = checked; + updateAppearance(); +} + +bool SkeletonEditEdgeItem::checked() +{ + return m_checked; +} + +void SkeletonEditEdgeItem::updateAppearance() +{ + QPen pen(m_checked ? Theme::skeletonMasterNodeBorderHighlightColor : Theme::skeletonMasterNodeBorderColor); pen.setWidth(Theme::skeletonMasterNodeBorderSize); setPen(pen); } diff --git a/src/skeletoneditedgeitem.h b/src/skeletoneditedgeitem.h index 2faa702e..40099e9b 100644 --- a/src/skeletoneditedgeitem.h +++ b/src/skeletoneditedgeitem.h @@ -12,9 +12,14 @@ public: SkeletonEditNodeItem *firstNode(); SkeletonEditNodeItem *secondNode(); bool connects(SkeletonEditNodeItem *nodeItem); + void setChecked(bool checked); + bool checked(); private: SkeletonEditNodeItem *m_firstNode; SkeletonEditNodeItem *m_secondNode; + bool m_checked; +private: + void updateAppearance(); }; #endif diff --git a/src/skeletoneditgraphicsview.cpp b/src/skeletoneditgraphicsview.cpp index ea01a3ba..a69c8176 100644 --- a/src/skeletoneditgraphicsview.cpp +++ b/src/skeletoneditgraphicsview.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "skeletoneditgraphicsview.h" #include "skeletoneditnodeitem.h" #include "skeletoneditedgeitem.h" @@ -21,7 +22,8 @@ SkeletonEditGraphicsView::SkeletonEditGraphicsView(QWidget *parent) : m_lastHoverNodeItem(NULL), m_lastMousePos(0, 0), m_isMovingNodeItem(false), - m_backgroundLoaded(false) + m_backgroundLoaded(false), + m_selectedEdgeItem(NULL) { setScene(new QGraphicsScene()); @@ -76,7 +78,7 @@ SkeletonEditNodeItem *SkeletonEditGraphicsView::findNodeItemByPos(QPointF pos) for (it = list.begin(); it != list.end(); ++it) { if ((*it)->data(0).toString() == "node") { SkeletonEditNodeItem *nodeItem = static_cast(*it); - if (nodeItem->rect().contains(pos)) { + if (nodeItem->shape().contains(pos)) { return nodeItem; } } @@ -84,11 +86,45 @@ SkeletonEditNodeItem *SkeletonEditGraphicsView::findNodeItemByPos(QPointF pos) 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; +} + void SkeletonEditGraphicsView::mousePressEvent(QMouseEvent *event) { QWidget::mousePressEvent(event); if (!m_backgroundLoaded) return; + QPointF pos = mapToScene(event->pos()); if (event->button() == Qt::LeftButton) { if (!m_inAddNodeMode) { if (m_lastHoverNodeItem) { @@ -99,9 +135,27 @@ void SkeletonEditGraphicsView::mousePressEvent(QMouseEvent *event) setNextStartNodeItem(NULL); } } + SkeletonEditEdgeItem *edgeItem = findEdgeItemByPos(pos); + if (edgeItem) { + if (m_selectedEdgeItem != edgeItem) { + if (m_selectedEdgeItem) { + m_selectedEdgeItem->setChecked(false); + m_selectedEdgeItem = NULL; + } + edgeItem->setChecked(true); + m_selectedEdgeItem = edgeItem; + emit edgeCheckStateChanged(); + } + } else { + if (m_selectedEdgeItem) { + m_selectedEdgeItem->setChecked(false); + m_selectedEdgeItem = NULL; + emit edgeCheckStateChanged(); + } + } } } - m_lastMousePos = mapToScene(event->pos()); + m_lastMousePos = pos; } void SkeletonEditGraphicsView::mouseDoubleClickEvent(QMouseEvent *event) @@ -195,29 +249,40 @@ void SkeletonEditGraphicsView::mouseReleaseEvent(QMouseEvent *event) return; if (event->button() == Qt::LeftButton) { if (m_inAddNodeMode) { - SkeletonEditNodeItem *masterNode = new SkeletonEditNodeItem(m_pendingNodeItem->rect()); - scene()->addItem(masterNode); - QRectF slaveRect = m_pendingNodeItem->rect(); - float x = m_pendingNodeItem->x() + m_pendingNodeItem->rect().width() / 2; - slaveRect.translate(findXForSlave(x) - x, 0); - SkeletonEditNodeItem *slaveNode = new SkeletonEditNodeItem(slaveRect); - scene()->addItem(slaveNode); - masterNode->setSlave(slaveNode); - slaveNode->setMaster(masterNode); - if (m_nextStartNodeItem) { - SkeletonEditEdgeItem *newEdge = new SkeletonEditEdgeItem(); - newEdge->setNodes(masterNode, m_nextStartNodeItem); - scene()->addItem(newEdge); - masterNode->setGroup(m_nextStartNodeItem->group()); - slaveNode->setGroup(masterNode->group()); + if (m_lastHoverNodeItem && m_nextStartNodeItem && m_lastHoverNodeItem != m_nextStartNodeItem) { + if (!findEdgeItemByNodePair(m_lastHoverNodeItem->master(), m_nextStartNodeItem)) { + SkeletonEditEdgeItem *newEdge = new SkeletonEditEdgeItem(); + newEdge->setNodes(m_nextStartNodeItem, m_lastHoverNodeItem->master()); + scene()->addItem(newEdge); + emit nodesChanged(); + } } else { - QGraphicsItemGroup *group = new QGraphicsItemGroup(); - scene()->addItem(group); - masterNode->setGroup(group); - slaveNode->setGroup(masterNode->group()); + float newNodeX = m_pendingNodeItem->x(); + QRectF newNodeRect = m_pendingNodeItem->rect(); + SkeletonEditNodeItem *masterNode = new SkeletonEditNodeItem(newNodeRect); + scene()->addItem(masterNode); + QRectF slaveRect = newNodeRect; + float x = newNodeX + newNodeRect.width() / 2; + slaveRect.translate(findXForSlave(x) - x, 0); + SkeletonEditNodeItem *slaveNode = new SkeletonEditNodeItem(slaveRect); + scene()->addItem(slaveNode); + masterNode->setSlave(slaveNode); + slaveNode->setMaster(masterNode); + if (m_nextStartNodeItem) { + SkeletonEditEdgeItem *newEdge = new SkeletonEditEdgeItem(); + newEdge->setNodes(masterNode, m_nextStartNodeItem); + scene()->addItem(newEdge); + masterNode->setGroup(m_nextStartNodeItem->group()); + slaveNode->setGroup(masterNode->group()); + } else { + QGraphicsItemGroup *group = new QGraphicsItemGroup(); + scene()->addItem(group); + masterNode->setGroup(group); + slaveNode->setGroup(masterNode->group()); + } + setNextStartNodeItem(masterNode); + emit nodesChanged(); } - setNextStartNodeItem(masterNode); - emit nodesChanged(); } m_isMovingNodeItem = false; } @@ -260,21 +325,14 @@ void SkeletonEditGraphicsView::mouseMoveEvent(QMouseEvent *event) moveTo.y() + m_pendingNodeItem->rect().height() / 2))); } if (!m_isMovingNodeItem) { - if (!m_inAddNodeMode) { - SkeletonEditNodeItem *hoverNodeItem = findNodeItemByPos(pos); - if (hoverNodeItem) { - hoverNodeItem->setHighlighted(true); - } - if (hoverNodeItem != m_lastHoverNodeItem) { - if (m_lastHoverNodeItem) - m_lastHoverNodeItem->setHighlighted(false); - m_lastHoverNodeItem = hoverNodeItem; - } - } else { - if (m_lastHoverNodeItem) { + SkeletonEditNodeItem *hoverNodeItem = findNodeItemByPos(pos); + if (hoverNodeItem) { + hoverNodeItem->setHighlighted(true); + } + if (hoverNodeItem != m_lastHoverNodeItem) { + if (m_lastHoverNodeItem) m_lastHoverNodeItem->setHighlighted(false); - m_lastHoverNodeItem = NULL; - } + m_lastHoverNodeItem = hoverNodeItem; } } QPointF curMousePos = pos; @@ -363,10 +421,8 @@ void SkeletonEditGraphicsView::wheelEvent(QWheelEvent *event) delta = delta < 0 ? -1.0 : 1.0; AddItemRadius(m_pendingNodeItem, delta); if (!m_inAddNodeMode && m_lastHoverNodeItem) { - if (canAddItemRadius(m_lastHoverNodeItem, delta) && - canAddItemRadius(m_lastHoverNodeItem->pair(), delta)) { + if (canAddItemRadius(m_lastHoverNodeItem, delta)) { AddItemRadius(m_lastHoverNodeItem, delta); - AddItemRadius(m_lastHoverNodeItem->pair(), delta); } emit nodesChanged(); } diff --git a/src/skeletoneditgraphicsview.h b/src/skeletoneditgraphicsview.h index b263407c..25f14fb0 100644 --- a/src/skeletoneditgraphicsview.h +++ b/src/skeletoneditgraphicsview.h @@ -15,6 +15,7 @@ signals: void sizeChanged(); void nodesChanged(); void changeTurnaroundTriggered(); + void edgeCheckStateChanged(); public slots: void turnOffAddNodeMode(); void turnOnAddNodeMode(); @@ -45,10 +46,14 @@ private: QPointF m_lastMousePos; bool m_isMovingNodeItem; bool m_backgroundLoaded; + SkeletonEditEdgeItem *m_selectedEdgeItem; private: void toggleAddNodeMode(); void applyAddNodeMode(); SkeletonEditNodeItem *findNodeItemByPos(QPointF pos); + SkeletonEditEdgeItem *findEdgeItemByPos(QPointF pos); + SkeletonEditEdgeItem *findEdgeItemByNodePair(SkeletonEditNodeItem *first, + SkeletonEditNodeItem *second); void setNextStartNodeItem(SkeletonEditNodeItem *item); float findXForSlave(float x); bool canNodeItemMoveTo(SkeletonEditNodeItem *item, QPointF moveTo); diff --git a/src/skeletontomesh.cpp b/src/skeletontomesh.cpp index dcaea2d6..90a1ff0d 100644 --- a/src/skeletontomesh.cpp +++ b/src/skeletontomesh.cpp @@ -4,13 +4,20 @@ #include "skeletoneditedgeitem.h" #include +#define USE_CARVE 1 + +#if USE_CARVE == 1 #if defined(HAVE_CONFIG_H) # include #endif #include #include #include +#endif +#define MAX_VERTICES_PER_FACE 100 + +#if USE_CARVE == 0 // Polygon_mesh_processing/corefinement_mesh_union.cpp // https://doc.cgal.org/latest/Polygon_mesh_processing/Polygon_mesh_processing_2corefinement_mesh_union_8cpp-example.html#a2 // https://doc.cgal.org/latest/Polygon_mesh_processing/Polygon_mesh_processing_2triangulate_faces_example_8cpp-example.html @@ -20,14 +27,10 @@ #include #include -// Modified from https://wiki.qt.io/QThreads_general_usage - typedef CGAL::Exact_predicates_inexact_constructions_kernel K; typedef CGAL::Surface_mesh CgalMesh; namespace PMP = CGAL::Polygon_mesh_processing; -#define MAX_VERTICES_PER_FACE 100 - CgalMesh *makeCgalMeshFromMeshlite(void *meshlite, int meshId) { CgalMesh *mesh = new CgalMesh; @@ -115,6 +118,10 @@ CgalMesh *unionCgalMeshs(CgalMesh *first, CgalMesh *second) return mesh; } +#endif + +#if USE_CARVE == 1 + carve::poly::Polyhedron *makeCarveMeshFromMeshlite(void *meshlite, int meshId) { carve::input::PolyhedronData data; @@ -187,6 +194,8 @@ carve::poly::Polyhedron *unionCarveMeshs(carve::poly::Polyhedron *first, return new carve::poly::Polyhedron(*result); } +#endif + struct NodeItemInfo { int index; @@ -231,6 +240,7 @@ SkeletonToMesh::SkeletonToMesh(SkeletonEditGraphicsView *graphicsView) : node.originZ = nodeItem->slave()->origin().x(); node.bmeshNodeId = -1; node.radius = nodeItem->radius(); + node.thickness = nodeItem->slave()->radius(); info.index = skeletonGroup->nodes.size(); info.neighborCount = 1; @@ -267,8 +277,6 @@ Mesh *SkeletonToMesh::takeResultMesh() return mesh; } -#define USE_CARVE 1 - #if USE_CARVE #define ExternalMesh carve::poly::Polyhedron #define makeExternalMeshFromMeshlite makeCarveMeshFromMeshlite @@ -334,7 +342,8 @@ void SkeletonToMesh::process() float y = (node->originY - top - height / 2) / height; float z = (node->originZ - zLeft - zWidth / 2) / height; float r = node->radius / height; - node->bmeshNodeId = meshlite_bmesh_add_node(context, group->bmeshId, x, y, z, r); + float t = node->thickness / height; + node->bmeshNodeId = meshlite_bmesh_add_node(context, group->bmeshId, x, y, z, r, t); } for (size_t j = 0; j < group->edges.size(); j++) { SkeletonNode *firstNode = &group->nodes[group->edges[j].firstNode]; diff --git a/src/skeletontomesh.h b/src/skeletontomesh.h index de83815c..6023ebdc 100644 --- a/src/skeletontomesh.h +++ b/src/skeletontomesh.h @@ -13,6 +13,7 @@ struct SkeletonNode float originY; float originZ; float radius; + float thickness; int bmeshNodeId; }; diff --git a/src/theme.cpp b/src/theme.cpp index 0cc09a38..4ac91dca 100644 --- a/src/theme.cpp +++ b/src/theme.cpp @@ -7,11 +7,11 @@ QColor Theme::skeletonMasterNodeBorderColor = QColor(0xfc, 0x66, 0x21, 128); QColor Theme::skeletonMasterNodeBorderHighlightColor = QColor(0xfc, 0x66, 0x21); QColor Theme::skeletonMasterNodeFillColor = QColor(0xfc, 0x66, 0x21, 50); -int Theme::skeletonMasterNodeBorderSize = 2; +int Theme::skeletonMasterNodeBorderSize = 7; QColor Theme::skeletonSlaveNodeBorderColor = QColor(0xcc, 0xcc, 0xcc, 64); QColor Theme::skeletonSlaveNodeBorderHighlightColor = QColor(0xcc, 0xcc, 0xcc); QColor Theme::skeletonSlaveNodeFillColor = QColor(0xcc, 0xcc, 0xcc, 25); -int Theme::skeletonSlaveNodeBorderSize = 2; +int Theme::skeletonSlaveNodeBorderSize = 7; QString Theme::tabButtonSelectedStylesheet = "QPushButton { color: #efefef; background-color: #fc6621; border: 0px; padding-top: 2px; padding-bottom: 2px; padding-left: 25px; padding-right: 25px;}"; QString Theme::tabButtonStylesheet = "QPushButton { color: #efefef; background-color: #353535; border: 0px; padding-top: 2px; padding-bottom: 2px; padding-left: 25px; padding-right: 25px;}";