diff --git a/ACKNOWLEDGEMENTS.html b/ACKNOWLEDGEMENTS.html
new file mode 100644
index 00000000..ae461b54
--- /dev/null
+++ b/ACKNOWLEDGEMENTS.html
@@ -0,0 +1,402 @@
+
Acknowledgements
+
+ Portions of this Software(Dust3D) may utilize the following copyrighted material, the use of which is hereby acknowledged.
+
+
+QtAwesome
+
+ MIT License
+ ===========
+
+ Copyright 2013-2015 [Reliable Bits Software by Blommers IT](http://blommersit.nl). All Rights Reserved.
+ Author [Rick Blommers](mailto:rick@blommersit.nl)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
+ documentation files (the "Software"), to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
+ THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+ OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+ Font Awesome License
+ ====================
+
+ [https://github.com/FortAwesome/Font-Awesome](https://github.com/FortAwesome/Font-Awesome)
+
+ The Font Awesome font is licensed under the SIL Open Font License - [http://scripts.sil.org/OFL](http://scripts.sil.org/OFL)
+ The Font Awesome pictograms are licensed under the CC BY 3.0 License - [http://creativecommons.org/licenses/by/3.0/](http://creativecommons.org/licenses/by/3.0/)
+ "Font Awesome by Dave Gandy - http://fortawesome.github.com/Font-Awesome"
+
+
+CGAL
+
+ LICENSE
+ ----------------------------------------------------------------------------
+
+ The CGAL software consists of several parts, each of which is licensed under
+ an open source license. It is also possible to obtain commercial licenses
+ from GeometryFactory (www.geometryfactory.com) for all or parts of CGAL.
+
+ The source code of the CGAL library can be found in the directories
+ "src/CGAL", "src/CGALQt", "src/CGALQt5" and "include/CGAL" (with the
+ exception of "include/CGAL/CORE", "include/CGAL/OpenNL").
+ It is specified in each file of the CGAL library which
+ license applies to it. This is either the GNU General Public License
+ or the GNU Lesser General Public License (as published by the Free Software
+ Foundation; either version 3 of the License or (at your option) any later
+ version). The texts of both licenses can be found in the files LICENSE.GPL
+ and LICENSE.LGPL.
+
+ The following files are modified versions taken from Boost and are licensed
+ under the Boost Software License (see LICENSE.BSL).
+ - include/CGAL/auto_link/auto_link.h
+ - include/CGAL/internal/container_fwd_fixed.hpp
+ - include/CGAL/internal/boost/array_binary_tree.hpp
+ - include/CGAL/internal/boost/mutable_heap.hpp
+ - include/CGAL/internal/boost/mutable_queue.hpp
+
+ Distributed along with CGAL (for the users' convenience), but not part of
+ CGAL, are the following third-party libraries, available under their own
+ licenses:
+
+ - CORE, in the directories "include/CGAL/CORE" and "src/CGAL_Core", is
+ licensed under the LGPL (see LICENSE.LGPL).
+ - ImageIO, in the directory "src/CGAL_ImageIO", is licensed under the LGPL
+ (see LICENSE.LGPL).
+ - OpenNL, in the directory "include/CGAL/OpenNL", is licensed under the LGPL
+ (see LICENSE.LGPL).
+
+ All other files that do not have an explicit copyright notice (e.g., all
+ examples and some demos) are licensed under a very permissive license. The
+ exact license text can be found in the file LICENSE.FREE_USE.
+
+ More information on the CGAL license can be found at
+ https://www.cgal.org/license.html
+
+
+cgmath
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+petgraph
+
+ Copyright (c) 2015
+
+ Permission is hereby granted, free of charge, to any
+ person obtaining a copy of this software and associated
+ documentation files (the "Software"), to deal in the
+ Software without restriction, including without
+ limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of
+ the Software, and to permit persons to whom the Software
+ is furnished to do so, subject to the following
+ conditions:
+
+ The above copyright notice and this permission notice
+ shall be included in all copies or substantial portions
+ of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+ TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+ SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
+
+meshlite
+
+ MIT License
+
+ Copyright (c) 2018 XingYi Hu
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+
+Reza Nourai
+
+ https://blogs.msdn.microsoft.com/rezanour/2011/08/07/barycentric-coordinates-and-point-in-triangle-tests/
+
+
+Cyranose
+
+ https://www.opengl.org/discussion_boards/showthread.php/159385-Deriving-angles-from-0-to-360-from-Dot-Product
+
+
+Dan Sunday
+
+ http://geomalgorithms.com/index.html
+
+
+user3146587
+
+ https://stackoverflow.com/questions/21114796/3d-ray-quad-intersection-test-in-java
+
+
+Qt
+
+ https://www.qt.io/
+
+
+Qt Dark Theme
+
+ https://gist.github.com/QuantumCD/6245215
+
+
+A Browser for QDebug Log Output
+
+ https://wiki.qt.io/Browser_for_QDebug_output
+
+
+Hello GL2 Example
+
+ http://doc.qt.io/qt-5/qtopengl-hellogl2-example.html
+
+
+Jimmy Gunawan
+
+ INSPIRATION / Pixar Monster Factory Part One
+ http://blendersushi.blogspot.com.au/2013/06/inspiration-pixar-monster-factory-part.html
+
+
+Zhongping Ji, Ligang Liu, Yigang Wang
+
+ B-Mesh: A Fast Modeling System for Base Meshes of 3D Articulated Shapes
+ http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.357.7134&rep=rep1&type=pdf
+
+
+danielhst
+
+ https://github.com/danielhst/3d-Hull-gift-wrap/blob/master/giftWrap.lua
+
+
+Catmull–Clark subdivision surface/C
+
+ https://rosettacode.org/wiki/Catmull%E2%80%93Clark_subdivision_surface/C
+
\ No newline at end of file
diff --git a/README.md b/README.md
index f40da77d..121905df 100644
--- a/README.md
+++ b/README.md
@@ -3,22 +3,5 @@ WIP...
Build
--------
```
-$ cd thirdparty/carve-1.4.0/build
-$ cmake ../
-$ make && make install
-
$ 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
diff --git a/dust3d.pro b/dust3d.pro
index c3a00a71..c0be1869 100644
--- a/dust3d.pro
+++ b/dust3d.pro
@@ -2,6 +2,21 @@ QT += core widgets opengl
CONFIG += debug
RESOURCES += resources.qrc
+HUMAN_VERSION = "0.0-alpha1"
+REPOSITORY_URL = "https://github.com/huxingyi/dust3d"
+ISSUES_URL = "https://github.com/huxingyi/dust3d/issues"
+VERSION = 0.0.0.1
+QMAKE_TARGET_COMPANY = Dust3D
+QMAKE_TARGET_PRODUCT = Dust3D
+QMAKE_TARGET_DESCRIPTION = "Aim to be a quick modeling tool for game development"
+QMAKE_TARGET_COPYRIGHT = "Copyright (C) 2018 Dust3D Project. All Rights Reserved."
+
+DEFINES += "APP_NAME=\"\\\"$$QMAKE_TARGET_PRODUCT\\\"\""
+DEFINES += "APP_VER=\"\\\"$$VERSION\\\"\""
+DEFINES += "APP_HUMAN_VER=\"\\\"$$HUMAN_VERSION\\\"\""
+DEFINES += "APP_REPOSITORY_URL=\"\\\"$$REPOSITORY_URL\\\"\""
+DEFINES += "APP_ISSUES_URL=\"\\\"$$ISSUES_URL\\\"\""
+
include(thirdparty/QtAwesome/QtAwesome/QtAwesome.pri)
INCLUDEPATH += src
@@ -30,6 +45,9 @@ HEADERS += src/skeletongraphicswidget.h
SOURCES += src/skeletonpartlistwidget.cpp
HEADERS += src/skeletonpartlistwidget.h
+SOURCES += src/aboutwidget.cpp
+HEADERS += src/aboutwidget.h
+
SOURCES += src/meshgenerator.cpp
HEADERS += src/meshgenerator.h
diff --git a/dust3d_jezzasoft.xcf b/dust3d_jezzasoft.xcf
deleted file mode 100644
index c92d2f58..00000000
Binary files a/dust3d_jezzasoft.xcf and /dev/null differ
diff --git a/resources.qrc b/resources.qrc
index 2438be5a..4c549750 100644
--- a/resources.qrc
+++ b/resources.qrc
@@ -1,5 +1,6 @@
- resources/dust3d_jezzasoft.png
+ resources/dust3d_vertical.png
+ ACKNOWLEDGEMENTS.html
\ No newline at end of file
diff --git a/resources/dust3d_jezzasoft.png b/resources/dust3d_jezzasoft.png
deleted file mode 100644
index 0e5d3fb8..00000000
Binary files a/resources/dust3d_jezzasoft.png and /dev/null differ
diff --git a/resources/dust3d_vertical.png b/resources/dust3d_vertical.png
new file mode 100644
index 00000000..a8f67156
Binary files /dev/null and b/resources/dust3d_vertical.png differ
diff --git a/src/aboutwidget.cpp b/src/aboutwidget.cpp
new file mode 100644
index 00000000..d6138e44
--- /dev/null
+++ b/src/aboutwidget.cpp
@@ -0,0 +1,18 @@
+#include
+#include
+#include "aboutwidget.h"
+
+AboutWidget::AboutWidget()
+{
+ QTextEdit *versionInfoLabel = new QTextEdit;
+ versionInfoLabel->setText(QString("%1 %2 (version: %3 build: %4 %5)").arg(APP_NAME).arg(APP_HUMAN_VER).arg(APP_VER).arg(__DATE__).arg(__TIME__));
+ versionInfoLabel->setReadOnly(true);
+
+ QVBoxLayout *mainLayout = new QVBoxLayout;
+ mainLayout->addWidget(versionInfoLabel);
+
+ setLayout(mainLayout);
+ setFixedSize(QSize(350, 75));
+
+ setWindowTitle(APP_NAME);
+}
diff --git a/src/aboutwidget.h b/src/aboutwidget.h
new file mode 100644
index 00000000..b81c54a6
--- /dev/null
+++ b/src/aboutwidget.h
@@ -0,0 +1,12 @@
+#ifndef ABOUT_WIDGET_H
+#define ABOUT_WIDGET_H
+#include
+
+class AboutWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ AboutWidget();
+};
+
+#endif
diff --git a/src/logbrowser.cpp b/src/logbrowser.cpp
index 431d8ed5..c42efc84 100644
--- a/src/logbrowser.cpp
+++ b/src/logbrowser.cpp
@@ -9,7 +9,6 @@ LogBrowser::LogBrowser(QObject *parent) :
qRegisterMetaType("QtMsgType");
m_browserDialog = new LogBrowserDialog;
connect(this, SIGNAL(sendMessage(QtMsgType,QString)), m_browserDialog, SLOT(outputMessage(QtMsgType,QString)), Qt::QueuedConnection);
- m_browserDialog->show();
}
LogBrowser::~LogBrowser()
@@ -17,6 +16,23 @@ LogBrowser::~LogBrowser()
delete m_browserDialog;
}
+void LogBrowser::showDialog()
+{
+ m_browserDialog->show();
+ m_browserDialog->activateWindow();
+ m_browserDialog->raise();
+}
+
+void LogBrowser::hideDialog()
+{
+ m_browserDialog->hide();
+}
+
+bool LogBrowser::isDialogVisible()
+{
+ return m_browserDialog->isVisible();
+}
+
void LogBrowser::outputMessage(QtMsgType type, const QString &msg)
{
printf("%s\n", msg.toUtf8().constData());
diff --git a/src/logbrowser.h b/src/logbrowser.h
index bc8df370..407a152e 100644
--- a/src/logbrowser.h
+++ b/src/logbrowser.h
@@ -14,6 +14,9 @@ public:
public slots:
void outputMessage(QtMsgType type, const QString &msg);
+ void showDialog();
+ void hideDialog();
+ bool isDialogVisible();
signals:
void sendMessage(QtMsgType type, const QString &msg);
@@ -22,4 +25,4 @@ private:
LogBrowserDialog *m_browserDialog;
};
-#endif // LOGBROWSER_H
\ No newline at end of file
+#endif // LOGBROWSER_H
diff --git a/src/logbrowserdialog.cpp b/src/logbrowserdialog.cpp
index d3958ff2..a0911321 100644
--- a/src/logbrowserdialog.cpp
+++ b/src/logbrowserdialog.cpp
@@ -38,6 +38,8 @@ LogBrowserDialog::LogBrowserDialog(QWidget *parent) :
connect(m_saveButton, SIGNAL(clicked()), this, SLOT(save()));
resize(400, 300);
+
+ hide();
}
@@ -100,15 +102,8 @@ void LogBrowserDialog::save()
void LogBrowserDialog::closeEvent(QCloseEvent *e)
{
- QMessageBox::StandardButton answer = QMessageBox::question(this,
- tr("Close Log Browser?"),
- tr("Do you really want to close the log browser?"),
- QMessageBox::Yes | QMessageBox::No);
-
- if (answer == QMessageBox::Yes)
- e->accept();
- else
- e->ignore();
+ e->ignore();
+ hide();
}
void LogBrowserDialog::keyPressEvent(QKeyEvent *e)
diff --git a/src/main.cpp b/src/main.cpp
index 5606e957..522818ab 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -2,21 +2,11 @@
#include
#include
#include
-#include
#include
#include
-#include "logbrowser.h"
#include "skeletondocumentwindow.h"
#include "theme.h"
-QPointer g_logBrowser;
-
-void outputMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg)
-{
- if (g_logBrowser)
- g_logBrowser->outputMessage(type, msg);
-}
-
int main(int argc, char ** argv)
{
QApplication app(argc, argv);
@@ -49,11 +39,8 @@ int main(int argc, char ** argv)
font.setPixelSize(9);
font.setBold(false);
QApplication::setFont(font);
-
- g_logBrowser = new LogBrowser;
- qInstallMessageHandler(&outputMessage);
- SkeletonDocumentWindow mainWindow;
- mainWindow.showMaximized();
+ SkeletonDocumentWindow::createDocumentWindow();
+
return app.exec();
}
diff --git a/src/modelwidget.cpp b/src/modelwidget.cpp
index acfb0c01..a189b576 100644
--- a/src/modelwidget.cpp
+++ b/src/modelwidget.cpp
@@ -37,6 +37,21 @@ ModelWidget::ModelWidget(QWidget *parent)
setMouseTracking(true);
}
+int ModelWidget::xRot()
+{
+ return m_xRot;
+}
+
+int ModelWidget::yRot()
+{
+ return m_yRot;
+}
+
+int ModelWidget::zRot()
+{
+ return m_zRot;
+}
+
void ModelWidget::setGraphicsFunctions(SkeletonGraphicsFunctions *graphicsFunctions)
{
m_graphicsFunctions = graphicsFunctions;
diff --git a/src/modelwidget.h b/src/modelwidget.h
index 475ce3d5..c73e3120 100644
--- a/src/modelwidget.h
+++ b/src/modelwidget.h
@@ -47,6 +47,12 @@ protected:
void mouseMoveEvent(QMouseEvent *event) override;
void wheelEvent(QWheelEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
+
+public:
+ int xRot();
+ int yRot();
+ int zRot();
+
private:
int m_xRot;
int m_yRot;
diff --git a/src/skeletondocument.cpp b/src/skeletondocument.cpp
index 4fb17bb0..c7844d46 100644
--- a/src/skeletondocument.cpp
+++ b/src/skeletondocument.cpp
@@ -26,6 +26,7 @@ SkeletonDocument::~SkeletonDocument()
void SkeletonDocument::uiReady()
{
+ qDebug() << "uiReady";
emit editModeChanged();
}
@@ -615,24 +616,19 @@ void SkeletonDocument::addFromSnapshot(const SkeletonSnapshot &snapshot)
emit skeletonChanged();
}
-void SkeletonDocument::fromSnapshot(const SkeletonSnapshot &snapshot)
+void SkeletonDocument::reset()
{
- for (const auto &nodeIt: nodeMap) {
- emit nodeRemoved(nodeIt.first);
- }
- for (const auto &edgeIt: edgeMap) {
- emit edgeRemoved(edgeIt.first);
- }
- for (const auto &partIt : partMap) {
- emit partRemoved(partIt.first);
- }
-
nodeMap.clear();
edgeMap.clear();
partMap.clear();
partIds.clear();
- emit partListChanged();
-
+ emit cleanup();
+ emit skeletonChanged();
+}
+
+void SkeletonDocument::fromSnapshot(const SkeletonSnapshot &snapshot)
+{
+ reset();
addFromSnapshot(snapshot);
}
diff --git a/src/skeletondocument.h b/src/skeletondocument.h
index 984b2cf8..4e2e31e0 100644
--- a/src/skeletondocument.h
+++ b/src/skeletondocument.h
@@ -123,6 +123,7 @@ signals:
void partLockStateChanged(QUuid partId);
void partVisibleStateChanged(QUuid partId);
void partSubdivStateChanged(QUuid partId);
+ void cleanup();
public:
SkeletonDocument();
~SkeletonDocument();
@@ -168,6 +169,7 @@ public slots:
void paste();
void batchChangeBegin();
void batchChangeEnd();
+ void reset();
private:
void splitPartByNode(std::vector> *groups, QUuid nodeId);
void joinNodeAndNeiborsToGroup(std::vector *group, QUuid nodeId, std::set *visitMap, QUuid noUseEdgeId=QUuid());
diff --git a/src/skeletondocumentwindow.cpp b/src/skeletondocumentwindow.cpp
index dae69cbc..0022208e 100644
--- a/src/skeletondocumentwindow.cpp
+++ b/src/skeletondocumentwindow.cpp
@@ -6,15 +6,74 @@
#include
#include
#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
#include "skeletondocumentwindow.h"
#include "skeletongraphicswidget.h"
#include "skeletonpartlistwidget.h"
#include "theme.h"
+#include "ds3file.h"
+#include "skeletonsnapshot.h"
+#include "skeletonxml.h"
+#include "logbrowser.h"
+#include "util.h"
+#include "aboutwidget.h"
+
+QPointer g_logBrowser;
+std::set g_documentWindows;
+QTextBrowser *g_acknowlegementsWidget = nullptr;
+AboutWidget *g_aboutWidget = nullptr;
+
+void outputMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg)
+{
+ if (g_logBrowser)
+ g_logBrowser->outputMessage(type, msg);
+}
+
+void SkeletonDocumentWindow::SkeletonDocumentWindow::showAcknowlegements()
+{
+ if (!g_acknowlegementsWidget) {
+ g_acknowlegementsWidget = new QTextBrowser;
+ g_acknowlegementsWidget->setWindowTitle(APP_NAME);
+ g_acknowlegementsWidget->setMinimumSize(QSize(400, 300));
+ QFile file(":/ACKNOWLEDGEMENTS.html");
+ file.open(QFile::ReadOnly | QFile::Text);
+ QTextStream stream(&file);
+ g_acknowlegementsWidget->setHtml(stream.readAll());
+ }
+ g_acknowlegementsWidget->show();
+ g_acknowlegementsWidget->activateWindow();
+ g_acknowlegementsWidget->raise();
+}
+
+void SkeletonDocumentWindow::SkeletonDocumentWindow::showAbout()
+{
+ if (!g_aboutWidget) {
+ g_aboutWidget = new AboutWidget;
+ }
+ g_aboutWidget->show();
+ g_aboutWidget->activateWindow();
+ g_aboutWidget->raise();
+}
SkeletonDocumentWindow::SkeletonDocumentWindow() :
m_document(nullptr),
- m_firstShow(true)
+ m_firstShow(true),
+ m_documentSaved(true)
{
+ if (!g_logBrowser) {
+ g_logBrowser = new LogBrowser;
+ qInstallMessageHandler(&outputMessage);
+ }
+
+ g_documentWindows.insert(this);
+
m_document = new SkeletonDocument;
QVBoxLayout *toolButtonLayout = new QVBoxLayout;
@@ -47,13 +106,13 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() :
toolButtonLayout->addWidget(zoomInButton);
toolButtonLayout->addWidget(zoomOutButton);
- QLabel *dust3dJezzasoftLabel = new QLabel;
- QImage dust3dJezzasoftImage;
- dust3dJezzasoftImage.load(":/resources/dust3d_jezzasoft.png");
- dust3dJezzasoftLabel->setPixmap(QPixmap::fromImage(dust3dJezzasoftImage));
+ QLabel *verticalLogoLabel = new QLabel;
+ QImage verticalLogoImage;
+ verticalLogoImage.load(":/resources/dust3d_vertical.png");
+ verticalLogoLabel->setPixmap(QPixmap::fromImage(verticalLogoImage));
QHBoxLayout *logoLayout = new QHBoxLayout;
- logoLayout->addWidget(dust3dJezzasoftLabel);
+ logoLayout->addWidget(verticalLogoLabel);
logoLayout->setContentsMargins(0, 0, 0, 0);
QVBoxLayout *mainLeftLayout = new QVBoxLayout;
@@ -65,9 +124,9 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() :
mainLeftLayout->addSpacing(10);
SkeletonGraphicsWidget *graphicsWidget = new SkeletonGraphicsWidget(m_document);
+ m_graphicsWidget = graphicsWidget;
SkeletonGraphicsContainerWidget *containerWidget = new SkeletonGraphicsContainerWidget;
-
containerWidget->setGraphicsWidget(graphicsWidget);
QGridLayout *containerLayout = new QGridLayout;
containerLayout->setSpacing(0);
@@ -103,6 +162,147 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() :
setCentralWidget(centralWidget);
setWindowTitle(tr("Dust3D"));
+ m_fileMenu = menuBar()->addMenu(tr("File"));
+
+ m_newWindowAction = new QAction(tr("New Window"), this);
+ connect(m_newWindowAction, &QAction::triggered, this, &SkeletonDocumentWindow::newWindow);
+ m_fileMenu->addAction(m_newWindowAction);
+
+ m_newDocumentAction = new QAction(tr("New"), this);
+ connect(m_newDocumentAction, &QAction::triggered, this, &SkeletonDocumentWindow::newDocument);
+ m_fileMenu->addAction(m_newDocumentAction);
+
+ m_openAction = new QAction(tr("Open..."), this);
+ connect(m_openAction, &QAction::triggered, this, &SkeletonDocumentWindow::open);
+ m_fileMenu->addAction(m_openAction);
+
+ m_saveAction = new QAction(tr("Save"), this);
+ connect(m_saveAction, &QAction::triggered, this, &SkeletonDocumentWindow::save);
+ m_fileMenu->addAction(m_saveAction);
+
+ m_saveAsAction = new QAction(tr("Save As..."), this);
+ connect(m_saveAsAction, &QAction::triggered, this, &SkeletonDocumentWindow::saveAs);
+ m_fileMenu->addAction(m_saveAsAction);
+
+ m_saveAllAction = new QAction(tr("Save All"), this);
+ connect(m_saveAllAction, &QAction::triggered, this, &SkeletonDocumentWindow::saveAll);
+ m_fileMenu->addAction(m_saveAllAction);
+
+ m_fileMenu->addSeparator();
+
+ m_exportAction = new QAction(tr("Export..."), this);
+ connect(m_exportAction, &QAction::triggered, this, &SkeletonDocumentWindow::exportResult);
+ m_fileMenu->addAction(m_exportAction);
+
+ m_changeTurnaroundAction = new QAction(tr("Change Turnaround..."), this);
+ connect(m_changeTurnaroundAction, &QAction::triggered, this, &SkeletonDocumentWindow::changeTurnaround);
+ m_fileMenu->addAction(m_changeTurnaroundAction);
+
+ connect(m_fileMenu, &QMenu::aboutToShow, [=]() {
+ m_exportAction->setEnabled(m_graphicsWidget->hasItems());
+ });
+
+ m_editMenu = menuBar()->addMenu(tr("Edit"));
+
+ m_addAction = new QAction(tr("Add..."), this);
+ connect(m_addAction, &QAction::triggered, [=]() {
+ m_document->setEditMode(SkeletonDocumentEditMode::Add);
+ });
+ m_editMenu->addAction(m_addAction);
+
+ m_undoAction = new QAction(tr("Undo"), this);
+ connect(m_undoAction, &QAction::triggered, m_document, &SkeletonDocument::undo);
+ m_editMenu->addAction(m_undoAction);
+
+ m_redoAction = new QAction(tr("Redo"), this);
+ connect(m_redoAction, &QAction::triggered, m_document, &SkeletonDocument::redo);
+ m_editMenu->addAction(m_redoAction);
+
+ m_deleteAction = new QAction(tr("Delete"), this);
+ connect(m_deleteAction, &QAction::triggered, m_graphicsWidget, &SkeletonGraphicsWidget::deleteSelected);
+ m_editMenu->addAction(m_deleteAction);
+
+ m_cutAction = new QAction(tr("Cut"), this);
+ connect(m_cutAction, &QAction::triggered, m_graphicsWidget, &SkeletonGraphicsWidget::cut);
+ m_editMenu->addAction(m_cutAction);
+
+ m_copyAction = new QAction(tr("Copy"), this);
+ connect(m_copyAction, &QAction::triggered, m_graphicsWidget, &SkeletonGraphicsWidget::copy);
+ m_editMenu->addAction(m_copyAction);
+
+ m_pasteAction = new QAction(tr("Paste"), this);
+ connect(m_pasteAction, &QAction::triggered, m_document, &SkeletonDocument::paste);
+ m_editMenu->addAction(m_pasteAction);
+
+ m_flipHorizontallyAction = new QAction(tr("H Flip"), this);
+ connect(m_flipHorizontallyAction, &QAction::triggered, m_graphicsWidget, &SkeletonGraphicsWidget::flipHorizontally);
+ m_editMenu->addAction(m_flipHorizontallyAction);
+
+ m_flipVerticallyAction = new QAction(tr("V Flip"), this);
+ connect(m_flipVerticallyAction, &QAction::triggered, m_graphicsWidget, &SkeletonGraphicsWidget::flipVertically);
+ m_editMenu->addAction(m_flipVerticallyAction);
+
+ m_selectAllAction = new QAction(tr("Select All"), this);
+ connect(m_selectAllAction, &QAction::triggered, m_graphicsWidget, &SkeletonGraphicsWidget::selectAll);
+ m_editMenu->addAction(m_selectAllAction);
+
+ m_selectPartAllAction = new QAction(tr("Select Part"), this);
+ connect(m_selectPartAllAction, &QAction::triggered, m_graphicsWidget, &SkeletonGraphicsWidget::selectPartAll);
+ m_editMenu->addAction(m_selectPartAllAction);
+
+ m_unselectAllAction = new QAction(tr("Unselect All"), this);
+ connect(m_unselectAllAction, &QAction::triggered, m_graphicsWidget, &SkeletonGraphicsWidget::unselectAll);
+ m_editMenu->addAction(m_unselectAllAction);
+
+ connect(m_editMenu, &QMenu::aboutToShow, [=]() {
+ m_undoAction->setEnabled(m_document->undoable());
+ m_redoAction->setEnabled(m_document->redoable());
+ m_deleteAction->setEnabled(m_graphicsWidget->hasSelection());
+ m_cutAction->setEnabled(m_graphicsWidget->hasSelection());
+ m_copyAction->setEnabled(m_graphicsWidget->hasSelection());
+ m_pasteAction->setEnabled(m_document->hasPastableContentInClipboard());
+ m_flipHorizontallyAction->setEnabled(m_graphicsWidget->hasMultipleSelection());
+ m_flipVerticallyAction->setEnabled(m_graphicsWidget->hasMultipleSelection());
+ m_selectAllAction->setEnabled(m_graphicsWidget->hasItems());
+ m_selectPartAllAction->setEnabled(m_graphicsWidget->hasItems());
+ m_unselectAllAction->setEnabled(m_graphicsWidget->hasSelection());
+ });
+
+ m_viewMenu = menuBar()->addMenu(tr("View"));
+
+ m_resetModelWidgetPosAction = new QAction(tr("Show Model"), this);
+ connect(m_resetModelWidgetPosAction, &QAction::triggered, [=]() {
+ QRect parentRect = QRect(QPoint(0, 0), m_modelWidget->parentWidget()->size());
+ if (!parentRect.contains(m_modelWidget->geometry().center())) {
+ m_modelWidget->move(16, 16);
+ }
+ });
+ m_viewMenu->addAction(m_resetModelWidgetPosAction);
+
+ m_showDebugDialogAction = new QAction(tr("Show Debug Dialog"), this);
+ connect(m_showDebugDialogAction, &QAction::triggered, g_logBrowser, &LogBrowser::showDialog);
+ m_viewMenu->addAction(m_showDebugDialogAction);
+
+ m_helpMenu = menuBar()->addMenu(tr("Help"));
+
+ m_viewSourceAction = new QAction(tr("Fork me on GitHub"), this);
+ connect(m_viewSourceAction, &QAction::triggered, this, &SkeletonDocumentWindow::viewSource);
+ m_helpMenu->addAction(m_viewSourceAction);
+
+ m_helpMenu->addSeparator();
+
+ m_aboutAction = new QAction(tr("About"), this);
+ connect(m_aboutAction, &QAction::triggered, this, &SkeletonDocumentWindow::about);
+ m_helpMenu->addAction(m_aboutAction);
+
+ m_reportIssuesAction = new QAction(tr("Report Issues"), this);
+ connect(m_reportIssuesAction, &QAction::triggered, this, &SkeletonDocumentWindow::reportIssues);
+ m_helpMenu->addAction(m_reportIssuesAction);
+
+ m_seeAcknowlegementsAction = new QAction(tr("Acknowlegements"), this);
+ connect(m_seeAcknowlegementsAction, &QAction::triggered, this, &SkeletonDocumentWindow::seeAcknowlegements);
+ m_helpMenu->addAction(m_seeAcknowlegementsAction);
+
connect(containerWidget, &SkeletonGraphicsContainerWidget::containerSizeChanged,
graphicsWidget, &SkeletonGraphicsWidget::canvasResized);
@@ -146,10 +346,14 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() :
connect(graphicsWidget, &SkeletonGraphicsWidget::undo, m_document, &SkeletonDocument::undo);
connect(graphicsWidget, &SkeletonGraphicsWidget::redo, m_document, &SkeletonDocument::redo);
connect(graphicsWidget, &SkeletonGraphicsWidget::paste, m_document, &SkeletonDocument::paste);
- connect(graphicsWidget, &SkeletonGraphicsWidget::changeTurnaround, this, &SkeletonDocumentWindow::changeTurnaround);
connect(graphicsWidget, &SkeletonGraphicsWidget::batchChangeBegin, m_document, &SkeletonDocument::batchChangeBegin);
connect(graphicsWidget, &SkeletonGraphicsWidget::batchChangeEnd, m_document, &SkeletonDocument::batchChangeEnd);
+ connect(graphicsWidget, &SkeletonGraphicsWidget::changeTurnaround, this, &SkeletonDocumentWindow::changeTurnaround);
+ connect(graphicsWidget, &SkeletonGraphicsWidget::save, this, &SkeletonDocumentWindow::save);
+ connect(graphicsWidget, &SkeletonGraphicsWidget::open, this, &SkeletonDocumentWindow::open);
+ connect(graphicsWidget, &SkeletonGraphicsWidget::exportResult, this, &SkeletonDocumentWindow::exportResult);
+
connect(m_document, &SkeletonDocument::nodeAdded, graphicsWidget, &SkeletonGraphicsWidget::nodeAdded);
connect(m_document, &SkeletonDocument::nodeRemoved, graphicsWidget, &SkeletonGraphicsWidget::nodeRemoved);
connect(m_document, &SkeletonDocument::edgeAdded, graphicsWidget, &SkeletonGraphicsWidget::edgeAdded);
@@ -157,12 +361,14 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() :
connect(m_document, &SkeletonDocument::nodeRadiusChanged, graphicsWidget, &SkeletonGraphicsWidget::nodeRadiusChanged);
connect(m_document, &SkeletonDocument::nodeOriginChanged, graphicsWidget, &SkeletonGraphicsWidget::nodeOriginChanged);
connect(m_document, &SkeletonDocument::partVisibleStateChanged, graphicsWidget, &SkeletonGraphicsWidget::partVisibleStateChanged);
+ connect(m_document, &SkeletonDocument::cleanup, graphicsWidget, &SkeletonGraphicsWidget::removeAllContent);
connect(m_document, &SkeletonDocument::partListChanged, partListWidget, &SkeletonPartListWidget::partListChanged);
connect(m_document, &SkeletonDocument::partPreviewChanged, partListWidget, &SkeletonPartListWidget::partPreviewChanged);
connect(m_document, &SkeletonDocument::partLockStateChanged, partListWidget, &SkeletonPartListWidget::partLockStateChanged);
connect(m_document, &SkeletonDocument::partVisibleStateChanged, partListWidget, &SkeletonPartListWidget::partVisibleStateChanged);
connect(m_document, &SkeletonDocument::partSubdivStateChanged, partListWidget, &SkeletonPartListWidget::partSubdivStateChanged);
+ connect(m_document, &SkeletonDocument::cleanup, partListWidget, &SkeletonPartListWidget::partListChanged);
connect(m_document, &SkeletonDocument::skeletonChanged, m_document, &SkeletonDocument::generateMesh);
@@ -174,14 +380,118 @@ SkeletonDocumentWindow::SkeletonDocumentWindow() :
m_modelWidget->setCursor(graphicsWidget->cursor());
});
- connect(this, &SkeletonDocumentWindow::initialized, m_document, [=]() {
- m_document->saveSnapshot();
- graphicsWidget->setFocus();
- });
+ connect(m_document, &SkeletonDocument::skeletonChanged, this, &SkeletonDocumentWindow::documentChanged);
+ connect(m_document, &SkeletonDocument::turnaroundChanged, this, &SkeletonDocumentWindow::documentChanged);
connect(this, &SkeletonDocumentWindow::initialized, m_document, &SkeletonDocument::uiReady);
}
+SkeletonDocumentWindow *SkeletonDocumentWindow::createDocumentWindow()
+{
+ SkeletonDocumentWindow *documentWindow = new SkeletonDocumentWindow();
+ documentWindow->setAttribute(Qt::WA_DeleteOnClose);
+ documentWindow->showMaximized();
+ return documentWindow;
+}
+
+void SkeletonDocumentWindow::closeEvent(QCloseEvent *event)
+{
+ if (m_documentSaved) {
+ event->accept();
+ return;
+ }
+
+ QMessageBox::StandardButton answer = QMessageBox::question(this,
+ APP_NAME,
+ tr("Do you really want to close while there are unsaved changes?"),
+ QMessageBox::Yes | QMessageBox::No);
+ if (answer == QMessageBox::Yes)
+ event->accept();
+ else
+ event->ignore();
+}
+
+void SkeletonDocumentWindow::setCurrentFilename(const QString &filename)
+{
+ m_currentFilename = filename;
+ m_documentSaved = true;
+ updateTitle();
+}
+
+void SkeletonDocumentWindow::updateTitle()
+{
+ QString appName = APP_NAME;
+ QString appVer = APP_HUMAN_VER;
+ setWindowTitle(QString("%1 %2 %3%4").arg(appName).arg(appVer).arg(m_currentFilename).arg(m_documentSaved ? "" : "*"));
+}
+
+void SkeletonDocumentWindow::documentChanged()
+{
+ if (m_documentSaved) {
+ m_documentSaved = false;
+ updateTitle();
+ }
+}
+
+void SkeletonDocumentWindow::newWindow()
+{
+ SkeletonDocumentWindow::createDocumentWindow();
+}
+
+void SkeletonDocumentWindow::newDocument()
+{
+ if (!m_documentSaved) {
+ QMessageBox::StandardButton answer = QMessageBox::question(this,
+ APP_NAME,
+ tr("Do you really want to create new document and lose the unsaved changes?"),
+ QMessageBox::Yes | QMessageBox::No);
+ if (answer != QMessageBox::Yes)
+ return;
+ }
+ m_document->reset();
+}
+
+void SkeletonDocumentWindow::saveAs()
+{
+ QString filename = QFileDialog::getSaveFileName(this, QString(), QString(),
+ tr("Dust3D Document (*.ds3)"));
+ if (filename.isEmpty()) {
+ return;
+ }
+ saveTo(filename);
+}
+
+void SkeletonDocumentWindow::saveAll()
+{
+ for (auto &window: g_documentWindows) {
+ window->save();
+ }
+}
+
+void SkeletonDocumentWindow::viewSource()
+{
+ QString url = APP_REPOSITORY_URL;
+ qDebug() << "viewSource:" << url;
+ QDesktopServices::openUrl(QUrl(url));
+}
+
+void SkeletonDocumentWindow::about()
+{
+ SkeletonDocumentWindow::showAbout();
+}
+
+void SkeletonDocumentWindow::reportIssues()
+{
+ QString url = APP_ISSUES_URL;
+ qDebug() << "reportIssues:" << url;
+ QDesktopServices::openUrl(QUrl(url));
+}
+
+void SkeletonDocumentWindow::seeAcknowlegements()
+{
+ SkeletonDocumentWindow::showAcknowlegements();
+}
+
void SkeletonDocumentWindow::initButton(QPushButton *button)
{
button->setFont(Theme::awesome()->font(Theme::toolIconFontSize));
@@ -191,6 +501,7 @@ void SkeletonDocumentWindow::initButton(QPushButton *button)
SkeletonDocumentWindow::~SkeletonDocumentWindow()
{
+ g_documentWindows.erase(this);
}
void SkeletonDocumentWindow::showEvent(QShowEvent *event)
@@ -198,14 +509,16 @@ void SkeletonDocumentWindow::showEvent(QShowEvent *event)
QMainWindow::showEvent(event);
if (m_firstShow) {
m_firstShow = false;
+ updateTitle();
+ m_document->saveSnapshot();
+ m_graphicsWidget->setFocus();
emit initialized();
}
}
void SkeletonDocumentWindow::changeTurnaround()
{
- QString fileName = QFileDialog::getOpenFileName(this, tr("Open Turnaround Reference Image"),
- QString(),
+ QString fileName = QFileDialog::getOpenFileName(this, QString(), QString(),
tr("Image Files (*.png *.jpg *.bmp)")).trimmed();
if (fileName.isEmpty())
return;
@@ -214,3 +527,102 @@ void SkeletonDocumentWindow::changeTurnaround()
return;
m_document->updateTurnaround(image);
}
+
+void SkeletonDocumentWindow::save()
+{
+ saveTo(m_currentFilename);
+}
+
+void SkeletonDocumentWindow::saveTo(const QString &saveAsFilename)
+{
+ QString filename = saveAsFilename;
+
+ if (filename.isEmpty()) {
+ filename = QFileDialog::getSaveFileName(this, QString(), QString(),
+ tr("Dust3D Document (*.ds3)"));
+ if (filename.isEmpty()) {
+ return;
+ }
+ }
+
+ QApplication::setOverrideCursor(Qt::WaitCursor);
+
+ Ds3FileWriter ds3Writer;
+
+ QByteArray modelXml;
+ QXmlStreamWriter stream(&modelXml);
+ SkeletonSnapshot snapshot;
+ m_document->toSnapshot(&snapshot);
+ saveSkeletonToXmlStream(&snapshot, &stream);
+ if (modelXml.size() > 0)
+ ds3Writer.add("model.xml", "model", &modelXml);
+
+ QByteArray imageByteArray;
+ QBuffer pngBuffer(&imageByteArray);
+ if (!m_document->turnaround.isNull()) {
+ pngBuffer.open(QIODevice::WriteOnly);
+ m_document->turnaround.save(&pngBuffer, "PNG");
+ if (imageByteArray.size() > 0)
+ ds3Writer.add("canvas.png", "asset", &imageByteArray);
+ }
+
+ if (ds3Writer.save(filename)) {
+ setCurrentFilename(filename);
+ }
+
+ QApplication::restoreOverrideCursor();
+}
+
+void SkeletonDocumentWindow::open()
+{
+ if (!m_documentSaved) {
+ QMessageBox::StandardButton answer = QMessageBox::question(this,
+ APP_NAME,
+ tr("Do you really want to open another file and lose the unsaved changes?"),
+ QMessageBox::Yes | QMessageBox::No);
+ if (answer != QMessageBox::Yes)
+ return;
+ }
+
+ QString filename = QFileDialog::getOpenFileName(this, QString(), QString(),
+ tr("Dust3D Document (*.ds3)"));
+ if (filename.isEmpty())
+ return;
+
+ QApplication::setOverrideCursor(Qt::WaitCursor);
+ Ds3FileReader ds3Reader(filename);
+ for (int i = 0; i < ds3Reader.items().size(); ++i) {
+ Ds3ReaderItem item = ds3Reader.items().at(i);
+ if (item.type == "model") {
+ QByteArray data;
+ ds3Reader.loadItem(item.name, &data);
+ QXmlStreamReader stream(data);
+ SkeletonSnapshot snapshot;
+ loadSkeletonFromXmlStream(&snapshot, stream);
+ m_document->fromSnapshot(snapshot);
+ } else if (item.type == "asset") {
+ if (item.name == "canvas.png") {
+ QByteArray data;
+ ds3Reader.loadItem(item.name, &data);
+ QImage image = QImage::fromData(data, "PNG");
+ m_document->updateTurnaround(image);
+ }
+ }
+ }
+ QApplication::restoreOverrideCursor();
+
+ setCurrentFilename(filename);
+}
+
+void SkeletonDocumentWindow::exportResult()
+{
+ QString filename = QFileDialog::getSaveFileName(this, QString(), QString(),
+ tr("Wavefront (*.obj)"));
+ if (filename.isEmpty()) {
+ return;
+ }
+ QApplication::setOverrideCursor(Qt::WaitCursor);
+ m_modelWidget->exportMeshAsObj(filename);
+ QApplication::restoreOverrideCursor();
+}
+
diff --git a/src/skeletondocumentwindow.h b/src/skeletondocumentwindow.h
index 3581fee9..1aefb72a 100644
--- a/src/skeletondocumentwindow.h
+++ b/src/skeletondocumentwindow.h
@@ -3,9 +3,15 @@
#include
#include
#include
+#include
+#include
+#include
+#include
#include "skeletondocument.h"
#include "modelwidget.h"
+class SkeletonGraphicsWidget;
+
class SkeletonDocumentWindow : public QMainWindow
{
Q_OBJECT
@@ -14,16 +20,72 @@ signals:
public:
SkeletonDocumentWindow();
~SkeletonDocumentWindow();
+ static SkeletonDocumentWindow *createDocumentWindow();
+ static void showAcknowlegements();
+ static void showAbout();
protected:
void showEvent(QShowEvent *event);
+ void closeEvent(QCloseEvent *event);
public slots:
void changeTurnaround();
+ void save();
+ void saveTo(const QString &saveAsFilename);
+ void open();
+ void exportResult();
+ void newWindow();
+ void newDocument();
+ void saveAs();
+ void saveAll();
+ void viewSource();
+ void about();
+ void reportIssues();
+ void seeAcknowlegements();
+ void documentChanged();
private:
void initButton(QPushButton *button);
+ void setCurrentFilename(const QString &filename);
+ void updateTitle();
private:
SkeletonDocument *m_document;
bool m_firstShow;
ModelWidget *m_modelWidget;
+ SkeletonGraphicsWidget *m_graphicsWidget;
+ QString m_currentFilename;
+ bool m_documentSaved;
+
+ QMenu *m_fileMenu;
+ QAction *m_newWindowAction;
+ QAction *m_newDocumentAction;
+ QAction *m_openAction;
+ QAction *m_saveAction;
+ QAction *m_saveAsAction;
+ QAction *m_saveAllAction;
+ QAction *m_exportAction;
+ QAction *m_changeTurnaroundAction;
+
+ QMenu *m_editMenu;
+ QAction *m_addAction;
+ QAction *m_undoAction;
+ QAction *m_redoAction;
+ QAction *m_deleteAction;
+ QAction *m_cutAction;
+ QAction *m_copyAction;
+ QAction *m_pasteAction;
+ QAction *m_flipHorizontallyAction;
+ QAction *m_flipVerticallyAction;
+ QAction *m_selectAllAction;
+ QAction *m_selectPartAllAction;
+ QAction *m_unselectAllAction;
+
+ QMenu *m_viewMenu;
+ QAction *m_resetModelWidgetPosAction;
+ QAction *m_showDebugDialogAction;
+
+ QMenu *m_helpMenu;
+ QAction *m_viewSourceAction;
+ QAction *m_aboutAction;
+ QAction *m_reportIssuesAction;
+ QAction *m_seeAcknowlegementsAction;
};
#endif
diff --git a/src/skeletongraphicswidget.cpp b/src/skeletongraphicswidget.cpp
index 5ca43861..c385dbfc 100644
--- a/src/skeletongraphicswidget.cpp
+++ b/src/skeletongraphicswidget.cpp
@@ -46,10 +46,12 @@ SkeletonGraphicsWidget::SkeletonGraphicsWidget(const SkeletonDocument *document)
m_cursorNodeItem = new SkeletonGraphicsNodeItem();
m_cursorNodeItem->hide();
+ m_cursorNodeItem->setData(0, "cursorNode");
scene()->addItem(m_cursorNodeItem);
m_cursorEdgeItem = new SkeletonGraphicsEdgeItem();
m_cursorEdgeItem->hide();
+ m_cursorEdgeItem->setData(0, "cursorEdge");
scene()->addItem(m_cursorEdgeItem);
m_selectionItem = new SkeletonGraphicsSelectionItem();
@@ -77,7 +79,7 @@ void SkeletonGraphicsWidget::showContextMenu(const QPoint &pos)
QMenu contextMenu(this);
- QAction addAction(tr("Add.."), this);
+ QAction addAction(tr("Add..."), this);
connect(&addAction, &QAction::triggered, [=]() {
emit setEditMode(SkeletonDocumentEditMode::Add);
});
@@ -96,19 +98,19 @@ void SkeletonGraphicsWidget::showContextMenu(const QPoint &pos)
}
QAction deleteAction(tr("Delete"), this);
- if (!m_rangeSelectionSet.empty()) {
+ if (hasSelection()) {
connect(&deleteAction, &QAction::triggered, this, &SkeletonGraphicsWidget::deleteSelected);
contextMenu.addAction(&deleteAction);
}
QAction cutAction(tr("Cut"), this);
- if (!m_rangeSelectionSet.empty()) {
+ if (hasSelection()) {
connect(&cutAction, &QAction::triggered, this, &SkeletonGraphicsWidget::cut);
contextMenu.addAction(&cutAction);
}
QAction copyAction(tr("Copy"), this);
- if (!m_rangeSelectionSet.empty()) {
+ if (hasSelection()) {
connect(©Action, &QAction::triggered, this, &SkeletonGraphicsWidget::copy);
contextMenu.addAction(©Action);
}
@@ -120,38 +122,44 @@ void SkeletonGraphicsWidget::showContextMenu(const QPoint &pos)
}
QAction flipHorizontallyAction(tr("H Flip"), this);
- if (!m_rangeSelectionSet.empty() && !isSingleNodeSelected()) {
+ if (hasMultipleSelection()) {
connect(&flipHorizontallyAction, &QAction::triggered, this, &SkeletonGraphicsWidget::flipHorizontally);
contextMenu.addAction(&flipHorizontallyAction);
}
QAction flipVerticallyAction(tr("V Flip"), this);
- if (!m_rangeSelectionSet.empty() && !isSingleNodeSelected()) {
+ if (hasMultipleSelection()) {
connect(&flipVerticallyAction, &QAction::triggered, this, &SkeletonGraphicsWidget::flipVertically);
contextMenu.addAction(&flipVerticallyAction);
}
QAction selectAllAction(tr("Select All"), this);
- if (!nodeItemMap.empty()) {
+ if (hasItems()) {
connect(&selectAllAction, &QAction::triggered, this, &SkeletonGraphicsWidget::selectAll);
contextMenu.addAction(&selectAllAction);
}
QAction selectPartAllAction(tr("Select Part"), this);
- if (!nodeItemMap.empty()) {
+ if (hasItems()) {
connect(&selectPartAllAction, &QAction::triggered, this, &SkeletonGraphicsWidget::selectPartAll);
contextMenu.addAction(&selectPartAllAction);
}
QAction unselectAllAction(tr("Unselect All"), this);
- if (!m_rangeSelectionSet.empty()) {
+ if (hasSelection()) {
connect(&unselectAllAction, &QAction::triggered, this, &SkeletonGraphicsWidget::unselectAll);
contextMenu.addAction(&unselectAllAction);
}
contextMenu.addSeparator();
- QAction changeTurnaroundAction(tr("Change Turnaround.."), this);
+ QAction exportResultAction(tr("Export..."), this);
+ if (hasItems()) {
+ connect(&exportResultAction, &QAction::triggered, this, &SkeletonGraphicsWidget::exportResult);
+ contextMenu.addAction(&exportResultAction);
+ }
+
+ QAction changeTurnaroundAction(tr("Change Turnaround..."), this);
connect(&changeTurnaroundAction, &QAction::triggered, [=]() {
emit changeTurnaround();
});
@@ -160,6 +168,21 @@ void SkeletonGraphicsWidget::showContextMenu(const QPoint &pos)
contextMenu.exec(mapToGlobal(pos));
}
+bool SkeletonGraphicsWidget::hasSelection()
+{
+ return !m_rangeSelectionSet.empty();
+}
+
+bool SkeletonGraphicsWidget::hasItems()
+{
+ return !nodeItemMap.empty();
+}
+
+bool SkeletonGraphicsWidget::hasMultipleSelection()
+{
+ return !m_rangeSelectionSet.empty() && !isSingleNodeSelected();
+}
+
void SkeletonGraphicsWidget::updateItems()
{
for (auto nodeItemIt = nodeItemMap.begin(); nodeItemIt != nodeItemMap.end(); nodeItemIt++) {
@@ -669,6 +692,10 @@ bool SkeletonGraphicsWidget::mouseDoubleClick(QMouseEvent *event)
selectPartAll();
return true;
}
+ if (QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ControlModifier)) {
+ emit open();
+ return true;
+ }
return false;
}
@@ -734,6 +761,10 @@ bool SkeletonGraphicsWidget::keyPress(QKeyEvent *event)
if (QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ControlModifier)) {
emit paste();
}
+ } else if (event->key() == Qt::Key_S) {
+ if (QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ControlModifier)) {
+ emit save();
+ }
}
return false;
}
@@ -1153,6 +1184,33 @@ void SkeletonGraphicsWidget::copy()
clipboard->setText(snapshotXml);
}
+void SkeletonGraphicsWidget::removeAllContent()
+{
+ nodeItemMap.clear();
+ edgeItemMap.clear();
+ m_rangeSelectionSet.clear();
+ m_hoveredEdgeItem = nullptr;
+ m_hoveredNodeItem = nullptr;
+ m_addFromNodeItem = nullptr;
+ m_cursorEdgeItem->hide();
+ m_cursorNodeItem->hide();
+ std::set contentItems;
+ for (const auto &it: items()) {
+ QGraphicsItem *item = it;
+ if (item->data(0) == "node") {
+ contentItems.insert(item);
+ } else if (item->data(0) == "edge") {
+ contentItems.insert(item);
+ }
+ }
+ for (const auto &it: contentItems) {
+ QGraphicsItem *item = it;
+ Q_ASSERT(item != m_cursorEdgeItem);
+ Q_ASSERT(item != m_cursorNodeItem);
+ scene()->removeItem(item);
+ }
+}
+
bool SkeletonGraphicsWidget::isSingleNodeSelected()
{
if (m_rangeSelectionSet.size() != 1)
diff --git a/src/skeletongraphicswidget.h b/src/skeletongraphicswidget.h
index 1cc74409..a88b5521 100644
--- a/src/skeletongraphicswidget.h
+++ b/src/skeletongraphicswidget.h
@@ -266,6 +266,9 @@ signals:
void changeTurnaround();
void batchChangeBegin();
void batchChangeEnd();
+ void save();
+ void open();
+ void exportResult();
public:
SkeletonGraphicsWidget(const SkeletonDocument *document);
std::map> nodeItemMap;
@@ -281,6 +284,9 @@ public:
void readMergedSkeletonNodeSetFromRangeSelection(std::set *nodeItemSet);
void readSkeletonNodeAndEdgeIdSetFromRangeSelection(std::set *nodeIdSet, std::set *edgeIdSet);
bool readSkeletonNodeAndAnyEdgeOfNodeFromRangeSelection(SkeletonGraphicsNodeItem **nodeItem, SkeletonGraphicsEdgeItem **edgeItem);
+ bool hasSelection();
+ bool hasItems();
+ bool hasMultipleSelection();
protected:
void mouseMoveEvent(QMouseEvent *event);
void wheelEvent(QWheelEvent *event);
@@ -310,6 +316,7 @@ public slots:
void copy();
void flipHorizontally();
void flipVertically();
+ void removeAllContent();
private slots:
void turnaroundImageReady();
private:
diff --git a/src/skeletonsnapshot.cpp b/src/skeletonsnapshot.cpp
index c829b64e..872c1717 100644
--- a/src/skeletonsnapshot.cpp
+++ b/src/skeletonsnapshot.cpp
@@ -1,282 +1,2 @@
#include "skeletonsnapshot.h"
#include
-
-SkeletonSnapshot::SkeletonSnapshot() :
- m_boundingBoxResolved(false),
- m_rootNodeResolved(false)
-{
-}
-
-void joinNodeAndNeighborsToGroup(std::map> &nodeLinkMap,
- std::map &nodeGroupMap,
- const QString &nodeId,
- int groupId,
- std::vector &group)
-{
- group.push_back(nodeId);
- nodeGroupMap[nodeId] = groupId;
- std::vector *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 *groups)
-{
- std::map> nodeLinkMap;
- std::map>::iterator edgeIterator;
- std::map neighborCountMap;
- for (edgeIterator = edges.begin(); edgeIterator != edges.end(); edgeIterator++) {
- neighborCountMap[edgeIterator->second["from"]]++;
- neighborCountMap[edgeIterator->second["to"]]++;
- }
- int nextNewXnodeId = 1;
- std::vector> newPendingEdges;
- for (edgeIterator = edges.begin(); edgeIterator != edges.end(); ) {
- std::map *oneNode = &nodes[edgeIterator->second["from"]];
- std::map *linkNode = &nodes[edgeIterator->second["to"]];
- if ("true" != (*oneNode)["isBranch"]) {
- oneNode = &nodes[edgeIterator->second["to"]];
- linkNode = &nodes[edgeIterator->second["from"]];
- if ("true" != (*oneNode)["isBranch"]) {
- edgeIterator++;
- continue;
- } else {
- if (neighborCountMap[(*linkNode)["id"]] < 3) {
- edgeIterator++;
- continue;
- }
- }
- } else {
- if (neighborCountMap[(*linkNode)["id"]] < 3) {
- edgeIterator++;
- continue;
- }
- }
-
- if ("red" == (*oneNode)["sideColorName"]) {
- QString nodeId = QString("nodex%1").arg(nextNewXnodeId++);
- std::map *newNode = &nodes[nodeId];
- *newNode = *linkNode;
- (*newNode)["id"] = nodeId;
- (*newNode)["radius"] = QString("%1").arg((*newNode)["radius"].toFloat() / 5);
-
- std::map *pairNode = &nodes[(*oneNode)["nextSidePair"]];
- QString pairNodeId = QString("nodex%1").arg(nextNewXnodeId++);
- std::map *newPairNode = &nodes[pairNodeId];
- *newPairNode = *pairNode;
- (*newPairNode)["id"] = pairNodeId;
- (*newPairNode)["radius"] = QString("%1").arg((*newPairNode)["radius"].toFloat() / 5);
-
- (*newNode)["nextSidePair"] = pairNodeId;
- (*newPairNode)["nextSidePair"] = nodeId;
-
- newPendingEdges.push_back(std::make_pair((*oneNode)["id"], nodeId));
- }
-
- edgeIterator = edges.erase(edgeIterator);
- }
- int nextNewXedgeId = 1;
- for (size_t i = 0; i < newPendingEdges.size(); i++) {
- QString edgeId = QString("edgex%1").arg(nextNewXedgeId++);
- std::map *newEdge = &edges[edgeId];
- (*newEdge)["id"] = edgeId;
- (*newEdge)["from"] = newPendingEdges[i].first;
- (*newEdge)["to"] = newPendingEdges[i].second;
-
- edgeId = QString("edgex%1").arg(nextNewXedgeId++);
- newEdge = &edges[edgeId];
- (*newEdge)["id"] = edgeId;
- (*newEdge)["from"] = nodes[newPendingEdges[i].first]["nextSidePair"];
- (*newEdge)["to"] = nodes[newPendingEdges[i].second]["nextSidePair"];
- }
- 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>::iterator nodeIterator;
- for (nodeIterator = nodes.begin(); nodeIterator != nodes.end(); nodeIterator++) {
- nodeLinkMap[nodeIterator->first].push_back(nodeIterator->second["nextSidePair"]);
- }
- std::map nodeGroupMap;
- std::vector> nameGroups;
- for (nodeIterator = nodes.begin(); nodeIterator != nodes.end(); nodeIterator++) {
- if (nodeGroupMap.find(nodeIterator->first) != nodeGroupMap.end())
- continue;
- std::vector 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>::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>::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>::iterator edgeIterator;
- std::map 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& p1, const std::pair& p2) {
- return p1.second < p2.second;
- });
- return x->first;
-}
-
-void SkeletonSnapshot::resolveRootNode()
-{
- if (m_rootNodeResolved)
- return;
- m_rootNodeResolved = true;
-
- std::map>::iterator edgeIterator;
- std::map 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>::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 ("true" == nodeIterator->second["isRoot"]) {
- m_rootNode = nodeIterator->first;
- break;
- }
- 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();
-
- if (m_rootNode.isEmpty()) {
- nodeIterator = nodes.begin();
- if (nodeIterator != nodes.end()) {
- m_rootNode = nodeIterator->first;
- }
- }
-}
diff --git a/src/skeletonsnapshot.h b/src/skeletonsnapshot.h
index f6e5b850..154984f6 100644
--- a/src/skeletonsnapshot.h
+++ b/src/skeletonsnapshot.h
@@ -13,26 +13,7 @@ public:
std::map> nodes;
std::map> edges;
std::map> parts;
- std::map> mirrors;
- std::map> trivialMarkers;
std::vector partIdList;
-public:
- SkeletonSnapshot();
- void splitByConnectivity(std::vector *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