Add thickness parameter for part.

It's showed as mini button on part list widget, this parameter can be use to make fish body like mesh.
master
Jeremy Hu 2018-04-17 21:13:32 +08:00
parent 87e64fc51c
commit ce41e654fb
15 changed files with 182 additions and 26 deletions

View File

@ -81,6 +81,9 @@ HEADERS += src/logbrowser.h
SOURCES += src/logbrowserdialog.cpp
HEADERS += src/logbrowserdialog.h
SOURCES += src/floatnumberwidget.cpp
HEADERS += src/floatnumberwidget.h
SOURCES += src/main.cpp
HEADERS += src/version.h

50
src/floatnumberwidget.cpp Normal file
View File

@ -0,0 +1,50 @@
#include <QtWidgets>
#include "floatnumberwidget.h"
FloatNumberWidget::FloatNumberWidget(QWidget *parent) :
QWidget(parent)
{
m_slider = new QSlider(Qt::Horizontal, this);
m_slider->setRange(0, 100);
m_label = new QLabel(this);
m_label->setAlignment(Qt::AlignCenter);
m_label->setNum(0);
m_label->setFixedWidth(30);
connect(m_slider, &QAbstractSlider::valueChanged, [=](int value) {
float fvalue = value / 100.0;
m_label->setText(QString().sprintf("%.2f", fvalue));
emit valueChanged(fvalue);
});
QBoxLayout *popupLayout = new QHBoxLayout(this);
popupLayout->setMargin(2);
popupLayout->addWidget(m_slider);
popupLayout->addWidget(m_label);
}
void FloatNumberWidget::setRange(float min, float max)
{
m_slider->setRange(min * 100, max * 100);
}
void FloatNumberWidget::increaseValue()
{
m_slider->triggerAction(QSlider::SliderPageStepAdd);
}
void FloatNumberWidget::descreaseValue()
{
m_slider->triggerAction(QSlider::SliderPageStepSub);
}
float FloatNumberWidget::value() const
{
return m_slider->value() / 100.0;
}
void FloatNumberWidget::setValue(float value)
{
m_slider->setValue(value * 100);
}

29
src/floatnumberwidget.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef FLOAT_NUMBER_WIDGET_H
#define FLOAT_NUMBER_WIDGET_H
#include <QToolButton>
QT_FORWARD_DECLARE_CLASS(QLabel)
QT_FORWARD_DECLARE_CLASS(QSlider)
class FloatNumberWidget : public QWidget
{
Q_OBJECT
public:
explicit FloatNumberWidget(QWidget *parent = nullptr);
void setRange(float min, float max);
float value() const;
public slots:
void increaseValue();
void descreaseValue();
void setValue(float value);
signals:
void valueChanged(float value);
private:
QLabel *m_label = nullptr;
QSlider *m_slider = nullptr;
};
#endif

View File

@ -125,6 +125,9 @@ void MeshGenerator::process()
int bmeshId = meshlite_bmesh_create(meshliteContext);
if (subdived)
meshlite_bmesh_set_cut_subdiv_count(meshliteContext, bmeshId, 1);
QString thicknessString = valueOfKeyInMapOrEmpty(part->second, "thickness");
if (!thicknessString.isEmpty())
meshlite_bmesh_set_thickness(meshliteContext, bmeshId, thicknessString.toFloat());
if (MeshGenerator::enableDebug)
meshlite_bmesh_enable_debug(meshliteContext, bmeshId, 1);
partBmeshMap[partIdIt] = bmeshId;

View File

