Files
OpenSpace/src/navigation/navigationstate.cpp
T
Emma Broman 1fbd5e827d Utilize new docs functionality in API scripts that are defined in Lua files (#3505)
* Fix a reference to a deprecated and renamed function in navigationstate docs

* Review documentation for scripts defined in .lua files

And utilize new markdown functionality

* Update renderable box grid size specs

So that it can have a size less than 1, and throws a specification error when the size is given as negative

* Fix some issues with the gaia filtering scripts

* Try adding param documentation to the lua script documentation

It works!

* Utiliize mroe markdown features in the updated documentations

* Add function parameter info and make small updated to gaia filtering functions

* Replace a long parameter info in description with `param` description

* Update usage examples

* Add example of how to create debug axes for the current SGN
2025-02-11 08:59:33 +01:00

307 lines
11 KiB
C++

/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2025 *
* *
* 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 <openspace/camera/camerapose.h>
#include <ghoul/logging/logmanager.h>
#include <openspace/navigation/navigationstate.h>
#include <openspace/query/query.h>
#include <openspace/scene/scenegraphnode.h>
#include <openspace/util/spicemanager.h>
namespace {
constexpr std::string_view _loggerCat = "NavigationState";
constexpr double Epsilon = 1E-7;
// A NavigationState is an object describing an exact camera position and rotation,
// in a certain reference frame (per default, the one of the specified Anchor node).
// It can be used to set the same camera position at a later point in time, or
// navigating to a specific camera position using the pathnavigation system.
//
// The camera rotation is specified using Euler angles, in radians. It is also
// possible to specify a node to be used as Aim, but note that this will not affect
// the actual camera position or view direction.
//
// To get the current navigation state of the camera, use the
// `openspace.navigation.navigationState()` function in the Scripting API.
//
// Note that when loading a NavigationState, the visuals may be different depending
// on what the simulation timestamp is, as the relative positions of objects in the
// scene may have changed. The get the exact same visuals as when the NavigationState
// was saved you need to also set the simulation time to correpsond to the timestamp.
struct [[codegen::Dictionary(NavigationState)]] Parameters {
// The identifier of the anchor node
std::string anchor;
// The identifier of the aim node, if used
std::optional<std::string> aim;
// The identifier of the scene graph node to use as reference frame. If not
// specified, this will be the same as the anchor
std::optional<std::string> referenceFrame;
// The position of the camera relative to the anchor node, expressed in meters in
// the specified reference frame
glm::dvec3 position;
// The up vector expressed in the coordinate system of the reference frame
std::optional<glm::dvec3> up;
// The yaw angle in radians. Positive angle means yawing camera to the right
std::optional<double> yaw;
// The pitch angle in radians. Positive angle means pitching camera upwards
std::optional<double> pitch;
// The timestamp for when the navigation state was captured or is valid. Specified
// either as seconds past the J2000 epoch, or as a date string in ISO 8601 format:
// 'YYYY MM DD HH:mm:ss.xxx'
std::optional<std::variant<double, std::string>> timestamp;
};
#include "navigationstate_codegen.cpp"
} // namespace
namespace openspace::interaction {
NavigationState::NavigationState(const ghoul::Dictionary& dictionary) {
const Parameters p = codegen::bake<Parameters>(dictionary);
anchor = p.anchor;
position = p.position;
referenceFrame = p.referenceFrame.value_or(anchor);
aim = p.aim.value_or(aim);
up = p.up;
yaw = p.yaw.value_or(yaw);
pitch = p.pitch.value_or(pitch);
if (p.timestamp.has_value()) {
if (std::holds_alternative<double>(*p.timestamp)) {
timestamp = std::get<double>(*p.timestamp);
}
else {
timestamp = SpiceManager::ref().ephemerisTimeFromDate(
std::get<std::string>(*p.timestamp)
);
}
}
}
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>();
}
if (auto it = json.find("timestamp"); it != json.end()) {
if (it->is_string()) {
timestamp = SpiceManager::ref().ephemerisTimeFromDate(
it->get<std::string>()
);
}
else {
timestamp = it->get<double>();
}
}
}
NavigationState::NavigationState(std::string anchor_, std::string aim_,
std::string referenceFrame_, glm::dvec3 position_,
std::optional<glm::dvec3> up_,
double yaw_, double pitch_,
std::optional<double> timestamp_)
: anchor(std::move(anchor_))
, aim(std::move(aim_))
, referenceFrame(std::move(referenceFrame_))
, position(std::move(position_))
, up(std::move(up_))
, yaw(yaw_)
, pitch(pitch_)
, timestamp(timestamp_)
{}
CameraPose NavigationState::cameraPose() const {
const SceneGraphNode* referenceFrameNode = sceneGraphNode(referenceFrame);
const SceneGraphNode* anchorNode = sceneGraphNode(anchor);
if (!anchorNode) {
LERROR(std::format(
"Could not find scene graph node '{}' used as anchor", anchor
));
return CameraPose();
}
if (!referenceFrameNode) {
LERROR(std::format(
"Could not find scene graph node '{}' used as reference frame",
referenceFrame
));
return CameraPose();
}
CameraPose resultingPose;
const glm::dmat3 referenceFrameTransform = referenceFrameNode->modelTransform();
// @TODO (2023-05-16, emmbr) This computation is wrong and has to be fixed! Only
// works if the reference frame is also the anchor node. I remember that fixing it
// was not as easy as using referenceFrameNode instead of anchor node though..
resultingPose.position = anchorNode->worldPosition() +
referenceFrameTransform * glm::dvec3(position);
const glm::dvec3 upVector = up.has_value() ?
glm::normalize(referenceFrameTransform * *up) :
glm::dvec3(0.0, 1.0, 0.0);
// Construct vectors of a "neutral" view, i.e. when the anchor is centered in view
const glm::dvec3 neutralView =
glm::normalize(anchorNode->worldPosition() - resultingPose.position);
const glm::dquat neutralCameraRotation = glm::inverse(glm::quat_cast(
glm::lookAt(glm::dvec3(0.0), neutralView, upVector)
));
const glm::dquat pitchRotation = glm::angleAxis(pitch, glm::dvec3(1.0, 0.0, 0.0));
const glm::dquat yawRotation = glm::angleAxis(yaw, glm::dvec3(0.0, -1.0, 0.0));
resultingPose.rotation = neutralCameraRotation * yawRotation * pitchRotation;
return resultingPose;
}
ghoul::Dictionary NavigationState::dictionary() const {
ghoul::Dictionary cameraDict;
cameraDict.setValue("Position", position);
cameraDict.setValue("Anchor", anchor);
if (anchor != referenceFrame) {
cameraDict.setValue("ReferenceFrame", referenceFrame);
}
if (!aim.empty()) {
cameraDict.setValue("Aim", aim);
}
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);
}
if (timestamp.has_value()) {
cameraDict.setValue(
"Timestamp",
SpiceManager::ref().dateFromEphemerisTime(
*timestamp,
"YYYY MON DD HR:MN:SC"
)
);
}
return cameraDict;
}
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;
}
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;
}
if (timestamp.has_value()) {
result["timestamp"] = SpiceManager::ref().dateFromEphemerisTime(
*timestamp,
"YYYY MON DD HR:MN:SC"
);
}
return result;
}
documentation::Documentation NavigationState::Documentation() {
return codegen::doc<Parameters>("core_navigation_state");
}
} // namespace openspace::interaction