Files
OpenSpace/modules/imgui/src/guiglobebrowsingcomponent.cpp
2021-03-01 13:24:28 +01:00

373 lines
12 KiB
C++

/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2021 *
* *
* 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 <modules/imgui/include/guiglobebrowsingcomponent.h>
#include <modules/globebrowsing/globebrowsingmodule.h>
#include <modules/globebrowsing/src/renderableglobe.h>
#include <modules/imgui/include/imgui_include.h>
#include <openspace/engine/globals.h>
#include <openspace/engine/moduleengine.h>
#include <openspace/interaction/navigationhandler.h>
#include <openspace/interaction/orbitalnavigator.h>
#include <openspace/rendering/renderable.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/scene/scene.h>
#include <openspace/scripting/scriptengine.h>
#include <ghoul/fmt.h>
#include <ghoul/logging/logmanager.h>
#include <numeric>
namespace {
const ImVec2 WindowSize = ImVec2(350, 500);
} // namespace
namespace openspace::gui {
GuiGlobeBrowsingComponent::GuiGlobeBrowsingComponent()
: GuiPropertyComponent("GlobeBrowsing", "Globe Browsing")
{}
void GuiGlobeBrowsingComponent::render() {
GlobeBrowsingModule* module = global::moduleEngine->module<GlobeBrowsingModule>();
using UrlInfo = GlobeBrowsingModule::UrlInfo;
using Capabilities = GlobeBrowsingModule::Capabilities;
using Layer = GlobeBrowsingModule::Layer;
ImGui::SetNextWindowCollapsed(_isCollapsed);
bool e = _isEnabled;
ImGui::SetNextWindowSize(WindowSize, ImGuiCond_FirstUseEver);
ImGui::SetNextWindowBgAlpha(0.5f);
ImGui::Begin("Globe Browsing", &e);
_isEnabled = e;
_isCollapsed = ImGui::IsWindowCollapsed();
defer { ImGui::End(); };
// Render the list of planets
std::vector<SceneGraphNode*> nodes =
global::renderEngine->scene()->allSceneGraphNodes();
nodes.erase(
std::remove_if(
nodes.begin(),
nodes.end(),
[](SceneGraphNode* n) {
using namespace globebrowsing;
const Renderable* r = n->renderable();
const RenderableGlobe* rg = dynamic_cast<const RenderableGlobe*>(r);
return rg == nullptr;
}
),
nodes.end()
);
std::sort(
nodes.begin(),
nodes.end(),
[module](SceneGraphNode* lhs, SceneGraphNode* rhs) {
const bool lhsHasUrl = module->hasUrlInfo(lhs->identifier());
const bool rhsHasUrl = module->hasUrlInfo(rhs->identifier());
if (lhsHasUrl && !rhsHasUrl) {
return true;
}
if (!lhsHasUrl && rhsHasUrl) {
return false;
}
return lhs->guiName() < rhs->guiName();
}
);
auto firstWithoutUrl = std::find_if(
nodes.begin(),
nodes.end(),
[module](SceneGraphNode* n) {
return !module->hasUrlInfo(n->identifier());
}
);
nodes.insert(firstWithoutUrl, nullptr);
std::string nodeNames;
for (SceneGraphNode* n : nodes) {
// Add separator between nodes with URL and nodes without
if (n) {
nodeNames += n->identifier() + '\0';
}
else {
nodeNames += std::string("===== =====") + '\0';
}
}
int iNode = -1;
if (_currentNode.empty()) {
// We haven't selected a node yet, so first instinct is to use the current focus
// node
// Check if the focus node is a RenderableGlobe
const SceneGraphNode* const focus =
global::navigationHandler->orbitalNavigator().anchorNode();
const auto it = std::find(nodes.cbegin(), nodes.cend(), focus);
if (it != nodes.end()) {
_currentNode = focus->identifier();
iNode = static_cast<int>(std::distance(nodes.cbegin(), it));
}
}
else {
const auto it = std::find_if(
nodes.cbegin(),
nodes.cend(),
[this](SceneGraphNode* lhs) {
return lhs && (lhs->identifier() == _currentNode);
}
);
iNode = static_cast<int>(std::distance(nodes.cbegin(), it));
}
bool isNodeChanged = ImGui::Combo("Globe", &iNode, nodeNames.c_str());
ImGui::SameLine();
bool selectFocusNode = ImGui::Button("From Focus");
if (selectFocusNode) {
const SceneGraphNode* const focus =
global::navigationHandler->orbitalNavigator().anchorNode();
const auto it = std::find(nodes.cbegin(), nodes.cend(), focus);
if (it != nodes.end()) {
_currentNode = focus->identifier();
iNode = static_cast<int>(std::distance(nodes.cbegin(), it));
isNodeChanged = true;
}
}
if (iNode == -1) {
// This should only occur if the Focusnode is not a RenderableGlobe
// or if there are no nodes
return;
}
if (!nodes[iNode]) {
// The user selected the separator
return;
}
_currentNode = nodes[iNode]->identifier();
if (isNodeChanged) {
_currentServer = "";
}
ImGui::Separator();
// Render the list of servers for the planet
std::vector<UrlInfo> urlInfo = module->urlInfo(_currentNode);
std::string serverList = std::accumulate(
urlInfo.cbegin(),
urlInfo.cend(),
std::string(),
[](const std::string& lhs, const UrlInfo& i) {
return lhs + i.name + ": (" + i.url + ")" + '\0';
}
);
int iServer = -1;
if (_currentServer.empty()) {
// We haven't selected a server yet, so first instinct is to just use the first
if (!urlInfo.empty()) {
_currentServer = urlInfo[0].name;
iServer = 0;
}
}
else {
const auto it = std::find_if(
urlInfo.cbegin(),
urlInfo.cend(),
[this](const UrlInfo& i) {
return i.name == _currentServer;
}
);
if (it != urlInfo.end()) {
iServer = static_cast<int>(std::distance(urlInfo.cbegin(), it));
}
}
ImGui::Combo("Server", &iServer, serverList.c_str());
ImGui::SameLine(0.f, 60.f);
if (ImGui::Button("Add Server")) {
ImGui::OpenPopup("globebrowsing_add_server");
}
if (ImGui::BeginPopup("globebrowsing_add_server")) {
constexpr const int InputBufferSize = 512;
static char NameInputBuffer[InputBufferSize];
ImGui::InputText("Server Name", NameInputBuffer, InputBufferSize);
static char UrlInputBuffer[InputBufferSize];
ImGui::InputText("Server URL", UrlInputBuffer, InputBufferSize);
const bool addServer = ImGui::Button("Add Server");
if (addServer && (!_currentNode.empty())) {
module->loadWMSCapabilities(
std::string(NameInputBuffer),
_currentNode,
std::string(UrlInputBuffer)
);
std::memset(NameInputBuffer, 0, InputBufferSize * sizeof(char));
std::memset(UrlInputBuffer, 0, InputBufferSize * sizeof(char));
urlInfo = module->urlInfo(_currentNode);
_currentServer = urlInfo.back().name;
--iServer;
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
ImGui::SameLine(0.f, 20.f);
const bool deleteServer = ImGui::Button("Delete Server");
if (deleteServer) {
module->removeWMSServer(_currentServer);
_currentServer = "";
iServer = -1;
}
if (iServer < 0 || urlInfo.empty()) {
// There are no server so we have to bail
return;
}
_currentServer = urlInfo[iServer].name;
ImGui::Separator();
Capabilities cap = module->capabilities(_currentServer);
if (cap.empty()) {
LWARNINGC("GlobeBrowsing", fmt::format("Unknown server: '{}'", _currentServer));
}
ImGui::Columns(6, nullptr, false);
const float width = ImGui::GetWindowWidth();
constexpr const float ButtonWidth = 60.f;
ImGui::SetColumnOffset(5, width - 1.5f * ButtonWidth);
ImGui::SetColumnOffset(4, width - 2.5f * ButtonWidth);
ImGui::SetColumnOffset(3, width - 3.5f * ButtonWidth);
ImGui::SetColumnOffset(2, width - 4.5f * ButtonWidth);
ImGui::SetColumnOffset(1, width - 5.5f * ButtonWidth);
ImGui::SetColumnOffset(0, 0);
ImGui::Text("%s", "Layer name");
ImGui::NextColumn();
ImGui::Text("%s", "Add as ...");
ImGui::NextColumn();
ImGui::NextColumn();
ImGui::NextColumn();
ImGui::NextColumn();
ImGui::NextColumn();
ImGui::Separator();
for (const Layer& l : cap) {
if (l.name.empty() || l.url.empty()) {
continue;
}
ImGui::PushID(l.url.c_str());
ImGui::Text("%s", l.name.c_str());
ImGui::NextColumn();
const bool addColor = ImGui::Button("Color", { ButtonWidth, 25.f });
ImGui::NextColumn();
const bool addNight = ImGui::Button("Night", { ButtonWidth, 25.f });
ImGui::NextColumn();
const bool addOverlay = ImGui::Button("Overlay", { ButtonWidth, 25.f });
ImGui::NextColumn();
const bool addHeight = ImGui::Button("Height", { ButtonWidth, 25.f });
ImGui::NextColumn();
const bool addWaterMask = ImGui::Button("Water", { ButtonWidth, 25.f });
ImGui::NextColumn();
auto addFunc = [n = _currentNode, &l](const std::string& type) {
std::string layerName = l.name;
std::replace(layerName.begin(), layerName.end(), '.', '-');
layerName.erase(
std::remove(layerName.begin(), layerName.end(), ' '),
layerName.end()
);
global::scriptEngine->queueScript(
fmt::format(
"openspace.globebrowsing.addLayer(\
'{}', \
'{}', \
{{ \
Identifier = '{}',\
Name = '{}',\
FilePath = '{}',\
Enabled = true\
}}\
);",
n,
type,
layerName,
l.name,
l.url
),
scripting::ScriptEngine::RemoteScripting::Yes
);
};
if (addColor) {
addFunc("ColorLayers");
}
if (addNight) {
addFunc("NightLayers");
}
if (addOverlay) {
addFunc("Overlays");
}
if (addHeight) {
addFunc("HeightLayers");
}
if (addWaterMask) {
addFunc("WaterMasks");
}
ImGui::PopID();
}
ImGui::Columns(1);
}
} // namespace openspace::gui