@ -585,6 +585,8 @@ void SkeletonDocument::toSnapshot(SkeletonSnapshot *snapshot, const std::set<QUu
part["disabled"] = partIt.second.disabled ? "true" : "false";
part["xMirrored"] = partIt.second.xMirrored ? "true" : "false";
part["zMirrored"] = partIt.second.zMirrored ? "true" : "false";
if (partIt.second.thicknessAdjusted())
part["thickness"] = QString::number(partIt.second.thickness);
if (!partIt.second.name.isEmpty())
part["name"] = partIt.second.name;
snapshot->parts[part["id"]] = part;
@ -655,6 +657,9 @@ void SkeletonDocument::addFromSnapshot(const SkeletonSnapshot &snapshot)
part.disabled = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "disabled"));
part.xMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "xMirrored"));
part.zMirrored = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "zMirrored"));
const auto &thicknessIt = partKv.second.find("thickness");
if (thicknessIt != partKv.second.end())
part.setThickness(thicknessIt->second.toFloat());
partMap[part.id] = part;
}
for (const auto &nodeKv : snapshot.nodes) {
@ -924,6 +929,18 @@ void SkeletonDocument::setPartZmirrorState(QUuid partId, bool mirrored)
emit skeletonChanged();
}
void SkeletonDocument::setPartThickness(QUuid partId, float thickness)
{
auto part = partMap.find(partId);
if (part == partMap.end()) {
qDebug() << "Part not found:" << partId;
return;
}
part->second.setThickness(thickness);
emit partThicknessChanged(partId);
emit skeletonChanged();
}
void SkeletonDocument::saveSnapshot()
{
if (m_undoItems.size() + 1 > m_maxSnapshot)

View File

@ -7,6 +7,7 @@
#include <set>
#include <deque>
#include <QImage>
#include <cmath>
#include "skeletonsnapshot.h"
#include "mesh.h"
#include "meshgenerator.h"
@ -70,6 +71,7 @@ public:
bool disabled;
bool xMirrored;
bool zMirrored;
float thickness;
QImage preview;
std::vector<QUuid> nodeIds;
SkeletonPart(const QUuid &withId=QUuid()) :
@ -78,10 +80,23 @@ public:
subdived(false),
disabled(false),
xMirrored(false),
zMirrored(false)
zMirrored(false),
thickness(1.0)
{
id = withId.isNull() ? QUuid::createUuid() : withId;
}
void setThickness(float toThickness)
{
if (toThickness < 0)
toThickness = 0;
else if (toThickness > 2)
toThickness = 2;
thickness = toThickness;
}
bool thicknessAdjusted() const
{
return fabs(thickness - 1.0) >= 0.01;
}
bool isEditVisible() const
{
return visible && !disabled;
@ -94,6 +109,7 @@ public:
disabled = other.disabled;
xMirrored = other.xMirrored;
zMirrored = other.zMirrored;
thickness = other.thickness;
}
};
@ -145,6 +161,7 @@ signals:
void partDisableStateChanged(QUuid partId);
void partXmirrorStateChanged(QUuid partId);
void partZmirrorStateChanged(QUuid partId);
void partThicknessChanged(QUuid partId);
void cleanup();
void originChanged();
void xlockStateChanged();
@ -203,6 +220,7 @@ public slots:
void setPartDisableState(QUuid partId, bool disabled);
void setPartXmirrorState(QUuid partId, bool mirrored);
void setPartZmirrorState(QUuid partId, bool mirrored);
void setPartThickness(QUuid partId, float thickness);
void saveSnapshot();
void undo();
void redo();

View File

