Add skeleton snapshot support (Prepare for undo/redo feature)

master
Jeremy Hu 2018-03-26 20:41:46 +08:00
parent 4c9767835f
commit 09d21e8935
1172 changed files with 890 additions and 14267 deletions

View File

@ -1,3 +1,5 @@
WIP...
Build Build
-------- --------
``` ```
@ -7,3 +9,16 @@ $ make && make install
$ qmake -spec macx-xcode $ qmake -spec macx-xcode
``` ```
UI Resources Source
--------
Qt Dark Theme:
- https://gist.github.com/QuantumCD/6245215
Icons:
- https://material.io/icons/
18dp white
3d rotation -> rotate
add circle outline -> add
zoom in -> zoomin
zoom out -> zoomout

View File

@ -1,13 +1,13 @@
convert thirdparty/capicon/png/white/059.png -fuzz 90% -fill 'rgb(252,102,33)' -opaque 'rgb(255,255,255)' resources/add.png cp -f thirdparty/capicon/svg/059.svg resources/add.svg
convert thirdparty/capicon/png/white/262.png -fuzz 90% -fill 'rgb(252,102,33)' -opaque 'rgb(255,255,255)' resources/select.png cp -f thirdparty/capicon/svg/262.svg resources/select.svg
convert thirdparty/capicon/png/white/078.png -fuzz 90% -fill 'rgb(252,102,33)' -opaque 'rgb(255,255,255)' resources/picture.png cp -f thirdparty/capicon/svg/078.svg resources/picture.svg
convert thirdparty/capicon/png/white/226.png -fuzz 90% -fill 'rgb(252,102,33)' -opaque 'rgb(255,255,255)' resources/save.png cp -f thirdparty/capicon/svg/226.svg resources/save.svg
convert thirdparty/capicon/png/white/341.png -fuzz 90% -fill 'rgb(252,102,33)' -opaque 'rgb(255,255,255)' resources/info.png cp -f thirdparty/capicon/svg/341.svg resources/info.svg
convert thirdparty/capicon/png/white/279.png -fuzz 90% -fill 'rgb(252,102,33)' -opaque 'rgb(255,255,255)' resources/up.png cp -f thirdparty/capicon/svg/279.svg resources/up.svg
convert thirdparty/capicon/png/white/278.png -fuzz 90% -fill 'rgb(252,102,33)' -opaque 'rgb(255,255,255)' resources/down.png cp -f thirdparty/capicon/svg/278.svg resources/down.svg
convert thirdparty/capicon/png/white/274.png -fuzz 90% -fill 'rgb(252,102,33)' -opaque 'rgb(255,255,255)' resources/clip.png cp -f thirdparty/capicon/svg/274.svg resources/clip.svg
convert thirdparty/capicon/png/white/254.png -fuzz 90% -fill 'rgb(252,102,33)' -opaque 'rgb(255,255,255)' resources/rangeselect.png cp -f thirdparty/capicon/svg/254.svg resources/rangeselect.svg
convert thirdparty/capicon/png/white/057.png -fuzz 90% -fill 'rgb(252,102,33)' -opaque 'rgb(255,255,255)' resources/delete.png cp -f thirdparty/capicon/svg/057.svg resources/delete.svg
convert thirdparty/capicon/png/white/047.png -fuzz 90% -fill 'rgb(252,102,33)' -opaque 'rgb(255,255,255)' resources/new.png cp -f thirdparty/capicon/svg/047.svg resources/new.svg
convert thirdparty/capicon/png/white/039.png -fuzz 90% -fill 'rgb(252,102,33)' -opaque 'rgb(255,255,255)' resources/palette.png cp -f thirdparty/capicon/svg/039.svg resources/palette.svg
convert thirdparty/capicon/png/white/030.png -fuzz 90% -fill 'rgb(252,102,33)' -opaque 'rgb(255,255,255)' resources/trashbin.png cp -f thirdparty/capicon/svg/030.svg resources/trashbin.svg

View File

@ -28,6 +28,12 @@ HEADERS += src/turnaroundloader.h
SOURCES += src/skeletonwidget.cpp SOURCES += src/skeletonwidget.cpp
HEADERS += src/skeletonwidget.h HEADERS += src/skeletonwidget.h
SOURCES += src/skeletonsnapshot.cpp
HEADERS += src/skeletonsnapshot.h
SOURCES += src/skeletonxml.cpp
HEADERS += src/skeletonxml.h
SOURCES += src/ds3file.cpp SOURCES += src/ds3file.cpp
HEADERS += src/ds3file.h HEADERS += src/ds3file.h

View File

@ -1,17 +1,8 @@
<!DOCTYPE RCC><RCC version="1.0"> <!DOCTYPE RCC><RCC version="1.0">
<qresource> <qresource>
<file>resources/add.png</file> <file>resources/add.svg</file>
<file>resources/select.png</file> <file>resources/rotate.svg</file>
<file>resources/picture.png</file> <file>resources/zoomin.svg</file>
<file>resources/save.png</file> <file>resources/zoomout.svg</file>
<file>resources/info.png</file>
<file>resources/up.png</file>
<file>resources/down.png</file>
<file>resources/clip.png</file>
<file>resources/rangeselect.png</file>
<file>resources/delete.png</file>
<file>resources/new.png</file>
<file>resources/palette.png</file>
<file>resources/trashbin.png</file>
</qresource> </qresource>
</RCC> </RCC>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -33,7 +33,8 @@ int main(int argc, char ** argv)
QFont font; QFont font;
font.setWeight(QFont::Light); font.setWeight(QFont::Light);
font.setPointSize(9); font.setPixelSize(9);
font.setBold(false);
QApplication::setFont(font); QApplication::setFont(font);
MainWindow mainWindow; MainWindow mainWindow;

View File

