diff --git a/CMakeLists.txt b/CMakeLists.txt index bd4adeff10..7343b90e4a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,9 +27,9 @@ cmake_minimum_required(VERSION 3.10 FATAL_ERROR) project(OpenSpace) set(OPENSPACE_VERSION_MAJOR 0) -set(OPENSPACE_VERSION_MINOR 15) -set(OPENSPACE_VERSION_PATCH 2) -set(OPENSPACE_VERSION_STRING "Beta-7") +set(OPENSPACE_VERSION_MINOR 16) +set(OPENSPACE_VERSION_PATCH 0) +set(OPENSPACE_VERSION_STRING "Beta-8 RC1") set(OPENSPACE_BASE_DIR "${PROJECT_SOURCE_DIR}") set(OPENSPACE_CMAKE_EXT_DIR "${OPENSPACE_BASE_DIR}/support/cmake") diff --git a/Jenkinsfile b/Jenkinsfile index 545a72709b..53b9c5ab90 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -95,7 +95,8 @@ parallel tools: { tool: cppCheck() ) } - } + cleanWs() + } // node('tools') }, linux_gcc: { node('linux' && 'gcc') { @@ -119,6 +120,7 @@ linux_gcc: { stage('linux-gcc/test') { // testHelper.runUnitTests('build/OpenSpaceTest'); } + cleanWs() } // node('linux') }, linux_clang: { @@ -143,6 +145,7 @@ linux_clang: { stage('linux-clang/test') { // testHelper.runUnitTests('build/OpenSpaceTest'); } + cleanWs() } // node('linux') }, windows: { @@ -164,7 +167,8 @@ windows: { // testHelper.runUnitTests('bin\\Debug\\OpenSpaceTest') } } // node('windows') - } + cleanWs() + } // node('windows') }, macos: { node('macos') { @@ -182,5 +186,6 @@ macos: { // Currently, the unit tests are crashing on OS X // testHelper.runUnitTests('build/Debug/OpenSpaceTest') } - } // node('osx') + cleanWs() + } // node('macos') } diff --git a/apps/OpenSpace/ext/launcher/CMakeLists.txt b/apps/OpenSpace/ext/launcher/CMakeLists.txt index 14ae92bd01..573d872042 100644 --- a/apps/OpenSpace/ext/launcher/CMakeLists.txt +++ b/apps/OpenSpace/ext/launcher/CMakeLists.txt @@ -34,6 +34,7 @@ set(HEADER_FILES include/profile/cameradialog.h include/profile/deltatimesdialog.h include/profile/keybindingsdialog.h + include/profile/line.h include/profile/marknodesdialog.h include/profile/metadialog.h include/profile/modulesdialog.h @@ -52,6 +53,7 @@ set(SOURCE_FILES src/profile/cameradialog.cpp src/profile/deltatimesdialog.cpp src/profile/keybindingsdialog.cpp + src/profile/line.cpp src/profile/marknodesdialog.cpp src/profile/metadialog.cpp src/profile/modulesdialog.cpp diff --git a/apps/OpenSpace/ext/launcher/include/filesystemaccess.h b/apps/OpenSpace/ext/launcher/include/filesystemaccess.h index 35e0dcdde9..4538e33835 100644 --- a/apps/OpenSpace/ext/launcher/include/filesystemaccess.h +++ b/apps/OpenSpace/ext/launcher/include/filesystemaccess.h @@ -54,7 +54,7 @@ public: * * \param dir The directory from which to start the search from */ - std::string useQtFileSystemModelToTraverseDir(QString dir); + std::string useQtFileSystemModelToTraverseDir(std::string dir); private: void parseChildDirElements(QFileInfo item, std::string space, int level, diff --git a/apps/OpenSpace/ext/launcher/include/launcherwindow.h b/apps/OpenSpace/ext/launcher/include/launcherwindow.h index f85248851d..6b7283ea23 100644 --- a/apps/OpenSpace/ext/launcher/include/launcherwindow.h +++ b/apps/OpenSpace/ext/launcher/include/launcherwindow.h @@ -27,20 +27,20 @@ #include -#include -#include "profile/profileedit.h" -#include "filesystemaccess.h" #include -#include #include +namespace openspace::configuration { struct Configuration; } + +class QComboBox; +class QLabel; + class LauncherWindow : public QMainWindow { Q_OBJECT public: /** * Constructor for LauncherWindow class * - * \param basePath The base OpenSpace installation path * \param profileEnabled true if profile selection combo box will be enabled * \param globalConfig reference to global configuration for OpenSpace settings * \param sgctConfigEnabled true if window selection combo box will be enabled @@ -49,9 +49,9 @@ public: * \param parentItem The parent that contains this (and possibly other) children * in the tree structure. */ - LauncherWindow(std::string basePath, bool profileEnabled, - openspace::configuration::Configuration& globalConfig, bool sgctConfigEnabled, - std::string sgctConfigName, QWidget* parent); + LauncherWindow(bool profileEnabled, + const openspace::configuration::Configuration& globalConfig, + bool sgctConfigEnabled, std::string sgctConfigName, QWidget* parent); /** * Returns bool for whether "start OpenSpace" was chosen when this window closed. @@ -61,14 +61,6 @@ public: */ bool wasLaunchSelected() const; - /** - * Returns true if both the profile and sgct window configuration were specified - * at the command line (and so the launcher will not run). - * - * \return true if both profile and sgct window config were specified at CLI - */ - bool isFullyConfiguredFromCliArgs() const; - /** * Returns the selected profile name when launcher window closed * @@ -85,29 +77,23 @@ public: */ std::string selectedWindowConfig() const; -public slots: - void openWindowEdit(); - void openWindowNew(); - void startOpenSpace(); - private: + QWidget* createCentralWidget(); + void setBackgroundImage(const std::string& syncPath); + + void openProfileEditor(const std::string& profile); + void populateProfilesList(std::string preset); void populateWindowConfigsList(std::string preset); - std::optional loadProfileFromFile(std::string filename); - void saveProfileToFile(const std::string& path, const openspace::Profile& p); - FileSystemAccess _fileAccessProfiles; - FileSystemAccess _fileAccessWinConfigs; - FileSystemAccess _filesystemAccess; - std::string _reportAssetsInFilesystem; - openspace::configuration::Configuration& _globalConfig; - QString _basePath; - bool _launch = false; - bool _fullyConfiguredViaCliArgs = false; - bool _profileChangeAllowed = true; - bool _sgctConfigChangeAllowed = true; + const std::string _assetPath; + const std::string _configPath; + const std::string _profilePath; + const std::vector& _readOnlyProfiles; + bool _shouldLaunch = false; QComboBox* _profileBox = nullptr; QComboBox* _windowConfigBox = nullptr; + QLabel* _backgroundImage = nullptr; }; #endif // __OPENSPACE_UI_LAUNCHER___LAUNCHERWINDOW___H__ diff --git a/apps/OpenSpace/ext/launcher/include/profile/additionalscriptsdialog.h b/apps/OpenSpace/ext/launcher/include/profile/additionalscriptsdialog.h index afc48b068d..c8288565bc 100644 --- a/apps/OpenSpace/ext/launcher/include/profile/additionalscriptsdialog.h +++ b/apps/OpenSpace/ext/launcher/include/profile/additionalscriptsdialog.h @@ -47,6 +47,8 @@ private slots: void parseScript(); private: + void createWidgets(); + openspace::Profile& _profile; QTextEdit* _textScripts = nullptr; }; diff --git a/apps/OpenSpace/ext/launcher/include/profile/assetsdialog.h b/apps/OpenSpace/ext/launcher/include/profile/assetsdialog.h index 1090f5ac45..4bb35e62f2 100644 --- a/apps/OpenSpace/ext/launcher/include/profile/assetsdialog.h +++ b/apps/OpenSpace/ext/launcher/include/profile/assetsdialog.h @@ -28,7 +28,6 @@ #include #include "assettreemodel.h" -#include "filesystemaccess.h" namespace openspace { class Profile; } @@ -43,14 +42,18 @@ public: * * \param profile The #openspace::Profile object containing all data of the * new or imported profile. - * \param reportAssets A full summary of all assets and their respective paths in - * a text format unique to this application (more details are - * in class #assetTreeModel) + * \param assetBasePath The path to the folder in which all of the assets are living * \param parent Pointer to parent Qt widget */ - AssetsDialog(openspace::Profile& profile, const std::string reportAssets, + AssetsDialog(openspace::Profile& profile, const std::string& assetBasePath, QWidget* parent); +private slots: + void parseSelections(); + void selected(const QModelIndex&); + +private: + void createWidgets(); /** * Creates a text summary of all assets and their paths * @@ -58,11 +61,6 @@ public: */ QString createTextSummary(); -private slots: - void parseSelections(); - void selected(const QModelIndex&); - -private: openspace::Profile& _profile; AssetTreeModel _assetTreeModel; QTreeView* _assetTree = nullptr; diff --git a/apps/OpenSpace/ext/launcher/include/profile/assettreemodel.h b/apps/OpenSpace/ext/launcher/include/profile/assettreemodel.h index 42fad3ab8c..1426e3ff39 100644 --- a/apps/OpenSpace/ext/launcher/include/profile/assettreemodel.h +++ b/apps/OpenSpace/ext/launcher/include/profile/assettreemodel.h @@ -32,7 +32,7 @@ #include class AssetTreeModel : public QAbstractItemModel { - Q_OBJECT +Q_OBJECT public: AssetTreeModel(QObject* parent = nullptr); @@ -145,23 +145,11 @@ public: std::vector& outputItems); /** - * Imports asset tree data for this model. The import text format is unique to - * this asset tree structure. - * Each line starts with an character (0, 1, or x) to represent its checked status - * or if it doesn't exist in the current filesystem. - * This is followed by the name of either an asset or directory, with a space for - * each level of sub-directory. Example: - * 0Base - * 0 Directory - * 0 Asset1 - * 1 Asset2 - * 0 Asset3 - * 1Asset4 - * This format is used internally to translate from code that reads the filesystem - * - * \param contents asset recursive listing of directory in format described above + * Imports asset tree data for this model by recursively traversing the folder + * structure. + * \param assetBasePath The base path where to find all assets */ - void importModelData(const std::string& contents); + void importModelData(const std::string& assetBasePath); /** * Returns bool for if item is checked/selected diff --git a/apps/OpenSpace/ext/launcher/include/profile/cameradialog.h b/apps/OpenSpace/ext/launcher/include/profile/cameradialog.h index 8bb6994023..34b497b34a 100644 --- a/apps/OpenSpace/ext/launcher/include/profile/cameradialog.h +++ b/apps/OpenSpace/ext/launcher/include/profile/cameradialog.h @@ -50,6 +50,7 @@ private slots: void tabSelect(int); private: + void createWidgets(); QWidget* createNavStateWidget(); QWidget* createGeoWidget(); diff --git a/apps/OpenSpace/ext/launcher/include/profile/deltatimesdialog.h b/apps/OpenSpace/ext/launcher/include/profile/deltatimesdialog.h index a98b433dcd..3d192af852 100644 --- a/apps/OpenSpace/ext/launcher/include/profile/deltatimesdialog.h +++ b/apps/OpenSpace/ext/launcher/include/profile/deltatimesdialog.h @@ -41,9 +41,9 @@ public: /** * Constructor for deltaTimes class * - * \param imported The #openspace::Profile object containing all data of the + * \param profile The #openspace::Profile object containing all data of the * new or imported profile. - * \param parent Pointer to parent Qt widget (optional) + * \param parent Pointer to parent Qt widget */ DeltaTimesDialog(openspace::Profile& profile, QWidget* parent); @@ -61,10 +61,10 @@ public: * * \param evt #QKeyEvent object for the key press event */ - void keyPressEvent(QKeyEvent *evt); + void keyPressEvent(QKeyEvent* evt); -public slots: +private slots: void listItemSelected(); void valueChanged(const QString& text); void saveDeltaTimeValue(); @@ -74,6 +74,8 @@ public slots: void parseSelections(); private: + void createWidgets(); + /** * Called to transition to editing a particular dt value (gui settings) * diff --git a/apps/OpenSpace/ext/launcher/include/profile/keybindingsdialog.h b/apps/OpenSpace/ext/launcher/include/profile/keybindingsdialog.h index a9a5033233..2bc01477ca 100644 --- a/apps/OpenSpace/ext/launcher/include/profile/keybindingsdialog.h +++ b/apps/OpenSpace/ext/launcher/include/profile/keybindingsdialog.h @@ -45,7 +45,7 @@ public: /** * Constructor for keybindings class * - * \param imported The #openspace::Profile object containing all data of the + * \param profile The #openspace::Profile object containing all data of the * new or imported profile. * \param parent Pointer to parent Qt widget (optional) */ @@ -56,9 +56,9 @@ public: * * \param evt #QKeyEvent object for the key press event */ - void keyPressEvent(QKeyEvent *evt); + void keyPressEvent(QKeyEvent* evt); -public slots: +private slots: void listItemSelected(); void listItemAdded(); void listItemRemove(); @@ -69,13 +69,11 @@ public slots: void keySelected(int index); private: + void createWidgets(); void transitionFromEditMode(); void editBoxDisabled(bool disabled); int indexInKeyMapping(std::vector& mapVector, int keyInt); bool areRequiredFormsFilled(); - std::string truncateString(std::string& s); - void replaceChars(std::string& src, const std::string& from, - const std::string& to); bool isLineEmpty(int index); openspace::Profile& _profile; diff --git a/apps/OpenSpace/ext/launcher/include/profile/line.h b/apps/OpenSpace/ext/launcher/include/profile/line.h new file mode 100644 index 0000000000..ebe27607dc --- /dev/null +++ b/apps/OpenSpace/ext/launcher/include/profile/line.h @@ -0,0 +1,35 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2020 * + * * + * 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. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_UI_LAUNCHER___LINE___H__ +#define __OPENSPACE_UI_LAUNCHER___LINE___H__ + +#include + +class Line : public QFrame { +public: + Line(); +}; + +#endif // __OPENSPACE_UI_LAUNCHER___LINE___H__ diff --git a/apps/OpenSpace/ext/launcher/include/profile/marknodesdialog.h b/apps/OpenSpace/ext/launcher/include/profile/marknodesdialog.h index 5c96392bc7..eae3617543 100644 --- a/apps/OpenSpace/ext/launcher/include/profile/marknodesdialog.h +++ b/apps/OpenSpace/ext/launcher/include/profile/marknodesdialog.h @@ -40,8 +40,8 @@ public: /** * Constructor for markNodes class * - * \param imported The #openspace::Profile object containing all data of the - * new or imported profile. + * \param profile The #openspace::Profile object containing all data of the + * new or imported profile. * \param parent Pointer to parent Qt widget */ MarkNodesDialog(openspace::Profile& profile, QWidget* parent); @@ -53,13 +53,15 @@ public: */ void keyPressEvent(QKeyEvent* evt); -public slots: +private slots: void listItemSelected(); void listItemAdded(); void listItemRemove(); void parseSelections(); private: + void createWidgets(); + std::vector _markedNodesListItems; openspace::Profile& _profile; std::vector _data; diff --git a/apps/OpenSpace/ext/launcher/include/profile/metadialog.h b/apps/OpenSpace/ext/launcher/include/profile/metadialog.h index 952cccbb3f..f3a79acdfd 100644 --- a/apps/OpenSpace/ext/launcher/include/profile/metadialog.h +++ b/apps/OpenSpace/ext/launcher/include/profile/metadialog.h @@ -35,20 +35,21 @@ class QTextEdit; class MetaDialog : public QDialog { Q_OBJECT public: - /** * Constructor for meta class * - * \param imported The #openspace::Profile object containing all data of the - * new or imported profile. + * \param profile The #openspace::Profile object containing all data of the + * new or imported profile. * \param parent Pointer to parent Qt widget */ MetaDialog(openspace::Profile& profile, QWidget* parent); -public slots: +private slots: void save(); private: + void createWidgets(); + openspace::Profile& _profile; QLineEdit* _nameEdit = nullptr; diff --git a/apps/OpenSpace/ext/launcher/include/profile/modulesdialog.h b/apps/OpenSpace/ext/launcher/include/profile/modulesdialog.h index cf790ae8bf..c2c419528f 100644 --- a/apps/OpenSpace/ext/launcher/include/profile/modulesdialog.h +++ b/apps/OpenSpace/ext/launcher/include/profile/modulesdialog.h @@ -41,20 +41,20 @@ public: /** * Constructor for osmodules class * - * \param imported The #openspace::Profile object containing all data of the - * new or imported profile. + * \param profile The #openspace::Profile object containing all data of the + * new or imported profile. * \param parent Pointer to parent Qt widget */ - ModulesDialog(openspace::Profile& profiles, QWidget* parent); + ModulesDialog(openspace::Profile& profile, QWidget* parent); /** * Handles keypress while the Qt dialog window is open * * \param evt #QKeyEvent object for the key press event */ - void keyPressEvent(QKeyEvent *evt); + void keyPressEvent(QKeyEvent* evt); -public slots: +private slots: void listItemSelected(); void listItemAdded(); void listItemRemove(); @@ -64,15 +64,16 @@ public slots: void parseSelections(); private: + void createWidgets(); + QString createOneLineSummary(openspace::Profile::Module m); void transitionFromEditMode(); void editBoxDisabled(bool disabled); - bool isLineEmpty(int index); + bool isLineEmpty(int index) const; openspace::Profile& _profile; std::vector _data; bool _editModeNewItem = false; - const openspace::Profile::Module kBlank = {"", "", ""}; QListWidget* _list = nullptr; QLabel* _moduleLabel = nullptr; diff --git a/apps/OpenSpace/ext/launcher/include/profile/profileedit.h b/apps/OpenSpace/ext/launcher/include/profile/profileedit.h index d595cbd4c8..d8de4c4ab9 100644 --- a/apps/OpenSpace/ext/launcher/include/profile/profileedit.h +++ b/apps/OpenSpace/ext/launcher/include/profile/profileedit.h @@ -26,18 +26,15 @@ #define __OPENSPACE_UI_LAUNCHER___PROFILEEDIT___H__ #include -#include -#include "profile/metadialog.h" -#include "profile/propertiesdialog.h" -#include "profile/modulesdialog.h" -#include "profile/keybindingsdialog.h" -#include "profile/assetsdialog.h" -#include "profile/timedialog.h" -#include "profile/additionalscriptsdialog.h" -#include "profile/deltatimesdialog.h" -#include "profile/cameradialog.h" -#include "profile/marknodesdialog.h" -#include +#include +#include + +namespace openspace { class Profile; } + +class QWidget; +class QLabel; +class QLineEdit; +class QTextEdit; class ProfileEdit : public QDialog { Q_OBJECT @@ -45,24 +42,18 @@ public: /** * Constructor for ProfileEdit class * - * \param imported The #openspace::Profile object containing all data of the - * new or imported profile. - * \param reportedAssets string list of assets reported by filesystemAccess class + * \param profile The #openspace::Profile object containing all data of the + * new or imported profile. + * \param profileName The name of the profile to create + * \param assetBasePath The path to the folder where the assets live + * \param profileName The path to the folder in which all profiles live * \param profilesReadOnly vector list of profile names that are read-only and must * not be overwritten - * \param parent Pointer to parent Qt widget (optional) + * \param parent Pointer to parent Qt widget */ - ProfileEdit(openspace::Profile& profile, const std::string reportedAssets, - std::vector& profilesReadOnly, QWidget* parent); - - /** - * Sets the profile name in top save/edit window. This can be changed by user in - * order to save to a different file. - * - * \param profileToSet name of the profile to set to - */ - void setProfileName(QString profileToSet); - + ProfileEdit(openspace::Profile& profile, const std::string& profileName, + std::string assetBasePath, std::string profileBasePath, + const std::vector& profilesReadOnly, QWidget* parent); /** * Gets the status of the save when the window is closed; was the file saved? @@ -86,7 +77,7 @@ public: */ void keyPressEvent(QKeyEvent* evt); -public slots: +private slots: void duplicateProfile(); void openMeta(); void openProperties(); @@ -102,16 +93,14 @@ public slots: void approved(); private: + void createWidgets(const std::string& profileName); void initSummaryTextForEachCategory(); - std::string summarizeAssets(); - std::string summarizeProperties(); - std::string summarizeKeybindings(); - bool isReadOnly(std::string profileToSave); openspace::Profile& _profile; - const std::string _reportedAssets; + const std::string _assetBasePath; + const std::string _profileBasePath; bool _saveSelected = false; - std::vector _readOnlyProfiles; + const std::vector& _readOnlyProfiles; QLineEdit* _profileEdit = nullptr; QLabel* _modulesLabel = nullptr; diff --git a/apps/OpenSpace/ext/launcher/include/profile/propertiesdialog.h b/apps/OpenSpace/ext/launcher/include/profile/propertiesdialog.h index 0aab138fd3..ac13ed474e 100644 --- a/apps/OpenSpace/ext/launcher/include/profile/propertiesdialog.h +++ b/apps/OpenSpace/ext/launcher/include/profile/propertiesdialog.h @@ -42,9 +42,9 @@ public: /** * Constructor for properties class * - * \param imported The #openspace::Profile object containing all data of the - * new or imported profile. - * \param parent Pointer to parent Qt widget (optional) + * \param profile The #openspace::Profile object containing all data of the + * new or imported profile. + * \param parent Pointer to parent Qt widget */ PropertiesDialog(openspace::Profile& profile, QWidget* parent); @@ -53,9 +53,9 @@ public: * * \param evt #QKeyEvent object for the key press event */ - void keyPressEvent(QKeyEvent *evt); + void keyPressEvent(QKeyEvent* evt); -public slots: +private slots: void listItemSelected(); void listItemAdded(); void listItemRemove(); @@ -65,6 +65,8 @@ public slots: void parseSelections(); private: + void createWidgets(); + QString createOneLineSummary(openspace::Profile::Property p); void transitionFromEditMode(); void editBoxDisabled(bool disabled); diff --git a/apps/OpenSpace/ext/launcher/include/profile/timedialog.h b/apps/OpenSpace/ext/launcher/include/profile/timedialog.h index 9dd5bd222f..9a0a6c4421 100644 --- a/apps/OpenSpace/ext/launcher/include/profile/timedialog.h +++ b/apps/OpenSpace/ext/launcher/include/profile/timedialog.h @@ -40,18 +40,20 @@ public: /** * Constructor for ostime class * - * \param imported The #openspace::Profile object containing all data of the - * new or imported profile. - * \param parent Pointer to parent Qt widget (optional) + * \param profile The #openspace::Profile object containing all data of the + * new or imported profile. + * \param parent Pointer to parent Qt widget */ TimeDialog(openspace::Profile& profile, QWidget* parent); -public slots: +private slots: void enableAccordingToType(int); void approved(); private: + void createWidgets(); void enableFormatForAbsolute(bool enableAbs); + openspace::Profile& _profile; openspace::Profile::Time _data; bool _initializedAsAbsolute = true; diff --git a/apps/OpenSpace/ext/launcher/src/filesystemaccess.cpp b/apps/OpenSpace/ext/launcher/src/filesystemaccess.cpp index bf5e760174..f0a98ce58d 100644 --- a/apps/OpenSpace/ext/launcher/src/filesystemaccess.cpp +++ b/apps/OpenSpace/ext/launcher/src/filesystemaccess.cpp @@ -33,8 +33,8 @@ FileSystemAccess::FileSystemAccess(std::string fileExtension, , _useCheckboxes(useCheckboxes) {} -std::string FileSystemAccess::useQtFileSystemModelToTraverseDir(QString dir) { - _filesystemModel.setRootPath(dir); +std::string FileSystemAccess::useQtFileSystemModelToTraverseDir(std::string dir) { + _filesystemModel.setRootPath(QString::fromStdString(dir)); QModelIndex index = _filesystemModel.index(_filesystemModel.rootPath()); QFileInfo fileInfo = _filesystemModel.fileInfo(index); std::vector dirsNested; diff --git a/apps/OpenSpace/ext/launcher/src/launcherwindow.cpp b/apps/OpenSpace/ext/launcher/src/launcherwindow.cpp index c957289c09..6d1e775849 100644 --- a/apps/OpenSpace/ext/launcher/src/launcherwindow.cpp +++ b/apps/OpenSpace/ext/launcher/src/launcherwindow.cpp @@ -22,19 +22,22 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include #include "launcherwindow.h" -#include -#include -#include "filesystemaccess.h" + +#include "profile/profileedit.h" + #include -#include -#include -#include -#include -#include +#include #include +#include +#include +#include #include +#include +#include +#include + +using namespace openspace; namespace { constexpr const int ScreenWidth = 480; @@ -68,29 +71,82 @@ namespace { LeftRuler, TopRuler + 380, SmallItemWidth, SmallItemHeight ); } // geometry + + std::optional loadProfileFromFile(QWidget* parent, std::string filename) { + std::ifstream inFile; + try { + inFile.open(filename, std::ifstream::in); + } + catch (const std::ifstream::failure& e) { + throw ghoul::RuntimeError(fmt::format( + "Exception opening {} profile for read: {}", filename, e.what() + )); + } + std::string content; + std::string line; + while (std::getline(inFile, line)) { + content += line; + } + try { + return Profile(content); + } + catch (const Profile::ParsingError& e) { + QMessageBox::critical( + parent, + "Exception", + QString::fromStdString(fmt::format( + "ParsingError exception in {}: {}, {}", + filename, e.component, e.message + )) + ); + return std::nullopt; + } + catch (const ghoul::RuntimeError& e) { + QMessageBox::critical( + parent, + "Exception", + QString::fromStdString(fmt::format( + "RuntimeError exception in {}, component {}: {}", + filename, e.component, e.message + )) + ); + return std::nullopt; + } + } + + void saveProfile(QWidget* parent, const std::string& path, const Profile& p) { + std::ofstream outFile; + try { + outFile.open(path, std::ofstream::out); + outFile << p.serialize(); + } + catch (const std::ofstream::failure& e) { + QMessageBox::critical( + parent, + "Exception", + QString::fromStdString(fmt::format( + "Error writing data to file: {} ({})", path, e.what() + )) + ); + } + } } // namespace using namespace openspace; -LauncherWindow::LauncherWindow(std::string basePath, bool profileEnabled, - configuration::Configuration& globalConfig, +LauncherWindow::LauncherWindow(bool profileEnabled, + const configuration::Configuration& globalConfig, bool sgctConfigEnabled, std::string sgctConfigName, QWidget* parent) : QMainWindow(parent) - , _fileAccessProfiles(".profile", { "./" }, true, false) - , _fileAccessWinConfigs(".xml", { "./" }, true, false) - , _filesystemAccess( - ".asset", { "scene", "global", "customization", "examples", "util" }, true, true - ) - , _basePath(QString::fromStdString(basePath)) - , _profileChangeAllowed(profileEnabled) - , _sgctConfigChangeAllowed(sgctConfigEnabled) - , _globalConfig(globalConfig) + , _assetPath(absPath(globalConfig.pathTokens.at("ASSETS")) + '/') + , _configPath(absPath(globalConfig.pathTokens.at("CONFIG")) + '/') + , _profilePath(absPath(globalConfig.pathTokens.at("PROFILES")) + '/') + , _readOnlyProfiles(globalConfig.readOnlyProfiles) { Q_INIT_RESOURCE(resources); qInstallMessageHandler( - // Now that the log is enabled and available, we can pipe all Qt messages to that [](QtMsgType type, const QMessageLogContext&, const QString& msg) { if (type == QtCriticalMsg || type == QtFatalMsg || type == QtSystemMsg) { std::cerr << msg.toStdString() << std::endl; @@ -109,11 +165,33 @@ LauncherWindow::LauncherWindow(std::string basePath, bool profileEnabled, setStyleSheet(styleSheet); } + setCentralWidget(createCentralWidget()); - QWidget* centralWidget = new QWidget(this); - QLabel* backgroundImage = new QLabel(centralWidget); - backgroundImage->setGeometry(geometry::BackgroundImage); + populateProfilesList(globalConfig.profile); + _profileBox->setEnabled(profileEnabled); + + populateWindowConfigsList(sgctConfigName); + _windowConfigBox->setEnabled(sgctConfigEnabled); + + + std::string p = absPath(globalConfig.pathTokens.at("SYNC") + "/http/launcher_images"); + if (std::filesystem::exists(p)) { + try { + setBackgroundImage(p); + } + catch (const std::exception& e) { + std::cerr << "Error occurrred while reading background images: " << e.what(); + } + } +} + +QWidget* LauncherWindow::createCentralWidget() { + QWidget* centralWidget = new QWidget; + + _backgroundImage = new QLabel(centralWidget); + _backgroundImage->setGeometry(geometry::BackgroundImage); + _backgroundImage->setPixmap(QPixmap(":/images/launcher-background.png")); QLabel* logoImage = new QLabel(centralWidget); logoImage->setObjectName("clear"); @@ -139,227 +217,164 @@ LauncherWindow::LauncherWindow(std::string basePath, bool profileEnabled, _windowConfigBox->setGeometry(geometry::WindowConfigBox); QPushButton* startButton = new QPushButton("START", centralWidget); - connect(startButton, &QPushButton::released, this, &LauncherWindow::startOpenSpace); + connect( + startButton, &QPushButton::released, + [this]() { + _shouldLaunch = true; + close(); + } + ); startButton->setObjectName("large"); startButton->setGeometry(geometry::StartButton); startButton->setCursor(Qt::PointingHandCursor); - + QPushButton* newButton = new QPushButton("New", centralWidget); - connect(newButton, &QPushButton::released, this, &LauncherWindow::openWindowNew); + connect( + newButton, &QPushButton::released, + [this]() { + openProfileEditor(""); + } + ); newButton->setObjectName("small"); newButton->setGeometry(geometry::NewButton); newButton->setCursor(Qt::PointingHandCursor); QPushButton* editButton = new QPushButton("Edit", centralWidget); - connect(editButton, &QPushButton::released, this, &LauncherWindow::openWindowEdit); + connect( + editButton, &QPushButton::released, + [this]() { + const std::string selection = _profileBox->currentText().toStdString(); + openProfileEditor(selection); + } + ); editButton->setObjectName("small"); editButton->setGeometry(geometry::EditButton); editButton->setCursor(Qt::PointingHandCursor); - setCentralWidget(centralWidget); + return centralWidget; +} +void LauncherWindow::setBackgroundImage(const std::string& syncPath) { + namespace fs = std::filesystem; + // First, we iterate through all folders in the launcher_images sync folder and we get + // the folder with the highest number + struct { + fs::directory_entry path; + int version = -1; + } latest; + for (const fs::directory_entry& p : fs::directory_iterator(syncPath)) { + if (!p.is_directory()) { + continue; + } + const std::string versionStr = p.path().stem().string(); + // All folder names in the sync folder should only be a digit, so we should be + // find to just convert it here + const int version = std::stoi(versionStr); - _reportAssetsInFilesystem = _filesystemAccess.useQtFileSystemModelToTraverseDir( - QString::fromStdString(basePath) + "/data/assets" - ); - populateProfilesList(globalConfig.profile); - - _profileBox->setEnabled(_profileChangeAllowed); - - populateWindowConfigsList(sgctConfigName); - _windowConfigBox->setEnabled(_sgctConfigChangeAllowed); - _fullyConfiguredViaCliArgs = (!profileEnabled && !sgctConfigEnabled); - - bool hasSyncFiles = false; - QString syncFilePath = QString::fromStdString( - globalConfig.pathTokens["SYNC"] + "/http/launcher_images/1/profile1.png" - ); - QFileInfo check_file(syncFilePath); - // check if file exists and if yes: Is it really a file and no directory? - if (check_file.exists() && check_file.isFile()) { - hasSyncFiles = true; + if (version > latest.version) { + latest.version = version; + latest.path = p; + } } - QString filename; - QString bgpath; - if (hasSyncFiles) { - std::random_device rd; - std::mt19937 rng(rd()); - std::uniform_int_distribution uni(0, 4); - int random_integer = uni(rng); - filename = QString::fromStdString( - "/http/launcher_images/1/profile" + std::to_string(random_integer) + ".png" - ); - bgpath = QString::fromStdString(globalConfig.pathTokens["SYNC"]) + filename; - } - else { - bgpath = QString::fromStdString(":/images/launcher-background.png"); + if (latest.version == -1) { + // The sync folder existed, but nothing was in there. Kinda weird, but still + return; } - backgroundImage->setPixmap(QPixmap(bgpath)); + // Now we know which folder to use, we will pick an image at random + std::vector files; + for (const fs::directory_entry& p : fs::directory_iterator(latest.path)) { + files.push_back(p.path().string()); + } + std::random_device rd; + std::mt19937 g(rd()); + std::shuffle(files.begin(), files.end(), g); + // We know there has to be at least one folder, so it's fine to just pick the first + std::string image = files.front(); + _backgroundImage->setPixmap(QPixmap(QString::fromStdString(image))); } void LauncherWindow::populateProfilesList(std::string preset) { + namespace fs = std::filesystem; + _profileBox->clear(); - std::string reportProfiles = _fileAccessProfiles.useQtFileSystemModelToTraverseDir( - _basePath + "/data/profiles" - ); - std::stringstream instream(reportProfiles); - std::string iline; - QStringList profilesListLine; - while (std::getline(instream, iline)) { - if (_profileBox->findText(QString::fromStdString(iline)) == -1) { - _profileBox->addItem(QString::fromStdString(iline)); + + // Add all the files with the .profile extension to the dropdown + for (const fs::directory_entry& p : fs::directory_iterator(_profilePath)) { + if (p.path().extension() != ".profile") { + continue; } + _profileBox->addItem(QString::fromStdString(p.path().stem().string())); } - if (preset.length() > 0) { - int presetMatchIdx = _profileBox->findText(QString::fromStdString(preset)); - if (presetMatchIdx != -1) { - _profileBox->setCurrentIndex(presetMatchIdx); - } + + // Try to find the requested profile and set it as the current one + const int idx = _profileBox->findText(QString::fromStdString(std::move(preset))); + if (idx != -1) { + _profileBox->setCurrentIndex(idx); + } } void LauncherWindow::populateWindowConfigsList(std::string preset) { - std::string reportConfigs = _fileAccessWinConfigs.useQtFileSystemModelToTraverseDir( - _basePath + "/config" - ); - std::stringstream instream(reportConfigs); - std::string iline; - QStringList windowConfigsListLine; - while (std::getline(instream, iline)) { - windowConfigsListLine << QString::fromStdString(iline); + namespace fs = std::filesystem; + + _windowConfigBox->clear(); + // Add all the files with the .xml extension to the dropdown + for (const fs::directory_entry& p : fs::directory_iterator(_configPath)) { + if (p.path().extension() != ".xml") { + continue; + } + _windowConfigBox->addItem(QString::fromStdString(p.path().stem().string())); } - _windowConfigBox->addItems(windowConfigsListLine); - if (preset.length() > 0) { - int presetMatchIdx = _windowConfigBox->findText(QString::fromStdString(preset)); - if (presetMatchIdx != -1) { - _windowConfigBox->setCurrentIndex(presetMatchIdx); - } - else { - _windowConfigBox->addItem(QString::fromStdString(preset)); - _windowConfigBox->setCurrentIndex(_windowConfigBox->count() - 1); - } + + // Try to find the requested configuration file and set it as the current one. As we + // have support for function-generated configuration files that will not be in the + // list we need to add a preset that doesn't exist a file for + const int idx = _windowConfigBox->findText(QString::fromStdString(std::move(preset))); + if (idx != -1) { + _windowConfigBox->setCurrentIndex(idx); + } + else { + // Add the requested preset at the top + _windowConfigBox->insertItem(0, QString::fromStdString(preset)); + _windowConfigBox->setCurrentIndex(0); } } -void LauncherWindow::openWindowNew() { - std::string initialProfileSelection = _profileBox->currentText().toStdString(); - Profile profile; - ProfileEdit editor( - profile, - _reportAssetsInFilesystem, - _globalConfig.readOnlyProfiles, - this - ); +void LauncherWindow::openProfileEditor(const std::string& profile) { + std::optional p; + if (profile.empty()) { + // If the requested profile is the empty string, then we want to create a new one + + p = Profile(); + } + else { + // Otherwise, we want to load that profile + + std::string fullProfilePath = _profilePath + profile + ".profile"; + p = loadProfileFromFile(this, fullProfilePath); + if (!p.has_value()) { + return; + } + } + + ProfileEdit editor(*p, profile, _assetPath, _profilePath, _readOnlyProfiles, this); editor.exec(); if (editor.wasSaved()) { - std::string saveProfilePath = _basePath.toStdString(); - saveProfilePath += "/data/profiles/"; - saveProfilePath += editor.specifiedFilename() + ".profile"; - saveProfileToFile(saveProfilePath, profile); + const std::string path = _profilePath + editor.specifiedFilename() + ".profile"; + saveProfile(this, path, *p); populateProfilesList(editor.specifiedFilename()); } else { - populateProfilesList(initialProfileSelection); - } -} - -void LauncherWindow::openWindowEdit() { - std::string initialProfileSelection = _profileBox->currentText().toStdString(); - std::string profilePath = _basePath.toStdString() + "/data/profiles/"; - int selectedProfileIdx = _profileBox->currentIndex(); - QString profileToSet = _profileBox->itemText(selectedProfileIdx); - std::string editProfilePath = profilePath + profileToSet.toStdString() + ".profile"; - - std::optional profile = loadProfileFromFile(editProfilePath); - if (profile.has_value()) { - ProfileEdit editor( - *profile, - _reportAssetsInFilesystem, - _globalConfig.readOnlyProfiles, - this - ); - editor.setProfileName(profileToSet); - editor.exec(); - if (editor.wasSaved()) { - profilePath += editor.specifiedFilename() + ".profile"; - saveProfileToFile(profilePath, *profile); - populateProfilesList(editor.specifiedFilename()); - } - else { - populateProfilesList(initialProfileSelection); - } - } -} - -void LauncherWindow::saveProfileToFile(const std::string& path, const Profile& p) { - std::ofstream outFile; - try { - outFile.open(path, std::ofstream::out); - outFile << p.serialize(); - } - catch (const std::ofstream::failure& e) { - QMessageBox::critical( - this, - "Exception", - QString::fromStdString(fmt::format( - "Error writing data to file: {} ({})", path, e.what() - )) - ); - } -} - -std::optional LauncherWindow::loadProfileFromFile(std::string filename) { - std::ifstream inFile; - try { - inFile.open(filename, std::ifstream::in); - } - catch (const std::ifstream::failure& e) { - throw ghoul::RuntimeError(fmt::format( - "Exception opening {} profile for read: ({})", - filename, - e.what() - )); - } - std::string content; - std::string line; - while (std::getline(inFile, line)) { - content += line; - } - try { - return Profile(content); - } - catch (const Profile::ParsingError& e) { - QMessageBox::critical( - this, - "Exception", - QString::fromStdString(fmt::format( - "ParsingError exception in {}: {}, {}", filename, e.component, e.message - )) - ); - return std::nullopt; - } - catch (const ghoul::RuntimeError& e) { - QMessageBox::critical( - this, - "Exception", - QString::fromStdString(fmt::format( - "RuntimeError exception in {}, component {}: {}", - filename, e.component, e.message - )) - ); - return std::nullopt; + const std::string current = _profileBox->currentText().toStdString(); + populateProfilesList(current); } } bool LauncherWindow::wasLaunchSelected() const { - return _launch; -} - -bool LauncherWindow::isFullyConfiguredFromCliArgs() const { - return _fullyConfiguredViaCliArgs; + return _shouldLaunch; } std::string LauncherWindow::selectedProfile() const { @@ -369,8 +384,3 @@ std::string LauncherWindow::selectedProfile() const { std::string LauncherWindow::selectedWindowConfig() const { return _windowConfigBox->currentText().toStdString(); } - -void LauncherWindow::startOpenSpace() { - _launch = true; - close(); -} diff --git a/apps/OpenSpace/ext/launcher/src/profile/additionalscriptsdialog.cpp b/apps/OpenSpace/ext/launcher/src/profile/additionalscriptsdialog.cpp index 4f13e52092..30a6ccec84 100644 --- a/apps/OpenSpace/ext/launcher/src/profile/additionalscriptsdialog.cpp +++ b/apps/OpenSpace/ext/launcher/src/profile/additionalscriptsdialog.cpp @@ -24,6 +24,7 @@ #include "profile/additionalscriptsdialog.h" +#include "profile/line.h" #include #include #include @@ -37,7 +38,18 @@ AdditionalScriptsDialog::AdditionalScriptsDialog(openspace::Profile& profile, , _profile(profile) { setWindowTitle("Additional Scripts"); + createWidgets(); + std::vector scripts = _profile.additionalScripts(); + std::string scriptText = std::accumulate( + scripts.begin(), scripts.end(), + std::string(), [](std::string lhs, std::string rhs) { return lhs + rhs + '\n'; } + ); + _textScripts->setText(QString::fromStdString(std::move(scriptText))); + _textScripts->moveCursor(QTextCursor::MoveOperation::End); +} + +void AdditionalScriptsDialog::createWidgets() { QBoxLayout* layout = new QVBoxLayout(this); { QLabel* heading = new QLabel("Additional Lua Scripts for Configuration"); @@ -49,12 +61,7 @@ AdditionalScriptsDialog::AdditionalScriptsDialog(openspace::Profile& profile, _textScripts->setAcceptRichText(false); layout->addWidget(_textScripts, 1); - { - QFrame* line = new QFrame; - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - layout->addWidget(line); - } + layout->addWidget(new Line); { QDialogButtonBox* buttons = new QDialogButtonBox; @@ -69,24 +76,16 @@ AdditionalScriptsDialog::AdditionalScriptsDialog(openspace::Profile& profile, ); layout->addWidget(buttons); } - - std::vector scripts = _profile.additionalScripts(); - std::string scpts = std::accumulate( - scripts.begin(), scripts.end(), - std::string(), [](std::string lhs, std::string rhs) { return lhs + rhs + '\n'; } - ); - _textScripts->setText(QString::fromStdString(std::move(scpts))); - _textScripts->moveCursor(QTextCursor::MoveOperation::End); } void AdditionalScriptsDialog::parseScript() { - std::vector tmpMultilineStringToVector; + std::vector additionalScripts; std::istringstream iss(_textScripts->toPlainText().toStdString()); while (!iss.eof()) { std::string s; - getline(iss, s); - tmpMultilineStringToVector.push_back(s); + std::getline(iss, s); + additionalScripts.push_back(std::move(s)); } - _profile.setAdditionalScripts(tmpMultilineStringToVector); + _profile.setAdditionalScripts(additionalScripts); accept(); } diff --git a/apps/OpenSpace/ext/launcher/src/profile/assetsdialog.cpp b/apps/OpenSpace/ext/launcher/src/profile/assetsdialog.cpp index 4cc42fb61a..e8d99b617c 100644 --- a/apps/OpenSpace/ext/launcher/src/profile/assetsdialog.cpp +++ b/apps/OpenSpace/ext/launcher/src/profile/assetsdialog.cpp @@ -24,6 +24,7 @@ #include "profile/assetsdialog.h" +#include "profile/line.h" #include #include #include @@ -58,8 +59,8 @@ namespace { void traverseToFindFilesystemMatch(AssetTreeModel& model, QModelIndex parent, int nRows, const std::string& path) { - size_t slash = path.find_first_of('/', 0); - bool endOfPath = (slash == std::string::npos); + const size_t slash = path.find_first_of('/', 0); + const bool endOfPath = (slash == std::string::npos); std::string firstDir = endOfPath ? "" : path.substr(0, slash); if (!endOfPath) { @@ -71,7 +72,7 @@ namespace { QModelIndex idx = model.index(r, 0, parent); std::string assetName = model.name(idx).toStdString(); if (!model.isAsset(idx)) { - if (firstDir.compare(assetName) == 0) { + if (firstDir == assetName) { int nChildRows = model.childCount(idx); foundDirMatch = true; traverseToFindFilesystemMatch(model, idx, nChildRows, nextPath); @@ -83,7 +84,7 @@ namespace { } } if (!foundDirMatch) { - //Insert missing directory here with name and exists=false condition + // Insert missing directory here with name and exists=false condition model.assetItem(parent)->insertChildren(nRows, 1, 3); QModelIndex idx = model.index(nRows, 0, parent); model.setName(idx, QString::fromStdString(firstDir)); @@ -97,14 +98,14 @@ namespace { QModelIndex idx = model.index(r, 0, parent); std::string assetName = model.name(idx).toStdString(); - if (path.compare(assetName) == 0) { + if (path == assetName) { foundFileMatch = true; model.setChecked(idx, true); break; } } if (!foundFileMatch) { - //Insert missing file here with name and exists=false condition + // Insert missing file here with name and exists=false condition model.assetItem(parent)->insertChildren(nRows, 1, 3); QModelIndex idx = model.index(nRows, 0, parent); model.setName(idx, QString::fromStdString(path)); @@ -115,13 +116,17 @@ namespace { } } // namespace -AssetsDialog::AssetsDialog(openspace::Profile& profile, const std::string reportAssets, +AssetsDialog::AssetsDialog(openspace::Profile& profile, const std::string& assetBasePath, QWidget* parent) : QDialog(parent) , _profile(profile) { setWindowTitle("Assets"); + _assetTreeModel.importModelData(assetBasePath); + createWidgets(); +} +void AssetsDialog::createWidgets() { QBoxLayout* layout = new QVBoxLayout(this); { QLabel* heading = new QLabel("Select assets from /data/assets"); @@ -129,8 +134,6 @@ AssetsDialog::AssetsDialog(openspace::Profile& profile, const std::string report layout->addWidget(heading); } { - _assetTreeModel.importModelData(reportAssets); - _assetTree = new QTreeView; _assetTree->setToolTip( "Expand arrow entries to browse assets in this OpenSpace installation. " @@ -177,12 +180,8 @@ AssetsDialog::AssetsDialog(openspace::Profile& profile, const std::string report _summary->setText(createTextSummary()); layout->addWidget(_summary); } - { - QFrame* line = new QFrame; - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - layout->addWidget(line); - } + + layout->addWidget(new Line); { QDialogButtonBox* buttons = new QDialogButtonBox; @@ -210,10 +209,13 @@ QString AssetsDialog::createTextSummary() { QString summary; for (int i = 0; i < summaryItems.size(); ++i) { bool existsInFilesystem = summaryItems.at(i)->doesExistInFilesystem(); - std::string s = fmt::format("{}
", - (existsInFilesystem ? "black" : "red"), - summaryPaths.at(i) - ); + + constexpr const char* ExistsFormat = "{}
"; + constexpr const char* NotExistsFormat = "{}
"; + + std::string s = existsInFilesystem ? + fmt::format("{}
", summaryPaths.at(i)) : + fmt::format("{}
", summaryPaths.at(i)); summary += QString::fromStdString(s); } return summary; diff --git a/apps/OpenSpace/ext/launcher/src/profile/assettreeitem.cpp b/apps/OpenSpace/ext/launcher/src/profile/assettreeitem.cpp index f02b57ce3d..199a755fb1 100644 --- a/apps/OpenSpace/ext/launcher/src/profile/assettreeitem.cpp +++ b/apps/OpenSpace/ext/launcher/src/profile/assettreeitem.cpp @@ -30,7 +30,9 @@ AssetTreeItem::AssetTreeItem(const std::vector& data, AssetTreeItem* p {} AssetTreeItem::~AssetTreeItem() { - qDeleteAll(_childItems); + for (AssetTreeItem* item : _childItems) { + delete item; + } } AssetTreeItem* AssetTreeItem::child(int row) { @@ -48,12 +50,12 @@ int AssetTreeItem::childCount() const { int AssetTreeItem::row() const { if (_parentItem) { - auto it = std::find( - _parentItem->_childItems.begin(), - _parentItem->_childItems.end(), + const auto it = std::find( + _parentItem->_childItems.cbegin(), + _parentItem->_childItems.cend(), this ); - return std::distance(_parentItem->_childItems.begin(), it); + return std::distance(_parentItem->_childItems.cbegin(), it); } else { return 0; diff --git a/apps/OpenSpace/ext/launcher/src/profile/assettreemodel.cpp b/apps/OpenSpace/ext/launcher/src/profile/assettreemodel.cpp index 4b1573c5bd..16b7ba2b77 100644 --- a/apps/OpenSpace/ext/launcher/src/profile/assettreemodel.cpp +++ b/apps/OpenSpace/ext/launcher/src/profile/assettreemodel.cpp @@ -24,6 +24,7 @@ #include "profile/assettreeitem.h" #include "profile/assettreemodel.h" +#include "filesystemaccess.h" #include #include @@ -57,7 +58,8 @@ namespace { } bool importGetNextLine(ImportElement& elem, std::istringstream& iss) { - bool ok = std::getline(iss, elem.line) ? true : false; + std::getline(iss, elem.line); + const bool ok = iss.good(); if (!ok) { elem.line = ""; elem.level = -1; @@ -143,8 +145,16 @@ AssetTreeModel::AssetTreeModel(QObject* parent) ); } -void AssetTreeModel::importModelData(const std::string& contents) { - std::istringstream iss(contents); +void AssetTreeModel::importModelData(const std::string& assetBasePath) { + FileSystemAccess assets( + ".asset", + { "scene", "global", "customization", "examples", "util" }, + true, + true + ); + std::string assetList = assets.useQtFileSystemModelToTraverseDir(assetBasePath); + + std::istringstream iss(assetList); ImportElement rootElem = { "", 0, false }; if (importGetNextLine(rootElem, iss)) { @@ -164,8 +174,8 @@ AssetTreeItem* AssetTreeModel::getItem(const QModelIndex& index) const { bool AssetTreeModel::isChecked(QModelIndex& index) const { AssetTreeItem* item = getItem(index); - int checked = item->data(1).toInt(); - return checked == Qt::Checked; + const int isChecked = item->data(1).toInt(); + return isChecked == Qt::Checked; } bool AssetTreeModel::isAsset(QModelIndex& index) const { diff --git a/apps/OpenSpace/ext/launcher/src/profile/cameradialog.cpp b/apps/OpenSpace/ext/launcher/src/profile/cameradialog.cpp index 1689dffc31..2137770c36 100644 --- a/apps/OpenSpace/ext/launcher/src/profile/cameradialog.cpp +++ b/apps/OpenSpace/ext/launcher/src/profile/cameradialog.cpp @@ -24,6 +24,7 @@ #include "profile/cameradialog.h" +#include "profile/line.h" #include #include #include @@ -60,37 +61,7 @@ CameraDialog::CameraDialog(openspace::Profile& profile, QWidget *parent) , _profile(profile) { setWindowTitle("Set Camera Position"); - - QBoxLayout* layout = new QVBoxLayout(this); - _tabWidget = new QTabWidget; - connect(_tabWidget, &QTabWidget::tabBarClicked, this, &CameraDialog::tabSelect); - _tabWidget->addTab(createNavStateWidget(), "Navigation State"); - _tabWidget->addTab(createGeoWidget(), "Geo State"); - layout->addWidget(_tabWidget); - - { - QFrame* line = new QFrame; - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - layout->addWidget(line); - } - - { - QBoxLayout* footerLayout = new QHBoxLayout; - - _errorMsg = new QLabel; - _errorMsg->setObjectName("error-message"); - _errorMsg->setWordWrap(true); - footerLayout->addWidget(_errorMsg); - - QDialogButtonBox* buttons = new QDialogButtonBox; - buttons->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Cancel); - connect(buttons, &QDialogButtonBox::accepted, this, &CameraDialog::approved); - connect(buttons, &QDialogButtonBox::rejected, this, &CameraDialog::reject); - footerLayout->addWidget(buttons); - - layout->addLayout(footerLayout); - } + createWidgets(); if (_profile.camera().has_value()) { openspace::Profile::CameraType type = *_profile.camera(); @@ -163,6 +134,34 @@ CameraDialog::CameraDialog(openspace::Profile& profile, QWidget *parent) } } +void CameraDialog::createWidgets() { + QBoxLayout* layout = new QVBoxLayout(this); + _tabWidget = new QTabWidget; + connect(_tabWidget, &QTabWidget::tabBarClicked, this, &CameraDialog::tabSelect); + _tabWidget->addTab(createNavStateWidget(), "Navigation State"); + _tabWidget->addTab(createGeoWidget(), "Geo State"); + layout->addWidget(_tabWidget); + + layout->addWidget(new Line); + + { + QBoxLayout* footerLayout = new QHBoxLayout; + + _errorMsg = new QLabel; + _errorMsg->setObjectName("error-message"); + _errorMsg->setWordWrap(true); + footerLayout->addWidget(_errorMsg); + + QDialogButtonBox* buttons = new QDialogButtonBox; + buttons->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Cancel); + connect(buttons, &QDialogButtonBox::accepted, this, &CameraDialog::approved); + connect(buttons, &QDialogButtonBox::rejected, this, &CameraDialog::reject); + footerLayout->addWidget(buttons); + + layout->addLayout(footerLayout); + } +} + QWidget* CameraDialog::createNavStateWidget() { QWidget* box = new QWidget; QGridLayout* layout = new QGridLayout(box); @@ -175,14 +174,16 @@ QWidget* CameraDialog::createNavStateWidget() { layout->addWidget(new QLabel("Aim:"), 1, 0); _navState.aim = new QLineEdit; _navState.aim->setToolTip( - "[OPTIONAL] If specified, camera will be aimed at this node while keeping the " - "anchor node in the same view location" + "If specified, camera will be aimed at this node while keeping the anchor node " + "in the same view location" ); + _navState.aim->setPlaceholderText("optional"); layout->addWidget(_navState.aim, 1, 1); layout->addWidget(new QLabel("Reference Frame:"), 2, 0); _navState.refFrame = new QLineEdit; - _navState.refFrame->setToolTip("[OPTIONAL] Camera location in reference to this frame"); + _navState.refFrame->setToolTip("Camera location in reference to this frame"); + _navState.refFrame->setPlaceholderText("optional"); layout->addWidget(_navState.refFrame, 2, 1); layout->addWidget(new QLabel("Position:"), 3, 0); @@ -218,19 +219,22 @@ QWidget* CameraDialog::createNavStateWidget() { upLayout->addWidget(new QLabel("X")); _navState.upX = new QLineEdit; _navState.upX->setValidator(new QDoubleValidator); - _navState.upX->setToolTip("[OPTIONAL] Camera up vector (x)"); + _navState.upX->setToolTip("Camera up vector (x)"); + _navState.upX->setPlaceholderText("semioptional"); upLayout->addWidget(_navState.upX); upLayout->addWidget(new QLabel("Y")); _navState.upY = new QLineEdit; _navState.upY->setValidator(new QDoubleValidator); - _navState.upY->setToolTip("[OPTIONAL] Camera up vector (y)"); + _navState.upY->setToolTip("Camera up vector (y)"); + _navState.upY->setPlaceholderText("semioptional"); upLayout->addWidget(_navState.upY); upLayout->addWidget(new QLabel("Z")); _navState.upZ = new QLineEdit; _navState.upZ->setValidator(new QDoubleValidator); - _navState.upZ->setToolTip("[OPTIONAL] Camera up vector (z)"); + _navState.upZ->setToolTip("Camera up vector (z)"); + _navState.upZ->setPlaceholderText("semioptional"); upLayout->addWidget(_navState.upZ); layout->addWidget(upBox, 4, 1); } @@ -238,13 +242,15 @@ QWidget* CameraDialog::createNavStateWidget() { layout->addWidget(new QLabel("Yaw angle:"), 5, 0); _navState.yaw = new QLineEdit; _navState.yaw->setValidator(new QDoubleValidator); - _navState.yaw->setToolTip("[OPTIONAL] yaw angle +/- 360 degrees"); + _navState.yaw->setToolTip("Yaw angle +/- 360 degrees"); + _navState.yaw->setPlaceholderText("optional"); layout->addWidget(_navState.yaw, 5, 1); layout->addWidget(new QLabel("Pitch angle:"), 6, 0); _navState.pitch = new QLineEdit; _navState.pitch->setValidator(new QDoubleValidator); - _navState.pitch->setToolTip("[OPTIONAL] pitch angle +/- 360 degrees"); + _navState.pitch->setToolTip("Pitch angle +/- 360 degrees"); + _navState.pitch->setPlaceholderText("optional"); layout->addWidget(_navState.pitch, 6, 1); return box; @@ -274,8 +280,8 @@ QWidget* CameraDialog::createGeoWidget() { layout->addWidget(new QLabel("Altitude"), 3, 0); _geoState.altitude = new QLineEdit; _geoState.altitude->setValidator(new QDoubleValidator); - _geoState.altitude->setToolTip("[OPTIONAL] Altitude of camera (meters)"); - //altitude->setPlaceholderText("optional"); + _geoState.altitude->setToolTip("Altitude of camera (meters)"); + _geoState.altitude->setPlaceholderText("optional"); layout->addWidget(_geoState.altitude, 3, 1); return box; @@ -354,7 +360,7 @@ bool CameraDialog::areRequiredFormsFilledAndValid() { void CameraDialog::addErrorMsg(QString errorDescription) { QString contents = _errorMsg->text(); - if (contents.length() > 0) { + if (!contents.isEmpty()) { contents += ", "; } contents += errorDescription; diff --git a/apps/OpenSpace/ext/launcher/src/profile/deltatimesdialog.cpp b/apps/OpenSpace/ext/launcher/src/profile/deltatimesdialog.cpp index 6cea3ec749..8beac8fb1d 100644 --- a/apps/OpenSpace/ext/launcher/src/profile/deltatimesdialog.cpp +++ b/apps/OpenSpace/ext/launcher/src/profile/deltatimesdialog.cpp @@ -24,6 +24,7 @@ #include "profile/deltatimesdialog.h" +#include "profile/line.h" #include #include #include @@ -76,7 +77,6 @@ namespace { } return checkForTimeDescription(i, value); } - } // namespace DeltaTimesDialog::DeltaTimesDialog(openspace::Profile& profile, QWidget *parent) @@ -84,6 +84,19 @@ DeltaTimesDialog::DeltaTimesDialog(openspace::Profile& profile, QWidget *parent) , _profile(profile) { setWindowTitle("Simulation Time Increments"); + createWidgets(); + + _data = _profile.deltaTimes(); + + for (size_t d = 0; d < _data.size(); ++d) { + std::string summary = createSummaryForDeltaTime(d, true); + _listWidget->addItem(new QListWidgetItem(QString::fromStdString(summary))); + } + + transitionEditMode(_listWidget->count() - 1, false); +} + +void DeltaTimesDialog::createWidgets() { QBoxLayout* layout = new QVBoxLayout(this); { _listWidget = new QListWidget; @@ -105,7 +118,7 @@ DeltaTimesDialog::DeltaTimesDialog(openspace::Profile& profile, QWidget *parent) ); buttonLayout->addWidget(_addButton); - _removeButton = new QPushButton("Remove LastEntry"); + _removeButton = new QPushButton("Remove Last Entry"); connect( _removeButton, &QPushButton::clicked, this, &DeltaTimesDialog::removeDeltaTimeValue @@ -130,7 +143,7 @@ DeltaTimesDialog::DeltaTimesDialog(openspace::Profile& profile, QWidget *parent) box->addWidget(_value); layout->addLayout(box); } - + { QBoxLayout* box = new QHBoxLayout; _saveButton = new QPushButton("Save"); @@ -150,12 +163,7 @@ DeltaTimesDialog::DeltaTimesDialog(openspace::Profile& profile, QWidget *parent) box->addStretch(); layout->addLayout(box); } - { - QFrame* line = new QFrame; - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - layout->addWidget(line); - } + layout->addWidget(new Line); { QBoxLayout* footer = new QHBoxLayout; _errorMsg = new QLabel; @@ -173,16 +181,6 @@ DeltaTimesDialog::DeltaTimesDialog(openspace::Profile& profile, QWidget *parent) footer->addWidget(_buttonBox); layout->addLayout(footer); } - - - _data = _profile.deltaTimes(); - - for (size_t d = 0; d < _data.size(); ++d) { - std::string summary = createSummaryForDeltaTime(d, true); - _listWidget->addItem(new QListWidgetItem(QString::fromStdString(summary))); - } - - transitionEditMode(_listWidget->count() - 1, false); } std::string DeltaTimesDialog::createSummaryForDeltaTime(size_t idx, bool forListView) { @@ -205,9 +203,7 @@ std::string DeltaTimesDialog::createSummaryForDeltaTime(size_t idx, bool forList } if (forListView) { - s += "\t" + std::to_string(_data.at(idx)); - s += "\t"; - s += timeDescription(_data.at(idx)); + s += '\t' + std::to_string(_data.at(idx)) + '\t' + timeDescription(_data.at(idx)); } return s; } @@ -220,7 +216,7 @@ void DeltaTimesDialog::listItemSelected() { _listWidget->setCurrentRow(index); } - if (_data.size() > 0) { + if (!_data.empty()) { if (_data.at(index) == 0) { _seconds->clear(); } @@ -238,9 +234,7 @@ void DeltaTimesDialog::setLabelForKey(int index, bool editMode, std::string colo index = _data.size() - 1; } if (editMode) { - labelS += " '"; - labelS += createSummaryForDeltaTime(index, false); - labelS += "':"; + labelS += " '" + createSummaryForDeltaTime(index, false) + "':"; } _adjustLabel->setText(QString::fromStdString( "" + labelS + "" @@ -248,7 +242,7 @@ void DeltaTimesDialog::setLabelForKey(int index, bool editMode, std::string colo } void DeltaTimesDialog::valueChanged(const QString& text) { - if (_seconds->text() == "") { + if (_seconds->text().isEmpty()) { _errorMsg->setText(""); } else { @@ -297,21 +291,20 @@ void DeltaTimesDialog::addDeltaTimeValue() { } void DeltaTimesDialog::saveDeltaTimeValue() { - QListWidgetItem *item = _listWidget->currentItem(); + QListWidgetItem* item = _listWidget->currentItem(); if (item != nullptr) { int index = _listWidget->row(item); if (_data.size() > 0) { _data.at(index) = _seconds->text().toDouble(); std::string summary = createSummaryForDeltaTime(index, true); _listWidget->item(index)->setText(QString::fromStdString(summary)); - //setLabelForKey(index, true, "black"); transitionEditMode(index, false); _editModeNewItem = false; } } } -void DeltaTimesDialog::discardDeltaTimeValue(void) { +void DeltaTimesDialog::discardDeltaTimeValue() { listItemSelected(); transitionEditMode(_listWidget->count() - 1, false); if (_editModeNewItem && !_data.empty() && _data.back() == 0) { diff --git a/apps/OpenSpace/ext/launcher/src/profile/keybindingsdialog.cpp b/apps/OpenSpace/ext/launcher/src/profile/keybindingsdialog.cpp index 657e886efc..2201d728ba 100644 --- a/apps/OpenSpace/ext/launcher/src/profile/keybindingsdialog.cpp +++ b/apps/OpenSpace/ext/launcher/src/profile/keybindingsdialog.cpp @@ -24,6 +24,7 @@ #include "profile/keybindingsdialog.h" +#include "profile/line.h" #include #include #include @@ -38,9 +39,11 @@ #include #include +using namespace openspace; + namespace { - const openspace::Profile::Keybinding kBlank = { - { openspace::Key::Unknown, openspace::KeyModifier::NoModifier }, + const Profile::Keybinding BlankKey= { + { Key::Unknown, KeyModifier::NoModifier }, "", "", "", @@ -52,8 +55,7 @@ namespace { std::string newString; std::string::size_type found, last = 0; - while ((found = src.find(from, last)) != std::string::npos) - { + while ((found = src.find(from, last)) != std::string::npos) { newString.append(src, last, (found - last)); newString += to; last = found + from.length(); @@ -72,16 +74,16 @@ namespace { return s; } - std::string createOneLineSummary(openspace::Profile::Keybinding k) { + std::string createOneLineSummary(Profile::Keybinding k) { std::string summary; int keymod = static_cast(k.key.modifier); - if (keymod != static_cast(openspace::KeyModifier::NoModifier)) { - summary += openspace::KeyModifierNames.at(keymod) + " "; + if (keymod != static_cast(KeyModifier::NoModifier)) { + summary += KeyModifierNames.at(keymod) + " "; } int keyname = static_cast(k.key.key); - summary += openspace::KeyNames.at(keyname) + " "; + summary += KeyNames.at(keyname) + " "; summary += truncateString(k.name) + " ("; summary += truncateString(k.documentation) + ") @ "; summary += truncateString(k.guiPath) + " "; @@ -93,13 +95,18 @@ namespace { } // namespace -KeybindingsDialog::KeybindingsDialog(openspace::Profile& profile, QWidget *parent) +KeybindingsDialog::KeybindingsDialog(Profile& profile, QWidget *parent) : QDialog(parent) , _profile(profile) , _data(_profile.keybindings()) { setWindowTitle("Assign Keybindings"); + createWidgets(); + transitionFromEditMode(); +} + +void KeybindingsDialog::createWidgets() { QBoxLayout* layout = new QVBoxLayout(this); { _list = new QListWidget; @@ -136,12 +143,7 @@ KeybindingsDialog::KeybindingsDialog(openspace::Profile& profile, QWidget *paren box->addStretch(); layout->addLayout(box); } - { - QFrame* line = new QFrame; - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - layout->addWidget(line); - } + layout->addWidget(new Line); { QGridLayout* box = new QGridLayout; @@ -154,7 +156,7 @@ KeybindingsDialog::KeybindingsDialog(openspace::Profile& profile, QWidget *paren QStringList comboModKeysStringList; int modIdx = 0; - for (const std::pair& m : openspace::KeyModifierNames) { + for (const std::pair& m : KeyModifierNames) { comboModKeysStringList += QString::fromStdString(m.second); _mapModKeyComboBoxIndexToKeyValue.push_back(modIdx++); } @@ -168,9 +170,9 @@ KeybindingsDialog::KeybindingsDialog(openspace::Profile& profile, QWidget *paren _keyCombo->setToolTip("Key to press for this keybinding"); QStringList comboKeysStringList; - for (int i = 0; i < static_cast(openspace::Key::Last); ++i) { - if (openspace::KeyNames.find(i) != openspace::KeyNames.end()) { - comboKeysStringList += QString::fromStdString(openspace::KeyNames.at(i)); + for (int i = 0; i < static_cast(Key::Last); ++i) { + if (KeyNames.find(i) != KeyNames.end()) { + comboKeysStringList += QString::fromStdString(KeyNames.at(i)); // Create map to relate key combo box to integer value defined in Key _mapKeyComboBoxIndexToKeyValue.push_back(i); } @@ -243,12 +245,7 @@ KeybindingsDialog::KeybindingsDialog(openspace::Profile& profile, QWidget *paren box->addLayout(buttonBox, 8, 1, 1, 2); layout->addLayout(box); } - { - QFrame* line = new QFrame; - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - layout->addWidget(line); - } + layout->addWidget(new Line); { QBoxLayout* footerLayout = new QHBoxLayout; @@ -270,23 +267,21 @@ KeybindingsDialog::KeybindingsDialog(openspace::Profile& profile, QWidget *paren footerLayout->addWidget(_buttonBox); layout->addLayout(footerLayout); } - - transitionFromEditMode(); } -void KeybindingsDialog::listItemSelected(void) { +void KeybindingsDialog::listItemSelected() { QListWidgetItem *item = _list->currentItem(); int index = _list->row(item); if (_data.size() > 0) { - openspace::Profile::Keybinding& k = _data[index]; + Profile::Keybinding& k = _data[index]; const int modifierKey = indexInKeyMapping( _mapModKeyComboBoxIndexToKeyValue, static_cast(k.key.modifier) ); _keyModCombo->setCurrentIndex(modifierKey); - if (k.key.key == openspace::Key::Unknown) { + if (k.key.key == Key::Unknown) { _keyCombo->setCurrentIndex(0); } else { @@ -308,13 +303,13 @@ void KeybindingsDialog::listItemSelected(void) { } void KeybindingsDialog::keySelected(int index) { - const QString numKeyWarning = "Warning: using a number key may conflict with the " - "keybindings for simulation time increments. "; + const QString numKeyWarning = "Warning: Using a number key may conflict with the " + "keybindings for simulation time increments."; QString errorContents = _errorMsg->text(); bool alreadyContainsWarning = (errorContents.length() >= numKeyWarning.length() && errorContents.left(numKeyWarning.length()) == numKeyWarning); - if (_mapKeyComboBoxIndexToKeyValue[index] >= static_cast(openspace::Key::Num0) - && _mapKeyComboBoxIndexToKeyValue[index] <= static_cast(openspace::Key::Num9)) + if (_mapKeyComboBoxIndexToKeyValue[index] >= static_cast(Key::Num0) + && _mapKeyComboBoxIndexToKeyValue[index] <= static_cast(Key::Num9)) { if (!alreadyContainsWarning) { errorContents = numKeyWarning + errorContents; @@ -327,8 +322,8 @@ void KeybindingsDialog::keySelected(int index) { } int KeybindingsDialog::indexInKeyMapping(std::vector& mapVector, int keyInt) { - auto it = std::find(mapVector.begin(), mapVector.end(), keyInt); - return std::distance(mapVector.begin(), it); + const auto it = std::find(mapVector.cbegin(), mapVector.cend(), keyInt); + return std::distance(mapVector.cbegin(), it); } bool KeybindingsDialog::isLineEmpty(int index) { @@ -345,25 +340,14 @@ bool KeybindingsDialog::isLineEmpty(int index) { void KeybindingsDialog::listItemAdded() { int currentListSize = _list->count(); - //if ((currentListSize == 1) && (isLineEmpty(0))) { - //if (currentListSize == 0) { - // // Special case where list is "empty" but really has one line that is blank. - // // This is done because QListWidget does not seem to like having its sole - // // remaining item being removed. - // _data.at(0) = kBlank; - // _list->item(0)->setText(" (Enter details below & click 'Save')"); - // transitionToEditMode(); - // } - // else { - _data.push_back(kBlank); - _list->addItem(new QListWidgetItem(" (Enter details below & click 'Save')")); - //Scroll down to that blank line highlighted - _list->setCurrentRow(_list->count() - 1); - //} + _data.push_back(BlankKey); + _list->addItem(new QListWidgetItem(" (Enter details below & click 'Save')")); + // Scroll down to that blank line highlighted + _list->setCurrentRow(_list->count() - 1); // Blank-out the 2 text fields, set combo box to index 0 _keyModCombo->setCurrentIndex(static_cast(_data.back().key.modifier)); - if (_data.back().key.key == openspace::Key::Unknown) { + if (_data.back().key.key == Key::Unknown) { _keyCombo->setCurrentIndex(0); } else { @@ -379,7 +363,7 @@ void KeybindingsDialog::listItemAdded() { _editModeNewItem = true; } -void KeybindingsDialog::listItemSave(void) { +void KeybindingsDialog::listItemSave() { if (!areRequiredFormsFilled()) { return; } @@ -390,9 +374,9 @@ void KeybindingsDialog::listItemSave(void) { if (!_data.empty()) { int keyModIdx = _mapModKeyComboBoxIndexToKeyValue.at( _keyModCombo->currentIndex()); - _data[index].key.modifier = static_cast(keyModIdx); + _data[index].key.modifier = static_cast(keyModIdx); int keyIdx = _mapKeyComboBoxIndexToKeyValue.at(_keyCombo->currentIndex()); - _data[index].key.key = static_cast(keyIdx); + _data[index].key.key = static_cast(keyIdx); _data[index].name = _nameEdit->text().toStdString(); _data[index].guiPath = _guiPathEdit->text().toStdString(); _data[index].documentation = _documentationEdit->text().toStdString(); @@ -412,14 +396,14 @@ bool KeybindingsDialog::areRequiredFormsFilled() { requiredFormsFilled = false; } if (_nameEdit->text().length() == 0) { - if (errors.length() > 0) { + if (!errors.empty()) { errors += ", "; } errors += "Missing keybinding name"; requiredFormsFilled = false; } - if (_scriptEdit->toPlainText().length() == 0) { - if (errors.length() > 0) { + if (_scriptEdit->toPlainText().isEmpty()) { + if (!errors.empty()) { errors += ", "; } errors += "Missing script"; @@ -429,31 +413,31 @@ bool KeybindingsDialog::areRequiredFormsFilled() { return requiredFormsFilled; } -void KeybindingsDialog::listItemCancelSave(void) { +void KeybindingsDialog::listItemCancelSave() { listItemSelected(); transitionFromEditMode(); if (_editModeNewItem && !_data.empty() && (_data.back().name.length() == 0 || _data.back().script.length() == 0 || - _data.back().key.key == openspace::Key::Unknown)) + _data.back().key.key == Key::Unknown)) { listItemRemove(); } _editModeNewItem = false; } -void KeybindingsDialog::listItemRemove(void) { +void KeybindingsDialog::listItemRemove() { if (_list->count() > 0) { if (_list->count() == 1) { // Special case where last remaining item is being removed (QListWidget does // not like the final item being removed so instead clear it & leave it) - _data.at(0) = kBlank; + _data.at(0) = BlankKey; _list->item(0)->setText(""); } else { int index = _list->currentRow(); if (index >= 0 && index < _list->count()) { _list->takeItem(index); - if (_data.size() > 0) { + if (!_data.empty()) { _data.erase(_data.begin() + index); } } @@ -481,7 +465,7 @@ void KeybindingsDialog::transitionToEditMode() { _errorMsg->setText(""); } -void KeybindingsDialog::transitionFromEditMode(void) { +void KeybindingsDialog::transitionFromEditMode() { _list->setDisabled(false); _addButton->setDisabled(false); _removeButton->setDisabled(false); diff --git a/apps/OpenSpace/ext/launcher/src/profile/line.cpp b/apps/OpenSpace/ext/launcher/src/profile/line.cpp new file mode 100644 index 0000000000..336dc722a0 --- /dev/null +++ b/apps/OpenSpace/ext/launcher/src/profile/line.cpp @@ -0,0 +1,30 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2020 * + * * + * 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. * + ****************************************************************************************/ + +#include "profile/line.h" + +Line::Line() { + setFrameShape(QFrame::HLine); + setFrameShadow(QFrame::Sunken); +} diff --git a/apps/OpenSpace/ext/launcher/src/profile/marknodesdialog.cpp b/apps/OpenSpace/ext/launcher/src/profile/marknodesdialog.cpp index 5564f1582e..604827f837 100644 --- a/apps/OpenSpace/ext/launcher/src/profile/marknodesdialog.cpp +++ b/apps/OpenSpace/ext/launcher/src/profile/marknodesdialog.cpp @@ -24,6 +24,7 @@ #include "profile/marknodesdialog.h" +#include "profile/line.h" #include #include #include @@ -40,7 +41,10 @@ MarkNodesDialog::MarkNodesDialog(openspace::Profile& profile, QWidget* parent) , _data(_profile.markNodes()) { setWindowTitle("Mark Interesting Nodes"); + createWidgets(); +} +void MarkNodesDialog::createWidgets() { QBoxLayout* layout = new QVBoxLayout(this); _list = new QListWidget; connect( @@ -80,6 +84,7 @@ MarkNodesDialog::MarkNodesDialog(openspace::Profile& profile, QWidget* parent) box->addWidget(addButton); layout->addLayout(box); } + layout->addWidget(new Line); { QDialogButtonBox* buttons = new QDialogButtonBox; buttons->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Cancel); @@ -95,11 +100,11 @@ MarkNodesDialog::MarkNodesDialog(openspace::Profile& profile, QWidget* parent) } } -void MarkNodesDialog::listItemSelected(void) { +void MarkNodesDialog::listItemSelected() { _removeButton->setEnabled(true); } -void MarkNodesDialog::listItemAdded(void) { +void MarkNodesDialog::listItemAdded() { if (_newNode->text().isEmpty()) { return; } @@ -140,7 +145,7 @@ void MarkNodesDialog::parseSelections() { accept(); } -void MarkNodesDialog::keyPressEvent(QKeyEvent *evt) { +void MarkNodesDialog::keyPressEvent(QKeyEvent* evt) { if (evt->key() == Qt::Key_Enter || evt->key() == Qt::Key_Return) { if (_newNode->text().length() > 0 && _newNode->hasFocus()) { listItemAdded(); diff --git a/apps/OpenSpace/ext/launcher/src/profile/metadialog.cpp b/apps/OpenSpace/ext/launcher/src/profile/metadialog.cpp index 35d184ecd9..c96c206426 100644 --- a/apps/OpenSpace/ext/launcher/src/profile/metadialog.cpp +++ b/apps/OpenSpace/ext/launcher/src/profile/metadialog.cpp @@ -24,6 +24,7 @@ #include "profile/metadialog.h" +#include "profile/line.h" #include #include #include @@ -38,40 +39,7 @@ MetaDialog::MetaDialog(openspace::Profile& profile, QWidget *parent) , _profile(profile) { setWindowTitle("Meta"); - - QBoxLayout* layout = new QVBoxLayout(this); - layout->addWidget(new QLabel("Name")); - _nameEdit = new QLineEdit; - layout->addWidget(_nameEdit); - - layout->addWidget(new QLabel("Version")); - _versionEdit = new QLineEdit; - layout->addWidget(_versionEdit); - - layout->addWidget(new QLabel("Description")); - _descriptionEdit = new QTextEdit; - _descriptionEdit->setAcceptRichText(false); - _descriptionEdit->setTabChangesFocus(true); - layout->addWidget(_descriptionEdit); - - layout->addWidget(new QLabel("Author")); - _authorEdit = new QLineEdit; - layout->addWidget(_authorEdit); - - layout->addWidget(new QLabel("URL")); - _urlEdit = new QLineEdit; - layout->addWidget(_urlEdit); - - layout->addWidget(new QLabel("License")); - _licenseEdit = new QLineEdit; - layout->addWidget(_licenseEdit); - - QDialogButtonBox* buttons = new QDialogButtonBox; - buttons->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Cancel); - QObject::connect(buttons, &QDialogButtonBox::accepted, this, &MetaDialog::save); - QObject::connect(buttons, &QDialogButtonBox::rejected, this, &MetaDialog::reject); - layout->addWidget(buttons); - + createWidgets(); if (_profile.meta().has_value()) { openspace::Profile::Meta meta = *_profile.meta(); @@ -96,6 +64,43 @@ MetaDialog::MetaDialog(openspace::Profile& profile, QWidget *parent) } } +void MetaDialog::createWidgets() { + QBoxLayout* layout = new QVBoxLayout(this); + layout->addWidget(new QLabel("Name")); + _nameEdit = new QLineEdit; + layout->addWidget(_nameEdit); + + layout->addWidget(new QLabel("Version")); + _versionEdit = new QLineEdit; + layout->addWidget(_versionEdit); + + layout->addWidget(new QLabel("Description")); + _descriptionEdit = new QTextEdit; + _descriptionEdit->setAcceptRichText(false); + _descriptionEdit->setTabChangesFocus(true); + layout->addWidget(_descriptionEdit); + + layout->addWidget(new QLabel("Author")); + _authorEdit = new QLineEdit; + layout->addWidget(_authorEdit); + + layout->addWidget(new QLabel("URL")); + _urlEdit = new QLineEdit; + layout->addWidget(_urlEdit); + + layout->addWidget(new QLabel("License")); + _licenseEdit = new QLineEdit; + layout->addWidget(_licenseEdit); + + layout->addWidget(new Line); + + QDialogButtonBox* buttons = new QDialogButtonBox; + buttons->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Cancel); + QObject::connect(buttons, &QDialogButtonBox::accepted, this, &MetaDialog::save); + QObject::connect(buttons, &QDialogButtonBox::rejected, this, &MetaDialog::reject); + layout->addWidget(buttons); +} + void MetaDialog::save() { const bool allEmpty = _nameEdit->text().isEmpty() && _versionEdit->text().isEmpty() && diff --git a/apps/OpenSpace/ext/launcher/src/profile/modulesdialog.cpp b/apps/OpenSpace/ext/launcher/src/profile/modulesdialog.cpp index f2576028b0..217ea9e5d8 100644 --- a/apps/OpenSpace/ext/launcher/src/profile/modulesdialog.cpp +++ b/apps/OpenSpace/ext/launcher/src/profile/modulesdialog.cpp @@ -24,6 +24,7 @@ #include "profile/modulesdialog.h" +#include "profile/line.h" #include #include #include @@ -33,15 +34,25 @@ #include #include -ModulesDialog::ModulesDialog(openspace::Profile& profile, QWidget *parent) +using namespace openspace; + +namespace { + const Profile::Module Blank = { "", "", "" }; +} // namespace + +ModulesDialog::ModulesDialog(Profile& profile, QWidget *parent) : QDialog(parent) , _profile(profile) , _data(_profile.modules()) { setWindowTitle("Modules"); + createWidgets(); + transitionFromEditMode(); +} + +void ModulesDialog::createWidgets() { QBoxLayout* layout = new QVBoxLayout(this); - { _list = new QListWidget; connect( @@ -52,8 +63,8 @@ ModulesDialog::ModulesDialog(openspace::Profile& profile, QWidget *parent) _list->setMovement(QListView::Free); _list->setResizeMode(QListView::Adjust); - for (size_t i = 0; i < _data.size(); ++i) { - _list->addItem(new QListWidgetItem(createOneLineSummary(_data[i]))); + for (const Profile::Module& m : _data) { + _list->addItem(new QListWidgetItem(createOneLineSummary(m))); } layout->addWidget(_list); } @@ -117,12 +128,7 @@ ModulesDialog::ModulesDialog(openspace::Profile& profile, QWidget *parent) layout->addLayout(box); } - { - QFrame* line = new QFrame; - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - layout->addWidget(line); - } + layout->addWidget(new Line); { QBoxLayout* footerLayout = new QHBoxLayout; @@ -144,11 +150,9 @@ ModulesDialog::ModulesDialog(openspace::Profile& profile, QWidget *parent) footerLayout->addWidget(_buttonBox); layout->addLayout(footerLayout); } - - transitionFromEditMode(); } -QString ModulesDialog::createOneLineSummary(openspace::Profile::Module m) { +QString ModulesDialog::createOneLineSummary(Profile::Module m) { QString summary = QString::fromStdString(m.name); bool hasCommandForLoaded = (m.loadedInstruction->length() > 0); bool hasCommandForNotLoaded = (m.notLoadedInstruction->length() > 0); @@ -169,11 +173,11 @@ QString ModulesDialog::createOneLineSummary(openspace::Profile::Module m) { } void ModulesDialog::listItemSelected() { - QListWidgetItem *item = _list->currentItem(); + QListWidgetItem* item = _list->currentItem(); int index = _list->row(item); - if (_data.size() > 0) { - const openspace::Profile::Module& m = _data[index]; + if (!_data.empty()) { + const Profile::Module& m = _data[index]; _moduleEdit->setText(QString::fromStdString(m.name)); if (m.loadedInstruction.has_value()) { _loadedEdit->setText(QString::fromStdString(*m.loadedInstruction)); @@ -191,7 +195,7 @@ void ModulesDialog::listItemSelected() { transitionToEditMode(); } -bool ModulesDialog::isLineEmpty(int index) { +bool ModulesDialog::isLineEmpty(int index) const { bool isEmpty = true; if (!_list->item(index)->text().isEmpty()) { isEmpty = false; @@ -202,20 +206,20 @@ bool ModulesDialog::isLineEmpty(int index) { return isEmpty; } -void ModulesDialog::listItemAdded(void) { +void ModulesDialog::listItemAdded() { int currentListSize = _list->count(); if ((currentListSize == 1) && (isLineEmpty(0))) { - //Special case where list is "empty" but really has one line that is blank. + // Special case where list is "empty" but really has one line that is blank. // This is done because QListWidget does not seem to like having its sole // remaining item being removed. - _data.at(0) = kBlank; + _data.at(0) = Blank; _list->item(0)->setText(" (Enter details below & click 'Save')"); _list->setCurrentRow(0); transitionToEditMode(); } else { - _data.push_back(kBlank); + _data.push_back(Blank); _list->addItem(new QListWidgetItem(" (Enter details below & click 'Save')")); //Scroll down to that blank line highlighted _list->setCurrentRow(_list->count() - 1); @@ -242,9 +246,8 @@ void ModulesDialog::listItemAdded(void) { _editModeNewItem = true; } -void ModulesDialog::listItemSave(void) { +void ModulesDialog::listItemSave() { if (_moduleEdit->text().isEmpty()) { - //ui->label_module->setText("Module"); _errorMsg->setText("Missing module name"); return; } @@ -262,32 +265,28 @@ void ModulesDialog::listItemSave(void) { _editModeNewItem = false; } -void ModulesDialog::listItemCancelSave(void) { +void ModulesDialog::listItemCancelSave() { transitionFromEditMode(); - if (_editModeNewItem) { - if (_data.size() > 0) { - if(_data.back().name.length() == 0) { - listItemRemove(); - } - } + if (_editModeNewItem && !_data.empty() && _data.back().name.empty()) { + listItemRemove(); } _editModeNewItem = false; } -void ModulesDialog::listItemRemove(void) { +void ModulesDialog::listItemRemove() { if (_list->count() > 0) { if (_list->currentRow() >= 0 && _list->currentRow() < _list->count()) { if (_list->count() == 1) { - //Special case where last remaining item is being removed (QListWidget + // Special case where last remaining item is being removed (QListWidget // doesn't like the final item being removed so instead clear it) - _data.at(0) = kBlank; + _data.at(0) = Blank; _list->item(0)->setText(""); } else { int index = _list->currentRow(); if (index >= 0 && index < _list->count()) { delete _list->takeItem(index); - if (_data.size() > 0) { + if (!_data.empty()) { _data.erase(_data.begin() + index); } } @@ -297,7 +296,7 @@ void ModulesDialog::listItemRemove(void) { transitionFromEditMode(); } -void ModulesDialog::transitionToEditMode(void) { +void ModulesDialog::transitionToEditMode() { _list->setDisabled(true); _buttonAdd->setDisabled(true); _buttonRemove->setDisabled(true); @@ -312,7 +311,7 @@ void ModulesDialog::transitionToEditMode(void) { _errorMsg->setText(""); } -void ModulesDialog::transitionFromEditMode(void) { +void ModulesDialog::transitionFromEditMode() { _list->setDisabled(false); _buttonAdd->setDisabled(false); _buttonRemove->setDisabled(false); diff --git a/apps/OpenSpace/ext/launcher/src/profile/profileedit.cpp b/apps/OpenSpace/ext/launcher/src/profile/profileedit.cpp index fe3a78509c..46e7a8d942 100644 --- a/apps/OpenSpace/ext/launcher/src/profile/profileedit.cpp +++ b/apps/OpenSpace/ext/launcher/src/profile/profileedit.cpp @@ -22,22 +22,34 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include #include "profile/profileedit.h" -#include "filesystemaccess.h" + +#include "profile/additionalscriptsdialog.h" +#include "profile/assetsdialog.h" +#include "profile/cameradialog.h" +#include "profile/deltatimesdialog.h" +#include "profile/keybindingsdialog.h" +#include "profile/line.h" +#include "profile/marknodesdialog.h" +#include "profile/metadialog.h" +#include "profile/modulesdialog.h" +#include "profile/propertiesdialog.h" +#include "profile/timedialog.h" +#include +#include #include -#include -#include #include #include #include #include -#include +#include +#include +#include +#include + +using namespace openspace; namespace { - template struct overloaded : Ts... { using Ts::operator()...; }; - template overloaded(Ts...)->overloaded; - QString labelText(int size, QString title) { QString label; if (size > 0) { @@ -48,19 +60,56 @@ namespace { } return label; } + + std::string summarizeAssets(const std::vector& assets) { + std::string results; + for (const std::string& a : assets) { + results += a + '\n'; + } + return results; + } + + std::string summarizeKeybindings(const std::vector& keybindings) + { + std::string results; + for (Profile::Keybinding k : keybindings) { + results += k.name + " ("; + int keymod = static_cast(k.key.modifier); + if (keymod != static_cast(openspace::KeyModifier::NoModifier)) { + results += openspace::KeyModifierNames.at(keymod) + "+"; + } + results += openspace::KeyNames.at(static_cast(k.key.key)); + results += ")\n"; + } + return results; + } + + std::string summarizeProperties(const std::vector& properties) { + std::string results; + for (openspace::Profile::Property p : properties) { + results += p.name + " = " + p.value + '\n'; + } + return results; + } } // namespace -using namespace openspace; - -ProfileEdit::ProfileEdit(Profile& profile, const std::string reportedAssets, - std::vector& readOnlyProfiles, QWidget* parent) +ProfileEdit::ProfileEdit(Profile& profile, const std::string& profileName, + std::string assetBasePath, std::string profileBasePath, + const std::vector& readOnlyProfiles, + QWidget* parent) : QDialog(parent) - , _reportedAssets(reportedAssets) + , _assetBasePath(std::move(assetBasePath)) + , _profileBasePath(std::move(profileBasePath)) , _profile(profile) , _readOnlyProfiles(readOnlyProfiles) { setWindowTitle("Profile Editor"); + createWidgets(profileName); + initSummaryTextForEachCategory(); +} + +void ProfileEdit::createWidgets(const std::string& profileName) { QBoxLayout* layout = new QVBoxLayout(this); QBoxLayout* topLayout = new QHBoxLayout; QBoxLayout* leftLayout = new QVBoxLayout; @@ -70,7 +119,7 @@ ProfileEdit::ProfileEdit(Profile& profile, const std::string reportedAssets, profileLabel->setObjectName("profile"); container->addWidget(profileLabel); - _profileEdit = new QLineEdit; + _profileEdit = new QLineEdit(QString::fromStdString(profileName)); container->addWidget(_profileEdit); QPushButton* duplicateButton = new QPushButton("Duplicate Profile"); @@ -82,12 +131,7 @@ ProfileEdit::ProfileEdit(Profile& profile, const std::string reportedAssets, layout->addLayout(container); } - { - QFrame* line = new QFrame; - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - layout->addWidget(line); - } + layout->addWidget(new Line); { QGridLayout* container = new QGridLayout; container->setColumnStretch(1, 1); @@ -110,12 +154,7 @@ ProfileEdit::ProfileEdit(Profile& profile, const std::string reportedAssets, leftLayout->addLayout(container); } - { - QFrame* line = new QFrame; - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - leftLayout->addWidget(line); - } + leftLayout->addWidget(new Line); { QGridLayout* container = new QGridLayout; container->setColumnStretch(1, 1); @@ -135,12 +174,7 @@ ProfileEdit::ProfileEdit(Profile& profile, const std::string reportedAssets, leftLayout->addLayout(container); } - { - QFrame* line = new QFrame; - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - leftLayout->addWidget(line); - } + leftLayout->addWidget(new Line); { QGridLayout* container = new QGridLayout; container->setColumnStretch(1, 1); @@ -165,12 +199,7 @@ ProfileEdit::ProfileEdit(Profile& profile, const std::string reportedAssets, } topLayout->addLayout(leftLayout, 3); - { - QFrame* line = new QFrame; - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - topLayout->addWidget(line); - } + topLayout->addWidget(new Line); QBoxLayout* rightLayout = new QVBoxLayout; { @@ -186,12 +215,7 @@ ProfileEdit::ProfileEdit(Profile& profile, const std::string reportedAssets, container->addWidget(metaEdit); rightLayout->addLayout(container); } - { - QFrame* line = new QFrame; - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - rightLayout->addWidget(line); - } + rightLayout->addWidget(new Line); { QBoxLayout* container = new QVBoxLayout; _interestingNodesLabel = new QLabel("Mark Interesting Nodes"); @@ -208,12 +232,7 @@ ProfileEdit::ProfileEdit(Profile& profile, const std::string reportedAssets, container->addWidget(interestingNodesEdit); rightLayout->addLayout(container); } - { - QFrame* line = new QFrame; - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - rightLayout->addWidget(line); - } + rightLayout->addWidget(new Line); { QBoxLayout* container = new QVBoxLayout; _deltaTimesLabel = new QLabel("Simulation Time Increments"); @@ -230,12 +249,7 @@ ProfileEdit::ProfileEdit(Profile& profile, const std::string reportedAssets, container->addWidget(deltaTimesEdit); rightLayout->addLayout(container); } - { - QFrame* line = new QFrame; - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - rightLayout->addWidget(line); - } + rightLayout->addWidget(new Line); { QBoxLayout* container = new QVBoxLayout; _cameraLabel = new QLabel("Camera"); @@ -249,12 +263,7 @@ ProfileEdit::ProfileEdit(Profile& profile, const std::string reportedAssets, container->addWidget(cameraEdit); rightLayout->addLayout(container); } - { - QFrame* line = new QFrame; - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - rightLayout->addWidget(line); - } + rightLayout->addWidget(new Line); { QBoxLayout* container = new QVBoxLayout; _timeLabel = new QLabel("Time"); @@ -268,12 +277,7 @@ ProfileEdit::ProfileEdit(Profile& profile, const std::string reportedAssets, container->addWidget(timeEdit); rightLayout->addLayout(container); } - { - QFrame* line = new QFrame; - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - rightLayout->addWidget(line); - } + rightLayout->addWidget(new Line); { QBoxLayout* container = new QVBoxLayout; _modulesLabel = new QLabel("Modules"); @@ -287,12 +291,7 @@ ProfileEdit::ProfileEdit(Profile& profile, const std::string reportedAssets, container->addWidget(modulesEdit); rightLayout->addLayout(container); } - { - QFrame* line = new QFrame; - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - rightLayout->addWidget(line); - } + rightLayout->addWidget(new Line); { QBoxLayout* container = new QVBoxLayout; _additionalScriptsLabel = new QLabel("Additional Scripts"); @@ -312,12 +311,7 @@ ProfileEdit::ProfileEdit(Profile& profile, const std::string reportedAssets, topLayout->addLayout(rightLayout); layout->addLayout(topLayout); - { - QFrame* line = new QFrame; - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - layout->addWidget(line); - } + layout->addWidget(new Line); { QBoxLayout* footer = new QHBoxLayout; @@ -333,21 +327,23 @@ ProfileEdit::ProfileEdit(Profile& profile, const std::string reportedAssets, footer->addWidget(buttons); layout->addLayout(footer); } - - initSummaryTextForEachCategory(); } void ProfileEdit::initSummaryTextForEachCategory() { _modulesLabel->setText(labelText(_profile.modules().size(), "Modules")); _assetsLabel->setText(labelText(_profile.assets().size(), "Assets")); - _assetsEdit->setText(QString::fromStdString(summarizeAssets())); + _assetsEdit->setText(QString::fromStdString(summarizeAssets(_profile.assets()))); _propertiesLabel->setText(labelText(_profile.properties().size(), "Properties")); - _propertiesEdit->setText(QString::fromStdString(summarizeProperties())); + _propertiesEdit->setText( + QString::fromStdString(summarizeProperties(_profile.properties())) + ); _keybindingsLabel->setText(labelText(_profile.keybindings().size(), "Keybindings")); - _keybindingsEdit->setText(QString::fromStdString(summarizeKeybindings())); + _keybindingsEdit->setText( + QString::fromStdString(summarizeKeybindings(_profile.keybindings())) + ); _deltaTimesLabel->setText( labelText(_profile.deltaTimes().size(), "Simulation Time Increments") @@ -357,28 +353,47 @@ void ProfileEdit::initSummaryTextForEachCategory() { ); } -void ProfileEdit::setProfileName(QString profileToSet) { - _profileEdit->setText(profileToSet); -} - void ProfileEdit::duplicateProfile() { - QString currentProfile = _profileEdit->text(); - if (!currentProfile.isEmpty()) { - QString duplicatedName = currentProfile + "_1"; - if ((currentProfile.length() > 2) - && (currentProfile.midRef(currentProfile.length() - 2, 1) == "_")) - { - QStringRef num = currentProfile.midRef(currentProfile.length() - 1, 1); - bool validConversion = false; - int val = num.toInt(&validConversion, 10); - if (validConversion && val < 9) { - duplicatedName = currentProfile.left(currentProfile.length() - 2) - + "_" + QString::number(val + 1); - } - } - _profileEdit->setText(duplicatedName); - } _errorMsg->clear(); + std::string profile = _profileEdit->text().toStdString(); + if (profile.empty()) { + return; + } + + constexpr const char Separator = '_'; + int version = 0; + if (size_t it = profile.rfind(Separator); it != std::string::npos) { + // If the value exists, we have a profile that potentially already has a version + // number attached to it + std::string versionStr = profile.substr(it + 1); + try { + version = std::stoi(versionStr); + + // We will re-add the separator with the new version string to the file, so we + // will remove the suffix here first + profile = profile.substr(0, it); + } + catch (const std::invalid_argument& e) { + // If this exception is thrown, we did find a separator character but the + // substring afterwards was not a number, so the user just added a separator + // by themselves. In this case we don't do anything + } + } + + // By this point we have our current profile (without any suffix) in 'profile' and the + // currently active version in 'version'. Now we need to put both together again and + // also make sure that we don't pick a version number that already exists + while (true) { + version++; + + std::string candidate = profile + Separator + std::to_string(version); + std::string candidatePath = _profileBasePath + candidate + ".profile"; + + if (!std::filesystem::exists(candidatePath)) { + _profileEdit->setText(QString::fromStdString(std::move(candidate))); + return; + } + } } void ProfileEdit::openMeta() { @@ -396,23 +411,25 @@ void ProfileEdit::openProperties() { _errorMsg->clear(); PropertiesDialog(_profile, this).exec(); _propertiesLabel->setText(labelText(_profile.properties().size(), "Properties")); - _propertiesEdit->setText(QString::fromStdString(summarizeProperties())); + _propertiesEdit->setText( + QString::fromStdString(summarizeProperties(_profile.properties())) + ); } void ProfileEdit::openKeybindings() { _errorMsg->clear(); KeybindingsDialog(_profile, this).exec(); _keybindingsLabel->setText(labelText(_profile.keybindings().size(), "Keybindings")); - _keybindingsEdit->setText(QString::fromStdString(summarizeKeybindings())); + _keybindingsEdit->setText( + QString::fromStdString(summarizeKeybindings(_profile.keybindings())) + ); } void ProfileEdit::openAssets() { _errorMsg->clear(); - AssetsDialog assets(_profile, _reportedAssets, this); - assets.exec(); + AssetsDialog(_profile, _assetBasePath, this).exec(); _assetsLabel->setText(labelText(_profile.assets().size(), "Assets")); - _assetsEdit->setText(assets.createTextSummary()); - _assetsEdit->setText(QString::fromStdString(summarizeAssets())); + _assetsEdit->setText(QString::fromStdString(summarizeAssets(_profile.assets()))); } void ProfileEdit::openTime() { @@ -446,36 +463,6 @@ void ProfileEdit::openMarkNodes() { ); } -std::string ProfileEdit::summarizeProperties() { - std::string results; - for (openspace::Profile::Property p : _profile.properties()) { - results += p.name + " = " + p.value + '\n'; - } - return results; -} - -std::string ProfileEdit::summarizeKeybindings() { - std::string results; - for (openspace::Profile::Keybinding k : _profile.keybindings()) { - results += k.name + " ("; - int keymod = static_cast(k.key.modifier); - if (keymod != static_cast(openspace::KeyModifier::NoModifier)) { - results += openspace::KeyModifierNames.at(keymod) + "+"; - } - results += openspace::KeyNames.at(static_cast(k.key.key)); - results += ")\n"; - } - return results; -} - -std::string ProfileEdit::summarizeAssets() { - std::string results; - for (const std::string& a : _profile.assets()) { - results += a + '\n'; - } - return results; -} - bool ProfileEdit::wasSaved() const { return _saveSelected; } @@ -489,34 +476,30 @@ void ProfileEdit::cancel() { reject(); } -bool ProfileEdit::isReadOnly(std::string profileSave) { - auto it = std::find(_readOnlyProfiles.begin(), _readOnlyProfiles.end(), profileSave); - return !(it == _readOnlyProfiles.end()); -} - void ProfileEdit::approved() { - QString profileName = _profileEdit->text(); - if ((profileName.length() > 0) && !isReadOnly(profileName.toStdString())) { + std::string profileName = _profileEdit->text().toStdString(); + if (profileName.empty()) { + _errorMsg->setText("Profile name must be specified"); + return; + } + + auto it = std::find(_readOnlyProfiles.begin(), _readOnlyProfiles.end(), profileName); + if (it == _readOnlyProfiles.end()) { _saveSelected = true; _errorMsg->setText(""); accept(); } else { - //QString formatText = ""; - //formatText += ui->label_profile->text(); - //formatText += ""; - //ui->label_profile->setText(formatText); - QString errorLabel = ""; - errorLabel += "This is a read-only profile. Click 'duplicate' or rename & save."; - errorLabel += ""; - _errorMsg->setText(errorLabel); + _errorMsg->setText( + "This is a read-only profile. Click 'Duplicate' or rename & save" + ); } } -void ProfileEdit::keyPressEvent(QKeyEvent *evt) -{ - if(evt->key() == Qt::Key_Enter || evt->key() == Qt::Key_Return) +void ProfileEdit::keyPressEvent(QKeyEvent* evt) { + if (evt->key() == Qt::Key_Enter || evt->key() == Qt::Key_Return) { return; + } QDialog::keyPressEvent(evt); } diff --git a/apps/OpenSpace/ext/launcher/src/profile/propertiesdialog.cpp b/apps/OpenSpace/ext/launcher/src/profile/propertiesdialog.cpp index b78fc49531..5ae897d8b8 100644 --- a/apps/OpenSpace/ext/launcher/src/profile/propertiesdialog.cpp +++ b/apps/OpenSpace/ext/launcher/src/profile/propertiesdialog.cpp @@ -24,6 +24,7 @@ #include "profile/propertiesdialog.h" +#include "profile/line.h" #include #include #include @@ -35,21 +36,28 @@ #include #include +using namespace openspace; + namespace { - const openspace::Profile::Property kBlank { - openspace::Profile::Property::SetType::SetPropertyValue, + const Profile::Property Blank { + Profile::Property::SetType::SetPropertyValueSingle, "", "" }; } // namespace -PropertiesDialog::PropertiesDialog(openspace::Profile& profile, QWidget *parent) +PropertiesDialog::PropertiesDialog(Profile& profile, QWidget *parent) : QDialog(parent) , _profile(profile) , _data(_profile.properties()) { setWindowTitle("Set Property Values"); + createWidgets(); + transitionFromEditMode(); +} + +void PropertiesDialog::createWidgets() { QBoxLayout* layout = new QVBoxLayout(this); { _list = new QListWidget; @@ -82,18 +90,13 @@ PropertiesDialog::PropertiesDialog(openspace::Profile& profile, QWidget *parent) layout->addLayout(box); } - { - QFrame* line = new QFrame; - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - layout->addWidget(line); - } + layout->addWidget(new Line); { _commandLabel = new QLabel("Property Set Command"); layout->addWidget(_commandLabel); _commandCombo = new QComboBox; - _commandCombo->addItems({ "SetPropertyValue", "SetPropertyValueSingle" }); + _commandCombo->addItems({ "SetPropertyValueSingle", "SetPropertyValue" }); layout->addWidget(_commandCombo); _propertyLabel = new QLabel("Property"); @@ -128,6 +131,7 @@ PropertiesDialog::PropertiesDialog(openspace::Profile& profile, QWidget *parent) layout->addLayout(box); } } + layout->addWidget(new Line); { QBoxLayout* footerLayout = new QHBoxLayout; @@ -150,16 +154,14 @@ PropertiesDialog::PropertiesDialog(openspace::Profile& profile, QWidget *parent) footerLayout->addWidget(_buttonBox); layout->addLayout(footerLayout); } - - transitionFromEditMode(); } -QString PropertiesDialog::createOneLineSummary(openspace::Profile::Property p) { +QString PropertiesDialog::createOneLineSummary(Profile::Property p) { QString summary = QString::fromStdString(p.name); summary += " = "; summary += QString::fromStdString(p.value); summary += " (SetPropertyValue"; - if (p.setType == openspace::Profile::Property::SetType::SetPropertyValueSingle) { + if (p.setType == Profile::Property::SetType::SetPropertyValueSingle) { summary += "Single"; } summary += ")"; @@ -171,8 +173,8 @@ void PropertiesDialog::listItemSelected() { int index = _list->row(item); if (_data.size() > 0) { - openspace::Profile::Property& p = _data[index]; - if (p.setType == openspace::Profile::Property::SetType::SetPropertyValue) { + Profile::Property& p = _data[index]; + if (p.setType == Profile::Property::SetType::SetPropertyValueSingle) { _commandCombo->setCurrentIndex(0); } else { @@ -195,20 +197,20 @@ bool PropertiesDialog::isLineEmpty(int index) { return isEmpty; } -void PropertiesDialog::listItemAdded(void) { +void PropertiesDialog::listItemAdded() { int currentListSize = _list->count(); if ((currentListSize == 1) && (isLineEmpty(0))) { - //Special case where list is "empty" but really has one line that is blank. + // Special case where list is "empty" but really has one line that is blank. // This is done because QListWidget does not seem to like having its sole // remaining item being removed. - _data.at(0) = kBlank; + _data.at(0) = Blank; _list->item(0)->setText(" (Enter details below & click 'Save')"); _list->setCurrentRow(0); transitionToEditMode(); } else { - _data.push_back(kBlank); + _data.push_back(Blank); _list->addItem(new QListWidgetItem(" (Enter details below & click 'Save')")); //Scroll down to that blank line highlighted _list->setCurrentRow(_list->count() - 1); @@ -222,7 +224,7 @@ void PropertiesDialog::listItemAdded(void) { _editModeNewItem = true; } -void PropertiesDialog::listItemSave(void) { +void PropertiesDialog::listItemSave() { if (!areRequiredFormsFilled()) { return; } @@ -232,12 +234,10 @@ void PropertiesDialog::listItemSave(void) { if ( _data.size() > 0) { if (_commandCombo->currentIndex() == 0) { - _data[index].setType - = openspace::Profile::Property::SetType::SetPropertyValue; + _data[index].setType = Profile::Property::SetType::SetPropertyValueSingle; } else { - _data[index].setType - = openspace::Profile::Property::SetType::SetPropertyValueSingle; + _data[index].setType = Profile::Property::SetType::SetPropertyValue; } _data[index].name = _propertyEdit->text().toStdString(); _data[index].value = _valueEdit->text().toStdString(); @@ -265,7 +265,7 @@ bool PropertiesDialog::areRequiredFormsFilled() { return requiredFormsFilled; } -void PropertiesDialog::listItemCancelSave(void) { +void PropertiesDialog::listItemCancelSave() { listItemSelected(); transitionFromEditMode(); if (_editModeNewItem) { @@ -278,13 +278,13 @@ void PropertiesDialog::listItemCancelSave(void) { _editModeNewItem = false; } -void PropertiesDialog::listItemRemove(void) { +void PropertiesDialog::listItemRemove() { if (_list->count() > 0) { if (_list->currentRow() >= 0 && _list->currentRow() < _list->count()) { if (_list->count() == 1) { //Special case where last remaining item is being removed (QListWidget // doesn't like the final item being removed so instead clear it) - _data.at(0) = kBlank; + _data.at(0) = Blank; _list->item(0)->setText(""); } else { @@ -301,7 +301,7 @@ void PropertiesDialog::listItemRemove(void) { transitionFromEditMode(); } -void PropertiesDialog::transitionToEditMode(void) { +void PropertiesDialog::transitionToEditMode() { _list->setDisabled(true); _addButton->setDisabled(true); _removeButton->setDisabled(true); @@ -316,7 +316,7 @@ void PropertiesDialog::transitionToEditMode(void) { _errorMsg->setText(""); } -void PropertiesDialog::transitionFromEditMode(void) { +void PropertiesDialog::transitionFromEditMode() { _list->setDisabled(false); _addButton->setDisabled(false); _removeButton->setDisabled(false); diff --git a/apps/OpenSpace/ext/launcher/src/profile/timedialog.cpp b/apps/OpenSpace/ext/launcher/src/profile/timedialog.cpp index 71d2966b89..678b5bf504 100644 --- a/apps/OpenSpace/ext/launcher/src/profile/timedialog.cpp +++ b/apps/OpenSpace/ext/launcher/src/profile/timedialog.cpp @@ -24,6 +24,7 @@ #include "profile/timedialog.h" +#include "profile/line.h" #include #include #include @@ -41,9 +42,32 @@ TimeDialog::TimeDialog(openspace::Profile& profile, QWidget* parent) , _profile(profile) { setWindowTitle("Time"); + createWidgets(); + QStringList types = { "Absolute", "Relative" }; + _typeCombo->addItems(types); + if (_profile.time().has_value()) { + _data = *_profile.time(); + if (_data.type == Profile::Time::Type::Relative) { + if (_data.value == "") { + _data.value = "now"; + } + _relativeEdit->setSelection(0, _relativeEdit->text().length()); + } + else { + _absoluteEdit->setSelectedSection(QDateTimeEdit::YearSection); + } + } + else { + _data.type = Profile::Time::Type::Relative; + _data.value = "now"; + } + _initializedAsAbsolute = (_data.type == Profile::Time::Type::Absolute); + enableAccordingToType(static_cast(_data.type)); +} + +void TimeDialog::createWidgets() { QBoxLayout* layout = new QVBoxLayout(this); - { layout->addWidget(new QLabel("Time Type")); _typeCombo = new QComboBox; @@ -71,12 +95,7 @@ TimeDialog::TimeDialog(openspace::Profile& profile, QWidget* parent) ); layout->addWidget(_relativeEdit); } - { - QFrame* line = new QFrame; - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - layout->addWidget(line); - } + layout->addWidget(new Line); { QDialogButtonBox* buttons = new QDialogButtonBox; buttons->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Cancel); @@ -85,29 +104,6 @@ TimeDialog::TimeDialog(openspace::Profile& profile, QWidget* parent) QObject::connect(buttons, &QDialogButtonBox::rejected, this, &TimeDialog::reject); layout->addWidget(buttons); } - - - - QStringList types { "Absolute", "Relative" }; - _typeCombo->addItems(types); - if (_profile.time().has_value()) { - _data = *_profile.time(); - if (_data.type == Profile::Time::Type::Relative) { - if (_data.value == "") { - _data.value = "now"; - } - _relativeEdit->setSelection(0, _relativeEdit->text().length()); - } - else { - _absoluteEdit->setSelectedSection(QDateTimeEdit::YearSection); - } - } - else { - _data.type = Profile::Time::Type::Relative; - _data.value = "now"; - } - _initializedAsAbsolute = (_data.type == Profile::Time::Type::Absolute); - enableAccordingToType(static_cast(_data.type)); } void TimeDialog::enableAccordingToType(int idx) { diff --git a/apps/OpenSpace/main.cpp b/apps/OpenSpace/main.cpp index 8684d9d744..d7d4c60fd5 100644 --- a/apps/OpenSpace/main.cpp +++ b/apps/OpenSpace/main.cpp @@ -927,29 +927,29 @@ void setSgctDelegateFunctions() { }; } -void analyzeCommandLineArgsForSettings(int& argc, char** argv, bool& sgct, bool& profile, - std::string& sgctFunctionName) +void checkCommandLineForSettings(int& argc, char** argv, bool& hasSGCT, bool& hasProfile, + std::string& sgctFunctionName) { - bool haveCliConfigFlag = false; for (int i = 1; i < argc; ++i) { - std::string a = argv[i]; - if (a.compare("-c") == 0 || a.compare("--config") == 0) { - haveCliConfigFlag = true; - } - else if (haveCliConfigFlag) { - a.erase(remove_if(a.begin(), a.end(), isspace), a.end()); + const std::string arg = argv[i]; + if (arg == "-c" || arg == "--config") { + std::string p = ((i + 1) < argc) ? argv[i + 1] : ""; + p.erase(std::remove_if(p.begin(), p.end(), ::isspace), p.end()); + const std::string sgctAssignment = "SGCTConfig="; - size_t findSgct = a.find(sgctAssignment); - size_t findBracket = a.find("}"); + const size_t findSgct = p.find(sgctAssignment); + const size_t findBracket = p.find("}"); if (findSgct != std::string::npos) { if (findBracket != std::string::npos) { - sgctFunctionName = a.substr(findSgct + sgctAssignment.length(), - findBracket - findSgct); + sgctFunctionName = arg.substr( + findSgct + sgctAssignment.length(), + findBracket - findSgct + ); } - sgct = true; + hasSGCT = true; } - if (a.find("Profile=") != std::string::npos) { - profile = true; + if (p.find("Profile=") != std::string::npos) { + hasProfile = true; } } } @@ -959,40 +959,35 @@ std::string setWindowConfigPresetForGui(const std::string labelFromCfgFile, const std::string xmlExt, bool haveCliSGCTConfig, const std::string& sgctFunctionName) { - const std::string labelFromCli = " (from CLI)"; + configuration::Configuration& config = global::configuration; + std::string preset; - bool sgctConfigFileSpecifiedByLuaFunction - = (global::configuration.sgctConfigNameInitialized.length() > 0); + bool sgctConfigFileSpecifiedByLuaFunction = !config.sgctConfigNameInitialized.empty(); if (haveCliSGCTConfig) { - preset = (sgctFunctionName.length() > 0) ? sgctFunctionName - : global::configuration.windowConfiguration; - preset += labelFromCli; + preset = sgctFunctionName.empty() ? config.windowConfiguration : sgctFunctionName; + preset += " (from CLI)"; } else if (sgctConfigFileSpecifiedByLuaFunction) { - preset = global::configuration.sgctConfigNameInitialized; - preset += labelFromCfgFile; + preset = config.sgctConfigNameInitialized + labelFromCfgFile; } else { - preset = global::configuration.windowConfiguration; - if (preset.find("/") != std::string::npos) { - preset.erase(0, preset.find_last_of("/") + 1); + preset = config.windowConfiguration; + if (preset.find('/') != std::string::npos) { + preset.erase(0, preset.find_last_of('/') + 1); } if (preset.length() >= xmlExt.length()) { - if (preset.substr(preset.length() - xmlExt.length()) - .compare(xmlExt) == 0) - { - preset = preset.substr(0, preset.length() - - xmlExt.length()); + if (preset.substr(preset.length() - xmlExt.length()) == xmlExt) { + preset = preset.substr(0, preset.length() - xmlExt.length()); } } } return preset; } -std::string getSelectedSgctProfileFromLauncher(LauncherWindow& lw, bool hasCliSGCTConfig, - std::string windowConfiguration, - const std::string& labelFromCfgFile, - const std::string& xmlExt) +std::string selectedSgctProfileFromLauncher(LauncherWindow& lw, bool hasCliSGCTConfig, + std::string windowConfiguration, + const std::string& labelFromCfgFile, + const std::string& xmlExt) { std::string config = windowConfiguration; if (!hasCliSGCTConfig) { @@ -1152,39 +1147,40 @@ int main(int argc, char** argv) { global::openSpaceEngine.registerPathTokens(); - bool haveCliSGCTConfig = false; - bool haveCliProfile = false; - std::string sgctFunctionName; - analyzeCommandLineArgsForSettings(argc, argv, haveCliSGCTConfig, haveCliProfile, - sgctFunctionName); - //Call profile GUI + bool hasSGCTConfig = false; + bool hasProfile = false; + std::string sgctFunctionName; + checkCommandLineForSettings(argc, argv, hasSGCTConfig, hasProfile, sgctFunctionName); + + // Call profile GUI const std::string labelFromCfgFile = " (from .cfg)"; const std::string xmlExt = ".xml"; - std::string windowCfgPreset = setWindowConfigPresetForGui(labelFromCfgFile, xmlExt, - haveCliSGCTConfig, sgctFunctionName); + std::string windowCfgPreset = setWindowConfigPresetForGui( + labelFromCfgFile, + xmlExt, + hasSGCTConfig, + sgctFunctionName + ); - std::shared_ptr qaobj; - std::shared_ptr launchwin; - bool skipLauncherSinceProfileAndWindowAreConfiguredInCli = - (haveCliProfile && haveCliSGCTConfig) || global::configuration.bypassLauncher; - - if (!skipLauncherSinceProfileAndWindowAreConfiguredInCli) { + bool skipLauncher = + (hasProfile && hasSGCTConfig) || global::configuration.bypassLauncher; + if (!skipLauncher) { int qac = 0; - qaobj.reset(new QApplication(qac, nullptr)); - launchwin.reset(new LauncherWindow(absPath("${BASE}"), !haveCliProfile, - global::configuration, !haveCliSGCTConfig, windowCfgPreset, nullptr)); - launchwin->show(); - qaobj->exec(); + QApplication app(qac, nullptr); + LauncherWindow win(!hasProfile, + global::configuration, !hasSGCTConfig, windowCfgPreset, nullptr); + win.show(); + app.exec(); - if (!launchwin->wasLaunchSelected()) { - exit(EXIT_FAILURE); + if (!win.wasLaunchSelected()) { + exit(EXIT_SUCCESS); } - global::configuration.profile = launchwin->selectedProfile(); - windowConfiguration = getSelectedSgctProfileFromLauncher( - *launchwin, - haveCliSGCTConfig, + global::configuration.profile = win.selectedProfile(); + windowConfiguration = selectedSgctProfileFromLauncher( + win, + hasSGCTConfig, windowConfiguration, labelFromCfgFile, xmlExt @@ -1305,13 +1301,6 @@ int main(int argc, char** argv) { } #endif // OPENSPACE_HAS_SPOUT - if (launchwin != nullptr) { - launchwin->close(); - } - if (qaobj != nullptr) { - qaobj->quit(); - } - ghoul::deinitialize(); exit(EXIT_SUCCESS); } diff --git a/apps/Wormhole/main.cpp b/apps/Wormhole/main.cpp index 12b3764abb..487244aef8 100644 --- a/apps/Wormhole/main.cpp +++ b/apps/Wormhole/main.cpp @@ -44,42 +44,33 @@ int main(int argc, char** argv) { CommandlineParser::AllowUnknownCommands::Yes ); - std::stringstream defaultPassword; - defaultPassword << std::hex << std::setfill('0') << std::setw(6) << - (std::hash{}( - std::chrono::system_clock::now().time_since_epoch().count() - ) % 0xffffff); + struct { + std::string port; + std::string password; + std::string changeHostPassword; + } settings; - std::stringstream defaultChangeHostPassword; - defaultChangeHostPassword << std::hex << std::setfill('0') << std::setw(6) << - (std::hash{}( - std::chrono::system_clock::now().time_since_epoch().count() + 1 - ) % 0xffffff); - - std::string portString; commandlineParser.addCommand( std::make_unique>( - portString, + settings.port, "--port", "-p", "Sets the port to listen on" ) ); - std::string password; commandlineParser.addCommand( std::make_unique>( - password, + settings.password, "--password", "-l", "Sets the password to use" ) ); - std::string changeHostPassword; commandlineParser.addCommand( std::make_unique>( - changeHostPassword, + settings.changeHostPassword, "--hostpassword", "-h", "Sets the host password to use" @@ -89,29 +80,41 @@ int main(int argc, char** argv) { commandlineParser.setCommandLine(arguments); commandlineParser.execute(); - if (password.empty()) { - password = defaultPassword.str(); + if (settings.password.empty()) { + std::stringstream defaultPassword; + defaultPassword << std::hex << std::setfill('0') << std::setw(6) << + (std::hash{}( + std::chrono::system_clock::now().time_since_epoch().count() + ) % 0xffffff); + + settings.password = defaultPassword.str(); } - if (changeHostPassword.empty()) { - changeHostPassword = defaultChangeHostPassword.str(); + if (settings.changeHostPassword.empty()) { + std::stringstream defaultChangeHostPassword; + defaultChangeHostPassword << std::hex << std::setfill('0') << std::setw(6) << + (std::hash{}( + std::chrono::system_clock::now().time_since_epoch().count() + 1 + ) % 0xffffff); + + settings.changeHostPassword = defaultChangeHostPassword.str(); } - LINFO(fmt::format("Connection password: {}", password)); - LINFO(fmt::format("Host password: {}", changeHostPassword)); + LINFO(fmt::format("Connection password: {}", settings.password)); + LINFO(fmt::format("Host password: {}", settings.changeHostPassword)); int port = 25001; - if (!portString.empty()) { + if (!settings.port.empty()) { try { - port = std::stoi(portString); + port = std::stoi(settings.port); } catch (const std::invalid_argument&) { - LERROR(fmt::format("Invalid port: {}", portString)); + LERROR(fmt::format("Invalid port: {}", settings.port)); } } ParallelServer server; - server.start(port, password, changeHostPassword); + server.start(port, settings.password, settings.changeHostPassword); server.setDefaultHostAddress("127.0.0.1"); LINFO(fmt::format("Server listening to port {}", port)); diff --git a/data/assets/base.asset b/data/assets/base.asset index 88811f30d6..e23d958c4e 100644 --- a/data/assets/base.asset +++ b/data/assets/base.asset @@ -16,7 +16,6 @@ asset.require('scene/solarsystem/planets/mars/moons/deimos') asset.require('scene/solarsystem/dwarf_planets/pluto/system') asset.require('scene/solarsystem/dwarf_planets/pluto/default_layers') asset.require('scene/solarsystem/dwarf_planets/pluto/charon/default_layers') -asset.require('scene/solarsystem/missions/voyagerpioneer/voyager1_2__pioneer10_11') asset.require('scene/milkyway/milkyway/volume') asset.require('scene/milkyway/constellations/constellation_art') asset.require('scene/milkyway/constellations/constellation_keybinds') diff --git a/data/assets/util/webgui.asset b/data/assets/util/webgui.asset index 42932e3636..f72e690643 100644 --- a/data/assets/util/webgui.asset +++ b/data/assets/util/webgui.asset @@ -3,7 +3,7 @@ asset.require('./static_server') local guiCustomization = asset.require('customization/gui') -- Select which commit hashes to use for the frontend and backend -local frontendHash = "6b5ac33a77fff6f46088c1fffdaf04d59ec5bb5f" +local frontendHash = "5d9b8d52e43260c71f641e73e44e4c90bc9f5d6a" local dataProvider = "data.openspaceproject.com/files/webgui" local frontend = asset.syncedResource({ @@ -40,8 +40,12 @@ asset.onInitialize(function () -- The GUI contains date and simulation increment, -- so let's remove these from the dashboard. if openspace.getPropertyValue('Modules.CefWebGui.Visible') then - openspace.setPropertyValueSingle('Dashboard.Date.Enabled', false) - openspace.setPropertyValueSingle('Dashboard.SimulationIncrement.Enabled', false) + if openspace.hasProperty('Dashboard.Date.Enabled') then + openspace.setPropertyValueSingle('Dashboard.Date.Enabled', false) + end + if openspace.hasProperty('Dashboard.SimulationIncrement.Enabled') then + openspace.setPropertyValueSingle('Dashboard.SimulationIncrement.Enabled', false) + end end end) diff --git a/data/profiles/newhorizons.profile b/data/profiles/newhorizons.profile index 0bccbac92b..abe410ecc9 100644 --- a/data/profiles/newhorizons.profile +++ b/data/profiles/newhorizons.profile @@ -8,18 +8,20 @@ "camera": { "aim": "", "anchor": "NewHorizons", - "frame": "", + "frame": "Root", + "pitch": 0.036092, "position": { - "x": -65.72656, - "y": -72.39404, - "z": -21.1189 + "x": -111.9326, + "y": -35.20605, + "z": 33.42737 }, "type": "setNavigationState", "up": { - "x": 0.102164, - "y": -0.362945, - "z": 0.926193 - } + "x": -0.188963, + "y": 0.921904, + "z": 0.338209 + }, + "yaw": 0.0563239 }, "delta_times": [ 1.0, diff --git a/data/profiles/rosetta.profile b/data/profiles/rosetta.profile index b5f3c0ba9e..3f344c3893 100644 --- a/data/profiles/rosetta.profile +++ b/data/profiles/rosetta.profile @@ -98,7 +98,7 @@ "is_local": false, "key": "O", "name": "Toggle Philae trail", - "script": "openspace.setPropertyValueSingle(Scene.PhilaeTrail.Renderable.Enabled', not openspace.getPropertyValue(Scene.PhilaeTrail.Renderable.Enabled'));" + "script": "openspace.setPropertyValueSingle('Scene.PhilaeTrail.Renderable.Enabled', not openspace.getPropertyValue('Scene.PhilaeTrail.Renderable.Enabled'));" }, { "documentation": "Enables or disables the image projection on 67P.", @@ -106,7 +106,7 @@ "is_local": false, "key": "P", "name": "Toggle 67P projection", - "script": "openspace.setPropertyValueSingle(Scene.67P.Renderable.ProjectionComponent.PerformProjection', not openspace.getPropertyValue(Scene.67P.Renderable.ProjectionComponent.PerformProjection'));" + "script": "openspace.setPropertyValueSingle('Scene.67P.Renderable.ProjectionComponent.PerformProjection', not openspace.getPropertyValue('Scene.67P.Renderable.ProjectionComponent.PerformProjection'));" } ], "mark_nodes": [ diff --git a/ext/ghoul b/ext/ghoul index 8f7d89335b..e75af2a3be 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 8f7d89335b70b0ac2007d4dd12bafa640903a8fd +Subproject commit e75af2a3be7cf1b71db04e4a083efa899a2faa01 diff --git a/include/openspace/network/messagestructures.h b/include/openspace/network/messagestructures.h index ec27c27dd1..bea328662d 100644 --- a/include/openspace/network/messagestructures.h +++ b/include/openspace/network/messagestructures.h @@ -26,6 +26,8 @@ #define __OPENSPACE_CORE___MESSAGESTRUCTURES___H__ #include +#include +#include #include #include #include @@ -373,17 +375,31 @@ struct ScriptMessage { double _timestamp = 0.0; void serialize(std::vector& buffer) const { - size_t strLen = _script.size(); - size_t writeSize_bytes = sizeof(size_t); + uint32_t strLen = static_cast(_script.size()); - unsigned char const *p = reinterpret_cast(&strLen); - buffer.insert(buffer.end(), p, p + writeSize_bytes); + const char* p = reinterpret_cast(&strLen); + buffer.insert(buffer.end(), p, p + sizeof(uint32_t)); buffer.insert(buffer.end(), _script.begin(), _script.end()); }; void deserialize(const std::vector& buffer) { - _script.assign(buffer.begin(), buffer.end()); + const char* p = buffer.data(); + const uint32_t len = *reinterpret_cast(p); + + if (buffer.size() != (sizeof(uint32_t) + len)) { + LERRORC( + "ParallelPeer", + fmt::format( + "Received buffer with wrong size. Expected {} got {}", + len, buffer.size() + ) + ); + return; + } + + // We can skip over the first uint32_t that encoded the length + _script.assign(buffer.begin() + sizeof(uint32_t), buffer.end()); }; void write(std::ostream* out) const { diff --git a/include/openspace/network/parallelserver.h b/include/openspace/network/parallelserver.h index 18ce60e573..684ac62f2a 100644 --- a/include/openspace/network/parallelserver.h +++ b/include/openspace/network/parallelserver.h @@ -50,9 +50,6 @@ public: private: struct Peer { - //Peer(size_t id_, std::string name_, ParallelConnection parallelConnection_, - //ParallelConnection::Status status_, std::thread ) - size_t id; std::string name; ParallelConnection parallelConnection; @@ -87,7 +84,6 @@ private: void handleData(const Peer& peer, std::vector data); void handleHostshipRequest(std::shared_ptr peer, std::vector message); void handleHostshipResignation(Peer& peer); - void handleDisconnection(std::shared_ptr peer); void handleNewPeers(); void eventLoop(); diff --git a/modules/base/dashboard/dashboarditemangle.cpp b/modules/base/dashboard/dashboarditemangle.cpp index 991afa4520..e2a3c49b5d 100644 --- a/modules/base/dashboard/dashboarditemangle.cpp +++ b/modules/base/dashboard/dashboarditemangle.cpp @@ -404,7 +404,6 @@ void DashboardItemAngle::render(glm::vec2& penPosition) { std::fill(_buffer.begin(), _buffer.end(), 0); if (glm::length(a) == 0.0 || glm::length(b) == 0) { - penPosition.y -= _font->height(); char* end = fmt::format_to( _buffer.data(), "Could not compute angle at {} between {} and {}", @@ -412,13 +411,13 @@ void DashboardItemAngle::render(glm::vec2& penPosition) { ); std::string_view text = std::string_view(_buffer.data(), end - _buffer.data()); RenderFont(*_font, penPosition, text); + penPosition.y -= _font->height(); } else { const double angle = glm::degrees( glm::acos(glm::dot(a, b) / (glm::length(a) * glm::length(b))) ); - penPosition.y -= _font->height(); char* end = fmt::format_to( _buffer.data(), "Angle at {} between {} and {}: {} degrees", @@ -426,6 +425,7 @@ void DashboardItemAngle::render(glm::vec2& penPosition) { ); std::string_view text = std::string_view(_buffer.data(), end - _buffer.data()); RenderFont(*_font, penPosition, text); + penPosition.y -= _font->height(); } } diff --git a/modules/base/dashboard/dashboarditemdate.cpp b/modules/base/dashboard/dashboarditemdate.cpp index b003e06882..0cd58ffe29 100644 --- a/modules/base/dashboard/dashboarditemdate.cpp +++ b/modules/base/dashboard/dashboarditemdate.cpp @@ -113,12 +113,12 @@ DashboardItemDate::DashboardItemDate(const ghoul::Dictionary& dictionary) void DashboardItemDate::render(glm::vec2& penPosition) { ZoneScoped - penPosition.y -= _font->height(); RenderFont( *_font, penPosition, fmt::format("Date: {} UTC", global::timeManager.time().UTC()) ); + penPosition.y -= _font->height(); } glm::vec2 DashboardItemDate::size() const { diff --git a/modules/base/dashboard/dashboarditemdistance.cpp b/modules/base/dashboard/dashboarditemdistance.cpp index 655e7881a8..5780801960 100644 --- a/modules/base/dashboard/dashboarditemdistance.cpp +++ b/modules/base/dashboard/dashboarditemdistance.cpp @@ -444,7 +444,6 @@ void DashboardItemDistance::render(glm::vec2& penPosition) { dist = { convertedD, nameForDistanceUnit(unit, convertedD != 1.0) }; } - penPosition.y -= _font->height(); std::fill(_buffer.begin(), _buffer.end(), 0); char* end = fmt::format_to( _buffer.data(), @@ -454,6 +453,7 @@ void DashboardItemDistance::render(glm::vec2& penPosition) { std::string_view text = std::string_view(_buffer.data(), end - _buffer.data()); RenderFont(*_font, penPosition, text); + penPosition.y -= _font->height(); } glm::vec2 DashboardItemDistance::size() const { diff --git a/modules/base/dashboard/dashboarditemframerate.cpp b/modules/base/dashboard/dashboarditemframerate.cpp index 08c660c509..8471fa2280 100644 --- a/modules/base/dashboard/dashboarditemframerate.cpp +++ b/modules/base/dashboard/dashboarditemframerate.cpp @@ -149,6 +149,21 @@ namespace { throw ghoul::MissingCaseException(); } } + + [[ nodiscard ]] int nLines( + openspace::DashboardItemFramerate::FrametimeType frametimeType) + { + using FrametimeType = openspace::DashboardItemFramerate::FrametimeType; + switch (frametimeType) { + case FrametimeType::DtTimeAvg: return 1; + case FrametimeType::DtTimeExtremes: return 2; + case FrametimeType::DtStandardDeviation: return 1; + case FrametimeType::DtCoefficientOfVariation: return 1; + case FrametimeType::FPS: return 1; + case FrametimeType::FPSAvg: return 1; + default: throw ghoul::MissingCaseException(); + } + } } // namespace namespace openspace { @@ -309,13 +324,12 @@ void DashboardItemFramerate::render(glm::vec2& penPosition) { int nLines = output.empty() ? 0 : static_cast((std::count(output.begin(), output.end(), '\n') + 1)); - penPosition.y -= _font->height() * static_cast(nLines); - ghoul::fontrendering::FontRenderer::defaultRenderer().render( *_font, penPosition, output ); + penPosition.y -= _font->height() * static_cast(nLines); } glm::vec2 DashboardItemFramerate::size() const { diff --git a/modules/base/dashboard/dashboarditemmission.cpp b/modules/base/dashboard/dashboarditemmission.cpp index eb65bc8651..e2be50766f 100644 --- a/modules/base/dashboard/dashboarditemmission.cpp +++ b/modules/base/dashboard/dashboarditemmission.cpp @@ -209,7 +209,6 @@ void DashboardItemMission::render(glm::vec2& penPosition) { 1.0 - remaining / phase->timeRange().duration() ); const std::string progress = progressToStr(25, t); - penPosition.y -= _font->height(); RenderFont( *_font, penPosition, @@ -219,16 +218,17 @@ void DashboardItemMission::render(glm::vec2& penPosition) { ), currentMissionColor ); + penPosition.y -= _font->height(); } else { if (!phase->name().empty()) { - penPosition.y -= _font->height(); RenderFont( *_font, penPosition, phase->name(), nonCurrentMissionColor ); + penPosition.y -= _font->height(); } } penPosition.x -= depth * PixelIndentation; diff --git a/modules/base/dashboard/dashboarditemparallelconnection.cpp b/modules/base/dashboard/dashboarditemparallelconnection.cpp index 54a2642387..be39c9bac4 100644 --- a/modules/base/dashboard/dashboarditemparallelconnection.cpp +++ b/modules/base/dashboard/dashboarditemparallelconnection.cpp @@ -121,6 +121,8 @@ void DashboardItemParallelConnection::render(glm::vec2& penPosition) { const size_t nConnections = global::parallelPeer.nConnections(); const std::string& hostName = global::parallelPeer.hostName(); + int nLines = 1; + std::string connectionInfo; int nClients = static_cast(nConnections); if (status == ParallelConnection::Status::Host) { @@ -158,11 +160,13 @@ void DashboardItemParallelConnection::render(glm::vec2& penPosition) { else if (nClients == 1) { connectionInfo += "You are the only client"; } + + nLines = 2; } if (!connectionInfo.empty()) { - penPosition.y -= _font->height(); RenderFont(*_font, penPosition, connectionInfo); + penPosition.y -= _font->height() * nLines; } } diff --git a/modules/base/dashboard/dashboarditempropertyvalue.cpp b/modules/base/dashboard/dashboarditempropertyvalue.cpp index d388e379fb..052370463d 100644 --- a/modules/base/dashboard/dashboarditempropertyvalue.cpp +++ b/modules/base/dashboard/dashboarditempropertyvalue.cpp @@ -163,8 +163,8 @@ void DashboardItemPropertyValue::render(glm::vec2& penPosition) { std::string value; _property->getStringValue(value); - penPosition.y -= _font->height(); RenderFont(*_font, penPosition, fmt::format(_displayString.value(), value)); + penPosition.y -= _font->height(); } } diff --git a/modules/base/dashboard/dashboarditemsimulationincrement.cpp b/modules/base/dashboard/dashboarditemsimulationincrement.cpp index def2b6f9e1..06b6375cf0 100644 --- a/modules/base/dashboard/dashboarditemsimulationincrement.cpp +++ b/modules/base/dashboard/dashboarditemsimulationincrement.cpp @@ -203,7 +203,6 @@ void DashboardItemSimulationIncrement::render(glm::vec2& penPosition) { std::string pauseText = global::timeManager.isPaused() ? " (Paused)" : ""; - penPosition.y -= _font->height(); if (targetDt != currentDt && !global::timeManager.isPaused()) { // We are in the middle of a transition RenderFont( @@ -227,6 +226,7 @@ void DashboardItemSimulationIncrement::render(glm::vec2& penPosition) { ) ); } + penPosition.y -= _font->height(); } glm::vec2 DashboardItemSimulationIncrement::size() const { diff --git a/modules/base/dashboard/dashboarditemvelocity.cpp b/modules/base/dashboard/dashboarditemvelocity.cpp index e89fedb6be..7c12cc40be 100644 --- a/modules/base/dashboard/dashboarditemvelocity.cpp +++ b/modules/base/dashboard/dashboarditemvelocity.cpp @@ -208,7 +208,6 @@ void DashboardItemVelocity::render(glm::vec2& penPosition) { dist = { convertedD, nameForDistanceUnit(unit, convertedD != 1.0) }; } - penPosition.y -= _font->height(); RenderFont( *_font, penPosition, @@ -216,6 +215,7 @@ void DashboardItemVelocity::render(glm::vec2& penPosition) { "Camera velocity: {} {}/s", dist.first, dist.second ) ); + penPosition.y -= _font->height(); _prevPosition = currentPos; } diff --git a/modules/base/rendering/renderableplaneimageonline.cpp b/modules/base/rendering/renderableplaneimageonline.cpp index 148da49c50..3914cd2ae0 100644 --- a/modules/base/rendering/renderableplaneimageonline.cpp +++ b/modules/base/rendering/renderableplaneimageonline.cpp @@ -120,24 +120,30 @@ void RenderablePlaneImageOnline::update(const UpdateData&) { return; } - std::unique_ptr texture = - ghoul::io::TextureReader::ref().loadTexture( - reinterpret_cast(imageFile.buffer), - imageFile.size, - imageFile.format - ); + try { + std::unique_ptr texture = + ghoul::io::TextureReader::ref().loadTexture( + reinterpret_cast(imageFile.buffer), + imageFile.size, + imageFile.format + ); - if (texture) { - // Images don't need to start on 4-byte boundaries, for example if the - // image is only RGB - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + if (texture) { + // Images don't need to start on 4-byte boundaries, for example if the + // image is only RGB + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - texture->uploadTexture(); - texture->setFilter(ghoul::opengl::Texture::FilterMode::LinearMipMap); - texture->purgeFromRAM(); + texture->uploadTexture(); + texture->setFilter(ghoul::opengl::Texture::FilterMode::LinearMipMap); + texture->purgeFromRAM(); - _texture = std::move(texture); + _texture = std::move(texture); + _textureIsDirty = false; + } + } + catch (const ghoul::io::TextureReader::InvalidLoadException& e) { _textureIsDirty = false; + LERRORC(e.component, e.message); } } } diff --git a/modules/base/rendering/screenspaceimageonline.cpp b/modules/base/rendering/screenspaceimageonline.cpp index 10ad87dbde..5dd4b7c736 100644 --- a/modules/base/rendering/screenspaceimageonline.cpp +++ b/modules/base/rendering/screenspaceimageonline.cpp @@ -129,25 +129,31 @@ void ScreenSpaceImageOnline::update() { return; } - std::unique_ptr texture = - ghoul::io::TextureReader::ref().loadTexture( - reinterpret_cast(imageFile.buffer), - imageFile.size, - imageFile.format - ); + try { + std::unique_ptr texture = + ghoul::io::TextureReader::ref().loadTexture( + reinterpret_cast(imageFile.buffer), + imageFile.size, + imageFile.format + ); - if (texture) { - // Images don't need to start on 4-byte boundaries, for example if the - // image is only RGB - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + if (texture) { + // Images don't need to start on 4-byte boundaries, for example if the + // image is only RGB + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - texture->uploadTexture(); - texture->setFilter(ghoul::opengl::Texture::FilterMode::LinearMipMap); - texture->purgeFromRAM(); + texture->uploadTexture(); + texture->setFilter(ghoul::opengl::Texture::FilterMode::LinearMipMap); + texture->purgeFromRAM(); - _texture = std::move(texture); - _objectSize = _texture->dimensions(); + _texture = std::move(texture); + _objectSize = _texture->dimensions(); + _textureIsDirty = false; + } + } + catch (const ghoul::io::TextureReader::InvalidLoadException& e) { _textureIsDirty = false; + LERRORC(e.component, e.message); } } } diff --git a/modules/digitaluniverse/rendering/renderablebillboardscloud.cpp b/modules/digitaluniverse/rendering/renderablebillboardscloud.cpp index 54a9dc6649..7481deea0c 100644 --- a/modules/digitaluniverse/rendering/renderablebillboardscloud.cpp +++ b/modules/digitaluniverse/rendering/renderablebillboardscloud.cpp @@ -186,6 +186,12 @@ namespace { "astronomical objects." }; + constexpr openspace::properties::Property::PropertyInfo OptionColorRangeInfo = { + "OptionColorRange", + "Option Color Range", + "This value changes the range of values to be mapped with the current color map." + }; + constexpr openspace::properties::Property::PropertyInfo SizeOptionInfo = { "SizeOption", "Size Option Variable", @@ -251,6 +257,19 @@ namespace { "Enable pixel size control.", "Enable pixel size control for rectangular projections." }; + + constexpr openspace::properties::Property::PropertyInfo UseLinearFiltering = { + "UseLinearFiltering", + "Use Linear Filtering", + "Determines whether the provided color map should be sampled nearest neighbor " + "(=off) or linearly (=on" + }; + + constexpr openspace::properties::Property::PropertyInfo SetRangeFromData = { + "SetRangeFromData", + "Set Data Range from Data", + "Set the data range based on the available data" + }; } // namespace namespace openspace { @@ -416,6 +435,12 @@ documentation::Documentation RenderableBillboardsCloud::Documentation() { new BoolVerifier, Optional::Yes, PixelSizeControlInfo.description + }, + { + UseLinearFiltering.identifier, + new BoolVerifier, + Optional::Yes, + UseLinearFiltering.description } } }; @@ -435,6 +460,8 @@ RenderableBillboardsCloud::RenderableBillboardsCloud(const ghoul::Dictionary& di , _drawLabels(DrawLabelInfo, false) , _pixelSizeControl(PixelSizeControlInfo, false) , _colorOption(ColorOptionInfo, properties::OptionProperty::DisplayType::Dropdown) + , _optionColorRangeData(OptionColorRangeInfo, glm::vec2(0.f)) + , _datavarSizeOption( SizeOptionInfo, properties::OptionProperty::DisplayType::Dropdown @@ -451,6 +478,8 @@ RenderableBillboardsCloud::RenderableBillboardsCloud(const ghoul::Dictionary& di , _correctionSizeEndDistance(CorrectionSizeEndDistanceInfo, 17.f, 12.f, 25.f) , _correctionSizeFactor(CorrectionSizeFactorInfo, 8.f, 0.f, 20.f) , _renderOption(RenderOptionInfo, properties::OptionProperty::DisplayType::Dropdown) + , _setRangeFromData(SetRangeFromData) + , _useLinearFiltering(UseLinearFiltering, false) { documentation::testSpecificationAndThrow( Documentation(), @@ -473,7 +502,7 @@ RenderableBillboardsCloud::RenderableBillboardsCloud(const ghoul::Dictionary& di _renderOption.addOption(RenderOptionViewDirection, "Camera View Direction"); _renderOption.addOption(RenderOptionPositionNormal, "Camera Position Normal"); - _renderOption = RenderOptionPositionNormal; + _renderOption = RenderOptionViewDirection; if (dictionary.hasKeyAndValue(RenderOptionInfo.identifier)) { const std::string o = dictionary.value(RenderOptionInfo.identifier); @@ -548,6 +577,8 @@ RenderableBillboardsCloud::RenderableBillboardsCloud(const ghoul::Dictionary& di } _colorOption.onChange([&]() { _dataIsDirty = true; + const glm::vec2 colorRange = _colorRangeData[_colorOption.value()]; + _optionColorRangeData = colorRange; _colorOptionString = _optionConversionMap[_colorOption.value()]; }); addProperty(_colorOption); @@ -561,7 +592,14 @@ RenderableBillboardsCloud::RenderableBillboardsCloud(const ghoul::Dictionary& di rangeDataDict.value(std::to_string(i + 1)) ); } + _optionColorRangeData = _colorRangeData[_colorRangeData.size() - 1]; } + _optionColorRangeData.onChange([&]() { + const glm::vec2 colorRange = _optionColorRangeData; + _colorRangeData[_colorOption.value()] = colorRange; + _dataIsDirty = true; + }); + addProperty(_optionColorRangeData); if (dictionary.hasKey(ExactColorMapInfo.identifier)) { _isColorMapExact = dictionary.value(ExactColorMapInfo.identifier); @@ -696,6 +734,28 @@ RenderableBillboardsCloud::RenderableBillboardsCloud(const ghoul::Dictionary& di _pixelSizeControl = dictionary.value(PixelSizeControlInfo.identifier); addProperty(_pixelSizeControl); } + + _setRangeFromData.onChange([this]() { + const int colorMapInUse = + _hasColorMapFile ? _variableDataPositionMap[_colorOptionString] : 0; + + float minValue = std::numeric_limits::max(); + float maxValue = std::numeric_limits::min(); + for (size_t i = 0; i < _fullData.size(); i += _nValuesPerAstronomicalObject) { + float colorIdx = _fullData[i + 3 + colorMapInUse]; + maxValue = colorIdx >= maxValue ? colorIdx : maxValue; + minValue = colorIdx < minValue ? colorIdx : minValue; + } + + _optionColorRangeData = glm::vec2(minValue, maxValue); + }); + addProperty(_setRangeFromData); + + if (dictionary.hasKey(UseLinearFiltering.identifier)) { + _useLinearFiltering = dictionary.value(UseLinearFiltering.identifier); + } + _useLinearFiltering.onChange([&]() { _dataIsDirty = true; }); + addProperty(_useLinearFiltering); } bool RenderableBillboardsCloud::isReady() const { @@ -1651,11 +1711,9 @@ void RenderableBillboardsCloud::createDataSlice() { _slicedData.push_back(position[j]); biggestCoord = biggestCoord < position[j] ? position[j] : biggestCoord; } - // Note: if exact colormap option is not selected, the first color and the // last color in the colormap file are the outliers colors. - int variableColor = static_cast(_fullData[i + 3 + colorMapInUse]); - int colorIndex = 0; + float variableColor = _fullData[i + 3 + colorMapInUse]; float cmax, cmin; if (_colorRangeData.empty()) { @@ -1669,19 +1727,51 @@ void RenderableBillboardsCloud::createDataSlice() { } if (_isColorMapExact) { - colorIndex = variableColor + cmin; + int colorIndex = variableColor + cmin; + for (int j = 0; j < 4; ++j) { + _slicedData.push_back(_colorMapData[colorIndex][j]); + } } else { - float ncmap = static_cast(_colorMapData.size()); - float normalization = ((cmax != cmin) && (ncmap > 2)) ? - (ncmap - 2) / (cmax - cmin) : 0; - colorIndex = (variableColor - cmin) * normalization + 1; - colorIndex = colorIndex < 0 ? 0 : colorIndex; - colorIndex = colorIndex >= ncmap ? ncmap - 1 : colorIndex; - } + if (_useLinearFiltering) { + const float value = variableColor; - for (int j = 0; j < 4; ++j) { - _slicedData.push_back(_colorMapData[colorIndex][j]); + float valueT = (value - cmin) / (cmax - cmin); // in [0, 1) + valueT = std::clamp(valueT, 0.f, 1.f); + + const float idx = valueT * (_colorMapData.size() - 1); + const int floorIdx = static_cast(std::floor(idx)); + const int ceilIdx = static_cast(std::ceil(idx)); + + const glm::vec4 floorColor = _colorMapData[floorIdx]; + const glm::vec4 ceilColor = _colorMapData[ceilIdx]; + + if (floorColor != ceilColor) { + const glm::vec4 c = floorColor + idx * (ceilColor - floorColor); + _slicedData.push_back(c.r); + _slicedData.push_back(c.g); + _slicedData.push_back(c.b); + _slicedData.push_back(c.a); + } + else { + _slicedData.push_back(floorColor.r); + _slicedData.push_back(floorColor.g); + _slicedData.push_back(floorColor.b); + _slicedData.push_back(floorColor.a); + } + } + else { + float ncmap = static_cast(_colorMapData.size()); + float normalization = ((cmax != cmin) && (ncmap > 2)) ? + (ncmap - 2) / (cmax - cmin) : 0; + int colorIndex = (variableColor - cmin) * normalization + 1; + colorIndex = colorIndex < 0 ? 0 : colorIndex; + colorIndex = colorIndex >= ncmap ? ncmap - 1 : colorIndex; + + for (int j = 0; j < 4; ++j) { + _slicedData.push_back(_colorMapData[colorIndex][j]); + } + } } if (_hasDatavarSize) { diff --git a/modules/digitaluniverse/rendering/renderablebillboardscloud.h b/modules/digitaluniverse/rendering/renderablebillboardscloud.h index 1c95698927..e4e6d40d9b 100644 --- a/modules/digitaluniverse/rendering/renderablebillboardscloud.h +++ b/modules/digitaluniverse/rendering/renderablebillboardscloud.h @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -123,6 +124,7 @@ private: properties::BoolProperty _drawLabels; properties::BoolProperty _pixelSizeControl; properties::OptionProperty _colorOption; + properties::Vec2Property _optionColorRangeData; properties::OptionProperty _datavarSizeOption; properties::Vec2Property _fadeInDistance; properties::BoolProperty _disableFadeInDistance; @@ -130,6 +132,8 @@ private: properties::FloatProperty _billboardMinSize; properties::FloatProperty _correctionSizeEndDistance; properties::FloatProperty _correctionSizeFactor; + properties::BoolProperty _useLinearFiltering; + properties::TriggerProperty _setRangeFromData; // DEBUG: properties::OptionProperty _renderOption; diff --git a/modules/digitaluniverse/rendering/renderableplanescloud.cpp b/modules/digitaluniverse/rendering/renderableplanescloud.cpp index 863c844aac..bf103c7920 100644 --- a/modules/digitaluniverse/rendering/renderableplanescloud.cpp +++ b/modules/digitaluniverse/rendering/renderableplanescloud.cpp @@ -299,7 +299,7 @@ documentation::Documentation RenderablePlanesCloud::Documentation() { RenderablePlanesCloud::RenderablePlanesCloud(const ghoul::Dictionary& dictionary) : Renderable(dictionary) - , _scaleFactor(ScaleFactorInfo, 1.f, 0.f, 10000.f) + , _scaleFactor(ScaleFactorInfo, 1.f, 0.f, 100.f) , _textColor(TextColorInfo, glm::vec3(1.f), glm::vec3(0.f), glm::vec3(1.f)) , _textOpacity(TextOpacityInfo, 1.f, 0.f, 1.f) , _textSize(TextSizeInfo, 8.0, 0.5, 24.0) diff --git a/modules/exoplanets/exoplanetshelper.cpp b/modules/exoplanets/exoplanetshelper.cpp index 05b8105e17..ed624d67de 100644 --- a/modules/exoplanets/exoplanetshelper.cpp +++ b/modules/exoplanets/exoplanetshelper.cpp @@ -24,6 +24,7 @@ #include +#include #include #include #include @@ -81,6 +82,17 @@ std::string_view csvStarName(std::string_view name) { return name; } +bool hasSufficientData(const Exoplanet& p) { + bool invalidPos = std::isnan(p.positionX) + || std::isnan(p.positionY) + || std::isnan(p.positionZ); + + bool hasSemiMajorAxis = !std::isnan(p.a); + bool hasOrbitalPeriod = !std::isnan(p.per); + + return !invalidPos && hasSemiMajorAxis && hasOrbitalPeriod; +} + std::string starColor(float bv) { std::ifstream colorMap(absPath(BvColormapPath), std::ios::in); @@ -126,49 +138,54 @@ glm::dmat4 computeOrbitPlaneRotationMatrix(float i, float bigom, float omega) { return orbitPlaneRotation; } -// Rotate the original coordinate system (where x is pointing to First Point of Aries) -// so that x is pointing from star to the sun. -// Modified from "http://www.opengl-tutorial.org/intermediate-tutorials/ -// tutorial-17-quaternions/ #how-do-i-find-the-rotation-between-2-vectors" -glm::dmat3 exoplanetSystemRotation(glm::dvec3 start, glm::dvec3 end) { - glm::quat rotationQuat; - glm::dvec3 rotationAxis; - const float cosTheta = static_cast(glm::dot(start, end)); - constexpr float Epsilon = 1E-3f; +glm::dmat3 computeSystemRotation(glm::dvec3 starPosition) { + const glm::dvec3 sunPosition = glm::dvec3(0.0, 0.0, 0.0); + const glm::dvec3 starToSunVec = glm::normalize(sunPosition - starPosition); + const glm::dvec3 galacticNorth = glm::dvec3(0.0, 0.0, 1.0); - if (cosTheta < -1.f + Epsilon) { - // special case when vectors in opposite directions: - // there is no "ideal" rotation axis - // So guess one; any will do as long as it's perpendicular to start vector - rotationAxis = glm::cross(glm::dvec3(0.0, 0.0, 1.0), start); - if (glm::length2(rotationAxis) < 0.01f) { - // bad luck, they were parallel, try again! - rotationAxis = glm::cross(glm::dvec3(1.0, 0.0, 0.0), start); - } + const glm::dmat3 galacticToCelestialMatrix = + SpiceManager::ref().positionTransformMatrix("GALACTIC", "J2000", 0.0); - rotationAxis = glm::normalize(rotationAxis); - rotationQuat = glm::quat(glm::radians(180.f), rotationAxis); - return glm::dmat3(glm::toMat4(rotationQuat)); - } - - rotationAxis = glm::cross(start, end); - - const float s = sqrt((1.f + cosTheta) * 2.f); - const float invs = 1.f / s; - - rotationQuat = glm::quat( - s * 0.5f, - static_cast(rotationAxis.x * invs), - static_cast(rotationAxis.y * invs), - static_cast(rotationAxis.z * invs) + const glm::dvec3 celestialNorth = glm::normalize( + galacticToCelestialMatrix * galacticNorth ); - return glm::dmat3(glm::toMat4(rotationQuat)); + // Earth's north vector projected onto the skyplane, the plane perpendicular to the + // viewing vector (starToSunVec) + const float celestialAngle = static_cast(glm::dot( + celestialNorth, + starToSunVec + )); + glm::dvec3 northProjected = glm::normalize( + celestialNorth - (celestialAngle / glm::length(starToSunVec)) * starToSunVec + ); + + const glm::dvec3 beta = glm::normalize(glm::cross(starToSunVec, northProjected)); + + return glm::dmat3( + northProjected.x, + northProjected.y, + northProjected.z, + beta.x, + beta.y, + beta.z, + starToSunVec.x, + starToSunVec.y, + starToSunVec.z + ); } std::string createIdentifier(std::string name) { std::replace(name.begin(), name.end(), ' ', '_'); + sanitizeNameString(name); return name; } +void sanitizeNameString(std::string& s) { + // We want to avoid quotes and apostrophes in names, since they cause problems + // when a string is translated to a script call + s.erase(remove(s.begin(), s.end(), '\"'), s.end()); + s.erase(remove(s.begin(), s.end(), '\''), s.end()); +} + } // namespace openspace::exoplanets diff --git a/modules/exoplanets/exoplanetshelper.h b/modules/exoplanets/exoplanetshelper.h index 81bd3221c7..29e6b5a7ac 100644 --- a/modules/exoplanets/exoplanetshelper.h +++ b/modules/exoplanets/exoplanetshelper.h @@ -72,6 +72,9 @@ std::string_view speckStarName(std::string_view name); // Convert speck-file specific names to the corresponding name in the csv data file std::string_view csvStarName(std::string_view name); +// Check if the exoplanet p has sufficient data for visualization +bool hasSufficientData(const Exoplanet& p); + // Compute star color in RGB from b-v color index std::string starColor(float bv); @@ -79,11 +82,13 @@ glm::dmat4 computeOrbitPlaneRotationMatrix(float i, float bigom, float omega); // Rotate the original coordinate system (where x is pointing to First Point of Aries) // so that x is pointing from star to the sun. -glm::dmat3 exoplanetSystemRotation(glm::dvec3 start, glm::dvec3 end); +glm::dmat3 computeSystemRotation(glm::dvec3 starPosition); // Create an identifier without whitespaces std::string createIdentifier(std::string name); +void sanitizeNameString(std::string& s); + } // namespace openspace::exoplanets #endif // __OPENSPACE_MODULE_EXOPLANETS___EXOPLANETSMODULE___H__ diff --git a/modules/exoplanets/exoplanetsmodule_lua.inl b/modules/exoplanets/exoplanetsmodule_lua.inl index a44ef59f87..5d69860264 100644 --- a/modules/exoplanets/exoplanetsmodule_lua.inl +++ b/modules/exoplanets/exoplanetsmodule_lua.inl @@ -24,12 +24,10 @@ #include #include -#include #include #include #include #include -#include #include #include #include @@ -37,13 +35,7 @@ #include #include #include -#include -#include -#include -#include -#include #include -#include #include namespace { @@ -67,7 +59,11 @@ void createExoplanetSystem(std::string_view starName) { const std::string starNameSpeck = std::string(speckStarName(starName)); const std::string starIdentifier = createIdentifier(starNameSpeck); - const std::string guiPath = ExoplanetsGuiPath + starNameSpeck; + + std::string sanitizedStarName = starNameSpeck; + sanitizeNameString(sanitizedStarName); + + const std::string guiPath = ExoplanetsGuiPath + sanitizedStarName; SceneGraphNode* existingStarNode = sceneGraphNode(starIdentifier); if (existingStarNode) { @@ -118,9 +114,18 @@ void createExoplanetSystem(std::string_view starName) { data.seekg(location); data.read(reinterpret_cast(&p), sizeof(Exoplanet)); + sanitizeNameString(name); planetNames.push_back(name); planetSystem.push_back(p); found = true; + + if (!hasSufficientData(p)) { + LERROR(fmt::format( + "Insufficient data available for visualizion of exoplanet system: '{}'", + starName + )); + return; + } } } @@ -132,56 +137,13 @@ void createExoplanetSystem(std::string_view starName) { return; } - bool notEnoughData = std::isnan(p.positionX) || std::isnan(p.a) || std::isnan(p.per); - - if (notEnoughData) { - LERROR(fmt::format( - "Insufficient data available for representing the exoplanet system: '{}'", - starName - )); - return; - } - const glm::dvec3 starPosition = glm::dvec3( p.positionX * distanceconstants::Parsec, p.positionY * distanceconstants::Parsec, p.positionZ * distanceconstants::Parsec ); - const glm::dvec3 sunPosition = glm::dvec3(0.0, 0.0, 0.0); - const glm::dvec3 starToSunVec = glm::normalize(sunPosition - starPosition); - const glm::dvec3 galacticNorth = glm::dvec3(0.0, 0.0, 1.0); - - const glm::dmat3 galaxticToCelestialMatrix = - SpiceManager::ref().positionTransformMatrix("GALACTIC", "J2000", 0.0); - - const glm::dvec3 celestialNorth = glm::normalize( - galaxticToCelestialMatrix * galacticNorth - ); - - // Earth's north vector projected onto the skyplane, the plane perpendicular to the - // viewing vector (starToSunVec) - const float celestialAngle = static_cast(glm::dot( - celestialNorth, - starToSunVec - )); - glm::dvec3 northProjected = glm::normalize( - celestialNorth - (celestialAngle / glm::length(starToSunVec)) * starToSunVec - ); - - const glm::dvec3 beta = glm::normalize(glm::cross(starToSunVec, northProjected)); - - const glm::dmat3 exoplanetSystemRotation = glm::dmat3( - northProjected.x, - northProjected.y, - northProjected.z, - beta.x, - beta.y, - beta.z, - starToSunVec.x, - starToSunVec.y, - starToSunVec.z - ); + const glm::dmat3 exoplanetSystemRotation = computeSystemRotation(starPosition); // Star renderable globe, if we have a radius and bv color index std::string starGlobeRenderableString; @@ -243,7 +205,7 @@ void createExoplanetSystem(std::string_view starName) { "}," "Tag = {'exoplanet_system'}," "GUI = {" - "Name = '" + starNameSpeck + " (Star)'," + "Name = '" + sanitizedStarName + " (Star)'," "Path = '" + guiPath + "'" "}" "}"; @@ -261,15 +223,18 @@ void createExoplanetSystem(std::string_view starName) { if (std::isnan(planet.ecc)) { planet.ecc = 0.f; } - if (std::isnan(planet.i)) { - planet.i = 90.f; - } - if (std::isnan(planet.bigOmega)) { - planet.bigOmega = 180.f; - } - if (std::isnan(planet.omega)) { - planet.omega = 90.f; - } + + // KeplerTranslation requires angles in range [0, 360] + auto validAngle = [](float angle, float defaultValue) { + if (std::isnan(angle)) { return defaultValue; } + if (angle < 0.f) { return angle + 360.f; } + if (angle > 360.f) { return angle - 360.f; } + }; + + planet.i = validAngle(planet.i, 90.f); + planet.bigOmega = validAngle(planet.bigOmega, 180.f); + planet.omega = validAngle(planet.omega, 90.f); + Time epoch; std::string sEpoch; if (!std::isnan(planet.tt)) { @@ -472,10 +437,22 @@ int removeExoplanetSystem(lua_State* L) { return 0; } -std::vector readHostStarNames(std::ifstream& lookupTableFile) { +std::vector hostStarsWithSufficientData() { std::vector names; std::string line; + std::ifstream lookupTableFile(absPath(LookUpTablePath)); + if (!lookupTableFile.good()) { + LERROR(fmt::format("Failed to open lookup table file '{}'", LookUpTablePath)); + return {}; + } + + std::ifstream data(absPath(ExoplanetsDataPath), std::ios::in | std::ios::binary); + if (!data.good()) { + LERROR(fmt::format("Failed to open data file '{}'", ExoplanetsDataPath)); + return {}; + } + // Read number of lines int nExoplanets = 0; while (getline(lookupTableFile, line)) { @@ -485,13 +462,26 @@ std::vector readHostStarNames(std::ifstream& lookupTableFile) { lookupTableFile.seekg(0); names.reserve(nExoplanets); + Exoplanet p; while (getline(lookupTableFile, line)) { std::stringstream ss(line); std::string name; getline(ss, name, ','); // Remove the last two characters, that specify the planet name = name.substr(0, name.size() - 2); - names.push_back(name); + + // Don't want to list systems where there is not enough data to visualize. + // So, test if there is before adding the name to the list. + std::string location_s; + getline(ss, location_s); + long location = std::stol(location_s.c_str()); + + data.seekg(location); + data.read(reinterpret_cast(&p), sizeof(Exoplanet)); + + if (hasSufficientData(p)) { + names.push_back(name); + } } // For easier read, sort by names and remove duplicates @@ -504,14 +494,7 @@ std::vector readHostStarNames(std::ifstream& lookupTableFile) { int getListOfExoplanets(lua_State* L) { ghoul::lua::checkArgumentsAndThrow(L, 0, "lua::getListOfExoplanets"); - std::ifstream file(absPath(LookUpTablePath)); - if (!file.good()) { - return ghoul::lua::luaError( - L, fmt::format("Failed to open file '{}'", LookUpTablePath - )); - } - - std::vector names = readHostStarNames(file); + std::vector names = hostStarsWithSufficientData(); lua_newtable(L); int number = 1; @@ -527,14 +510,7 @@ int getListOfExoplanets(lua_State* L) { int listAvailableExoplanetSystems(lua_State* L) { ghoul::lua::checkArgumentsAndThrow(L, 0, "lua::listAvailableExoplanetSystems"); - std::ifstream file(absPath(LookUpTablePath)); - if (!file.good()) { - return ghoul::lua::luaError( - L, fmt::format("Failed to open file '{}'", LookUpTablePath - )); - } - - std::vector names = readHostStarNames(file); + std::vector names = hostStarsWithSufficientData(); std::string output; for (auto it = names.begin(); it != names.end(); ++it) { diff --git a/modules/exoplanets/shaders/orbitdisc_fs.glsl b/modules/exoplanets/shaders/orbitdisc_fs.glsl index 373ea936dc..bf8cb01c5c 100644 --- a/modules/exoplanets/shaders/orbitdisc_fs.glsl +++ b/modules/exoplanets/shaders/orbitdisc_fs.glsl @@ -56,12 +56,16 @@ Fragment getFragment() { // Moving the origin to the center vec2 st = (vs_st - vec2(0.5)) * 2.0; + float offsetLower = textureOffset.x; + float offsetUpper = textureOffset.y; + float offsetIntervalSize = offsetLower + offsetUpper; + float AUpper = semiMajorAxis; float BUpper = semiMinorAxis(AUpper, eccentricity); float CUpper = sqrt(AUpper*AUpper - BUpper*BUpper); float outerApoapsisDistance = AUpper * (1 + eccentricity); - float ALower = AUpper - AstronomicalUnit * (textureOffset.x + textureOffset.y); + float ALower = AUpper - AstronomicalUnit * offsetIntervalSize; float BLower = semiMinorAxis(ALower, eccentricity); float CLower = sqrt(ALower*ALower - BLower*BLower); float innerApoapsisDistance = ALower * (1 + eccentricity); @@ -91,18 +95,28 @@ Fragment getFragment() { // Find outer ellipse: where along the direction does the equation == 1? float denominator = pow(BU_n * dir.x, 2.0) + pow(AU_n * dir.y, 2.0); - float first = -(pow(BU_n, 2.0) * dir.x * CU_n) / denominator; - float second = pow((pow(BU_n, 2.0) * dir.x * CU_n) / denominator, 2.0); + float first = -(BU_n * BU_n * dir.x * CU_n) / denominator; + float second = pow((BU_n * BU_n * dir.x * CU_n) / denominator, 2.0); float third = (pow(BU_n * CU_n, 2.0) - pow(AU_n * BU_n, 2.0)) / denominator; float scale = first + sqrt(second - third); - vec2 max = dir * scale; - vec2 min = max * (innerApoapsisDistance / outerApoapsisDistance); + vec2 outerPoint = dir * scale; + vec2 innerPoint = outerPoint * (innerApoapsisDistance / outerApoapsisDistance); - float distance1 = distance(max, min); - float distance2 = distance(max, st); - float textureCoord = distance2 / distance1; + float discWidth = distance(outerPoint, innerPoint); + float distanceFromOuterEdge = distance(outerPoint, st); + float textureCoord = distanceFromOuterEdge / discWidth; + + // Scale texture coordinate to handle asymmetric offset intervals + float textureMid = offsetUpper / offsetIntervalSize; + + if(textureCoord > textureMid) { + textureCoord = 0.5 + 0.5 * (textureCoord - textureMid) / (1.0 - textureMid); + } + else { + textureCoord = 0.5 * textureCoord / textureMid; + } vec4 diffuse = texture(discTexture, textureCoord); diffuse.a *= opacity; diff --git a/modules/globebrowsing/shaders/texturetilemapping.hglsl b/modules/globebrowsing/shaders/texturetilemapping.hglsl index a866211b85..b6bcf749fd 100644 --- a/modules/globebrowsing/shaders/texturetilemapping.hglsl +++ b/modules/globebrowsing/shaders/texturetilemapping.hglsl @@ -363,7 +363,7 @@ vec4 calculateNight(vec4 currentColor, vec2 uv, vec3 levelWeights, // Filter to night side vec4 newColor = vec4(cosineFactor * colorSample.xyz, adjustedAlpha); - color = blendNightLayers#{i}(currentColor, newColor, adjustedAlpha); + color = blendNightLayers#{i}(color, newColor, adjustedAlpha); } #endfor diff --git a/modules/globebrowsing/src/dashboarditemglobelocation.cpp b/modules/globebrowsing/src/dashboarditemglobelocation.cpp index d23f1cf868..a696958163 100644 --- a/modules/globebrowsing/src/dashboarditemglobelocation.cpp +++ b/modules/globebrowsing/src/dashboarditemglobelocation.cpp @@ -139,10 +139,10 @@ DashboardItemGlobeLocation::DashboardItemGlobeLocation( auto updateFormatString = [this]() { using namespace fmt::literals; - _formatString = - "Position: {{:03.{0}f}}{{}}, {{:03.{0}f}}{{}} Altitude: {{}} {{}}"_format( - _significantDigits.value() - ); + _formatString = fmt::format( + "Position: {{:03.{0}f}}{{}}, {{:03.{0}f}}{{}} Altitude: {{:03.{0}f}} {{}}", + _significantDigits.value() + ); }; _significantDigits.onChange(updateFormatString); addProperty(_significantDigits); @@ -199,8 +199,6 @@ void DashboardItemGlobeLocation::render(glm::vec2& penPosition) { std::pair dist = simplifyDistance(altitude); - penPosition.y -= _font->height(); - std::fill(_buffer.begin(), _buffer.end(), 0); char* end = fmt::format_to( _buffer.data(), @@ -212,7 +210,9 @@ void DashboardItemGlobeLocation::render(glm::vec2& penPosition) { std::string_view text = std::string_view(_buffer.data(), end - _buffer.data()); RenderFont(*_font, penPosition, text); + penPosition.y -= _font->height(); } + glm::vec2 DashboardItemGlobeLocation::size() const { ZoneScoped diff --git a/modules/globebrowsing/src/renderableglobe.cpp b/modules/globebrowsing/src/renderableglobe.cpp index 697c32f96e..4ea6fb38e7 100644 --- a/modules/globebrowsing/src/renderableglobe.cpp +++ b/modules/globebrowsing/src/renderableglobe.cpp @@ -1050,6 +1050,7 @@ void RenderableGlobe::renderChunks(const RenderData& data, RendererTasks&, if (_globalRenderer.updatedSinceLastCall) { const std::array& layerGroups = _layerManager.layerGroups(); + for (size_t i = 0; i < layerGroups.size(); ++i) { const std::string& nameBase = layergroupid::LAYER_GROUP_IDENTIFIERS[i]; _globalRenderer.gpuLayerGroups[i].bind( @@ -1074,6 +1075,7 @@ void RenderableGlobe::renderChunks(const RenderData& data, RendererTasks&, if (_localRenderer.updatedSinceLastCall) { const std::array& layerGroups = _layerManager.layerGroups(); + for (size_t i = 0; i < layerGroups.size(); ++i) { const std::string& nameBase = layergroupid::LAYER_GROUP_IDENTIFIERS[i]; _localRenderer.gpuLayerGroups[i].bind( diff --git a/src/network/parallelserver.cpp b/src/network/parallelserver.cpp index 499aa642f8..a52caeb410 100644 --- a/src/network/parallelserver.cpp +++ b/src/network/parallelserver.cpp @@ -50,12 +50,12 @@ void ParallelServer::start(int port, const std::string& password, } void ParallelServer::setDefaultHostAddress(std::string defaultHostAddress) { - std::lock_guard lock(_hostInfoMutex); + std::lock_guard lock(_hostInfoMutex); _defaultHostAddress = std::move(defaultHostAddress); } std::string ParallelServer::defaultHostAddress() const { - std::lock_guard lock(_hostInfoMutex); + std::lock_guard lock(_hostInfoMutex); return _defaultHostAddress; } @@ -66,29 +66,26 @@ void ParallelServer::stop() { void ParallelServer::handleNewPeers() { while (!_shouldStop) { - std::unique_ptr socket = - _socketServer.awaitPendingTcpSocket(); + std::unique_ptr s = _socketServer.awaitPendingTcpSocket(); - socket->startStreams(); + s->startStreams(); const size_t id = _nextConnectionId++; std::shared_ptr p = std::make_shared(Peer{ id, "", - ParallelConnection(std::move(socket)), + ParallelConnection(std::move(s)), ParallelConnection::Status::Connecting, std::thread() }); auto it = _peers.emplace(p->id, p); - it.first->second->thread = std::thread([this, id]() { - handlePeer(id); - }); + it.first->second->thread = std::thread([this, id]() { handlePeer(id); }); } } std::shared_ptr ParallelServer::peer(size_t id) { - std::lock_guard lock(_peerListMutex); - auto it = _peers.find(id); + std::lock_guard lock(_peerListMutex); + const auto it = _peers.find(id); if (it == _peers.end()) { return nullptr; } @@ -107,15 +104,19 @@ void ParallelServer::handlePeer(size_t id) { } try { ParallelConnection::Message m = p->parallelConnection.receiveMessage(); - _incomingMessages.push({id, m}); - } catch (const ParallelConnection::ConnectionLostError&) { + PeerMessage msg; + msg.peerId = id; + msg.message = m; + _incomingMessages.push(msg); + } + catch (const ParallelConnection::ConnectionLostError&) { LERROR(fmt::format("Connection lost to {}", p->id)); - _incomingMessages.push({ - id, - ParallelConnection::Message( - ParallelConnection::MessageType::Disconnection, std::vector() - ) - }); + PeerMessage msg; + msg.peerId = id; + msg.message = ParallelConnection::Message( + ParallelConnection::MessageType::Disconnection, std::vector() + ); + _incomingMessages.push(msg); return; } } @@ -137,9 +138,9 @@ void ParallelServer::handlePeerMessage(PeerMessage peerMessage) { std::shared_ptr& peer = it->second; - const ParallelConnection::MessageType messageType = peerMessage.message.type; + const ParallelConnection::MessageType type = peerMessage.message.type; std::vector& data = peerMessage.message.content; - switch (messageType) { + switch (type) { case ParallelConnection::MessageType::Authentication: handleAuthentication(peer, std::move(data)); break; @@ -156,9 +157,7 @@ void ParallelServer::handlePeerMessage(PeerMessage peerMessage) { disconnect(*peer); break; default: - LERROR(fmt::format( - "Unsupported message type: {}", static_cast(messageType) - )); + LERROR(fmt::format("Unsupported message type: {}", static_cast(type))); break; } } @@ -192,19 +191,19 @@ void ParallelServer::handleAuthentication(std::shared_ptr peer, setName(*peer, name); - LINFO(fmt::format("Connection established with {} \"{}\"", peer->id, name)); + LINFO(fmt::format("Connection established with {} ('{}')", peer->id, name)); std::string defaultHostAddress; { - std::lock_guard _hostMutex(_hostInfoMutex); + std::lock_guard _hostMutex(_hostInfoMutex); defaultHostAddress = _defaultHostAddress; } if (_hostPeerId == 0 && peer->parallelConnection.socket()->address() == defaultHostAddress) { - // Directly promote the conenction to host (initialize) - // if there is no host, and ip matches default host ip. - LINFO(fmt::format("Connection {} directly promoted to host.", peer->id)); + // Directly promote the conenction to host (initialize) if there is no host, and + // ip matches default host ip. + LINFO(fmt::format("Connection {} directly promoted to host", peer->id)); assignHost(peer); for (std::pair>& it : _peers) { // sendConnectionStatus(it->second) ? @@ -221,11 +220,10 @@ void ParallelServer::handleAuthentication(std::shared_ptr peer, void ParallelServer::handleData(const Peer& peer, std::vector data) { if (peer.id != _hostPeerId) { LINFO(fmt::format( - "Connection {} tried to send data without being the host. Ignoring", peer.id + "Ignoring connection {} trying to send data without being host", peer.id )); } sendMessageToClients(ParallelConnection::MessageType::Data, data); - } void ParallelServer::handleHostshipRequest(std::shared_ptr peer, @@ -233,43 +231,43 @@ void ParallelServer::handleHostshipRequest(std::shared_ptr peer, { std::stringstream input(std::string(message.begin(), message.end())); - LINFO(fmt::format("Connection {} requested hostship.", peer->id)); + LINFO(fmt::format("Connection {} requested hostship", peer->id)); uint64_t passwordHash = 0; input.read(reinterpret_cast(&passwordHash), sizeof(uint64_t)); if (passwordHash != _changeHostPasswordHash) { - LERROR(fmt::format("Connection {} provided incorrect host password.", peer->id)); + LERROR(fmt::format("Connection {} provided incorrect host password", peer->id)); return; } size_t oldHostPeerId = 0; { - std::lock_guard lock(_hostInfoMutex); + std::lock_guard lock(_hostInfoMutex); oldHostPeerId = _hostPeerId; } if (oldHostPeerId == peer->id) { - LINFO(fmt::format("Connection {} is already the host.", peer->id)); + LINFO(fmt::format("Connection {} is already the host", peer->id)); return; } assignHost(peer); - LINFO(fmt::format("Switched host from {} to {}.", oldHostPeerId, peer->id)); + LINFO(fmt::format("Switched host from {} to {}", oldHostPeerId, peer->id)); } void ParallelServer::handleHostshipResignation(Peer& peer) { - LINFO(fmt::format("Connection {} wants to resign its hostship.", peer.id)); + LINFO(fmt::format("Connection {} wants to resign its hostship", peer.id)); size_t oldHostPeerId = 0; { - std::lock_guard lock(_hostInfoMutex); + std::lock_guard lock(_hostInfoMutex); oldHostPeerId = _hostPeerId; } setToClient(peer); - LINFO(fmt::format("Connection {} resigned as host.", peer.id)); + LINFO(fmt::format("Connection {} resigned as host", peer.id)); } bool ParallelServer::isConnected(const Peer& peer) const { @@ -277,8 +275,7 @@ bool ParallelServer::isConnected(const Peer& peer) const { peer.status != ParallelConnection::Status::Disconnected; } -void ParallelServer::sendMessage(Peer& peer, - ParallelConnection::MessageType messageType, +void ParallelServer::sendMessage(Peer& peer, ParallelConnection::MessageType messageType, const std::vector& message) { peer.parallelConnection.sendMessage({ messageType, message }); @@ -311,12 +308,12 @@ void ParallelServer::disconnect(Peer& peer) { size_t hostPeerId = 0; { - std::lock_guard lock(_hostInfoMutex); + std::lock_guard lock(_hostInfoMutex); hostPeerId = _hostPeerId; } - // Make sure any disconnecting host is first degraded to client, - // in order to notify other clients about host disconnection. + // Make sure any disconnecting host is first degraded to client, in order to notify + // other clients about host disconnection. if (peer.id == hostPeerId) { setToClient(peer); } @@ -327,7 +324,7 @@ void ParallelServer::disconnect(Peer& peer) { } void ParallelServer::setName(Peer& peer, std::string name) { - peer.name = name; + peer.name = std::move(name); size_t hostPeerId = 0; { std::lock_guard lock(_hostInfoMutex); @@ -338,7 +335,7 @@ void ParallelServer::setName(Peer& peer, std::string name) { if (peer.id == hostPeerId) { { std::lock_guard lock(_hostInfoMutex); - _hostName = name; + _hostName = peer.name; } for (std::pair>& it : _peers) { @@ -374,7 +371,7 @@ void ParallelServer::setToClient(Peer& peer) { { std::lock_guard lock(_hostInfoMutex); _hostPeerId = 0; - _hostName = ""; + _hostName.clear(); } // If host becomes client, make all clients hostless. @@ -421,8 +418,8 @@ void ParallelServer::sendConnectionStatus(Peer& peer) { data.insert( data.end(), - reinterpret_cast(_hostName.data()), - reinterpret_cast(_hostName.data() + outHostNameSize) + _hostName.data(), + _hostName.data() + outHostNameSize ); sendMessage(peer, ParallelConnection::MessageType::ConnectionStatus, data); diff --git a/src/rendering/dashboard.cpp b/src/rendering/dashboard.cpp index c700326cbc..43bc3bb4c5 100644 --- a/src/rendering/dashboard.cpp +++ b/src/rendering/dashboard.cpp @@ -52,7 +52,7 @@ Dashboard::Dashboard() : properties::PropertyOwner({ "Dashboard" }) , _isEnabled(EnabledInfo, true) , _startPositionOffset( - properties::IVec2Property(StartPositionOffsetInfo, glm::ivec2(10, -10)) + properties::IVec2Property(StartPositionOffsetInfo, glm::ivec2(10, -25)) ) { addProperty(_isEnabled); diff --git a/src/rendering/framebufferrenderer.cpp b/src/rendering/framebufferrenderer.cpp index bb8cf2e839..93d54d6f43 100644 --- a/src/rendering/framebufferrenderer.cpp +++ b/src/rendering/framebufferrenderer.cpp @@ -171,111 +171,35 @@ void FramebufferRenderer::initialize() { // GBuffers glGenTextures(1, &_gBuffers.colorTexture); - if (HasGLDebugInfo) { - glObjectLabel(GL_TEXTURE, _gBuffers.colorTexture, -1, "G-Buffer Color"); - } glGenTextures(1, &_gBuffers.depthTexture); - if (HasGLDebugInfo) { - glObjectLabel(GL_TEXTURE, _gBuffers.depthTexture, -1, "G-Buffer Depth"); - } glGenTextures(1, &_gBuffers.positionTexture); - if (HasGLDebugInfo) { - glObjectLabel(GL_TEXTURE, _gBuffers.positionTexture, -1, "G-Buffer Position"); - } glGenTextures(1, &_gBuffers.normalTexture); - if (HasGLDebugInfo) { - glObjectLabel(GL_TEXTURE, _gBuffers.normalTexture, -1, "G-Buffer Normal"); - } glGenFramebuffers(1, &_gBuffers.framebuffer); - if (HasGLDebugInfo) { - glObjectLabel(GL_FRAMEBUFFER, _gBuffers.framebuffer, -1, "G-Buffer Main"); - } + // PingPong Buffers // The first pingpong buffer shares the color texture with the renderbuffer: _pingPongBuffers.colorTexture[0] = _gBuffers.colorTexture; glGenTextures(1, &_pingPongBuffers.colorTexture[1]); - if (glbinding::Binding::ObjectLabel.isResolved()) { - glObjectLabel( - GL_TEXTURE, - _pingPongBuffers.colorTexture[1], - -1, - "G-Buffer Color Ping-Pong" - ); - } glGenFramebuffers(1, &_pingPongBuffers.framebuffer); - if (glbinding::Binding::ObjectLabel.isResolved()) { - glObjectLabel( - GL_FRAMEBUFFER, - _pingPongBuffers.framebuffer, - -1, - "G-Buffer Ping-Pong" - ); - } - + // Exit framebuffer glGenTextures(1, &_exitColorTexture); - if (glbinding::Binding::ObjectLabel.isResolved()) { - glObjectLabel(GL_TEXTURE, _exitColorTexture, -1, "Exit color"); - } - glGenTextures(1, &_exitDepthTexture); - if (glbinding::Binding::ObjectLabel.isResolved()) { - glObjectLabel(GL_TEXTURE, _exitColorTexture, -1, "Exit depth"); - } + glGenTextures(1, &_exitDepthTexture); glGenFramebuffers(1, &_exitFramebuffer); - if (glbinding::Binding::ObjectLabel.isResolved()) { - glObjectLabel(GL_FRAMEBUFFER, _exitFramebuffer, -1, "Exit"); - } // HDR / Filtering Buffers glGenFramebuffers(1, &_hdrBuffers.hdrFilteringFramebuffer); - if (glbinding::Binding::ObjectLabel.isResolved()) { - glObjectLabel(GL_FRAMEBUFFER, _exitFramebuffer, -1, "HDR filtering"); - } glGenTextures(1, &_hdrBuffers.hdrFilteringTexture); - if (glbinding::Binding::ObjectLabel.isResolved()) { - glObjectLabel(GL_TEXTURE, _hdrBuffers.hdrFilteringTexture, -1, "HDR filtering"); - } - - + // FXAA Buffers glGenFramebuffers(1, &_fxaaBuffers.fxaaFramebuffer); - if (glbinding::Binding::ObjectLabel.isResolved()) { - glObjectLabel(GL_FRAMEBUFFER, _fxaaBuffers.fxaaFramebuffer, -1, "FXAA"); - } glGenTextures(1, &_fxaaBuffers.fxaaTexture); - if (glbinding::Binding::ObjectLabel.isResolved()) { - glObjectLabel(GL_TEXTURE, _fxaaBuffers.fxaaTexture, -1, "FXAA"); - } - + // DownscaleVolumeRendering glGenFramebuffers(1, &_downscaleVolumeRendering.framebuffer); - if (glbinding::Binding::ObjectLabel.isResolved()) { - glObjectLabel( - GL_FRAMEBUFFER, - _downscaleVolumeRendering.framebuffer, - -1, - "Downscaled Volume" - ); - } glGenTextures(1, &_downscaleVolumeRendering.colorTexture); - if (glbinding::Binding::ObjectLabel.isResolved()) { - glObjectLabel( - GL_TEXTURE, - _downscaleVolumeRendering.colorTexture, - -1, - "Downscaled Volume Color" - ); - } glGenTextures(1, &_downscaleVolumeRendering.depthbuffer); - if (glbinding::Binding::ObjectLabel.isResolved()) { - glObjectLabel( - GL_TEXTURE, - _downscaleVolumeRendering.depthbuffer, - -1, - "Downscaled Volume Depth" - ); - } // Allocate Textures/Buffers Memory updateResolution(); @@ -311,6 +235,9 @@ void FramebufferRenderer::initialize() { _gBuffers.depthTexture, 0 ); + if (HasGLDebugInfo) { + glObjectLabel(GL_FRAMEBUFFER, _gBuffers.framebuffer, -1, "G-Buffer Main"); + } GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { @@ -339,6 +266,14 @@ void FramebufferRenderer::initialize() { _gBuffers.depthTexture, 0 ); + if (glbinding::Binding::ObjectLabel.isResolved()) { + glObjectLabel( + GL_FRAMEBUFFER, + _pingPongBuffers.framebuffer, + -1, + "G-Buffer Ping-Pong" + ); + } status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { @@ -362,6 +297,9 @@ void FramebufferRenderer::initialize() { _exitDepthTexture, 0 ); + if (glbinding::Binding::ObjectLabel.isResolved()) { + glObjectLabel(GL_FRAMEBUFFER, _exitFramebuffer, -1, "Exit"); + } status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { @@ -378,6 +316,9 @@ void FramebufferRenderer::initialize() { _hdrBuffers.hdrFilteringTexture, 0 ); + if (glbinding::Binding::ObjectLabel.isResolved()) { + glObjectLabel(GL_FRAMEBUFFER, _hdrBuffers.hdrFilteringFramebuffer, -1, "HDR filtering"); + } status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { @@ -394,6 +335,9 @@ void FramebufferRenderer::initialize() { _fxaaBuffers.fxaaTexture, 0 ); + if (glbinding::Binding::ObjectLabel.isResolved()) { + glObjectLabel(GL_FRAMEBUFFER, _fxaaBuffers.fxaaFramebuffer, -1, "FXAA"); + } status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { @@ -416,6 +360,14 @@ void FramebufferRenderer::initialize() { _downscaleVolumeRendering.depthbuffer, 0 ); + if (glbinding::Binding::ObjectLabel.isResolved()) { + glObjectLabel( + GL_FRAMEBUFFER, + _downscaleVolumeRendering.framebuffer, + -1, + "Downscaled Volume" + ); + } status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { @@ -797,6 +749,10 @@ void FramebufferRenderer::updateResolution() { ZoneScoped TracyGpuZone("Renderer updateResolution") + HasGLDebugInfo = glbinding::Binding::ObjectLabel.isResolved() && + glbinding::Binding::PushDebugGroup.isResolved() && + glbinding::Binding::PopDebugGroup.isResolved(); + glBindTexture(GL_TEXTURE_2D, _gBuffers.colorTexture); glTexImage2D( GL_TEXTURE_2D, @@ -811,6 +767,9 @@ void FramebufferRenderer::updateResolution() { ); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + if (HasGLDebugInfo) { + glObjectLabel(GL_TEXTURE, _gBuffers.colorTexture, -1, "G-Buffer Color"); + } glBindTexture(GL_TEXTURE_2D, _gBuffers.positionTexture); glTexImage2D( @@ -826,6 +785,9 @@ void FramebufferRenderer::updateResolution() { ); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + if (HasGLDebugInfo) { + glObjectLabel(GL_TEXTURE, _gBuffers.positionTexture, -1, "G-Buffer Position"); + } glBindTexture(GL_TEXTURE_2D, _gBuffers.normalTexture); glTexImage2D( @@ -841,6 +803,9 @@ void FramebufferRenderer::updateResolution() { ); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + if (HasGLDebugInfo) { + glObjectLabel(GL_TEXTURE, _gBuffers.normalTexture, -1, "G-Buffer Normal"); + } glBindTexture(GL_TEXTURE_2D, _gBuffers.depthTexture); glTexImage2D( @@ -856,6 +821,9 @@ void FramebufferRenderer::updateResolution() { ); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + if (HasGLDebugInfo) { + glObjectLabel(GL_TEXTURE, _gBuffers.depthTexture, -1, "G-Buffer Depth"); + } glBindTexture(GL_TEXTURE_2D, _pingPongBuffers.colorTexture[1]); glTexImage2D( @@ -871,6 +839,14 @@ void FramebufferRenderer::updateResolution() { ); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + if (glbinding::Binding::ObjectLabel.isResolved()) { + glObjectLabel( + GL_TEXTURE, + _pingPongBuffers.colorTexture[1], + -1, + "G-Buffer Color Ping-Pong" + ); + } // HDR / Filtering glBindTexture(GL_TEXTURE_2D, _hdrBuffers.hdrFilteringTexture); @@ -887,6 +863,9 @@ void FramebufferRenderer::updateResolution() { ); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + if (glbinding::Binding::ObjectLabel.isResolved()) { + glObjectLabel(GL_TEXTURE, _hdrBuffers.hdrFilteringTexture, -1, "HDR filtering"); + } // FXAA glBindTexture(GL_TEXTURE_2D, _fxaaBuffers.fxaaTexture); @@ -903,6 +882,9 @@ void FramebufferRenderer::updateResolution() { ); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + if (glbinding::Binding::ObjectLabel.isResolved()) { + glObjectLabel(GL_TEXTURE, _fxaaBuffers.fxaaTexture, -1, "FXAA"); + } // Downscale Volume Rendering glBindTexture(GL_TEXTURE_2D, _downscaleVolumeRendering.colorTexture); @@ -925,6 +907,14 @@ void FramebufferRenderer::updateResolution() { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); float volumeBorderColor[] = { 0.0f, 0.0f, 0.0f, 1.0f }; glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, volumeBorderColor); + if (glbinding::Binding::ObjectLabel.isResolved()) { + glObjectLabel( + GL_TEXTURE, + _downscaleVolumeRendering.colorTexture, + -1, + "Downscaled Volume Color" + ); + } glBindTexture(GL_TEXTURE_2D, _downscaleVolumeRendering.depthbuffer); glTexImage2D( @@ -944,6 +934,14 @@ void FramebufferRenderer::updateResolution() { ); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + if (glbinding::Binding::ObjectLabel.isResolved()) { + glObjectLabel( + GL_TEXTURE, + _downscaleVolumeRendering.depthbuffer, + -1, + "Downscaled Volume Depth" + ); + } // Volume Rendering Textures glBindTexture(GL_TEXTURE_2D, _exitColorTexture); @@ -958,12 +956,13 @@ void FramebufferRenderer::updateResolution() { GL_UNSIGNED_SHORT, nullptr ); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + if (glbinding::Binding::ObjectLabel.isResolved()) { + glObjectLabel(GL_TEXTURE, _exitColorTexture, -1, "Exit color"); + } glBindTexture(GL_TEXTURE_2D, _exitDepthTexture); - glTexImage2D( GL_TEXTURE_2D, 0, @@ -975,9 +974,11 @@ void FramebufferRenderer::updateResolution() { GL_FLOAT, nullptr ); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + if (glbinding::Binding::ObjectLabel.isResolved()) { + glObjectLabel(GL_TEXTURE, _exitColorTexture, -1, "Exit depth"); + } _dirtyResolution = false; } @@ -1208,10 +1209,6 @@ void FramebufferRenderer::render(Scene* scene, Camera* camera, float blackoutFac TracyGpuZone("Raycaster Tasks") GLDebugGroup group("Raycaster Tasks"); performRaycasterTasks(tasks.raycasterTasks); - - if (HasGLDebugInfo) { - glPopDebugGroup(); - } } if (!tasks.deferredcasterTasks.empty()) { diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index 8985e28cee..6c2c1a5965 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -758,7 +758,7 @@ bool RenderEngine::mouseActivationCallback(const glm::dvec2& mousePosition) cons if (intersects(mousePosition, _cameraButtonLocations.rotation)) { constexpr const char* ToggleRotationFrictionScript = R"( local f = 'NavigationHandler.OrbitalNavigator.Friction.RotationalFriction'; - openspace.setPropertyValue(f, not openspace.getPropertyValue(f));)"; + openspace.setPropertyValueSingle(f, not openspace.getPropertyValue(f));)"; global::scriptEngine.queueScript( ToggleRotationFrictionScript, @@ -770,7 +770,7 @@ bool RenderEngine::mouseActivationCallback(const glm::dvec2& mousePosition) cons if (intersects(mousePosition, _cameraButtonLocations.zoom)) { constexpr const char* ToggleZoomFrictionScript = R"( local f = 'NavigationHandler.OrbitalNavigator.Friction.ZoomFriction'; - openspace.setPropertyValue(f, not openspace.getPropertyValue(f));)"; + openspace.setPropertyValueSingle(f, not openspace.getPropertyValue(f));)"; global::scriptEngine.queueScript( ToggleZoomFrictionScript, @@ -782,7 +782,7 @@ bool RenderEngine::mouseActivationCallback(const glm::dvec2& mousePosition) cons if (intersects(mousePosition, _cameraButtonLocations.roll)) { constexpr const char* ToggleRollFrictionScript = R"( local f = 'NavigationHandler.OrbitalNavigator.Friction.RollFriction'; - openspace.setPropertyValue(f, not openspace.getPropertyValue(f));)"; + openspace.setPropertyValueSingle(f, not openspace.getPropertyValue(f));)"; global::scriptEngine.queueScript( ToggleRollFrictionScript, @@ -1409,9 +1409,9 @@ void RenderEngine::renderScreenLog() { _log->removeExpiredEntries(); - constexpr const int MaxNumberMessages = 10; + constexpr const int MaxNumberMessages = 20; constexpr const int CategoryLength = 30; - constexpr const int MessageLength = 140; + constexpr const int MessageLength = 280; constexpr const std::chrono::seconds FadeTime(5); const std::vector& entries = _log->entries(); @@ -1447,7 +1447,7 @@ void RenderEngine::renderScreenLog() { const glm::vec4 white(0.9f, 0.9f, 0.9f, alpha); - std::array buf; + std::array buf; { std::fill(buf.begin(), buf.end(), char(0)); char* end = fmt::format_to( @@ -1493,7 +1493,7 @@ void RenderEngine::renderScreenLog() { RenderFont( *_fontLog, glm::vec2( - 10 + 30 * _fontLog->pointSize(), + 10 + (30 + 3) * _fontLog->pointSize(), _fontLog->pointSize() * nr * 2 + fontRes.y * _verticalLogOffset ), levelText, @@ -1504,7 +1504,7 @@ void RenderEngine::renderScreenLog() { RenderFont( *_fontLog, glm::vec2( - 10 + 41 * _fontLog->pointSize(), + 10 + 44 * _fontLog->pointSize(), _fontLog->pointSize() * nr * 2 + fontRes.y * _verticalLogOffset ), message, diff --git a/src/scene/profile_lua.inl b/src/scene/profile_lua.inl index a60c6b4c59..970eb5ac45 100644 --- a/src/scene/profile_lua.inl +++ b/src/scene/profile_lua.inl @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -65,11 +66,14 @@ int saveSettingsToProfile(lua_State* L) { utcTime->tm_min, utcTime->tm_sec ); - std::string newFile = fmt::format( - "{}_{}.{}", - f.fullBaseName(), time, f.fileExtension() - ); - std::filesystem::copy(global::configuration.profile, newFile); + std::string newFile = fmt::format("{}_{}", f.fullBaseName(), time); + std::string sourcePath = + absPath("${PROFILES}") + '/' + global::configuration.profile + ".profile"; + std::string destPath = + absPath("${PROFILES}") + '/' + newFile + ".profile"; + + LINFOC("Profile", fmt::format("Saving a copy of the old profile as {}", newFile)); + std::filesystem::copy(sourcePath, destPath); saveFilePath = global::configuration.profile; } else { @@ -97,14 +101,14 @@ int saveSettingsToProfile(lua_State* L) { } const std::string absFilename = absPath("${PROFILES}/" + saveFilePath + ".profile"); - const bool overwrite = (n == 2) ? ghoul::lua::value(L, 2) : false; + const bool overwrite = (n == 2) ? ghoul::lua::value(L, 2) : true; if (FileSys.fileExists(absFilename) && !overwrite) { return luaL_error( L, fmt::format( - "Unable to save profile '{}'. File of same name already exists.", - absFilename.c_str() + "Unable to save profile '{}'. File of same name already exists", + absFilename ).c_str() ); } @@ -130,8 +134,7 @@ int saveSettingsToProfile(lua_State* L) { return luaL_error( L, fmt::format( - "Data write error to file: {} ({})", - absFilename, e.what() + "Data write error to file: {} ({})", absFilename, e.what() ).c_str() ); } diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index aa2e434579..d364354be3 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -636,13 +636,19 @@ scripting::LuaLibrary Scene::luaLibrary() { "This is the same as calling the setValue method and passing 'single' as " "the fourth argument to setPropertyValue." }, + { + "hasProperty", + &luascriptfunctions::property_hasProperty, + {}, + "string", + "Returns whether a property with the given URI exists" + }, { "getPropertyValue", &luascriptfunctions::property_getValue, {}, "string", - "Returns the value the property, identified by " - "the provided URI." + "Returns the value the property, identified by the provided URI." }, { "getProperty", @@ -675,6 +681,14 @@ scripting::LuaLibrary Scene::luaLibrary() { "string", "Removes the SceneGraphNode identified by name" }, + { + "removeSceneGraphNodesFromRegex", + &luascriptfunctions::removeSceneGraphNodesFromRegex, + {}, + "string", + "Removes all SceneGraphNodes with identifiers matching the input regular " + "expression" + }, { "hasSceneGraphNode", &luascriptfunctions::hasSceneGraphNode, diff --git a/src/scene/scene_lua.inl b/src/scene/scene_lua.inl index 67ed7b2991..9af3720b5a 100644 --- a/src/scene/scene_lua.inl +++ b/src/scene/scene_lua.inl @@ -376,6 +376,25 @@ int property_setValueSingle(lua_State* L) { return property_setValue(L); } +/** + * \ingroup LuaScripts + * hasProperty(string): + * Returns whether a property with the given URI exists + */ +int property_hasProperty(lua_State* L) { + ghoul::lua::checkArgumentsAndThrow(L, 1, "lua::property_hasProperty"); + + std::string uri = ghoul::lua::value( + L, + 1, + ghoul::lua::PopValue::Yes + ); + + openspace::properties::Property* prop = property(uri); + ghoul::lua::push(L, prop != nullptr); + return 1; +} + /** * \ingroup LuaScripts * getPropertyValue(string): @@ -389,7 +408,7 @@ int property_getValue(lua_State* L) { L, 1, ghoul::lua::PopValue::Yes - ); + ); openspace::properties::Property* prop = property(uri); if (!prop) { @@ -531,6 +550,54 @@ int removeSceneGraphNode(lua_State* L) { std::string name = ghoul::lua::value(L, 1, ghoul::lua::PopValue::Yes); + SceneGraphNode* foundNode = sceneGraphNode(name); + if (!foundNode) { + LERRORC( + "removeSceneGraphNode", + fmt::format("Did not find a match for identifier: {} ", name) + ); + return 0; + } + + SceneGraphNode* parent = foundNode->parent(); + if (!parent) { + LERRORC( + "removeSceneGraphNode", + fmt::format("Cannot remove root node") + ); + return 0; + } + + // Remove the node and all its children + std::function removeNode = + [&removeNode](SceneGraphNode* localNode) { + std::vector children = localNode->children(); + + ghoul::mm_unique_ptr n = localNode->parent()->detachChild( + *localNode + ); + ghoul_assert(n.get() == localNode, "Wrong node returned from detaching"); + + for (SceneGraphNode* c : children) { + removeNode(c); + } + + localNode->deinitializeGL(); + localNode->deinitialize(); + n = nullptr; + }; + + removeNode(foundNode); + + ghoul_assert(lua_gettop(L) == 0, "Incorrect number of items left on stack"); + return 0; +} + +int removeSceneGraphNodesFromRegex(lua_State* L) { + ghoul::lua::checkArgumentsAndThrow(L, 1, "lua::removeSceneGraphNodesFromRegex"); + + std::string name = ghoul::lua::value(L, 1, ghoul::lua::PopValue::Yes); + const std::vector& nodes = global::renderEngine.scene()->allSceneGraphNodes(); @@ -553,8 +620,8 @@ int removeSceneGraphNode(lua_State* L) { SceneGraphNode* parent = node->parent(); if (!parent) { LERRORC( - "removeSceneGraphNode", - fmt::format("{}: Cannot remove root node") + "removeSceneGraphNodesFromRegex", + fmt::format("Cannot remove root node") ); } else { @@ -565,8 +632,8 @@ int removeSceneGraphNode(lua_State* L) { if (!foundMatch) { LERRORC( - "removeSceneGraphNode", - "Did not find a match for identifier: " + name + "removeSceneGraphNodesFromRegex", + fmt::format("Did not find a match for identifier: {}", name) ); return 0; } diff --git a/src/util/spicemanager.cpp b/src/util/spicemanager.cpp index ab1bfb678a..51ed9fdc47 100644 --- a/src/util/spicemanager.cpp +++ b/src/util/spicemanager.cpp @@ -194,6 +194,9 @@ void throwSpiceError(const std::string& errorMessage) { reset_c(); throw openspace::SpiceManager::SpiceException(errorMessage + ": " + buffer); } + else { + reset_c(); + } } SpiceManager::KernelHandle SpiceManager::loadKernel(std::string filePath) {