diff --git a/README.md b/README.md index 35c4c5e6c..e13c5b4d8 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ Further information and artwork: An Nvidia graphics card with compute capability 6.0 or higher is needed. Please check [https://en.wikipedia.org/wiki/CUDA#GPUs_supported](https://en.wikipedia.org/wiki/CUDA#GPUs_supported). # 💽 Installer -Installer for Windows: [alien-installer.msi](https://alien-project.org/media/files/alien-installer.msi) (Updated: 2023-09-14) +Installer for Windows: [alien-installer.msi](https://alien-project.org/media/files/alien-installer.msi) (Updated: 2023-09-21) In the case that the program crashes for an unknown reason, please refer to the troubleshooting section in [alien-project.org/downloads.html](https://alien-project.org/downloads.html). diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 702150a59..e6f62dc93 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,5 +1,15 @@ # Release notes +## [4.3.0] - 2023-09-23 +### Added +- gui/browser: tab widget added to show the uploaded genomes and simulations from server +- gui/browser: possibility to upload and download genomes +- gui/genome editor: toolbar button added to upload current genome +- cli: file logger added (creates log.txt) + +### Fixed +- gui/browser: layout problem for multiline descriptions + ## [4.2.0] - 2023-09-21 ### Added - command-line interface for running simulation files for a specified number of time steps diff --git a/imgui.ini b/imgui.ini index 5cf67dd52..de4ca070d 100644 --- a/imgui.ini +++ b/imgui.ini @@ -298,6 +298,11 @@ Pos=733,403 Size=400,218 Collapsed=0 +[Window][Upload genome] +Pos=733,403 +Size=400,218 +Collapsed=0 + [Window][Shader parameters] Pos=207,336 Size=302,127 @@ -308,6 +313,11 @@ Pos=345,242 Size=1100,632 Collapsed=0 +[Window][Delete user] +Pos=623,480 +Size=501,168 +Collapsed=0 + [Table][0xFB3C2B09,3] RefScale=13 Column 0 Sort=0v diff --git a/source/Base/CMakeLists.txt b/source/Base/CMakeLists.txt index 898b51d04..86eece9fa 100644 --- a/source/Base/CMakeLists.txt +++ b/source/Base/CMakeLists.txt @@ -3,6 +3,8 @@ add_library(alien_base_lib Definitions.cpp Definitions.h Exceptions.h + FileLogger.cpp + FileLogger.h GlobalSettings.cpp GlobalSettings.h Hashes.h diff --git a/source/Base/Definitions.h b/source/Base/Definitions.h index 3f2e4ef4b..e7b4c6e18 100644 --- a/source/Base/Definitions.h +++ b/source/Base/Definitions.h @@ -20,6 +20,9 @@ using std::int64_t; using std::uint32_t; using std::uint64_t; +class _FileLogger; +using FileLogger = std::shared_ptr<_FileLogger>; + constexpr float NEAR_ZERO = 1.0e-4f; template diff --git a/source/Gui/FileLogger.cpp b/source/Base/FileLogger.cpp similarity index 100% rename from source/Gui/FileLogger.cpp rename to source/Base/FileLogger.cpp diff --git a/source/Gui/FileLogger.h b/source/Base/FileLogger.h similarity index 100% rename from source/Gui/FileLogger.h rename to source/Base/FileLogger.h diff --git a/source/Base/Resources.h b/source/Base/Resources.h index bd9e60f35..b8224a88a 100644 --- a/source/Base/Resources.h +++ b/source/Base/Resources.h @@ -2,7 +2,7 @@ namespace Const { - std::string const ProgramVersion = "4.2.0"; + std::string const ProgramVersion = "4.3.0"; std::string const BasePath = "resources/"; diff --git a/source/Cli/Main.cpp b/source/Cli/Main.cpp index a8af072db..78595138a 100644 --- a/source/Cli/Main.cpp +++ b/source/Cli/Main.cpp @@ -7,6 +7,7 @@ #include "Base/LoggingService.h" #include "Base/Resources.h" #include "Base/StringHelper.h" +#include "Base/FileLogger.h" #include "EngineImpl/SimulationControllerImpl.h" #include "EngineInterface/Serializer.h" @@ -91,6 +92,8 @@ namespace int main(int argc, char** argv) { try { + FileLogger fileLogger = std::make_shared<_FileLogger>(); + CLI::App app{"Command-line interface for ALIEN v" + Const::ProgramVersion}; //parse command line arguments diff --git a/source/EngineInterface/Serializer.cpp b/source/EngineInterface/Serializer.cpp index 728970431..035d1eb67 100644 --- a/source/EngineInterface/Serializer.cpp +++ b/source/EngineInterface/Serializer.cpp @@ -764,7 +764,9 @@ bool Serializer::serializeGenomeToFile(std::string const& filename, std::vector< try { //wrap constructor cell around genome ClusteredDataDescription data; - data.addCluster(ClusterDescription().addCell(CellDescription().setCellFunction(ConstructorDescription().setGenome(genome)))); + if (!wrapGenome(data, genome)) { + return false; + } zstr::ofstream stream(filename, std::ios::binary); if (!stream) { @@ -781,24 +783,57 @@ bool Serializer::serializeGenomeToFile(std::string const& filename, std::vector< bool Serializer::deserializeGenomeFromFile(std::vector& genome, std::string const& filename) { try { - //constructor cell is wrapped around genome ClusteredDataDescription data; if (!deserializeDataDescription(data, filename)) { return false; } - if (data.clusters.size() != 1) { + if (!unwrapGenome(genome, data)) { + return false; + } + return true; + } catch (...) { + return false; + } +} + +bool Serializer::serializeGenomeToString(std::string& output, std::vector const& input) +{ + try { + std::stringstream stdStream; + zstr::ostream stream(stdStream, std::ios::binary); + if (!stream) { return false; } - auto cluster = data.clusters.front(); - if (cluster.cells.size() != 1) { + + ClusteredDataDescription data; + if (!wrapGenome(data, input)) { return false; } - auto cell = cluster.cells.front(); - if (cell.getCellFunctionType() != CellFunction_Constructor) { + + serializeDataDescription(data, stream); + stream.flush(); + output = stdStream.str(); + return true; + } catch (...) { + return false; + } +} + +bool Serializer::deserializeGenomeFromString(std::vector& output, std::string const& input) +{ + try { + std::stringstream stdStream(input); + zstr::istream stream(stdStream, std::ios::binary); + if (!stream) { return false; } - genome = std::get(*cell.cellFunction).genome; + ClusteredDataDescription data; + deserializeDataDescription(data, stream); + + if (!unwrapGenome(output, data)) { + return false; + } return true; } catch (...) { return false; @@ -918,3 +953,27 @@ void Serializer::deserializeSimulationParameters(SimulationParameters& parameter parameters = AuxiliaryDataParser::decodeSimulationParameters(tree); } +bool Serializer::wrapGenome(ClusteredDataDescription& output, std::vector const& input) +{ + output.clear(); + output.addCluster(ClusterDescription().addCell(CellDescription().setCellFunction(ConstructorDescription().setGenome(input)))); + return true; +} + + +bool Serializer::unwrapGenome(std::vector& output, ClusteredDataDescription const& input) +{ + if (input.clusters.size() != 1) { + return false; + } + auto cluster = input.clusters.front(); + if (cluster.cells.size() != 1) { + return false; + } + auto cell = cluster.cells.front(); + if (cell.getCellFunctionType() != CellFunction_Constructor) { + return false; + } + output = std::get(*cell.cellFunction).genome; + return true; +} diff --git a/source/EngineInterface/Serializer.h b/source/EngineInterface/Serializer.h index e3dd84671..79e7d2632 100644 --- a/source/EngineInterface/Serializer.h +++ b/source/EngineInterface/Serializer.h @@ -30,6 +30,9 @@ class Serializer static bool serializeGenomeToFile(std::string const& filename, std::vector const& genome); static bool deserializeGenomeFromFile(std::vector& genome, std::string const& filename); + static bool serializeGenomeToString(std::string& output, std::vector const& input); + static bool deserializeGenomeFromString(std::vector& output, std::string const& input); + static bool serializeSimulationParametersToFile(std::string const& filename, SimulationParameters const& parameters); static bool deserializeSimulationParametersFromFile(SimulationParameters& parameters, std::string const& filename); @@ -46,4 +49,7 @@ class Serializer static void serializeSimulationParameters(SimulationParameters const& parameters, std::ostream& stream); static void deserializeSimulationParameters(SimulationParameters& parameters, std::istream& stream); + + static bool wrapGenome(ClusteredDataDescription& output, std::vector const& input); + static bool unwrapGenome(std::vector& output, ClusteredDataDescription const& input); }; diff --git a/source/Gui/BrowserWindow.cpp b/source/Gui/BrowserWindow.cpp index 18b185a2a..87551df09 100644 --- a/source/Gui/BrowserWindow.cpp +++ b/source/Gui/BrowserWindow.cpp @@ -14,6 +14,7 @@ #include "Base/Resources.h" #include "Base/StringHelper.h" #include "Base/VersionChecker.h" +#include "EngineInterface/GenomeDescriptionConverter.h" #include "EngineInterface/Serializer.h" #include "EngineInterface/SimulationController.h" @@ -28,8 +29,10 @@ #include "LoginDialog.h" #include "UploadSimulationDialog.h" #include "DelayedExecutionController.h" +#include "EditorController.h" #include "OpenGLHelper.h" #include "OverlayMessageController.h" +#include "GenomeEditorWindow.h" namespace { @@ -47,13 +50,15 @@ _BrowserWindow::_BrowserWindow( NetworkController const& networkController, StatisticsWindow const& statisticsWindow, Viewport const& viewport, - TemporalControlWindow const& temporalControlWindow) + TemporalControlWindow const& temporalControlWindow, + EditorController const& editorController) : _AlienWindow("Browser", "windows.browser", true) , _simController(simController) , _networkController(networkController) , _statisticsWindow(statisticsWindow) , _viewport(viewport) , _temporalControlWindow(temporalControlWindow) + , _editorController(editorController) { _showCommunityCreations = GlobalSettings::getInstance().getBoolState("windows.browser.show community creations", _showCommunityCreations); _userTableWidth = GlobalSettings::getInstance().getFloatState("windows.browser.user table width", scale(UserTableWidth)); @@ -91,15 +96,25 @@ void _BrowserWindow::onRefresh() void _BrowserWindow::refreshIntern(bool withRetry) { try { - bool success = _networkController->getRemoteSimulationList(_rawRemoteSimulationList, withRetry); + bool success = _networkController->getRemoteSimulationList(_rawRemoteDataList, withRetry); success &= _networkController->getUserList(_userList, withRetry); if (!success) { if (withRetry) { MessageDialog::getInstance().information("Error", "Failed to retrieve browser data. Please try again."); } + } else { + _numSimulations = 0; + _numGenomes = 0; + for (auto const& entry : _rawRemoteDataList) { + if (entry.type == DataType_Simulation) { + ++_numSimulations; + } else { + ++_numGenomes; + } + } } - calcFilteredSimulationDatas(); + calcFilteredSimulationAndGenomeLists(); if (_networkController->getLoggedInUserName()) { if (!_networkController->getEmojiTypeBySimId(_ownEmojiTypeBySimId)) { @@ -129,7 +144,17 @@ void _BrowserWindow::processIntern() ImVec2(sizeAvailable.x - scale(_userTableWidth), sizeAvailable.y - scale(BrowserBottomHeight)), false, ImGuiWindowFlags_HorizontalScrollbar)) { - processSimulationTable(); + if (ImGui::BeginTabBar("##Type", ImGuiTabBarFlags_FittingPolicyResizeDown)) { + if (ImGui::BeginTabItem("Simulations", nullptr, ImGuiTabItemFlags_None)) { + processSimulationList(); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Genomes", nullptr, ImGuiTabItemFlags_None)) { + processGenomeList(); + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); + } } ImGui::EndChild(); } @@ -148,7 +173,7 @@ void _BrowserWindow::processIntern() auto sizeAvailable = ImGui::GetContentRegionAvail(); if (ImGui::BeginChild( "##2", ImVec2(sizeAvailable.x, sizeAvailable.y - scale(BrowserBottomHeight)), false, ImGuiWindowFlags_HorizontalScrollbar)) { - processUserTable(); + processUserList(); } ImGui::EndChild(); } @@ -196,27 +221,25 @@ void _BrowserWindow::processToolbar() ImGui::SameLine(); if (AlienImGui::ToolbarButton(ICON_FA_SHARE_ALT)) { - if (_networkController->getLoggedInUserName()) { - if (auto uploadSimulationDialog = _uploadSimulationDialog.lock()) { - uploadSimulationDialog->open(); - } - } else { - _loginDialog.lock()->open(); - } + _uploadSimulationDialog.lock()->open(_selectedDataType); } - AlienImGui::Tooltip("Share your simulation with other users:\nYour current simulation will be uploaded to the server and made visible in the browser."); + std::string dataType = _selectedDataType == DataType_Simulation + ? "simulation" + : "genome"; + AlienImGui::Tooltip( + "Share your " + dataType + " with other users:\nYour current " + dataType + " will be uploaded to the server and made visible in the browser."); AlienImGui::Separator(); } -void _BrowserWindow::processSimulationTable() +void _BrowserWindow::processSimulationList() { - ImGui::PushID("SimTable"); + ImGui::PushID("SimulationList"); + _selectedDataType = DataType_Simulation; auto styleRepository = StyleRepository::getInstance(); static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_ScrollY | ImGuiTableFlags_ScrollX; - AlienImGui::Group("Simulations"); if (ImGui::BeginTable("Browser", 12, flags, ImVec2(0, 0), 0.0f)) { ImGui::TableSetupColumn( "Actions", @@ -279,48 +302,9 @@ void _BrowserWindow::processSimulationTable() ImGui::TableNextRow(0, scale(RowHeight)); ImGui::TableNextColumn(); - - //like button - auto liked = isLiked(item->id); - if (liked) { - ImGui::PushStyleColor(ImGuiCol_Text, (ImU32)Const::LikeButtonTextColor); - } else { - ImGui::PushStyleColor(ImGuiCol_Text, (ImU32)Const::NoLikeButtonTextColor); - } - auto likeButtonResult = processActionButton(ICON_FA_SMILE); - ImGui::PopStyleColor(); - if (likeButtonResult) { - _activateEmojiPopup = true; - _simIndexOfEmojiPopup = row; - } - AlienImGui::Tooltip("Choose a reaction"); - ImGui::SameLine(); - - //download button - ImGui::PushStyleColor(ImGuiCol_Text, (ImU32)Const::DownloadButtonTextColor); - auto downloadButtonResult = processActionButton(ICON_FA_DOWNLOAD); - ImGui::PopStyleColor(); - if (downloadButtonResult) { - onDownloadSimulation(item); - } - AlienImGui::Tooltip("Download"); - ImGui::SameLine(); - - //delete color - if (item->userName == _networkController->getLoggedInUserName().value_or("")) { - ImGui::PushStyleColor(ImGuiCol_Text, (ImU32)Const::DeleteButtonTextColor); - auto deleteButtonResult = processActionButton(ICON_FA_TRASH); - ImGui::PopStyleColor(); - if (deleteButtonResult) { - onDeleteSimulation(item); - } - AlienImGui::Tooltip("Delete"); - } - + processActionButtons(item); ImGui::TableNextColumn(); - pushTextColor(*item); - AlienImGui::Text(item->timestamp); ImGui::TableNextColumn(); processShortenedText(item->userName); @@ -352,6 +336,103 @@ void _BrowserWindow::processSimulationTable() ImGui::PopID(); } +void _BrowserWindow::processGenomeList() +{ + ImGui::PushID("GenomeList"); + _selectedDataType = DataType_Genome; + auto styleRepository = StyleRepository::getInstance(); + static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable + | ImGuiTableFlags_SortMulti | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_NoBordersInBody + | ImGuiTableFlags_ScrollY | ImGuiTableFlags_ScrollX; + + if (ImGui::BeginTable("Browser", 10, flags, ImVec2(0, 0), 0.0f)) { + ImGui::TableSetupColumn( + "Actions", ImGuiTableColumnFlags_PreferSortDescending | ImGuiTableColumnFlags_WidthFixed, scale(90.0f), RemoteSimulationDataColumnId_Actions); + ImGui::TableSetupColumn( + "Timestamp", + ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_PreferSortDescending, + scale(135.0f), + RemoteSimulationDataColumnId_Timestamp); + ImGui::TableSetupColumn( + "User name", + ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed, + styleRepository.scale(120.0f), + RemoteSimulationDataColumnId_UserName); + ImGui::TableSetupColumn( + "Genome name", + ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed, + styleRepository.scale(160.0f), + RemoteSimulationDataColumnId_SimulationName); + ImGui::TableSetupColumn( + "Description", + ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed, + styleRepository.scale(120.0f), + RemoteSimulationDataColumnId_Description); + ImGui::TableSetupColumn( + "Reactions", + ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed, + styleRepository.scale(120.0f), + RemoteSimulationDataColumnId_Likes); + ImGui::TableSetupColumn( + "Downloads", ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed, 0.0f, RemoteSimulationDataColumnId_NumDownloads); + ImGui::TableSetupColumn("Cells", ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed, 0.0f, RemoteSimulationDataColumnId_Particles); + ImGui::TableSetupColumn("File size", ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed, 0.0f, RemoteSimulationDataColumnId_FileSize); + ImGui::TableSetupColumn("Version", ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed, 0.0f, RemoteSimulationDataColumnId_Version); + ImGui::TableSetupScrollFreeze(0, 1); + ImGui::TableHeadersRow(); + + //sort our data if sort specs have been changed! + if (ImGuiTableSortSpecs* sortSpecs = ImGui::TableGetSortSpecs()) { + if (sortSpecs->SpecsDirty || _scheduleSort) { + if (_filteredRemoteGenomeList.size() > 1) { + std::sort(_filteredRemoteGenomeList.begin(), _filteredRemoteGenomeList.end(), [&](auto const& left, auto const& right) { + return RemoteSimulationData::compare(&left, &right, sortSpecs) < 0; + }); + } + sortSpecs->SpecsDirty = false; + } + } + ImGuiListClipper clipper; + clipper.Begin(_filteredRemoteGenomeList.size()); + while (clipper.Step()) + for (int row = clipper.DisplayStart; row < clipper.DisplayEnd; row++) { + + RemoteSimulationData* item = &_filteredRemoteGenomeList[row]; + + ImGui::PushID(row); + ImGui::TableNextRow(0, scale(RowHeight)); + + ImGui::TableNextColumn(); + processActionButtons(item); + ImGui::TableNextColumn(); + pushTextColor(*item); + AlienImGui::Text(item->timestamp); + ImGui::TableNextColumn(); + processShortenedText(item->userName); + ImGui::TableNextColumn(); + processShortenedText(item->simName); + ImGui::TableNextColumn(); + processShortenedText(item->description); + ImGui::TableNextColumn(); + processEmojiList(item); + + ImGui::TableNextColumn(); + AlienImGui::Text(std::to_string(item->numDownloads)); + ImGui::TableNextColumn(); + AlienImGui::Text(StringHelper::format(item->particles)); + ImGui::TableNextColumn(); + AlienImGui::Text(StringHelper::format(item->contentSize) + " Bytes"); + ImGui::TableNextColumn(); + AlienImGui::Text(item->version); + + ImGui::PopStyleColor(); + ImGui::PopID(); + } + ImGui::EndTable(); + } + ImGui::PopID(); +} + namespace { std::string getGpuString(std::string const& gpu) @@ -363,7 +444,7 @@ namespace } } -void _BrowserWindow::processUserTable() +void _BrowserWindow::processUserList() { ImGui::PushID("UserTable"); auto styleRepository = StyleRepository::getInstance(); @@ -439,7 +520,10 @@ void _BrowserWindow::processStatus() ImGui::PushStyleColor(ImGuiCol_Text, (ImVec4)Const::MonospaceColor); std::string statusText; statusText += std::string(" " ICON_FA_INFO_CIRCLE " "); - statusText += std::to_string(_rawRemoteSimulationList.size()) + " simulations found"; + statusText += std::to_string(_numSimulations) + " simulations found"; + + statusText += std::string(" " ICON_FA_INFO_CIRCLE " "); + statusText += std::to_string(_numGenomes) + " genomes found"; statusText += std::string(" " ICON_FA_INFO_CIRCLE " "); statusText += std::to_string(_userList.size()) + " simulators found"; @@ -465,11 +549,11 @@ void _BrowserWindow::processFilter() { ImGui::Spacing(); if (AlienImGui::ToggleButton(AlienImGui::ToggleButtonParameters().name("Community creations"), _showCommunityCreations)) { - calcFilteredSimulationDatas(); + calcFilteredSimulationAndGenomeLists(); } ImGui::SameLine(); if (AlienImGui::InputText(AlienImGui::InputTextParameters().name("Filter"), _filter)) { - calcFilteredSimulationDatas(); + calcFilteredSimulationAndGenomeLists(); } } @@ -529,14 +613,14 @@ void _BrowserWindow::processEmojiButton(int emojiType) auto cursorPos = ImGui::GetCursorScreenPos(); auto emojiWidth = scale(toFloat(emoji.width)); auto emojiHeight = scale(toFloat(emoji.height)); - auto& sim = _filteredRemoteSimulationList.at(_simIndexOfEmojiPopup); + auto const& sim = _simOfEmojiPopup; if (ImGui::ImageButton((void*)(intptr_t)emoji.textureId, {emojiWidth, emojiHeight}, {0, 0}, {1.0f, 1.0f})) { onToggleLike(sim, toInt(emojiType)); ImGui::CloseCurrentPopup(); } ImGui::PopStyleColor(2); - bool isLiked = _ownEmojiTypeBySimId.contains(sim.id) && _ownEmojiTypeBySimId.at(sim.id) == emojiType; + bool isLiked = _ownEmojiTypeBySimId.contains(sim->id) && _ownEmojiTypeBySimId.at(sim->id) == emojiType; if (isLiked) { ImDrawList* drawList = ImGui::GetWindowDrawList(); auto& style = ImGui::GetStyle(); @@ -609,7 +693,47 @@ void _BrowserWindow::processEmojiList(RemoteSimulationData* sim) } } if (toggleEmojiType) { - onToggleLike(*sim, *toggleEmojiType); + onToggleLike(sim, *toggleEmojiType); + } +} + +void _BrowserWindow::processActionButtons(RemoteSimulationData* simData) +{ + //like button + auto liked = isLiked(simData->id); + if (liked) { + ImGui::PushStyleColor(ImGuiCol_Text, (ImU32)Const::LikeButtonTextColor); + } else { + ImGui::PushStyleColor(ImGuiCol_Text, (ImU32)Const::NoLikeButtonTextColor); + } + auto likeButtonResult = processActionButton(ICON_FA_SMILE); + ImGui::PopStyleColor(); + if (likeButtonResult) { + _activateEmojiPopup = true; + _simOfEmojiPopup = simData; + } + AlienImGui::Tooltip("Choose a reaction"); + ImGui::SameLine(); + + //download button + ImGui::PushStyleColor(ImGuiCol_Text, (ImU32)Const::DownloadButtonTextColor); + auto downloadButtonResult = processActionButton(ICON_FA_DOWNLOAD); + ImGui::PopStyleColor(); + if (downloadButtonResult) { + onDownloadItem(simData); + } + AlienImGui::Tooltip("Download"); + ImGui::SameLine(); + + //delete button + if (simData->userName == _networkController->getLoggedInUserName().value_or("")) { + ImGui::PushStyleColor(ImGuiCol_Text, (ImU32)Const::DeleteButtonTextColor); + auto deleteButtonResult = processActionButton(ICON_FA_TRASH); + ImGui::PopStyleColor(); + if (deleteButtonResult) { + onDeleteItem(simData); + } + AlienImGui::Tooltip("Delete"); } } @@ -617,8 +741,8 @@ namespace { std::vector splitString(const std::string& str) { - std::vector tokens; - boost::algorithm::split_regex(tokens, str, boost::regex("(\r\n)+")); + std::vector tokens; + boost::algorithm::split_regex(tokens, str, boost::regex("(\n)+")); return tokens; } } @@ -685,66 +809,77 @@ void _BrowserWindow::sortUserList() std::sort(_userList.begin(), _userList.end(), [&](auto const& left, auto const& right) { return UserData::compareOnlineAndTimestamp(left, right) > 0; }); } -void _BrowserWindow::onDownloadSimulation(RemoteSimulationData* sim) +void _BrowserWindow::onDownloadItem(RemoteSimulationData* sim) { printOverlayMessage("Downloading ..."); delayedExecution([=, this] { + std::string dataTypeString = _selectedDataType == DataType_Simulation ? "simulation" : "genome"; SerializedSimulation serializedSim; if (!_networkController->downloadSimulation(serializedSim.mainData, serializedSim.auxiliaryData, sim->id)) { - MessageDialog::getInstance().information("Error", "Failed to download simulation."); + MessageDialog::getInstance().information("Error", "Failed to download " + dataTypeString + "."); return; } - DeserializedSimulation deserializedSim; - if (!Serializer::deserializeSimulationFromStrings(deserializedSim, serializedSim)) { - MessageDialog::getInstance().information("Error", "Failed to load simulation. Your program version may not match."); - return; - } - - _simController->closeSimulation(); - _statisticsWindow->reset(); - - std::optional errorMessage; - try { - _simController->newSimulation( - deserializedSim.auxiliaryData.timestep, deserializedSim.auxiliaryData.generalSettings, deserializedSim.auxiliaryData.simulationParameters); - _simController->setClusteredSimulationData(deserializedSim.mainData); - } catch (CudaMemoryAllocationException const& exception) { - errorMessage = exception.what(); - } catch (...) { - errorMessage = "Failed to load simulation."; - } + if (_selectedDataType == DataType_Simulation) { + DeserializedSimulation deserializedSim; + if (!Serializer::deserializeSimulationFromStrings(deserializedSim, serializedSim)) { + MessageDialog::getInstance().information("Error", "Failed to load simulation. Your program version may not match."); + return; + } - if (errorMessage) { - printMessage("Error", *errorMessage); _simController->closeSimulation(); - _simController->newSimulation( - deserializedSim.auxiliaryData.timestep, deserializedSim.auxiliaryData.generalSettings, deserializedSim.auxiliaryData.simulationParameters); - } + _statisticsWindow->reset(); + + std::optional errorMessage; + try { + _simController->newSimulation( + deserializedSim.auxiliaryData.timestep, deserializedSim.auxiliaryData.generalSettings, deserializedSim.auxiliaryData.simulationParameters); + _simController->setClusteredSimulationData(deserializedSim.mainData); + } catch (CudaMemoryAllocationException const& exception) { + errorMessage = exception.what(); + } catch (...) { + errorMessage = "Failed to load simulation."; + } - _viewport->setCenterInWorldPos(deserializedSim.auxiliaryData.center); - _viewport->setZoomFactor(deserializedSim.auxiliaryData.zoom); - _temporalControlWindow->onSnapshot(); + if (errorMessage) { + showMessage("Error", *errorMessage); + _simController->closeSimulation(); + _simController->newSimulation( + deserializedSim.auxiliaryData.timestep, deserializedSim.auxiliaryData.generalSettings, deserializedSim.auxiliaryData.simulationParameters); + } + + _viewport->setCenterInWorldPos(deserializedSim.auxiliaryData.center); + _viewport->setZoomFactor(deserializedSim.auxiliaryData.zoom); + _temporalControlWindow->onSnapshot(); + } else { + std::vector genome; + if (!Serializer::deserializeGenomeFromString(genome, serializedSim.mainData)) { + MessageDialog::getInstance().information("Error", "Failed to load genome. Your program version may not match."); + return; + } + _editorController->setOn(true); + _editorController->getGenomeEditorWindow()->openTab(GenomeDescriptionConverter::convertBytesToDescription(genome)); + } if (VersionChecker::isVersionNewer(sim->version)) { MessageDialog::getInstance().information( "Warning", - "The download was successful but the simulation was generated using a more recent\n" - "version of ALIEN. Consequently, the simulation might not function as expected.\n" + "The download was successful but the " + dataTypeString +" was generated using a more recent\n" + "version of ALIEN. Consequently, the " + dataTypeString + "might not function as expected.\n" "Please visit\n\nhttps://github.com/chrxh/alien\n\nto obtain the latest version."); } }); } -void _BrowserWindow::onDeleteSimulation(RemoteSimulationData* sim) +void _BrowserWindow::onDeleteItem(RemoteSimulationData* sim) { - MessageDialog::getInstance().yesNo("Delete simulation", "Do you really want to delete the simulation?", [sim, this]() { + MessageDialog::getInstance().yesNo("Delete item", "Do you really want to delete the selected item?", [sim, this]() { printOverlayMessage("Deleting ..."); delayedExecution([remoteData = sim, this] { if (!_networkController->deleteSimulation(remoteData->id)) { - MessageDialog::getInstance().information("Error", "Failed to delete simulation. Please try again later."); + MessageDialog::getInstance().information("Error", "Failed to delete item. Please try again later."); return; } _scheduleRefresh = true; @@ -752,35 +887,35 @@ void _BrowserWindow::onDeleteSimulation(RemoteSimulationData* sim) }); } -void _BrowserWindow::onToggleLike(RemoteSimulationData& sim, int emojiType) +void _BrowserWindow::onToggleLike(RemoteSimulationData* sim, int emojiType) { if (_networkController->getLoggedInUserName()) { //remove existing like - auto findResult = _ownEmojiTypeBySimId.find(sim.id); + auto findResult = _ownEmojiTypeBySimId.find(sim->id); auto onlyRemoveLike = false; if (findResult != _ownEmojiTypeBySimId.end()) { auto origEmojiType = findResult->second; - if (--sim.numLikesByEmojiType[origEmojiType] == 0) { - sim.numLikesByEmojiType.erase(origEmojiType); + if (--sim->numLikesByEmojiType[origEmojiType] == 0) { + sim->numLikesByEmojiType.erase(origEmojiType); } _ownEmojiTypeBySimId.erase(findResult); - _userNamesByEmojiTypeBySimIdCache.erase(std::make_pair(sim.id, origEmojiType)); //invalidate cache entry + _userNamesByEmojiTypeBySimIdCache.erase(std::make_pair(sim->id, origEmojiType)); //invalidate cache entry onlyRemoveLike = origEmojiType == emojiType; //remove like if same like icon has been clicked } //create new like if (!onlyRemoveLike) { - _ownEmojiTypeBySimId[sim.id] = emojiType; - if (sim.numLikesByEmojiType.contains(emojiType)) { - ++sim.numLikesByEmojiType[emojiType]; + _ownEmojiTypeBySimId[sim->id] = emojiType; + if (sim->numLikesByEmojiType.contains(emojiType)) { + ++sim->numLikesByEmojiType[emojiType]; } else { - sim.numLikesByEmojiType[emojiType] = 1; + sim->numLikesByEmojiType[emojiType] = 1; } } - _userNamesByEmojiTypeBySimIdCache.erase(std::make_pair(sim.id, emojiType)); //invalidate cache entry - _networkController->toggleLikeSimulation(sim.id, emojiType); + _userNamesByEmojiTypeBySimIdCache.erase(std::make_pair(sim->id, emojiType)); //invalidate cache entry + _networkController->toggleLikeSimulation(sim->id, emojiType); sortSimulationList(); } else { _loginDialog.lock()->open(); @@ -818,13 +953,19 @@ void _BrowserWindow::pushTextColor(RemoteSimulationData const& entry) } } -void _BrowserWindow::calcFilteredSimulationDatas() +void _BrowserWindow::calcFilteredSimulationAndGenomeLists() { _filteredRemoteSimulationList.clear(); - _filteredRemoteSimulationList.reserve(_rawRemoteSimulationList.size()); - for (auto const& simData : _rawRemoteSimulationList) { + _filteredRemoteSimulationList.reserve(_rawRemoteDataList.size()); + _filteredRemoteGenomeList.clear(); + _filteredRemoteGenomeList.reserve(_filteredRemoteGenomeList.size()); + for (auto const& simData : _rawRemoteDataList) { if (simData.matchWithFilter(_filter) &&_showCommunityCreations != simData.fromRelease) { - _filteredRemoteSimulationList.emplace_back(simData); + if (simData.type == RemoteDataType_Simulation) { + _filteredRemoteSimulationList.emplace_back(simData); + } else { + _filteredRemoteGenomeList.emplace_back(simData); + } } } } diff --git a/source/Gui/BrowserWindow.h b/source/Gui/BrowserWindow.h index 087d28bbf..3b9347da7 100644 --- a/source/Gui/BrowserWindow.h +++ b/source/Gui/BrowserWindow.h @@ -16,7 +16,8 @@ class _BrowserWindow : public _AlienWindow NetworkController const& networkController, StatisticsWindow const& statisticsWindow, Viewport const& viewport, - TemporalControlWindow const& temporalControlWindow); + TemporalControlWindow const& temporalControlWindow, + EditorController const& editorController); ~_BrowserWindow(); void registerCyclicReferences(LoginDialogWeakPtr const& loginDialog, UploadSimulationDialogWeakPtr const& uploadSimulationDialog); @@ -28,8 +29,9 @@ class _BrowserWindow : public _AlienWindow void processIntern() override; - void processSimulationTable(); - void processUserTable(); + void processSimulationList(); + void processGenomeList(); + void processUserList(); void processStatus(); void processFilter(); @@ -39,6 +41,8 @@ class _BrowserWindow : public _AlienWindow void processEmojiButton(int emojiType); void processEmojiList(RemoteSimulationData* sim); + void processActionButtons(RemoteSimulationData* simData); + void processShortenedText(std::string const& text, bool bold = false); bool processActionButton(std::string const& text); bool processDetailButton(); @@ -48,16 +52,17 @@ class _BrowserWindow : public _AlienWindow void sortSimulationList(); void sortUserList(); - void onDownloadSimulation(RemoteSimulationData* sim); - void onDeleteSimulation(RemoteSimulationData* sim); - void onToggleLike(RemoteSimulationData& sim, int emojiType); + void onDownloadItem(RemoteSimulationData* sim); + void onDeleteItem(RemoteSimulationData* sim); + void onToggleLike(RemoteSimulationData* sim, int emojiType); bool isLiked(std::string const& simId); std::string getUserNamesToEmojiType(std::string const& simId, int emojiType); void pushTextColor(RemoteSimulationData const& entry); - void calcFilteredSimulationDatas(); + void calcFilteredSimulationAndGenomeLists(); + DataType _selectedDataType = DataType_Simulation; bool _scheduleRefresh = false; bool _scheduleSort = false; std::string _filter; @@ -66,15 +71,20 @@ class _BrowserWindow : public _AlienWindow std::unordered_set _selectionIds; std::unordered_map _ownEmojiTypeBySimId; std::unordered_map, std::set> _userNamesByEmojiTypeBySimIdCache; - std::vector _rawRemoteSimulationList; + + int _numSimulations = 0; + int _numGenomes = 0; + std::vector _rawRemoteDataList; std::vector _filteredRemoteSimulationList; + std::vector _filteredRemoteGenomeList; + std::vector _userList; std::vector _emojis; bool _activateEmojiPopup = false; bool _showAllEmojis = false; - int _simIndexOfEmojiPopup = 0; //index in _filteredRemoteSimulationList + RemoteSimulationData* _simOfEmojiPopup = nullptr; SimulationController _simController; NetworkController _networkController; @@ -82,5 +92,6 @@ class _BrowserWindow : public _AlienWindow Viewport _viewport; TemporalControlWindow _temporalControlWindow; LoginDialogWeakPtr _loginDialog; + EditorController _editorController; UploadSimulationDialogWeakPtr _uploadSimulationDialog; }; diff --git a/source/Gui/CMakeLists.txt b/source/Gui/CMakeLists.txt index da81ddba4..ad5e67e67 100644 --- a/source/Gui/CMakeLists.txt +++ b/source/Gui/CMakeLists.txt @@ -37,8 +37,6 @@ PUBLIC EditorModel.h ExitDialog.cpp ExitDialog.h - FileLogger.cpp - FileLogger.h FpsController.cpp FpsController.h GenericFileDialogs.cpp @@ -49,6 +47,8 @@ PUBLIC GettingStartedWindow.h GpuSettingsDialog.cpp GpuSettingsDialog.h + GuiLogger.cpp + GuiLogger.h HelpStrings.h ImageToPatternDialog.cpp ImageToPatternDialog.h @@ -101,8 +101,6 @@ PUBLIC Shader.h ShaderWindow.cpp ShaderWindow.h - SimpleLogger.cpp - SimpleLogger.h SimulationParametersWindow.cpp SimulationParametersWindow.h SimulationScrollbar.cpp diff --git a/source/Gui/CreatorWindow.cpp b/source/Gui/CreatorWindow.cpp index 575d785e2..cc92bc3c7 100644 --- a/source/Gui/CreatorWindow.cpp +++ b/source/Gui/CreatorWindow.cpp @@ -35,7 +35,7 @@ namespace } _CreatorWindow::_CreatorWindow(EditorModel const& editorModel, SimulationController const& simController, Viewport const& viewport) - : _AlienWindow("Creator", "editors.creator", true), _editorModel(editorModel) + : _AlienWindow("Creator", "editors.creator", false), _editorModel(editorModel) , _simController(simController) , _viewport(viewport) { diff --git a/source/Gui/Definitions.h b/source/Gui/Definitions.h index de92f8a27..5733a2a9d 100644 --- a/source/Gui/Definitions.h +++ b/source/Gui/Definitions.h @@ -58,11 +58,8 @@ using MassOperationsDialog = std::shared_ptr<_MassOperationsDialog>; class _LogWindow; using LogWindow = std::shared_ptr<_LogWindow>; -class _SimpleLogger; -using SimpleLogger = std::shared_ptr<_SimpleLogger>; - -class _FileLogger; -using FileLogger = std::shared_ptr<_FileLogger>; +class _GuiLogger; +using GuiLogger = std::shared_ptr<_GuiLogger>; class _UiController; using UiController = std::shared_ptr<_UiController>; @@ -173,3 +170,10 @@ struct TextureData int width; int height; }; + +using DataType = int; +enum DataType_ +{ + DataType_Simulation, + DataType_Genome +}; diff --git a/source/Gui/EditorController.cpp b/source/Gui/EditorController.cpp index 7372659e1..43b4fc9a4 100644 --- a/source/Gui/EditorController.cpp +++ b/source/Gui/EditorController.cpp @@ -34,6 +34,11 @@ _EditorController::_EditorController(SimulationController const& simController, _multiplierWindow = std::make_shared<_MultiplierWindow>(_editorModel, _simController, _viewport); } +void _EditorController::registerCyclicReferences(UploadSimulationDialogWeakPtr const& uploadSimulationDialog) +{ + _genomeEditorWindow->registerCyclicReferences(uploadSimulationDialog); +} + bool _EditorController::isOn() const { return _on; @@ -124,7 +129,7 @@ void _EditorController::onInspectSelectedObjects() DataDescription selectedData = _simController->getSelectedSimulationData(false); onInspectObjects(DescriptionHelper::getObjects(selectedData), false); } else { - printMessage( + showMessage( "Inspection not possible", "Too many objects are selected for inspection. A maximum of " + std::to_string(MaxInspectorWindowsToAdd) + " objects are allowed."); diff --git a/source/Gui/EditorController.h b/source/Gui/EditorController.h index 99cd97f66..59d2d4bc3 100644 --- a/source/Gui/EditorController.h +++ b/source/Gui/EditorController.h @@ -10,6 +10,8 @@ class _EditorController public: _EditorController(SimulationController const& simController, Viewport const& viewport); + void registerCyclicReferences(UploadSimulationDialogWeakPtr const& uploadSimulationDialog); + bool isOn() const; void setOn(bool value); diff --git a/source/Gui/GenomeEditorWindow.cpp b/source/Gui/GenomeEditorWindow.cpp index 3145191b8..1049aa1e2 100644 --- a/source/Gui/GenomeEditorWindow.cpp +++ b/source/Gui/GenomeEditorWindow.cpp @@ -19,6 +19,7 @@ #include "AlienImGui.h" #include "CellFunctionStrings.h" +#include "DelayedExecutionController.h" #include "EditorModel.h" #include "GenericFileDialogs.h" #include "MessageDialog.h" @@ -26,6 +27,7 @@ #include "StyleRepository.h" #include "Viewport.h" #include "HelpStrings.h" +#include "UploadSimulationDialog.h" namespace { @@ -58,9 +60,15 @@ _GenomeEditorWindow::~_GenomeEditorWindow() GlobalSettings::getInstance().setFloatState("windows.genome editor.preview height", _previewHeight); } +void _GenomeEditorWindow::registerCyclicReferences(UploadSimulationDialogWeakPtr const& uploadSimulationDialog) +{ + _uploadSimulationDialog = uploadSimulationDialog; +} + void _GenomeEditorWindow::openTab(GenomeDescription const& genome) { - setOn(true); + setOn(false); + delayedExecution([this] { setOn(true); }); if (_tabDatas.size() == 1 && _tabDatas.front().genome.cells.empty()) { _tabDatas.clear(); } @@ -122,6 +130,12 @@ void _GenomeEditorWindow::processToolbar() } AlienImGui::Tooltip("Save genome to file"); + ImGui::SameLine(); + if (AlienImGui::ToolbarButton(ICON_FA_SHARE_ALT)) { + onUploadGenome(); + } + AlienImGui::Tooltip("Share your genome with other users:\nYour current genome will be uploaded to the server and made visible in the browser."); + ImGui::SameLine(); AlienImGui::ToolbarSeparator(); @@ -792,6 +806,11 @@ void _GenomeEditorWindow::onSaveGenome() }); } +void _GenomeEditorWindow::onUploadGenome() +{ + _uploadSimulationDialog.lock()->open(DataType_Genome); +} + void _GenomeEditorWindow::onAddNode() { auto& tabData = _tabDatas.at(_selectedTabIndex); diff --git a/source/Gui/GenomeEditorWindow.h b/source/Gui/GenomeEditorWindow.h index e932245fe..5ac40a673 100644 --- a/source/Gui/GenomeEditorWindow.h +++ b/source/Gui/GenomeEditorWindow.h @@ -12,6 +12,8 @@ class _GenomeEditorWindow : public _AlienWindow _GenomeEditorWindow(EditorModel const& editorModel, SimulationController const& simulationController, Viewport const& viewport); ~_GenomeEditorWindow() override; + void registerCyclicReferences(UploadSimulationDialogWeakPtr const& uploadSimulationDialog); + void openTab(GenomeDescription const& genome); GenomeDescription const& getCurrentGenome() const; @@ -35,6 +37,7 @@ class _GenomeEditorWindow : public _AlienWindow void onOpenGenome(); void onSaveGenome(); + void onUploadGenome(); void onAddNode(); void onDeleteNode(); void onNodeDecreaseSequenceNumber(); @@ -50,10 +53,6 @@ class _GenomeEditorWindow : public _AlienWindow void updateGeometry(GenomeDescription& genome, ConstructionShape shape); - EditorModel _editorModel; - SimulationController _simController; - Viewport _viewport; - float _previewHeight = 0; mutable int _tabSequenceNumber = 0; @@ -71,4 +70,8 @@ class _GenomeEditorWindow : public _AlienWindow std::optional _tabToAdd; std::optional _expandNodes; + EditorModel _editorModel; + SimulationController _simController; + Viewport _viewport; + UploadSimulationDialogWeakPtr _uploadSimulationDialog; }; diff --git a/source/Gui/SimpleLogger.cpp b/source/Gui/GuiLogger.cpp similarity index 62% rename from source/Gui/SimpleLogger.cpp rename to source/Gui/GuiLogger.cpp index 2bd0c907a..fc8afc1e0 100644 --- a/source/Gui/SimpleLogger.cpp +++ b/source/Gui/GuiLogger.cpp @@ -1,18 +1,18 @@ -#include "SimpleLogger.h" +#include "GuiLogger.h" #include "Base/LoggingService.h" -_SimpleLogger::_SimpleLogger() +_GuiLogger::_GuiLogger() { LoggingService::getInstance().registerCallBack(this); } -_SimpleLogger::~_SimpleLogger() +_GuiLogger::~_GuiLogger() { LoggingService::getInstance().unregisterCallBack(this); } -std::vector const& _SimpleLogger::getMessages(Priority minPriority) const +std::vector const& _GuiLogger::getMessages(Priority minPriority) const { if (Priority::Important == minPriority) { return _importantLogMessages; @@ -20,7 +20,7 @@ std::vector const& _SimpleLogger::getMessages(Priority minPriority) return _allLogMessages; } -void _SimpleLogger::newLogMessage(Priority priority, std::string const& message) +void _GuiLogger::newLogMessage(Priority priority, std::string const& message) { _allLogMessages.emplace_back(message); if (Priority::Important == priority) { diff --git a/source/Gui/SimpleLogger.h b/source/Gui/GuiLogger.h similarity index 78% rename from source/Gui/SimpleLogger.h rename to source/Gui/GuiLogger.h index 4e6918b4f..8367e349f 100644 --- a/source/Gui/SimpleLogger.h +++ b/source/Gui/GuiLogger.h @@ -3,11 +3,11 @@ #include "Base/LoggingService.h" #include "Definitions.h" -class _SimpleLogger : public LoggingCallBack +class _GuiLogger : public LoggingCallBack { public: - _SimpleLogger(); - virtual ~_SimpleLogger(); + _GuiLogger(); + virtual ~_GuiLogger(); std::vector const& getMessages(Priority minPriority) const; diff --git a/source/Gui/LogWindow.cpp b/source/Gui/LogWindow.cpp index 4d65a7301..f6ef87248 100644 --- a/source/Gui/LogWindow.cpp +++ b/source/Gui/LogWindow.cpp @@ -7,10 +7,10 @@ #include "Base/GlobalSettings.h" #include "StyleRepository.h" -#include "SimpleLogger.h" +#include "GuiLogger.h" #include "AlienImGui.h" -_LogWindow::_LogWindow(SimpleLogger const& logger) +_LogWindow::_LogWindow(GuiLogger const& logger) : _AlienWindow("Log", "windows.log", false) , _logger(logger) { diff --git a/source/Gui/LogWindow.h b/source/Gui/LogWindow.h index b52e40e8a..0c69932ac 100644 --- a/source/Gui/LogWindow.h +++ b/source/Gui/LogWindow.h @@ -6,7 +6,7 @@ class _LogWindow : public _AlienWindow { public: - _LogWindow(SimpleLogger const& logger); + _LogWindow(GuiLogger const& logger); ~_LogWindow(); private: @@ -14,5 +14,5 @@ class _LogWindow : public _AlienWindow bool _verbose = false; - SimpleLogger _logger; + GuiLogger _logger; }; diff --git a/source/Gui/Main.cpp b/source/Gui/Main.cpp index 9122a6659..a7a10b919 100644 --- a/source/Gui/Main.cpp +++ b/source/Gui/Main.cpp @@ -4,13 +4,13 @@ #include "Base/GlobalSettings.h" #include "Base/LoggingService.h" +#include "Base/Resources.h" +#include "Base/FileLogger.h" #include "EngineInterface/Serializer.h" #include "EngineImpl/SimulationControllerImpl.h" -#include "Base/Resources.h" #include "MainWindow.h" -#include "SimpleLogger.h" -#include "FileLogger.h" +#include "GuiLogger.h" #include "HelpStrings.h" namespace @@ -26,7 +26,7 @@ int main(int argc, char** argv) auto inDebugMode = isInDebugMode(argc, argv); GlobalSettings::getInstance().setDebugMode(inDebugMode); - SimpleLogger logger = std::make_shared<_SimpleLogger>(); + GuiLogger logger = std::make_shared<_GuiLogger>(); FileLogger fileLogger = std::make_shared<_FileLogger>(); if (inDebugMode) { diff --git a/source/Gui/MainWindow.cpp b/source/Gui/MainWindow.cpp index e76b0ebc5..3e1278a1e 100644 --- a/source/Gui/MainWindow.cpp +++ b/source/Gui/MainWindow.cpp @@ -39,7 +39,7 @@ #include "AboutDialog.h" #include "MassOperationsDialog.h" #include "LogWindow.h" -#include "SimpleLogger.h" +#include "GuiLogger.h" #include "UiController.h" #include "AutosaveController.h" #include "GettingStartedWindow.h" @@ -90,7 +90,7 @@ namespace } } -_MainWindow::_MainWindow(SimulationController const& simController, SimpleLogger const& logger) +_MainWindow::_MainWindow(SimulationController const& simController, GuiLogger const& logger) { _logger = logger; _simController = simController; @@ -149,13 +149,15 @@ _MainWindow::_MainWindow(SimulationController const& simController, SimpleLogger _displaySettingsDialog = std::make_shared<_DisplaySettingsDialog>(); _patternAnalysisDialog = std::make_shared<_PatternAnalysisDialog>(_simController); _fpsController = std::make_shared<_FpsController>(); - _browserWindow = std::make_shared<_BrowserWindow>(_simController, _networkController, _statisticsWindow, _viewport, _temporalControlWindow); + _browserWindow = + std::make_shared<_BrowserWindow>(_simController, _networkController, _statisticsWindow, _viewport, _temporalControlWindow, _editorController); _activateUserDialog = std::make_shared<_ActivateUserDialog>(_simController, _browserWindow, _networkController); _createUserDialog = std::make_shared<_CreateUserDialog>(_activateUserDialog, _networkController); _newPasswordDialog = std::make_shared<_NewPasswordDialog>(_simController, _browserWindow, _networkController); _resetPasswordDialog = std::make_shared<_ResetPasswordDialog>(_newPasswordDialog, _networkController); _loginDialog = std::make_shared<_LoginDialog>(_simController, _browserWindow, _createUserDialog, _activateUserDialog, _resetPasswordDialog, _networkController); - _uploadSimulationDialog = std::make_shared<_UploadSimulationDialog>(_browserWindow, _simController, _networkController, _viewport); + _uploadSimulationDialog = std::make_shared<_UploadSimulationDialog>( + _browserWindow, _loginDialog, _simController, _networkController, _viewport, _editorController->getGenomeEditorWindow()); _deleteUserDialog = std::make_shared<_DeleteUserDialog>(_browserWindow, _networkController); _networkSettingsDialog = std::make_shared<_NetworkSettingsDialog>(_browserWindow, _networkController); _imageToPatternDialog = std::make_shared<_ImageToPatternDialog>(_viewport, _simController); @@ -164,6 +166,7 @@ _MainWindow::_MainWindow(SimulationController const& simController, SimpleLogger //cyclic references _browserWindow->registerCyclicReferences(_loginDialog, _uploadSimulationDialog); _activateUserDialog->registerCyclicReferences(_createUserDialog); + _editorController->registerCyclicReferences(_uploadSimulationDialog); ifd::FileDialog::Instance().CreateTexture = [](uint8_t* data, int w, int h, char fmt) -> void* { GLuint tex; @@ -416,14 +419,19 @@ void _MainWindow::processMenubar() } ImGui::EndDisabled(); ImGui::BeginDisabled(!_networkController->getLoggedInUserName()); - if (ImGui::MenuItem("Upload", "ALT+D")) { - _uploadSimulationDialog->open(); + if (ImGui::MenuItem("Upload simulation", "ALT+D")) { + _uploadSimulationDialog->open(DataType_Simulation); + } + ImGui::EndDisabled(); + ImGui::BeginDisabled(!_networkController->getLoggedInUserName()); + if (ImGui::MenuItem("Upload genome", "ALT+Q")) { + _uploadSimulationDialog->open(DataType_Genome); } ImGui::EndDisabled(); ImGui::Separator(); ImGui::BeginDisabled(!_networkController->getLoggedInUserName()); - if (ImGui::MenuItem("Delete", "ALT+J")) { + if (ImGui::MenuItem("Delete user", "ALT+J")) { _deleteUserDialog->open(); } ImGui::EndDisabled(); @@ -601,7 +609,10 @@ void _MainWindow::processMenubar() _browserWindow->onRefresh(); } if (io.KeyAlt && ImGui::IsKeyPressed(GLFW_KEY_D) && _networkController->getLoggedInUserName()) { - _uploadSimulationDialog->open(); + _uploadSimulationDialog->open(DataType_Simulation); + } + if (io.KeyAlt && ImGui::IsKeyPressed(GLFW_KEY_Q) && _networkController->getLoggedInUserName()) { + _uploadSimulationDialog->open(DataType_Genome); } if (io.KeyAlt && ImGui::IsKeyPressed(GLFW_KEY_J) && _networkController->getLoggedInUserName()) { _deleteUserDialog->open(); @@ -781,7 +792,7 @@ void _MainWindow::onOpenSimulation() } if (errorMessage) { - printMessage("Error", *errorMessage); + showMessage("Error", *errorMessage); _simController->closeSimulation(); _simController->newSimulation( deserializedData.auxiliaryData.timestep, @@ -795,7 +806,7 @@ void _MainWindow::onOpenSimulation() printOverlayMessage(firstFilename.filename().string()); }); } else { - printMessage("Open simulation", "The selected file could not be opened."); + showMessage("Open simulation", "The selected file could not be opened."); } }); } diff --git a/source/Gui/MainWindow.h b/source/Gui/MainWindow.h index 4d0707af1..811b9de68 100644 --- a/source/Gui/MainWindow.h +++ b/source/Gui/MainWindow.h @@ -6,7 +6,7 @@ class _MainWindow { public: - _MainWindow(SimulationController const& simController, SimpleLogger const& logger); + _MainWindow(SimulationController const& simController, GuiLogger const& logger); void mainLoop(); void shutdown(); @@ -35,7 +35,7 @@ class _MainWindow void reset(); GLFWwindow* _window; - SimpleLogger _logger; + GuiLogger _logger; Viewport _viewport; diff --git a/source/Gui/MessageDialog.cpp b/source/Gui/MessageDialog.cpp index 799586d25..eb9e554a6 100644 --- a/source/Gui/MessageDialog.cpp +++ b/source/Gui/MessageDialog.cpp @@ -48,9 +48,10 @@ void MessageDialog::yesNo(std::string const& title, std::string const& message, void MessageDialog::processInformation() { - ImGui::OpenPopup(_title.c_str()); + auto title = (_title + "##msg").c_str(); + ImGui::OpenPopup(title); ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - if (ImGui::BeginPopupModal(_title.c_str(), NULL, ImGuiWindowFlags_AlwaysAutoResize)) { + if (ImGui::BeginPopupModal(title, NULL, ImGuiWindowFlags_AlwaysAutoResize)) { if (!_sizeInitialized) { auto size = ImGui::GetWindowSize(); auto& windowController = WindowController::getInstance(); diff --git a/source/Gui/MessageDialog.h b/source/Gui/MessageDialog.h index 9d1b36d90..08627577e 100644 --- a/source/Gui/MessageDialog.h +++ b/source/Gui/MessageDialog.h @@ -30,7 +30,7 @@ class MessageDialog std::function _execFunction; }; -inline void printMessage(std::string const& title, std::string const& message) +inline void showMessage(std::string const& title, std::string const& message) { MessageDialog::getInstance().information(title, message); } diff --git a/source/Gui/NetworkController.cpp b/source/Gui/NetworkController.cpp index 66dda9a33..2c30b3914 100644 --- a/source/Gui/NetworkController.cpp +++ b/source/Gui/NetworkController.cpp @@ -410,7 +410,8 @@ bool _NetworkController::uploadSimulation( IntVector2D const& size, int particles, std::string const& mainData, - std::string const& auxiliaryData) + std::string const& auxiliaryData, + RemoteDataType type) { log(Priority::Important, "network: upload simulation with name='" + simulationName + "'"); @@ -429,6 +430,7 @@ bool _NetworkController::uploadSimulation( {"content", mainData, "", "application/octet-stream"}, {"settings", auxiliaryData, "", ""}, {"symbolMap", "", "", ""}, + {"type", std::to_string(type), "", ""}, }; try { diff --git a/source/Gui/NetworkController.h b/source/Gui/NetworkController.h index d6a14bdb0..49df2b4c5 100644 --- a/source/Gui/NetworkController.h +++ b/source/Gui/NetworkController.h @@ -52,7 +52,8 @@ class _NetworkController IntVector2D const& size, int particles, std::string const& data, - std::string const& auxiliaryData); + std::string const& auxiliaryData, + RemoteDataType type); bool downloadSimulation(std::string& mainData, std::string& auxiliaryData, std::string const& simId); bool deleteSimulation(std::string const& simId); diff --git a/source/Gui/NetworkDataParser.cpp b/source/Gui/NetworkDataParser.cpp index f099c9e53..ae426976a 100644 --- a/source/Gui/NetworkDataParser.cpp +++ b/source/Gui/NetworkDataParser.cpp @@ -29,6 +29,7 @@ std::vector NetworkDataParser::decodeRemoteSimulationData( } entry.numDownloads = subTree.get("numDownloads"); entry.fromRelease = subTree.get("fromRelease") == 1; + entry.type = subTree.get("type"); result.emplace_back(entry); } return result; diff --git a/source/Gui/RemoteSimulationData.h b/source/Gui/RemoteSimulationData.h index 40a707ee1..1f8ddbda0 100644 --- a/source/Gui/RemoteSimulationData.h +++ b/source/Gui/RemoteSimulationData.h @@ -21,6 +21,13 @@ enum RemoteSimulationDataColumnId RemoteSimulationDataColumnId_Actions }; +using RemoteDataType = int; +enum RemoteDataType_ +{ + RemoteDataType_Simulation, + RemoteDataType_Genome +}; + class RemoteSimulationData { public: @@ -37,6 +44,7 @@ class RemoteSimulationData std::string description; std::string version; bool fromRelease; + RemoteDataType type; static int compare(void const* left, void const* right, ImGuiTableSortSpecs const* specs); bool matchWithFilter(std::string const& filter) const; diff --git a/source/Gui/StartupController.cpp b/source/Gui/StartupController.cpp index bb5cb9634..ae5dec3ed 100644 --- a/source/Gui/StartupController.cpp +++ b/source/Gui/StartupController.cpp @@ -5,6 +5,7 @@ #include "Base/Definitions.h" #include "Base/GlobalSettings.h" #include "Base/Resources.h" +#include "Base/LoggingService.h" #include "EngineInterface/Serializer.h" #include "EngineInterface/SimulationController.h" @@ -32,6 +33,7 @@ _StartupController::_StartupController( , _temporalControlWindow(temporalControlWindow) , _viewport(viewport) { + log(Priority::Important, "starting ALIEN v" + Const::ProgramVersion); _logo = OpenGLHelper::loadTexture(Const::LogoFilename); _lineDistance = scale(InitialLineDistance); _startupTimepoint = std::chrono::steady_clock::now(); diff --git a/source/Gui/UploadSimulationDialog.cpp b/source/Gui/UploadSimulationDialog.cpp index b966507ba..9ba13859b 100644 --- a/source/Gui/UploadSimulationDialog.cpp +++ b/source/Gui/UploadSimulationDialog.cpp @@ -5,6 +5,7 @@ #include "Base/GlobalSettings.h" #include "EngineInterface/Serializer.h" #include "EngineInterface/SimulationController.h" +#include "EngineInterface/GenomeDescriptionConverter.h" #include "AlienImGui.h" #include "MessageDialog.h" @@ -14,17 +15,33 @@ #include "DelayedExecutionController.h" #include "OverlayMessageController.h" #include "Viewport.h" +#include "GenomeEditorWindow.h" +#include "LoginDialog.h" + +namespace +{ + std::map const BrowserDataTypeToLowerString = { + {DataType_Simulation, "simulation"}, + {DataType_Genome, "genome"}}; + std::map const BrowserDataTypeToUpperString = { + {DataType_Simulation, "Simulation"}, + {DataType_Genome, "Genome"}}; +} _UploadSimulationDialog::_UploadSimulationDialog( BrowserWindow const& browserWindow, + LoginDialog const& loginDialog, SimulationController const& simController, NetworkController const& networkController, - Viewport const& viewport) - : _AlienDialog("Upload simulation") - ,_simController(simController) + Viewport const& viewport, + GenomeEditorWindow const& genomeEditorWindow) + : _AlienDialog("") + , _simController(simController) , _networkController(networkController) , _browserWindow(browserWindow) + , _loginDialog(loginDialog) , _viewport(viewport) + , _genomeEditorWindow(genomeEditorWindow) { auto& settings = GlobalSettings::getInstance(); _simName = settings.getStringState("dialogs.upload.simulation name", ""); @@ -38,13 +55,26 @@ _UploadSimulationDialog::~_UploadSimulationDialog() settings.setStringState("dialogs.upload.simulation description", _simDescription); } +void _UploadSimulationDialog::open(DataType dataType) +{ + if (_networkController->getLoggedInUserName()) { + changeTitle("Upload " + BrowserDataTypeToLowerString.at(dataType)); + _dataType = dataType; + _AlienDialog::open(); + } else { + _loginDialog->open(); + } +} + void _UploadSimulationDialog::processIntern() { AlienImGui::Text("Data privacy policy"); - AlienImGui::HelpMarker("The simulation file, name and description are stored on the server. It cannot be guaranteed that the data will not be deleted."); + AlienImGui::HelpMarker( + "The " + BrowserDataTypeToLowerString.at(_dataType) + + " file, name and description are stored on the server. It cannot be guaranteed that the data will not be deleted."); AlienImGui::Separator(); - AlienImGui::InputText(AlienImGui::InputTextParameters().hint("Simulation name").textWidth(0), _simName); + AlienImGui::InputText(AlienImGui::InputTextParameters().hint(BrowserDataTypeToUpperString.at(_dataType) + " name").textWidth(0), _simName); AlienImGui::Separator(); AlienImGui::InputTextMultiline( AlienImGui::InputTextMultilineParameters() @@ -82,28 +112,47 @@ void _UploadSimulationDialog::onUpload() printOverlayMessage("Uploading ..."); delayedExecution([=, this] { - DeserializedSimulation deserializedSim; - deserializedSim.auxiliaryData.timestep = static_cast(_simController->getCurrentTimestep()); - deserializedSim.auxiliaryData.zoom = _viewport->getZoomFactor(); - deserializedSim.auxiliaryData.center = _viewport->getCenterInWorldPos(); - deserializedSim.auxiliaryData.generalSettings = _simController->getGeneralSettings(); - deserializedSim.auxiliaryData.simulationParameters = _simController->getSimulationParameters(); - deserializedSim.mainData = _simController->getClusteredSimulationData(); - - SerializedSimulation serializedSim; - if (!Serializer::serializeSimulationToStrings(serializedSim, deserializedSim)) { - MessageDialog::getInstance().information("Save simulation", "The simulation could not be uploaded."); - return; + std::string mainData; + std::string auxiliaryData; + IntVector2D size; + int numObjects = 0; + + if (_dataType == DataType_Simulation) { + DeserializedSimulation deserializedSim; + deserializedSim.auxiliaryData.timestep = static_cast(_simController->getCurrentTimestep()); + deserializedSim.auxiliaryData.zoom = _viewport->getZoomFactor(); + deserializedSim.auxiliaryData.center = _viewport->getCenterInWorldPos(); + deserializedSim.auxiliaryData.generalSettings = _simController->getGeneralSettings(); + deserializedSim.auxiliaryData.simulationParameters = _simController->getSimulationParameters(); + deserializedSim.mainData = _simController->getClusteredSimulationData(); + + SerializedSimulation serializedSim; + if (!Serializer::serializeSimulationToStrings(serializedSim, deserializedSim)) { + MessageDialog::getInstance().information( + "Upload simulation", "The simulation could not be serialized for uploading."); + return; + } + mainData = serializedSim.mainData; + auxiliaryData = serializedSim.auxiliaryData; + size = {deserializedSim.auxiliaryData.generalSettings.worldSizeX, deserializedSim.auxiliaryData.generalSettings.worldSizeY}; + numObjects = deserializedSim.mainData.getNumberOfCellAndParticles(); + } else { + auto genome = _genomeEditorWindow->getCurrentGenome(); + if (genome.cells.empty()) { + showMessage("Upload genome", "The is no valid genome in the genome editor selected."); + return; + } + auto genomeData = GenomeDescriptionConverter::convertDescriptionToBytes(genome); + numObjects = GenomeDescriptionConverter::getNumNodesRecursively(genomeData); + + if (!Serializer::serializeGenomeToString(mainData, genomeData)) { + showMessage("Upload genome", "The genome could not be serialized for uploading."); + return; + } } - if (!_networkController->uploadSimulation( - _simName, - _simDescription, - {deserializedSim.auxiliaryData.generalSettings.worldSizeX, deserializedSim.auxiliaryData.generalSettings.worldSizeY}, - deserializedSim.mainData.getNumberOfCellAndParticles(), - serializedSim.mainData, - serializedSim.auxiliaryData)) { - MessageDialog::getInstance().information("Error", "Failed to upload simulation."); + if (!_networkController->uploadSimulation(_simName, _simDescription, size, numObjects, mainData, auxiliaryData, _dataType)) { + showMessage("Error", "Failed to upload " + BrowserDataTypeToLowerString.at(_dataType) + "."); return; } _browserWindow->onRefresh(); diff --git a/source/Gui/UploadSimulationDialog.h b/source/Gui/UploadSimulationDialog.h index 93ef7b8a7..4f18b98a2 100644 --- a/source/Gui/UploadSimulationDialog.h +++ b/source/Gui/UploadSimulationDialog.h @@ -9,25 +9,33 @@ class _UploadSimulationDialog : public _AlienDialog public: _UploadSimulationDialog( BrowserWindow const& browserWindow, + LoginDialog const& loginDialog, SimulationController const& simController, NetworkController const& networkController, - Viewport const& viewport); + Viewport const& viewport, + GenomeEditorWindow const& genomeEditorWindow); ~_UploadSimulationDialog(); + void open(DataType dataType); + private: void processIntern(); void openIntern(); void onUpload(); - BrowserWindow _browserWindow; - SimulationController _simController; - Viewport _viewport; - NetworkController _networkController; - std::string _simName; std::string _simDescription; std::string _origSimName; std::string _origSimDescription; + + DataType _dataType = DataType_Simulation; + + BrowserWindow _browserWindow; + LoginDialog _loginDialog; + SimulationController _simController; + Viewport _viewport; + NetworkController _networkController; + GenomeEditorWindow _genomeEditorWindow; }; \ No newline at end of file diff --git a/source/Server/getversionedsimulationlist.php b/source/Server/getversionedsimulationlist.php index e4275e0fd..9b390b8d9 100644 --- a/source/Server/getversionedsimulationlist.php +++ b/source/Server/getversionedsimulationlist.php @@ -32,7 +32,8 @@ sim.TIMESTAMP as timestamp, sim.NUM_DOWNLOADS as numDownloads, sim.SIZE as contentSize, - sim.FROM_RELEASE as fromRelease + sim.FROM_RELEASE as fromRelease, + sim.TYPE as type FROM simulation sim LEFT JOIN user u @@ -66,7 +67,8 @@ "likes" => $totalLikes, "likesByType" => $likesByType, "numDownloads" => (int)$obj->numDownloads, - "fromRelease" => (int)$obj->fromRelease + "fromRelease" => (int)$obj->fromRelease, + "type" => is_null($obj->type) ? 0 : $obj->type ]; } diff --git a/source/Server/uploadsimulation.php b/source/Server/uploadsimulation.php index 47bb891fe..cd1111d87 100644 --- a/source/Server/uploadsimulation.php +++ b/source/Server/uploadsimulation.php @@ -31,11 +31,13 @@ $settings = $_POST['settings']; $symbolMap = $_POST['symbolMap']; $size = strlen($content); + $type = array_key_exists("type", $_POST) ? $_POST['type'] : 0; + if ($db->query("INSERT INTO - simulation (ID, USER_ID, NAME, WIDTH, HEIGHT, PARTICLES, VERSION, DESCRIPTION, CONTENT, SETTINGS, SYMBOL_MAP, PICTURE, TIMESTAMP, SIZE) + simulation (ID, USER_ID, NAME, WIDTH, HEIGHT, PARTICLES, VERSION, DESCRIPTION, CONTENT, SETTINGS, SYMBOL_MAP, PICTURE, TIMESTAMP, SIZE, TYPE) VALUES (NULL, {$obj->id}, '" . addslashes($simName) . "', $width, $height, $particles, '" . addslashes($version) . "', '" - . addslashes($simDesc) . "', '" . addslashes($content) . "', '" . addslashes($settings) . "', '" . addslashes($symbolMap) . "', 'a', NULL, $size)")) { + . addslashes($simDesc) . "', '" . addslashes($content) . "', '" . addslashes($settings) . "', '" . addslashes($symbolMap) . "', 'a', NULL, $size, $type)")) { $success = true; }