@ -12,11 +12,14 @@
#include <QBuffer> #include <QBuffer>
#include <QFormLayout> #include <QFormLayout>
#include <QComboBox> #include <QComboBox>
#include <QMenu>
#include <QMenuBar>
#include <assert.h> #include <assert.h>
#include "mainwindow.h" #include "mainwindow.h"
#include "skeletonwidget.h" #include "skeletonwidget.h"
#include "theme.h" #include "theme.h"
#include "ds3file.h" #include "ds3file.h"
#include "skeletonxml.h"
MainWindow::MainWindow() MainWindow::MainWindow()
{ {
@ -24,7 +27,7 @@ MainWindow::MainWindow()
m_sharePageButton = new QPushButton("Share"); m_sharePageButton = new QPushButton("Share");
QWidget *hrWidget = new QWidget; QWidget *hrWidget = new QWidget;
hrWidget->setFixedHeight(2); hrWidget->setFixedHeight(1);
hrWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); hrWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
hrWidget->setStyleSheet(QString("background-color: #fc6621;")); hrWidget->setStyleSheet(QString("background-color: #fc6621;"));
hrWidget->setContentsMargins(0, 0, 0, 0); hrWidget->setContentsMargins(0, 0, 0, 0);
@ -42,8 +45,7 @@ MainWindow::MainWindow()
QHBoxLayout *topButtonsLayout = new QHBoxLayout; QHBoxLayout *topButtonsLayout = new QHBoxLayout;
topButtonsLayout->setContentsMargins(0, 0, 0, 0); topButtonsLayout->setContentsMargins(0, 0, 0, 0);
topButtonsLayout->setSpacing(0); topButtonsLayout->setSpacing(20);
topButtonsLayout->addStretch();
topButtonsLayout->addWidget(m_modelPageButton); topButtonsLayout->addWidget(m_modelPageButton);
topButtonsLayout->addWidget(m_sharePageButton); topButtonsLayout->addWidget(m_sharePageButton);
topButtonsLayout->addStretch(); topButtonsLayout->addStretch();
@ -96,7 +98,7 @@ MainWindow::MainWindow()
QHBoxLayout *modelPageLayout = new QHBoxLayout; QHBoxLayout *modelPageLayout = new QHBoxLayout;
modelPageLayout->addWidget(skeletonWidget); modelPageLayout->addWidget(skeletonWidget);
modelPageLayout->addLayout(modelRightLayout); //modelPageLayout->addLayout(modelRightLayout);
QWidget *modelPageWidget = new QWidget; QWidget *modelPageWidget = new QWidget;
modelPageWidget->setLayout(modelPageLayout); modelPageWidget->setLayout(modelPageLayout);
@ -106,12 +108,14 @@ MainWindow::MainWindow()
QStackedWidget *stackedWidget = new QStackedWidget; QStackedWidget *stackedWidget = new QStackedWidget;
stackedWidget->addWidget(modelPageWidget); stackedWidget->addWidget(modelPageWidget);
stackedWidget->addWidget(sharePageWidget); stackedWidget->addWidget(sharePageWidget);
stackedWidget->setContentsMargins(0, 0, 0, 0);
m_stackedWidget = stackedWidget; m_stackedWidget = stackedWidget;
QVBoxLayout *mainLayout = new QVBoxLayout; QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(topLayout); //mainLayout->addLayout(topLayout);
mainLayout->addWidget(stackedWidget); mainLayout->addWidget(stackedWidget);
mainLayout->setContentsMargins(0, 0, 0, 0);
QWidget *centralWidget = new QWidget; QWidget *centralWidget = new QWidget;
centralWidget->setLayout(mainLayout); centralWidget->setLayout(mainLayout);
@ -119,6 +123,74 @@ MainWindow::MainWindow()
setCentralWidget(centralWidget); setCentralWidget(centralWidget);
setWindowTitle(tr("Dust 3D")); setWindowTitle(tr("Dust 3D"));
QAction *newAct = new QAction(tr("&New"), this);
newAct->setShortcuts(QKeySequence::New);
newAct->setStatusTip(tr("Create a new file"));
connect(newAct, &QAction::triggered, this, &MainWindow::newFile);
QAction *openAct = new QAction(tr("&Open..."), this);
openAct->setShortcuts(QKeySequence::Open);
openAct->setStatusTip(tr("Open an existing file"));
connect(openAct, &QAction::triggered, this, &MainWindow::open);
QAction *saveAct = new QAction(tr("&Save"), this);
saveAct->setShortcuts(QKeySequence::Save);
saveAct->setStatusTip(tr("Save the document to disk"));
connect(saveAct, &QAction::triggered, this, &MainWindow::save);
QAction *exitAct = new QAction(tr("E&xit"), this);
exitAct->setShortcuts(QKeySequence::Quit);
exitAct->setStatusTip(tr("Exit the application"));
connect(exitAct, &QAction::triggered, this, &QWidget::close);
QAction *undoAct = new QAction(tr("&Undo"), this);
undoAct->setShortcuts(QKeySequence::Undo);
undoAct->setStatusTip(tr("Undo the last operation"));
connect(undoAct, &QAction::triggered, this, &MainWindow::undo);
QAction *redoAct = new QAction(tr("&Redo"), this);
redoAct->setShortcuts(QKeySequence::Redo);
redoAct->setStatusTip(tr("Redo the last operation"));
connect(redoAct, &QAction::triggered, this, &MainWindow::redo);
QAction *cutAct = new QAction(tr("Cu&t"), this);
cutAct->setShortcuts(QKeySequence::Cut);
cutAct->setStatusTip(tr("Cut the current selection's contents to the "
"clipboard"));
connect(cutAct, &QAction::triggered, this, &MainWindow::cut);
QAction *copyAct = new QAction(tr("&Copy"), this);
copyAct->setShortcuts(QKeySequence::Copy);
copyAct->setStatusTip(tr("Copy the current selection's contents to the "
"clipboard"));
connect(copyAct, &QAction::triggered, this, &MainWindow::copy);
QAction *pasteAct = new QAction(tr("&Paste"), this);
pasteAct->setShortcuts(QKeySequence::Paste);
pasteAct->setStatusTip(tr("Paste the clipboard's contents into the current "
"selection"));
connect(pasteAct, &QAction::triggered, this, &MainWindow::paste);
QAction *changeTurnaroundAct = new QAction(tr("&Change Turnaround..."), this);
connect(changeTurnaroundAct, &QAction::triggered, skeletonWidget, &SkeletonWidget::changeTurnaround);
QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
fileMenu->addAction(newAct);
fileMenu->addAction(openAct);
fileMenu->addAction(saveAct);
fileMenu->addSeparator();
fileMenu->addAction(exitAct);
QMenu *editMenu = menuBar()->addMenu(tr("&Edit"));
editMenu->addAction(undoAct);
editMenu->addAction(redoAct);
editMenu->addSeparator();
editMenu->addAction(cutAct);
editMenu->addAction(copyAct);
editMenu->addAction(pasteAct);
editMenu->addSeparator();
editMenu->addAction(changeTurnaroundAct);
bool connectResult = false; bool connectResult = false;
connectResult = connect(changeTurnaroundButton, SIGNAL(clicked()), skeletonWidget, SLOT(changeTurnaround())); connectResult = connect(changeTurnaroundButton, SIGNAL(clicked()), skeletonWidget, SLOT(changeTurnaround()));
@ -142,6 +214,46 @@ MainWindow::MainWindow()
updatePageButtons(); updatePageButtons();
} }
void MainWindow::newFile()
{
}
void MainWindow::open()
{
loadModel();
}
void MainWindow::save()
{
saveModel();
}
void MainWindow::undo()
{
}
void MainWindow::redo()
{
}
void MainWindow::cut()
{
}
void MainWindow::copy()
{
}
void MainWindow::paste()
{
}
void MainWindow::exportModel() void MainWindow::exportModel()
{ {
QString exportTo = QFileDialog::getSaveFileName(this, QString exportTo = QFileDialog::getSaveFileName(this,
@ -167,7 +279,9 @@ void MainWindow::loadModel()
QByteArray data; QByteArray data;
ds3Reader.loadItem(item.name, &data); ds3Reader.loadItem(item.name, &data);
QXmlStreamReader xmlReader(data); QXmlStreamReader xmlReader(data);
m_skeletonWidget->graphicsView()->loadFromXmlStream(xmlReader); SkeletonSnapshot snapshot;
loadSkeletonFromXmlStream(&snapshot, xmlReader);
m_skeletonWidget->graphicsView()->loadFromSnapshot(&snapshot);
} else if (item.type == "asset") { } else if (item.type == "asset") {
if (item.name == "canvas.png") { if (item.name == "canvas.png") {
QByteArray data; QByteArray data;
@ -195,7 +309,9 @@ void MainWindow::saveModel()
QByteArray modelXml; QByteArray modelXml;
QXmlStreamWriter stream(&modelXml); QXmlStreamWriter stream(&modelXml);
m_skeletonWidget->graphicsView()->saveToXmlStream(&stream); SkeletonSnapshot snapshot;
m_skeletonWidget->graphicsView()->saveToSnapshot(&snapshot);
saveSkeletonToXmlStream(&snapshot, &stream);
if (modelXml.size() > 0) if (modelXml.size() > 0)
ds3Writer.add("model1.xml", "model", &modelXml); ds3Writer.add("model1.xml", "model", &modelXml);

View File

@ -17,6 +17,14 @@ public slots:
void saveModel(); void saveModel();
void loadModel(); void loadModel();
void exportModel(); void exportModel();
void newFile();
void open();
void save();
void undo();
void redo();
void cut();
void copy();
void paste();
private: private:
QPushButton *m_modelPageButton; QPushButton *m_modelPageButton;
QPushButton *m_sharePageButton; QPushButton *m_sharePageButton;

View File

@ -28,8 +28,13 @@ bool SkeletonEditEdgeItem::checked()
void SkeletonEditEdgeItem::updateAppearance() void SkeletonEditEdgeItem::updateAppearance()
{ {
QPen pen(m_checked ? Theme::skeletonMasterNodeBorderHighlightColor : Theme::skeletonMasterNodeBorderColor); QPen pen;
pen.setWidth(Theme::skeletonMasterNodeBorderSize); if (m_firstNode) {
pen.setColor(m_firstNode->sideColor());
} else if (m_secondNode) {
pen.setColor(m_secondNode->sideColor());
}
pen.setWidth(Theme::skeletonEdgeWidth);
setPen(pen); setPen(pen);
} }
@ -51,6 +56,7 @@ void SkeletonEditEdgeItem::updatePosition()
QLineF line(m_firstNode->origin(), m_secondNode->origin()); QLineF line(m_firstNode->origin(), m_secondNode->origin());
setLine(line); setLine(line);
} }
updateAppearance();
} }
SkeletonEditNodeItem *SkeletonEditEdgeItem::firstNode() SkeletonEditNodeItem *SkeletonEditEdgeItem::firstNode()

View File

@ -9,6 +9,7 @@
#include "skeletoneditgraphicsview.h" #include "skeletoneditgraphicsview.h"
#include "skeletoneditnodeitem.h" #include "skeletoneditnodeitem.h"
#include "skeletoneditedgeitem.h" #include "skeletoneditedgeitem.h"
#include "theme.h"
qreal SkeletonEditGraphicsView::m_initialNodeSize = 128; qreal SkeletonEditGraphicsView::m_initialNodeSize = 128;
qreal SkeletonEditGraphicsView::m_minimalNodeSize = 8; qreal SkeletonEditGraphicsView::m_minimalNodeSize = 8;
@ -22,8 +23,7 @@ SkeletonEditGraphicsView::SkeletonEditGraphicsView(QWidget *parent) :
m_lastHoverNodeItem(NULL), m_lastHoverNodeItem(NULL),
m_lastMousePos(0, 0), m_lastMousePos(0, 0),
m_isMovingNodeItem(false), m_isMovingNodeItem(false),
m_backgroundLoaded(false), m_backgroundLoaded(false)
m_selectedEdgeItem(NULL)
{ {
setScene(new QGraphicsScene()); setScene(new QGraphicsScene());
@ -128,31 +128,13 @@ void SkeletonEditGraphicsView::mousePressEvent(QMouseEvent *event)
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
if (!m_inAddNodeMode) { if (!m_inAddNodeMode) {
if (m_lastHoverNodeItem) { if (m_lastHoverNodeItem) {
setNextStartNodeItem(m_lastHoverNodeItem->master()); setNextStartNodeItem(m_lastHoverNodeItem);
m_lastHoverNodeItem = NULL; m_lastHoverNodeItem = NULL;
} else { } else {
if (m_nextStartNodeItem) { if (m_nextStartNodeItem) {
setNextStartNodeItem(NULL); setNextStartNodeItem(NULL);
} }
} }
SkeletonEditEdgeItem *edgeItem = findEdgeItemByPos(pos);
if (edgeItem) {
if (m_selectedEdgeItem != edgeItem) {
if (m_selectedEdgeItem) {
m_selectedEdgeItem->setChecked(false);
m_selectedEdgeItem = NULL;
}
edgeItem->setChecked(true);
m_selectedEdgeItem = edgeItem;
emit edgeCheckStateChanged();
}
} else {
if (m_selectedEdgeItem) {
m_selectedEdgeItem->setChecked(false);
m_selectedEdgeItem = NULL;
emit edgeCheckStateChanged();
}
}
} }
} }
m_lastMousePos = pos; m_lastMousePos = pos;
@ -165,11 +147,6 @@ void SkeletonEditGraphicsView::mouseDoubleClickEvent(QMouseEvent *event)
emit changeTurnaroundTriggered(); emit changeTurnaroundTriggered();
} }
float SkeletonEditGraphicsView::findXForSlave(float x)
{
return x - m_backgroundItem->boundingRect().width() / 4;
}
void SkeletonEditGraphicsView::removeSelectedItems() void SkeletonEditGraphicsView::removeSelectedItems()
{ {
if (m_nextStartNodeItem) { if (m_nextStartNodeItem) {
@ -182,9 +159,6 @@ void SkeletonEditGraphicsView::removeSelectedItems()
void SkeletonEditGraphicsView::removeNodeItem(SkeletonEditNodeItem *nodeItem) void SkeletonEditGraphicsView::removeNodeItem(SkeletonEditNodeItem *nodeItem)
{ {
if (nodeItem->pair()) {
scene()->removeItem(nodeItem->pair());
}
scene()->removeItem(nodeItem); scene()->removeItem(nodeItem);
QList<QGraphicsItem *>::iterator it; QList<QGraphicsItem *>::iterator it;
QList<QGraphicsItem *> list = scene()->items(); QList<QGraphicsItem *> list = scene()->items();
@ -198,29 +172,19 @@ void SkeletonEditGraphicsView::removeNodeItem(SkeletonEditNodeItem *nodeItem)
} }
} }
void SkeletonEditGraphicsView::removeGroupByNodeItem(SkeletonEditNodeItem *nodeItem) void SkeletonEditGraphicsView::fetchNodeItemAndAllSidePairs(SkeletonEditNodeItem *nodeItem, std::vector<SkeletonEditNodeItem *> *sidePairs)
{ {
if (nodeItem->pair()) { sidePairs->push_back(nodeItem);
scene()->removeItem(nodeItem->pair()); sidePairs->push_back(nodeItem->nextSidePair());
sidePairs->push_back(nodeItem->nextSidePair()->nextSidePair());
} }
scene()->removeItem(nodeItem);
QList<QGraphicsItem *>::iterator it; void SkeletonEditGraphicsView::removeNodeItemAndSidePairs(SkeletonEditNodeItem *nodeItem)
QList<QGraphicsItem *> list = scene()->items(); {
std::vector<SkeletonEditNodeItem *> delayRemoveList; std::vector<SkeletonEditNodeItem *> nodes;
for (it = list.begin(); it != list.end(); ++it) { fetchNodeItemAndAllSidePairs(nodeItem, &nodes);
if ((*it)->data(0).toString() == "edge") { for (size_t i = 0; i < nodes.size(); i++) {
SkeletonEditEdgeItem *edgeItem = static_cast<SkeletonEditEdgeItem *>(*it); removeNodeItem(nodes[i]);
if (edgeItem->firstNode() == nodeItem) {
scene()->removeItem(edgeItem);
delayRemoveList.push_back(edgeItem->secondNode());
} else if (edgeItem->secondNode() == nodeItem) {
scene()->removeItem(edgeItem);
delayRemoveList.push_back(edgeItem->firstNode());
}
}
}
for (size_t i = 0; i < delayRemoveList.size(); i++) {
removeNodeItem(delayRemoveList[i]);
} }
} }
@ -242,6 +206,52 @@ void SkeletonEditGraphicsView::resizeEvent(QResizeEvent *event)
emit sizeChanged(); 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);
}
void SkeletonEditGraphicsView::mouseReleaseEvent(QMouseEvent *event) void SkeletonEditGraphicsView::mouseReleaseEvent(QMouseEvent *event)
{ {
QWidget::mouseReleaseEvent(event); QWidget::mouseReleaseEvent(event);
@ -249,38 +259,16 @@ void SkeletonEditGraphicsView::mouseReleaseEvent(QMouseEvent *event)
return; return;
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
if (m_inAddNodeMode) { if (m_inAddNodeMode) {
if (m_lastHoverNodeItem && m_nextStartNodeItem && m_lastHoverNodeItem != m_nextStartNodeItem) { if (m_lastHoverNodeItem && m_nextStartNodeItem &&
if (!findEdgeItemByNodePair(m_lastHoverNodeItem->master(), m_nextStartNodeItem)) { m_lastHoverNodeItem != m_nextStartNodeItem &&
SkeletonEditEdgeItem *newEdge = new SkeletonEditEdgeItem(); m_lastHoverNodeItem->sideColor() == m_nextStartNodeItem->sideColor()) {
newEdge->setNodes(m_nextStartNodeItem, m_lastHoverNodeItem->master()); if (!findEdgeItemByNodePair(m_lastHoverNodeItem, m_nextStartNodeItem)) {
scene()->addItem(newEdge); addEdgeItem(m_nextStartNodeItem, m_lastHoverNodeItem);
addEdgeItem(m_nextStartNodeItem->nextSidePair(), m_lastHoverNodeItem->nextSidePair());
emit nodesChanged(); emit nodesChanged();
} }
} else { } else {
float newNodeX = m_pendingNodeItem->x(); addNodeItemAndSidePairs(m_pendingNodeItem->rect(), m_nextStartNodeItem);
QRectF newNodeRect = m_pendingNodeItem->rect();
SkeletonEditNodeItem *masterNode = new SkeletonEditNodeItem(newNodeRect);
scene()->addItem(masterNode);
QRectF slaveRect = newNodeRect;
float x = newNodeX + newNodeRect.width() / 2;
slaveRect.translate(findXForSlave(x) - x, 0);
SkeletonEditNodeItem *slaveNode = new SkeletonEditNodeItem(slaveRect);
scene()->addItem(slaveNode);
masterNode->setSlave(slaveNode);
slaveNode->setMaster(masterNode);
if (m_nextStartNodeItem) {
SkeletonEditEdgeItem *newEdge = new SkeletonEditEdgeItem();
newEdge->setNodes(masterNode, m_nextStartNodeItem);
scene()->addItem(newEdge);
masterNode->setGroup(m_nextStartNodeItem->group());
slaveNode->setGroup(masterNode->group());
} else {
QGraphicsItemGroup *group = new QGraphicsItemGroup();
scene()->addItem(group);
masterNode->setGroup(group);
slaveNode->setGroup(masterNode->group());
}
setNextStartNodeItem(masterNode);
emit nodesChanged(); emit nodesChanged();
} }
} }
@ -294,9 +282,9 @@ bool SkeletonEditGraphicsView::canNodeItemMoveTo(SkeletonEditNodeItem *item, QPo
return false; return false;
if (moveTo.y() < 0) if (moveTo.y() < 0)
return false; return false;
if (moveTo.x() + item->rect().width() >= m_backgroundItem->boundingRect().width()) if (moveTo.x() + item->rect().width() >= scene()->sceneRect().width())
return false; return false;
if (moveTo.y() + item->rect().height() >= m_backgroundItem->boundingRect().height()) if (moveTo.y() + item->rect().height() >= scene()->sceneRect().height())
return false; return false;
return true; return true;
} }
@ -312,10 +300,10 @@ void SkeletonEditGraphicsView::mouseMoveEvent(QMouseEvent *event)
moveTo.setX(0); moveTo.setX(0);
if (moveTo.y() < 0) if (moveTo.y() < 0)
moveTo.setY(0); moveTo.setY(0);
if (moveTo.x() + m_pendingNodeItem->rect().width() >= m_backgroundItem->boundingRect().width()) if (moveTo.x() + m_pendingNodeItem->rect().width() >= scene()->sceneRect().width())
moveTo.setX(m_backgroundItem->boundingRect().width() - m_pendingNodeItem->rect().width()); moveTo.setX(scene()->sceneRect().width() - m_pendingNodeItem->rect().width());
if (moveTo.y() + m_pendingNodeItem->rect().height() >= m_backgroundItem->boundingRect().height()) if (moveTo.y() + m_pendingNodeItem->rect().height() >= scene()->sceneRect().height())
moveTo.setY(m_backgroundItem->boundingRect().height() - m_pendingNodeItem->rect().height()); moveTo.setY(scene()->sceneRect().height() - m_pendingNodeItem->rect().height());
QSizeF oldSize = m_pendingNodeItem->rect().size(); QSizeF oldSize = m_pendingNodeItem->rect().size();
m_pendingNodeItem->setRect(moveTo.x(), moveTo.y(), m_pendingNodeItem->setRect(moveTo.x(), moveTo.y(),
oldSize.width(), oldSize.width(),
@ -327,11 +315,11 @@ void SkeletonEditGraphicsView::mouseMoveEvent(QMouseEvent *event)
if (!m_isMovingNodeItem) { if (!m_isMovingNodeItem) {
SkeletonEditNodeItem *hoverNodeItem = findNodeItemByPos(pos); SkeletonEditNodeItem *hoverNodeItem = findNodeItemByPos(pos);
if (hoverNodeItem) { if (hoverNodeItem) {
hoverNodeItem->setHighlighted(true); hoverNodeItem->setHovered(true);
} }
if (hoverNodeItem != m_lastHoverNodeItem) { if (hoverNodeItem != m_lastHoverNodeItem) {
if (m_lastHoverNodeItem) if (m_lastHoverNodeItem)
m_lastHoverNodeItem->setHighlighted(false); m_lastHoverNodeItem->setHovered(false);
m_lastHoverNodeItem = hoverNodeItem; m_lastHoverNodeItem = hoverNodeItem;
} }
} }
@ -341,32 +329,28 @@ void SkeletonEditGraphicsView::mouseMoveEvent(QMouseEvent *event)
(curMousePos != m_lastMousePos || m_isMovingNodeItem)) { (curMousePos != m_lastMousePos || m_isMovingNodeItem)) {
m_isMovingNodeItem = true; m_isMovingNodeItem = true;
QRectF rect = m_lastHoverNodeItem->rect(); QRectF rect = m_lastHoverNodeItem->rect();
QRectF slaveRect; QRectF pairedRect;
if (m_lastHoverNodeItem->isMaster()) {
rect.translate(curMousePos.x() - m_lastMousePos.x(), curMousePos.y() - m_lastMousePos.y()); rect.translate(curMousePos.x() - m_lastMousePos.x(), curMousePos.y() - m_lastMousePos.y());
slaveRect = m_lastHoverNodeItem->slave()->rect(); pairedRect = m_lastHoverNodeItem->nextSidePair()->rect();
slaveRect.translate(0, curMousePos.y() - m_lastMousePos.y()); pairedRect.translate(0, curMousePos.y() - m_lastMousePos.y());
} else {
rect.translate(curMousePos.x() - m_lastMousePos.x(), 0);
}
if (canNodeItemMoveTo(m_lastHoverNodeItem, QPointF(rect.x(), rect.y()))) {
if (m_lastHoverNodeItem->isMaster()) {
m_lastHoverNodeItem->slave()->setRect(slaveRect);
}
m_lastHoverNodeItem->setRect(rect); m_lastHoverNodeItem->setRect(rect);
m_lastHoverNodeItem->nextSidePair()->setRect(pairedRect);
QList<QGraphicsItem *>::iterator it; QList<QGraphicsItem *>::iterator it;
QList<QGraphicsItem *> list = scene()->items(); QList<QGraphicsItem *> list = scene()->items();
for (it = list.begin(); it != list.end(); ++it) { for (it = list.begin(); it != list.end(); ++it) {
if ((*it)->data(0).toString() == "edge") { if ((*it)->data(0).toString() == "edge") {
SkeletonEditEdgeItem *edgeItem = static_cast<SkeletonEditEdgeItem *>(*it); SkeletonEditEdgeItem *edgeItem = static_cast<SkeletonEditEdgeItem *>(*it);
if (edgeItem->connects(m_lastHoverNodeItem)) if (edgeItem->connects(m_lastHoverNodeItem) ||
edgeItem->connects(m_lastHoverNodeItem->nextSidePair()))
edgeItem->updatePosition(); edgeItem->updatePosition();
} }
} }
emit nodesChanged(); emit nodesChanged();
} }
} }
}
m_lastMousePos = curMousePos; m_lastMousePos = curMousePos;
} }
@ -382,9 +366,9 @@ void SkeletonEditGraphicsView::AddItemRadius(QGraphicsEllipseItem *item, float d
} }
QPointF newLeftTop = QPointF(originPt.x() - newSize.width() / 2, QPointF newLeftTop = QPointF(originPt.x() - newSize.width() / 2,
originPt.y() - newSize.height() / 2); originPt.y() - newSize.height() / 2);
if (newLeftTop.x() < 0 || newLeftTop.x() + newSize.width() >= m_backgroundItem->boundingRect().width()) if (newLeftTop.x() < 0 || newLeftTop.x() + newSize.width() >= scene()->sceneRect().width())
return; return;
if (newLeftTop.y() < 0 || newLeftTop.y() + newSize.height() >= m_backgroundItem->boundingRect().height()) if (newLeftTop.y() < 0 || newLeftTop.y() + newSize.height() >= scene()->sceneRect().height())
return; return;
item->setRect(newLeftTop.x(), item->setRect(newLeftTop.x(),
newLeftTop.y(), newLeftTop.y(),
@ -404,9 +388,9 @@ bool SkeletonEditGraphicsView::canAddItemRadius(QGraphicsEllipseItem *item, floa
} }
QPointF newLeftTop = QPointF(originPt.x() - newSize.width() / 2, QPointF newLeftTop = QPointF(originPt.x() - newSize.width() / 2,
originPt.y() - newSize.height() / 2); originPt.y() - newSize.height() / 2);
if (newLeftTop.x() < 0 || newLeftTop.x() + newSize.width() >= m_backgroundItem->boundingRect().width()) if (newLeftTop.x() < 0 || newLeftTop.x() + newSize.width() >= scene()->sceneRect().width())
return false; return false;
if (newLeftTop.y() < 0 || newLeftTop.y() + newSize.height() >= m_backgroundItem->boundingRect().height()) if (newLeftTop.y() < 0 || newLeftTop.y() + newSize.height() >= scene()->sceneRect().height())
return false; return false;
return true; return true;
} }
@ -421,9 +405,9 @@ void SkeletonEditGraphicsView::wheelEvent(QWheelEvent *event)
delta = delta < 0 ? -1.0 : 1.0; delta = delta < 0 ? -1.0 : 1.0;
AddItemRadius(m_pendingNodeItem, delta); AddItemRadius(m_pendingNodeItem, delta);
if (!m_inAddNodeMode && m_lastHoverNodeItem) { if (!m_inAddNodeMode && m_lastHoverNodeItem) {
if (canAddItemRadius(m_lastHoverNodeItem, delta)) { //if (canAddItemRadius(m_lastHoverNodeItem, delta)) {
AddItemRadius(m_lastHoverNodeItem, delta); AddItemRadius(m_lastHoverNodeItem, delta);
} //}
emit nodesChanged(); emit nodesChanged();
} }
} }
@ -432,11 +416,11 @@ void SkeletonEditGraphicsView::setNextStartNodeItem(SkeletonEditNodeItem *item)
{ {
if (m_nextStartNodeItem != item) { if (m_nextStartNodeItem != item) {
if (m_nextStartNodeItem) if (m_nextStartNodeItem)
m_nextStartNodeItem->setIsNextStartNode(false); m_nextStartNodeItem->setChecked(false);
} }
m_nextStartNodeItem = item; m_nextStartNodeItem = item;
if (m_nextStartNodeItem) if (m_nextStartNodeItem)
m_nextStartNodeItem->setIsNextStartNode(true); m_nextStartNodeItem->setChecked(true);
applyAddNodeMode(); applyAddNodeMode();
} }
@ -444,8 +428,8 @@ void SkeletonEditGraphicsView::updateBackgroundImage(const QImage &image)
{ {
QSizeF oldSceneSize = scene()->sceneRect().size(); QSizeF oldSceneSize = scene()->sceneRect().size();
QPixmap pixmap = QPixmap::fromImage(image); QPixmap pixmap = QPixmap::fromImage(image);
m_backgroundItem->setPixmap(pixmap);
scene()->setSceneRect(pixmap.rect()); scene()->setSceneRect(pixmap.rect());
m_backgroundItem->setPixmap(pixmap);
adjustItems(oldSceneSize, scene()->sceneRect().size()); adjustItems(oldSceneSize, scene()->sceneRect().size());
if (!m_backgroundLoaded) { if (!m_backgroundLoaded) {
m_backgroundLoaded = true; m_backgroundLoaded = true;
@ -488,169 +472,99 @@ void SkeletonEditGraphicsView::adjustItems(QSizeF oldSceneSize, QSizeF newSceneS
} }
} }
void SkeletonEditGraphicsView::loadFromXmlStream(QXmlStreamReader &reader) void SkeletonEditGraphicsView::loadFromSnapshot(SkeletonSnapshot *snapshot)
{ {
float radiusMul = 1.0; float radiusMul = 1.0;
float xMul = 1.0; float xMul = 1.0;
float yMul = radiusMul; float yMul = radiusMul;
std::vector<std::map<QString, QString>> pendingNodes; QString canvasWidth = snapshot->canvas["width"];
std::vector<std::map<QString, QString>> pendingEdges; QString canvasHeight = snapshot->canvas["height"];
std::map<QString, SkeletonEditNodeItem *> addedNodeMapById; float canvasWidthVal = canvasWidth.toFloat();
std::map<QString, QGraphicsItemGroup *> addedGroupMapById;
while (!reader.atEnd()) {
reader.readNext();
if (reader.isStartElement()) {
if (reader.name() == "canvas") {
QString canvasWidth = reader.attributes().value("width").toString();
QString canvasHeight = reader.attributes().value("height").toString();
float canvasHeightWidth = canvasWidth.toFloat();
float canvasHeightVal = canvasHeight.toFloat(); float canvasHeightVal = canvasHeight.toFloat();
if (!hasBackgroundImage()) { if (!hasBackgroundImage()) {
QPixmap emptyImage((int)canvasHeightWidth, (int)canvasHeightVal); QPixmap emptyImage((int)canvasWidthVal, (int)canvasHeightVal);
emptyImage.fill(QWidget::palette().color(QWidget::backgroundRole())); emptyImage.fill(QWidget::palette().color(QWidget::backgroundRole()));
updateBackgroundImage(emptyImage.toImage()); updateBackgroundImage(emptyImage.toImage());
} }
if (canvasHeightVal > 0) if (canvasHeightVal > 0)
radiusMul = (float)scene()->sceneRect().height() / canvasHeightVal; radiusMul = (float)scene()->sceneRect().height() / canvasHeightVal;
if (canvasHeightWidth > 0) if (canvasWidthVal > 0)
xMul = (float)scene()->sceneRect().width() / canvasHeightWidth; xMul = (float)scene()->sceneRect().width() / canvasWidthVal;
yMul = radiusMul; yMul = radiusMul;
} else if (reader.name() == "origin") {
if (pendingNodes.size() > 0) { std::map<QString, SkeletonEditNodeItem *> nodeItemMap;
pendingNodes[pendingNodes.size() - 1]["x"] = QString("%1").arg(reader.attributes().value("x").toString().toFloat() * xMul); std::map<QString, std::map<QString, QString>>::iterator nodeIterator;
pendingNodes[pendingNodes.size() - 1]["y"] = QString("%1").arg(reader.attributes().value("y").toString().toFloat() * yMul); for (nodeIterator = snapshot->nodes.begin(); nodeIterator != snapshot->nodes.end(); nodeIterator++) {
} std::map<QString, QString> *snapshotNode = &nodeIterator->second;
} else if (reader.name() == "node") { SkeletonEditNodeItem *nodeItem = addNodeItem((*snapshotNode)["x"].toFloat() * xMul,
QString nodeId = reader.attributes().value("id").toString(); (*snapshotNode)["y"].toFloat() * yMul,
QString nodeGroupId = reader.attributes().value("group").toString(); (*snapshotNode)["radius"].toFloat() * radiusMul);
QString nodeType = reader.attributes().value("type").toString(); nodeItem->setSideColorName((*snapshotNode)["sideColorName"]);
QString nodePairId = reader.attributes().value("pair").toString(); nodeItemMap[nodeIterator->first] = nodeItem;
QString nodeRadius = reader.attributes().value("radius").toString();
std::map<QString, QString> pendingNode;
pendingNode["id"] = nodeId;
pendingNode["group"] = nodeGroupId;
pendingNode["type"] = nodeType;
pendingNode["pair"] = nodePairId;
pendingNode["radius"] = QString("%1").arg(nodeRadius.toFloat() * radiusMul);
pendingNode["x"] = "0";
pendingNode["y"] = "0";
pendingNodes.push_back(pendingNode);
} else if (reader.name() == "edge") {
QString edgeId = reader.attributes().value("id").toString();
QString edgeFromNodeId = reader.attributes().value("from").toString();
QString edgeToNodeId = reader.attributes().value("to").toString();
if (!edgeFromNodeId.isEmpty() && !edgeToNodeId.isEmpty()) {
std::map<QString, QString> pendingEdge;
pendingEdge["id"] = edgeId;
pendingEdge["from"] = edgeFromNodeId;
pendingEdge["to"] = edgeToNodeId;
pendingEdges.push_back(pendingEdge);
}
}
} }
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"]]);
} }
for (size_t i = 0; i < pendingNodes.size(); i++) { std::map<QString, std::map<QString, QString>>::iterator edgeIterator;
std::map<QString, QString> *pendingNode = &pendingNodes[i]; for (edgeIterator = snapshot->edges.begin(); edgeIterator != snapshot->edges.end(); edgeIterator++) {
float radius = (*pendingNode)["radius"].toFloat(); std::map<QString, QString> *snapshotEdge = &edgeIterator->second;
QRectF nodeRect((*pendingNode)["x"].toFloat() - radius, (*pendingNode)["y"].toFloat() - radius, addEdgeItem(nodeItemMap[(*snapshotEdge)["from"]], nodeItemMap[(*snapshotEdge)["to"]]);
radius * 2, radius * 2);
SkeletonEditNodeItem *nodeItem = new SkeletonEditNodeItem(nodeRect);
addedNodeMapById[(*pendingNode)["id"]] = nodeItem;
std::map<QString, QGraphicsItemGroup *>::iterator findGroup = addedGroupMapById.find((*pendingNode)["group"]);
if (findGroup == addedGroupMapById.end()) {
QGraphicsItemGroup *group = new QGraphicsItemGroup;
scene()->addItem(group);
addedGroupMapById[(*pendingNode)["group"]] = group;
}
nodeItem->setGroup(addedGroupMapById[(*pendingNode)["group"]]);
}
for (size_t i = 0; i < pendingNodes.size(); i++) {
std::map<QString, QString> *pendingNode = &pendingNodes[i];
if ((*pendingNode)["type"] == "master") {
addedNodeMapById[(*pendingNode)["id"]]->setSlave(addedNodeMapById[(*pendingNode)["pair"]]);
} else if ((*pendingNode)["type"] == "slave") {
addedNodeMapById[(*pendingNode)["id"]]->setMaster(addedNodeMapById[(*pendingNode)["pair"]]);
}
scene()->addItem(addedNodeMapById[(*pendingNode)["id"]]);
}
for (size_t i = 0; i < pendingEdges.size(); i++) {
std::map<QString, QString> *pendingEdge = &pendingEdges[i];
SkeletonEditEdgeItem *newEdge = new SkeletonEditEdgeItem();
SkeletonEditNodeItem *fromNodeItem = addedNodeMapById[(*pendingEdge)["from"]];
SkeletonEditNodeItem *toNodeItem = addedNodeMapById[(*pendingEdge)["to"]];
newEdge->setNodes(fromNodeItem, toNodeItem);
scene()->addItem(newEdge);
} }
emit nodesChanged(); emit nodesChanged();
} }
void SkeletonEditGraphicsView::saveToXmlStream(QXmlStreamWriter *writer) void SkeletonEditGraphicsView::saveToSnapshot(SkeletonSnapshot *snapshot)
{ {
writer->setAutoFormatting(true); snapshot->canvas["width"] = QString("%1").arg(scene()->sceneRect().width());
writer->writeStartDocument(); snapshot->canvas["height"] = QString("%1").arg(scene()->sceneRect().height());
writer->writeStartElement("canvas");
writer->writeAttribute("width", QString("%1").arg(scene()->sceneRect().width()));
writer->writeAttribute("height", QString("%1").arg(scene()->sceneRect().height()));
QList<QGraphicsItem *>::iterator it; QList<QGraphicsItem *>::iterator it;
QList<QGraphicsItem *> list = scene()->items(); QList<QGraphicsItem *> list = scene()->items();
std::map<SkeletonEditNodeItem *, int> nodeIdMap;
std::map<QGraphicsItemGroup *, int> groupIdMap;
int nextNodeId = 1; int nextNodeId = 1;
int nextGroupId = 1; std::map<SkeletonEditNodeItem *, QString> nodeIdMap;
for (it = list.begin(); it != list.end(); ++it) { for (it = list.begin(); it != list.end(); ++it) {
if ((*it)->data(0).toString() == "node") { if ((*it)->data(0).toString() == "node") {
SkeletonEditNodeItem *nodeItem = static_cast<SkeletonEditNodeItem *>(*it); SkeletonEditNodeItem *nodeItem = static_cast<SkeletonEditNodeItem *>(*it);
nodeIdMap[nodeItem] = nextNodeId; 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());
QPointF origin = nodeItem->origin();
(*snapshotNode)["x"] = QString("%1").arg(origin.x());
(*snapshotNode)["y"] = QString("%1").arg(origin.y());
nodeIdMap[nodeItem] = nodeId;
nextNodeId++; nextNodeId++;
} }
} }
writer->writeStartElement("nodes");
for (it = list.begin(); it != list.end(); ++it) { for (it = list.begin(); it != list.end(); ++it) {
if ((*it)->data(0).toString() == "node") { if ((*it)->data(0).toString() == "node") {
SkeletonEditNodeItem *nodeItem = static_cast<SkeletonEditNodeItem *>(*it); SkeletonEditNodeItem *nodeItem = static_cast<SkeletonEditNodeItem *>(*it);
writer->writeStartElement("node"); QString nodeId = nodeIdMap[nodeItem];
std::map<QGraphicsItemGroup *, int>::iterator findGroup = groupIdMap.find(nodeItem->group()); std::map<QString, QString> *snapshotNode = &snapshot->nodes[nodeId];
if (findGroup == groupIdMap.end()) { (*snapshotNode)["nextSidePair"] = nodeIdMap[nodeItem->nextSidePair()];
groupIdMap[nodeItem->group()] = nextGroupId++;
}
writer->writeAttribute("id", QString("node%1").arg(nodeIdMap[nodeItem]));
writer->writeAttribute("group", QString("group%1").arg(groupIdMap[nodeItem->group()]));
writer->writeAttribute("type", nodeItem->isMaster() ? "master" : "slave");
writer->writeAttribute("pair", QString("node%1").arg(nodeIdMap[nodeItem->pair()]));
writer->writeAttribute("radius", QString("%1").arg(nodeItem->radius()));
writer->writeStartElement("origin");
QPointF origin = nodeItem->origin();
writer->writeAttribute("x", QString("%1").arg(origin.x()));
writer->writeAttribute("y", QString("%1").arg(origin.y()));
writer->writeEndElement();
writer->writeEndElement();
} }
} }
writer->writeEndElement();
writer->writeStartElement("edges");
int nextEdgeId = 1; int nextEdgeId = 1;
for (it = list.begin(); it != list.end(); ++it) { for (it = list.begin(); it != list.end(); ++it) {
if ((*it)->data(0).toString() == "edge") { if ((*it)->data(0).toString() == "edge") {
SkeletonEditEdgeItem *edgeItem = static_cast<SkeletonEditEdgeItem *>(*it); SkeletonEditEdgeItem *edgeItem = static_cast<SkeletonEditEdgeItem *>(*it);
writer->writeStartElement("edge"); QString edgeId = QString("edge%1").arg(nextEdgeId);
writer->writeAttribute("id", QString("edge%1").arg(nextEdgeId)); std::map<QString, QString> *snapshotEdge = &snapshot->edges[edgeId];
writer->writeAttribute("from", QString("node%1").arg(nodeIdMap[edgeItem->firstNode()])); (*snapshotEdge)["id"] = edgeId;
writer->writeAttribute("to", QString("node%1").arg(nodeIdMap[edgeItem->secondNode()])); (*snapshotEdge)["from"] = nodeIdMap[edgeItem->firstNode()];
writer->writeEndElement(); (*snapshotEdge)["to"] = nodeIdMap[edgeItem->secondNode()];
nextEdgeId++; nextEdgeId++;
} }
} }
writer->writeEndElement();
writer->writeEndElement();
writer->writeEndDocument();
} }

