mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-05-06 19:39:56 -05:00
NavigationState handling (#3023)
* Change the navigation state format from Lua to JSON. Add the ability to set the navigation state in the profile editor based on a file
This commit is contained in:
@@ -25,15 +25,18 @@
|
||||
#include "profile/cameradialog.h"
|
||||
|
||||
#include "profile/line.h"
|
||||
#include <openspace/navigation/navigationstate.h>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDoubleValidator>
|
||||
#include <QFileDialog>
|
||||
#include <QFrame>
|
||||
#include <QGridLayout>
|
||||
#include <QKeyEvent>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QTabWidget>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QPushButton>
|
||||
#include <QTabWidget>
|
||||
|
||||
namespace {
|
||||
constexpr int CameraTypeNode = 0;
|
||||
@@ -345,6 +348,45 @@ QWidget* CameraDialog::createNavStateWidget() {
|
||||
mainLayout->addWidget(box);
|
||||
}
|
||||
|
||||
QPushButton* loadFile = new QPushButton("Load state from file");
|
||||
loadFile->setIcon(loadFile->style()->standardIcon(QStyle::SP_FileIcon));
|
||||
connect(
|
||||
loadFile, &QPushButton::clicked,
|
||||
[this]() {
|
||||
QString file = QFileDialog::getOpenFileName(
|
||||
this,
|
||||
"Select navigate state file"
|
||||
);
|
||||
|
||||
std::ifstream f(file.toStdString());
|
||||
std::string contents = std::string(
|
||||
(std::istreambuf_iterator<char>(f)),
|
||||
std::istreambuf_iterator<char>()
|
||||
);
|
||||
nlohmann::json json = nlohmann::json::parse(contents);
|
||||
|
||||
using namespace openspace::interaction;
|
||||
NavigationState state = NavigationState(json);
|
||||
|
||||
_navState.anchor->setText(QString::fromStdString(state.anchor));
|
||||
_navState.aim->setText(QString::fromStdString(state.aim));
|
||||
_navState.refFrame->setText(QString::fromStdString(state.referenceFrame));
|
||||
_navState.positionX->setText(QString::number(state.position.x));
|
||||
_navState.positionY->setText(QString::number(state.position.y));
|
||||
_navState.positionZ->setText(QString::number(state.position.z));
|
||||
|
||||
if (state.up.has_value()) {
|
||||
_navState.upX->setText(QString::number(state.up->x));
|
||||
_navState.upY->setText(QString::number(state.up->y));
|
||||
_navState.upZ->setText(QString::number(state.up->z));
|
||||
}
|
||||
|
||||
_navState.yaw->setText(QString::number(state.yaw));
|
||||
_navState.pitch->setText(QString::number(state.pitch));
|
||||
}
|
||||
);
|
||||
mainLayout->addWidget(loadFile);
|
||||
|
||||
return tab;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#define __OPENSPACE_CORE___NAVIGATIONSTATE___H__
|
||||
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <openspace/json.h>
|
||||
#include <optional>
|
||||
|
||||
namespace openspace {
|
||||
@@ -37,12 +38,14 @@ namespace openspace::interaction {
|
||||
struct NavigationState {
|
||||
NavigationState() = default;
|
||||
explicit NavigationState(const ghoul::Dictionary& dictionary);
|
||||
explicit NavigationState(const nlohmann::json& json);
|
||||
NavigationState(std::string anchor, std::string aim, std::string referenceFrame,
|
||||
glm::dvec3 position, std::optional<glm::dvec3> up = std::nullopt,
|
||||
double yaw = 0.0, double pitch = 0.0);
|
||||
|
||||
CameraPose cameraPose() const;
|
||||
ghoul::Dictionary dictionary() const;
|
||||
nlohmann::json toJson() const;
|
||||
static documentation::Documentation Documentation();
|
||||
|
||||
std::string anchor;
|
||||
|
||||
@@ -557,6 +557,10 @@ void NavigationHandler::saveNavigationState(const std::filesystem::path& filepat
|
||||
}
|
||||
|
||||
std::filesystem::path absolutePath = absPath(filepath);
|
||||
if (!absolutePath.has_extension()) {
|
||||
// Adding the .navstate extension to the filepath if it came without one
|
||||
absolutePath.replace_extension(".navstate");
|
||||
}
|
||||
LINFO(fmt::format("Saving camera position: {}", absolutePath));
|
||||
|
||||
std::ofstream ofs(absolutePath);
|
||||
@@ -567,7 +571,7 @@ void NavigationHandler::saveNavigationState(const std::filesystem::path& filepat
|
||||
));
|
||||
}
|
||||
|
||||
ofs << "return " << ghoul::formatLua(state.dictionary());
|
||||
ofs << state.toJson().dump(2);
|
||||
}
|
||||
|
||||
void NavigationHandler::loadNavigationState(const std::string& filepath) {
|
||||
@@ -578,22 +582,15 @@ void NavigationHandler::loadNavigationState(const std::string& filepath) {
|
||||
throw ghoul::FileNotFoundError(absolutePath.string(), "NavigationState");
|
||||
}
|
||||
|
||||
ghoul::Dictionary navigationStateDictionary;
|
||||
try {
|
||||
ghoul::lua::loadDictionaryFromFile(
|
||||
absolutePath.string(),
|
||||
navigationStateDictionary
|
||||
);
|
||||
openspace::documentation::testSpecificationAndThrow(
|
||||
NavigationState::Documentation(),
|
||||
navigationStateDictionary,
|
||||
"NavigationState"
|
||||
);
|
||||
setNavigationStateNextFrame(NavigationState(navigationStateDictionary));
|
||||
}
|
||||
catch (ghoul::RuntimeError& e) {
|
||||
LERROR(fmt::format("Unable to set camera position: {}", e.message));
|
||||
}
|
||||
std::ifstream f(filepath);
|
||||
std::string contents = std::string(
|
||||
std::istreambuf_iterator<char>(f),
|
||||
std::istreambuf_iterator<char>()
|
||||
);
|
||||
nlohmann::json json = nlohmann::json::parse(contents);
|
||||
|
||||
NavigationState state = NavigationState(json);
|
||||
setNavigationStateNextFrame(state);
|
||||
}
|
||||
|
||||
std::vector<std::string> NavigationHandler::listAllJoysticks() const {
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current navigation state as a lua table. The optional argument is the scene
|
||||
* Return the current navigation state as a Lua table. The optional argument is the scene
|
||||
* graph node to use as reference frame. By default, the reference frame will picked based
|
||||
* on whether the orbital navigator is currently following the anchor node rotation. If it
|
||||
* is, the anchor will be chosen as reference frame. If not, the reference frame will be
|
||||
|
||||
@@ -89,11 +89,42 @@ NavigationState::NavigationState(const ghoul::Dictionary& dictionary) {
|
||||
referenceFrame = p.referenceFrame.value_or(anchor);
|
||||
aim = p.aim.value_or(aim);
|
||||
|
||||
if (p.up.has_value()) {
|
||||
up = *p.up;
|
||||
up = p.up;
|
||||
yaw = p.yaw.value_or(yaw);
|
||||
pitch = p.pitch.value_or(pitch);
|
||||
}
|
||||
|
||||
yaw = p.yaw.value_or(yaw);
|
||||
pitch = p.pitch.value_or(pitch);
|
||||
NavigationState::NavigationState(const nlohmann::json& json) {
|
||||
position.x = json["position"]["x"].get<double>();
|
||||
position.y = json["position"]["y"].get<double>();
|
||||
position.z = json["position"]["z"].get<double>();
|
||||
|
||||
anchor = json["anchor"];
|
||||
|
||||
if (auto it = json.find("referenceframe"); it != json.end()) {
|
||||
referenceFrame = it->get<std::string>();
|
||||
}
|
||||
else {
|
||||
referenceFrame = anchor;
|
||||
}
|
||||
|
||||
if (auto it = json.find("aim"); it != json.end()) {
|
||||
aim = it->get<std::string>();
|
||||
}
|
||||
|
||||
if (auto it = json.find("up"); it != json.end()) {
|
||||
up = glm::dvec3();
|
||||
up->x = it->at("x").get<double>();
|
||||
up->y = it->at("y").get<double>();
|
||||
up->z = it->at("z").get<double>();
|
||||
}
|
||||
|
||||
if (auto it = json.find("yaw"); it != json.end()) {
|
||||
yaw = it->get<double>();
|
||||
}
|
||||
|
||||
if (auto it = json.find("pitch"); it != json.end()) {
|
||||
pitch = it->get<double>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,16 +205,56 @@ ghoul::Dictionary NavigationState::dictionary() const {
|
||||
}
|
||||
if (up.has_value()) {
|
||||
cameraDict.setValue("Up", *up);
|
||||
}
|
||||
if (std::abs(yaw) > Epsilon) {
|
||||
cameraDict.setValue("Yaw", yaw);
|
||||
}
|
||||
if (std::abs(pitch) > Epsilon) {
|
||||
cameraDict.setValue("Pitch", pitch);
|
||||
}
|
||||
return cameraDict;
|
||||
}
|
||||
|
||||
if (std::abs(yaw) > Epsilon) {
|
||||
cameraDict.setValue("Yaw", yaw);
|
||||
}
|
||||
if (std::abs(pitch) > Epsilon) {
|
||||
cameraDict.setValue("Pitch", pitch);
|
||||
}
|
||||
nlohmann::json NavigationState::toJson() const {
|
||||
nlohmann::json result = nlohmann::json::object();
|
||||
|
||||
// Obligatory version number
|
||||
result["version"] = 1;
|
||||
|
||||
{
|
||||
nlohmann::json posObj = nlohmann::json::object();
|
||||
posObj["x"] = position.x;
|
||||
posObj["y"] = position.y;
|
||||
posObj["z"] = position.z;
|
||||
result["position"] = posObj;
|
||||
}
|
||||
|
||||
return cameraDict;
|
||||
result["anchor"] = anchor;
|
||||
|
||||
if (anchor != referenceFrame) {
|
||||
result["referenceframe"] = referenceFrame;
|
||||
}
|
||||
|
||||
if (!aim.empty()) {
|
||||
result["aim"] = aim;
|
||||
}
|
||||
|
||||
if (up.has_value()) {
|
||||
nlohmann::json upObj = nlohmann::json::object();
|
||||
upObj["x"] = up->x;
|
||||
upObj["y"] = up->y;
|
||||
upObj["z"] = up->z;
|
||||
result["up"] = upObj;
|
||||
}
|
||||
|
||||
if (std::abs(yaw) > Epsilon) {
|
||||
result["yaw"] = yaw;
|
||||
}
|
||||
if (std::abs(pitch) > Epsilon) {
|
||||
result["pitch"] = pitch;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
documentation::Documentation NavigationState::Documentation() {
|
||||
|
||||
Reference in New Issue
Block a user