parent
b99e0db0b2
commit
af451bd5ab
48
dust3d.pro
48
dust3d.pro
|
@ -2,10 +2,9 @@ QT += core widgets opengl
|
||||||
CONFIG += debug
|
CONFIG += debug
|
||||||
RESOURCES += resources.qrc
|
RESOURCES += resources.qrc
|
||||||
|
|
||||||
INCLUDEPATH += src
|
include(thirdparty/QtAwesome/QtAwesome/QtAwesome.pri)
|
||||||
|
|
||||||
SOURCES += src/mainwindow.cpp
|
INCLUDEPATH += src
|
||||||
HEADERS += src/mainwindow.h
|
|
||||||
|
|
||||||
SOURCES += src/modelshaderprogram.cpp
|
SOURCES += src/modelshaderprogram.cpp
|
||||||
HEADERS += src/modelshaderprogram.h
|
HEADERS += src/modelshaderprogram.h
|
||||||
|
@ -13,27 +12,42 @@ HEADERS += src/modelshaderprogram.h
|
||||||
SOURCES += src/modelmeshbinder.cpp
|
SOURCES += src/modelmeshbinder.cpp
|
||||||
HEADERS += src/modelmeshbinder.h
|
HEADERS += src/modelmeshbinder.h
|
||||||
|
|
||||||
|
SOURCES += src/modelofflinerender.cpp
|
||||||
|
HEADERS += src/modelofflinerender.h
|
||||||
|
|
||||||
SOURCES += src/modelwidget.cpp
|
SOURCES += src/modelwidget.cpp
|
||||||
HEADERS += src/modelwidget.h
|
HEADERS += src/modelwidget.h
|
||||||
|
|
||||||
SOURCES += src/skeletoneditgraphicsview.cpp
|
SOURCES += src/skeletonnodepropertywidget.cpp
|
||||||
HEADERS += src/skeletoneditgraphicsview.h
|
HEADERS += src/skeletonnodepropertywidget.h
|
||||||
|
|
||||||
SOURCES += src/skeletoneditnodeitem.cpp
|
SOURCES += src/skeletonedgepropertywidget.cpp
|
||||||
HEADERS += src/skeletoneditnodeitem.h
|
HEADERS += src/skeletonedgepropertywidget.h
|
||||||
|
|
||||||
SOURCES += src/skeletoneditedgeitem.cpp
|
SOURCES += src/skeletondocument.cpp
|
||||||
HEADERS += src/skeletoneditedgeitem.h
|
HEADERS += src/skeletondocument.h
|
||||||
|
|
||||||
SOURCES += src/skeletontomesh.cpp
|
SOURCES += src/skeletondocumentwindow.cpp
|
||||||
HEADERS += src/skeletontomesh.h
|
HEADERS += src/skeletondocumentwindow.h
|
||||||
|
|
||||||
|
SOURCES += src/skeletongraphicswidget.cpp
|
||||||
|
HEADERS += src/skeletongraphicswidget.h
|
||||||
|
|
||||||
|
SOURCES += src/skeletonhistorylistwidget.cpp
|
||||||
|
HEADERS += src/skeletonhistorylistwidget.h
|
||||||
|
|
||||||
|
SOURCES += src/skeletonpartlistwidget.cpp
|
||||||
|
HEADERS += src/skeletonpartlistwidget.h
|
||||||
|
|
||||||
|
SOURCES += src/meshgenerator.cpp
|
||||||
|
HEADERS += src/meshgenerator.h
|
||||||
|
|
||||||
|
SOURCES += src/util.cpp
|
||||||
|
HEADERS += src/util.h
|
||||||
|
|
||||||
SOURCES += src/turnaroundloader.cpp
|
SOURCES += src/turnaroundloader.cpp
|
||||||
HEADERS += src/turnaroundloader.h
|
HEADERS += src/turnaroundloader.h
|
||||||
|
|
||||||
SOURCES += src/skeletonwidget.cpp
|
|
||||||
HEADERS += src/skeletonwidget.h
|
|
||||||
|
|
||||||
SOURCES += src/skeletonsnapshot.cpp
|
SOURCES += src/skeletonsnapshot.cpp
|
||||||
HEADERS += src/skeletonsnapshot.h
|
HEADERS += src/skeletonsnapshot.h
|
||||||
|
|
||||||
|
@ -52,6 +66,12 @@ HEADERS += src/mesh.h
|
||||||
SOURCES += src/unionmesh.cpp
|
SOURCES += src/unionmesh.cpp
|
||||||
HEADERS += src/unionmesh.h
|
HEADERS += src/unionmesh.h
|
||||||
|
|
||||||
|
SOURCES += src/logbrowser.cpp
|
||||||
|
HEADERS += src/logbrowser.h
|
||||||
|
|
||||||
|
SOURCES += src/logbrowserdialog.cpp
|
||||||
|
HEADERS += src/logbrowserdialog.h
|
||||||
|
|
||||||
SOURCES += src/main.cpp
|
SOURCES += src/main.cpp
|
||||||
|
|
||||||
INCLUDEPATH += ../meshlite/include
|
INCLUDEPATH += ../meshlite/include
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
#include "logbrowser.h"
|
||||||
|
// Modified from https://wiki.qt.io/Browser_for_QDebug_output
|
||||||
|
#include <QMetaType>
|
||||||
|
#include "logbrowserdialog.h"
|
||||||
|
|
||||||
|
LogBrowser::LogBrowser(QObject *parent) :
|
||||||
|
QObject(parent)
|
||||||
|
{
|
||||||
|
qRegisterMetaType<QtMsgType>("QtMsgType");
|
||||||
|
m_browserDialog = new LogBrowserDialog;
|
||||||
|
connect(this, SIGNAL(sendMessage(QtMsgType,QString)), m_browserDialog, SLOT(outputMessage(QtMsgType,QString)), Qt::QueuedConnection);
|
||||||
|
m_browserDialog->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
LogBrowser::~LogBrowser()
|
||||||
|
{
|
||||||
|
delete m_browserDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogBrowser::outputMessage(QtMsgType type, const QString &msg)
|
||||||
|
{
|
||||||
|
printf("%s\n", msg.toUtf8().constData());
|
||||||
|
emit sendMessage( type, msg );
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef LOG_BROWSER_H
|
||||||
|
#define LOG_BROWSER_H
|
||||||
|
// Modified from https://wiki.qt.io/Browser_for_QDebug_output
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class LogBrowserDialog;
|
||||||
|
|
||||||
|
class LogBrowser : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit LogBrowser(QObject *parent = 0);
|
||||||
|
~LogBrowser();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void outputMessage(QtMsgType type, const QString &msg);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void sendMessage(QtMsgType type, const QString &msg);
|
||||||
|
|
||||||
|
private:
|
||||||
|
LogBrowserDialog *m_browserDialog;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // LOGBROWSER_H
|
|
@ -0,0 +1,120 @@
|
||||||
|
#include "logbrowserdialog.h"
|
||||||
|
// Modified from https://wiki.qt.io/Browser_for_QDebug_output
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QTextBrowser>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QCloseEvent>
|
||||||
|
#include <QKeyEvent>
|
||||||
|
|
||||||
|
LogBrowserDialog::LogBrowserDialog(QWidget *parent) :
|
||||||
|
QDialog(parent)
|
||||||
|
{
|
||||||
|
QVBoxLayout *layout = new QVBoxLayout;
|
||||||
|
setLayout(layout);
|
||||||
|
|
||||||
|
m_browser = new QTextBrowser(this);
|
||||||
|
layout->addWidget(m_browser);
|
||||||
|
|
||||||
|
QHBoxLayout *buttonLayout = new QHBoxLayout;
|
||||||
|
buttonLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
layout->addLayout(buttonLayout);
|
||||||
|
|
||||||
|
buttonLayout->addStretch(10);
|
||||||
|
|
||||||
|
m_clearButton = new QPushButton(this);
|
||||||
|
m_clearButton->setText("Clear");
|
||||||
|
buttonLayout->addWidget(m_clearButton);
|
||||||
|
connect(m_clearButton, SIGNAL(clicked()), m_browser, SLOT(clear()));
|
||||||
|
|
||||||
|
m_saveButton = new QPushButton(this);
|
||||||
|
m_saveButton->setText("Save Output");
|
||||||
|
buttonLayout->addWidget(m_saveButton);
|
||||||
|
connect(m_saveButton, SIGNAL(clicked()), this, SLOT(save()));
|
||||||
|
|
||||||
|
resize(400, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LogBrowserDialog::~LogBrowserDialog()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LogBrowserDialog::outputMessage(QtMsgType type, const QString &msg)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case QtDebugMsg:
|
||||||
|
m_browser->append(tr("— DEBUG: %1").arg(msg));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QtWarningMsg:
|
||||||
|
m_browser->append(tr("— WARNING: %1").arg(msg));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QtCriticalMsg:
|
||||||
|
m_browser->append(tr("— CRITICAL: %1").arg(msg));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QtInfoMsg:
|
||||||
|
m_browser->append(tr("— INFO: %1").arg(msg));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QtFatalMsg:
|
||||||
|
m_browser->append(tr("— FATAL: %1").arg(msg));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LogBrowserDialog::save()
|
||||||
|
{
|
||||||
|
QString saveFileName = QFileDialog::getSaveFileName(this,
|
||||||
|
tr("Save Log Output"),
|
||||||
|
tr("%1/logfile.txt").arg(QDir::homePath()),
|
||||||
|
tr("Text Files (*.txt);;All Files (*)"));
|
||||||
|
|
||||||
|
if (saveFileName.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QFile file(saveFileName);
|
||||||
|
if (!file.open(QIODevice::WriteOnly)) {
|
||||||
|
QMessageBox::warning(this,
|
||||||
|
tr("Error"),
|
||||||
|
QString(tr("<nobr>File '%1'<br/>cannot be opened for writing.<br/><br/>"
|
||||||
|
"The log output could <b>not</b> be saved!</nobr>"))
|
||||||
|
.arg(saveFileName));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextStream stream(&file);
|
||||||
|
stream << m_browser->toPlainText();
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogBrowserDialog::closeEvent(QCloseEvent *e)
|
||||||
|
{
|
||||||
|
QMessageBox::StandardButton answer = QMessageBox::question(this,
|
||||||
|
tr("Close Log Browser?"),
|
||||||
|
tr("Do you really want to close the log browser?"),
|
||||||
|
QMessageBox::Yes | QMessageBox::No);
|
||||||
|
|
||||||
|
if (answer == QMessageBox::Yes)
|
||||||
|
e->accept();
|
||||||
|
else
|
||||||
|
e->ignore();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogBrowserDialog::keyPressEvent(QKeyEvent *e)
|
||||||
|
{
|
||||||
|
// ignore all keyboard events
|
||||||
|
// protects against accidentally closing of the dialog
|
||||||
|
// without asking the user
|
||||||
|
e->ignore();
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
#ifndef LOG_BROWSER_DIALOG_H
|
||||||
|
#define LOG_BROWSER_DIALOG_H
|
||||||
|
// Modified from https://wiki.qt.io/Browser_for_QDebug_output
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
class QTextBrowser;
|
||||||
|
class QPushButton;
|
||||||
|
|
||||||
|
class LogBrowserDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
LogBrowserDialog(QWidget *parent = 0);
|
||||||
|
~LogBrowserDialog();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void outputMessage(QtMsgType type, const QString &msg);
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void save();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void keyPressEvent(QKeyEvent *e);
|
||||||
|
virtual void closeEvent(QCloseEvent *e);
|
||||||
|
|
||||||
|
QTextBrowser *m_browser;
|
||||||
|
QPushButton *m_clearButton;
|
||||||
|
QPushButton *m_saveButton;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // DIALOG_H
|
25
src/main.cpp
25
src/main.cpp
|
@ -2,8 +2,20 @@
|
||||||
#include <QDesktopWidget>
|
#include <QDesktopWidget>
|
||||||
#include <QStyleFactory>
|
#include <QStyleFactory>
|
||||||
#include <QFontDatabase>
|
#include <QFontDatabase>
|
||||||
#include "mainwindow.h"
|
#include <QPointer>
|
||||||
#include "meshlite.h"
|
#include <QDebug>
|
||||||
|
#include <QtGlobal>
|
||||||
|
#include "logbrowser.h"
|
||||||
|
#include "skeletondocumentwindow.h"
|
||||||
|
#include "theme.h"
|
||||||
|
|
||||||
|
QPointer<LogBrowser> g_logBrowser;
|
||||||
|
|
||||||
|
void outputMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
||||||
|
{
|
||||||
|
if (g_logBrowser)
|
||||||
|
g_logBrowser->outputMessage(type, msg);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char ** argv)
|
int main(int argc, char ** argv)
|
||||||
{
|
{
|
||||||
|
@ -24,8 +36,8 @@ int main(int argc, char ** argv)
|
||||||
darkPalette.setColor(QPalette::ButtonText, QColor(239,239,239));
|
darkPalette.setColor(QPalette::ButtonText, QColor(239,239,239));
|
||||||
darkPalette.setColor(QPalette::BrightText, Qt::red);
|
darkPalette.setColor(QPalette::BrightText, Qt::red);
|
||||||
darkPalette.setColor(QPalette::Link, QColor(42, 130, 218));
|
darkPalette.setColor(QPalette::Link, QColor(42, 130, 218));
|
||||||
darkPalette.setColor(QPalette::Highlight, QColor(252, 102, 33));
|
darkPalette.setColor(QPalette::Highlight, Theme::white);
|
||||||
darkPalette.setColor(QPalette::HighlightedText, Qt::black);
|
darkPalette.setColor(QPalette::HighlightedText, Qt::white);
|
||||||
qApp->setPalette(darkPalette);
|
qApp->setPalette(darkPalette);
|
||||||
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #fc6621; border: 1px solid white; }");
|
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #fc6621; border: 1px solid white; }");
|
||||||
|
|
||||||
|
@ -36,8 +48,11 @@ int main(int argc, char ** argv)
|
||||||
font.setPixelSize(9);
|
font.setPixelSize(9);
|
||||||
font.setBold(false);
|
font.setBold(false);
|
||||||
QApplication::setFont(font);
|
QApplication::setFont(font);
|
||||||
|
|
||||||
|
g_logBrowser = new LogBrowser;
|
||||||
|
qInstallMessageHandler(&outputMessage);
|
||||||
|
|
||||||
MainWindow mainWindow;
|
SkeletonDocumentWindow mainWindow;
|
||||||
mainWindow.showMaximized();
|
mainWindow.showMaximized();
|
||||||
return app.exec();
|
return app.exec();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,230 @@
|
||||||
|
#include <vector>
|
||||||
|
#include <QGuiApplication>
|
||||||
|
#include "meshgenerator.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "skeletondocument.h"
|
||||||
|
#include "meshlite.h"
|
||||||
|
#include "modelofflinerender.h"
|
||||||
|
#include "unionmesh.h"
|
||||||
|
#include "theme.h"
|
||||||
|
|
||||||
|
MeshGenerator::MeshGenerator(SkeletonSnapshot *snapshot, QThread *thread) :
|
||||||
|
m_snapshot(snapshot),
|
||||||
|
m_mesh(nullptr),
|
||||||
|
m_preview(nullptr),
|
||||||
|
m_requirePreview(false),
|
||||||
|
m_previewRender(nullptr),
|
||||||
|
m_thread(thread)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
MeshGenerator::~MeshGenerator()
|
||||||
|
{
|
||||||
|
delete m_snapshot;
|
||||||
|
delete m_mesh;
|
||||||
|
delete m_preview;
|
||||||
|
for (const auto &partPreviewIt: m_partPreviewMap) {
|
||||||
|
delete partPreviewIt.second;
|
||||||
|
}
|
||||||
|
for (const auto &render: m_partPreviewRenderMap) {
|
||||||
|
delete render.second;
|
||||||
|
}
|
||||||
|
delete m_previewRender;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshGenerator::addPreviewRequirement()
|
||||||
|
{
|
||||||
|
m_requirePreview = true;
|
||||||
|
if (nullptr == m_previewRender) {
|
||||||
|
m_previewRender = new ModelOfflineRender;
|
||||||
|
m_previewRender->setRenderThread(m_thread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshGenerator::addPartPreviewRequirement(const QString &partId)
|
||||||
|
{
|
||||||
|
qDebug() << "addPartPreviewRequirement:" << partId;
|
||||||
|
m_requirePartPreviewMap.insert(partId);
|
||||||
|
if (m_partPreviewRenderMap.find(partId) == m_partPreviewRenderMap.end()) {
|
||||||
|
ModelOfflineRender *render = new ModelOfflineRender;
|
||||||
|
render->setRenderThread(m_thread);
|
||||||
|
m_partPreviewRenderMap[partId] = render;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Mesh *MeshGenerator::takeResultMesh()
|
||||||
|
{
|
||||||
|
Mesh *resultMesh = m_mesh;
|
||||||
|
m_mesh = nullptr;
|
||||||
|
return resultMesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage *MeshGenerator::takePreview()
|
||||||
|
{
|
||||||
|
QImage *resultPreview = m_preview;
|
||||||
|
m_preview = nullptr;
|
||||||
|
return resultPreview;
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage *MeshGenerator::takePartPreview(const QString &partId)
|
||||||
|
{
|
||||||
|
QImage *resultImage = m_partPreviewMap[partId];
|
||||||
|
m_partPreviewMap[partId] = nullptr;
|
||||||
|
return resultImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshGenerator::resolveBoundingBox(QRectF *mainProfile, QRectF *sideProfile)
|
||||||
|
{
|
||||||
|
float left = -1;
|
||||||
|
float right = -1;
|
||||||
|
float top = -1;
|
||||||
|
float bottom = -1;
|
||||||
|
float zLeft = -1;
|
||||||
|
float zRight = -1;
|
||||||
|
for (const auto &nodeIt: m_snapshot->nodes) {
|
||||||
|
float radius = valueOfKeyInMapOrEmpty(nodeIt.second, "radius").toFloat();
|
||||||
|
float x = valueOfKeyInMapOrEmpty(nodeIt.second, "x").toFloat();
|
||||||
|
float y = valueOfKeyInMapOrEmpty(nodeIt.second, "y").toFloat();
|
||||||
|
float z = valueOfKeyInMapOrEmpty(nodeIt.second, "z").toFloat();
|
||||||
|
if (left < 0 || x - radius < left) {
|
||||||
|
left = x - radius;
|
||||||
|
}
|
||||||
|
if (top < 0 || y - radius < top) {
|
||||||
|
top = y - radius;
|
||||||
|
}
|
||||||
|
if (x + radius > right) {
|
||||||
|
right = x + radius;
|
||||||
|
}
|
||||||
|
if (y + radius > bottom) {
|
||||||
|
bottom = y + radius;
|
||||||
|
}
|
||||||
|
if (zLeft < 0 || z - radius < zLeft) {
|
||||||
|
zLeft = z - radius;
|
||||||
|
}
|
||||||
|
if (z + radius > zRight) {
|
||||||
|
zRight = z + radius;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*mainProfile = QRectF(left, top, right - left, bottom - top);
|
||||||
|
*sideProfile = QRectF(zLeft, top, zRight - zLeft, bottom - top);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MeshGenerator::process()
|
||||||
|
{
|
||||||
|
if (nullptr == m_snapshot)
|
||||||
|
return;
|
||||||
|
|
||||||
|
void *meshliteContext = meshlite_create_context();
|
||||||
|
std::map<QString, int> partBmeshMap;
|
||||||
|
std::map<QString, int> bmeshNodeMap;
|
||||||
|
|
||||||
|
QRectF mainProfile, sideProfile;
|
||||||
|
resolveBoundingBox(&mainProfile, &sideProfile);
|
||||||
|
float longHeight = mainProfile.height();
|
||||||
|
if (mainProfile.width() > longHeight)
|
||||||
|
longHeight = mainProfile.width();
|
||||||
|
if (sideProfile.width() > longHeight)
|
||||||
|
longHeight = sideProfile.width();
|
||||||
|
float mainProfileMiddleX = mainProfile.x() + mainProfile.width() / 2;
|
||||||
|
float sideProfileMiddleX = sideProfile.x() + sideProfile.width() / 2;
|
||||||
|
float mainProfileMiddleY = mainProfile.y() + mainProfile.height() / 2;
|
||||||
|
|
||||||
|
for (const auto &partIdIt: m_snapshot->partIdList) {
|
||||||
|
int bmeshId = meshlite_bmesh_create(meshliteContext);
|
||||||
|
partBmeshMap[partIdIt] = bmeshId;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &edgeIt: m_snapshot->edges) {
|
||||||
|
QString partId = valueOfKeyInMapOrEmpty(edgeIt.second, "partId");
|
||||||
|
QString fromNodeId = valueOfKeyInMapOrEmpty(edgeIt.second, "from");
|
||||||
|
QString toNodeId = valueOfKeyInMapOrEmpty(edgeIt.second, "to");
|
||||||
|
qDebug() << "Processing edge " << fromNodeId << "<=>" << toNodeId;
|
||||||
|
const auto fromIt = m_snapshot->nodes.find(fromNodeId);
|
||||||
|
const auto toIt = m_snapshot->nodes.find(toNodeId);
|
||||||
|
if (fromIt == m_snapshot->nodes.end() || toIt == m_snapshot->nodes.end())
|
||||||
|
continue;
|
||||||
|
const auto partBmeshIt = partBmeshMap.find(partId);
|
||||||
|
if (partBmeshIt == partBmeshMap.end())
|
||||||
|
continue;
|
||||||
|
int bmeshId = partBmeshIt->second;
|
||||||
|
|
||||||
|
int bmeshFromNodeId = 0;
|
||||||
|
const auto bmeshFromIt = bmeshNodeMap.find(fromNodeId);
|
||||||
|
if (bmeshFromIt == bmeshNodeMap.end()) {
|
||||||
|
float radius = valueOfKeyInMapOrEmpty(fromIt->second, "radius").toFloat() / longHeight;
|
||||||
|
float x = (valueOfKeyInMapOrEmpty(fromIt->second, "x").toFloat() - mainProfileMiddleX) / longHeight;
|
||||||
|
float y = (valueOfKeyInMapOrEmpty(fromIt->second, "y").toFloat() - mainProfileMiddleY) / longHeight;
|
||||||
|
float z = (valueOfKeyInMapOrEmpty(fromIt->second, "z").toFloat() - sideProfileMiddleX) / longHeight;
|
||||||
|
bmeshFromNodeId = meshlite_bmesh_add_node(meshliteContext, bmeshId, x, y, z, radius);
|
||||||
|
qDebug() << "bmeshId[" << bmeshId << "] add node[" << bmeshFromNodeId << "]" << radius << x << y << z;
|
||||||
|
bmeshNodeMap[fromNodeId] = bmeshFromNodeId;
|
||||||
|
} else {
|
||||||
|
bmeshFromNodeId = bmeshFromIt->second;
|
||||||
|
qDebug() << "bmeshId[" << bmeshId << "] use existed node[" << bmeshFromNodeId << "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
int bmeshToNodeId = 0;
|
||||||
|
const auto bmeshToIt = bmeshNodeMap.find(toNodeId);
|
||||||
|
if (bmeshToIt == bmeshNodeMap.end()) {
|
||||||
|
float radius = valueOfKeyInMapOrEmpty(toIt->second, "radius").toFloat() / longHeight;
|
||||||
|
float x = (valueOfKeyInMapOrEmpty(toIt->second, "x").toFloat() - mainProfileMiddleX) / longHeight;
|
||||||
|
float y = (valueOfKeyInMapOrEmpty(toIt->second, "y").toFloat() - mainProfileMiddleY) / longHeight;
|
||||||
|
float z = (valueOfKeyInMapOrEmpty(toIt->second, "z").toFloat() - sideProfileMiddleX) / longHeight;
|
||||||
|
bmeshToNodeId = meshlite_bmesh_add_node(meshliteContext, bmeshId, x, y, z, radius);
|
||||||
|
qDebug() << "bmeshId[" << bmeshId << "] add node[" << bmeshToNodeId << "]" << radius << x << y << z;
|
||||||
|
bmeshNodeMap[toNodeId] = bmeshToNodeId;
|
||||||
|
} else {
|
||||||
|
bmeshToNodeId = bmeshToIt->second;
|
||||||
|
qDebug() << "bmeshId[" << bmeshId << "] use existed node[" << bmeshToNodeId << "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
meshlite_bmesh_add_edge(meshliteContext, bmeshId, bmeshFromNodeId, bmeshToNodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<QString, int> partMeshMap;
|
||||||
|
std::vector<int> meshIds;
|
||||||
|
for (const auto &partIdIt: m_snapshot->partIdList) {
|
||||||
|
int bmeshId = partBmeshMap[partIdIt];
|
||||||
|
int meshId = meshlite_bmesh_generate_mesh(meshliteContext, bmeshId, 0);
|
||||||
|
qDebug() << "m_requirePartPreviewMap.find:" << partIdIt;
|
||||||
|
if (m_requirePartPreviewMap.find(partIdIt) != m_requirePartPreviewMap.end()) {
|
||||||
|
ModelOfflineRender *render = m_partPreviewRenderMap[partIdIt];
|
||||||
|
render->updateMesh(new Mesh(meshliteContext, meshId));
|
||||||
|
QImage *image = new QImage(render->toImage(QSize(Theme::previewImageSize, Theme::previewImageSize)));
|
||||||
|
m_partPreviewMap[partIdIt] = image;
|
||||||
|
}
|
||||||
|
int triangulatedMeshId = meshlite_triangulate(meshliteContext, meshId);
|
||||||
|
meshIds.push_back(triangulatedMeshId);
|
||||||
|
}
|
||||||
|
|
||||||
|
int mergedMeshId = 0;
|
||||||
|
if (meshIds.size() > 0) {
|
||||||
|
mergedMeshId = unionMeshs(meshliteContext, meshIds);
|
||||||
|
if (mergedMeshId > 0) {
|
||||||
|
mergedMeshId = meshlite_combine_coplanar_faces(meshliteContext, mergedMeshId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mergedMeshId > 0) {
|
||||||
|
if (m_requirePreview) {
|
||||||
|
m_previewRender->updateMesh(new Mesh(meshliteContext, mergedMeshId));
|
||||||
|
QImage *image = new QImage(m_previewRender->toImage(QSize(Theme::previewImageSize, Theme::previewImageSize)));
|
||||||
|
m_preview = image;
|
||||||
|
}
|
||||||
|
m_mesh = new Mesh(meshliteContext, mergedMeshId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_previewRender) {
|
||||||
|
m_previewRender->setRenderThread(QGuiApplication::instance()->thread());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &partPreviewRender: m_partPreviewRenderMap) {
|
||||||
|
partPreviewRender.second->setRenderThread(QGuiApplication::instance()->thread());
|
||||||
|
}
|
||||||
|
|
||||||
|
meshlite_destroy_context(meshliteContext);
|
||||||
|
|
||||||
|
this->moveToThread(QGuiApplication::instance()->thread());
|
||||||
|
|
||||||
|
emit finished();
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
#ifndef MESH_GENERATOR_H
|
||||||
|
#define MESH_GENERATOR_H
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
#include <QImage>
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
#include <QThread>
|
||||||
|
#include "skeletonsnapshot.h"
|
||||||
|
#include "mesh.h"
|
||||||
|
#include "modelofflinerender.h"
|
||||||
|
|
||||||
|
class MeshGenerator : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
MeshGenerator(SkeletonSnapshot *snapshot, QThread *thread);
|
||||||
|
~MeshGenerator();
|
||||||
|
void addPreviewRequirement();
|
||||||
|
void addPartPreviewRequirement(const QString &partId);
|
||||||
|
Mesh *takeResultMesh();
|
||||||
|
QImage *takePreview();
|
||||||
|
QImage *takePartPreview(const QString &partId);
|
||||||
|
signals:
|
||||||
|
void finished();
|
||||||
|
public slots:
|
||||||
|
void process();
|
||||||
|
private:
|
||||||
|
SkeletonSnapshot *m_snapshot;
|
||||||
|
Mesh *m_mesh;
|
||||||
|
QImage *m_preview;
|
||||||
|
std::map<QString, QImage *> m_partPreviewMap;
|
||||||
|
bool m_requirePreview;
|
||||||
|
std::set<QString> m_requirePartPreviewMap;
|
||||||
|
ModelOfflineRender *m_previewRender;
|
||||||
|
std::map<QString, ModelOfflineRender *> m_partPreviewRenderMap;
|
||||||
|
QThread *m_thread;
|
||||||
|
private:
|
||||||
|
void resolveBoundingBox(QRectF *mainProfile, QRectF *sideProfile);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,107 @@
|
||||||
|
#include <QOpenGLFramebufferObjectFormat>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QDebug>
|
||||||
|
#include "modelofflinerender.h"
|
||||||
|
|
||||||
|
ModelOfflineRender::ModelOfflineRender(QScreen *targetScreen) :
|
||||||
|
QOffscreenSurface(targetScreen),
|
||||||
|
m_context(nullptr),
|
||||||
|
m_mesh(nullptr)
|
||||||
|
{
|
||||||
|
create();
|
||||||
|
m_context = new QOpenGLContext();
|
||||||
|
m_context->setFormat(format());
|
||||||
|
m_context->create();
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelOfflineRender::~ModelOfflineRender()
|
||||||
|
{
|
||||||
|
delete m_context;
|
||||||
|
m_context = nullptr;
|
||||||
|
destroy();
|
||||||
|
delete m_mesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModelOfflineRender::updateMesh(Mesh *mesh)
|
||||||
|
{
|
||||||
|
delete m_mesh;
|
||||||
|
m_mesh = mesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModelOfflineRender::setRenderThread(QThread *thread)
|
||||||
|
{
|
||||||
|
m_context->moveToThread(thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage ModelOfflineRender::toImage(const QSize &size)
|
||||||
|
{
|
||||||
|
QImage image;
|
||||||
|
|
||||||
|
m_context->makeCurrent(this);
|
||||||
|
|
||||||
|
QOpenGLFramebufferObjectFormat format;
|
||||||
|
format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
|
||||||
|
QOpenGLFramebufferObject *renderFbo = new QOpenGLFramebufferObject(size, format);
|
||||||
|
renderFbo->bind();
|
||||||
|
m_context->functions()->glViewport(0, 0, size.width(), size.height());
|
||||||
|
|
||||||
|
if (nullptr != m_mesh) {
|
||||||
|
int xRot = 0;
|
||||||
|
int yRot = 0;
|
||||||
|
int zRot = 0;
|
||||||
|
QMatrix4x4 proj;
|
||||||
|
QMatrix4x4 camera;
|
||||||
|
QMatrix4x4 world;
|
||||||
|
|
||||||
|
ModelShaderProgram *program = new ModelShaderProgram;
|
||||||
|
ModelMeshBinder meshBinder;
|
||||||
|
meshBinder.initialize();
|
||||||
|
|
||||||
|
program->setUniformValue(program->lightPosLoc(), QVector3D(0, 0, 70));
|
||||||
|
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
glEnable(GL_CULL_FACE);
|
||||||
|
glEnable(GL_LINE_SMOOTH);
|
||||||
|
|
||||||
|
camera.setToIdentity();
|
||||||
|
camera.translate(0, 0, -2.5);
|
||||||
|
|
||||||
|
world.setToIdentity();
|
||||||
|
world.rotate(180.0f - (xRot / 16.0f), 1, 0, 0);
|
||||||
|
world.rotate(yRot / 16.0f, 0, 1, 0);
|
||||||
|
world.rotate(zRot / 16.0f, 0, 0, 1);
|
||||||
|
|
||||||
|
proj.setToIdentity();
|
||||||
|
proj.perspective(45.0f, GLfloat(size.width()) / size.height(), 0.01f, 100.0f);
|
||||||
|
|
||||||
|
program->bind();
|
||||||
|
program->setUniformValue(program->projMatrixLoc(), proj);
|
||||||
|
program->setUniformValue(program->mvMatrixLoc(), camera * world);
|
||||||
|
QMatrix3x3 normalMatrix = world.normalMatrix();
|
||||||
|
program->setUniformValue(program->normalMatrixLoc(), normalMatrix);
|
||||||
|
|
||||||
|
meshBinder.updateMesh(m_mesh);
|
||||||
|
meshBinder.paint();
|
||||||
|
|
||||||
|
meshBinder.cleanup();
|
||||||
|
|
||||||
|
program->release();
|
||||||
|
delete program;
|
||||||
|
|
||||||
|
m_mesh = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_context->functions()->glFlush();
|
||||||
|
|
||||||
|
image = renderFbo->toImage();
|
||||||
|
|
||||||
|
qDebug() << "Generated image size:" << image.size();
|
||||||
|
|
||||||
|
renderFbo->bindDefault();
|
||||||
|
delete renderFbo;
|
||||||
|
|
||||||
|
m_context->doneCurrent();
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
#ifndef MODEL_OFFLINE_RENDER_H
|
||||||
|
#define MODEL_OFFLINE_RENDER_H
|
||||||
|
#include <QOffscreenSurface>
|
||||||
|
#include <QScreen>
|
||||||
|
#include <QOpenGLFunctions>
|
||||||
|
#include <QOpenGLContext>
|
||||||
|
#include <QImage>
|
||||||
|
#include <QThread>
|
||||||
|
#include "modelshaderprogram.h"
|
||||||
|
#include "modelmeshbinder.h"
|
||||||
|
#include "mesh.h"
|
||||||
|
|
||||||
|
class ModelOfflineRender : QOffscreenSurface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ModelOfflineRender(QScreen *targetScreen = Q_NULLPTR);
|
||||||
|
~ModelOfflineRender();
|
||||||
|
void setRenderThread(QThread *thread);
|
||||||
|
void updateMesh(Mesh *mesh);
|
||||||
|
QImage toImage(const QSize &size);
|
||||||
|
private:
|
||||||
|
QOpenGLContext *m_context;
|
||||||
|
Mesh *m_mesh;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,6 +1,6 @@
|
||||||
#include "modelwidget.h"
|
#include "modelwidget.h"
|
||||||
#include "ds3file.h"
|
#include "ds3file.h"
|
||||||
#include "skeletoneditgraphicsview.h"
|
#include "skeletongraphicswidget.h"
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
#include <QOpenGLShaderProgram>
|
#include <QOpenGLShaderProgram>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
@ -16,9 +16,9 @@ ModelWidget::ModelWidget(QWidget *parent)
|
||||||
m_xRot(0),
|
m_xRot(0),
|
||||||
m_yRot(0),
|
m_yRot(0),
|
||||||
m_zRot(0),
|
m_zRot(0),
|
||||||
m_program(NULL),
|
m_program(nullptr),
|
||||||
m_moveStarted(false),
|
m_moveStarted(false),
|
||||||
m_graphicsView(NULL)
|
m_graphicsFunctions(NULL)
|
||||||
{
|
{
|
||||||
// --transparent causes the clear color to be transparent. Therefore, on systems that
|
// --transparent causes the clear color to be transparent. Therefore, on systems that
|
||||||
// support it, the widget will become transparent apart from the logo.
|
// support it, the widget will become transparent apart from the logo.
|
||||||
|
@ -34,11 +34,12 @@ ModelWidget::ModelWidget(QWidget *parent)
|
||||||
fmt.setSamples(4);
|
fmt.setSamples(4);
|
||||||
setFormat(fmt);
|
setFormat(fmt);
|
||||||
}
|
}
|
||||||
|
setMouseTracking(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelWidget::setGraphicsView(SkeletonEditGraphicsView *view)
|
void ModelWidget::setGraphicsFunctions(SkeletonGraphicsFunctions *graphicsFunctions)
|
||||||
{
|
{
|
||||||
m_graphicsView = view;
|
m_graphicsFunctions = graphicsFunctions;
|
||||||
}
|
}
|
||||||
|
|
||||||
ModelWidget::~ModelWidget()
|
ModelWidget::~ModelWidget()
|
||||||
|
@ -91,7 +92,7 @@ void ModelWidget::cleanup()
|
||||||
makeCurrent();
|
makeCurrent();
|
||||||
m_meshBinder.cleanup();
|
m_meshBinder.cleanup();
|
||||||
delete m_program;
|
delete m_program;
|
||||||
m_program = 0;
|
m_program = nullptr;
|
||||||
doneCurrent();
|
doneCurrent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +125,7 @@ void ModelWidget::initializeGL()
|
||||||
|
|
||||||
// Our camera never changes in this example.
|
// Our camera never changes in this example.
|
||||||
m_camera.setToIdentity();
|
m_camera.setToIdentity();
|
||||||
m_camera.translate(0, 0, -2.5);
|
m_camera.translate(0, 0, -3.5);
|
||||||
|
|
||||||
// Light position is fixed.
|
// Light position is fixed.
|
||||||
m_program->setUniformValue(m_program->lightPosLoc(), QVector3D(0, 0, 70));
|
m_program->setUniformValue(m_program->lightPosLoc(), QVector3D(0, 0, 70));
|
||||||
|
@ -164,10 +165,9 @@ void ModelWidget::resizeGL(int w, int h)
|
||||||
|
|
||||||
void ModelWidget::mousePressEvent(QMouseEvent *event)
|
void ModelWidget::mousePressEvent(QMouseEvent *event)
|
||||||
{
|
{
|
||||||
if (!m_moveStarted && m_graphicsView && m_graphicsView->mousePress(event, m_graphicsView->mapToScene(m_graphicsView->mapFromGlobal(event->globalPos()))))
|
if (!m_moveStarted && m_graphicsFunctions && m_graphicsFunctions->mousePress(event))
|
||||||
return;
|
return;
|
||||||
m_lastPos = event->pos();
|
m_lastPos = event->pos();
|
||||||
setMouseTracking(true);
|
|
||||||
if (event->button() == Qt::LeftButton) {
|
if (event->button() == Qt::LeftButton) {
|
||||||
if (!m_moveStarted) {
|
if (!m_moveStarted) {
|
||||||
m_moveStartPos = mapToParent(event->pos());
|
m_moveStartPos = mapToParent(event->pos());
|
||||||
|
@ -179,7 +179,7 @@ void ModelWidget::mousePressEvent(QMouseEvent *event)
|
||||||
|
|
||||||
void ModelWidget::mouseReleaseEvent(QMouseEvent *event)
|
void ModelWidget::mouseReleaseEvent(QMouseEvent *event)
|
||||||
{
|
{
|
||||||
if (!m_moveStarted && m_graphicsView && m_graphicsView->mouseRelease(event, m_graphicsView->mapToScene(m_graphicsView->mapFromGlobal(event->globalPos()))))
|
if (!m_moveStarted && m_graphicsFunctions && m_graphicsFunctions->mouseRelease(event))
|
||||||
return;
|
return;
|
||||||
if (m_moveStarted) {
|
if (m_moveStarted) {
|
||||||
m_moveStarted = false;
|
m_moveStarted = false;
|
||||||
|
@ -188,7 +188,7 @@ void ModelWidget::mouseReleaseEvent(QMouseEvent *event)
|
||||||
|
|
||||||
void ModelWidget::mouseMoveEvent(QMouseEvent *event)
|
void ModelWidget::mouseMoveEvent(QMouseEvent *event)
|
||||||
{
|
{
|
||||||
if (!m_moveStarted && m_graphicsView && m_graphicsView->mouseMove(event, m_graphicsView->mapToScene(m_graphicsView->mapFromGlobal(event->globalPos()))))
|
if (!m_moveStarted && m_graphicsFunctions && m_graphicsFunctions->mouseMove(event))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int dx = event->x() - m_lastPos.x();
|
int dx = event->x() - m_lastPos.x();
|
||||||
|
@ -210,7 +210,7 @@ void ModelWidget::mouseMoveEvent(QMouseEvent *event)
|
||||||
|
|
||||||
void ModelWidget::wheelEvent(QWheelEvent *event)
|
void ModelWidget::wheelEvent(QWheelEvent *event)
|
||||||
{
|
{
|
||||||
if (!m_moveStarted && m_graphicsView && m_graphicsView->wheel(event, m_graphicsView->mapToScene(m_graphicsView->mapFromGlobal(event->globalPos()))))
|
if (!m_moveStarted && m_graphicsFunctions && m_graphicsFunctions->wheel(event))
|
||||||
return;
|
return;
|
||||||
if (m_moveStarted)
|
if (m_moveStarted)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
#include "modelshaderprogram.h"
|
#include "modelshaderprogram.h"
|
||||||
#include "modelmeshbinder.h"
|
#include "modelmeshbinder.h"
|
||||||
|
|
||||||
class SkeletonEditGraphicsView;
|
class SkeletonGraphicsFunctions;
|
||||||
|
|
||||||
class ModelWidget : public QOpenGLWidget, protected QOpenGLFunctions
|
class ModelWidget : public QOpenGLWidget, protected QOpenGLFunctions
|
||||||
{
|
{
|
||||||
|
@ -26,7 +26,7 @@ public:
|
||||||
|
|
||||||
void updateMesh(Mesh *mesh);
|
void updateMesh(Mesh *mesh);
|
||||||
void exportMeshAsObj(const QString &filename);
|
void exportMeshAsObj(const QString &filename);
|
||||||
void setGraphicsView(SkeletonEditGraphicsView *view);
|
void setGraphicsFunctions(SkeletonGraphicsFunctions *graphicsFunctions);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setXRotation(int angle);
|
void setXRotation(int angle);
|
||||||
|
@ -61,7 +61,7 @@ private:
|
||||||
bool m_moveStarted;
|
bool m_moveStarted;
|
||||||
QPoint m_moveStartPos;
|
QPoint m_moveStartPos;
|
||||||
QRect m_moveStartGeometry;
|
QRect m_moveStartGeometry;
|
||||||
SkeletonEditGraphicsView *m_graphicsView;
|
SkeletonGraphicsFunctions *m_graphicsFunctions;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,706 @@
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QGuiApplication>
|
||||||
|
#include "skeletondocument.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
SkeletonDocument::SkeletonDocument() :
|
||||||
|
m_resultMeshIsObsolete(false),
|
||||||
|
m_resultMesh(nullptr),
|
||||||
|
m_meshGenerator(nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SkeletonDocument::~SkeletonDocument()
|
||||||
|
{
|
||||||
|
delete m_resultMesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::uiReady()
|
||||||
|
{
|
||||||
|
emit editModeChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::removePart(QUuid partId)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::showPart(QUuid partId)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::hidePart(QUuid partId)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::removeEdge(QUuid edgeId)
|
||||||
|
{
|
||||||
|
const SkeletonEdge *edge = findEdge(edgeId);
|
||||||
|
if (nullptr == edge) {
|
||||||
|
qDebug() << "Find edge failed:" << edgeId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const SkeletonPart *oldPart = findPart(edge->partId);
|
||||||
|
if (nullptr == oldPart) {
|
||||||
|
qDebug() << "Find part failed:" << edge->partId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QString nextPartName = oldPart->name;
|
||||||
|
QUuid oldPartId = oldPart->id;
|
||||||
|
std::vector<std::vector<QUuid>> groups;
|
||||||
|
splitPartByEdge(&groups, edgeId);
|
||||||
|
for (auto groupIt = groups.begin(); groupIt != groups.end(); groupIt++) {
|
||||||
|
SkeletonPart part;
|
||||||
|
part.name = nextPartName;
|
||||||
|
for (auto nodeIdIt = (*groupIt).begin(); nodeIdIt != (*groupIt).end(); nodeIdIt++) {
|
||||||
|
auto nodeIt = nodeMap.find(*nodeIdIt);
|
||||||
|
if (nodeIt == nodeMap.end()) {
|
||||||
|
qDebug() << "Find node failed:" << *nodeIdIt;
|
||||||
|
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()) {
|
||||||
|
qDebug() << "Find edge failed:" << *edgeIdIt;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
edgeIt->second.partId = part.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
partMap[part.id] = part;
|
||||||
|
partIds.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()) {
|
||||||
|
qDebug() << "Find node failed:" << *nodeIdIt;
|
||||||
|
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);
|
||||||
|
partIds.erase(std::remove(partIds.begin(), partIds.end(), oldPartId), partIds.end());
|
||||||
|
partMap.erase(oldPartId);
|
||||||
|
emit partRemoved(oldPartId);
|
||||||
|
emit partListChanged();
|
||||||
|
|
||||||
|
emit skeletonChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::removeNode(QUuid nodeId)
|
||||||
|
{
|
||||||
|
const SkeletonNode *node = findNode(nodeId);
|
||||||
|
if (nullptr == node) {
|
||||||
|
qDebug() << "Find node failed:" << nodeId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const SkeletonPart *oldPart = findPart(node->partId);
|
||||||
|
if (nullptr == oldPart) {
|
||||||
|
qDebug() << "Find part failed:" << node->partId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QString nextPartName = oldPart->name;
|
||||||
|
QUuid oldPartId = oldPart->id;
|
||||||
|
std::vector<std::vector<QUuid>> groups;
|
||||||
|
splitPartByNode(&groups, nodeId);
|
||||||
|
for (auto groupIt = groups.begin(); groupIt != groups.end(); groupIt++) {
|
||||||
|
SkeletonPart part;
|
||||||
|
part.name = nextPartName;
|
||||||
|
for (auto nodeIdIt = (*groupIt).begin(); nodeIdIt != (*groupIt).end(); nodeIdIt++) {
|
||||||
|
auto nodeIt = nodeMap.find(*nodeIdIt);
|
||||||
|
if (nodeIt == nodeMap.end()) {
|
||||||
|
qDebug() << "Find node failed:" << *nodeIdIt;
|
||||||
|
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()) {
|
||||||
|
qDebug() << "Find edge failed:" << *edgeIdIt;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
edgeIt->second.partId = part.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
partMap[part.id] = part;
|
||||||
|
partIds.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()) {
|
||||||
|
qDebug() << "Find edge failed:" << *edgeIdIt;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
QUuid neighborId = edgeIt->second.neighborOf(nodeId);
|
||||||
|
auto nodeIt = nodeMap.find(neighborId);
|
||||||
|
if (nodeIt == nodeMap.end()) {
|
||||||
|
qDebug() << "Find node failed:" << neighborId;
|
||||||
|
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);
|
||||||
|
partIds.erase(std::remove(partIds.begin(), partIds.end(), oldPartId), partIds.end());
|
||||||
|
partMap.erase(oldPartId);
|
||||||
|
emit partRemoved(oldPartId);
|
||||||
|
emit partListChanged();
|
||||||
|
|
||||||
|
emit skeletonChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::addNode(float x, float y, float z, float radius, QUuid fromNodeId)
|
||||||
|
{
|
||||||
|
QUuid partId;
|
||||||
|
const SkeletonNode *fromNode = nullptr;
|
||||||
|
bool newPartAdded = false;
|
||||||
|
if (fromNodeId.isNull()) {
|
||||||
|
SkeletonPart part;
|
||||||
|
partMap[part.id] = part;
|
||||||
|
partIds.push_back(part.id);
|
||||||
|
partId = part.id;
|
||||||
|
emit partAdded(partId);
|
||||||
|
newPartAdded = true;
|
||||||
|
} else {
|
||||||
|
fromNode = findNode(fromNodeId);
|
||||||
|
if (nullptr == fromNode) {
|
||||||
|
qDebug() << "Find node failed:" << fromNodeId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
partId = fromNode->partId;
|
||||||
|
}
|
||||||
|
SkeletonNode node;
|
||||||
|
node.partId = partId;
|
||||||
|
node.radius = radius;
|
||||||
|
node.x = x;
|
||||||
|
node.y = y;
|
||||||
|
node.z = z;
|
||||||
|
nodeMap[node.id] = node;
|
||||||
|
partMap[partId].nodeIds.push_back(node.id);
|
||||||
|
|
||||||
|
qDebug() << "Add node " << node.id << x << y << z;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit partChanged(partId);
|
||||||
|
|
||||||
|
if (newPartAdded)
|
||||||
|
emit partListChanged();
|
||||||
|
|
||||||
|
emit skeletonChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
const SkeletonEdge *SkeletonDocument::findEdgeByNodes(QUuid firstNodeId, QUuid secondNodeId) const
|
||||||
|
{
|
||||||
|
const SkeletonNode *firstNode = nullptr;
|
||||||
|
firstNode = findNode(firstNodeId);
|
||||||
|
if (nullptr == firstNode) {
|
||||||
|
qDebug() << "Find node failed:" << firstNodeId;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
for (auto edgeIdIt = firstNode->edgeIds.begin(); edgeIdIt != firstNode->edgeIds.end(); edgeIdIt++) {
|
||||||
|
auto edgeIt = edgeMap.find(*edgeIdIt);
|
||||||
|
if (edgeIt == edgeMap.end()) {
|
||||||
|
qDebug() << "Find edge failed:" << *edgeIdIt;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (std::find(edgeIt->second.nodeIds.begin(), edgeIt->second.nodeIds.end(), secondNodeId) != edgeIt->second.nodeIds.end())
|
||||||
|
return &edgeIt->second;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::addEdge(QUuid fromNodeId, QUuid toNodeId)
|
||||||
|
{
|
||||||
|
const SkeletonNode *fromNode = nullptr;
|
||||||
|
const SkeletonNode *toNode = nullptr;
|
||||||
|
bool toPartRemoved = false;
|
||||||
|
|
||||||
|
fromNode = findNode(fromNodeId);
|
||||||
|
if (nullptr == fromNode) {
|
||||||
|
qDebug() << "Find node failed:" << fromNodeId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
toNode = findNode(toNodeId);
|
||||||
|
if (nullptr == toNode) {
|
||||||
|
qDebug() << "Find node failed:" << toNodeId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QUuid toPartId = toNode->partId;
|
||||||
|
|
||||||
|
auto fromPart = partMap.find(fromNode->partId);
|
||||||
|
if (fromPart == partMap.end()) {
|
||||||
|
qDebug() << "Find part failed:" << fromNode->partId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fromNode->partId != toNode->partId) {
|
||||||
|
toPartRemoved = true;
|
||||||
|
std::vector<QUuid> toGroup;
|
||||||
|
std::set<QUuid> visitMap;
|
||||||
|
joinNodeAndNeiborsToGroup(&toGroup, toNodeId, &visitMap);
|
||||||
|
for (auto nodeIdIt = toGroup.begin(); nodeIdIt != toGroup.end(); nodeIdIt++) {
|
||||||
|
auto nodeIt = nodeMap.find(*nodeIdIt);
|
||||||
|
if (nodeIt == nodeMap.end()) {
|
||||||
|
qDebug() << "Find node failed:" << *nodeIdIt;
|
||||||
|
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()) {
|
||||||
|
qDebug() << "Find edge failed:" << *edgeIdIt;
|
||||||
|
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) {
|
||||||
|
partIds.erase(std::remove(partIds.begin(), partIds.end(), toPartId), partIds.end());
|
||||||
|
partMap.erase(toPartId);
|
||||||
|
emit partRemoved(toPartId);
|
||||||
|
emit partListChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
emit skeletonChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
const SkeletonNode *SkeletonDocument::findNode(QUuid nodeId) const
|
||||||
|
{
|
||||||
|
auto it = nodeMap.find(nodeId);
|
||||||
|
if (it == nodeMap.end())
|
||||||
|
return nullptr;
|
||||||
|
return &it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SkeletonEdge *SkeletonDocument::findEdge(QUuid edgeId) const
|
||||||
|
{
|
||||||
|
auto it = edgeMap.find(edgeId);
|
||||||
|
if (it == edgeMap.end())
|
||||||
|
return nullptr;
|
||||||
|
return &it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SkeletonPart *SkeletonDocument::findPart(QUuid partId) const
|
||||||
|
{
|
||||||
|
auto it = partMap.find(partId);
|
||||||
|
if (it == partMap.end())
|
||||||
|
return nullptr;
|
||||||
|
return &it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::scaleNodeByAddRadius(QUuid nodeId, float amount)
|
||||||
|
{
|
||||||
|
auto it = nodeMap.find(nodeId);
|
||||||
|
if (it == nodeMap.end()) {
|
||||||
|
qDebug() << "Find node failed:" << nodeId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
it->second.radius += amount;
|
||||||
|
emit nodeRadiusChanged(nodeId);
|
||||||
|
emit skeletonChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::moveNodeBy(QUuid nodeId, float x, float y, float z)
|
||||||
|
{
|
||||||
|
auto it = nodeMap.find(nodeId);
|
||||||
|
if (it == nodeMap.end()) {
|
||||||
|
qDebug() << "Find node failed:" << nodeId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
it->second.x += x;
|
||||||
|
it->second.y += y;
|
||||||
|
it->second.z += z;
|
||||||
|
emit nodeOriginChanged(nodeId);
|
||||||
|
emit skeletonChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::setNodeOrigin(QUuid nodeId, float x, float y, float z)
|
||||||
|
{
|
||||||
|
auto it = nodeMap.find(nodeId);
|
||||||
|
if (it == nodeMap.end()) {
|
||||||
|
qDebug() << "Find node failed:" << nodeId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
it->second.x = x;
|
||||||
|
it->second.y = y;
|
||||||
|
it->second.z = z;
|
||||||
|
emit nodeOriginChanged(nodeId);
|
||||||
|
emit skeletonChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::setNodeRadius(QUuid nodeId, float radius)
|
||||||
|
{
|
||||||
|
auto it = nodeMap.find(nodeId);
|
||||||
|
if (it == nodeMap.end()) {
|
||||||
|
qDebug() << "Find node failed:" << nodeId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
it->second.radius = radius;
|
||||||
|
emit nodeRadiusChanged(nodeId);
|
||||||
|
emit skeletonChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::updateTurnaround(const QImage &image)
|
||||||
|
{
|
||||||
|
turnaround = image;
|
||||||
|
emit turnaroundChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::setEditMode(SkeletonDocumentEditMode mode)
|
||||||
|
{
|
||||||
|
if (editMode == mode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
editMode = mode;
|
||||||
|
emit editModeChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::joinNodeAndNeiborsToGroup(std::vector<QUuid> *group, QUuid nodeId, std::set<QUuid> *visitMap, QUuid noUseEdgeId)
|
||||||
|
{
|
||||||
|
if (nodeId.isNull() || visitMap->find(nodeId) != visitMap->end())
|
||||||
|
return;
|
||||||
|
const SkeletonNode *node = findNode(nodeId);
|
||||||
|
if (nullptr == node) {
|
||||||
|
qDebug() << "Find node failed:" << nodeId;
|
||||||
|
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) {
|
||||||
|
qDebug() << "Find edge failed:" << *edgeIt;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (auto nodeIt = edge->nodeIds.begin(); nodeIt != edge->nodeIds.end(); nodeIt++) {
|
||||||
|
joinNodeAndNeiborsToGroup(group, *nodeIt, visitMap, noUseEdgeId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::splitPartByNode(std::vector<std::vector<QUuid>> *groups, QUuid nodeId)
|
||||||
|
{
|
||||||
|
const SkeletonNode *node = findNode(nodeId);
|
||||||
|
std::set<QUuid> visitMap;
|
||||||
|
for (auto edgeIt = node->edgeIds.begin(); edgeIt != node->edgeIds.end(); edgeIt++) {
|
||||||
|
std::vector<QUuid> group;
|
||||||
|
const SkeletonEdge *edge = findEdge(*edgeIt);
|
||||||
|
if (nullptr == edge) {
|
||||||
|
qDebug() << "Find edge failed:" << *edgeIt;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
joinNodeAndNeiborsToGroup(&group, edge->neighborOf(nodeId), &visitMap, *edgeIt);
|
||||||
|
if (!group.empty())
|
||||||
|
groups->push_back(group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::splitPartByEdge(std::vector<std::vector<QUuid>> *groups, QUuid edgeId)
|
||||||
|
{
|
||||||
|
const SkeletonEdge *edge = findEdge(edgeId);
|
||||||
|
if (nullptr == edge) {
|
||||||
|
qDebug() << "Find edge failed:" << edgeId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::set<QUuid> visitMap;
|
||||||
|
for (auto nodeIt = edge->nodeIds.begin(); nodeIt != edge->nodeIds.end(); nodeIt++) {
|
||||||
|
std::vector<QUuid> group;
|
||||||
|
joinNodeAndNeiborsToGroup(&group, *nodeIt, &visitMap, edgeId);
|
||||||
|
if (!group.empty())
|
||||||
|
groups->push_back(group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::setEdgeBranchMode(QUuid edgeId, SkeletonEdgeBranchMode mode)
|
||||||
|
{
|
||||||
|
auto edgeIt = edgeMap.find(edgeId);
|
||||||
|
if (edgeIt == edgeMap.end()) {
|
||||||
|
qDebug() << "Find edge failed:" << edgeId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
edgeIt->second.branchMode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::setNodeRootMarkMode(QUuid nodeId, SkeletonNodeRootMarkMode mode)
|
||||||
|
{
|
||||||
|
auto nodeIt = nodeMap.find(nodeId);
|
||||||
|
if (nodeIt == nodeMap.end()) {
|
||||||
|
qDebug() << "Find node failed:" << nodeId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
nodeIt->second.rootMarkMode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::toSnapshot(SkeletonSnapshot *snapshot)
|
||||||
|
{
|
||||||
|
for (const auto &partIt : partMap) {
|
||||||
|
std::map<QString, QString> part;
|
||||||
|
part["id"] = partIt.second.id.toString();
|
||||||
|
part["visible"] = partIt.second.visible ? "true" : "false";
|
||||||
|
if (!partIt.second.name.isEmpty())
|
||||||
|
part["name"] = partIt.second.name;
|
||||||
|
snapshot->parts[part["id"]] = part;
|
||||||
|
}
|
||||||
|
for (const auto &nodeIt: nodeMap) {
|
||||||
|
std::map<QString, QString> node;
|
||||||
|
node["id"] = nodeIt.second.id.toString();
|
||||||
|
node["radius"] = QString::number(nodeIt.second.radius);
|
||||||
|
node["x"] = QString::number(nodeIt.second.x);
|
||||||
|
node["y"] = QString::number(nodeIt.second.y);
|
||||||
|
node["z"] = QString::number(nodeIt.second.z);
|
||||||
|
node["rootMarkMode"] = SkeletonNodeRootMarkModeToString(nodeIt.second.rootMarkMode);
|
||||||
|
node["partId"] = nodeIt.second.partId.toString();
|
||||||
|
if (!nodeIt.second.name.isEmpty())
|
||||||
|
node["name"] = nodeIt.second.name;
|
||||||
|
snapshot->nodes[node["id"]] = node;
|
||||||
|
qDebug() << "Export node to snapshot " << node["id"] << node["x"] << node["y"] << node["z"];
|
||||||
|
}
|
||||||
|
for (const auto &edgeIt: edgeMap) {
|
||||||
|
if (edgeIt.second.nodeIds.size() != 2)
|
||||||
|
continue;
|
||||||
|
std::map<QString, QString> edge;
|
||||||
|
edge["id"] = edgeIt.second.id.toString();
|
||||||
|
edge["from"] = edgeIt.second.nodeIds[0].toString();
|
||||||
|
edge["to"] = edgeIt.second.nodeIds[1].toString();
|
||||||
|
edge["branchMode"] = SkeletonEdgeBranchModeToString(edgeIt.second.branchMode);
|
||||||
|
edge["partId"] = edgeIt.second.partId.toString();
|
||||||
|
if (!edgeIt.second.name.isEmpty())
|
||||||
|
edge["name"] = edgeIt.second.name;
|
||||||
|
snapshot->edges[edge["id"]] = edge;
|
||||||
|
qDebug() << "Export edge to snapshot " << edge["from"] << "<=>" << edge["to"];
|
||||||
|
}
|
||||||
|
for (const auto &partIdIt: partIds) {
|
||||||
|
snapshot->partIdList.push_back(partIdIt.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::fromSnapshot(const SkeletonSnapshot &snapshot)
|
||||||
|
{
|
||||||
|
for (const auto &nodeIt: nodeMap) {
|
||||||
|
emit nodeRemoved(nodeIt.first);
|
||||||
|
}
|
||||||
|
for (const auto &edgeIt: edgeMap) {
|
||||||
|
emit edgeRemoved(edgeIt.first);
|
||||||
|
}
|
||||||
|
for (const auto &partIt : partMap) {
|
||||||
|
emit partRemoved(partIt.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeMap.clear();
|
||||||
|
edgeMap.clear();
|
||||||
|
partMap.clear();
|
||||||
|
|
||||||
|
for (const auto &nodeKv : snapshot.nodes) {
|
||||||
|
SkeletonNode node(QUuid(nodeKv.first));
|
||||||
|
node.name = valueOfKeyInMapOrEmpty(nodeKv.second, "name");
|
||||||
|
node.radius = valueOfKeyInMapOrEmpty(nodeKv.second, "radius").toFloat();
|
||||||
|
node.x = valueOfKeyInMapOrEmpty(nodeKv.second, "x").toFloat();
|
||||||
|
node.y = valueOfKeyInMapOrEmpty(nodeKv.second, "y").toFloat();
|
||||||
|
node.z = valueOfKeyInMapOrEmpty(nodeKv.second, "z").toFloat();
|
||||||
|
node.partId = QUuid(valueOfKeyInMapOrEmpty(nodeKv.second, "partId"));
|
||||||
|
node.rootMarkMode = SkeletonNodeRootMarkModeFromString(valueOfKeyInMapOrEmpty(nodeKv.second, "rootMarkMode"));
|
||||||
|
nodeMap[node.id] = node;
|
||||||
|
}
|
||||||
|
for (const auto &edgeKv : snapshot.edges) {
|
||||||
|
SkeletonEdge edge(QUuid(edgeKv.first));
|
||||||
|
edge.name = valueOfKeyInMapOrEmpty(edgeKv.second, "name");
|
||||||
|
edge.partId = QUuid(valueOfKeyInMapOrEmpty(edgeKv.second, "partId"));
|
||||||
|
edge.branchMode = SkeletonEdgeBranchModeFromString(valueOfKeyInMapOrEmpty(edgeKv.second, "branchMode"));
|
||||||
|
QString fromNodeId = valueOfKeyInMapOrEmpty(edgeKv.second, "from");
|
||||||
|
if (!fromNodeId.isEmpty())
|
||||||
|
edge.nodeIds.push_back(QUuid(fromNodeId));
|
||||||
|
QString toNodeId = valueOfKeyInMapOrEmpty(edgeKv.second, "to");
|
||||||
|
if (!toNodeId.isEmpty())
|
||||||
|
edge.nodeIds.push_back(QUuid(toNodeId));
|
||||||
|
edgeMap[edge.id] = edge;
|
||||||
|
}
|
||||||
|
for (const auto &partKv : snapshot.parts) {
|
||||||
|
SkeletonPart part(QUuid(partKv.first));
|
||||||
|
part.name = valueOfKeyInMapOrEmpty(partKv.second, "name");
|
||||||
|
part.visible = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "visible"));
|
||||||
|
partMap[part.id] = part;
|
||||||
|
}
|
||||||
|
for (const auto &partIdIt: snapshot.partIdList) {
|
||||||
|
partIds.push_back(QUuid(partIdIt));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &nodeIt: nodeMap) {
|
||||||
|
emit nodeAdded(nodeIt.first);
|
||||||
|
}
|
||||||
|
for (const auto &edgeIt: edgeMap) {
|
||||||
|
emit edgeAdded(edgeIt.first);
|
||||||
|
}
|
||||||
|
for (const auto &partIt : partMap) {
|
||||||
|
emit partAdded(partIt.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit partListChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *SkeletonNodeRootMarkModeToString(SkeletonNodeRootMarkMode mode)
|
||||||
|
{
|
||||||
|
switch (mode) {
|
||||||
|
case SkeletonNodeRootMarkMode::Auto:
|
||||||
|
return "auto";
|
||||||
|
case SkeletonNodeRootMarkMode::MarkAsRoot:
|
||||||
|
return "markAsRoot";
|
||||||
|
case SkeletonNodeRootMarkMode::MarkAsNotRoot:
|
||||||
|
return "markAsNotRoot";
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SkeletonNodeRootMarkMode SkeletonNodeRootMarkModeFromString(const QString &mode)
|
||||||
|
{
|
||||||
|
if (mode == "auto")
|
||||||
|
return SkeletonNodeRootMarkMode::Auto;
|
||||||
|
if (mode == "markAsRoot")
|
||||||
|
return SkeletonNodeRootMarkMode::MarkAsRoot;
|
||||||
|
if (mode == "markAsNotRoot")
|
||||||
|
return SkeletonNodeRootMarkMode::MarkAsNotRoot;
|
||||||
|
return SkeletonNodeRootMarkMode::Auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *SkeletonEdgeBranchModeToString(SkeletonEdgeBranchMode mode)
|
||||||
|
{
|
||||||
|
switch (mode) {
|
||||||
|
case SkeletonEdgeBranchMode::Auto:
|
||||||
|
return "auto";
|
||||||
|
case SkeletonEdgeBranchMode::NoTrivial:
|
||||||
|
return "noTrivial";
|
||||||
|
case SkeletonEdgeBranchMode::Trivial:
|
||||||
|
return "trivial";
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SkeletonEdgeBranchMode SkeletonEdgeBranchModeFromString(const QString &mode)
|
||||||
|
{
|
||||||
|
if (mode == "auto")
|
||||||
|
return SkeletonEdgeBranchMode::Auto;
|
||||||
|
if (mode == "noTrivial")
|
||||||
|
return SkeletonEdgeBranchMode::NoTrivial;
|
||||||
|
if (mode == "trivial")
|
||||||
|
return SkeletonEdgeBranchMode::Trivial;
|
||||||
|
return SkeletonEdgeBranchMode::Auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mesh *SkeletonDocument::takeResultMesh()
|
||||||
|
{
|
||||||
|
Mesh *resultMesh = m_resultMesh;
|
||||||
|
m_resultMesh = nullptr;
|
||||||
|
return resultMesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::meshReady()
|
||||||
|
{
|
||||||
|
Mesh *resultMesh = m_meshGenerator->takeResultMesh();
|
||||||
|
|
||||||
|
QImage *resultPreview = m_meshGenerator->takePreview();
|
||||||
|
if (resultPreview) {
|
||||||
|
preview = *resultPreview;
|
||||||
|
delete resultPreview;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &part: partMap) {
|
||||||
|
QImage *resultPartPreview = m_meshGenerator->takePartPreview(part.first.toString());
|
||||||
|
if (resultPartPreview) {
|
||||||
|
part.second.preview = *resultPartPreview;
|
||||||
|
emit partPreviewChanged(part.first);
|
||||||
|
delete resultPartPreview;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete m_resultMesh;
|
||||||
|
m_resultMesh = resultMesh;
|
||||||
|
|
||||||
|
if (nullptr == m_resultMesh) {
|
||||||
|
qDebug() << "Result mesh is null";
|
||||||
|
}
|
||||||
|
|
||||||
|
delete m_meshGenerator;
|
||||||
|
m_meshGenerator = nullptr;
|
||||||
|
|
||||||
|
qDebug() << "Mesh generation done";
|
||||||
|
|
||||||
|
emit resultMeshChanged();
|
||||||
|
|
||||||
|
if (m_resultMeshIsObsolete) {
|
||||||
|
generateMesh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocument::generateMesh()
|
||||||
|
{
|
||||||
|
if (nullptr != m_meshGenerator) {
|
||||||
|
m_resultMeshIsObsolete = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Mesh generating..";
|
||||||
|
|
||||||
|
m_resultMeshIsObsolete = false;
|
||||||
|
|
||||||
|
QThread *thread = new QThread;
|
||||||
|
|
||||||
|
SkeletonSnapshot *snapshot = new SkeletonSnapshot;
|
||||||
|
toSnapshot(snapshot);
|
||||||
|
m_meshGenerator = new MeshGenerator(snapshot, thread);
|
||||||
|
m_meshGenerator->moveToThread(thread);
|
||||||
|
m_meshGenerator->addPreviewRequirement();
|
||||||
|
for (auto &part: partMap) {
|
||||||
|
//if (part.second.previewIsObsolete) {
|
||||||
|
// part.second.previewIsObsolete = false;
|
||||||
|
m_meshGenerator->addPartPreviewRequirement(part.first.toString());
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
connect(thread, &QThread::started, m_meshGenerator, &MeshGenerator::process);
|
||||||
|
connect(m_meshGenerator, &MeshGenerator::finished, this, &SkeletonDocument::meshReady);
|
||||||
|
connect(m_meshGenerator, &MeshGenerator::finished, thread, &QThread::quit);
|
||||||
|
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
|
||||||
|
thread->start();
|
||||||
|
}
|
|
@ -0,0 +1,184 @@
|
||||||
|
#ifndef SKELETON_DOCUMENT_H
|
||||||
|
#define SKELETON_DOCUMENT_H
|
||||||
|
#include <QObject>
|
||||||
|
#include <QUuid>
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
#include <QImage>
|
||||||
|
#include "skeletonsnapshot.h"
|
||||||
|
#include "mesh.h"
|
||||||
|
#include "meshgenerator.h"
|
||||||
|
|
||||||
|
enum class SkeletonNodeRootMarkMode
|
||||||
|
{
|
||||||
|
Auto = 0,
|
||||||
|
MarkAsRoot,
|
||||||
|
MarkAsNotRoot
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *SkeletonNodeRootMarkModeToString(SkeletonNodeRootMarkMode mode);
|
||||||
|
SkeletonNodeRootMarkMode SkeletonNodeRootMarkModeFromString(const QString &mode);
|
||||||
|
|
||||||
|
class SkeletonNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SkeletonNode(const QUuid &withId=QUuid()) :
|
||||||
|
x(0),
|
||||||
|
y(0),
|
||||||
|
z(0),
|
||||||
|
radius(0),
|
||||||
|
rootMarkMode(SkeletonNodeRootMarkMode::Auto)
|
||||||
|
{
|
||||||
|
id = withId.isNull() ? QUuid::createUuid() : withId;
|
||||||
|
}
|
||||||
|
QUuid id;
|
||||||
|
QUuid partId;
|
||||||
|
QString name;
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
float z;
|
||||||
|
float radius;
|
||||||
|
SkeletonNodeRootMarkMode rootMarkMode;
|
||||||
|
std::vector<QUuid> edgeIds;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class SkeletonEdgeBranchMode
|
||||||
|
{
|
||||||
|
Auto = 0,
|
||||||
|
Trivial,
|
||||||
|
NoTrivial
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *SkeletonEdgeBranchModeToString(SkeletonEdgeBranchMode mode);
|
||||||
|
SkeletonEdgeBranchMode SkeletonEdgeBranchModeFromString(const QString &mode);
|
||||||
|
|
||||||
|
class SkeletonEdge
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SkeletonEdge(const QUuid &withId=QUuid()) :
|
||||||
|
branchMode(SkeletonEdgeBranchMode::Auto)
|
||||||
|
{
|
||||||
|
id = withId.isNull() ? QUuid::createUuid() : withId;
|
||||||
|
}
|
||||||
|
QUuid id;
|
||||||
|
QUuid partId;
|
||||||
|
QString name;
|
||||||
|
SkeletonEdgeBranchMode branchMode;
|
||||||
|
std::vector<QUuid> nodeIds;
|
||||||
|
QUuid neighborOf(QUuid nodeId) const
|
||||||
|
{
|
||||||
|
if (nodeIds.size() != 2)
|
||||||
|
return QUuid();
|
||||||
|
return nodeIds[0] == nodeId ? nodeIds[1] : nodeIds[0];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class SkeletonPart
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QUuid id;
|
||||||
|
QString name;
|
||||||
|
bool visible;
|
||||||
|
QImage preview;
|
||||||
|
std::vector<QUuid> nodeIds;
|
||||||
|
bool previewIsObsolete;
|
||||||
|
SkeletonPart(const QUuid &withId=QUuid()) :
|
||||||
|
visible(true),
|
||||||
|
previewIsObsolete(true)
|
||||||
|
{
|
||||||
|
id = withId.isNull() ? QUuid::createUuid() : withId;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class SkeletonHistoryItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SkeletonSnapshot snapshot;
|
||||||
|
QImage preview;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class SkeletonDocumentEditMode
|
||||||
|
{
|
||||||
|
Add = 0,
|
||||||
|
Select,
|
||||||
|
Drag,
|
||||||
|
ZoomIn,
|
||||||
|
ZoomOut
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class SkeletonProfile
|
||||||
|
{
|
||||||
|
Unknown = 0,
|
||||||
|
Main,
|
||||||
|
Side
|
||||||
|
};
|
||||||
|
|
||||||
|
class SkeletonDocument : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
signals:
|
||||||
|
void partAdded(QUuid partId);
|
||||||
|
void nodeAdded(QUuid nodeId);
|
||||||
|
void edgeAdded(QUuid edgeId);
|
||||||
|
void partRemoved(QUuid partId);
|
||||||
|
void partListChanged();
|
||||||
|
void nodeRemoved(QUuid nodeId);
|
||||||
|
void edgeRemoved(QUuid edgeId);
|
||||||
|
void nodeRadiusChanged(QUuid nodeId);
|
||||||
|
void nodeOriginChanged(QUuid nodeId);
|
||||||
|
void edgeChanged(QUuid edgeId);
|
||||||
|
void partChanged(QUuid partId);
|
||||||
|
void partPreviewChanged(QUuid partId);
|
||||||
|
void resultMeshChanged();
|
||||||
|
void turnaroundChanged();
|
||||||
|
void editModeChanged();
|
||||||
|
void skeletonChanged();
|
||||||
|
public:
|
||||||
|
SkeletonDocument();
|
||||||
|
~SkeletonDocument();
|
||||||
|
std::map<QUuid, SkeletonPart> partMap;
|
||||||
|
std::map<QUuid, SkeletonNode> nodeMap;
|
||||||
|
std::map<QUuid, SkeletonEdge> edgeMap;
|
||||||
|
std::vector<SkeletonHistoryItem> historyItems;
|
||||||
|
std::vector<QUuid> partIds;
|
||||||
|
QImage turnaround;
|
||||||
|
SkeletonDocumentEditMode editMode;
|
||||||
|
void toSnapshot(SkeletonSnapshot *snapshot);
|
||||||
|
void fromSnapshot(const SkeletonSnapshot &snapshot);
|
||||||
|
const SkeletonNode *findNode(QUuid nodeId) const;
|
||||||
|
const SkeletonEdge *findEdge(QUuid edgeId) const;
|
||||||
|
const SkeletonPart *findPart(QUuid partId) const;
|
||||||
|
const SkeletonEdge *findEdgeByNodes(QUuid firstNodeId, QUuid secondNodeId) const;
|
||||||
|
Mesh *takeResultMesh();
|
||||||
|
QImage preview;
|
||||||
|
void updateTurnaround(const QImage &image);
|
||||||
|
public slots:
|
||||||
|
void removeNode(QUuid nodeId);
|
||||||
|
void removeEdge(QUuid edgeId);
|
||||||
|
void removePart(QUuid partId);
|
||||||
|
void showPart(QUuid partId);
|
||||||
|
void hidePart(QUuid partId);
|
||||||
|
void addNode(float x, float y, float z, float radius, QUuid fromNodeId);
|
||||||
|
void scaleNodeByAddRadius(QUuid nodeId, float amount);
|
||||||
|
void moveNodeBy(QUuid nodeId, float x, float y, float z);
|
||||||
|
void setNodeOrigin(QUuid nodeId, float x, float y, float z);
|
||||||
|
void setNodeRadius(QUuid nodeId, float radius);
|
||||||
|
void addEdge(QUuid fromNodeId, QUuid toNodeId);
|
||||||
|
void setEditMode(SkeletonDocumentEditMode mode);
|
||||||
|
void setEdgeBranchMode(QUuid edgeId, SkeletonEdgeBranchMode mode);
|
||||||
|
void setNodeRootMarkMode(QUuid nodeId, SkeletonNodeRootMarkMode mode);
|
||||||
|
void uiReady();
|
||||||
|
void generateMesh();
|
||||||
|
void meshReady();
|
||||||
|
private:
|
||||||
|
void splitPartByNode(std::vector<std::vector<QUuid>> *groups, QUuid nodeId);
|
||||||
|
void joinNodeAndNeiborsToGroup(std::vector<QUuid> *group, QUuid nodeId, std::set<QUuid> *visitMap, QUuid noUseEdgeId=QUuid());
|
||||||
|
void splitPartByEdge(std::vector<std::vector<QUuid>> *groups, QUuid edgeId);
|
||||||
|
private:
|
||||||
|
bool m_resultMeshIsObsolete;
|
||||||
|
MeshGenerator *m_meshGenerator;
|
||||||
|
Mesh *m_resultMesh;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,256 @@
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QGridLayout>
|
||||||
|
#include <QToolBar>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QTabWidget>
|
||||||
|
#include "skeletondocumentwindow.h"
|
||||||
|
#include "skeletongraphicswidget.h"
|
||||||
|
#include "skeletonpartlistwidget.h"
|
||||||
|
#include "skeletonedgepropertywidget.h"
|
||||||
|
#include "skeletonnodepropertywidget.h"
|
||||||
|
#include "theme.h"
|
||||||
|
|
||||||
|
SkeletonDocumentWindow::SkeletonDocumentWindow() :
|
||||||
|
m_document(nullptr),
|
||||||
|
m_firstShow(true)
|
||||||
|
{
|
||||||
|
m_document = new SkeletonDocument;
|
||||||
|
|
||||||
|
QVBoxLayout *toolButtonLayout = new QVBoxLayout;
|
||||||
|
toolButtonLayout->setSpacing(0);
|
||||||
|
toolButtonLayout->setContentsMargins(5, 10, 4, 0);
|
||||||
|
|
||||||
|
QPushButton *undoButton = new QPushButton(QChar(fa::undo));
|
||||||
|
undoButton->setFont(Theme::awesome()->font(Theme::toolIconFontSize));
|
||||||
|
undoButton->setFixedSize(Theme::toolIconSize, Theme::toolIconSize);
|
||||||
|
|
||||||
|
QPushButton *addButton = new QPushButton(QChar(fa::plus));
|
||||||
|
addButton->setFont(Theme::awesome()->font(Theme::toolIconFontSize));
|
||||||
|
addButton->setFixedSize(Theme::toolIconSize, Theme::toolIconSize);
|
||||||
|
|
||||||
|
QPushButton *selectButton = new QPushButton(QChar(fa::mousepointer));
|
||||||
|
selectButton->setFont(Theme::awesome()->font(Theme::toolIconFontSize));
|
||||||
|
selectButton->setFixedSize(Theme::toolIconSize, Theme::toolIconSize);
|
||||||
|
|
||||||
|
QPushButton *dragButton = new QPushButton(QChar(fa::handrocko));
|
||||||
|
dragButton->setFont(Theme::awesome()->font(Theme::toolIconFontSize));
|
||||||
|
dragButton->setFixedSize(Theme::toolIconSize, Theme::toolIconSize);
|
||||||
|
|
||||||
|
QPushButton *zoomInButton = new QPushButton(QChar(fa::searchplus));
|
||||||
|
zoomInButton->setFont(Theme::awesome()->font(Theme::toolIconFontSize));
|
||||||
|
zoomInButton->setFixedSize(Theme::toolIconSize, Theme::toolIconSize);
|
||||||
|
|
||||||
|
QPushButton *zoomOutButton = new QPushButton(QChar(fa::searchminus));
|
||||||
|
zoomOutButton->setFont(Theme::awesome()->font(Theme::toolIconFontSize));
|
||||||
|
zoomOutButton->setFixedSize(Theme::toolIconSize, Theme::toolIconSize);
|
||||||
|
|
||||||
|
QPushButton *changeTurnaroundButton = new QPushButton(QChar(fa::image));
|
||||||
|
changeTurnaroundButton->setFont(Theme::awesome()->font(Theme::toolIconFontSize));
|
||||||
|
changeTurnaroundButton->setFixedSize(Theme::toolIconSize, Theme::toolIconSize);
|
||||||
|
|
||||||
|
QPushButton *resetButton = new QPushButton(QChar(fa::trash));
|
||||||
|
resetButton->setFont(Theme::awesome()->font(Theme::toolIconFontSize));
|
||||||
|
resetButton->setFixedSize(Theme::toolIconSize, Theme::toolIconSize);
|
||||||
|
|
||||||
|
toolButtonLayout->addWidget(undoButton);
|
||||||
|
toolButtonLayout->addSpacing(10);
|
||||||
|
toolButtonLayout->addWidget(addButton);
|
||||||
|
toolButtonLayout->addWidget(selectButton);
|
||||||
|
toolButtonLayout->addWidget(dragButton);
|
||||||
|
toolButtonLayout->addWidget(zoomInButton);
|
||||||
|
toolButtonLayout->addWidget(zoomOutButton);
|
||||||
|
toolButtonLayout->addSpacing(10);
|
||||||
|
toolButtonLayout->addWidget(changeTurnaroundButton);
|
||||||
|
toolButtonLayout->addSpacing(30);
|
||||||
|
toolButtonLayout->addWidget(resetButton);
|
||||||
|
|
||||||
|
QLabel *dust3dJezzasoftLabel = new QLabel;
|
||||||
|
QImage dust3dJezzasoftImage;
|
||||||
|
dust3dJezzasoftImage.load(":/resources/dust3d_jezzasoft.png");
|
||||||
|
dust3dJezzasoftLabel->setPixmap(QPixmap::fromImage(dust3dJezzasoftImage));
|
||||||
|
|
||||||
|
QHBoxLayout *logoLayout = new QHBoxLayout;
|
||||||
|
logoLayout->addWidget(dust3dJezzasoftLabel);
|
||||||
|
logoLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
||||||
|
QVBoxLayout *mainLeftLayout = new QVBoxLayout;
|
||||||
|
mainLeftLayout->setSpacing(0);
|
||||||
|
mainLeftLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
mainLeftLayout->addLayout(toolButtonLayout);
|
||||||
|
mainLeftLayout->addStretch();
|
||||||
|
mainLeftLayout->addLayout(logoLayout);
|
||||||
|
mainLeftLayout->addSpacing(10);
|
||||||
|
|
||||||
|
SkeletonGraphicsWidget *graphicsWidget = new SkeletonGraphicsWidget(m_document);
|
||||||
|
|
||||||
|
SkeletonGraphicsContainerWidget *containerWidget = new SkeletonGraphicsContainerWidget;
|
||||||
|
|
||||||
|
containerWidget->setGraphicsWidget(graphicsWidget);
|
||||||
|
QGridLayout *containerLayout = new QGridLayout;
|
||||||
|
containerLayout->setSpacing(0);
|
||||||
|
containerLayout->setContentsMargins(1, 0, 0, 0);
|
||||||
|
containerLayout->addWidget(graphicsWidget);
|
||||||
|
containerWidget->setLayout(containerLayout);
|
||||||
|
containerWidget->setMinimumSize(400, 400);
|
||||||
|
|
||||||
|
m_modelWidget = new ModelWidget(containerWidget);
|
||||||
|
m_modelWidget->setMinimumSize(128, 128);
|
||||||
|
m_modelWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||||
|
m_modelWidget->move(10, 10);
|
||||||
|
m_modelWidget->setGraphicsFunctions(graphicsWidget);
|
||||||
|
|
||||||
|
SkeletonPartListWidget *partListWidget = new SkeletonPartListWidget(m_document);
|
||||||
|
|
||||||
|
QTabWidget *firstTabWidget = new QTabWidget;
|
||||||
|
firstTabWidget->setFont(Theme::awesome()->font(Theme::toolIconFontSize * 3 / 4));
|
||||||
|
firstTabWidget->setMaximumWidth(200);
|
||||||
|
firstTabWidget->addTab(partListWidget, QChar(fa::puzzlepiece));
|
||||||
|
firstTabWidget->addTab(new QWidget, QChar(fa::history));
|
||||||
|
firstTabWidget->addTab(new QWidget, QChar(fa::wrench));
|
||||||
|
|
||||||
|
SkeletonEdgePropertyWidget *edgePropertyWidget = new SkeletonEdgePropertyWidget(m_document);
|
||||||
|
SkeletonNodePropertyWidget *nodePropertyWidget = new SkeletonNodePropertyWidget(m_document);
|
||||||
|
|
||||||
|
QVBoxLayout *propertyLayout = new QVBoxLayout;
|
||||||
|
propertyLayout->addWidget(edgePropertyWidget);
|
||||||
|
propertyLayout->addWidget(nodePropertyWidget);
|
||||||
|
|
||||||
|
QWidget *propertyWidget = new QWidget;
|
||||||
|
propertyWidget->setLayout(propertyLayout);
|
||||||
|
|
||||||
|
QTabWidget *secondTabWidget = new QTabWidget;
|
||||||
|
secondTabWidget->setFont(Theme::awesome()->font(Theme::toolIconFontSize * 3 / 4));
|
||||||
|
secondTabWidget->setMaximumWidth(200);
|
||||||
|
secondTabWidget->setMaximumHeight(90);
|
||||||
|
secondTabWidget->addTab(propertyWidget, QChar(fa::adjust));
|
||||||
|
|
||||||
|
QVBoxLayout *mainRightLayout = new QVBoxLayout;
|
||||||
|
mainRightLayout->setSpacing(0);
|
||||||
|
mainRightLayout->setContentsMargins(5, 5, 5, 5);
|
||||||
|
mainRightLayout->addWidget(firstTabWidget);
|
||||||
|
mainRightLayout->addWidget(secondTabWidget);
|
||||||
|
|
||||||
|
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||||
|
mainLayout->setSpacing(0);
|
||||||
|
mainLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
mainLayout->addLayout(mainLeftLayout);
|
||||||
|
mainLayout->addWidget(containerWidget);
|
||||||
|
mainLayout->addLayout(mainRightLayout);
|
||||||
|
|
||||||
|
QWidget *centralWidget = new QWidget;
|
||||||
|
centralWidget->setLayout(mainLayout);
|
||||||
|
|
||||||
|
setCentralWidget(centralWidget);
|
||||||
|
setWindowTitle(tr("Dust3D"));
|
||||||
|
|
||||||
|
connect(containerWidget, &SkeletonGraphicsContainerWidget::containerSizeChanged,
|
||||||
|
graphicsWidget, &SkeletonGraphicsWidget::canvasResized);
|
||||||
|
|
||||||
|
connect(m_document, &SkeletonDocument::turnaroundChanged,
|
||||||
|
graphicsWidget, &SkeletonGraphicsWidget::turnaroundChanged);
|
||||||
|
|
||||||
|
connect(changeTurnaroundButton, &QPushButton::clicked, this, &SkeletonDocumentWindow::changeTurnaround);
|
||||||
|
|
||||||
|
connect(addButton, &QPushButton::clicked, [=]() {
|
||||||
|
m_document->setEditMode(SkeletonDocumentEditMode::Add);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(selectButton, &QPushButton::clicked, [=]() {
|
||||||
|
m_document->setEditMode(SkeletonDocumentEditMode::Select);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(dragButton, &QPushButton::clicked, [=]() {
|
||||||
|
m_document->setEditMode(SkeletonDocumentEditMode::Drag);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(zoomInButton, &QPushButton::clicked, [=]() {
|
||||||
|
m_document->setEditMode(SkeletonDocumentEditMode::ZoomIn);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(zoomOutButton, &QPushButton::clicked, [=]() {
|
||||||
|
m_document->setEditMode(SkeletonDocumentEditMode::ZoomOut);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_document, &SkeletonDocument::editModeChanged, graphicsWidget, &SkeletonGraphicsWidget::editModeChanged);
|
||||||
|
|
||||||
|
connect(graphicsWidget, &SkeletonGraphicsWidget::addNode, m_document, &SkeletonDocument::addNode);
|
||||||
|
connect(graphicsWidget, &SkeletonGraphicsWidget::scaleNodeByAddRadius, m_document, &SkeletonDocument::scaleNodeByAddRadius);
|
||||||
|
connect(graphicsWidget, &SkeletonGraphicsWidget::moveNodeBy, m_document, &SkeletonDocument::moveNodeBy);
|
||||||
|
connect(graphicsWidget, &SkeletonGraphicsWidget::removeNode, m_document, &SkeletonDocument::removeNode);
|
||||||
|
connect(graphicsWidget, &SkeletonGraphicsWidget::setEditMode, m_document, &SkeletonDocument::setEditMode);
|
||||||
|
connect(graphicsWidget, &SkeletonGraphicsWidget::removeEdge, m_document, &SkeletonDocument::removeEdge);
|
||||||
|
connect(graphicsWidget, &SkeletonGraphicsWidget::addEdge, m_document, &SkeletonDocument::addEdge);
|
||||||
|
|
||||||
|
connect(m_document, &SkeletonDocument::nodeAdded, graphicsWidget, &SkeletonGraphicsWidget::nodeAdded);
|
||||||
|
connect(m_document, &SkeletonDocument::nodeRemoved, graphicsWidget, &SkeletonGraphicsWidget::nodeRemoved);
|
||||||
|
connect(m_document, &SkeletonDocument::edgeAdded, graphicsWidget, &SkeletonGraphicsWidget::edgeAdded);
|
||||||
|
connect(m_document, &SkeletonDocument::edgeRemoved, graphicsWidget, &SkeletonGraphicsWidget::edgeRemoved);
|
||||||
|
connect(m_document, &SkeletonDocument::nodeRadiusChanged, graphicsWidget, &SkeletonGraphicsWidget::nodeRadiusChanged);
|
||||||
|
connect(m_document, &SkeletonDocument::nodeOriginChanged, graphicsWidget, &SkeletonGraphicsWidget::nodeOriginChanged);
|
||||||
|
|
||||||
|
connect(m_document, &SkeletonDocument::partListChanged, partListWidget, &SkeletonPartListWidget::partListChanged);
|
||||||
|
connect(m_document, &SkeletonDocument::partPreviewChanged, partListWidget, &SkeletonPartListWidget::partPreviewChanged);
|
||||||
|
|
||||||
|
connect(graphicsWidget, &SkeletonGraphicsWidget::checkEdge, edgePropertyWidget, &SkeletonEdgePropertyWidget::showProperties);
|
||||||
|
connect(graphicsWidget, &SkeletonGraphicsWidget::uncheckEdge, [=](QUuid edgeId) {
|
||||||
|
edgePropertyWidget->hide();
|
||||||
|
});
|
||||||
|
connect(m_document, &SkeletonDocument::edgeRemoved, [=](QUuid edgeId) {
|
||||||
|
if (edgeId == edgePropertyWidget->currentEdgeId()) {
|
||||||
|
edgePropertyWidget->hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(edgePropertyWidget, &SkeletonEdgePropertyWidget::setEdgeBranchMode, m_document, &SkeletonDocument::setEdgeBranchMode);
|
||||||
|
|
||||||
|
connect(graphicsWidget, &SkeletonGraphicsWidget::checkNode, nodePropertyWidget, &SkeletonNodePropertyWidget::showProperties);
|
||||||
|
connect(graphicsWidget, &SkeletonGraphicsWidget::uncheckNode, [=](QUuid nodeId) {
|
||||||
|
nodePropertyWidget->hide();
|
||||||
|
});
|
||||||
|
connect(m_document, &SkeletonDocument::nodeRemoved, [=](QUuid nodeId) {
|
||||||
|
if (nodeId == nodePropertyWidget->currentNodeId()) {
|
||||||
|
nodePropertyWidget->hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(nodePropertyWidget, &SkeletonNodePropertyWidget::setNodeRootMarkMode, m_document, &SkeletonDocument::setNodeRootMarkMode);
|
||||||
|
|
||||||
|
connect(m_document, &SkeletonDocument::skeletonChanged, m_document, &SkeletonDocument::generateMesh);
|
||||||
|
|
||||||
|
connect(m_document, &SkeletonDocument::resultMeshChanged, [=]() {
|
||||||
|
m_modelWidget->updateMesh(m_document->takeResultMesh());
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(graphicsWidget, &SkeletonGraphicsWidget::cursorChanged, [=]() {
|
||||||
|
m_modelWidget->setCursor(graphicsWidget->cursor());
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(this, &SkeletonDocumentWindow::initialized, m_document, &SkeletonDocument::uiReady);
|
||||||
|
}
|
||||||
|
|
||||||
|
SkeletonDocumentWindow::~SkeletonDocumentWindow()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocumentWindow::showEvent(QShowEvent *event)
|
||||||
|
{
|
||||||
|
QMainWindow::showEvent(event);
|
||||||
|
if (m_firstShow) {
|
||||||
|
m_firstShow = false;
|
||||||
|
emit initialized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonDocumentWindow::changeTurnaround()
|
||||||
|
{
|
||||||
|
QString fileName = QFileDialog::getOpenFileName(this, tr("Open Turnaround Reference Image"),
|
||||||
|
QString(),
|
||||||
|
tr("Image Files (*.png *.jpg *.bmp)")).trimmed();
|
||||||
|
if (fileName.isEmpty())
|
||||||
|
return;
|
||||||
|
QImage image;
|
||||||
|
if (!image.load(fileName))
|
||||||
|
return;
|
||||||
|
m_document->updateTurnaround(image);
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
#ifndef SKELETON_DOCUMENT_WINDOW_H
|
||||||
|
#define SKELETON_DOCUMENT_WINDOW_H
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <QShowEvent>
|
||||||
|
#include "skeletondocument.h"
|
||||||
|
#include "modelwidget.h"
|
||||||
|
|
||||||
|
class SkeletonDocumentWindow : public QMainWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
signals:
|
||||||
|
void initialized();
|
||||||
|
public:
|
||||||
|
SkeletonDocumentWindow();
|
||||||
|
~SkeletonDocumentWindow();
|
||||||
|
protected:
|
||||||
|
void showEvent(QShowEvent *event);
|
||||||
|
public slots:
|
||||||
|
void changeTurnaround();
|
||||||
|
private:
|
||||||
|
SkeletonDocument *m_document;
|
||||||
|
bool m_firstShow;
|
||||||
|
ModelWidget *m_modelWidget;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
#include <QFormLayout>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QtGlobal>
|
||||||
|
#include "skeletonedgepropertywidget.h"
|
||||||
|
|
||||||
|
SkeletonEdgePropertyWidget::SkeletonEdgePropertyWidget(const SkeletonDocument *document) :
|
||||||
|
m_branchModeComboBox(nullptr),
|
||||||
|
m_document(document)
|
||||||
|
{
|
||||||
|
m_branchModeComboBox = new QComboBox;
|
||||||
|
|
||||||
|
m_branchModeComboBox->insertItem(0, tr("Auto"), (int)SkeletonEdgeBranchMode::Auto);
|
||||||
|
m_branchModeComboBox->insertItem(1, tr("No Trivial"), (int)SkeletonEdgeBranchMode::NoTrivial);
|
||||||
|
m_branchModeComboBox->insertItem(2, tr("Trivial"), (int)SkeletonEdgeBranchMode::Trivial);
|
||||||
|
|
||||||
|
QFormLayout *formLayout = new QFormLayout;
|
||||||
|
formLayout->addRow(tr("Joint Branch Mode:"), m_branchModeComboBox);
|
||||||
|
|
||||||
|
setLayout(formLayout);
|
||||||
|
|
||||||
|
hide();
|
||||||
|
|
||||||
|
connect(m_branchModeComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), [=](int index) {
|
||||||
|
if (-1 != index) {
|
||||||
|
int mode = m_branchModeComboBox->itemData(index).toInt();
|
||||||
|
emit setEdgeBranchMode(m_edgeId, static_cast<SkeletonEdgeBranchMode>(mode));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonEdgePropertyWidget::showProperties(QUuid edgeId)
|
||||||
|
{
|
||||||
|
m_edgeId = edgeId;
|
||||||
|
updateData();
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonEdgePropertyWidget::updateData()
|
||||||
|
{
|
||||||
|
const SkeletonEdge *edge = m_document->findEdge(m_edgeId);
|
||||||
|
if (nullptr == edge) {
|
||||||
|
hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int selectIndex = m_branchModeComboBox->findData((int)edge->branchMode);
|
||||||
|
m_branchModeComboBox->setCurrentIndex(selectIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
QUuid SkeletonEdgePropertyWidget::currentEdgeId()
|
||||||
|
{
|
||||||
|
return m_edgeId;
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
#ifndef SKELETON_EDGE_PROPERTY_WIDGET_H
|
||||||
|
#define SKELETON_EDGE_PROPERTY_WIDGET_H
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QObject>
|
||||||
|
#include "skeletondocument.h"
|
||||||
|
|
||||||
|
class SkeletonEdgePropertyWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
signals:
|
||||||
|
void setEdgeBranchMode(QUuid edgeId, SkeletonEdgeBranchMode mode);
|
||||||
|
public slots:
|
||||||
|
void showProperties(QUuid edgeId);
|
||||||
|
public:
|
||||||
|
SkeletonEdgePropertyWidget(const SkeletonDocument *document);
|
||||||
|
QUuid currentEdgeId();
|
||||||
|
private:
|
||||||
|
void updateData();
|
||||||
|
private:
|
||||||
|
QComboBox *m_branchModeComboBox;
|
||||||
|
const SkeletonDocument *m_document;
|
||||||
|
QUuid m_edgeId;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,74 +0,0 @@
|
||||||
#include <QPen>
|
|
||||||
#include "skeletoneditedgeitem.h"
|
|
||||||
#include "theme.h"
|
|
||||||
|
|
||||||
SkeletonEditEdgeItem::SkeletonEditEdgeItem(QGraphicsItem *parent) :
|
|
||||||
QGraphicsLineItem(parent),
|
|
||||||
m_firstNode(NULL),
|
|
||||||
m_secondNode(NULL)
|
|
||||||
{
|
|
||||||
setData(0, "edge");
|
|
||||||
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;
|
|
||||||
if (m_firstNode) {
|
|
||||||
QColor color = m_firstNode->sideColor();
|
|
||||||
color.setAlphaF(Theme::edgeAlpha);
|
|
||||||
pen.setColor(color);
|
|
||||||
} else if (m_secondNode) {
|
|
||||||
QColor color = m_secondNode->sideColor();
|
|
||||||
color.setAlphaF(Theme::edgeAlpha);
|
|
||||||
pen.setColor(color);
|
|
||||||
}
|
|
||||||
pen.setWidth(Theme::skeletonEdgeWidth);
|
|
||||||
setPen(pen);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SkeletonEditEdgeItem::setNodes(SkeletonEditNodeItem *first, SkeletonEditNodeItem *second)
|
|
||||||
{
|
|
||||||
m_firstNode = first;
|
|
||||||
m_secondNode = second;
|
|
||||||
updatePosition();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SkeletonEditEdgeItem::connects(SkeletonEditNodeItem *nodeItem)
|
|
||||||
{
|
|
||||||
return m_firstNode == nodeItem || m_secondNode == nodeItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SkeletonEditEdgeItem::updatePosition()
|
|
||||||
{
|
|
||||||
if (m_firstNode && m_secondNode) {
|
|
||||||
QLineF line(m_firstNode->origin(), m_secondNode->origin());
|
|
||||||
setLine(line);
|
|
||||||
}
|
|
||||||
updateAppearance();
|
|
||||||
}
|
|
||||||
|
|
||||||
SkeletonEditNodeItem *SkeletonEditEdgeItem::firstNode()
|
|
||||||
{
|
|
||||||
return m_firstNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
SkeletonEditNodeItem *SkeletonEditEdgeItem::secondNode()
|
|
||||||
{
|
|
||||||
return m_secondNode;
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
#ifndef SKELETON_EDIT_EDGE_ITEM_H
|
|
||||||
#define SKELETON_EDIT_EDGE_ITEM_H
|
|
||||||
#include <QGraphicsEllipseItem>
|
|
||||||
#include "skeletoneditnodeitem.h"
|
|
||||||
|
|
||||||
class SkeletonEditEdgeItem : public QGraphicsLineItem
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SkeletonEditEdgeItem(QGraphicsItem *parent = 0);
|
|
||||||
void setNodes(SkeletonEditNodeItem *first, SkeletonEditNodeItem *second);
|
|
||||||
void updatePosition();
|
|
||||||
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
|
|
|
@ -1,713 +0,0 @@
|
||||||
#include <QGraphicsPixmapItem>
|
|
||||||
#include <QXmlStreamWriter>
|
|
||||||
#include <QFile>
|
|
||||||
#include <QApplication>
|
|
||||||
#include <QGuiApplication>
|
|
||||||
#include <cmath>
|
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
|
||||||
#include <assert.h>
|
|
||||||
#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(true),
|
|
||||||
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<QGraphicsItem *>::iterator it;
|
|
||||||
QList<QGraphicsItem *> list = scene()->items();
|
|
||||||
for (it = list.begin(); it != list.end(); ++it) {
|
|
||||||
if ((*it)->data(0).toString() == "node") {
|
|
||||||
SkeletonEditNodeItem *nodeItem = static_cast<SkeletonEditNodeItem *>(*it);
|
|
||||||
if (nodeItem->shape().contains(pos)) {
|
|
||||||
return nodeItem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
SkeletonEditEdgeItem *SkeletonEditGraphicsView::findEdgeItemByPos(QPointF pos)
|
|
||||||
{
|
|
||||||
QList<QGraphicsItem *>::iterator it;
|
|
||||||
QList<QGraphicsItem *> list = scene()->items();
|
|
||||||
for (it = list.begin(); it != list.end(); ++it) {
|
|
||||||
if ((*it)->data(0).toString() == "edge") {
|
|
||||||
SkeletonEditEdgeItem *edgeItem = static_cast<SkeletonEditEdgeItem *>(*it);
|
|
||||||
if (edgeItem->shape().contains(pos)) {
|
|
||||||
return edgeItem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
SkeletonEditEdgeItem *SkeletonEditGraphicsView::findEdgeItemByNodePair(SkeletonEditNodeItem *first,
|
|
||||||
SkeletonEditNodeItem *second)
|
|
||||||
{
|
|
||||||
QList<QGraphicsItem *>::iterator it;
|
|
||||||
QList<QGraphicsItem *> list = scene()->items();
|
|
||||||
assert(first != second);
|
|
||||||
for (it = list.begin(); it != list.end(); ++it) {
|
|
||||||
if ((*it)->data(0).toString() == "edge") {
|
|
||||||
SkeletonEditEdgeItem *edgeItem = static_cast<SkeletonEditEdgeItem *>(*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<QGraphicsItem *>::iterator it;
|
|
||||||
QList<QGraphicsItem *> list = scene()->items();
|
|
||||||
for (it = list.begin(); it != list.end(); ++it) {
|
|
||||||
if ((*it)->data(0).toString() == "edge") {
|
|
||||||
SkeletonEditEdgeItem *edgeItem = static_cast<SkeletonEditEdgeItem *>(*it);
|
|
||||||
if (edgeItem->firstNode() == nodeItem || edgeItem->secondNode() == nodeItem) {
|
|
||||||
scene()->removeItem(edgeItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SkeletonEditGraphicsView::fetchNodeItemAndAllSidePairs(SkeletonEditNodeItem *nodeItem, std::vector<SkeletonEditNodeItem *> *sidePairs)
|
|
||||||
{
|
|
||||||
sidePairs->push_back(nodeItem);
|
|
||||||
sidePairs->push_back(nodeItem->nextSidePair());
|
|
||||||
sidePairs->push_back(nodeItem->nextSidePair()->nextSidePair());
|
|
||||||
}
|
|
||||||
|
|
||||||
void SkeletonEditGraphicsView::removeNodeItemAndSidePairs(SkeletonEditNodeItem *nodeItem)
|
|
||||||
{
|
|
||||||
std::vector<SkeletonEditNodeItem *> 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_Tab) {
|
|
||||||
if (m_nextStartNodeItem)
|
|
||||||
setNextStartNodeItem(m_nextStartNodeItem->nextSidePair());
|
|
||||||
} 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<QGraphicsItem *>::iterator it;
|
|
||||||
QList<QGraphicsItem *> list = scene()->items();
|
|
||||||
for (it = list.begin(); it != list.end(); ++it) {
|
|
||||||
if ((*it)->data(0).toString() == "edge") {
|
|
||||||
SkeletonEditEdgeItem *edgeItem = static_cast<SkeletonEditEdgeItem *>(*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);
|
|
||||||
AddItemRadius(m_lastHoverNodeItem->nextSidePair(), 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<QGraphicsItem *>::iterator it;
|
|
||||||
QList<QGraphicsItem *> list = scene()->items();
|
|
||||||
for (it = list.begin(); it != list.end(); ++it) {
|
|
||||||
if ((*it)->data(0).toString() == "node") {
|
|
||||||
SkeletonEditNodeItem *nodeItem = static_cast<SkeletonEditNodeItem *>(*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<SkeletonEditEdgeItem *>(*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<QString, SkeletonEditNodeItem *> nodeItemMap;
|
|
||||||
std::map<QString, std::map<QString, QString>>::iterator nodeIterator;
|
|
||||||
for (nodeIterator = snapshot->nodes.begin(); nodeIterator != snapshot->nodes.end(); nodeIterator++) {
|
|
||||||
std::map<QString, QString> *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<QString, QString> *snapshotNode = &nodeIterator->second;
|
|
||||||
SkeletonEditNodeItem *nodeItem = nodeItemMap[nodeIterator->first];
|
|
||||||
nodeItem->setNextSidePair(nodeItemMap[(*snapshotNode)["nextSidePair"]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<QString, std::map<QString, QString>>::iterator edgeIterator;
|
|
||||||
for (edgeIterator = snapshot->edges.begin(); edgeIterator != snapshot->edges.end(); edgeIterator++) {
|
|
||||||
std::map<QString, QString> *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<QGraphicsItem *>::iterator it;
|
|
||||||
QList<QGraphicsItem *> list = scene()->items();
|
|
||||||
|
|
||||||
int nextNodeId = 1;
|
|
||||||
std::map<SkeletonEditNodeItem *, QString> nodeIdMap;
|
|
||||||
for (it = list.begin(); it != list.end(); ++it) {
|
|
||||||
if ((*it)->data(0).toString() == "node") {
|
|
||||||
SkeletonEditNodeItem *nodeItem = static_cast<SkeletonEditNodeItem *>(*it);
|
|
||||||
QString nodeId = QString("node%1").arg(nextNodeId);
|
|
||||||
std::map<QString, QString> *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<SkeletonEditNodeItem *>(*it);
|
|
||||||
QString nodeId = nodeIdMap[nodeItem];
|
|
||||||
std::map<QString, QString> *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<SkeletonEditEdgeItem *>(*it);
|
|
||||||
QString edgeId = QString("edge%1").arg(nextEdgeId);
|
|
||||||
std::map<QString, QString> *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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,93 +0,0 @@
|
||||||
#ifndef SKELETON_EDIT_GRAPHICS_VIEW_H
|
|
||||||
#define SKELETON_EDIT_GRAPHICS_VIEW_H
|
|
||||||
#include <QGraphicsView>
|
|
||||||
#include <QMouseEvent>
|
|
||||||
#include <QWheelEvent>
|
|
||||||
#include <QKeyEvent>
|
|
||||||
#include <QXmlStreamWriter>
|
|
||||||
#include <QXmlStreamReader>
|
|
||||||
#include <QGraphicsProxyWidget>
|
|
||||||
#include "skeletoneditnodeitem.h"
|
|
||||||
#include "skeletoneditedgeitem.h"
|
|
||||||
#include "skeletonsnapshot.h"
|
|
||||||
#include "modelwidget.h"
|
|
||||||
|
|
||||||
class SkeletonEditGraphicsView : public QGraphicsView
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
signals:
|
|
||||||
void sizeChanged();
|
|
||||||
void nodesChanged();
|
|
||||||
void changeTurnaroundTriggered();
|
|
||||||
void edgeCheckStateChanged();
|
|
||||||
public slots:
|
|
||||||
void turnOffAddNodeMode();
|
|
||||||
void turnOnAddNodeMode();
|
|
||||||
void markAsBranch();
|
|
||||||
void markAsTrunk();
|
|
||||||
void markAsRoot();
|
|
||||||
void markAsChild();
|
|
||||||
bool mouseMove(QMouseEvent *event, const QPointF &scenePos);
|
|
||||||
bool wheel(QWheelEvent *event, const QPointF &scenePos);
|
|
||||||
bool mouseRelease(QMouseEvent *event, const QPointF &scenePos);
|
|
||||||
bool mousePress(QMouseEvent *event, const QPointF &scenePos);
|
|
||||||
bool mouseDoubleClick(QMouseEvent *event, const QPointF &scenePos);
|
|
||||||
bool keyPress(QKeyEvent *event, const QPointF &scenePos);
|
|
||||||
public:
|
|
||||||
SkeletonEditGraphicsView(QWidget *parent = 0);
|
|
||||||
void updateBackgroundImage(const QImage &image);
|
|
||||||
bool hasBackgroundImage();
|
|
||||||
QPixmap backgroundImage();
|
|
||||||
void saveToSnapshot(SkeletonSnapshot *snapshot);
|
|
||||||
void loadFromSnapshot(SkeletonSnapshot *snapshot);
|
|
||||||
ModelWidget *modelWidget();
|
|
||||||
bool combineEnabled();
|
|
||||||
bool unionEnabled();
|
|
||||||
bool subdivEnabled();
|
|
||||||
protected:
|
|
||||||
void mouseMoveEvent(QMouseEvent *event);
|
|
||||||
void wheelEvent(QWheelEvent *event);
|
|
||||||
void mouseReleaseEvent(QMouseEvent *event);
|
|
||||||
void mousePressEvent(QMouseEvent *event);
|
|
||||||
void mouseDoubleClickEvent(QMouseEvent *event);
|
|
||||||
void keyPressEvent(QKeyEvent *event);
|
|
||||||
void resizeEvent(QResizeEvent *event);
|
|
||||||
private:
|
|
||||||
QGraphicsPixmapItem *m_backgroundItem;
|
|
||||||
QGraphicsEllipseItem *m_pendingNodeItem;
|
|
||||||
QGraphicsLineItem *m_pendingEdgeItem;
|
|
||||||
static qreal m_initialNodeSize;
|
|
||||||
static qreal m_minimalNodeSize;
|
|
||||||
bool m_inAddNodeMode;
|
|
||||||
SkeletonEditNodeItem *m_nextStartNodeItem;
|
|
||||||
SkeletonEditNodeItem *m_lastHoverNodeItem;
|
|
||||||
QPointF m_lastMousePos;
|
|
||||||
bool m_isMovingNodeItem;
|
|
||||||
bool m_backgroundLoaded;
|
|
||||||
ModelWidget *m_modelWidget;
|
|
||||||
QGraphicsProxyWidget *m_modelWidgetProxy;
|
|
||||||
bool m_combineEnabled;
|
|
||||||
bool m_unionEnabled;
|
|
||||||
bool m_subdivEnabled;
|
|
||||||
private:
|
|
||||||
void toggleAddNodeMode();
|
|
||||||
void applyAddNodeMode();
|
|
||||||
SkeletonEditNodeItem *findNodeItemByPos(QPointF pos);
|
|
||||||
SkeletonEditEdgeItem *findEdgeItemByPos(QPointF pos);
|
|
||||||
SkeletonEditEdgeItem *findEdgeItemByNodePair(SkeletonEditNodeItem *first,
|
|
||||||
SkeletonEditNodeItem *second);
|
|
||||||
void setNextStartNodeItem(SkeletonEditNodeItem *item);
|
|
||||||
bool canNodeItemMoveTo(SkeletonEditNodeItem *item, QPointF moveTo);
|
|
||||||
void AddItemRadius(QGraphicsEllipseItem *item, float delta);
|
|
||||||
bool canAddItemRadius(QGraphicsEllipseItem *item, float delta);
|
|
||||||
void adjustItems(QSizeF oldSceneSize, QSizeF newSceneSize);
|
|
||||||
void removeSelectedItems();
|
|
||||||
void removeNodeItem(SkeletonEditNodeItem *nodeItem);
|
|
||||||
void removeNodeItemAndSidePairs(SkeletonEditNodeItem *nodeItem);
|
|
||||||
void fetchNodeItemAndAllSidePairs(SkeletonEditNodeItem *nodeItem, std::vector<SkeletonEditNodeItem *> *sidePairs);
|
|
||||||
SkeletonEditNodeItem *addNodeItemAndSidePairs(QRectF area, SkeletonEditNodeItem *fromNodeItem, const QString &sideColorName="red");
|
|
||||||
SkeletonEditNodeItem *addNodeItem(float originX, float originY, float radius);
|
|
||||||
void addEdgeItem(SkeletonEditNodeItem *first, SkeletonEditNodeItem *second);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,140 +0,0 @@
|
||||||
#include <QPen>
|
|
||||||
#include "skeletoneditnodeitem.h"
|
|
||||||
#include "theme.h"
|
|
||||||
|
|
||||||
SkeletonEditNodeItem::SkeletonEditNodeItem(const QRectF &rect, QGraphicsItem *parent) :
|
|
||||||
QGraphicsEllipseItem(rect, parent),
|
|
||||||
m_hovered(false),
|
|
||||||
m_checked(false),
|
|
||||||
m_nextSidePair(NULL),
|
|
||||||
m_sideColor(Theme::red),
|
|
||||||
m_sideColorName("red"),
|
|
||||||
m_isBranch(false),
|
|
||||||
m_isRoot(false)
|
|
||||||
{
|
|
||||||
setData(0, "node");
|
|
||||||
updateAppearance();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SkeletonEditNodeItem::markAsBranch(bool isBranch)
|
|
||||||
{
|
|
||||||
m_isBranch = isBranch;
|
|
||||||
updateAppearance();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SkeletonEditNodeItem::isBranch()
|
|
||||||
{
|
|
||||||
return m_isBranch;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SkeletonEditNodeItem::markAsRoot(bool isRoot)
|
|
||||||
{
|
|
||||||
m_isRoot = isRoot;
|
|
||||||
updateAppearance();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SkeletonEditNodeItem::isRoot()
|
|
||||||
{
|
|
||||||
return m_isRoot;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString &SkeletonEditNodeItem::sideColorName()
|
|
||||||
{
|
|
||||||
return m_sideColorName;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString SkeletonEditNodeItem::nextSideColorName()
|
|
||||||
{
|
|
||||||
return Theme::nextSideColorNameMap[m_sideColorName];
|
|
||||||
}
|
|
||||||
|
|
||||||
void SkeletonEditNodeItem::setSideColorName(const QString &name)
|
|
||||||
{
|
|
||||||
m_sideColorName = name;
|
|
||||||
m_sideColor = Theme::sideColorNameToColorMap[m_sideColorName];
|
|
||||||
updateAppearance();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SkeletonEditNodeItem::hovered()
|
|
||||||
{
|
|
||||||
return m_hovered;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SkeletonEditNodeItem::setHovered(bool hovered)
|
|
||||||
{
|
|
||||||
m_hovered = hovered;
|
|
||||||
updateAppearance();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SkeletonEditNodeItem::checked()
|
|
||||||
{
|
|
||||||
return m_checked;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SkeletonEditNodeItem::setChecked(bool checked)
|
|
||||||
{
|
|
||||||
m_checked = checked;
|
|
||||||
updateAppearance();
|
|
||||||
}
|
|
||||||
|
|
||||||
SkeletonEditNodeItem *SkeletonEditNodeItem::nextSidePair()
|
|
||||||
{
|
|
||||||
return m_nextSidePair;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SkeletonEditNodeItem::setNextSidePair(SkeletonEditNodeItem *nodeItem)
|
|
||||||
{
|
|
||||||
m_nextSidePair = nodeItem;
|
|
||||||
updateAppearance();
|
|
||||||
}
|
|
||||||
|
|
||||||
const QColor &SkeletonEditNodeItem::sideColor()
|
|
||||||
{
|
|
||||||
return m_sideColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
QPointF SkeletonEditNodeItem::origin()
|
|
||||||
{
|
|
||||||
return QPointF(rect().x() + rect().width() / 2,
|
|
||||||
rect().y() + rect().height() / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
float SkeletonEditNodeItem::radius()
|
|
||||||
{
|
|
||||||
return rect().width() / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SkeletonEditNodeItem::setRadius(float radius)
|
|
||||||
{
|
|
||||||
QPointF oldOrigin = origin();
|
|
||||||
setRect(oldOrigin.x() - radius, oldOrigin.y() - radius,
|
|
||||||
radius * 2, radius * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SkeletonEditNodeItem::setOrigin(QPointF point)
|
|
||||||
{
|
|
||||||
QPointF moveBy = point - origin();
|
|
||||||
QRectF newRect = rect();
|
|
||||||
newRect.adjust(moveBy.x(), moveBy.y(), moveBy.x(), moveBy.y());
|
|
||||||
setRect(newRect);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SkeletonEditNodeItem::updateAppearance()
|
|
||||||
{
|
|
||||||
QColor penColor = m_sideColor;
|
|
||||||
penColor.setAlphaF(m_checked ? Theme::checkedAlpha : (m_isBranch ? Theme::branchAlpha : Theme::normalAlpha));
|
|
||||||
QPen pen(penColor);
|
|
||||||
pen.setWidth(m_isRoot ? (Theme::skeletonNodeBorderSize * 2) : Theme::skeletonNodeBorderSize);
|
|
||||||
setPen(pen);
|
|
||||||
|
|
||||||
QColor brushColor = m_sideColor;
|
|
||||||
brushColor.setAlphaF((m_hovered || m_checked) ? Theme::fillAlpha : 0);
|
|
||||||
QBrush brush(brushColor);
|
|
||||||
setBrush(brush);
|
|
||||||
}
|
|
||||||
|
|
||||||
QColor SkeletonEditNodeItem::nextSideColor()
|
|
||||||
{
|
|
||||||
return Theme::sideColorNameToColorMap[Theme::nextSideColorNameMap[m_sideColorName]];
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
#ifndef SKELETON_EDIT_NODE_ITEM_H
|
|
||||||
#define SKELETON_EDIT_NODE_ITEM_H
|
|
||||||
#include <QGraphicsEllipseItem>
|
|
||||||
#include <map>
|
|
||||||
#include <QString>
|
|
||||||
#include <QColor>
|
|
||||||
|
|
||||||
class SkeletonEditEdgeItem;
|
|
||||||
|
|
||||||
class SkeletonEditNodeItem : public QGraphicsEllipseItem
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SkeletonEditNodeItem(const QRectF &rect, QGraphicsItem *parent = 0);
|
|
||||||
QPointF origin();
|
|
||||||
void setOrigin(QPointF point);
|
|
||||||
float radius();
|
|
||||||
void setRadius(float radius);
|
|
||||||
bool hovered();
|
|
||||||
void setHovered(bool hovered);
|
|
||||||
bool checked();
|
|
||||||
void setChecked(bool checked);
|
|
||||||
void markAsBranch(bool isBranch);
|
|
||||||
bool isBranch();
|
|
||||||
void markAsRoot(bool isRoot);
|
|
||||||
bool isRoot();
|
|
||||||
SkeletonEditNodeItem *nextSidePair();
|
|
||||||
void setNextSidePair(SkeletonEditNodeItem *nodeItem);
|
|
||||||
const QColor &sideColor();
|
|
||||||
QColor nextSideColor();
|
|
||||||
const QString &sideColorName();
|
|
||||||
QString nextSideColorName();
|
|
||||||
void setSideColorName(const QString &name);
|
|
||||||
private:
|
|
||||||
bool m_hovered;
|
|
||||||
bool m_checked;
|
|
||||||
SkeletonEditNodeItem *m_nextSidePair;
|
|
||||||
QColor m_sideColor;
|
|
||||||
QString m_sideColorName;
|
|
||||||
bool m_isBranch;
|
|
||||||
bool m_isRoot;
|
|
||||||
private:
|
|
||||||
void updateAppearance();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -0,0 +1,644 @@
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QScrollBar>
|
||||||
|
#include <QGuiApplication>
|
||||||
|
#include <cmath>
|
||||||
|
#include <QtGlobal>
|
||||||
|
#include <algorithm>
|
||||||
|
#include "skeletongraphicswidget.h"
|
||||||
|
#include "theme.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
SkeletonGraphicsWidget::SkeletonGraphicsWidget(const SkeletonDocument *document) :
|
||||||
|
m_document(document),
|
||||||
|
m_turnaroundChanged(false),
|
||||||
|
m_turnaroundLoader(nullptr),
|
||||||
|
m_dragStarted(false),
|
||||||
|
m_cursorNodeItem(nullptr),
|
||||||
|
m_cursorEdgeItem(nullptr),
|
||||||
|
m_checkedNodeItem(nullptr),
|
||||||
|
m_moveStarted(false),
|
||||||
|
m_hoveredNodeItem(nullptr),
|
||||||
|
m_hoveredEdgeItem(nullptr),
|
||||||
|
m_checkedEdgeItem(nullptr),
|
||||||
|
m_lastAddedX(0),
|
||||||
|
m_lastAddedY(0),
|
||||||
|
m_lastAddedZ(0)
|
||||||
|
{
|
||||||
|
setRenderHint(QPainter::Antialiasing, false);
|
||||||
|
setBackgroundBrush(QBrush(QWidget::palette().color(QWidget::backgroundRole()), Qt::SolidPattern));
|
||||||
|
setContentsMargins(0, 0, 0, 0);
|
||||||
|
setFrameStyle(QFrame::NoFrame);
|
||||||
|
|
||||||
|
setAlignment(Qt::AlignCenter);
|
||||||
|
|
||||||
|
setScene(new QGraphicsScene());
|
||||||
|
|
||||||
|
m_backgroundItem = new QGraphicsPixmapItem();
|
||||||
|
m_backgroundItem->setOpacity(0.25);
|
||||||
|
scene()->addItem(m_backgroundItem);
|
||||||
|
|
||||||
|
m_cursorNodeItem = new SkeletonGraphicsNodeItem();
|
||||||
|
m_cursorNodeItem->hide();
|
||||||
|
scene()->addItem(m_cursorNodeItem);
|
||||||
|
|
||||||
|
m_cursorEdgeItem = new SkeletonGraphicsEdgeItem();
|
||||||
|
m_cursorEdgeItem->hide();
|
||||||
|
scene()->addItem(m_cursorEdgeItem);
|
||||||
|
|
||||||
|
scene()->setSceneRect(rect());
|
||||||
|
|
||||||
|
setMouseTracking(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonGraphicsWidget::updateItems()
|
||||||
|
{
|
||||||
|
for (auto nodeItemIt = nodeItemMap.begin(); nodeItemIt != nodeItemMap.end(); nodeItemIt++) {
|
||||||
|
nodeRadiusChanged(nodeItemIt->first);
|
||||||
|
nodeOriginChanged(nodeItemIt->first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonGraphicsWidget::canvasResized()
|
||||||
|
{
|
||||||
|
updateTurnaround();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonGraphicsWidget::turnaroundChanged()
|
||||||
|
{
|
||||||
|
updateTurnaround();
|
||||||
|
setTransform(QTransform());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonGraphicsWidget::updateTurnaround()
|
||||||
|
{
|
||||||
|
if (m_document->turnaround.isNull())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_turnaroundChanged = true;
|
||||||
|
if (m_turnaroundLoader)
|
||||||
|
return;
|
||||||
|
|
||||||
|
qDebug() << "Fit turnaround to view size:" << parentWidget()->rect().size();
|
||||||
|
|
||||||
|
m_turnaroundChanged = false;
|
||||||
|
|
||||||
|
QThread *thread = new QThread;
|
||||||
|
m_turnaroundLoader = new TurnaroundLoader(m_document->turnaround,
|
||||||
|
parentWidget()->rect().size());
|
||||||
|
m_turnaroundLoader->moveToThread(thread);
|
||||||
|
connect(thread, SIGNAL(started()), m_turnaroundLoader, SLOT(process()));
|
||||||
|
connect(m_turnaroundLoader, SIGNAL(finished()), this, SLOT(turnaroundImageReady()));
|
||||||
|
connect(m_turnaroundLoader, SIGNAL(finished()), thread, SLOT(quit()));
|
||||||
|
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
|
||||||
|
thread->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonGraphicsWidget::turnaroundImageReady()
|
||||||
|
{
|
||||||
|
QImage *backgroundImage = m_turnaroundLoader->takeResultImage();
|
||||||
|
if (backgroundImage && backgroundImage->width() > 0 && backgroundImage->height() > 0) {
|
||||||
|
qDebug() << "Fit turnaround finished with image size:" << backgroundImage->size();
|
||||||
|
setFixedSize(backgroundImage->size());
|
||||||
|
scene()->setSceneRect(rect());
|
||||||
|
m_backgroundItem->setPixmap(QPixmap::fromImage(*backgroundImage));
|
||||||
|
updateItems();
|
||||||
|
} else {
|
||||||
|
qDebug() << "Fit turnaround failed";
|
||||||
|
}
|
||||||
|
delete backgroundImage;
|
||||||
|
delete m_turnaroundLoader;
|
||||||
|
m_turnaroundLoader = nullptr;
|
||||||
|
|
||||||
|
if (m_turnaroundChanged) {
|
||||||
|
updateTurnaround();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonGraphicsWidget::updateCursor()
|
||||||
|
{
|
||||||
|
if (SkeletonDocumentEditMode::Add != m_document->editMode) {
|
||||||
|
m_cursorEdgeItem->hide();
|
||||||
|
m_cursorNodeItem->hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (m_document->editMode) {
|
||||||
|
case SkeletonDocumentEditMode::Add:
|
||||||
|
setCursor(QCursor(Theme::awesome()->icon(fa::plus).pixmap(Theme::toolIconFontSize, Theme::toolIconFontSize)));
|
||||||
|
break;
|
||||||
|
case SkeletonDocumentEditMode::Select:
|
||||||
|
setCursor(QCursor(Theme::awesome()->icon(fa::mousepointer).pixmap(Theme::toolIconFontSize, Theme::toolIconFontSize)));
|
||||||
|
break;
|
||||||
|
case SkeletonDocumentEditMode::Drag:
|
||||||
|
setCursor(QCursor(Theme::awesome()->icon(m_dragStarted ? fa::handrocko : fa::handpapero).pixmap(Theme::toolIconFontSize, Theme::toolIconFontSize)));
|
||||||
|
break;
|
||||||
|
case SkeletonDocumentEditMode::ZoomIn:
|
||||||
|
setCursor(QCursor(Theme::awesome()->icon(fa::searchplus).pixmap(Theme::toolIconFontSize, Theme::toolIconFontSize)));
|
||||||
|
break;
|
||||||
|
case SkeletonDocumentEditMode::ZoomOut:
|
||||||
|
setCursor(QCursor(Theme::awesome()->icon(fa::searchminus).pixmap(Theme::toolIconFontSize, Theme::toolIconFontSize)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit cursorChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonGraphicsWidget::editModeChanged()
|
||||||
|
{
|
||||||
|
if (m_checkedNodeItem) {
|
||||||
|
if (!m_checkedNodeItem->checked()) {
|
||||||
|
if (m_hoveredNodeItem == m_checkedNodeItem) {
|
||||||
|
m_checkedNodeItem->setHovered(false);
|
||||||
|
m_hoveredNodeItem = nullptr;
|
||||||
|
}
|
||||||
|
m_checkedNodeItem = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonGraphicsWidget::mouseMoveEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
QGraphicsView::mouseMoveEvent(event);
|
||||||
|
mouseMove(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonGraphicsWidget::wheelEvent(QWheelEvent *event)
|
||||||
|
{
|
||||||
|
if (SkeletonDocumentEditMode::ZoomIn == m_document->editMode ||
|
||||||
|
SkeletonDocumentEditMode::ZoomOut == m_document->editMode ||
|
||||||
|
SkeletonDocumentEditMode::Drag == m_document->editMode)
|
||||||
|
QGraphicsView::wheelEvent(event);
|
||||||
|
wheel(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonGraphicsWidget::mouseReleaseEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
QGraphicsView::mouseReleaseEvent(event);
|
||||||
|
mouseRelease(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonGraphicsWidget::mousePressEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
QGraphicsView::mousePressEvent(event);
|
||||||
|
mousePress(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonGraphicsWidget::mouseDoubleClickEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
QGraphicsView::mouseDoubleClickEvent(event);
|
||||||
|
mouseDoubleClick(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonGraphicsWidget::keyPressEvent(QKeyEvent *event)
|
||||||
|
{
|
||||||
|
QGraphicsView::keyPressEvent(event);
|
||||||
|
keyPress(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SkeletonGraphicsWidget::mouseMove(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
if (m_dragStarted) {
|
||||||
|
QPoint currentGlobalPos = event->globalPos();
|
||||||
|
if (verticalScrollBar())
|
||||||
|
verticalScrollBar()->setValue(verticalScrollBar()->value() + m_lastGlobalPos.y() - currentGlobalPos.y());
|
||||||
|
if (horizontalScrollBar())
|
||||||
|
horizontalScrollBar()->setValue(horizontalScrollBar()->value() + m_lastGlobalPos.x() - currentGlobalPos.x());
|
||||||
|
m_lastGlobalPos = currentGlobalPos;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SkeletonDocumentEditMode::Select == m_document->editMode ||
|
||||||
|
SkeletonDocumentEditMode::Add == m_document->editMode) {
|
||||||
|
SkeletonGraphicsNodeItem *newHoverNodeItem = nullptr;
|
||||||
|
SkeletonGraphicsEdgeItem *newHoverEdgeItem = nullptr;
|
||||||
|
QList<QGraphicsItem *> items = scene()->items(mouseEventScenePos(event));
|
||||||
|
for (auto it = items.begin(); it != items.end(); it++) {
|
||||||
|
QGraphicsItem *item = *it;
|
||||||
|
if (item->data(0) == "node") {
|
||||||
|
newHoverNodeItem = (SkeletonGraphicsNodeItem *)item;
|
||||||
|
break;
|
||||||
|
} else if (item->data(0) == "edge") {
|
||||||
|
newHoverEdgeItem = (SkeletonGraphicsEdgeItem *)item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newHoverNodeItem) {
|
||||||
|
newHoverEdgeItem = nullptr;
|
||||||
|
}
|
||||||
|
if (newHoverNodeItem != m_hoveredNodeItem) {
|
||||||
|
if (nullptr != m_hoveredNodeItem) {
|
||||||
|
m_hoveredNodeItem->setHovered(false);
|
||||||
|
}
|
||||||
|
m_hoveredNodeItem = newHoverNodeItem;
|
||||||
|
if (nullptr != m_hoveredNodeItem) {
|
||||||
|
m_hoveredNodeItem->setHovered(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newHoverEdgeItem != m_hoveredEdgeItem) {
|
||||||
|
if (nullptr != m_hoveredEdgeItem) {
|
||||||
|
m_hoveredEdgeItem->setHovered(false);
|
||||||
|
}
|
||||||
|
m_hoveredEdgeItem = newHoverEdgeItem;
|
||||||
|
if (nullptr != m_hoveredEdgeItem) {
|
||||||
|
m_hoveredEdgeItem->setHovered(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SkeletonDocumentEditMode::Add == m_document->editMode) {
|
||||||
|
QPointF mouseScenePos = mouseEventScenePos(event);
|
||||||
|
m_cursorNodeItem->setOrigin(mouseScenePos);
|
||||||
|
if (!m_cursorNodeItem->isVisible()) {
|
||||||
|
m_cursorNodeItem->show();
|
||||||
|
}
|
||||||
|
if (m_checkedNodeItem) {
|
||||||
|
m_cursorEdgeItem->setEndpoints(m_checkedNodeItem, m_cursorNodeItem);
|
||||||
|
if (!m_cursorEdgeItem->isVisible()) {
|
||||||
|
m_cursorEdgeItem->show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SkeletonDocumentEditMode::Select == m_document->editMode) {
|
||||||
|
if (m_moveStarted && m_checkedNodeItem) {
|
||||||
|
QPointF mouseScenePos = mouseEventScenePos(event);
|
||||||
|
float byX = sceneRadiusToUnified(mouseScenePos.x() - m_lastScenePos.x());
|
||||||
|
float byY = sceneRadiusToUnified(mouseScenePos.y() - m_lastScenePos.y());
|
||||||
|
if (SkeletonProfile::Main == m_checkedNodeItem->profile()) {
|
||||||
|
emit moveNodeBy(m_checkedNodeItem->id(), byX, byY, 0);
|
||||||
|
} else {
|
||||||
|
emit moveNodeBy(m_checkedNodeItem->id(), 0, byY, byX);
|
||||||
|
}
|
||||||
|
m_lastScenePos = mouseScenePos;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SkeletonGraphicsWidget::wheel(QWheelEvent *event)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
if (SkeletonDocumentEditMode::Add == m_document->editMode) {
|
||||||
|
if (m_cursorNodeItem->isVisible()) {
|
||||||
|
m_cursorNodeItem->setRadius(m_cursorNodeItem->radius() + delta);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (SkeletonDocumentEditMode::Select == m_document->editMode) {
|
||||||
|
if (m_hoveredNodeItem) {
|
||||||
|
emit scaleNodeByAddRadius(m_hoveredNodeItem->id(), sceneRadiusToUnified(delta));
|
||||||
|
return true;
|
||||||
|
} else if (m_checkedNodeItem) {
|
||||||
|
emit scaleNodeByAddRadius(m_checkedNodeItem->id(), sceneRadiusToUnified(delta));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SkeletonGraphicsWidget::mouseRelease(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
if (event->button() == Qt::LeftButton) {
|
||||||
|
bool processed = m_dragStarted || m_moveStarted;
|
||||||
|
if (m_dragStarted) {
|
||||||
|
m_dragStarted = false;
|
||||||
|
updateCursor();
|
||||||
|
}
|
||||||
|
if (m_moveStarted) {
|
||||||
|
m_moveStarted = false;
|
||||||
|
}
|
||||||
|
return processed;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF SkeletonGraphicsWidget::mouseEventScenePos(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
return mapToScene(mapFromGlobal(event->globalPos()));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SkeletonGraphicsWidget::mousePress(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
if (event->button() == Qt::LeftButton) {
|
||||||
|
if (SkeletonDocumentEditMode::ZoomIn == m_document->editMode) {
|
||||||
|
ViewportAnchor lastAnchor = transformationAnchor();
|
||||||
|
setTransformationAnchor(QGraphicsView::AnchorUnderMouse) ;
|
||||||
|
scale(1.5, 1.5);
|
||||||
|
setTransformationAnchor(lastAnchor);
|
||||||
|
return true;
|
||||||
|
} else if (SkeletonDocumentEditMode::ZoomOut == m_document->editMode) {
|
||||||
|
ViewportAnchor lastAnchor = transformationAnchor();
|
||||||
|
setTransformationAnchor(QGraphicsView::AnchorUnderMouse) ;
|
||||||
|
scale(0.5, 0.5);
|
||||||
|
setTransformationAnchor(lastAnchor);
|
||||||
|
if ((!verticalScrollBar() || !verticalScrollBar()->isVisible()) &&
|
||||||
|
(!horizontalScrollBar() || !horizontalScrollBar()->isVisible())) {
|
||||||
|
setTransform(QTransform());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else if (SkeletonDocumentEditMode::Drag == m_document->editMode) {
|
||||||
|
if (!m_dragStarted) {
|
||||||
|
m_lastGlobalPos = event->globalPos();
|
||||||
|
m_dragStarted = true;
|
||||||
|
updateCursor();
|
||||||
|
}
|
||||||
|
} else if (SkeletonDocumentEditMode::Add == m_document->editMode) {
|
||||||
|
if (m_cursorNodeItem->isVisible()) {
|
||||||
|
if (m_checkedNodeItem) {
|
||||||
|
if (m_hoveredNodeItem && m_checkedNodeItem &&
|
||||||
|
m_hoveredNodeItem != m_checkedNodeItem &&
|
||||||
|
m_hoveredNodeItem->profile() == m_checkedNodeItem->profile()) {
|
||||||
|
if (m_document->findEdgeByNodes(m_checkedNodeItem->id(), m_hoveredNodeItem->id()))
|
||||||
|
return true;
|
||||||
|
emit addEdge(m_checkedNodeItem->id(), m_hoveredNodeItem->id());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QPointF mainProfile = m_cursorNodeItem->origin();
|
||||||
|
QPointF sideProfile = mainProfile;
|
||||||
|
if (mainProfile.x() >= scene()->width() / 2) {
|
||||||
|
sideProfile.setX(mainProfile.x() - scene()->width() / 4);
|
||||||
|
} else {
|
||||||
|
sideProfile.setX(mainProfile.x() +scene()->width() / 4);
|
||||||
|
}
|
||||||
|
QPointF unifiedMainPos = scenePosToUnified(mainProfile);
|
||||||
|
QPointF unifiedSidePos = scenePosToUnified(sideProfile);
|
||||||
|
if (isFloatEqual(m_lastAddedX, unifiedMainPos.x()) && isFloatEqual(m_lastAddedY, unifiedMainPos.y()) && isFloatEqual(m_lastAddedZ, unifiedSidePos.x()))
|
||||||
|
return true;
|
||||||
|
m_lastAddedX = unifiedMainPos.x();
|
||||||
|
m_lastAddedY = unifiedMainPos.y();
|
||||||
|
m_lastAddedZ = unifiedSidePos.x();
|
||||||
|
qDebug() << "Emit add node " << m_lastAddedX << m_lastAddedY << m_lastAddedZ;
|
||||||
|
emit addNode(unifiedMainPos.x(), unifiedMainPos.y(), unifiedSidePos.x(), sceneRadiusToUnified(m_cursorNodeItem->radius()), nullptr == m_checkedNodeItem ? QUuid() : m_checkedNodeItem->id());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (SkeletonDocumentEditMode::Select == m_document->editMode) {
|
||||||
|
bool processed = false;
|
||||||
|
if (m_hoveredNodeItem) {
|
||||||
|
if (m_checkedNodeItem != m_hoveredNodeItem) {
|
||||||
|
if (m_checkedNodeItem) {
|
||||||
|
emit uncheckNode(m_checkedNodeItem->id());
|
||||||
|
m_checkedNodeItem->setChecked(false);
|
||||||
|
}
|
||||||
|
m_checkedNodeItem = m_hoveredNodeItem;
|
||||||
|
m_checkedNodeItem->setChecked(true);
|
||||||
|
emit checkNode(m_checkedNodeItem->id());
|
||||||
|
}
|
||||||
|
m_moveStarted = true;
|
||||||
|
m_lastScenePos = mouseEventScenePos(event);
|
||||||
|
processed = true;
|
||||||
|
} else {
|
||||||
|
if (m_checkedNodeItem) {
|
||||||
|
m_checkedNodeItem->setChecked(false);
|
||||||
|
emit uncheckNode(m_checkedNodeItem->id());
|
||||||
|
m_checkedNodeItem = nullptr;
|
||||||
|
processed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m_hoveredEdgeItem) {
|
||||||
|
if (m_checkedEdgeItem != m_hoveredEdgeItem) {
|
||||||
|
if (m_checkedEdgeItem) {
|
||||||
|
emit uncheckEdge(m_checkedEdgeItem->id());
|
||||||
|
m_checkedEdgeItem->setChecked(false);
|
||||||
|
}
|
||||||
|
m_checkedEdgeItem = m_hoveredEdgeItem;
|
||||||
|
m_checkedEdgeItem->setChecked(true);
|
||||||
|
emit checkEdge(m_checkedEdgeItem->id());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (m_checkedEdgeItem) {
|
||||||
|
m_checkedEdgeItem->setChecked(false);
|
||||||
|
emit uncheckEdge(m_checkedEdgeItem->id());
|
||||||
|
m_checkedEdgeItem = nullptr;
|
||||||
|
processed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (processed) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
float SkeletonGraphicsWidget::sceneRadiusToUnified(float radius)
|
||||||
|
{
|
||||||
|
if (0 == scene()->height())
|
||||||
|
return 0;
|
||||||
|
return radius / scene()->height();
|
||||||
|
}
|
||||||
|
|
||||||
|
float SkeletonGraphicsWidget::sceneRadiusFromUnified(float radius)
|
||||||
|
{
|
||||||
|
return radius * scene()->height();
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF SkeletonGraphicsWidget::scenePosToUnified(QPointF pos)
|
||||||
|
{
|
||||||
|
if (0 == scene()->height())
|
||||||
|
return QPointF(0, 0);
|
||||||
|
return QPointF(pos.x() / scene()->height(), pos.y() / scene()->height());
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF SkeletonGraphicsWidget::scenePosFromUnified(QPointF pos)
|
||||||
|
{
|
||||||
|
return QPointF(pos.x() * scene()->height(), pos.y() * scene()->height());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SkeletonGraphicsWidget::mouseDoubleClick(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SkeletonGraphicsWidget::keyPress(QKeyEvent *event)
|
||||||
|
{
|
||||||
|
if (event->key() == Qt::Key_Delete || event->key() ==Qt::Key_Backspace) {
|
||||||
|
bool processed = false;
|
||||||
|
if (m_checkedNodeItem) {
|
||||||
|
emit removeNode(m_checkedNodeItem->id());
|
||||||
|
processed = true;
|
||||||
|
}
|
||||||
|
if (m_checkedEdgeItem) {
|
||||||
|
emit removeEdge(m_checkedEdgeItem->id());
|
||||||
|
processed = true;
|
||||||
|
}
|
||||||
|
if (processed) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (event->key() == Qt::Key_A) {
|
||||||
|
if (SkeletonDocumentEditMode::Add == m_document->editMode) {
|
||||||
|
emit setEditMode(SkeletonDocumentEditMode::Select);
|
||||||
|
} else {
|
||||||
|
emit setEditMode(SkeletonDocumentEditMode::Add);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonGraphicsWidget::nodeAdded(QUuid nodeId)
|
||||||
|
{
|
||||||
|
const SkeletonNode *node = m_document->findNode(nodeId);
|
||||||
|
if (nullptr == node) {
|
||||||
|
qDebug() << "New node added but node id not exist:" << nodeId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SkeletonGraphicsNodeItem *mainProfileItem = new SkeletonGraphicsNodeItem(SkeletonProfile::Main);
|
||||||
|
SkeletonGraphicsNodeItem *sideProfileItem = new SkeletonGraphicsNodeItem(SkeletonProfile::Side);
|
||||||
|
mainProfileItem->setOrigin(scenePosFromUnified(QPointF(node->x, node->y)));
|
||||||
|
sideProfileItem->setOrigin(scenePosFromUnified(QPointF(node->z, node->y)));
|
||||||
|
mainProfileItem->setRadius(sceneRadiusFromUnified(node->radius));
|
||||||
|
sideProfileItem->setRadius(sceneRadiusFromUnified(node->radius));
|
||||||
|
mainProfileItem->setId(nodeId);
|
||||||
|
sideProfileItem->setId(nodeId);
|
||||||
|
scene()->addItem(mainProfileItem);
|
||||||
|
scene()->addItem(sideProfileItem);
|
||||||
|
nodeItemMap[nodeId] = std::make_pair(mainProfileItem, sideProfileItem);
|
||||||
|
|
||||||
|
if (nullptr == m_checkedNodeItem) {
|
||||||
|
m_checkedNodeItem = mainProfileItem;
|
||||||
|
} else {
|
||||||
|
if (SkeletonProfile::Main == m_checkedNodeItem->profile()) {
|
||||||
|
m_checkedNodeItem = mainProfileItem;
|
||||||
|
} else {
|
||||||
|
m_checkedNodeItem = sideProfileItem;
|
||||||
|
}
|
||||||
|
m_cursorEdgeItem->setEndpoints(m_checkedNodeItem, m_cursorNodeItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonGraphicsWidget::edgeAdded(QUuid edgeId)
|
||||||
|
{
|
||||||
|
const SkeletonEdge *edge = m_document->findEdge(edgeId);
|
||||||
|
if (nullptr == edge) {
|
||||||
|
qDebug() << "New edge added but edge id not exist:" << edgeId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (edge->nodeIds.size() != 2) {
|
||||||
|
qDebug() << "Invalid node count of edge:" << edgeId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QUuid fromNodeId = edge->nodeIds[0];
|
||||||
|
QUuid toNodeId = edge->nodeIds[1];
|
||||||
|
auto fromIt = nodeItemMap.find(fromNodeId);
|
||||||
|
if (fromIt == nodeItemMap.end()) {
|
||||||
|
qDebug() << "Node not found:" << fromNodeId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto toIt = nodeItemMap.find(toNodeId);
|
||||||
|
if (toIt == nodeItemMap.end()) {
|
||||||
|
qDebug() << "Node not found:" << toNodeId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SkeletonGraphicsEdgeItem *mainProfileEdgeItem = new SkeletonGraphicsEdgeItem();
|
||||||
|
SkeletonGraphicsEdgeItem *sideProfileEdgeItem = new SkeletonGraphicsEdgeItem();
|
||||||
|
mainProfileEdgeItem->setId(edgeId);
|
||||||
|
sideProfileEdgeItem->setId(edgeId);
|
||||||
|
mainProfileEdgeItem->setEndpoints(fromIt->second.first, toIt->second.first);
|
||||||
|
sideProfileEdgeItem->setEndpoints(fromIt->second.second, toIt->second.second);
|
||||||
|
scene()->addItem(mainProfileEdgeItem);
|
||||||
|
scene()->addItem(sideProfileEdgeItem);
|
||||||
|
edgeItemMap[edgeId] = std::make_pair(mainProfileEdgeItem, sideProfileEdgeItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonGraphicsWidget::nodeRemoved(QUuid nodeId)
|
||||||
|
{
|
||||||
|
m_lastAddedX = 0;
|
||||||
|
m_lastAddedY = 0;
|
||||||
|
m_lastAddedZ = 0;
|
||||||
|
auto nodeItemIt = nodeItemMap.find(nodeId);
|
||||||
|
if (nodeItemIt == nodeItemMap.end()) {
|
||||||
|
qDebug() << "Node removed but node id not exist:" << nodeId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_hoveredNodeItem == nodeItemIt->second.first)
|
||||||
|
m_hoveredNodeItem = nullptr;
|
||||||
|
if (m_hoveredNodeItem == nodeItemIt->second.second)
|
||||||
|
m_hoveredNodeItem = nullptr;
|
||||||
|
if (m_checkedNodeItem == nodeItemIt->second.first)
|
||||||
|
m_checkedNodeItem = nullptr;
|
||||||
|
if (m_checkedNodeItem == nodeItemIt->second.second)
|
||||||
|
m_checkedNodeItem = nullptr;
|
||||||
|
delete nodeItemIt->second.first;
|
||||||
|
delete nodeItemIt->second.second;
|
||||||
|
nodeItemMap.erase(nodeItemIt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonGraphicsWidget::edgeRemoved(QUuid edgeId)
|
||||||
|
{
|
||||||
|
auto edgeItemIt = edgeItemMap.find(edgeId);
|
||||||
|
if (edgeItemIt == edgeItemMap.end()) {
|
||||||
|
qDebug() << "Edge removed but edge id not exist:" << edgeId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_hoveredEdgeItem == edgeItemIt->second.first)
|
||||||
|
m_hoveredEdgeItem = nullptr;
|
||||||
|
if (m_hoveredEdgeItem == edgeItemIt->second.second)
|
||||||
|
m_hoveredEdgeItem = nullptr;
|
||||||
|
if (m_checkedEdgeItem == edgeItemIt->second.first)
|
||||||
|
m_checkedEdgeItem = nullptr;
|
||||||
|
if (m_checkedEdgeItem == edgeItemIt->second.second)
|
||||||
|
m_checkedEdgeItem = nullptr;
|
||||||
|
delete edgeItemIt->second.first;
|
||||||
|
delete edgeItemIt->second.second;
|
||||||
|
edgeItemMap.erase(edgeItemIt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonGraphicsWidget::nodeRadiusChanged(QUuid nodeId)
|
||||||
|
{
|
||||||
|
const SkeletonNode *node = m_document->findNode(nodeId);
|
||||||
|
if (nullptr == node) {
|
||||||
|
qDebug() << "Node changed but node id not exist:" << nodeId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto it = nodeItemMap.find(nodeId);
|
||||||
|
if (it == nodeItemMap.end()) {
|
||||||
|
qDebug() << "Node not found:" << nodeId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
float sceneRadius = sceneRadiusFromUnified(node->radius);
|
||||||
|
it->second.first->setRadius(sceneRadius);
|
||||||
|
it->second.second->setRadius(sceneRadius);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonGraphicsWidget::nodeOriginChanged(QUuid nodeId)
|
||||||
|
{
|
||||||
|
const SkeletonNode *node = m_document->findNode(nodeId);
|
||||||
|
if (nullptr == node) {
|
||||||
|
qDebug() << "Node changed but node id not exist:" << nodeId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto it = nodeItemMap.find(nodeId);
|
||||||
|
if (it == nodeItemMap.end()) {
|
||||||
|
qDebug() << "Node not found:" << nodeId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QPointF mainPos = scenePosFromUnified(QPointF(node->x, node->y));
|
||||||
|
QPointF sidePos = scenePosFromUnified(QPointF(node->z, node->y));
|
||||||
|
it->second.first->setOrigin(mainPos);
|
||||||
|
it->second.second->setOrigin(sidePos);
|
||||||
|
for (auto edgeIt = node->edgeIds.begin(); edgeIt != node->edgeIds.end(); edgeIt++) {
|
||||||
|
auto edgeItemIt = edgeItemMap.find(*edgeIt);
|
||||||
|
if (edgeItemIt == edgeItemMap.end()) {
|
||||||
|
qDebug() << "Edge item not found:" << *edgeIt;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
edgeItemIt->second.first->updateAppearance();
|
||||||
|
edgeItemIt->second.second->updateAppearance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonGraphicsWidget::edgeChanged(QUuid edgeId)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,320 @@
|
||||||
|
#ifndef SKELETON_GRAPHICS_VIEW_H
|
||||||
|
#define SKELETON_GRAPHICS_VIEW_H
|
||||||
|
#include <map>
|
||||||
|
#include <QMouseEvent>
|
||||||
|
#include <QKeyEvent>
|
||||||
|
#include <QGraphicsEllipseItem>
|
||||||
|
#include <QGraphicsLineItem>
|
||||||
|
#include <QGraphicsView>
|
||||||
|
#include <QGraphicsPixmapItem>
|
||||||
|
#include <QThread>
|
||||||
|
#include <cmath>
|
||||||
|
#include "skeletondocument.h"
|
||||||
|
#include "turnaroundloader.h"
|
||||||
|
#include "theme.h"
|
||||||
|
|
||||||
|
class SkeletonGraphicsNodeItem : public QGraphicsEllipseItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SkeletonGraphicsNodeItem(SkeletonProfile profile=SkeletonProfile::Unknown) :
|
||||||
|
m_profile(profile),
|
||||||
|
m_hovered(false),
|
||||||
|
m_checked(false)
|
||||||
|
{
|
||||||
|
setData(0, "node");
|
||||||
|
setRadius(32);
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateAppearance()
|
||||||
|
{
|
||||||
|
QColor color = Theme::white;
|
||||||
|
|
||||||
|
switch (m_profile)
|
||||||
|
{
|
||||||
|
case SkeletonProfile::Unknown:
|
||||||
|
break;
|
||||||
|
case SkeletonProfile::Main:
|
||||||
|
color = Theme::red;
|
||||||
|
break;
|
||||||
|
case SkeletonProfile::Side:
|
||||||
|
color = Theme::green;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor penColor = color;
|
||||||
|
penColor.setAlphaF(m_checked ? Theme::checkedAlpha : Theme::normalAlpha);
|
||||||
|
QPen pen(penColor);
|
||||||
|
pen.setWidth(0);
|
||||||
|
setPen(pen);
|
||||||
|
|
||||||
|
QColor brushColor = color;
|
||||||
|
brushColor.setAlphaF((m_checked || m_hovered) ? Theme::fillAlpha : 0);
|
||||||
|
QBrush brush(brushColor);
|
||||||
|
setBrush(brush);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOrigin(QPointF point)
|
||||||
|
{
|
||||||
|
QPointF moveBy = point - origin();
|
||||||
|
QRectF newRect = rect();
|
||||||
|
newRect.adjust(moveBy.x(), moveBy.y(), moveBy.x(), moveBy.y());
|
||||||
|
setRect(newRect);
|
||||||
|
updateAppearance();
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF origin()
|
||||||
|
{
|
||||||
|
return QPointF(rect().x() + rect().width() / 2,
|
||||||
|
rect().y() + rect().height() / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
float radius()
|
||||||
|
{
|
||||||
|
return rect().width() / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setRadius(float radius)
|
||||||
|
{
|
||||||
|
if (radius < 4)
|
||||||
|
radius = 4;
|
||||||
|
QPointF oldOrigin = origin();
|
||||||
|
setRect(oldOrigin.x() - radius, oldOrigin.y() - radius,
|
||||||
|
radius * 2, radius * 2);
|
||||||
|
updateAppearance();
|
||||||
|
}
|
||||||
|
|
||||||
|
SkeletonProfile profile()
|
||||||
|
{
|
||||||
|
return m_profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
QUuid id()
|
||||||
|
{
|
||||||
|
return m_uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setId(QUuid id)
|
||||||
|
{
|
||||||
|
m_uuid = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setHovered(bool hovered)
|
||||||
|
{
|
||||||
|
m_hovered = hovered;
|
||||||
|
updateAppearance();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setChecked(bool checked)
|
||||||
|
{
|
||||||
|
m_checked = checked;
|
||||||
|
updateAppearance();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checked()
|
||||||
|
{
|
||||||
|
return m_checked;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
QUuid m_uuid;
|
||||||
|
SkeletonProfile m_profile;
|
||||||
|
bool m_hovered;
|
||||||
|
bool m_checked;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SkeletonGraphicsEdgeItem : public QGraphicsPolygonItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SkeletonGraphicsEdgeItem() :
|
||||||
|
m_firstItem(nullptr),
|
||||||
|
m_secondItem(nullptr),
|
||||||
|
m_hovered(false),
|
||||||
|
m_checked(false)
|
||||||
|
{
|
||||||
|
setData(0, "edge");
|
||||||
|
}
|
||||||
|
|
||||||
|
void setEndpoints(SkeletonGraphicsNodeItem *first, SkeletonGraphicsNodeItem *second)
|
||||||
|
{
|
||||||
|
m_firstItem = first;
|
||||||
|
m_secondItem = second;
|
||||||
|
updateAppearance();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateAppearance()
|
||||||
|
{
|
||||||
|
if (nullptr == m_firstItem || nullptr == m_secondItem)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QLineF line(m_firstItem->origin(), m_secondItem->origin());
|
||||||
|
|
||||||
|
QPolygonF polygon;
|
||||||
|
float radAngle = line.angle() * M_PI / 180;
|
||||||
|
float dx = 5 * sin(radAngle);
|
||||||
|
float dy = 5 * cos(radAngle);
|
||||||
|
QPointF offset1 = QPointF(dx, dy);
|
||||||
|
QPointF offset2 = QPointF(-dx, -dy);
|
||||||
|
polygon << line.p1() + offset1 << line.p1() + offset2 << line.p2() + offset2 << line.p2() + offset1;
|
||||||
|
setPolygon(polygon);
|
||||||
|
|
||||||
|
QColor color = Theme::white;
|
||||||
|
|
||||||
|
switch (m_firstItem->profile())
|
||||||
|
{
|
||||||
|
case SkeletonProfile::Unknown:
|
||||||
|
break;
|
||||||
|
case SkeletonProfile::Main:
|
||||||
|
color = Theme::red;
|
||||||
|
break;
|
||||||
|
case SkeletonProfile::Side:
|
||||||
|
color = Theme::green;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor penColor = color;
|
||||||
|
penColor.setAlphaF((m_checked || m_hovered) ? Theme::checkedAlpha : Theme::normalAlpha);
|
||||||
|
QPen pen(penColor);
|
||||||
|
pen.setWidth(0);
|
||||||
|
setPen(pen);
|
||||||
|
}
|
||||||
|
|
||||||
|
QUuid id()
|
||||||
|
{
|
||||||
|
return m_uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setId(QUuid id)
|
||||||
|
{
|
||||||
|
m_uuid = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setHovered(bool hovered)
|
||||||
|
{
|
||||||
|
m_hovered = hovered;
|
||||||
|
updateAppearance();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setChecked(bool checked)
|
||||||
|
{
|
||||||
|
m_checked = checked;
|
||||||
|
updateAppearance();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QUuid m_uuid;
|
||||||
|
SkeletonGraphicsNodeItem *m_firstItem;
|
||||||
|
SkeletonGraphicsNodeItem *m_secondItem;
|
||||||
|
QPolygonF m_selectionPolygon;
|
||||||
|
bool m_hovered;
|
||||||
|
bool m_checked;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SkeletonGraphicsFunctions
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual bool mouseMove(QMouseEvent *event) = 0;
|
||||||
|
virtual bool wheel(QWheelEvent *event) = 0;
|
||||||
|
virtual bool mouseRelease(QMouseEvent *event) = 0;
|
||||||
|
virtual bool mousePress(QMouseEvent *event) = 0;
|
||||||
|
virtual bool mouseDoubleClick(QMouseEvent *event) = 0;
|
||||||
|
virtual bool keyPress(QKeyEvent *event) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SkeletonGraphicsWidget : public QGraphicsView, public SkeletonGraphicsFunctions
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
signals:
|
||||||
|
void addNode(float x, float y, float z, float radius, QUuid fromNodeId);
|
||||||
|
void uncheckNode(QUuid nodeId);
|
||||||
|
void checkNode(QUuid nodeId);
|
||||||
|
void uncheckEdge(QUuid edgeId);
|
||||||
|
void checkEdge(QUuid edgeId);
|
||||||
|
void scaleNodeByAddRadius(QUuid nodeId, float amount);
|
||||||
|
void moveNodeBy(QUuid nodeId, float x, float y, float z);
|
||||||
|
void removeNode(QUuid nodeId);
|
||||||
|
void setEditMode(SkeletonDocumentEditMode mode);
|
||||||
|
void removeEdge(QUuid edgeId);
|
||||||
|
void addEdge(QUuid fromNodeId, QUuid toNodeId);
|
||||||
|
void cursorChanged();
|
||||||
|
public:
|
||||||
|
SkeletonGraphicsWidget(const SkeletonDocument *document);
|
||||||
|
std::map<QUuid, std::pair<SkeletonGraphicsNodeItem *, SkeletonGraphicsNodeItem *>> nodeItemMap;
|
||||||
|
std::map<QUuid, std::pair<SkeletonGraphicsEdgeItem *, SkeletonGraphicsEdgeItem *>> edgeItemMap;
|
||||||
|
bool mouseMove(QMouseEvent *event);
|
||||||
|
bool wheel(QWheelEvent *event);
|
||||||
|
bool mouseRelease(QMouseEvent *event);
|
||||||
|
bool mousePress(QMouseEvent *event);
|
||||||
|
bool mouseDoubleClick(QMouseEvent *event);
|
||||||
|
bool keyPress(QKeyEvent *event);
|
||||||
|
protected:
|
||||||
|
void mouseMoveEvent(QMouseEvent *event);
|
||||||
|
void wheelEvent(QWheelEvent *event);
|
||||||
|
void mouseReleaseEvent(QMouseEvent *event);
|
||||||
|
void mousePressEvent(QMouseEvent *event);
|
||||||
|
void mouseDoubleClickEvent(QMouseEvent *event);
|
||||||
|
void keyPressEvent(QKeyEvent *event);
|
||||||
|
public slots:
|
||||||
|
void nodeAdded(QUuid nodeId);
|
||||||
|
void edgeAdded(QUuid edgeId);
|
||||||
|
void nodeRemoved(QUuid nodeId);
|
||||||
|
void edgeRemoved(QUuid edgeId);
|
||||||
|
void nodeRadiusChanged(QUuid nodeId);
|
||||||
|
void nodeOriginChanged(QUuid nodeId);
|
||||||
|
void edgeChanged(QUuid edgeId);
|
||||||
|
void turnaroundChanged();
|
||||||
|
void canvasResized();
|
||||||
|
void editModeChanged();
|
||||||
|
void updateCursor();
|
||||||
|
private slots:
|
||||||
|
void turnaroundImageReady();
|
||||||
|
private:
|
||||||
|
QPointF mouseEventScenePos(QMouseEvent *event);
|
||||||
|
QPointF scenePosToUnified(QPointF pos);
|
||||||
|
QPointF scenePosFromUnified(QPointF pos);
|
||||||
|
float sceneRadiusToUnified(float radius);
|
||||||
|
float sceneRadiusFromUnified(float radius);
|
||||||
|
void updateTurnaround();
|
||||||
|
void updateItems();
|
||||||
|
private:
|
||||||
|
QGraphicsPixmapItem *m_backgroundItem;
|
||||||
|
const SkeletonDocument *m_document;
|
||||||
|
bool m_turnaroundChanged;
|
||||||
|
TurnaroundLoader *m_turnaroundLoader;
|
||||||
|
bool m_dragStarted;
|
||||||
|
QPoint m_lastGlobalPos;
|
||||||
|
bool m_moveStarted;
|
||||||
|
QPointF m_lastScenePos;
|
||||||
|
SkeletonGraphicsNodeItem *m_cursorNodeItem;
|
||||||
|
SkeletonGraphicsEdgeItem *m_cursorEdgeItem;
|
||||||
|
SkeletonGraphicsNodeItem *m_checkedNodeItem;
|
||||||
|
SkeletonGraphicsNodeItem *m_hoveredNodeItem;
|
||||||
|
SkeletonGraphicsEdgeItem *m_hoveredEdgeItem;
|
||||||
|
SkeletonGraphicsEdgeItem *m_checkedEdgeItem;
|
||||||
|
float m_lastAddedX;
|
||||||
|
float m_lastAddedY;
|
||||||
|
float m_lastAddedZ;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SkeletonGraphicsContainerWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
signals:
|
||||||
|
void containerSizeChanged(QSize size);
|
||||||
|
public:
|
||||||
|
SkeletonGraphicsContainerWidget() :
|
||||||
|
m_graphicsWidget(nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void resizeEvent(QResizeEvent *event) override
|
||||||
|
{
|
||||||
|
if (m_graphicsWidget && m_graphicsWidget->size() != event->size())
|
||||||
|
emit containerSizeChanged(event->size());
|
||||||
|
}
|
||||||
|
void setGraphicsWidget(SkeletonGraphicsWidget *graphicsWidget)
|
||||||
|
{
|
||||||
|
m_graphicsWidget = graphicsWidget;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
SkeletonGraphicsWidget *m_graphicsWidget;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,50 @@
|
||||||
|
#include <QFormLayout>
|
||||||
|
#include "skeletonnodepropertywidget.h"
|
||||||
|
|
||||||
|
SkeletonNodePropertyWidget::SkeletonNodePropertyWidget(const SkeletonDocument *document) :
|
||||||
|
m_rootMarkModeComboBox(nullptr),
|
||||||
|
m_document(document)
|
||||||
|
{
|
||||||
|
m_rootMarkModeComboBox = new QComboBox;
|
||||||
|
|
||||||
|
m_rootMarkModeComboBox->insertItem(0, tr("Auto"), (int)SkeletonNodeRootMarkMode::Auto);
|
||||||
|
m_rootMarkModeComboBox->insertItem(1, tr("Mark as Root"), (int)SkeletonNodeRootMarkMode::MarkAsRoot);
|
||||||
|
m_rootMarkModeComboBox->insertItem(2, tr("Mark as Not Root"), (int)SkeletonNodeRootMarkMode::MarkAsNotRoot);
|
||||||
|
|
||||||
|
QFormLayout *formLayout = new QFormLayout;
|
||||||
|
formLayout->addRow(tr("Root Mark:"), m_rootMarkModeComboBox);
|
||||||
|
|
||||||
|
setLayout(formLayout);
|
||||||
|
|
||||||
|
hide();
|
||||||
|
|
||||||
|
connect(m_rootMarkModeComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), [=](int index) {
|
||||||
|
if (-1 != index) {
|
||||||
|
int mode = m_rootMarkModeComboBox->itemData(index).toInt();
|
||||||
|
emit setNodeRootMarkMode(m_nodeId, static_cast<SkeletonNodeRootMarkMode>(mode));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonNodePropertyWidget::showProperties(QUuid nodeId)
|
||||||
|
{
|
||||||
|
m_nodeId = nodeId;
|
||||||
|
updateData();
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonNodePropertyWidget::updateData()
|
||||||
|
{
|
||||||
|
const SkeletonNode *node = m_document->findNode(m_nodeId);
|
||||||
|
if (nullptr == node) {
|
||||||
|
hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int selectIndex = m_rootMarkModeComboBox->findData((int)node->rootMarkMode);
|
||||||
|
m_rootMarkModeComboBox->setCurrentIndex(selectIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
QUuid SkeletonNodePropertyWidget::currentNodeId()
|
||||||
|
{
|
||||||
|
return m_nodeId;
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
#ifndef SKELETON_NODE_PROPERTY_WIDGET_H
|
||||||
|
#define SKELETON_NODE_PROPERTY_WIDGET_H
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QDoubleSpinBox>
|
||||||
|
#include <QComboBox>
|
||||||
|
#include "skeletondocument.h"
|
||||||
|
|
||||||
|
class SkeletonNodePropertyWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
signals:
|
||||||
|
void setNodeRootMarkMode(QUuid nodeId, SkeletonNodeRootMarkMode mode);
|
||||||
|
public slots:
|
||||||
|
void showProperties(QUuid nodeId);
|
||||||
|
public:
|
||||||
|
SkeletonNodePropertyWidget(const SkeletonDocument *document);
|
||||||
|
QUuid currentNodeId();
|
||||||
|
private:
|
||||||
|
void updateData();
|
||||||
|
private:
|
||||||
|
QComboBox *m_rootMarkModeComboBox;
|
||||||
|
const SkeletonDocument *m_document;
|
||||||
|
QUuid m_nodeId;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,89 @@
|
||||||
|
#include <QGridLayout>
|
||||||
|
#include <QDebug>
|
||||||
|
#include "skeletonpartlistwidget.h"
|
||||||
|
#include "theme.h"
|
||||||
|
|
||||||
|
SkeletonPartWidget::SkeletonPartWidget(const SkeletonDocument *document, QUuid partId) :
|
||||||
|
m_document(document),
|
||||||
|
m_partId(partId)
|
||||||
|
{
|
||||||
|
m_visibleButton = new QPushButton(QChar(fa::eye));
|
||||||
|
//m_visibleButton->setStyleSheet("QPushButton {border: none; background: none;}");
|
||||||
|
m_visibleButton->setFont(Theme::awesome()->font(Theme::toolIconFontSize * 2 / 3));
|
||||||
|
m_visibleButton->setFixedSize(Theme::toolIconSize * 2 / 3, Theme::toolIconSize * 2 / 3);
|
||||||
|
|
||||||
|
m_previewLabel = new QLabel;
|
||||||
|
|
||||||
|
//m_nameLabel = new QLabel;
|
||||||
|
|
||||||
|
QGridLayout *mainLayout = new QGridLayout;
|
||||||
|
mainLayout->addWidget(m_visibleButton, 0, 0);
|
||||||
|
mainLayout->addWidget(m_previewLabel, 0, 1);
|
||||||
|
//mainLayout->addWidget(m_nameLabel, 0, 2);
|
||||||
|
|
||||||
|
setLayout(mainLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
QLabel *SkeletonPartWidget::previewLabel()
|
||||||
|
{
|
||||||
|
return m_previewLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonPartWidget::reload()
|
||||||
|
{
|
||||||
|
const SkeletonPart *part = m_document->findPart(m_partId);
|
||||||
|
if (!part) {
|
||||||
|
qDebug() << "Part not found:" << m_partId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//m_nameLabel->setText(part->name.isEmpty() ? part->id.toString() : part->name);
|
||||||
|
m_previewLabel->setPixmap(QPixmap::fromImage(part->preview));
|
||||||
|
}
|
||||||
|
|
||||||
|
SkeletonPartListWidget::SkeletonPartListWidget(const SkeletonDocument *document) :
|
||||||
|
m_document(document)
|
||||||
|
{
|
||||||
|
setSelectionMode(QAbstractItemView::NoSelection);
|
||||||
|
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonPartListWidget::partChanged(QUuid partId)
|
||||||
|
{
|
||||||
|
auto itemIt = m_itemMap.find(partId);
|
||||||
|
if (itemIt == m_itemMap.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonPartListWidget::partListChanged()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
m_itemMap.clear();
|
||||||
|
|
||||||
|
for (auto partIdIt = m_document->partIds.begin(); partIdIt != m_document->partIds.end(); partIdIt++) {
|
||||||
|
QUuid partId = *partIdIt;
|
||||||
|
QListWidgetItem *item = new QListWidgetItem(this);
|
||||||
|
item->setSizeHint(QSize(width(), Theme::previewImageSize));
|
||||||
|
addItem(item);
|
||||||
|
SkeletonPartWidget *widget = new SkeletonPartWidget(m_document, partId);
|
||||||
|
setItemWidget(item, widget);
|
||||||
|
widget->reload();
|
||||||
|
m_itemMap[partId] = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkeletonPartListWidget::partPreviewChanged(QUuid partid)
|
||||||
|
{
|
||||||
|
const SkeletonPart *part = m_document->findPart(partid);
|
||||||
|
if (!part) {
|
||||||
|
qDebug() << "Part not found:" << partid;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto item = m_itemMap.find(partid);
|
||||||
|
if (item == m_itemMap.end()) {
|
||||||
|
qDebug() << "Part item not found:" << partid;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SkeletonPartWidget *widget = (SkeletonPartWidget *)itemWidget(item->second);
|
||||||
|
widget->previewLabel()->setPixmap(QPixmap::fromImage(part->preview));
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
#ifndef SKELETON_PART_LIST_WIDGET_H
|
||||||
|
#define SKELETON_PART_LIST_WIDGET_H
|
||||||
|
#include <QListWidget>
|
||||||
|
#include <map>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include "skeletondocument.h"
|
||||||
|
|
||||||
|
class SkeletonPartWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
SkeletonPartWidget(const SkeletonDocument *document, QUuid partId);
|
||||||
|
void reload();
|
||||||
|
QLabel *previewLabel();
|
||||||
|
private:
|
||||||
|
const SkeletonDocument *m_document;
|
||||||
|
QUuid m_partId;
|
||||||
|
QLabel *m_previewLabel;
|
||||||
|
QPushButton *m_visibleButton;
|
||||||
|
QLabel *m_nameLabel;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SkeletonPartListWidget : public QListWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
SkeletonPartListWidget(const SkeletonDocument *document);
|
||||||
|
public slots:
|
||||||
|
void partChanged(QUuid partId);
|
||||||
|
void partListChanged();
|
||||||
|
void partPreviewChanged(QUuid partid);
|
||||||
|
private:
|
||||||
|
const SkeletonDocument *m_document;
|
||||||
|
std::map<QUuid, QListWidgetItem *> m_itemMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -12,6 +12,8 @@ public:
|
||||||
std::map<QString, QString> canvas;
|
std::map<QString, QString> canvas;
|
||||||
std::map<QString, std::map<QString, QString>> nodes;
|
std::map<QString, std::map<QString, QString>> nodes;
|
||||||
std::map<QString, std::map<QString, QString>> edges;
|
std::map<QString, std::map<QString, QString>> edges;
|
||||||
|
std::map<QString, std::map<QString, QString>> parts;
|
||||||
|
std::vector<QString> partIdList;
|
||||||
public:
|
public:
|
||||||
SkeletonSnapshot();
|
SkeletonSnapshot();
|
||||||
void splitByConnectivity(std::vector<SkeletonSnapshot> *groups);
|
void splitByConnectivity(std::vector<SkeletonSnapshot> *groups);
|
||||||
|
|
|
@ -1,135 +0,0 @@
|
||||||
#include "skeletontomesh.h"
|
|
||||||
#include "meshlite.h"
|
|
||||||
#include "skeletoneditnodeitem.h"
|
|
||||||
#include "skeletoneditedgeitem.h"
|
|
||||||
#include "skeletonsnapshot.h"
|
|
||||||
#include "unionmesh.h"
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
struct NodeItemInfo
|
|
||||||
{
|
|
||||||
int index;
|
|
||||||
int neighborCount;
|
|
||||||
};
|
|
||||||
|
|
||||||
SkeletonToMesh::SkeletonToMesh(SkeletonSnapshot *snapshot) :
|
|
||||||
m_mesh(NULL),
|
|
||||||
m_snapshot(*snapshot)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
SkeletonToMesh::~SkeletonToMesh()
|
|
||||||
{
|
|
||||||
delete m_mesh;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mesh *SkeletonToMesh::takeResultMesh()
|
|
||||||
{
|
|
||||||
Mesh *mesh = m_mesh;
|
|
||||||
m_mesh = NULL;
|
|
||||||
return mesh;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if USE_CARVE == 1
|
|
||||||
#define ExternalMesh carve::poly::Polyhedron
|
|
||||||
#define makeExternalMeshFromMeshlite makeCarveMeshFromMeshlite
|
|
||||||
#define unionExternalMeshs unionCarveMeshs
|
|
||||||
#define makeMeshliteMeshFromExternal makeMeshliteMeshFromCarve
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if USE_CGAL == 1
|
|
||||||
#define ExternalMesh CgalMesh
|
|
||||||
#define makeExternalMeshFromMeshlite makeCgalMeshFromMeshlite
|
|
||||||
#define unionExternalMeshs unionCgalMeshs
|
|
||||||
#define makeMeshliteMeshFromExternal makeMeshliteMeshFromCgal
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void SkeletonToMesh::process()
|
|
||||||
{
|
|
||||||
std::vector<SkeletonSnapshot> groups;
|
|
||||||
QRectF globalFront = m_snapshot.boundingBoxFront();
|
|
||||||
QRectF globalSide = m_snapshot.boundingBoxSide();
|
|
||||||
QString rootNodeId = m_snapshot.rootNode();
|
|
||||||
printf("rootNodeId:%s\n", rootNodeId.toUtf8().constData());
|
|
||||||
float frontMiddleX = m_snapshot.nodes[rootNodeId]["x"].toFloat();
|
|
||||||
float frontMiddleY = m_snapshot.nodes[rootNodeId]["y"].toFloat();
|
|
||||||
float sideMiddleX = m_snapshot.nodes[m_snapshot.nodes[rootNodeId]["nextSidePair"]]["x"].toFloat();
|
|
||||||
bool combineEnabled = "true" == m_snapshot.canvas["combine"];
|
|
||||||
bool unionEnabled = "true" == m_snapshot.canvas["union"];
|
|
||||||
bool subdivEnabled = "true" == m_snapshot.canvas["subdiv"];
|
|
||||||
m_snapshot.splitByConnectivity(&groups);
|
|
||||||
|
|
||||||
std::vector<int> meshIds;
|
|
||||||
void *meshliteContext = meshlite_create_context();
|
|
||||||
for (size_t i = 0; i < groups.size(); i++) {
|
|
||||||
SkeletonSnapshot *skeleton = &groups[i];
|
|
||||||
//QRectF front = skeleton->boundingBoxFront();
|
|
||||||
//QRectF side = skeleton->boundingBoxSide();
|
|
||||||
//float canvasWidth = skeleton->canvas["width"].toFloat();
|
|
||||||
//float canvasHeight = skeleton->canvas["height"].toFloat();
|
|
||||||
float canvasHeight = globalFront.height();
|
|
||||||
|
|
||||||
std::map<QString, int> bmeshNodeMap;
|
|
||||||
|
|
||||||
std::map<QString, std::map<QString, QString>>::iterator nodeIterator;
|
|
||||||
int bmeshId = meshlite_bmesh_create(meshliteContext);
|
|
||||||
for (nodeIterator = skeleton->nodes.begin(); nodeIterator != skeleton->nodes.end(); nodeIterator++) {
|
|
||||||
if ("red" != nodeIterator->second["sideColorName"])
|
|
||||||
continue;
|
|
||||||
std::map<QString, std::map<QString, QString>>::iterator nextSidePair = skeleton->nodes.find(nodeIterator->second["nextSidePair"]);
|
|
||||||
if (nextSidePair == skeleton->nodes.end())
|
|
||||||
continue;
|
|
||||||
float x = (nodeIterator->second["x"].toFloat() - frontMiddleX) / canvasHeight;
|
|
||||||
float y = (nodeIterator->second["y"].toFloat() - frontMiddleY) / canvasHeight;
|
|
||||||
float z = (nextSidePair->second["x"].toFloat() - sideMiddleX) / canvasHeight;
|
|
||||||
float r = nodeIterator->second["radius"].toFloat() / canvasHeight;
|
|
||||||
int bmeshNodeId = meshlite_bmesh_add_node(meshliteContext, bmeshId, x, y, z, r);
|
|
||||||
printf("meshlite_bmesh_add_node x:%f y:%f z:%f r:%f nodeName:%s bmeshNodeId:%d\n", x, y, z, r, nodeIterator->first.toUtf8().constData(), bmeshNodeId);
|
|
||||||
bmeshNodeMap[nodeIterator->first] = bmeshNodeId;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<QString, std::map<QString, QString>>::iterator edgeIterator;
|
|
||||||
for (edgeIterator = skeleton->edges.begin(); edgeIterator != skeleton->edges.end(); edgeIterator++) {
|
|
||||||
std::map<QString, int>::iterator from = bmeshNodeMap.find(edgeIterator->second["from"]);
|
|
||||||
if (from == bmeshNodeMap.end())
|
|
||||||
continue;
|
|
||||||
std::map<QString, int>::iterator to = bmeshNodeMap.find(edgeIterator->second["to"]);
|
|
||||||
if (to == bmeshNodeMap.end())
|
|
||||||
continue;
|
|
||||||
printf("meshlite_bmesh_add_edge %d -> %d\n", from->second, to->second);
|
|
||||||
meshlite_bmesh_add_edge(meshliteContext, bmeshId, from->second, to->second);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bmeshNodeMap.size() > 0 && !skeleton->rootNode().isEmpty()) {
|
|
||||||
int meshId = meshlite_bmesh_generate_mesh(meshliteContext, bmeshId, bmeshNodeMap[skeleton->rootNode()]);
|
|
||||||
meshIds.push_back(meshId);
|
|
||||||
}
|
|
||||||
|
|
||||||
meshlite_bmesh_destroy(meshliteContext, bmeshId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (meshIds.size() > 0) {
|
|
||||||
int mergedMeshId = 0;
|
|
||||||
if (unionEnabled) {
|
|
||||||
mergedMeshId = unionMeshs(meshliteContext, meshIds);
|
|
||||||
} else {
|
|
||||||
mergedMeshId = mergeMeshs(meshliteContext, meshIds);
|
|
||||||
}
|
|
||||||
if (combineEnabled) {
|
|
||||||
if (mergedMeshId > 0) {
|
|
||||||
mergedMeshId = meshlite_combine_coplanar_faces(meshliteContext, mergedMeshId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (subdivEnabled) {
|
|
||||||
if (mergedMeshId > 0 && meshlite_is_triangulated_manifold(meshliteContext, mergedMeshId)) {
|
|
||||||
mergedMeshId = meshlite_subdivide(meshliteContext, mergedMeshId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mergedMeshId > 0) {
|
|
||||||
m_mesh = new Mesh(meshliteContext, mergedMeshId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
meshlite_destroy_context(meshliteContext);
|
|
||||||
emit finished();
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
#ifndef SKELETON_TO_MESH_H
|
|
||||||
#define SKELETON_TO_MESH_H
|
|
||||||
#include <QObject>
|
|
||||||
#include <QList>
|
|
||||||
#include <vector>
|
|
||||||
#include <map>
|
|
||||||
#include "skeletoneditgraphicsview.h"
|
|
||||||
#include "mesh.h"
|
|
||||||
#include "skeletonsnapshot.h"
|
|
||||||
|
|
||||||
class SkeletonToMesh : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
SkeletonToMesh(SkeletonSnapshot *snapshot);
|
|
||||||
~SkeletonToMesh();
|
|
||||||
Mesh *takeResultMesh();
|
|
||||||
signals:
|
|
||||||
void finished();
|
|
||||||
public slots:
|
|
||||||
void process();
|
|
||||||
private:
|
|
||||||
Mesh *m_mesh;
|
|
||||||
SkeletonSnapshot m_snapshot;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,214 +0,0 @@
|
||||||
#include <QVBoxLayout>
|
|
||||||
#include <QPushButton>
|
|
||||||
#include <QButtonGroup>
|
|
||||||
#include <QGridLayout>
|
|
||||||
#include <QToolBar>
|
|
||||||
#include <QThread>
|
|
||||||
#include <QFileDialog>
|
|
||||||
#include <QApplication>
|
|
||||||
#include <QDesktopWidget>
|
|
||||||
#include <assert.h>
|
|
||||||
#include "skeletonwidget.h"
|
|
||||||
#include "meshlite.h"
|
|
||||||
#include "skeletontomesh.h"
|
|
||||||
#include "turnaroundloader.h"
|
|
||||||
|
|
||||||
SkeletonWidget::SkeletonWidget(QWidget *parent) :
|
|
||||||
QWidget(parent),
|
|
||||||
m_skeletonToMesh(NULL),
|
|
||||||
m_skeletonDirty(false),
|
|
||||||
m_turnaroundLoader(NULL),
|
|
||||||
m_turnaroundDirty(false)
|
|
||||||
{
|
|
||||||
QHBoxLayout *topLayout = new QHBoxLayout;
|
|
||||||
topLayout->addStretch();
|
|
||||||
topLayout->setContentsMargins(0, 0, 0, 0);
|
|
||||||
|
|
||||||
m_graphicsView = new SkeletonEditGraphicsView(this);
|
|
||||||
m_graphicsView->setRenderHint(QPainter::Antialiasing, false);
|
|
||||||
m_graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
||||||
m_graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
||||||
m_graphicsView->setBackgroundBrush(QBrush(QWidget::palette().color(QWidget::backgroundRole()), Qt::SolidPattern));
|
|
||||||
m_graphicsView->setContentsMargins(0, 0, 0, 0);
|
|
||||||
m_graphicsView->setFrameStyle(QFrame::NoFrame);
|
|
||||||
|
|
||||||
/*
|
|
||||||
m_modelWidget = new ModelWidget(this);
|
|
||||||
m_modelWidget->setMinimumSize(128, 128);
|
|
||||||
m_modelWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
|
||||||
m_modelWidget->setWindowFlags(Qt::Tool | Qt::Window);
|
|
||||||
m_modelWidget->setWindowTitle("3D Model");
|
|
||||||
*/
|
|
||||||
|
|
||||||
QVBoxLayout *rightLayout = new QVBoxLayout;
|
|
||||||
rightLayout->addSpacing(0);
|
|
||||||
rightLayout->addStretch();
|
|
||||||
rightLayout->setContentsMargins(0, 0, 0, 0);
|
|
||||||
|
|
||||||
QToolBar *toolbar = new QToolBar;
|
|
||||||
toolbar->setIconSize(QSize(18, 18));
|
|
||||||
toolbar->setOrientation(Qt::Vertical);
|
|
||||||
|
|
||||||
QAction *addAction = new QAction(tr("Add"), this);
|
|
||||||
addAction->setIcon(QPixmap(":/resources/add.svg"));
|
|
||||||
toolbar->addAction(addAction);
|
|
||||||
|
|
||||||
QAction *selectAction = new QAction(tr("Select"), this);
|
|
||||||
selectAction->setIcon(QPixmap(":/resources/rotate.svg"));
|
|
||||||
toolbar->addAction(selectAction);
|
|
||||||
|
|
||||||
QAction *rangeSelectAction = new QAction(tr("Range Select"), this);
|
|
||||||
rangeSelectAction->setIcon(QPixmap(":/resources/zoomin.svg"));
|
|
||||||
toolbar->addAction(rangeSelectAction);
|
|
||||||
|
|
||||||
toolbar->setContentsMargins(0, 0, 0, 0);
|
|
||||||
|
|
||||||
QVBoxLayout *leftLayout = new QVBoxLayout;
|
|
||||||
leftLayout->addWidget(toolbar);
|
|
||||||
leftLayout->addStretch();
|
|
||||||
leftLayout->addSpacing(0);
|
|
||||||
leftLayout->setContentsMargins(0, 0, 0, 0);
|
|
||||||
|
|
||||||
QHBoxLayout *middleLayout = new QHBoxLayout;
|
|
||||||
//middleLayout->addLayout(leftLayout);
|
|
||||||
middleLayout->addWidget(m_graphicsView);
|
|
||||||
//middleLayout->addLayout(rightLayout);
|
|
||||||
middleLayout->setContentsMargins(0, 0, 0, 0);
|
|
||||||
|
|
||||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
|
||||||
//mainLayout->addLayout(topLayout);
|
|
||||||
//mainLayout->addSpacing(10);
|
|
||||||
mainLayout->addLayout(middleLayout);
|
|
||||||
mainLayout->setContentsMargins(0, 0, 0, 0);
|
|
||||||
|
|
||||||
setLayout(mainLayout);
|
|
||||||
setContentsMargins(0, 0, 0, 0);
|
|
||||||
|
|
||||||
setWindowTitle(tr("Dust 3D"));
|
|
||||||
|
|
||||||
bool connectResult;
|
|
||||||
|
|
||||||
connectResult = connect(addAction, SIGNAL(triggered(bool)), m_graphicsView, SLOT(turnOnAddNodeMode()));
|
|
||||||
assert(connectResult);
|
|
||||||
|
|
||||||
connectResult = connectResult = connect(selectAction, SIGNAL(triggered(bool)), m_graphicsView, SLOT(turnOffAddNodeMode()));
|
|
||||||
assert(connectResult);
|
|
||||||
|
|
||||||
connectResult = connect(m_graphicsView, SIGNAL(nodesChanged()), this, SLOT(skeletonChanged()));
|
|
||||||
assert(connectResult);
|
|
||||||
|
|
||||||
connectResult = connect(m_graphicsView, SIGNAL(sizeChanged()), this, SLOT(turnaroundChanged()));
|
|
||||||
assert(connectResult);
|
|
||||||
|
|
||||||
connectResult = connect(m_graphicsView, SIGNAL(changeTurnaroundTriggered()), this, SLOT(changeTurnaround()));
|
|
||||||
assert(connectResult);
|
|
||||||
|
|
||||||
//connectResult = connect(clipButton, SIGNAL(clicked()), this, SLOT(saveClip()));
|
|
||||||
//assert(connectResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
SkeletonEditGraphicsView *SkeletonWidget::graphicsView()
|
|
||||||
{
|
|
||||||
return m_graphicsView;
|
|
||||||
}
|
|
||||||
|
|
||||||
ModelWidget *SkeletonWidget::modelWidget()
|
|
||||||
{
|
|
||||||
//return m_modelWidget;
|
|
||||||
return graphicsView()->modelWidget();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SkeletonWidget::showModelingWidgetAtCorner()
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
if (!m_modelWidget->isVisible()) {
|
|
||||||
QPoint pos = QPoint(QApplication::desktop()->width(),
|
|
||||||
QApplication::desktop()->height());
|
|
||||||
m_modelWidget->move(pos.x() - m_modelWidget->width(),
|
|
||||||
pos.y() - m_modelWidget->height());
|
|
||||||
m_modelWidget->show();
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
||||||
void SkeletonWidget::meshReady()
|
|
||||||
{
|
|
||||||
Mesh *resultMesh = m_skeletonToMesh->takeResultMesh();
|
|
||||||
//showModelingWidgetAtCorner();
|
|
||||||
//m_modelWidget->updateMesh(resultMesh);
|
|
||||||
modelWidget()->updateMesh(resultMesh);
|
|
||||||
delete m_skeletonToMesh;
|
|
||||||
m_skeletonToMesh = NULL;
|
|
||||||
if (m_skeletonDirty) {
|
|
||||||
skeletonChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SkeletonWidget::skeletonChanged()
|
|
||||||
{
|
|
||||||
if (m_skeletonToMesh) {
|
|
||||||
m_skeletonDirty = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_skeletonDirty = false;
|
|
||||||
|
|
||||||
QThread *thread = new QThread;
|
|
||||||
SkeletonSnapshot snapshot;
|
|
||||||
m_graphicsView->saveToSnapshot(&snapshot);
|
|
||||||
m_skeletonToMesh = new SkeletonToMesh(&snapshot);
|
|
||||||
m_skeletonToMesh->moveToThread(thread);
|
|
||||||
connect(thread, SIGNAL(started()), m_skeletonToMesh, SLOT(process()));
|
|
||||||
connect(m_skeletonToMesh, SIGNAL(finished()), this, SLOT(meshReady()));
|
|
||||||
connect(m_skeletonToMesh, SIGNAL(finished()), thread, SLOT(quit()));
|
|
||||||
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
|
|
||||||
thread->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SkeletonWidget::turnaroundChanged()
|
|
||||||
{
|
|
||||||
if (m_turnaroundFilename.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (m_turnaroundLoader) {
|
|
||||||
m_turnaroundDirty = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_turnaroundDirty = false;
|
|
||||||
|
|
||||||
QThread *thread = new QThread;
|
|
||||||
m_turnaroundLoader = new TurnaroundLoader(m_turnaroundFilename,
|
|
||||||
m_graphicsView->rect().size());
|
|
||||||
m_turnaroundLoader->moveToThread(thread);
|
|
||||||
connect(thread, SIGNAL(started()), m_turnaroundLoader, SLOT(process()));
|
|
||||||
connect(m_turnaroundLoader, SIGNAL(finished()), this, SLOT(turnaroundImageReady()));
|
|
||||||
connect(m_turnaroundLoader, SIGNAL(finished()), thread, SLOT(quit()));
|
|
||||||
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
|
|
||||||
thread->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SkeletonWidget::turnaroundImageReady()
|
|
||||||
{
|
|
||||||
QImage *backgroundImage = m_turnaroundLoader->takeResultImage();
|
|
||||||
if (backgroundImage && backgroundImage->width() > 0 && backgroundImage->height() > 0) {
|
|
||||||
m_graphicsView->updateBackgroundImage(*backgroundImage);
|
|
||||||
}
|
|
||||||
delete backgroundImage;
|
|
||||||
delete m_turnaroundLoader;
|
|
||||||
m_turnaroundLoader = NULL;
|
|
||||||
if (m_turnaroundDirty) {
|
|
||||||
turnaroundChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SkeletonWidget::changeTurnaround()
|
|
||||||
{
|
|
||||||
QString fileName = QFileDialog::getOpenFileName(this, tr("Open Turnaround Reference Image"),
|
|
||||||
QString(),
|
|
||||||
tr("Image Files (*.png *.jpg *.bmp)")).trimmed();
|
|
||||||
if (fileName.isEmpty())
|
|
||||||
return;
|
|
||||||
m_turnaroundFilename = fileName;
|
|
||||||
turnaroundChanged();
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
#ifndef SKELETON_WIDGET_H
|
|
||||||
#define SKELETON_WIDGET_H
|
|
||||||
#include <QWidget>
|
|
||||||
#include <QString>
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
#include <QPushButton>
|
|
||||||
#include "modelwidget.h"
|
|
||||||
#include "skeletontomesh.h"
|
|
||||||
#include "turnaroundloader.h"
|
|
||||||
#include "skeletoneditgraphicsview.h"
|
|
||||||
|
|
||||||
class SkeletonWidget : public QWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
SkeletonWidget(QWidget *parent=0);
|
|
||||||
SkeletonEditGraphicsView *graphicsView();
|
|
||||||
ModelWidget *modelWidget();
|
|
||||||
public slots:
|
|
||||||
void skeletonChanged();
|
|
||||||
void meshReady();
|
|
||||||
void turnaroundChanged();
|
|
||||||
void turnaroundImageReady();
|
|
||||||
void changeTurnaround();
|
|
||||||
void showModelingWidgetAtCorner();
|
|
||||||
private:
|
|
||||||
ModelWidget *m_modelWidget;
|
|
||||||
SkeletonEditGraphicsView *m_graphicsView;
|
|
||||||
SkeletonToMesh *m_skeletonToMesh;
|
|
||||||
bool m_skeletonDirty;
|
|
||||||
TurnaroundLoader *m_turnaroundLoader;
|
|
||||||
bool m_turnaroundDirty;
|
|
||||||
QString m_turnaroundFilename;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -25,6 +25,23 @@ float Theme::edgeAlpha = 1.0;
|
||||||
float Theme::fillAlpha = 50.0 / 255;
|
float Theme::fillAlpha = 50.0 / 255;
|
||||||
int Theme::skeletonNodeBorderSize = 0;
|
int Theme::skeletonNodeBorderSize = 0;
|
||||||
int Theme::skeletonEdgeWidth = 0;
|
int Theme::skeletonEdgeWidth = 0;
|
||||||
|
int Theme::toolIconFontSize = 16;
|
||||||
|
int Theme::toolIconSize = 24;
|
||||||
|
int Theme::previewImageSize = 64;
|
||||||
|
|
||||||
|
QtAwesome *Theme::awesome()
|
||||||
|
{
|
||||||
|
static QtAwesome *s_awesome = nullptr;
|
||||||
|
if (nullptr == s_awesome) {
|
||||||
|
s_awesome = new QtAwesome();
|
||||||
|
s_awesome->initFontAwesome();
|
||||||
|
s_awesome->setDefaultOption("color", Theme::white);
|
||||||
|
s_awesome->setDefaultOption("color-disabled", QColor(0xcc, 0xcc, 0xcc));
|
||||||
|
s_awesome->setDefaultOption("color-active", Theme::white);
|
||||||
|
s_awesome->setDefaultOption("color-selected", Theme::white);
|
||||||
|
}
|
||||||
|
return s_awesome;
|
||||||
|
}
|
||||||
|
|
||||||
std::map<QString, QString> createSideColorNameMap() {
|
std::map<QString, QString> createSideColorNameMap() {
|
||||||
std::map<QString, QString> map;
|
std::map<QString, QString> map;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include "QtAwesome.h"
|
||||||
|
|
||||||
class Theme
|
class Theme
|
||||||
{
|
{
|
||||||
|
@ -22,6 +23,10 @@ public:
|
||||||
static QString tabButtonStylesheet;
|
static QString tabButtonStylesheet;
|
||||||
static std::map<QString, QString> nextSideColorNameMap;
|
static std::map<QString, QString> nextSideColorNameMap;
|
||||||
static std::map<QString, QColor> sideColorNameToColorMap;
|
static std::map<QString, QColor> sideColorNameToColorMap;
|
||||||
|
static QtAwesome *awesome();
|
||||||
|
static int toolIconFontSize;
|
||||||
|
static int toolIconSize;
|
||||||
|
static int previewImageSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
#include "turnaroundloader.h"
|
#include "turnaroundloader.h"
|
||||||
|
|
||||||
TurnaroundLoader::TurnaroundLoader(const QString &filename, QSize viewSize) :
|
TurnaroundLoader::TurnaroundLoader(const QString &filename, QSize viewSize) :
|
||||||
m_resultImage(NULL)
|
m_resultImage(nullptr)
|
||||||
{
|
{
|
||||||
m_filename = filename;
|
m_filename = filename;
|
||||||
m_viewSize = viewSize;
|
m_viewSize = viewSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TurnaroundLoader::TurnaroundLoader(const QImage &image, QSize viewSize)
|
||||||
|
{
|
||||||
|
m_inputImage = image;
|
||||||
|
m_viewSize = viewSize;
|
||||||
|
}
|
||||||
|
|
||||||
TurnaroundLoader::~TurnaroundLoader()
|
TurnaroundLoader::~TurnaroundLoader()
|
||||||
{
|
{
|
||||||
delete m_resultImage;
|
delete m_resultImage;
|
||||||
|
@ -15,13 +21,17 @@ TurnaroundLoader::~TurnaroundLoader()
|
||||||
QImage *TurnaroundLoader::takeResultImage()
|
QImage *TurnaroundLoader::takeResultImage()
|
||||||
{
|
{
|
||||||
QImage *returnImage = m_resultImage;
|
QImage *returnImage = m_resultImage;
|
||||||
m_resultImage = NULL;
|
m_resultImage = nullptr;
|
||||||
return returnImage;
|
return returnImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TurnaroundLoader::process()
|
void TurnaroundLoader::process()
|
||||||
{
|
{
|
||||||
QImage image(m_filename);
|
if (m_inputImage.isNull()) {
|
||||||
m_resultImage = new QImage(image.scaled(m_viewSize, Qt::KeepAspectRatio));
|
QImage image(m_filename);
|
||||||
|
m_resultImage = new QImage(image.scaled(m_viewSize, Qt::KeepAspectRatio));
|
||||||
|
} else {
|
||||||
|
m_resultImage = new QImage(m_inputImage.scaled(m_viewSize, Qt::KeepAspectRatio));
|
||||||
|
}
|
||||||
emit finished();
|
emit finished();
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ class TurnaroundLoader : public QObject
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
TurnaroundLoader(const QString &filename, QSize viewSize);
|
TurnaroundLoader(const QString &filename, QSize viewSize);
|
||||||
|
TurnaroundLoader(const QImage &image, QSize viewSize);
|
||||||
~TurnaroundLoader();
|
~TurnaroundLoader();
|
||||||
QImage *takeResultImage();
|
QImage *takeResultImage();
|
||||||
signals:
|
signals:
|
||||||
|
@ -18,6 +19,7 @@ public slots:
|
||||||
void process();
|
void process();
|
||||||
private:
|
private:
|
||||||
QImage *m_resultImage;
|
QImage *m_resultImage;
|
||||||
|
QImage m_inputImage;
|
||||||
QString m_filename;
|
QString m_filename;
|
||||||
QSize m_viewSize;
|
QSize m_viewSize;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
#include <cmath>
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
QString valueOfKeyInMapOrEmpty(const std::map<QString, QString> &map, const QString &key)
|
||||||
|
{
|
||||||
|
auto it = map.find(key);
|
||||||
|
if (it == map.end())
|
||||||
|
return QString();
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isTrueValueString(const QString &str)
|
||||||
|
{
|
||||||
|
return "true" == str || "True" == str || "1" == str;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isFloatEqual(float a, float b)
|
||||||
|
{
|
||||||
|
return fabs(a - b) <= 0.000001;
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
#ifndef UTIL_H
|
||||||
|
#define UTIL_H
|
||||||
|
#include <QString>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
QString valueOfKeyInMapOrEmpty(const std::map<QString, QString> &map, const QString &key);
|
||||||
|
bool isTrueValueString(const QString &str);
|
||||||
|
bool isFloatEqual(float a, float b);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,29 @@
|
||||||
|
MIT License
|
||||||
|
===========
|
||||||
|
|
||||||
|
Copyright 2013-2015 [Reliable Bits Software by Blommers IT](http://blommersit.nl). All Rights Reserved.
|
||||||
|
Author [Rick Blommers](mailto:rick@blommersit.nl)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||||
|
documentation files (the "Software"), to deal in the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||||
|
and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||||
|
THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
||||||
|
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
Font Awesome License
|
||||||
|
====================
|
||||||
|
|
||||||
|
[https://github.com/FortAwesome/Font-Awesome](https://github.com/FortAwesome/Font-Awesome)
|
||||||
|
|
||||||
|
The Font Awesome font is licensed under the SIL Open Font License - [http://scripts.sil.org/OFL](http://scripts.sil.org/OFL)
|
||||||
|
The Font Awesome pictograms are licensed under the CC BY 3.0 License - [http://creativecommons.org/licenses/by/3.0/](http://creativecommons.org/licenses/by/3.0/)
|
||||||
|
"Font Awesome by Dave Gandy - http://fortawesome.github.com/Font-Awesome"
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,876 @@
|
||||||
|
/**
|
||||||
|
* QtAwesome - use font-awesome (or other font icons) in your c++ / Qt Application
|
||||||
|
*
|
||||||
|
* MIT Licensed
|
||||||
|
*
|
||||||
|
* Copyright 2013-2015 - Reliable Bits Software by Blommers IT. All Rights Reserved.
|
||||||
|
* Author Rick Blommers
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef QTAWESOME_H
|
||||||
|
#define QTAWESOME_H
|
||||||
|
|
||||||
|
#include "QtAwesomeAnim.h"
|
||||||
|
|
||||||
|
#include <QIcon>
|
||||||
|
#include <QIconEngine>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QRect>
|
||||||
|
#include <QVariantMap>
|
||||||
|
|
||||||
|
|
||||||
|
/// A list of all icon-names with the codepoint (unicode-value) on the right
|
||||||
|
/// You can use the names on the page http://fortawesome.github.io/Font-Awesome/design.html
|
||||||
|
namespace fa {
|
||||||
|
enum icon {
|
||||||
|
fa_500px = 0xf26e,
|
||||||
|
addressbook = 0xf2b9,
|
||||||
|
addressbooko = 0xf2ba,
|
||||||
|
addresscard = 0xf2bb,
|
||||||
|
addresscardo = 0xf2bc,
|
||||||
|
adjust = 0xf042,
|
||||||
|
adn = 0xf170,
|
||||||
|
aligncenter = 0xf037,
|
||||||
|
alignjustify = 0xf039,
|
||||||
|
alignleft = 0xf036,
|
||||||
|
alignright = 0xf038,
|
||||||
|
amazon = 0xf270,
|
||||||
|
ambulance = 0xf0f9,
|
||||||
|
americansignlanguageinterpreting = 0xf2a3,
|
||||||
|
anchor = 0xf13d,
|
||||||
|
android = 0xf17b,
|
||||||
|
angellist = 0xf209,
|
||||||
|
angledoubledown = 0xf103,
|
||||||
|
angledoubleleft = 0xf100,
|
||||||
|
angledoubleright = 0xf101,
|
||||||
|
angledoubleup = 0xf102,
|
||||||
|
angledown = 0xf107,
|
||||||
|
angleleft = 0xf104,
|
||||||
|
angleright = 0xf105,
|
||||||
|
angleup = 0xf106,
|
||||||
|
apple = 0xf179,
|
||||||
|
archive = 0xf187,
|
||||||
|
areachart = 0xf1fe,
|
||||||
|
arrowcircledown = 0xf0ab,
|
||||||
|
arrowcircleleft = 0xf0a8,
|
||||||
|
arrowcircleodown = 0xf01a,
|
||||||
|
arrowcircleoleft = 0xf190,
|
||||||
|
arrowcircleoright = 0xf18e,
|
||||||
|
arrowcircleoup = 0xf01b,
|
||||||
|
arrowcircleright = 0xf0a9,
|
||||||
|
arrowcircleup = 0xf0aa,
|
||||||
|
arrowdown = 0xf063,
|
||||||
|
arrowleft = 0xf060,
|
||||||
|
arrowright = 0xf061,
|
||||||
|
arrowup = 0xf062,
|
||||||
|
arrows = 0xf047,
|
||||||
|
arrowsalt = 0xf0b2,
|
||||||
|
arrowsh = 0xf07e,
|
||||||
|
arrowsv = 0xf07d,
|
||||||
|
aslinterpreting = 0xf2a3,
|
||||||
|
assistivelisteningsystems = 0xf2a2,
|
||||||
|
asterisk = 0xf069,
|
||||||
|
at = 0xf1fa,
|
||||||
|
audiodescription = 0xf29e,
|
||||||
|
automobile = 0xf1b9,
|
||||||
|
backward = 0xf04a,
|
||||||
|
balancescale = 0xf24e,
|
||||||
|
ban = 0xf05e,
|
||||||
|
bandcamp = 0xf2d5,
|
||||||
|
bank = 0xf19c,
|
||||||
|
barchart = 0xf080,
|
||||||
|
barcharto = 0xf080,
|
||||||
|
barcode = 0xf02a,
|
||||||
|
bars = 0xf0c9,
|
||||||
|
bath = 0xf2cd,
|
||||||
|
bathtub = 0xf2cd,
|
||||||
|
battery = 0xf240,
|
||||||
|
battery0 = 0xf244,
|
||||||
|
battery1 = 0xf243,
|
||||||
|
battery2 = 0xf242,
|
||||||
|
battery3 = 0xf241,
|
||||||
|
battery4 = 0xf240,
|
||||||
|
batteryempty = 0xf244,
|
||||||
|
batteryfull = 0xf240,
|
||||||
|
batteryhalf = 0xf242,
|
||||||
|
batteryquarter = 0xf243,
|
||||||
|
batterythreequarters = 0xf241,
|
||||||
|
bed = 0xf236,
|
||||||
|
beer = 0xf0fc,
|
||||||
|
behance = 0xf1b4,
|
||||||
|
behancesquare = 0xf1b5,
|
||||||
|
bell = 0xf0f3,
|
||||||
|
bello = 0xf0a2,
|
||||||
|
bellslash = 0xf1f6,
|
||||||
|
bellslasho = 0xf1f7,
|
||||||
|
bicycle = 0xf206,
|
||||||
|
binoculars = 0xf1e5,
|
||||||
|
birthdaycake = 0xf1fd,
|
||||||
|
bitbucket = 0xf171,
|
||||||
|
bitbucketsquare = 0xf172,
|
||||||
|
bitcoin = 0xf15a,
|
||||||
|
blacktie = 0xf27e,
|
||||||
|
blind = 0xf29d,
|
||||||
|
bluetooth = 0xf293,
|
||||||
|
bluetoothb = 0xf294,
|
||||||
|
bold = 0xf032,
|
||||||
|
bolt = 0xf0e7,
|
||||||
|
bomb = 0xf1e2,
|
||||||
|
book = 0xf02d,
|
||||||
|
bookmark = 0xf02e,
|
||||||
|
bookmarko = 0xf097,
|
||||||
|
braille = 0xf2a1,
|
||||||
|
briefcase = 0xf0b1,
|
||||||
|
btc = 0xf15a,
|
||||||
|
bug = 0xf188,
|
||||||
|
building = 0xf1ad,
|
||||||
|
buildingo = 0xf0f7,
|
||||||
|
bullhorn = 0xf0a1,
|
||||||
|
bullseye = 0xf140,
|
||||||
|
bus = 0xf207,
|
||||||
|
buysellads = 0xf20d,
|
||||||
|
cab = 0xf1ba,
|
||||||
|
calculator = 0xf1ec,
|
||||||
|
calendar = 0xf073,
|
||||||
|
calendarchecko = 0xf274,
|
||||||
|
calendarminuso = 0xf272,
|
||||||
|
calendaro = 0xf133,
|
||||||
|
calendarpluso = 0xf271,
|
||||||
|
calendartimeso = 0xf273,
|
||||||
|
camera = 0xf030,
|
||||||
|
cameraretro = 0xf083,
|
||||||
|
car = 0xf1b9,
|
||||||
|
caretdown = 0xf0d7,
|
||||||
|
caretleft = 0xf0d9,
|
||||||
|
caretright = 0xf0da,
|
||||||
|
caretsquareodown = 0xf150,
|
||||||
|
caretsquareoleft = 0xf191,
|
||||||
|
caretsquareoright = 0xf152,
|
||||||
|
caretsquareoup = 0xf151,
|
||||||
|
caretup = 0xf0d8,
|
||||||
|
cartarrowdown = 0xf218,
|
||||||
|
cartplus = 0xf217,
|
||||||
|
cc = 0xf20a,
|
||||||
|
ccamex = 0xf1f3,
|
||||||
|
ccdinersclub = 0xf24c,
|
||||||
|
ccdiscover = 0xf1f2,
|
||||||
|
ccjcb = 0xf24b,
|
||||||
|
ccmastercard = 0xf1f1,
|
||||||
|
ccpaypal = 0xf1f4,
|
||||||
|
ccstripe = 0xf1f5,
|
||||||
|
ccvisa = 0xf1f0,
|
||||||
|
certificate = 0xf0a3,
|
||||||
|
chain = 0xf0c1,
|
||||||
|
chainbroken = 0xf127,
|
||||||
|
check = 0xf00c,
|
||||||
|
checkcircle = 0xf058,
|
||||||
|
checkcircleo = 0xf05d,
|
||||||
|
checksquare = 0xf14a,
|
||||||
|
checksquareo = 0xf046,
|
||||||
|
chevroncircledown = 0xf13a,
|
||||||
|
chevroncircleleft = 0xf137,
|
||||||
|
chevroncircleright = 0xf138,
|
||||||
|
chevroncircleup = 0xf139,
|
||||||
|
chevrondown = 0xf078,
|
||||||
|
chevronleft = 0xf053,
|
||||||
|
chevronright = 0xf054,
|
||||||
|
chevronup = 0xf077,
|
||||||
|
child = 0xf1ae,
|
||||||
|
chrome = 0xf268,
|
||||||
|
circle = 0xf111,
|
||||||
|
circleo = 0xf10c,
|
||||||
|
circleonotch = 0xf1ce,
|
||||||
|
circlethin = 0xf1db,
|
||||||
|
clipboard = 0xf0ea,
|
||||||
|
clocko = 0xf017,
|
||||||
|
clone = 0xf24d,
|
||||||
|
close = 0xf00d,
|
||||||
|
cloud = 0xf0c2,
|
||||||
|
clouddownload = 0xf0ed,
|
||||||
|
cloudupload = 0xf0ee,
|
||||||
|
cny = 0xf157,
|
||||||
|
code = 0xf121,
|
||||||
|
codefork = 0xf126,
|
||||||
|
codepen = 0xf1cb,
|
||||||
|
codiepie = 0xf284,
|
||||||
|
coffee = 0xf0f4,
|
||||||
|
cog = 0xf013,
|
||||||
|
cogs = 0xf085,
|
||||||
|
columns = 0xf0db,
|
||||||
|
comment = 0xf075,
|
||||||
|
commento = 0xf0e5,
|
||||||
|
commenting = 0xf27a,
|
||||||
|
commentingo = 0xf27b,
|
||||||
|
comments = 0xf086,
|
||||||
|
commentso = 0xf0e6,
|
||||||
|
compass = 0xf14e,
|
||||||
|
compress = 0xf066,
|
||||||
|
connectdevelop = 0xf20e,
|
||||||
|
contao = 0xf26d,
|
||||||
|
copy = 0xf0c5,
|
||||||
|
copyright = 0xf1f9,
|
||||||
|
creativecommons = 0xf25e,
|
||||||
|
creditcard = 0xf09d,
|
||||||
|
creditcardalt = 0xf283,
|
||||||
|
crop = 0xf125,
|
||||||
|
crosshairs = 0xf05b,
|
||||||
|
css3 = 0xf13c,
|
||||||
|
cube = 0xf1b2,
|
||||||
|
cubes = 0xf1b3,
|
||||||
|
cut = 0xf0c4,
|
||||||
|
cutlery = 0xf0f5,
|
||||||
|
dashboard = 0xf0e4,
|
||||||
|
dashcube = 0xf210,
|
||||||
|
database = 0xf1c0,
|
||||||
|
deaf = 0xf2a4,
|
||||||
|
deafness = 0xf2a4,
|
||||||
|
dedent = 0xf03b,
|
||||||
|
delicious = 0xf1a5,
|
||||||
|
desktop = 0xf108,
|
||||||
|
deviantart = 0xf1bd,
|
||||||
|
diamond = 0xf219,
|
||||||
|
digg = 0xf1a6,
|
||||||
|
dollar = 0xf155,
|
||||||
|
dotcircleo = 0xf192,
|
||||||
|
download = 0xf019,
|
||||||
|
dribbble = 0xf17d,
|
||||||
|
driverslicense = 0xf2c2,
|
||||||
|
driverslicenseo = 0xf2c3,
|
||||||
|
dropbox = 0xf16b,
|
||||||
|
drupal = 0xf1a9,
|
||||||
|
edge = 0xf282,
|
||||||
|
edit = 0xf044,
|
||||||
|
eercast = 0xf2da,
|
||||||
|
eject = 0xf052,
|
||||||
|
ellipsish = 0xf141,
|
||||||
|
ellipsisv = 0xf142,
|
||||||
|
empire = 0xf1d1,
|
||||||
|
envelope = 0xf0e0,
|
||||||
|
envelopeo = 0xf003,
|
||||||
|
envelopeopen = 0xf2b6,
|
||||||
|
envelopeopeno = 0xf2b7,
|
||||||
|
envelopesquare = 0xf199,
|
||||||
|
envira = 0xf299,
|
||||||
|
eraser = 0xf12d,
|
||||||
|
etsy = 0xf2d7,
|
||||||
|
eur = 0xf153,
|
||||||
|
euro = 0xf153,
|
||||||
|
exchange = 0xf0ec,
|
||||||
|
exclamation = 0xf12a,
|
||||||
|
exclamationcircle = 0xf06a,
|
||||||
|
exclamationtriangle = 0xf071,
|
||||||
|
expand = 0xf065,
|
||||||
|
expeditedssl = 0xf23e,
|
||||||
|
externallink = 0xf08e,
|
||||||
|
externallinksquare = 0xf14c,
|
||||||
|
eye = 0xf06e,
|
||||||
|
eyeslash = 0xf070,
|
||||||
|
eyedropper = 0xf1fb,
|
||||||
|
fa = 0xf2b4,
|
||||||
|
facebook = 0xf09a,
|
||||||
|
facebookf = 0xf09a,
|
||||||
|
facebookofficial = 0xf230,
|
||||||
|
facebooksquare = 0xf082,
|
||||||
|
fastbackward = 0xf049,
|
||||||
|
fastforward = 0xf050,
|
||||||
|
fax = 0xf1ac,
|
||||||
|
feed = 0xf09e,
|
||||||
|
female = 0xf182,
|
||||||
|
fighterjet = 0xf0fb,
|
||||||
|
file = 0xf15b,
|
||||||
|
filearchiveo = 0xf1c6,
|
||||||
|
fileaudioo = 0xf1c7,
|
||||||
|
filecodeo = 0xf1c9,
|
||||||
|
fileexcelo = 0xf1c3,
|
||||||
|
fileimageo = 0xf1c5,
|
||||||
|
filemovieo = 0xf1c8,
|
||||||
|
fileo = 0xf016,
|
||||||
|
filepdfo = 0xf1c1,
|
||||||
|
filephotoo = 0xf1c5,
|
||||||
|
filepictureo = 0xf1c5,
|
||||||
|
filepowerpointo = 0xf1c4,
|
||||||
|
filesoundo = 0xf1c7,
|
||||||
|
filetext = 0xf15c,
|
||||||
|
filetexto = 0xf0f6,
|
||||||
|
filevideoo = 0xf1c8,
|
||||||
|
filewordo = 0xf1c2,
|
||||||
|
filezipo = 0xf1c6,
|
||||||
|
fileso = 0xf0c5,
|
||||||
|
film = 0xf008,
|
||||||
|
filter = 0xf0b0,
|
||||||
|
fire = 0xf06d,
|
||||||
|
fireextinguisher = 0xf134,
|
||||||
|
firefox = 0xf269,
|
||||||
|
firstorder = 0xf2b0,
|
||||||
|
flag = 0xf024,
|
||||||
|
flagcheckered = 0xf11e,
|
||||||
|
flago = 0xf11d,
|
||||||
|
flash = 0xf0e7,
|
||||||
|
flask = 0xf0c3,
|
||||||
|
flickr = 0xf16e,
|
||||||
|
floppyo = 0xf0c7,
|
||||||
|
folder = 0xf07b,
|
||||||
|
foldero = 0xf114,
|
||||||
|
folderopen = 0xf07c,
|
||||||
|
folderopeno = 0xf115,
|
||||||
|
font = 0xf031,
|
||||||
|
fontawesome = 0xf2b4,
|
||||||
|
fonticons = 0xf280,
|
||||||
|
fortawesome = 0xf286,
|
||||||
|
forumbee = 0xf211,
|
||||||
|
forward = 0xf04e,
|
||||||
|
foursquare = 0xf180,
|
||||||
|
freecodecamp = 0xf2c5,
|
||||||
|
frowno = 0xf119,
|
||||||
|
futbolo = 0xf1e3,
|
||||||
|
gamepad = 0xf11b,
|
||||||
|
gavel = 0xf0e3,
|
||||||
|
gbp = 0xf154,
|
||||||
|
ge = 0xf1d1,
|
||||||
|
gear = 0xf013,
|
||||||
|
gears = 0xf085,
|
||||||
|
genderless = 0xf22d,
|
||||||
|
getpocket = 0xf265,
|
||||||
|
gg = 0xf260,
|
||||||
|
ggcircle = 0xf261,
|
||||||
|
gift = 0xf06b,
|
||||||
|
git = 0xf1d3,
|
||||||
|
gitsquare = 0xf1d2,
|
||||||
|
github = 0xf09b,
|
||||||
|
githubalt = 0xf113,
|
||||||
|
githubsquare = 0xf092,
|
||||||
|
gitlab = 0xf296,
|
||||||
|
gittip = 0xf184,
|
||||||
|
glass = 0xf000,
|
||||||
|
glide = 0xf2a5,
|
||||||
|
glideg = 0xf2a6,
|
||||||
|
globe = 0xf0ac,
|
||||||
|
google = 0xf1a0,
|
||||||
|
googleplus = 0xf0d5,
|
||||||
|
googlepluscircle = 0xf2b3,
|
||||||
|
googleplusofficial = 0xf2b3,
|
||||||
|
googleplussquare = 0xf0d4,
|
||||||
|
googlewallet = 0xf1ee,
|
||||||
|
graduationcap = 0xf19d,
|
||||||
|
gratipay = 0xf184,
|
||||||
|
grav = 0xf2d6,
|
||||||
|
group = 0xf0c0,
|
||||||
|
hsquare = 0xf0fd,
|
||||||
|
hackernews = 0xf1d4,
|
||||||
|
handgrabo = 0xf255,
|
||||||
|
handlizardo = 0xf258,
|
||||||
|
handodown = 0xf0a7,
|
||||||
|
handoleft = 0xf0a5,
|
||||||
|
handoright = 0xf0a4,
|
||||||
|
handoup = 0xf0a6,
|
||||||
|
handpapero = 0xf256,
|
||||||
|
handpeaceo = 0xf25b,
|
||||||
|
handpointero = 0xf25a,
|
||||||
|
handrocko = 0xf255,
|
||||||
|
handscissorso = 0xf257,
|
||||||
|
handspocko = 0xf259,
|
||||||
|
handstopo = 0xf256,
|
||||||
|
handshakeo = 0xf2b5,
|
||||||
|
hardofhearing = 0xf2a4,
|
||||||
|
hashtag = 0xf292,
|
||||||
|
hddo = 0xf0a0,
|
||||||
|
header = 0xf1dc,
|
||||||
|
headphones = 0xf025,
|
||||||
|
heart = 0xf004,
|
||||||
|
hearto = 0xf08a,
|
||||||
|
heartbeat = 0xf21e,
|
||||||
|
history = 0xf1da,
|
||||||
|
home = 0xf015,
|
||||||
|
hospitalo = 0xf0f8,
|
||||||
|
hotel = 0xf236,
|
||||||
|
hourglass = 0xf254,
|
||||||
|
hourglass1 = 0xf251,
|
||||||
|
hourglass2 = 0xf252,
|
||||||
|
hourglass3 = 0xf253,
|
||||||
|
hourglassend = 0xf253,
|
||||||
|
hourglasshalf = 0xf252,
|
||||||
|
hourglasso = 0xf250,
|
||||||
|
hourglassstart = 0xf251,
|
||||||
|
houzz = 0xf27c,
|
||||||
|
html5 = 0xf13b,
|
||||||
|
icursor = 0xf246,
|
||||||
|
idbadge = 0xf2c1,
|
||||||
|
idcard = 0xf2c2,
|
||||||
|
idcardo = 0xf2c3,
|
||||||
|
ils = 0xf20b,
|
||||||
|
image = 0xf03e,
|
||||||
|
imdb = 0xf2d8,
|
||||||
|
inbox = 0xf01c,
|
||||||
|
indent = 0xf03c,
|
||||||
|
industry = 0xf275,
|
||||||
|
info = 0xf129,
|
||||||
|
infocircle = 0xf05a,
|
||||||
|
inr = 0xf156,
|
||||||
|
instagram = 0xf16d,
|
||||||
|
institution = 0xf19c,
|
||||||
|
internetexplorer = 0xf26b,
|
||||||
|
intersex = 0xf224,
|
||||||
|
ioxhost = 0xf208,
|
||||||
|
italic = 0xf033,
|
||||||
|
joomla = 0xf1aa,
|
||||||
|
jpy = 0xf157,
|
||||||
|
jsfiddle = 0xf1cc,
|
||||||
|
key = 0xf084,
|
||||||
|
keyboardo = 0xf11c,
|
||||||
|
krw = 0xf159,
|
||||||
|
language = 0xf1ab,
|
||||||
|
laptop = 0xf109,
|
||||||
|
lastfm = 0xf202,
|
||||||
|
lastfmsquare = 0xf203,
|
||||||
|
leaf = 0xf06c,
|
||||||
|
leanpub = 0xf212,
|
||||||
|
legal = 0xf0e3,
|
||||||
|
lemono = 0xf094,
|
||||||
|
leveldown = 0xf149,
|
||||||
|
levelup = 0xf148,
|
||||||
|
lifebouy = 0xf1cd,
|
||||||
|
lifebuoy = 0xf1cd,
|
||||||
|
lifering = 0xf1cd,
|
||||||
|
lifesaver = 0xf1cd,
|
||||||
|
lightbulbo = 0xf0eb,
|
||||||
|
linechart = 0xf201,
|
||||||
|
link = 0xf0c1,
|
||||||
|
linkedin = 0xf0e1,
|
||||||
|
linkedinsquare = 0xf08c,
|
||||||
|
linode = 0xf2b8,
|
||||||
|
fa_linux = 0xf17c,
|
||||||
|
list = 0xf03a,
|
||||||
|
listalt = 0xf022,
|
||||||
|
listol = 0xf0cb,
|
||||||
|
listul = 0xf0ca,
|
||||||
|
locationarrow = 0xf124,
|
||||||
|
lock = 0xf023,
|
||||||
|
longarrowdown = 0xf175,
|
||||||
|
longarrowleft = 0xf177,
|
||||||
|
longarrowright = 0xf178,
|
||||||
|
longarrowup = 0xf176,
|
||||||
|
lowvision = 0xf2a8,
|
||||||
|
magic = 0xf0d0,
|
||||||
|
magnet = 0xf076,
|
||||||
|
mailforward = 0xf064,
|
||||||
|
mailreply = 0xf112,
|
||||||
|
mailreplyall = 0xf122,
|
||||||
|
male = 0xf183,
|
||||||
|
map = 0xf279,
|
||||||
|
mapmarker = 0xf041,
|
||||||
|
mapo = 0xf278,
|
||||||
|
mappin = 0xf276,
|
||||||
|
mapsigns = 0xf277,
|
||||||
|
mars = 0xf222,
|
||||||
|
marsdouble = 0xf227,
|
||||||
|
marsstroke = 0xf229,
|
||||||
|
marsstrokeh = 0xf22b,
|
||||||
|
marsstrokev = 0xf22a,
|
||||||
|
maxcdn = 0xf136,
|
||||||
|
meanpath = 0xf20c,
|
||||||
|
medium = 0xf23a,
|
||||||
|
medkit = 0xf0fa,
|
||||||
|
meetup = 0xf2e0,
|
||||||
|
meho = 0xf11a,
|
||||||
|
mercury = 0xf223,
|
||||||
|
microchip = 0xf2db,
|
||||||
|
microphone = 0xf130,
|
||||||
|
microphoneslash = 0xf131,
|
||||||
|
minus = 0xf068,
|
||||||
|
minuscircle = 0xf056,
|
||||||
|
minussquare = 0xf146,
|
||||||
|
minussquareo = 0xf147,
|
||||||
|
mixcloud = 0xf289,
|
||||||
|
mobile = 0xf10b,
|
||||||
|
mobilephone = 0xf10b,
|
||||||
|
modx = 0xf285,
|
||||||
|
money = 0xf0d6,
|
||||||
|
moono = 0xf186,
|
||||||
|
mortarboard = 0xf19d,
|
||||||
|
motorcycle = 0xf21c,
|
||||||
|
mousepointer = 0xf245,
|
||||||
|
music = 0xf001,
|
||||||
|
navicon = 0xf0c9,
|
||||||
|
neuter = 0xf22c,
|
||||||
|
newspapero = 0xf1ea,
|
||||||
|
objectgroup = 0xf247,
|
||||||
|
objectungroup = 0xf248,
|
||||||
|
odnoklassniki = 0xf263,
|
||||||
|
odnoklassnikisquare = 0xf264,
|
||||||
|
opencart = 0xf23d,
|
||||||
|
openid = 0xf19b,
|
||||||
|
opera = 0xf26a,
|
||||||
|
optinmonster = 0xf23c,
|
||||||
|
outdent = 0xf03b,
|
||||||
|
pagelines = 0xf18c,
|
||||||
|
paintbrush = 0xf1fc,
|
||||||
|
paperplane = 0xf1d8,
|
||||||
|
paperplaneo = 0xf1d9,
|
||||||
|
paperclip = 0xf0c6,
|
||||||
|
paragraph = 0xf1dd,
|
||||||
|
paste = 0xf0ea,
|
||||||
|
pause = 0xf04c,
|
||||||
|
pausecircle = 0xf28b,
|
||||||
|
pausecircleo = 0xf28c,
|
||||||
|
paw = 0xf1b0,
|
||||||
|
paypal = 0xf1ed,
|
||||||
|
pencil = 0xf040,
|
||||||
|
pencilsquare = 0xf14b,
|
||||||
|
pencilsquareo = 0xf044,
|
||||||
|
percent = 0xf295,
|
||||||
|
phone = 0xf095,
|
||||||
|
phonesquare = 0xf098,
|
||||||
|
photo = 0xf03e,
|
||||||
|
pictureo = 0xf03e,
|
||||||
|
piechart = 0xf200,
|
||||||
|
piedpiper = 0xf2ae,
|
||||||
|
piedpiperalt = 0xf1a8,
|
||||||
|
piedpiperpp = 0xf1a7,
|
||||||
|
pinterest = 0xf0d2,
|
||||||
|
pinterestp = 0xf231,
|
||||||
|
pinterestsquare = 0xf0d3,
|
||||||
|
plane = 0xf072,
|
||||||
|
play = 0xf04b,
|
||||||
|
playcircle = 0xf144,
|
||||||
|
playcircleo = 0xf01d,
|
||||||
|
plug = 0xf1e6,
|
||||||
|
plus = 0xf067,
|
||||||
|
pluscircle = 0xf055,
|
||||||
|
plussquare = 0xf0fe,
|
||||||
|
plussquareo = 0xf196,
|
||||||
|
podcast = 0xf2ce,
|
||||||
|
poweroff = 0xf011,
|
||||||
|
print = 0xf02f,
|
||||||
|
producthunt = 0xf288,
|
||||||
|
puzzlepiece = 0xf12e,
|
||||||
|
qq = 0xf1d6,
|
||||||
|
qrcode = 0xf029,
|
||||||
|
question = 0xf128,
|
||||||
|
questioncircle = 0xf059,
|
||||||
|
questioncircleo = 0xf29c,
|
||||||
|
quora = 0xf2c4,
|
||||||
|
quoteleft = 0xf10d,
|
||||||
|
quoteright = 0xf10e,
|
||||||
|
ra = 0xf1d0,
|
||||||
|
random = 0xf074,
|
||||||
|
ravelry = 0xf2d9,
|
||||||
|
rebel = 0xf1d0,
|
||||||
|
recycle = 0xf1b8,
|
||||||
|
reddit = 0xf1a1,
|
||||||
|
redditalien = 0xf281,
|
||||||
|
redditsquare = 0xf1a2,
|
||||||
|
refresh = 0xf021,
|
||||||
|
registered = 0xf25d,
|
||||||
|
remove = 0xf00d,
|
||||||
|
renren = 0xf18b,
|
||||||
|
reorder = 0xf0c9,
|
||||||
|
repeat = 0xf01e,
|
||||||
|
reply = 0xf112,
|
||||||
|
replyall = 0xf122,
|
||||||
|
resistance = 0xf1d0,
|
||||||
|
retweet = 0xf079,
|
||||||
|
rmb = 0xf157,
|
||||||
|
road = 0xf018,
|
||||||
|
rocket = 0xf135,
|
||||||
|
rotateleft = 0xf0e2,
|
||||||
|
rotateright = 0xf01e,
|
||||||
|
rouble = 0xf158,
|
||||||
|
rss = 0xf09e,
|
||||||
|
rsssquare = 0xf143,
|
||||||
|
rub = 0xf158,
|
||||||
|
ruble = 0xf158,
|
||||||
|
rupee = 0xf156,
|
||||||
|
s15 = 0xf2cd,
|
||||||
|
safari = 0xf267,
|
||||||
|
save = 0xf0c7,
|
||||||
|
scissors = 0xf0c4,
|
||||||
|
scribd = 0xf28a,
|
||||||
|
search = 0xf002,
|
||||||
|
searchminus = 0xf010,
|
||||||
|
searchplus = 0xf00e,
|
||||||
|
sellsy = 0xf213,
|
||||||
|
send = 0xf1d8,
|
||||||
|
sendo = 0xf1d9,
|
||||||
|
server = 0xf233,
|
||||||
|
share = 0xf064,
|
||||||
|
sharealt = 0xf1e0,
|
||||||
|
sharealtsquare = 0xf1e1,
|
||||||
|
sharesquare = 0xf14d,
|
||||||
|
sharesquareo = 0xf045,
|
||||||
|
shekel = 0xf20b,
|
||||||
|
sheqel = 0xf20b,
|
||||||
|
shield = 0xf132,
|
||||||
|
ship = 0xf21a,
|
||||||
|
shirtsinbulk = 0xf214,
|
||||||
|
shoppingbag = 0xf290,
|
||||||
|
shoppingbasket = 0xf291,
|
||||||
|
shoppingcart = 0xf07a,
|
||||||
|
shower = 0xf2cc,
|
||||||
|
signin = 0xf090,
|
||||||
|
signlanguage = 0xf2a7,
|
||||||
|
signout = 0xf08b,
|
||||||
|
signal = 0xf012,
|
||||||
|
signing = 0xf2a7,
|
||||||
|
simplybuilt = 0xf215,
|
||||||
|
sitemap = 0xf0e8,
|
||||||
|
skyatlas = 0xf216,
|
||||||
|
skype = 0xf17e,
|
||||||
|
slack = 0xf198,
|
||||||
|
sliders = 0xf1de,
|
||||||
|
slideshare = 0xf1e7,
|
||||||
|
smileo = 0xf118,
|
||||||
|
snapchat = 0xf2ab,
|
||||||
|
snapchatghost = 0xf2ac,
|
||||||
|
snapchatsquare = 0xf2ad,
|
||||||
|
snowflakeo = 0xf2dc,
|
||||||
|
soccerballo = 0xf1e3,
|
||||||
|
sort = 0xf0dc,
|
||||||
|
sortalphaasc = 0xf15d,
|
||||||
|
sortalphadesc = 0xf15e,
|
||||||
|
sortamountasc = 0xf160,
|
||||||
|
sortamountdesc = 0xf161,
|
||||||
|
sortasc = 0xf0de,
|
||||||
|
sortdesc = 0xf0dd,
|
||||||
|
sortdown = 0xf0dd,
|
||||||
|
sortnumericasc = 0xf162,
|
||||||
|
sortnumericdesc = 0xf163,
|
||||||
|
sortup = 0xf0de,
|
||||||
|
soundcloud = 0xf1be,
|
||||||
|
spaceshuttle = 0xf197,
|
||||||
|
spinner = 0xf110,
|
||||||
|
spoon = 0xf1b1,
|
||||||
|
spotify = 0xf1bc,
|
||||||
|
square = 0xf0c8,
|
||||||
|
squareo = 0xf096,
|
||||||
|
stackexchange = 0xf18d,
|
||||||
|
stackoverflow = 0xf16c,
|
||||||
|
star = 0xf005,
|
||||||
|
starhalf = 0xf089,
|
||||||
|
starhalfempty = 0xf123,
|
||||||
|
starhalffull = 0xf123,
|
||||||
|
starhalfo = 0xf123,
|
||||||
|
staro = 0xf006,
|
||||||
|
steam = 0xf1b6,
|
||||||
|
steamsquare = 0xf1b7,
|
||||||
|
stepbackward = 0xf048,
|
||||||
|
stepforward = 0xf051,
|
||||||
|
stethoscope = 0xf0f1,
|
||||||
|
stickynote = 0xf249,
|
||||||
|
stickynoteo = 0xf24a,
|
||||||
|
stop = 0xf04d,
|
||||||
|
stopcircle = 0xf28d,
|
||||||
|
stopcircleo = 0xf28e,
|
||||||
|
streetview = 0xf21d,
|
||||||
|
strikethrough = 0xf0cc,
|
||||||
|
stumbleupon = 0xf1a4,
|
||||||
|
stumbleuponcircle = 0xf1a3,
|
||||||
|
subscript = 0xf12c,
|
||||||
|
subway = 0xf239,
|
||||||
|
suitcase = 0xf0f2,
|
||||||
|
suno = 0xf185,
|
||||||
|
superpowers = 0xf2dd,
|
||||||
|
superscript = 0xf12b,
|
||||||
|
support = 0xf1cd,
|
||||||
|
table = 0xf0ce,
|
||||||
|
tablet = 0xf10a,
|
||||||
|
tachometer = 0xf0e4,
|
||||||
|
tag = 0xf02b,
|
||||||
|
tags = 0xf02c,
|
||||||
|
tasks = 0xf0ae,
|
||||||
|
taxi = 0xf1ba,
|
||||||
|
telegram = 0xf2c6,
|
||||||
|
television = 0xf26c,
|
||||||
|
tencentweibo = 0xf1d5,
|
||||||
|
terminal = 0xf120,
|
||||||
|
textheight = 0xf034,
|
||||||
|
textwidth = 0xf035,
|
||||||
|
th = 0xf00a,
|
||||||
|
thlarge = 0xf009,
|
||||||
|
thlist = 0xf00b,
|
||||||
|
themeisle = 0xf2b2,
|
||||||
|
thermometer = 0xf2c7,
|
||||||
|
thermometer0 = 0xf2cb,
|
||||||
|
thermometer1 = 0xf2ca,
|
||||||
|
thermometer2 = 0xf2c9,
|
||||||
|
thermometer3 = 0xf2c8,
|
||||||
|
thermometer4 = 0xf2c7,
|
||||||
|
thermometerempty = 0xf2cb,
|
||||||
|
thermometerfull = 0xf2c7,
|
||||||
|
thermometerhalf = 0xf2c9,
|
||||||
|
thermometerquarter = 0xf2ca,
|
||||||
|
thermometerthreequarters = 0xf2c8,
|
||||||
|
thumbtack = 0xf08d,
|
||||||
|
thumbsdown = 0xf165,
|
||||||
|
thumbsodown = 0xf088,
|
||||||
|
thumbsoup = 0xf087,
|
||||||
|
thumbsup = 0xf164,
|
||||||
|
ticket = 0xf145,
|
||||||
|
times = 0xf00d,
|
||||||
|
timescircle = 0xf057,
|
||||||
|
timescircleo = 0xf05c,
|
||||||
|
timesrectangle = 0xf2d3,
|
||||||
|
timesrectangleo = 0xf2d4,
|
||||||
|
tint = 0xf043,
|
||||||
|
toggledown = 0xf150,
|
||||||
|
toggleleft = 0xf191,
|
||||||
|
toggleoff = 0xf204,
|
||||||
|
toggleon = 0xf205,
|
||||||
|
toggleright = 0xf152,
|
||||||
|
toggleup = 0xf151,
|
||||||
|
trademark = 0xf25c,
|
||||||
|
train = 0xf238,
|
||||||
|
transgender = 0xf224,
|
||||||
|
transgenderalt = 0xf225,
|
||||||
|
trash = 0xf1f8,
|
||||||
|
trasho = 0xf014,
|
||||||
|
tree = 0xf1bb,
|
||||||
|
trello = 0xf181,
|
||||||
|
tripadvisor = 0xf262,
|
||||||
|
trophy = 0xf091,
|
||||||
|
truck = 0xf0d1,
|
||||||
|
fa_try = 0xf195,
|
||||||
|
tty = 0xf1e4,
|
||||||
|
tumblr = 0xf173,
|
||||||
|
tumblrsquare = 0xf174,
|
||||||
|
turkishlira = 0xf195,
|
||||||
|
tv = 0xf26c,
|
||||||
|
twitch = 0xf1e8,
|
||||||
|
twitter = 0xf099,
|
||||||
|
twittersquare = 0xf081,
|
||||||
|
umbrella = 0xf0e9,
|
||||||
|
underline = 0xf0cd,
|
||||||
|
undo = 0xf0e2,
|
||||||
|
universalaccess = 0xf29a,
|
||||||
|
university = 0xf19c,
|
||||||
|
unlink = 0xf127,
|
||||||
|
unlock = 0xf09c,
|
||||||
|
unlockalt = 0xf13e,
|
||||||
|
unsorted = 0xf0dc,
|
||||||
|
upload = 0xf093,
|
||||||
|
usb = 0xf287,
|
||||||
|
usd = 0xf155,
|
||||||
|
user = 0xf007,
|
||||||
|
usercircle = 0xf2bd,
|
||||||
|
usercircleo = 0xf2be,
|
||||||
|
usermd = 0xf0f0,
|
||||||
|
usero = 0xf2c0,
|
||||||
|
userplus = 0xf234,
|
||||||
|
usersecret = 0xf21b,
|
||||||
|
usertimes = 0xf235,
|
||||||
|
users = 0xf0c0,
|
||||||
|
vcard = 0xf2bb,
|
||||||
|
vcardo = 0xf2bc,
|
||||||
|
venus = 0xf221,
|
||||||
|
venusdouble = 0xf226,
|
||||||
|
venusmars = 0xf228,
|
||||||
|
viacoin = 0xf237,
|
||||||
|
viadeo = 0xf2a9,
|
||||||
|
viadeosquare = 0xf2aa,
|
||||||
|
videocamera = 0xf03d,
|
||||||
|
vimeo = 0xf27d,
|
||||||
|
vimeosquare = 0xf194,
|
||||||
|
vine = 0xf1ca,
|
||||||
|
vk = 0xf189,
|
||||||
|
volumecontrolphone = 0xf2a0,
|
||||||
|
volumedown = 0xf027,
|
||||||
|
volumeoff = 0xf026,
|
||||||
|
volumeup = 0xf028,
|
||||||
|
warning = 0xf071,
|
||||||
|
wechat = 0xf1d7,
|
||||||
|
weibo = 0xf18a,
|
||||||
|
weixin = 0xf1d7,
|
||||||
|
whatsapp = 0xf232,
|
||||||
|
wheelchair = 0xf193,
|
||||||
|
wheelchairalt = 0xf29b,
|
||||||
|
wifi = 0xf1eb,
|
||||||
|
wikipediaw = 0xf266,
|
||||||
|
windowclose = 0xf2d3,
|
||||||
|
windowcloseo = 0xf2d4,
|
||||||
|
windowmaximize = 0xf2d0,
|
||||||
|
windowminimize = 0xf2d1,
|
||||||
|
windowrestore = 0xf2d2,
|
||||||
|
windows = 0xf17a,
|
||||||
|
won = 0xf159,
|
||||||
|
wordpress = 0xf19a,
|
||||||
|
wpbeginner = 0xf297,
|
||||||
|
wpexplorer = 0xf2de,
|
||||||
|
wpforms = 0xf298,
|
||||||
|
wrench = 0xf0ad,
|
||||||
|
xing = 0xf168,
|
||||||
|
xingsquare = 0xf169,
|
||||||
|
ycombinator = 0xf23b,
|
||||||
|
ycombinatorsquare = 0xf1d4,
|
||||||
|
yahoo = 0xf19e,
|
||||||
|
yc = 0xf23b,
|
||||||
|
ycsquare = 0xf1d4,
|
||||||
|
yelp = 0xf1e9,
|
||||||
|
yen = 0xf157,
|
||||||
|
yoast = 0xf2b1,
|
||||||
|
youtube = 0xf167,
|
||||||
|
youtubeplay = 0xf16a,
|
||||||
|
youtubesquare = 0xf166
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class QtAwesomeIconPainter;
|
||||||
|
|
||||||
|
/// The main class for managing icons
|
||||||
|
/// This class requires a 2-phase construction. You must first create the class and then initialize it via an init* method
|
||||||
|
class QtAwesome : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
explicit QtAwesome(QObject *parent = 0);
|
||||||
|
virtual ~QtAwesome();
|
||||||
|
|
||||||
|
void init( const QString& fontname );
|
||||||
|
bool initFontAwesome();
|
||||||
|
|
||||||
|
void addNamedCodepoint( const QString& name, int codePoint );
|
||||||
|
QHash<QString,int> namedCodePoints() { return namedCodepoints_; }
|
||||||
|
|
||||||
|
void setDefaultOption( const QString& name, const QVariant& value );
|
||||||
|
QVariant defaultOption( const QString& name );
|
||||||
|
|
||||||
|
QIcon icon( int character, const QVariantMap& options = QVariantMap() );
|
||||||
|
QIcon icon( const QString& name, const QVariantMap& options = QVariantMap() );
|
||||||
|
QIcon icon(QtAwesomeIconPainter* painter, const QVariantMap& optionMap = QVariantMap() );
|
||||||
|
|
||||||
|
void give( const QString& name, QtAwesomeIconPainter* painter );
|
||||||
|
|
||||||
|
QFont font( int size );
|
||||||
|
|
||||||
|
/// Returns the font-name that is used as icon-map
|
||||||
|
QString fontName() { return fontName_ ; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString fontName_; ///< The font name used for this map
|
||||||
|
QHash<QString,int> namedCodepoints_; ///< A map with names mapped to code-points
|
||||||
|
|
||||||
|
QHash<QString, QtAwesomeIconPainter*> painterMap_; ///< A map of custom painters
|
||||||
|
QVariantMap defaultOptions_; ///< The default icon options
|
||||||
|
QtAwesomeIconPainter* fontIconPainter_; ///< A special painter fo painting codepoints
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
/// The QtAwesomeIconPainter is a specialized painter for painting icons
|
||||||
|
/// your can implement an iconpainter to create custom font-icon code
|
||||||
|
class QtAwesomeIconPainter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~QtAwesomeIconPainter() {}
|
||||||
|
virtual void paint( QtAwesome* awesome, QPainter* painter, const QRect& rect, QIcon::Mode mode, QIcon::State state, const QVariantMap& options ) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(QtAwesomeAnimation*)
|
||||||
|
|
||||||
|
#endif // QTAWESOME_H
|
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
INCLUDEPATH += $$PWD
|
||||||
|
|
||||||
|
SOURCES += $$PWD/QtAwesome.cpp \
|
||||||
|
$$PWD/QtAwesomeAnim.cpp
|
||||||
|
|
||||||
|
HEADERS += $$PWD/QtAwesome.h \
|
||||||
|
$$PWD/QtAwesomeAnim.h
|
||||||
|
|
||||||
|
RESOURCES += $$PWD/QtAwesome.qrc
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
#-------------------------------------------------
|
||||||
|
#
|
||||||
|
# Project created by QtCreator 2013-04-18T13:28:42
|
||||||
|
#
|
||||||
|
#-------------------------------------------------
|
||||||
|
|
||||||
|
TARGET = QtAwesome
|
||||||
|
TEMPLATE = lib
|
||||||
|
CONFIG += staticlib c++11
|
||||||
|
QT += widgets
|
||||||
|
|
||||||
|
SOURCES += QtAwesome.cpp QtAwesomeAnim.cpp
|
||||||
|
HEADERS += QtAwesome.h QtAwesomeAnim.h
|
||||||
|
|
||||||
|
isEmpty(PREFIX) {
|
||||||
|
unix {
|
||||||
|
PREFIX = /usr
|
||||||
|
} else {
|
||||||
|
PREFIX = $$[QT_INSTALL_PREFIX]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
install_headers.files = QtAwesome.h QtAwesomeAnim.h
|
||||||
|
install_headers.path = $$PREFIX/include
|
||||||
|
target.path = $$PREFIX/lib
|
||||||
|
INSTALLS += install_headers target
|
||||||
|
|
||||||
|
RESOURCES += \
|
||||||
|
QtAwesome.qrc
|
|
@ -0,0 +1,5 @@
|
||||||
|
<RCC>
|
||||||
|
<qresource prefix="/">
|
||||||
|
<file>fonts/fontawesome-4.7.0.ttf</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
|
@ -0,0 +1,46 @@
|
||||||
|
#include "QtAwesomeAnim.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QRect>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
|
||||||
|
QtAwesomeAnimation::QtAwesomeAnimation(QWidget *parentWidget, int interval, int step)
|
||||||
|
: parentWidgetRef_( parentWidget )
|
||||||
|
, timer_( 0 )
|
||||||
|
, interval_( interval )
|
||||||
|
, step_( step )
|
||||||
|
, angle_( 0.0f )
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtAwesomeAnimation::setup( QPainter &painter, const QRect &rect)
|
||||||
|
{
|
||||||
|
// first time set the timer
|
||||||
|
if( !timer_ )
|
||||||
|
{
|
||||||
|
timer_ = new QTimer();
|
||||||
|
connect(timer_,SIGNAL(timeout()), this, SLOT(update()) );
|
||||||
|
timer_->start(interval_);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//timer, angle, self.step = self.info[self.parent_widget]
|
||||||
|
float x_center = rect.width() * 0.5;
|
||||||
|
float y_center = rect.height() * 0.5;
|
||||||
|
painter.translate(x_center, y_center);
|
||||||
|
painter.rotate(angle_);
|
||||||
|
painter.translate(-x_center, -y_center);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void QtAwesomeAnimation::update()
|
||||||
|
{
|
||||||
|
angle_ += step_;
|
||||||
|
angle_ = std::fmod( angle_, 360);
|
||||||
|
parentWidgetRef_->update();
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
#ifndef QTAWESOMEANIMATION_H
|
||||||
|
#define QTAWESOMEANIMATION_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class QPainter;
|
||||||
|
class QRect;
|
||||||
|
class QTimer;
|
||||||
|
class QWidget;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Basic Animation Support for QtAwesome (Inspired by https://github.com/spyder-ide/qtawesome)
|
||||||
|
///
|
||||||
|
class QtAwesomeAnimation : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
QtAwesomeAnimation( QWidget* parentWidget, int interval=10, int step=1);
|
||||||
|
|
||||||
|
void setup( QPainter& painter, const QRect& rect );
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void update();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QWidget* parentWidgetRef_;
|
||||||
|
QTimer* timer_;
|
||||||
|
int interval_;
|
||||||
|
int step_;
|
||||||
|
float angle_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // QTAWESOMEANIMATION_H
|
Binary file not shown.
|
@ -0,0 +1,20 @@
|
||||||
|
#-------------------------------------------------
|
||||||
|
#
|
||||||
|
# Project created by QtCreator 2013-04-18T15:05:07
|
||||||
|
#
|
||||||
|
#-------------------------------------------------
|
||||||
|
|
||||||
|
QT += core gui
|
||||||
|
|
||||||
|
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||||
|
|
||||||
|
TARGET = QtAwesomeSample
|
||||||
|
TEMPLATE = app
|
||||||
|
|
||||||
|
|
||||||
|
SOURCES += main.cpp
|
||||||
|
|
||||||
|
HEADERS +=
|
||||||
|
|
||||||
|
|
||||||
|
include(../QtAwesome/QtAwesome.pri)
|
|
@ -0,0 +1,63 @@
|
||||||
|
/**
|
||||||
|
* MIT Licensed
|
||||||
|
*
|
||||||
|
* Copyright 2011-2015 - Reliable Bits Software by Blommers IT. All Rights Reserved.
|
||||||
|
* Author Rick Blommers
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "QtAwesome.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QApplication app(argc, argv);
|
||||||
|
QMainWindow w;
|
||||||
|
|
||||||
|
QtAwesome* awesome = new QtAwesome(&w);
|
||||||
|
awesome->initFontAwesome();
|
||||||
|
|
||||||
|
QVBoxLayout* layout = new QVBoxLayout();
|
||||||
|
|
||||||
|
// a simple beer button
|
||||||
|
//=====================
|
||||||
|
{
|
||||||
|
QPushButton* beerButton = new QPushButton( "Cheers!");
|
||||||
|
|
||||||
|
QVariantMap options;
|
||||||
|
options.insert("anim", qVariantFromValue( new QtAwesomeAnimation(beerButton) ) );
|
||||||
|
beerButton->setIcon( awesome->icon( fa::beer, options ) );
|
||||||
|
|
||||||
|
layout->addWidget(beerButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
// a simple beer checkbox button
|
||||||
|
//==============================
|
||||||
|
{
|
||||||
|
QPushButton* toggleButton = new QPushButton("Toggle Me");
|
||||||
|
toggleButton->setCheckable(true);
|
||||||
|
|
||||||
|
QVariantMap options;
|
||||||
|
options.insert("color", QColor(Qt::green) );
|
||||||
|
options.insert("text-off", QString(fa::squareo) );
|
||||||
|
options.insert("color-off", QColor(Qt::red) );
|
||||||
|
toggleButton->setIcon( awesome->icon( fa::checksquareo, options ));
|
||||||
|
|
||||||
|
|
||||||
|
layout->addWidget(toggleButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// add the samples
|
||||||
|
QWidget* samples = new QWidget();
|
||||||
|
samples->setLayout(layout);
|
||||||
|
w.setCentralWidget(samples);
|
||||||
|
|
||||||
|
w.show();
|
||||||
|
|
||||||
|
return app.exec();
|
||||||
|
}
|
|
@ -0,0 +1,230 @@
|
||||||
|
QtAwesome - Font Awesome support for Qt applications
|
||||||
|
====================================================
|
||||||
|
|
||||||
|
Description
|
||||||
|
-----------
|
||||||
|
|
||||||
|
QtAwesome is a simple library that can be used to add [Font Awesome](http://fortawesome.github.io/Font-Awesome/) icons to your [Qt application](http://qt-project.org/).
|
||||||
|
|
||||||
|
NOTE: Though the name is QtAwesome and currently it's very Font Awesome based, you can use every other icon/glyph font you want.
|
||||||
|
|
||||||
|
The class can also be used to manage your own dynamic code-drawn icons, by adding named icon-painters.
|
||||||
|
|
||||||
|
|
||||||
|
Updated to FontAwesome 4.7.0
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
This library has been updated to Font Awesome version **4.7.0**.
|
||||||
|
|
||||||
|
* In the 4.5.0 version the _linux name has been changed to fa_linux. (Makes the naming of conflicting/invalid names more consistent, like fa_try and fa_500px)
|
||||||
|
* You can find the previous FontAwesome 4 c++11 library in the [c++11 branch](https://github.com/gamecreature/QtAwesome/tree/c++11).
|
||||||
|
* You can find the previous FontAwesome 3 library in the [fontawesome-3 branch](https://github.com/gamecreature/QtAwesome/tree/fontawesome-3).
|
||||||
|
|
||||||
|
|
||||||
|
**Note about previous c++11**
|
||||||
|
|
||||||
|
I removed the C++11 requirement. And moved the c++11 code to a c++11 branch.
|
||||||
|
It's not that I don't like c++11, but the typed enum made the code less flexible then it is now.
|
||||||
|
Just integers it is. Simpler is better.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
|
The easiest way to include QtAweome in your project is to copy the QtAwesome directory to your
|
||||||
|
project tree and add the following `include()` to your Qt project file:
|
||||||
|
|
||||||
|
include(QtAwesome/QtAwesome.pri)
|
||||||
|
|
||||||
|
Now you are good to go!
|
||||||
|
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
You probably want to create a single QtAwesome object for your whole application:
|
||||||
|
|
||||||
|
````
|
||||||
|
QtAwesome* awesome = new QtAwesome( qApp )
|
||||||
|
awesome->initFontAwesome(); // This line is important as it loads the font and initializes the named icon map
|
||||||
|
|
||||||
|
````
|
||||||
|
|
||||||
|
* Add an accessor to this object (i.e. a global function, member of your application object, or whatever you like).
|
||||||
|
* Use an icon name from the [Font Awesome Cheatsheet](http://fortawesome.github.io/Font-Awesome/cheatsheet/).
|
||||||
|
|
||||||
|
|
||||||
|
Example
|
||||||
|
--------
|
||||||
|
|
||||||
|
```c++
|
||||||
|
// You should create a single object of QtAwesome.
|
||||||
|
QtAwesome* awesome = new QtAwesome( qApp );
|
||||||
|
awesome->initFontAwesome();
|
||||||
|
|
||||||
|
// Next create your icon with the help of the icon-enumeration (no dashes):
|
||||||
|
QPushButton* beerButton new QPushButton( awesome->icon( fa::beer ), "Cheers!" );
|
||||||
|
|
||||||
|
// You can also use 'string' names to access the icons. (The string version omits the 'fa-' or 'icon-' prefix and has no dashes )
|
||||||
|
QPushButton* coffeeButton new QPushButton( awesome->icon( "coffee" ), "Black please!" );
|
||||||
|
|
||||||
|
// When you create an icon you can supply some options for your icons:
|
||||||
|
// The available options can be found at the "Default options"-section
|
||||||
|
|
||||||
|
QVariantMap options;
|
||||||
|
options.insert( "color" , QColor(255,0,0) );
|
||||||
|
QPushButton* musicButton = new QPushButton( awesome->icon( fa::music, options ), "Music" );
|
||||||
|
|
||||||
|
// You can also change the default options.
|
||||||
|
// for example if you always would like to have green icons you could call)
|
||||||
|
awesome->setDefaultOption( "color-disabled", QColor(0,255,0) );
|
||||||
|
|
||||||
|
// You can also directly render a label with this font
|
||||||
|
QLabel* label = new QLabel( QChar( fa::group ) );
|
||||||
|
label->setFont( awesome->font(16) );
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Example custom painter
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
This example registers a custom painter for supporting a duplicate icon (it draws 2 "plus marks"):
|
||||||
|
|
||||||
|
```c++
|
||||||
|
class DuplicateIconPainter : public QtAwesomeIconPainter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void paint( QtAwesome* awesome, QPainter* painter, const QRect& rectIn, QIcon::Mode mode, QIcon::State state, const QVariantMap& options )
|
||||||
|
{
|
||||||
|
int drawSize = qRound(rectIn.height()*0.5);
|
||||||
|
int offset = rectIn.height() / 4;
|
||||||
|
QChar chr = QChar( static_cast<int>(fa::plus) );
|
||||||
|
|
||||||
|
painter->setFont( awesome->font( drawSize ) );
|
||||||
|
|
||||||
|
painter->setPen( QColor(100,100,100) );
|
||||||
|
painter->drawText( QRect( QPoint(offset*2, offset*2), QSize(drawSize, drawSize) ), chr , QTextOption( Qt::AlignCenter|Qt::AlignVCenter ) );
|
||||||
|
|
||||||
|
painter->setPen( QColor(50,50,50) );
|
||||||
|
painter->drawText( QRect( QPoint(rectIn.width()-drawSize-offset, rectIn.height()-drawSize-offset), QSize(drawSize, drawSize) ), chr , QTextOption( Qt::AlignCenter|Qt::AlignVCenter ) );
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
awesome->give("duplicate", new DuplicateIconPainter() );
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Default options:
|
||||||
|
----------------
|
||||||
|
|
||||||
|
The following options are default in the QtAwesome class.
|
||||||
|
|
||||||
|
```c++
|
||||||
|
setDefaultOption( "color", QColor(50,50,50) );
|
||||||
|
setDefaultOption( "color-disabled", QColor(70,70,70,60));
|
||||||
|
setDefaultOption( "color-active", QColor(10,10,10));
|
||||||
|
setDefaultOption( "color-selected", QColor(10,10,10));
|
||||||
|
|
||||||
|
setDefaultOption( "text", QString() ); // internal option
|
||||||
|
setDefaultOption( "text-disabled", QString() );
|
||||||
|
setDefaultOption( "text-active", QString() );
|
||||||
|
setDefaultOption( "text-selected", QString() );
|
||||||
|
|
||||||
|
setDefaultOption( "scale-factor", 0.9 );
|
||||||
|
```
|
||||||
|
|
||||||
|
When creating an icon, it first populates the options-map with the default options from the QtAwesome object.
|
||||||
|
After that the options are expanded/overwritten by the options supplied to the icon.
|
||||||
|
|
||||||
|
It is possible to use another glyph per icon-state. For example to make an icon-unlock symbol switch to locked when selected,
|
||||||
|
you could supply the following option:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
options.insert("text-selected", QString( fa::lock ) );
|
||||||
|
```
|
||||||
|
|
||||||
|
Color and text options have the following structure:
|
||||||
|
`keyname-iconmode-iconstate`
|
||||||
|
|
||||||
|
Where iconmode normal is empty
|
||||||
|
And iconstate On is off.
|
||||||
|
|
||||||
|
So the list of items used is:
|
||||||
|
|
||||||
|
- color
|
||||||
|
- color-disabled
|
||||||
|
- color-active
|
||||||
|
- color-selected
|
||||||
|
- color-off
|
||||||
|
- color-disabled-off
|
||||||
|
- color-active-off
|
||||||
|
- color-selected-off
|
||||||
|
- text
|
||||||
|
- text-disabled
|
||||||
|
- text-active
|
||||||
|
- text-selected
|
||||||
|
- text-off
|
||||||
|
- text-disabled-off
|
||||||
|
- text-active-off
|
||||||
|
- text-selected-off
|
||||||
|
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
|
||||||
|
MIT License. Copyright 2013 - Reliable Bits Software by Blommers IT. [http://blommersit.nl/](http://blommersit.nl)
|
||||||
|
|
||||||
|
The Font Awesome font is licensed under the SIL Open Font License - [http://scripts.sil.org/OFL](http://scripts.sil.org/OFL)
|
||||||
|
The Font Awesome pictograms are licensed under the CC BY 3.0 License - [http://creativecommons.org/licenses/by/3.0/](http://creativecommons.org/licenses/by/3.0/)
|
||||||
|
"Font Awesome by Dave Gandy - http://fortawesome.github.com/Font-Awesome"
|
||||||
|
|
||||||
|
Contact
|
||||||
|
-------
|
||||||
|
|
||||||
|
* email: <rick@blommersit.nl>
|
||||||
|
* twitter: [https://twitter.com/gamecreature](https://twitter.com/gamecreature)
|
||||||
|
* website: [http://blommersit.nl](http://blommersit.nl) (warning Dutch content ahead)
|
||||||
|
* github: [https://github.com/gamecreature/QtAwesome](https://github.com/gamecreature/QtAwesome)
|
||||||
|
|
||||||
|
|
||||||
|
Known issues and workarounds
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
On Mac OS X, placing an qtAwesome icon in QMainWindow menu, doesn't work directly.
|
||||||
|
See the following issue: [https://github.com/gamecreature/QtAwesome/issues/10]
|
||||||
|
|
||||||
|
A workaround for this problem is converting it to a Pixmap icon like this:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
QAction* menuAction = new QAction("test");
|
||||||
|
menuAction->setIcon( awesome->icon(fa::beer).pixmap(32,32) );
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Remarks
|
||||||
|
-------
|
||||||
|
|
||||||
|
I've created this project because I needed some nice icons for my own Qt project. After doing a lot of
|
||||||
|
css/html5 work and being spoiled by the ease of twitter bootstrap with Font Awesome,
|
||||||
|
I thought it would be nice to be able to use these icons for my Qt project.
|
||||||
|
|
||||||
|
I've slightly changed the code from the original, added some more documentation, but it's still
|
||||||
|
a work in progress. So feel free to drop me an e-mail for your suggestions and improvements!
|
||||||
|
|
||||||
|
There are still some things todo, like:
|
||||||
|
|
||||||
|
* document the usage of another icon font
|
||||||
|
* add some tests
|
||||||
|
* do some code cleanup
|
||||||
|
|
||||||
|
Thanks go to the contributors of this project!
|
||||||
|
|
||||||
|
And of course last but not least,
|
||||||
|
|
||||||
|
Many thanks go to Dave Gandy an the other Font Awesome contributors!! [http://fortawesome.github.com/Font-Awesome](http://fortawesome.github.com/Font-Awesome)
|
||||||
|
And of course to the Qt team/contributors for supplying this great cross-platform c++ library.
|
||||||
|
|
||||||
|
Contributions are welcome! Feel free to fork and send a pull request through Github.
|
Loading…
Reference in New Issue