View File

@ -7,6 +7,7 @@
#include <QXmlStreamReader> #include <QXmlStreamReader>
#include "skeletoneditnodeitem.h" #include "skeletoneditnodeitem.h"
#include "skeletoneditedgeitem.h" #include "skeletoneditedgeitem.h"
#include "skeletonsnapshot.h"
class SkeletonEditGraphicsView : public QGraphicsView class SkeletonEditGraphicsView : public QGraphicsView
{ {
@ -22,10 +23,10 @@ public slots:
public: public:
SkeletonEditGraphicsView(QWidget *parent = 0); SkeletonEditGraphicsView(QWidget *parent = 0);
void updateBackgroundImage(const QImage &image); void updateBackgroundImage(const QImage &image);
void saveToXmlStream(QXmlStreamWriter *writer);
void loadFromXmlStream(QXmlStreamReader &reader);
bool hasBackgroundImage(); bool hasBackgroundImage();
QPixmap backgroundImage(); QPixmap backgroundImage();
void saveToSnapshot(SkeletonSnapshot *snapshot);
void loadFromSnapshot(SkeletonSnapshot *snapshot);
protected: protected:
void mouseMoveEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event);
void wheelEvent(QWheelEvent *event); void wheelEvent(QWheelEvent *event);
@ -46,7 +47,6 @@ private:
QPointF m_lastMousePos; QPointF m_lastMousePos;
bool m_isMovingNodeItem; bool m_isMovingNodeItem;
bool m_backgroundLoaded; bool m_backgroundLoaded;
SkeletonEditEdgeItem *m_selectedEdgeItem;
private: private:
void toggleAddNodeMode(); void toggleAddNodeMode();
void applyAddNodeMode(); void applyAddNodeMode();
@ -55,14 +55,17 @@ private:
SkeletonEditEdgeItem *findEdgeItemByNodePair(SkeletonEditNodeItem *first, SkeletonEditEdgeItem *findEdgeItemByNodePair(SkeletonEditNodeItem *first,
SkeletonEditNodeItem *second); SkeletonEditNodeItem *second);
void setNextStartNodeItem(SkeletonEditNodeItem *item); void setNextStartNodeItem(SkeletonEditNodeItem *item);
float findXForSlave(float x);
bool canNodeItemMoveTo(SkeletonEditNodeItem *item, QPointF moveTo); bool canNodeItemMoveTo(SkeletonEditNodeItem *item, QPointF moveTo);
void AddItemRadius(QGraphicsEllipseItem *item, float delta); void AddItemRadius(QGraphicsEllipseItem *item, float delta);
bool canAddItemRadius(QGraphicsEllipseItem *item, float delta); bool canAddItemRadius(QGraphicsEllipseItem *item, float delta);
void adjustItems(QSizeF oldSceneSize, QSizeF newSceneSize); void adjustItems(QSizeF oldSceneSize, QSizeF newSceneSize);
void removeSelectedItems(); void removeSelectedItems();
void removeNodeItem(SkeletonEditNodeItem *nodeItem); void removeNodeItem(SkeletonEditNodeItem *nodeItem);
void removeGroupByNodeItem(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 #endif

View File

@ -4,50 +4,69 @@
SkeletonEditNodeItem::SkeletonEditNodeItem(const QRectF &rect, QGraphicsItem *parent) : SkeletonEditNodeItem::SkeletonEditNodeItem(const QRectF &rect, QGraphicsItem *parent) :
QGraphicsEllipseItem(rect, parent), QGraphicsEllipseItem(rect, parent),
m_highlighted(false), m_hovered(false),
m_isNextStartNode(false), m_checked(false),
m_master(NULL), m_nextSidePair(NULL),
m_slave(NULL) m_sideColor(Theme::red),
m_sideColorName("red")
{ {
setData(0, "node"); setData(0, "node");
updateBorder(); updateAppearance();
} }
bool SkeletonEditNodeItem::isSlave() const QString &SkeletonEditNodeItem::sideColorName()
{ {
return NULL != m_master; return m_sideColorName;
} }
bool SkeletonEditNodeItem::isMaster() QString SkeletonEditNodeItem::nextSideColorName()
{ {
return NULL == m_master; return Theme::nextSideColorNameMap[m_sideColorName];
} }
SkeletonEditNodeItem *SkeletonEditNodeItem::master() void SkeletonEditNodeItem::setSideColorName(const QString &name)
{ {
return m_master ? m_master : this; m_sideColorName = name;
m_sideColor = Theme::sideColorNameToColorMap[m_sideColorName];
updateAppearance();
} }
void SkeletonEditNodeItem::setMaster(SkeletonEditNodeItem *nodeItem) bool SkeletonEditNodeItem::hovered()
{ {
m_master = nodeItem; return m_hovered;
updateBorder();
} }
void SkeletonEditNodeItem::setSlave(SkeletonEditNodeItem *nodeItem) void SkeletonEditNodeItem::setHovered(bool hovered)
{ {
m_slave = nodeItem; m_hovered = hovered;
updateBorder(); updateAppearance();
} }
SkeletonEditNodeItem *SkeletonEditNodeItem::pair() bool SkeletonEditNodeItem::checked()
{ {
return m_master ? m_master : m_slave; return m_checked;
} }
SkeletonEditNodeItem *SkeletonEditNodeItem::slave() void SkeletonEditNodeItem::setChecked(bool checked)
{ {
return m_slave; 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() QPointF SkeletonEditNodeItem::origin()
@ -76,31 +95,22 @@ void SkeletonEditNodeItem::setOrigin(QPointF point)
setRect(newRect); setRect(newRect);
} }
void SkeletonEditNodeItem::setHighlighted(bool highlighted) void SkeletonEditNodeItem::updateAppearance()
{ {
m_highlighted = highlighted; QColor penColor = m_sideColor;
if (m_highlighted) { penColor.setAlphaF(m_checked ? Theme::checkedAlpha : Theme::normalAlpha);
setBrush(QBrush(isMaster() ? Theme::skeletonMasterNodeFillColor : Theme::skeletonSlaveNodeFillColor)); QPen pen(penColor);
} else { pen.setWidth(Theme::skeletonNodeBorderSize);
setBrush(QBrush(Qt::transparent));
}
if (m_slave)
{
m_slave->setHighlighted(highlighted);
}
}
void SkeletonEditNodeItem::setIsNextStartNode(bool isNextStartNode)
{
m_isNextStartNode = isNextStartNode;
updateBorder();
}
void SkeletonEditNodeItem::updateBorder()
{
QPen pen(m_isNextStartNode ?
(isMaster() ? Theme::skeletonMasterNodeBorderHighlightColor : Theme::skeletonSlaveNodeBorderHighlightColor) :
(isMaster() ? Theme::skeletonMasterNodeBorderColor : Theme::skeletonSlaveNodeBorderColor));
pen.setWidth(isMaster() ? Theme::skeletonMasterNodeBorderSize : Theme::skeletonSlaveNodeBorderSize);
setPen(pen); 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]];
}

View File

@ -1,6 +1,9 @@
#ifndef SKELETON_EDIT_NODE_ITEM_H #ifndef SKELETON_EDIT_NODE_ITEM_H
#define SKELETON_EDIT_NODE_ITEM_H #define SKELETON_EDIT_NODE_ITEM_H
#include <QGraphicsEllipseItem> #include <QGraphicsEllipseItem>
#include <map>
#include <QString>
#include <QColor>
class SkeletonEditEdgeItem; class SkeletonEditEdgeItem;
@ -9,25 +12,28 @@ class SkeletonEditNodeItem : public QGraphicsEllipseItem
public: public:
SkeletonEditNodeItem(const QRectF &rect, QGraphicsItem *parent = 0); SkeletonEditNodeItem(const QRectF &rect, QGraphicsItem *parent = 0);
QPointF origin(); QPointF origin();
float radius();
void setHighlighted(bool highlited);
void setIsNextStartNode(bool isNextStartNode);
bool isSlave();
bool isMaster();
void setMaster(SkeletonEditNodeItem *nodeItem);
void setSlave(SkeletonEditNodeItem *nodeItem);
SkeletonEditNodeItem *master();
SkeletonEditNodeItem *slave();
SkeletonEditNodeItem *pair();
void setRadius(float radius);
void setOrigin(QPointF point); void setOrigin(QPointF point);
float radius();
void setRadius(float radius);
bool hovered();
void setHovered(bool hovered);
bool checked();
void setChecked(bool checked);
SkeletonEditNodeItem *nextSidePair();
void setNextSidePair(SkeletonEditNodeItem *nodeItem);
const QColor &sideColor();
QColor nextSideColor();
const QString &sideColorName();
QString nextSideColorName();
void setSideColorName(const QString &name);
private: private:
bool m_highlighted; bool m_hovered;
bool m_isNextStartNode; bool m_checked;
SkeletonEditNodeItem *m_master; SkeletonEditNodeItem *m_nextSidePair;
SkeletonEditNodeItem *m_slave; QColor m_sideColor;
QString m_sideColorName;
private: private:
void updateBorder(); void updateAppearance();
}; };
#endif #endif