@ -182,7 +182,7 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() :
m_fileMenu = menuBar()->addMenu(tr("File"));
m_newWindowAction = new QAction(tr("New Window"), this);
connect(m_newWindowAction, &QAction::triggered, this, &SkeletonDocumentWindow::newWindow);
connect(m_newWindowAction, &QAction::triggered, this, &SkeletonDocumentWindow::newWindow, Qt::QueuedConnection);
m_fileMenu->addAction(m_newWindowAction);
m_newDocumentAction = new QAction(tr("New"), this);
@ -190,29 +190,29 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() :
m_fileMenu->addAction(m_newDocumentAction);
m_openAction = new QAction(tr("Open..."), this);
connect(m_openAction, &QAction::triggered, this, &SkeletonDocumentWindow::open);
connect(m_openAction, &QAction::triggered, this, &SkeletonDocumentWindow::open, Qt::QueuedConnection);
m_fileMenu->addAction(m_openAction);
m_saveAction = new QAction(tr("Save"), this);
connect(m_saveAction, &QAction::triggered, this, &SkeletonDocumentWindow::save);
connect(m_saveAction, &QAction::triggered, this, &SkeletonDocumentWindow::save, Qt::QueuedConnection);
m_fileMenu->addAction(m_saveAction);
m_saveAsAction = new QAction(tr("Save As..."), this);
connect(m_saveAsAction, &QAction::triggered, this, &SkeletonDocumentWindow::saveAs);
connect(m_saveAsAction, &QAction::triggered, this, &SkeletonDocumentWindow::saveAs, Qt::QueuedConnection);
m_fileMenu->addAction(m_saveAsAction);
m_saveAllAction = new QAction(tr("Save All"), this);
connect(m_saveAllAction, &QAction::triggered, this, &SkeletonDocumentWindow::saveAll);
connect(m_saveAllAction, &QAction::triggered, this, &SkeletonDocumentWindow::saveAll, Qt::QueuedConnection);
m_fileMenu->addAction(m_saveAllAction);
m_fileMenu->addSeparator();
m_exportAction = new QAction(tr("Export..."), this);
connect(m_exportAction, &QAction::triggered, this, &SkeletonDocumentWindow::exportResult);
connect(m_exportAction, &QAction::triggered, this, &SkeletonDocumentWindow::exportResult, Qt::QueuedConnection);
m_fileMenu->addAction(m_exportAction);
m_changeTurnaroundAction = new QAction(tr("Change Turnaround..."), this);
connect(m_changeTurnaroundAction, &QAction::triggered, this, &SkeletonDocumentWindow::changeTurnaround);
connect(m_changeTurnaroundAction, &QAction::triggered, this, &SkeletonDocumentWindow::changeTurnaround, Qt::QueuedConnection);
m_fileMenu->addAction(m_changeTurnaroundAction);
connect(m_fileMenu, &QMenu::aboutToShow, [=]() {
@ -422,7 +422,7 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() :
connect(m_document, &SkeletonDocument::partSubdivStateChanged, partListWidget, &SkeletonPartListWidget::partSubdivStateChanged);
connect(m_document, &SkeletonDocument::partDisableStateChanged, partListWidget, &SkeletonPartListWidget::partDisableStateChanged);
connect(m_document, &SkeletonDocument::partXmirrorStateChanged, partListWidget, &SkeletonPartListWidget::partXmirrorStateChanged);
connect(m_document, &SkeletonDocument::partZmirrorStateChanged, partListWidget, &SkeletonPartListWidget::partZmirrorStateChanged);
connect(m_document, &SkeletonDocument::partThicknessChanged, partListWidget, &SkeletonPartListWidget::partThicknessChanged);
connect(m_document, &SkeletonDocument::cleanup, partListWidget, &SkeletonPartListWidget::partListChanged);
connect(m_document, &SkeletonDocument::skeletonChanged, m_document, &SkeletonDocument::generateMesh);

View File

@ -1,8 +1,11 @@
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QDebug>
#include <QMenu>
#include <QWidgetAction>
#include "skeletonpartlistwidget.h"
#include "theme.h"
#include "floatnumberwidget.h"
SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid partId) :
m_document(document),
@ -28,9 +31,9 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p
initButton(m_xMirrorButton);
updateXmirrorButton();
m_zMirrorButton = new QPushButton();
initButton(m_zMirrorButton);
updateZmirrorButton();
m_thicknessButton = new QPushButton();
initButton(m_thicknessButton);
updateThicknessButton();
m_previewLabel = new QLabel;
@ -39,15 +42,15 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p
miniTopToolLayout->setContentsMargins(0, 0, 0, 0);
miniTopToolLayout->addWidget(m_visibleButton);
miniTopToolLayout->addWidget(m_lockButton);
miniTopToolLayout->addWidget(m_subdivButton);
miniTopToolLayout->addWidget(m_disableButton);
miniTopToolLayout->addStretch();
QHBoxLayout *miniBottomToolLayout = new QHBoxLayout;
miniBottomToolLayout->setSpacing(0);
miniBottomToolLayout->setContentsMargins(0, 0, 0, 0);
miniBottomToolLayout->addWidget(m_disableButton);
miniBottomToolLayout->addWidget(m_subdivButton);
miniBottomToolLayout->addWidget(m_xMirrorButton);
miniBottomToolLayout->addWidget(m_zMirrorButton);
miniBottomToolLayout->addWidget(m_thicknessButton);
miniBottomToolLayout->addStretch();
QWidget *hrWidget = new QWidget;
@ -71,7 +74,7 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p
connect(this, &SkeletonPartWidget::setPartSubdivState, m_document, &SkeletonDocument::setPartSubdivState);
connect(this, &SkeletonPartWidget::setPartDisableState, m_document, &SkeletonDocument::setPartDisableState);
connect(this, &SkeletonPartWidget::setPartXmirrorState, m_document, &SkeletonDocument::setPartXmirrorState);
connect(this, &SkeletonPartWidget::setPartZmirrorState, m_document, &SkeletonDocument::setPartZmirrorState);
connect(this, &SkeletonPartWidget::setPartThickness, m_document, &SkeletonDocument::setPartThickness);
connect(m_lockButton, &QPushButton::clicked, [=]() {
const SkeletonPart *part = m_document->findPart(m_partId);
@ -118,16 +121,42 @@ SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid p
emit setPartXmirrorState(m_partId, !part->xMirrored);
});
connect(m_zMirrorButton, &QPushButton::clicked, [=]() {
connect(m_thicknessButton, &QPushButton::clicked, [=]() {
const SkeletonPart *part = m_document->findPart(m_partId);
if (!part) {
qDebug() << "Part not found:" << m_partId;
return;
}
emit setPartZmirrorState(m_partId, !part->zMirrored);
showThicknessSettingPopup(mapFromGlobal(QCursor::pos()));
});
}
void SkeletonPartWidget::showThicknessSettingPopup(const QPoint &pos)
{
QMenu popupMenu;
const SkeletonPart *part = m_document->findPart(m_partId);
if (!part) {
qDebug() << "Find part failed:" << m_partId;
return;
}
FloatNumberWidget *popup = new FloatNumberWidget;
popup->setRange(0, 2);
popup->setValue(part->thickness);
connect(popup, &FloatNumberWidget::valueChanged, [=](float value) {
emit setPartThickness(m_partId, value);
});
QWidgetAction *action = new QWidgetAction(this);
action->setDefaultWidget(popup);
popupMenu.addAction(action);
popupMenu.exec(mapToGlobal(pos));
}
void SkeletonPartWidget::initButton(QPushButton *button)
{
button->setFont(Theme::awesome()->font(Theme::miniIconFontSize));
@ -219,17 +248,17 @@ void SkeletonPartWidget::updateXmirrorButton()
updateButton(m_xMirrorButton, QChar(fa::balancescale), false);
}
void SkeletonPartWidget::updateZmirrorButton()
void SkeletonPartWidget::updateThicknessButton()
{
const SkeletonPart *part = m_document->findPart(m_partId);
if (!part) {
qDebug() << "Part not found:" << m_partId;
return;
}
if (part->zMirrored)
updateButton(m_zMirrorButton, QChar(fa::balancescale), true);
if (part->thicknessAdjusted())
updateButton(m_thicknessButton, QChar(fa::handlizardo), true);
else
updateButton(m_zMirrorButton, QChar(fa::balancescale), false);
updateButton(m_thicknessButton, QChar(fa::handlizardo), false);
}
void SkeletonPartWidget::reload()
@ -240,7 +269,7 @@ void SkeletonPartWidget::reload()
updateSubdivButton();
updateDisableButton();
updateXmirrorButton();
updateZmirrorButton();
updateThicknessButton();
}
SkeletonPartListWidget::SkeletonPartListWidget(const SkeletonDocument *document) :
@ -347,7 +376,7 @@ void SkeletonPartListWidget::partXmirrorStateChanged(QUuid partId)
widget->updateXmirrorButton();
}
void SkeletonPartListWidget::partZmirrorStateChanged(QUuid partId)
void SkeletonPartListWidget::partThicknessChanged(QUuid partId)
{
auto item = m_itemMap.find(partId);
if (item == m_itemMap.end()) {
@ -355,5 +384,5 @@ void SkeletonPartListWidget::partZmirrorStateChanged(QUuid partId)
return;
}
SkeletonPartWidget *widget = (SkeletonPartWidget *)itemWidget(item->second);
widget->updateZmirrorButton();
widget->updateThicknessButton();
}