206
src/skeletonsnapshot.cpp Normal file
View File

@ -0,0 +1,206 @@
#include "skeletonsnapshot.h"
#include <assert.h>
SkeletonSnapshot::SkeletonSnapshot() :
m_boundingBoxResolved(false),
m_rootNodeResolved(false)
{
}
void joinNodeAndNeighborsToGroup(std::map<QString, std::vector<QString>> &nodeLinkMap,
std::map<QString, int> &nodeGroupMap,
const QString &nodeId,
int groupId,
std::vector<QString> &group)
{
group.push_back(nodeId);
nodeGroupMap[nodeId] = groupId;
std::vector<QString> *neighbors = &nodeLinkMap[nodeId];
for (size_t i = 0; i < neighbors->size(); i++) {
QString neighborNodeId = (*neighbors)[i];
if (nodeGroupMap.find(neighborNodeId) != nodeGroupMap.end())
continue;
nodeGroupMap[neighborNodeId] = groupId;
joinNodeAndNeighborsToGroup(nodeLinkMap,
nodeGroupMap,
neighborNodeId,
groupId,
group);
}
}
void SkeletonSnapshot::splitByConnectivity(std::vector<SkeletonSnapshot> *groups)
{
std::map<QString, std::vector<QString>> nodeLinkMap;
std::map<QString, std::map<QString, QString>>::iterator edgeIterator;
for (edgeIterator = edges.begin(); edgeIterator != edges.end(); edgeIterator++) {
nodeLinkMap[edgeIterator->second["from"]].push_back(edgeIterator->second["to"]);
nodeLinkMap[edgeIterator->second["to"]].push_back(edgeIterator->second["from"]);
}
std::map<QString, std::map<QString, QString>>::iterator nodeIterator;
for (nodeIterator = nodes.begin(); nodeIterator != nodes.end(); nodeIterator++) {
nodeLinkMap[nodeIterator->first].push_back(nodeIterator->second["nextSidePair"]);
}
std::map<QString, int> nodeGroupMap;
std::vector<std::vector<QString>> nameGroups;
for (nodeIterator = nodes.begin(); nodeIterator != nodes.end(); nodeIterator++) {
if (nodeGroupMap.find(nodeIterator->first) != nodeGroupMap.end())
continue;
std::vector<QString> nameGroup;
joinNodeAndNeighborsToGroup(nodeLinkMap,
nodeGroupMap,
nodeIterator->first,
nameGroups.size(),
nameGroup);
for (size_t i = 0; i < nameGroup.size(); i++) {
nodeGroupMap[nameGroup[i]] = nameGroups.size();
}
nameGroups.push_back(nameGroup);
}
groups->resize(nameGroups.size());
for (edgeIterator = edges.begin(); edgeIterator != edges.end(); edgeIterator++) {
int groupIndex = nodeGroupMap[edgeIterator->second["from"]];
(*groups)[groupIndex].edges[edgeIterator->first] = edgeIterator->second;
}
for (nodeIterator = nodes.begin(); nodeIterator != nodes.end(); nodeIterator++) {
int groupIndex = nodeGroupMap[nodeIterator->first];
(*groups)[groupIndex].nodes[nodeIterator->first] = nodeIterator->second;
}
for (size_t i = 0; i < groups->size(); i++) {
(*groups)[i].canvas = canvas;
}
}
const QRectF &SkeletonSnapshot::boundingBoxFront()
{
if (!m_boundingBoxResolved)
resolveBoundingBox();
return m_boundingBoxFront;
}
const QRectF &SkeletonSnapshot::boundingBoxSide()
{
if (!m_boundingBoxResolved)
resolveBoundingBox();
return m_boundingBoxSide;
}
void SkeletonSnapshot::resolveBoundingBox()
{
if (m_boundingBoxResolved)
return;
m_boundingBoxResolved = true;
float left = -1;
float right = -1;
float top = -1;
float bottom = -1;
float zLeft = -1;
float zRight = -1;
std::map<QString, std::map<QString, QString>>::iterator nodeIterator;
for (nodeIterator = nodes.begin(); nodeIterator != nodes.end(); nodeIterator++) {
printf("loop node:%s color: %s\n", nodeIterator->first.toUtf8().constData(), nodeIterator->second["sideColorName"].toUtf8().constData());
}
for (nodeIterator = nodes.begin(); nodeIterator != nodes.end(); nodeIterator++) {
if ("red" != nodeIterator->second["sideColorName"])
continue;
float originX = nodeIterator->second["x"].toFloat();
float originY = nodeIterator->second["y"].toFloat();
float originZ = 0;
QString nextSidePairId = nodeIterator->second["nextSidePair"];
printf("nextSidePair: %s\n", nextSidePairId.toUtf8().constData());
std::map<QString, std::map<QString, QString>>::iterator findNextSidePair = nodes.find(nextSidePairId);
if (findNextSidePair != nodes.end()) {
originZ = findNextSidePair->second["x"].toFloat();
}
if (left < 0 || originX < left) {
left = originX;
}
if (top < 0 || originY < top) {
top = originY;
}
if (originX > right) {
right = originX;
}
if (originY > bottom) {
bottom = originY;
}
if (zLeft < 0 || originZ < zLeft) {
zLeft = originZ;
}
if (originZ > zRight) {
zRight = originZ;
}
}
m_boundingBoxFront = QRectF(left, top, right - left, bottom - top);
m_boundingBoxSide = QRectF(zLeft, top, zRight - zLeft, bottom - top);
}
QString SkeletonSnapshot::rootNode()
{
if (!m_rootNodeResolved)
resolveRootNode();
return m_rootNode;
}
void SkeletonSnapshot::setRootNode(const QString &nodeName)
{
assert(!nodeName.isEmpty());
m_rootNode = nodeName;
m_rootNodeResolved = true;
}
QString SkeletonSnapshot::findMaxNeighborNumberNode()
{
std::map<QString, std::map<QString, QString>>::iterator edgeIterator;
std::map<QString, int> nodeNeighborCountMap;
for (edgeIterator = edges.begin(); edgeIterator != edges.end(); edgeIterator++) {
if ("red" != nodes[edgeIterator->second["from"]]["sideColorName"])
continue;
nodeNeighborCountMap[edgeIterator->second["from"]]++;
nodeNeighborCountMap[edgeIterator->second["to"]]++;
}
if (nodeNeighborCountMap.size() == 0)
return "";
auto x = std::max_element(nodeNeighborCountMap.begin(), nodeNeighborCountMap.end(),
[](const std::pair<QString, int>& p1, const std::pair<QString, int>& p2) {
return p1.second < p2.second;
});
return x->first;
}
void SkeletonSnapshot::resolveRootNode()
{
if (m_rootNodeResolved)
return;
m_rootNodeResolved = true;
std::map<QString, std::map<QString, QString>>::iterator edgeIterator;
std::map<QString, int> nodeNeighborCountMap;
for (edgeIterator = edges.begin(); edgeIterator != edges.end(); edgeIterator++) {
if ("red" != nodes[edgeIterator->second["from"]]["sideColorName"])
continue;
nodeNeighborCountMap[edgeIterator->second["from"]]++;
nodeNeighborCountMap[edgeIterator->second["to"]]++;
}
std::map<QString, std::map<QString, QString>>::iterator nodeIterator;
// First try to select the node with more than 2 neighbors and have the largest radius.
float maxRadius = 0;
m_rootNode = "";
for (nodeIterator = nodes.begin(); nodeIterator != nodes.end(); nodeIterator++) {
if ("red" != nodeIterator->second["sideColorName"])
continue;
if (nodeNeighborCountMap[nodeIterator->first] < 3)
continue;
float radius = nodeIterator->second["radius"].toFloat();
if (radius > maxRadius) {
maxRadius = radius;
m_rootNode = nodeIterator->first;
}
}
if (m_rootNode.isEmpty())
m_rootNode = findMaxNeighborNumberNode();
}

34
src/skeletonsnapshot.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef SKELETON_SNAPSHOT_H
#define SKELETON_SNAPSHOT_H
#include <vector>
#include <map>
#include <QString>
#include <QRectF>
#include <QSizeF>
class SkeletonSnapshot
{
public:
std::map<QString, QString> canvas;
std::map<QString, std::map<QString, QString>> nodes;
std::map<QString, std::map<QString, QString>> edges;
public:
SkeletonSnapshot();
void splitByConnectivity(std::vector<SkeletonSnapshot> *groups);
QString rootNode();
void setRootNode(const QString &nodeName);
const QRectF &boundingBoxFront();
const QRectF &boundingBoxSide();
private:
QString m_rootNode;
QRectF m_boundingBoxFront;
QRectF m_boundingBoxSide;
bool m_boundingBoxResolved;
bool m_rootNodeResolved;
private:
void resolveBoundingBox();
void resolveRootNode();
QString findMaxNeighborNumberNode();
};
#endif

View File

@ -2,6 +2,7 @@
#include "meshlite.h" #include "meshlite.h"
#include "skeletoneditnodeitem.h" #include "skeletoneditnodeitem.h"
#include "skeletoneditedgeitem.h" #include "skeletoneditedgeitem.h"
#include "skeletonsnapshot.h"
#include <vector> #include <vector>
#define USE_CARVE 1 #define USE_CARVE 1
@ -202,67 +203,10 @@ struct NodeItemInfo
int neighborCount; int neighborCount;
}; };
SkeletonToMesh::SkeletonToMesh(SkeletonEditGraphicsView *graphicsView) : SkeletonToMesh::SkeletonToMesh(SkeletonSnapshot *snapshot) :
m_mesh(NULL) m_mesh(NULL),
m_snapshot(*snapshot)
{ {
QList<QGraphicsItem *>::iterator it;
QList<QGraphicsItem *> list = graphicsView->scene()->items();
std::map<SkeletonEditNodeItem *, NodeItemInfo> nodeItemsMap;
std::map<QGraphicsItemGroup *, int> groupIdsMap;
int maxNeighborCount = 0;
for (it = list.begin(); it != list.end(); ++it) {
if ((*it)->data(0).toString() == "edge") {
SkeletonEditEdgeItem *edgeItem = static_cast<SkeletonEditEdgeItem *>(*it);
SkeletonEditNodeItem *nodeItems[] = {edgeItem->firstNode(), edgeItem->secondNode()};
int nodeIndices[] = {0, 0};
SkeletonGroup *skeletonGroup = NULL;
for (int i = 0; i < 2; i++) {
SkeletonEditNodeItem *nodeItem = nodeItems[i];
std::map<QGraphicsItemGroup *, int>::iterator findGroupId = groupIdsMap.find(nodeItem->group());
if (findGroupId == groupIdsMap.end()) {
SkeletonGroup group;
group.maxNeighborCount = 0;
group.rootNode = 0;
group.bmeshId = -1;
group.meshId = -1;
groupIdsMap[nodeItem->group()] = m_groups.size();
m_groups.push_back(group);
}
skeletonGroup = &m_groups[groupIdsMap[nodeItem->group()]];
std::map<SkeletonEditNodeItem *, NodeItemInfo>::iterator findNode = nodeItemsMap.find(nodeItem);
if (findNode == nodeItemsMap.end()) {
SkeletonNode node;
NodeItemInfo info;
QPointF origin = nodeItem->origin();
node.originX = origin.x();
node.originY = origin.y();
node.originZ = nodeItem->slave()->origin().x();
node.bmeshNodeId = -1;
node.radius = nodeItem->radius();
node.thickness = nodeItem->slave()->radius();
info.index = skeletonGroup->nodes.size();
info.neighborCount = 1;
nodeIndices[i] = info.index;
nodeItemsMap[nodeItem] = info;
skeletonGroup->nodes.push_back(node);
} else {
nodeIndices[i] = findNode->second.index;
findNode->second.neighborCount++;
if (findNode->second.neighborCount > skeletonGroup->maxNeighborCount) {
skeletonGroup->rootNode = findNode->second.index;
maxNeighborCount = findNode->second.neighborCount;
}
}
}
SkeletonEdge edge;
edge.firstNode = nodeIndices[0];
edge.secondNode = nodeIndices[1];
skeletonGroup->edges.push_back(edge);
}
}
} }
SkeletonToMesh::~SkeletonToMesh() SkeletonToMesh::~SkeletonToMesh()
@ -291,103 +235,64 @@ Mesh *SkeletonToMesh::takeResultMesh()
void SkeletonToMesh::process() void SkeletonToMesh::process()
{ {
if (m_groups.size() <= 0) { std::vector<SkeletonSnapshot> groups;
emit finished(); m_snapshot.splitByConnectivity(&groups);
return;
}
float left = -1;
float right = -1;
float top = -1;
float bottom = -1;
float zLeft = -1;
float zRight = -1;
for (size_t i = 0; i < m_groups.size(); i++) {
SkeletonGroup *group = &m_groups[i];
for (size_t j = 0; j < group->nodes.size(); j++) {
SkeletonNode *node = &group->nodes[j];
if (left < 0 || node->originX < left) {
left = node->originX;
}
if (top < 0 || node->originY < top) {
top = node->originY;
}
if (node->originX > right) {
right = node->originX;
}
if (node->originY > bottom) {
bottom = node->originY;
}
if (zLeft < 0 || node->originZ < zLeft) {
zLeft = node->originZ;
}
if (node->originZ > zRight) {
zRight = node->originZ;
}
}
}
float height = bottom - top;
if (height <= 0) {
emit finished();
return;
}
float zWidth = right - left;
void *context = meshlite_create_context();
for (size_t i = 0; i < m_groups.size(); i++) { std::vector<int> meshIds;
SkeletonGroup *group = &m_groups[i]; void *meshliteContext = meshlite_create_context();
group->bmeshId = meshlite_bmesh_create(context); for (size_t i = 0; i < groups.size(); i++) {
for (size_t j = 0; j < group->nodes.size(); j++) { SkeletonSnapshot *skeleton = &groups[i];
SkeletonNode *node = &group->nodes[j]; QRectF front = skeleton->boundingBoxFront();
float x = (node->originX - left - height / 2) / height; QRectF side = skeleton->boundingBoxSide();
float y = (node->originY - top - height / 2) / height;
float z = (node->originZ - zLeft - zWidth / 2) / height; std::map<QString, int> bmeshNodeMap;
float r = node->radius / height;
float t = node->thickness / height; std::map<QString, std::map<QString, QString>>::iterator nodeIterator;
node->bmeshNodeId = meshlite_bmesh_add_node(context, group->bmeshId, x, y, z, r, t); int bmeshId = meshlite_bmesh_create(meshliteContext);
} for (nodeIterator = skeleton->nodes.begin(); nodeIterator != skeleton->nodes.end(); nodeIterator++) {
for (size_t j = 0; j < group->edges.size(); j++) { if ("red" != nodeIterator->second["sideColorName"])
SkeletonNode *firstNode = &group->nodes[group->edges[j].firstNode]; continue;
SkeletonNode *secondNode = &group->nodes[group->edges[j].secondNode]; std::map<QString, std::map<QString, QString>>::iterator nextSidePair = skeleton->nodes.find(nodeIterator->second["nextSidePair"]);
meshlite_bmesh_add_edge(context, group->bmeshId, firstNode->bmeshNodeId, secondNode->bmeshNodeId); if (nextSidePair == skeleton->nodes.end())
} continue;
group->meshId = meshlite_bmesh_generate_mesh(context, group->bmeshId, group->nodes[group->rootNode].bmeshNodeId); float x = (nodeIterator->second["x"].toFloat() - front.left() - front.height() / 2) / front.height();
float y = (nodeIterator->second["y"].toFloat() - front.top() - front.height() / 2) / front.height();
float z = (nextSidePair->second["x"].toFloat() - side.left() - side.width() / 2) / side.height();
float r = nodeIterator->second["radius"].toFloat() / front.height();
float t = nextSidePair->second["radius"].toFloat() / side.height();
int bmeshNodeId = meshlite_bmesh_add_node(meshliteContext, bmeshId, x, y, z, r, t);
printf("meshlite_bmesh_add_node x:%f y:%f z:%f r:%f t:%f nodeName:%s bmeshNodeId:%d\n", x, y, z, r, t, nodeIterator->first.toUtf8().constData(), bmeshNodeId);
bmeshNodeMap[nodeIterator->first] = bmeshNodeId;
} }
int mergedMeshId = m_groups[0].meshId; std::map<QString, std::map<QString, QString>>::iterator edgeIterator;
for (size_t i = 1; i < m_groups.size(); i++) { for (edgeIterator = skeleton->edges.begin(); edgeIterator != skeleton->edges.end(); edgeIterator++) {
mergedMeshId = meshlite_merge(context, mergedMeshId, m_groups[i].meshId); 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()) {
ExternalMesh *unionPolyhedron = makeExternalMeshFromMeshlite(context, m_groups[0].meshId); int meshId = meshlite_bmesh_generate_mesh(meshliteContext, bmeshId, bmeshNodeMap[skeleton->rootNode()]);
for (size_t i = 1; i < m_groups.size(); i++) { meshIds.push_back(meshId);
ExternalMesh *polyhedron = makeExternalMeshFromMeshlite(context, m_groups[i].meshId);
ExternalMesh *newUnionPolyhedron = unionExternalMeshs(unionPolyhedron, polyhedron);
delete polyhedron;
delete unionPolyhedron;
unionPolyhedron = newUnionPolyhedron;
if (NULL == unionPolyhedron) {
break;
}
}
*/
for (size_t i = 1; i < m_groups.size(); i++) {
meshlite_bmesh_destroy(context, m_groups[i].bmeshId);
} }
/* meshlite_bmesh_destroy(meshliteContext, bmeshId);
if (unionPolyhedron) {
int meshIdGeneratedFromExternal = makeMeshliteMeshFromExternal(context, unionPolyhedron);
delete unionPolyhedron;
meshlite_export(context, meshIdGeneratedFromExternal, "/Users/jeremy/testlib.obj");
m_mesh = new Mesh(context, meshIdGeneratedFromExternal);
} }
*/
m_mesh = new Mesh(context, mergedMeshId); if (meshIds.size() > 0) {
int mergedMeshId = meshIds[0];
for (size_t i = 1; i < meshIds.size(); i++) {
mergedMeshId = meshlite_merge(meshliteContext, mergedMeshId, meshIds[i]);
}
m_mesh = new Mesh(meshliteContext, mergedMeshId);
}
meshlite_destroy_context(context); meshlite_destroy_context(meshliteContext);
emit finished(); emit finished();
} }

View File

@ -6,38 +6,13 @@
#include <map> #include <map>
#include "skeletoneditgraphicsview.h" #include "skeletoneditgraphicsview.h"
#include "mesh.h" #include "mesh.h"
#include "skeletonsnapshot.h"
struct SkeletonNode
{
float originX;
float originY;
float originZ;
float radius;
float thickness;
int bmeshNodeId;
};
struct SkeletonEdge
{
int firstNode;
int secondNode;
};
struct SkeletonGroup
{
std::vector<SkeletonNode> nodes;
std::vector<SkeletonEdge> edges;
int rootNode;
int maxNeighborCount;
int bmeshId;
int meshId;
};
class SkeletonToMesh : public QObject class SkeletonToMesh : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
SkeletonToMesh(SkeletonEditGraphicsView *graphicsView); SkeletonToMesh(SkeletonSnapshot *snapshot);
~SkeletonToMesh(); ~SkeletonToMesh();
Mesh *takeResultMesh(); Mesh *takeResultMesh();
signals: signals:
@ -46,7 +21,7 @@ public slots:
void process(); void process();
private: private:
Mesh *m_mesh; Mesh *m_mesh;
std::vector<SkeletonGroup> m_groups; SkeletonSnapshot m_snapshot;
}; };
#endif #endif