View File

@ -16,6 +16,7 @@ signals:
void setPartDisableState(QUuid partId, bool disabled);
void setPartXmirrorState(QUuid partId, bool mirrored);
void setPartZmirrorState(QUuid partId, bool mirrored);
void setPartThickness(QUuid partId, float thickness);
public:
SkeletonPartWidget(const SkeletonDocument *document, QUuid partId);
void reload();
@ -26,6 +27,9 @@ public:
void updateDisableButton();
void updateXmirrorButton();
void updateZmirrorButton();
void updateThicknessButton();
public slots:
void showThicknessSettingPopup(const QPoint &pos);
private:
const SkeletonDocument *m_document;
QUuid m_partId;
@ -36,6 +40,7 @@ private:
QPushButton *m_disableButton;
QPushButton *m_xMirrorButton;
QPushButton *m_zMirrorButton;
QPushButton *m_thicknessButton;
QLabel *m_nameLabel;
private:
void initButton(QPushButton *button);
@ -56,7 +61,7 @@ public slots:
void partSubdivStateChanged(QUuid partId);
void partDisableStateChanged(QUuid partId);
void partXmirrorStateChanged(QUuid partId);
void partZmirrorStateChanged(QUuid partId);
void partThicknessChanged(QUuid partId);
private:
const SkeletonDocument *m_document;
std::map<QUuid, QListWidgetItem *> m_itemMap;