View File

@ -22,13 +22,15 @@ SkeletonWidget::SkeletonWidget(QWidget *parent) :
{ {
QHBoxLayout *topLayout = new QHBoxLayout; QHBoxLayout *topLayout = new QHBoxLayout;
topLayout->addStretch(); topLayout->addStretch();
topLayout->setContentsMargins(0, 0, 0, 0);
m_graphicsView = new SkeletonEditGraphicsView(this); m_graphicsView = new SkeletonEditGraphicsView(this);
m_graphicsView->setRenderHint(QPainter::Antialiasing, false); m_graphicsView->setRenderHint(QPainter::Antialiasing, false);
m_graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_graphicsView->setBackgroundBrush(QBrush(QWidget::palette().color(QWidget::backgroundRole()), Qt::SolidPattern)); 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 = new ModelWidget(this);
m_modelWidget->setMinimumSize(128, 128); m_modelWidget->setMinimumSize(128, 128);
@ -37,41 +39,48 @@ SkeletonWidget::SkeletonWidget(QWidget *parent) :
m_modelWidget->setWindowTitle("3D Model"); m_modelWidget->setWindowTitle("3D Model");
QVBoxLayout *rightLayout = new QVBoxLayout; QVBoxLayout *rightLayout = new QVBoxLayout;
rightLayout->addSpacing(10); rightLayout->addSpacing(0);
rightLayout->addStretch(); rightLayout->addStretch();
rightLayout->setContentsMargins(0, 0, 0, 0);
QToolBar *toolbar = new QToolBar; QToolBar *toolbar = new QToolBar;
toolbar->setIconSize(QSize(16, 16)); toolbar->setIconSize(QSize(18, 18));
toolbar->setOrientation(Qt::Vertical); toolbar->setOrientation(Qt::Vertical);
QAction *addAction = new QAction(tr("Add"), this); QAction *addAction = new QAction(tr("Add"), this);
addAction->setIcon(QIcon(":/resources/add.png")); addAction->setIcon(QPixmap(":/resources/add.svg"));
toolbar->addAction(addAction); toolbar->addAction(addAction);
QAction *selectAction = new QAction(tr("Select"), this); QAction *selectAction = new QAction(tr("Select"), this);
selectAction->setIcon(QIcon(":/resources/select.png")); selectAction->setIcon(QPixmap(":/resources/rotate.svg"));
toolbar->addAction(selectAction); toolbar->addAction(selectAction);
QAction *rangeSelectAction = new QAction(tr("Range Select"), this); QAction *rangeSelectAction = new QAction(tr("Range Select"), this);
rangeSelectAction->setIcon(QIcon(":/resources/rangeselect.png")); rangeSelectAction->setIcon(QPixmap(":/resources/zoomin.svg"));
toolbar->addAction(rangeSelectAction); toolbar->addAction(rangeSelectAction);
toolbar->setContentsMargins(0, 0, 0, 0);
QVBoxLayout *leftLayout = new QVBoxLayout; QVBoxLayout *leftLayout = new QVBoxLayout;
leftLayout->addWidget(toolbar); leftLayout->addWidget(toolbar);
leftLayout->addStretch(); leftLayout->addStretch();
leftLayout->addSpacing(10); leftLayout->addSpacing(0);
leftLayout->setContentsMargins(0, 0, 0, 0);
QHBoxLayout *middleLayout = new QHBoxLayout; QHBoxLayout *middleLayout = new QHBoxLayout;
middleLayout->addLayout(leftLayout); //middleLayout->addLayout(leftLayout);
middleLayout->addWidget(m_graphicsView); middleLayout->addWidget(m_graphicsView);
middleLayout->addLayout(rightLayout); //middleLayout->addLayout(rightLayout);
middleLayout->setContentsMargins(0, 0, 0, 0);
QVBoxLayout *mainLayout = new QVBoxLayout; QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(topLayout); //mainLayout->addLayout(topLayout);
//mainLayout->addSpacing(10); //mainLayout->addSpacing(10);
mainLayout->addLayout(middleLayout); mainLayout->addLayout(middleLayout);
mainLayout->setContentsMargins(0, 0, 0, 0);
setLayout(mainLayout); setLayout(mainLayout);
setContentsMargins(0, 0, 0, 0);
setWindowTitle(tr("Dust 3D")); setWindowTitle(tr("Dust 3D"));
@ -139,7 +148,9 @@ void SkeletonWidget::skeletonChanged()
m_skeletonDirty = false; m_skeletonDirty = false;
QThread *thread = new QThread; QThread *thread = new QThread;
m_skeletonToMesh = new SkeletonToMesh(m_graphicsView); SkeletonSnapshot snapshot;
m_graphicsView->saveToSnapshot(&snapshot);
m_skeletonToMesh = new SkeletonToMesh(&snapshot);
m_skeletonToMesh->moveToThread(thread); m_skeletonToMesh->moveToThread(thread);
connect(thread, SIGNAL(started()), m_skeletonToMesh, SLOT(process())); connect(thread, SIGNAL(started()), m_skeletonToMesh, SLOT(process()));
connect(m_skeletonToMesh, SIGNAL(finished()), this, SLOT(meshReady())); connect(m_skeletonToMesh, SIGNAL(finished()), this, SLOT(meshReady()));

66
src/skeletonxml.cpp Normal file
View File

@ -0,0 +1,66 @@
#include "skeletonxml.h"
void saveSkeletonToXmlStream(SkeletonSnapshot *snapshot, QXmlStreamWriter *writer)
{
writer->setAutoFormatting(true);
writer->writeStartDocument();
writer->writeStartElement("canvas");
std::map<QString, QString>::iterator canvasIterator;
for (canvasIterator = snapshot->canvas.begin(); canvasIterator != snapshot->canvas.end(); canvasIterator++) {
writer->writeAttribute(canvasIterator->first, canvasIterator->second);
}
writer->writeStartElement("nodes");
std::map<QString, std::map<QString, QString>>::iterator nodeIterator;
for (nodeIterator = snapshot->nodes.begin(); nodeIterator != snapshot->nodes.end(); nodeIterator++) {
std::map<QString, QString>::iterator nodeAttributeIterator;
writer->writeStartElement("node");
for (nodeAttributeIterator = nodeIterator->second.begin(); nodeAttributeIterator != nodeIterator->second.end(); nodeAttributeIterator++) {
writer->writeAttribute(nodeAttributeIterator->first, nodeAttributeIterator->second);
}
writer->writeEndElement();
}
writer->writeEndElement();
writer->writeStartElement("edges");
std::map<QString, std::map<QString, QString>>::iterator edgeIterator;
for (edgeIterator = snapshot->edges.begin(); edgeIterator != snapshot->edges.end(); edgeIterator++) {
std::map<QString, QString>::iterator edgeAttributeIterator;
writer->writeStartElement("edge");
for (edgeAttributeIterator = edgeIterator->second.begin(); edgeAttributeIterator != edgeIterator->second.end(); edgeAttributeIterator++) {
writer->writeAttribute(edgeAttributeIterator->first, edgeAttributeIterator->second);
}
writer->writeEndElement();
}
writer->writeEndElement();
writer->writeEndElement();
writer->writeEndDocument();
}
void loadSkeletonFromXmlStream(SkeletonSnapshot *snapshot, QXmlStreamReader &reader)
{
while (!reader.atEnd()) {
reader.readNext();
if (reader.isStartElement()) {
if (reader.name() == "canvas") {
foreach(const QXmlStreamAttribute &attr, reader.attributes()) {
snapshot->canvas[attr.name().toString()] = attr.value().toString();
}
} else if (reader.name() == "node") {
QString nodeId = reader.attributes().value("id").toString();
std::map<QString, QString> *nodeMap = &snapshot->nodes[nodeId];
foreach(const QXmlStreamAttribute &attr, reader.attributes()) {
(*nodeMap)[attr.name().toString()] = attr.value().toString();
}
} else if (reader.name() == "edge") {
QString nodeId = reader.attributes().value("id").toString();
std::map<QString, QString> *edgeMap = &snapshot->edges[nodeId];
foreach(const QXmlStreamAttribute &attr, reader.attributes()) {
(*edgeMap)[attr.name().toString()] = attr.value().toString();
}
}
}
}
}

9
src/skeletonxml.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef SKELETON_XML_H
#define SKELETON_XML_H
#include <QXmlStreamWriter>
#include "skeletonsnapshot.h"
void saveSkeletonToXmlStream(SkeletonSnapshot *snapshot, QXmlStreamWriter *writer);
void loadSkeletonFromXmlStream(SkeletonSnapshot *snapshot, QXmlStreamReader &reader);
#endif

View File

@ -1,17 +1,45 @@
#include "theme.h" #include "theme.h"
// Red
// 0xfc, 0x66, 0x21 // 0xfc, 0x66, 0x21
// 252, 102, 33 // 252, 102, 33
// 0.99, 0.4, 0.13 // 0.99, 0.4, 0.13
QColor Theme::skeletonMasterNodeBorderColor = QColor(0xfc, 0x66, 0x21, 128); // Green
QColor Theme::skeletonMasterNodeBorderHighlightColor = QColor(0xfc, 0x66, 0x21); // 0xaa, 0xeb, 0xc4
QColor Theme::skeletonMasterNodeFillColor = QColor(0xfc, 0x66, 0x21, 50);
int Theme::skeletonMasterNodeBorderSize = 7; // Blue
QColor Theme::skeletonSlaveNodeBorderColor = QColor(0xcc, 0xcc, 0xcc, 64); // 0x2a, 0x5a, 0xac
QColor Theme::skeletonSlaveNodeBorderHighlightColor = QColor(0xcc, 0xcc, 0xcc);
QColor Theme::skeletonSlaveNodeFillColor = QColor(0xcc, 0xcc, 0xcc, 25); // White
int Theme::skeletonSlaveNodeBorderSize = 7; // 0xf7, 0xd9, 0xc8
QColor Theme::red = QColor(0xfc, 0x66, 0x21);
QColor Theme::green = QColor(0xaa, 0xeb, 0xc4);
QColor Theme::blue = QColor(0x2a, 0x5a, 0xac);
float Theme::normalAlpha = 128.0 / 255;
float Theme::checkedAlpha = 1.0;
float Theme::fillAlpha = 50.0 / 255;
int Theme::skeletonNodeBorderSize = 1;
int Theme::skeletonEdgeWidth = 1;
std::map<QString, QString> createSideColorNameMap() {
std::map<QString, QString> map;
map["red"] = "green";
map["green"] = "red";
return map;
}
std::map<QString, QColor> createSideColorNameToColorMap() {
std::map<QString, QColor> map;
map["red"] = Theme::red;
map["green"] = Theme::green;
return map;
}
std::map<QString, QString> Theme::nextSideColorNameMap = createSideColorNameMap();
std::map<QString, QColor> Theme::sideColorNameToColorMap = createSideColorNameToColorMap();
QString Theme::tabButtonSelectedStylesheet = "QPushButton { color: #efefef; background-color: #fc6621; border: 0px; padding-top: 2px; padding-bottom: 2px; padding-left: 25px; padding-right: 25px;}"; QString Theme::tabButtonSelectedStylesheet = "QPushButton { color: #efefef; background-color: #fc6621; border: 0px; padding-top: 2px; padding-bottom: 2px; padding-left: 25px; padding-right: 25px;}";
QString Theme::tabButtonStylesheet = "QPushButton { color: #efefef; background-color: #353535; border: 0px; padding-top: 2px; padding-bottom: 2px; padding-left: 25px; padding-right: 25px;}"; QString Theme::tabButtonStylesheet = "QPushButton { color: #efefef; background-color: #353535; border: 0px; padding-top: 2px; padding-bottom: 2px; padding-left: 25px; padding-right: 25px;}";

View File

@ -2,20 +2,23 @@
#define THEME_H #define THEME_H
#include <QColor> #include <QColor>
#include <QString> #include <QString>
#include <map>
class Theme class Theme
{ {
public: public:
static QColor skeletonMasterNodeBorderColor; static QColor red;
static QColor skeletonMasterNodeBorderHighlightColor; static QColor green;
static QColor skeletonMasterNodeFillColor; static QColor blue;
static int skeletonMasterNodeBorderSize; static float normalAlpha;
static QColor skeletonSlaveNodeBorderColor; static float checkedAlpha;
static QColor skeletonSlaveNodeBorderHighlightColor; static float fillAlpha;
static QColor skeletonSlaveNodeFillColor; static int skeletonNodeBorderSize;
static int skeletonSlaveNodeBorderSize; static int skeletonEdgeWidth;
static QString tabButtonSelectedStylesheet; static QString tabButtonSelectedStylesheet;
static QString tabButtonStylesheet; static QString tabButtonStylesheet;
static std::map<QString, QString> nextSideColorNameMap;
static std::map<QString, QColor> sideColorNameToColorMap;
}; };
#endif #endif

Binary file not shown.

View File

@ -1,150 +0,0 @@
body {
padding: 0;
margin: 0;
font-family: sans-serif;
font-size: 1em;
line-height: 1.5;
color: #555;
background: #fff;
}
h1 {
font-size: 1.5em;
font-weight: normal;
}
small {
font-size: .66666667em;
}
a {
color: #e74c3c;
text-decoration: none;
}
a:hover, a:focus {
box-shadow: 0 1px #e74c3c;
}
.bshadow0, input {
box-shadow: inset 0 -2px #e7e7e7;
}
input:hover {
box-shadow: inset 0 -2px #ccc;
}
input, fieldset {
font-size: 1em;
margin: 0;
padding: 0;
border: 0;
}
input {
color: inherit;
line-height: 1.5;
height: 1.5em;
padding: .25em 0;
}
input:focus {
outline: none;
box-shadow: inset 0 -2px #449fdb;
}
.glyph {
font-size: 16px;
width: 15em;
padding-bottom: 1em;
margin-right: 4em;
margin-bottom: 1em;
float: left;
overflow: hidden;
}
.liga {
width: 80%;
width: calc(100% - 2.5em);
}
.talign-right {
text-align: right;
}
.talign-center {
text-align: center;
}
.bgc1 {
background: #f1f1f1;
}
.fgc1 {
color: #999;
}
.fgc0 {
color: #000;
}
p {
margin-top: 1em;
margin-bottom: 1em;
}
.mvm {
margin-top: .75em;
margin-bottom: .75em;
}
.mtn {
margin-top: 0;
}
.mtl, .mal {
margin-top: 1.5em;
}
.mbl, .mal {
margin-bottom: 1.5em;
}
.mal, .mhl {
margin-left: 1.5em;
margin-right: 1.5em;
}
.mhmm {
margin-left: 1em;
margin-right: 1em;
}
.mls {
margin-left: .25em;
}
.ptl {
padding-top: 1.5em;
}
.pbs, .pvs {
padding-bottom: .25em;
}
.pvs, .pts {
padding-top: .25em;
}
.unit {
float: left;
}
.unitRight {
float: right;
}
.size1of2 {
width: 50%;
}
.size1of1 {
width: 100%;
}
.clearfix:before, .clearfix:after {
content: " ";
display: table;
}
.clearfix:after {
clear: both;
}
.hidden-true {
display: none;
}
.textbox0 {
width: 3em;
background: #f1f1f1;
padding: .25em .5em;
line-height: 1.5;
height: 1.5em;
}
#testDrive {
display: block;
padding-top: 24px;
line-height: 1.5;
}
.fs0 {
font-size: 16px;
}
.fs1 {
font-size: 32px;
}

View File

@ -1,30 +0,0 @@
if (!('boxShadow' in document.body.style)) {
document.body.setAttribute('class', 'noBoxShadow');
}
document.body.addEventListener("click", function(e) {
var target = e.target;
if (target.tagName === "INPUT" &&
target.getAttribute('class').indexOf('liga') === -1) {
target.select();
}
});
(function() {
var fontSize = document.getElementById('fontSize'),
testDrive = document.getElementById('testDrive'),
testText = document.getElementById('testText');
function updateTest() {
testDrive.innerHTML = testText.value || String.fromCharCode(160);
if (window.icomoonLiga) {
window.icomoonLiga(testDrive);
}
}
function updateSize() {
testDrive.style.fontSize = fontSize.value + 'px';
}
fontSize.addEventListener('change', updateSize, false);
testText.addEventListener('input', updateTest, false);
testText.addEventListener('change', updateTest, false);
updateSize();
}());

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 372 KiB

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -1,406 +0,0 @@
/* To avoid CSS expressions while still supporting IE 7 and IE 6, use this script */
/* The script tag referencing this file must be placed before the ending body tag. */
/* Use conditional comments in order to target IE 7 and older:
<!--[if lt IE 8]><!-->
<script src="ie7/ie7.js"></script>
<!--<![endif]-->
*/
(function() {
function addIcon(el, entity) {
var html = el.innerHTML;
el.innerHTML = '<span style="font-family: \'captainicon\'">' + entity + '</span>' + html;
}
var icons = {
'icon-001': '&#xe600;',
'icon-002': '&#xe601;',
'icon-003': '&#xe602;',
'icon-004': '&#xe603;',
'icon-005': '&#xe604;',
'icon-006': '&#xe605;',
'icon-007': '&#xe606;',
'icon-008': '&#xe607;',
'icon-009': '&#xe608;',
'icon-010': '&#xe609;',
'icon-011': '&#xe60a;',
'icon-012': '&#xe60b;',
'icon-013': '&#xe60c;',
'icon-014': '&#xe60d;',
'icon-015': '&#xe60e;',
'icon-016': '&#xe60f;',
'icon-017': '&#xe610;',
'icon-018': '&#xe611;',
'icon-019': '&#xe612;',
'icon-020': '&#xe613;',
'icon-021': '&#xe614;',
'icon-022': '&#xe615;',
'icon-023': '&#xe616;',
'icon-024': '&#xe617;',
'icon-025': '&#xe618;',
'icon-026': '&#xe619;',
'icon-027': '&#xe61a;',
'icon-028': '&#xe61b;',
'icon-029': '&#xe61c;',
'icon-030': '&#xe61d;',
'icon-031': '&#xe61e;',
'icon-032': '&#xe61f;',
'icon-033': '&#xe620;',
'icon-034': '&#xe621;',
'icon-035': '&#xe622;',
'icon-036': '&#xe623;',
'icon-037': '&#xe624;',
'icon-038': '&#xe625;',
'icon-039': '&#xe626;',
'icon-040': '&#xe627;',
'icon-041': '&#xe628;',
'icon-042': '&#xe629;',
'icon-043': '&#xe62a;',
'icon-044': '&#xe62b;',
'icon-045': '&#xe62c;',
'icon-046': '&#xe62d;',
'icon-047': '&#xe62e;',
'icon-048': '&#xe62f;',
'icon-049': '&#xe630;',
'icon-050': '&#xe631;',
'icon-051': '&#xe632;',
'icon-052': '&#xe633;',
'icon-053': '&#xe634;',
'icon-054': '&#xe635;',
'icon-055': '&#xe636;',
'icon-056': '&#xe637;',
'icon-057': '&#xe638;',
'icon-058': '&#xe639;',
'icon-059': '&#xe63a;',
'icon-060': '&#xe63b;',
'icon-061': '&#xe63c;',
'icon-062': '&#xe63d;',
'icon-063': '&#xe63e;',
'icon-064': '&#xe63f;',
'icon-065': '&#xe640;',
'icon-066': '&#xe641;',
'icon-067': '&#xe642;',
'icon-068': '&#xe643;',
'icon-069': '&#xe644;',
'icon-070': '&#xe645;',
'icon-071': '&#xe646;',
'icon-072': '&#xe647;',
'icon-073': '&#xe648;',
'icon-074': '&#xe649;',
'icon-075': '&#xe64a;',
'icon-076': '&#xe64b;',
'icon-077': '&#xe64c;',
'icon-078': '&#xe64d;',
'icon-079': '&#xe64e;',
'icon-080': '&#xe64f;',
'icon-081': '&#xe650;',
'icon-082': '&#xe651;',
'icon-083': '&#xe652;',
'icon-084': '&#xe653;',
'icon-085': '&#xe654;',
'icon-086': '&#xe655;',
'icon-087': '&#xe656;',
'icon-088': '&#xe657;',
'icon-089': '&#xe658;',
'icon-090': '&#xe659;',
'icon-091': '&#xe65a;',
'icon-092': '&#xe65b;',
'icon-093': '&#xe65c;',
'icon-094': '&#xe65d;',
'icon-095': '&#xe65e;',
'icon-096': '&#xe65f;',
'icon-097': '&#xe660;',
'icon-98': '&#xe661;',
'icon-099': '&#xe662;',
'icon-100': '&#xe663;',
'icon-101': '&#xe664;',
'icon-102': '&#xe665;',
'icon-103': '&#xe666;',
'icon-104': '&#xe667;',
'icon-105': '&#xe668;',
'icon-106': '&#xe669;',
'icon-107': '&#xe66a;',
'icon-108': '&#xe66b;',
'icon-109': '&#xe66c;',
'icon-110': '&#xe66d;',
'icon-111': '&#xe66e;',
'icon-112': '&#xe66f;',
'icon-113': '&#xe670;',
'icon-114': '&#xe671;',
'icon-115': '&#xe672;',
'icon-116': '&#xe673;',
'icon-117': '&#xe674;',
'icon-118': '&#xe675;',
'icon-119': '&#xe676;',
'icon-120': '&#xe677;',
'icon-121': '&#xe678;',
'icon-122': '&#xe679;',
'icon-123': '&#xe67a;',
'icon-124': '&#xe67b;',
'icon-125': '&#xe67c;',
'icon-126': '&#xe67d;',
'icon-127': '&#xe67e;',
'icon-128': '&#xe67f;',
'icon-129': '&#xe680;',
'icon-130': '&#xe681;',
'icon-131': '&#xe682;',
'icon-132': '&#xe683;',
'icon-133': '&#xe684;',
'icon-134': '&#xe685;',
'icon-135': '&#xe686;',
'icon-136': '&#xe687;',
'icon-137': '&#xe688;',
'icon-138': '&#xe689;',
'icon-139': '&#xe68a;',
'icon-140': '&#xe68b;',
'icon-141': '&#xe68c;',
'icon-142': '&#xe68d;',
'icon-143': '&#xe68e;',
'icon-144': '&#xe68f;',
'icon-145': '&#xe690;',
'icon-146': '&#xe691;',
'icon-147': '&#xe692;',
'icon-148': '&#xe693;',
'icon-149': '&#xe694;',
'icon-150': '&#xe695;',
'icon-151': '&#xe696;',
'icon-152': '&#xe697;',
'icon-153': '&#xe698;',
'icon-154': '&#xe699;',
'icon-155': '&#xe69a;',
'icon-156': '&#xe69b;',
'icon-157': '&#xe69c;',
'icon-158': '&#xe69d;',
'icon-159': '&#xe69e;',
'icon-160': '&#xe69f;',
'icon-161': '&#xe6a0;',
'icon-162': '&#xe6a1;',
'icon-163': '&#xe6a2;',
'icon-164': '&#xe6a3;',
'icon-165': '&#xe6a4;',
'icon-166': '&#xe6a5;',
'icon-167': '&#xe6a6;',
'icon-168': '&#xe6a7;',
'icon-169': '&#xe6a8;',
'icon-170': '&#xe6a9;',
'icon-171': '&#xe6aa;',
'icon-172': '&#xe6ab;',
'icon-173': '&#xe6ac;',
'icon-174': '&#xe6ad;',
'icon-175': '&#xe6ae;',
'icon-176': '&#xe6af;',
'icon-177': '&#xe6b0;',
'icon-178': '&#xe6b1;',
'icon-179': '&#xe6b2;',
'icon-180': '&#xe6b3;',
'icon-181': '&#xe6b4;',
'icon-182': '&#xe6b5;',
'icon-183': '&#xe6b6;',
'icon-184': '&#xe6b7;',
'icon-185': '&#xe6b8;',
'icon-186': '&#xe6b9;',
'icon-187': '&#xe6ba;',
'icon-188': '&#xe6bb;',
'icon-189': '&#xe6bc;',
'icon-190': '&#xe6bd;',
'icon-191': '&#xe6be;',
'icon-192': '&#xe6bf;',
'icon-193': '&#xe6c0;',
'icon-194': '&#xe6c1;',
'icon-195': '&#xe6c2;',
'icon-196': '&#xe6c3;',
'icon-197': '&#xe6c4;',
'icon-198': '&#xe6c5;',
'icon-199': '&#xe6c6;',
'icon-200': '&#xe6c7;',
'icon-201': '&#xe6c8;',
'icon-202': '&#xe6c9;',
'icon-203': '&#xe6ca;',
'icon-204': '&#xe6cb;',
'icon-205': '&#xe6cc;',
'icon-206': '&#xe6cd;',
'icon-207': '&#xe6ce;',
'icon-208': '&#xe6cf;',
'icon-209': '&#xe6d0;',
'icon-210': '&#xe6d1;',
'icon-211': '&#xe6d2;',
'icon-212': '&#xe6d3;',
'icon-213': '&#xe6d4;',
'icon-214': '&#xe6d5;',
'icon-215': '&#xe6d6;',
'icon-216': '&#xe6d7;',
'icon-217': '&#xe6d8;',
'icon-218': '&#xe6d9;',
'icon-219': '&#xe6da;',
'icon-220': '&#xe6db;',
'icon-221': '&#xe6dc;',
'icon-222': '&#xe6dd;',
'icon-223': '&#xe6de;',
'icon-224': '&#xe6df;',
'icon-225': '&#xe6e0;',
'icon-226': '&#xe6e1;',
'icon-227': '&#xe6e2;',
'icon-228': '&#xe6e3;',
'icon-229': '&#xe6e4;',
'icon-230': '&#xe6e5;',
'icon-231': '&#xe6e6;',
'icon-232': '&#xe6e7;',
'icon-233': '&#xe6e8;',
'icon-234': '&#xe6e9;',
'icon-235': '&#xe6ea;',
'icon-236': '&#xe6eb;',
'icon-237': '&#xe6ec;',
'icon-238': '&#xe6ed;',
'icon-239': '&#xe6ee;',
'icon-240': '&#xe6ef;',
'icon-241': '&#xe6f0;',
'icon-242': '&#xe6f1;',
'icon-243': '&#xe6f2;',
'icon-244': '&#xe6f3;',
'icon-245': '&#xe6f4;',
'icon-246': '&#xe6f5;',
'icon-247': '&#xe6f6;',
'icon-248': '&#xe6f7;',
'icon-249': '&#xe6f8;',
'icon-250': '&#xe6f9;',
'icon-251': '&#xe6fa;',
'icon-252': '&#xe6fb;',
'icon-253': '&#xe6fc;',
'icon-254': '&#xe6fd;',
'icon-255': '&#xe6fe;',
'icon-256': '&#xe6ff;',
'icon-257': '&#xe700;',
'icon-258': '&#xe701;',
'icon-259': '&#xe702;',
'icon-260': '&#xe703;',
'icon-261': '&#xe704;',
'icon-262': '&#xe705;',
'icon-263': '&#xe706;',
'icon-264': '&#xe707;',
'icon-265': '&#xe708;',
'icon-266': '&#xe709;',
'icon-267': '&#xe70a;',
'icon-268': '&#xe70b;',
'icon-269': '&#xe70c;',
'icon-270': '&#xe70d;',
'icon-271': '&#xe70e;',
'icon-272': '&#xe70f;',
'icon-273': '&#xe710;',
'icon-274': '&#xe711;',
'icon-275': '&#xe712;',
'icon-276': '&#xe713;',
'icon-277': '&#xe714;',
'icon-278': '&#xe715;',
'icon-279': '&#xe716;',
'icon-280': '&#xe717;',
'icon-281': '&#xe718;',
'icon-282': '&#xe719;',
'icon-283': '&#xe71a;',
'icon-284': '&#xe71b;',
'icon-285': '&#xe71c;',
'icon-286': '&#xe71d;',
'icon-287': '&#xe71e;',
'icon-288': '&#xe71f;',
'icon-289': '&#xe720;',
'icon-290': '&#xe721;',
'icon-291': '&#xe722;',
'icon-292': '&#xe723;',
'icon-293': '&#xe724;',
'icon-294': '&#xe725;',
'icon-295': '&#xe726;',
'icon-296': '&#xe727;',
'icon-297': '&#xe728;',
'icon-298': '&#xe729;',
'icon-299': '&#xe72a;',
'icon-300': '&#xe72b;',
'icon-301': '&#xe72c;',
'icon-302': '&#xe72d;',
'icon-303': '&#xe72e;',
'icon-304': '&#xe72f;',
'icon-305': '&#xe730;',
'icon-306': '&#xe731;',
'icon-307': '&#xe732;',
'icon-308': '&#xe733;',
'icon-309': '&#xe734;',
'icon-310': '&#xe735;',
'icon-311': '&#xe736;',
'icon-312': '&#xe737;',
'icon-313': '&#xe738;',
'icon-314': '&#xe739;',
'icon-315': '&#xe73a;',
'icon-316': '&#xe73b;',
'icon-317': '&#xe73c;',
'icon-318': '&#xe73d;',
'icon-319': '&#xe73e;',
'icon-320': '&#xe73f;',
'icon-321': '&#xe740;',
'icon-322': '&#xe741;',
'icon-323': '&#xe742;',
'icon-324': '&#xe743;',
'icon-325': '&#xe744;',
'icon-326': '&#xe745;',
'icon-327': '&#xe746;',
'icon-328': '&#xe747;',
'icon-329': '&#xe748;',
'icon-330': '&#xe749;',
'icon-331': '&#xe74a;',
'icon-332': '&#xe74b;',
'icon-333': '&#xe74c;',
'icon-334': '&#xe74d;',
'icon-335': '&#xe74e;',
'icon-336': '&#xe74f;',
'icon-337': '&#xe750;',
'icon-338': '&#xe751;',
'icon-339': '&#xe752;',
'icon-340': '&#xe753;',
'icon-341': '&#xe754;',
'icon-342': '&#xe755;',
'icon-343': '&#xe756;',
'icon-344': '&#xe757;',
'icon-345': '&#xe758;',
'icon-346': '&#xe759;',
'icon-347': '&#xe75a;',
'icon-348': '&#xe75b;',
'icon-349': '&#xe75c;',
'icon-350': '&#xe75d;',
'icon-351': '&#xe75e;',
'icon-352': '&#xe75f;',
'icon-353': '&#xe760;',
'icon-354': '&#xe761;',
'icon-355': '&#xe762;',
'icon-356': '&#xe763;',
'icon-357': '&#xe764;',
'icon-358': '&#xe765;',
'icon-359': '&#xe766;',
'icon-360': '&#xe767;',
'icon-361': '&#xe768;',
'icon-362': '&#xe769;',
'icon-363': '&#xe76a;',
'icon-364': '&#xe76b;',
'icon-365': '&#xe76c;',
'icon-366': '&#xe76d;',
'icon-367': '&#xe76e;',
'icon-368': '&#xe76f;',
'icon-369': '&#xe770;',
'icon-370': '&#xe771;',
'icon-371': '&#xe772;',
'icon-372': '&#xe773;',
'icon-373': '&#xe774;',
'icon-374': '&#xe775;',
'icon-375': '&#xe776;',
'0': 0
},
els = document.getElementsByTagName('*'),
i, c, el;
for (i = 0; ; i += 1) {
el = els[i];
if(!el) {
break;
}
c = el.className;
c = c.match(/icon-[^\s'"]+/);
if (c && icons[c[0]]) {
addIcon(el, icons[c[0]]);
}
}
}());

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Some files were not shown because too many files have changed in this diff Show More