View File

@ -33,6 +33,7 @@ int meshlite_get_halfedge_normal_array(void *context, int mesh_id, float *buffer
int meshlite_build(void *context, float *vertex_position_buffer, int vertex_count, int *face_index_buffer, int face_index_buffer_len);
int meshlite_bmesh_create(void *context);
int meshlite_bmesh_set_cut_subdiv_count(void *context, int bmesh_id, int subdiv_count);
int meshlite_bmesh_set_thickness(void *context, int bmesh_id, float thickness);
int meshlite_bmesh_enable_debug(void *context, int bmesh_id, int enable);
int meshlite_bmesh_add_node(void *context, int bmesh_id, float x, float y, float z, float radius);
int meshlite_bmesh_add_edge(void *context, int bmesh_id, int first_node_id, int second_node_id);

View File

@ -33,6 +33,7 @@ int meshlite_get_halfedge_normal_array(void *context, int mesh_id, float *buffer
int meshlite_build(void *context, float *vertex_position_buffer, int vertex_count, int *face_index_buffer, int face_index_buffer_len);
int meshlite_bmesh_create(void *context);
int meshlite_bmesh_set_cut_subdiv_count(void *context, int bmesh_id, int subdiv_count);
int meshlite_bmesh_set_thickness(void *context, int bmesh_id, float thickness);
int meshlite_bmesh_enable_debug(void *context, int bmesh_id, int enable);
int meshlite_bmesh_add_node(void *context, int bmesh_id, float x, float y, float z, float radius);
int meshlite_bmesh_add_edge(void *context, int bmesh_id, int first_node_id, int second_node_id);