refactor instructions

This commit is contained in:
Emma Broman
2020-04-06 16:25:20 +02:00
parent 387c2adbee
commit df5b518f07
7 changed files with 194 additions and 248 deletions
+11 -106
View File
@@ -102,7 +102,7 @@ const SceneGraphNode* AutoNavigationHandler::anchor() const {
}
bool AutoNavigationHandler::hasFinished() const {
int lastIndex = (int)_pathSegments.size() - 1;
unsigned int lastIndex = (unsigned int)_pathSegments.size() - 1;
return _currentSegmentIndex > lastIndex;
}
@@ -163,11 +163,13 @@ void AutoNavigationHandler::createPath(PathSpecification& spec) {
bool success = true;
for (int i = 0; i < spec.instructions()->size(); i++) {
const Instruction& ins = spec.instructions()->at(i);
success = handleInstruction(ins, i);
if (!success)
break;
const Instruction* ins = spec.instruction(i);
if (ins) {
// TODO: allow for a list of waypoints
std::vector<Waypoint> waypoints = ins->getWaypoints();
if (waypoints.size() > 0)
addSegment(waypoints[0], ins);
}
}
// Check if we have a specified start navigation state. If so, update first segment
@@ -291,112 +293,15 @@ Waypoint AutoNavigationHandler::lastWayPoint() {
return _pathSegments.empty() ? wayPointFromCamera() : _pathSegments.back()->end();
}
bool AutoNavigationHandler::handleInstruction(const Instruction& ins, int index) {
bool success = true;
switch (ins.type)
{
case InstructionType::TargetNode:
success = handleTargetNodeInstruction(ins);
break;
case InstructionType::NavigationState:
success = handleNavigationStateInstruction(ins);
break;
default:
LERROR("Non-implemented instruction type.");
success = false;
break;
}
if (!success) {
LERROR(fmt::format("Failed handling instruction number {}.", std::to_string(index + 1)));
return false;
}
return true;
}
bool AutoNavigationHandler::handleTargetNodeInstruction(const Instruction& ins) {
// Verify instruction type
TargetNodeInstructionProps* props =
dynamic_cast<TargetNodeInstructionProps*>(ins.props.get());
if (!props) {
LERROR("Could not handle target node instruction.");
return false;
}
// Compute end state
std::string& identifier = props->targetNode;
const SceneGraphNode* targetNode = sceneGraphNode(identifier);
if (!targetNode) {
LERROR(fmt::format("Could not find node '{}' to target", identifier));
return false;
}
glm::dvec3 targetPos;
if (props->position.has_value()) {
// note that the anchor and reference frame is our targetnode.
// The position in instruction is given is relative coordinates.
targetPos = targetNode->worldPosition() +
targetNode->worldRotationMatrix() * props->position.value();
}
else {
// TODO: Instead of this case, allow the curve to set its final position
glm::dvec3 nodePos = targetNode->worldPosition();
glm::dvec3 nodeToPrev = lastWayPoint().position() - nodePos;
// TODO: compute position in a more clever way
const double radius = WaypointNodeDetails::findValidBoundingSphere(targetNode, _minAllowedBoundingSphere);
const double defaultHeight = 2 * radius;
bool hasHeight = props->height.has_value();
double height = hasHeight ? props->height.value() : defaultHeight;
// move target position out from surface, along vector to camera
targetPos = nodePos + glm::normalize(nodeToPrev) * (radius + height);
}
glm::dmat4 lookAtMat = glm::lookAt(
targetPos,
targetNode->worldPosition(),
camera()->lookUpVectorWorldSpace()
);
glm::dquat targetRot = glm::normalize(glm::inverse(glm::quat_cast(lookAtMat)));
Waypoint endState{ targetPos, targetRot, identifier, _minAllowedBoundingSphere };
addSegment(endState, ins);
return true;
}
bool AutoNavigationHandler::handleNavigationStateInstruction(const Instruction& ins) {
// Verify instruction type
NavigationStateInstructionProps* props =
dynamic_cast<NavigationStateInstructionProps*>(ins.props.get());
if (!props) {
LERROR(fmt::format("Could not handle navigation state instruction."));
return false;
}
Waypoint endState{ props->navState , _minAllowedBoundingSphere };
addSegment(endState, ins);
return true;
}
void AutoNavigationHandler::addSegment(Waypoint& waypoint, const Instruction& ins){
void AutoNavigationHandler::addSegment(Waypoint& waypoint, const Instruction* ins){
// TODO: Improve how curve types are handled
const int curveType = _defaultCurveOption;
PathSegment segment = PathSegment(lastWayPoint(), waypoint, CurveType(curveType));
// TODO: handle duration better
if (ins.props->duration.has_value()) {
segment.setDuration(ins.props->duration.value());
if (ins->duration.has_value()) {
segment.setDuration(ins->duration.value());
}
_pathSegments.push_back(std::unique_ptr<PathSegment>(new PathSegment(segment)));
}
@@ -68,18 +68,11 @@ public:
private:
Waypoint wayPointFromCamera();
Waypoint lastWayPoint();
bool handleInstruction(const Instruction& ins, int index);
bool handleTargetNodeInstruction(const Instruction& ins);
bool handleNavigationStateInstruction(const Instruction& ins);
void addSegment(Waypoint& state, const Instruction& ins);
void addSegment(Waypoint& waypoint, const Instruction* ins);
// this list essentially represents the camera path
std::vector<std::unique_ptr<PathSegment>> _pathSegments;
bool _isPlaying = false;
unsigned int _currentSegmentIndex = 0;
@@ -83,7 +83,7 @@ namespace openspace::autonavigation::luascriptfunctions {
insDict.setValue("Duration", duration);
}
PathSpecification spec = PathSpecification(Instruction{insDict});
PathSpecification spec = PathSpecification(TargetNodeInstruction{insDict});
AutoNavigationModule* module = global::moduleEngine.module<AutoNavigationModule>();
AutoNavigationHandler& handler = module->AutoNavigationHandler();
+78 -86
View File
@@ -25,6 +25,11 @@
#include <modules/autonavigation/instruction.h>
#include <openspace/documentation/verifier.h>
#include <openspace/engine/globals.h>
#include <openspace/interaction/navigationhandler.h>
#include <openspace/scene/scenegraphnode.h>
#include <openspace/util/camera.h>
#include <openspace/query/query.h>
#include <ghoul/logging/logmanager.h>
namespace {
@@ -40,73 +45,30 @@ namespace {
namespace openspace::autonavigation {
documentation::Documentation TargetNodeInstructionDocumentation() {
using namespace documentation;
return {
"Target Node Instruction",
"target_node_instruction",
{
{
KeyTarget,
new StringVerifier,
Optional::No,
"The identifier of the target node."
},
{
KeyDuration,
new DoubleVerifier,
Optional::Yes,
"The desired duration for the camera movement."
},
{
KeyPosition,
new Vector3Verifier<double>,
Optional::Yes,
"The desired final position for the camera movement, given in model space."
},
{
KeyHeight,
new DoubleVerifier,
Optional::Yes,
"The desired height from surface for final position (meters). Will be ignored if a target position is set. "
},
}
};
}
InstructionProps::InstructionProps(const ghoul::Dictionary& dictionary) {
// TODO: validate against some documentation?
Instruction::Instruction(const ghoul::Dictionary& dictionary) {
if (dictionary.hasValue<double>(KeyDuration)) {
duration = dictionary.value<double>(KeyDuration);
}
// TODO: include info about pauses/stops
}
InstructionProps::~InstructionProps() {}
Instruction::~Instruction() {}
TargetNodeInstructionProps::TargetNodeInstructionProps(
const ghoul::Dictionary& dictionary) : InstructionProps(dictionary)
TargetNodeInstruction::TargetNodeInstruction(const ghoul::Dictionary& dictionary)
: Instruction(dictionary)
{
try {
documentation::testSpecificationAndThrow(
TargetNodeInstructionDocumentation(),
dictionary,
"Target Node Instruction"
);
}
catch (ghoul::RuntimeError& e) {
LERROR(fmt::format("Unable to generate target node instruction from dictionary. Does not match documentation: {}", e.message));
return;
}
if (!dictionary.hasValue<std::string>(KeyTarget)) {
throw ghoul::RuntimeError(
"A camera path instruction requires a target node, to go to or use as reference frame."
);
}
targetNode = dictionary.value<std::string>(KeyTarget);
nodeIdentifier = dictionary.value<std::string>(KeyTarget);
if (!sceneGraphNode(nodeIdentifier)) {
throw ghoul::RuntimeError(fmt::format("Could not find target node '{}'", nodeIdentifier));
}
if (dictionary.hasValue<glm::dvec3>(KeyPosition)) {
position = dictionary.value<glm::dvec3>(KeyPosition);
@@ -117,46 +79,76 @@ TargetNodeInstructionProps::TargetNodeInstructionProps(
}
}
NavigationStateInstructionProps::NavigationStateInstructionProps(
const ghoul::Dictionary& dictionary) : InstructionProps(dictionary)
std::vector<Waypoint> TargetNodeInstruction::getWaypoints() const {
const SceneGraphNode* targetNode = sceneGraphNode(nodeIdentifier);
if (!targetNode) {
LERROR(fmt::format("Could not find target node '{}'", nodeIdentifier));
return std::vector<Waypoint>();
}
glm::dvec3 targetPos;
if (position.has_value()) {
// note that the anchor and reference frame is our targetnode.
// The position in instruction is given is relative coordinates.
targetPos = targetNode->worldPosition() +
targetNode->worldRotationMatrix() * position.value();
}
else {
// TODO: Instead of this case, allow the curve to set its final position
//glm::dvec3 nodePos = targetNode->worldPosition();
//glm::dvec3 nodeToPrev = lastWayPoint().position() - nodePos;
//// TODO: compute position in a more clever way
//const double radius = WaypointNodeDetails::findValidBoundingSphere(targetNode, _minAllowedBoundingSphere);
//const double defaultHeight = 2 * radius;
//bool hasHeight = props->height.has_value();
//double height = hasHeight ? props->height.value() : defaultHeight;
//// move target position out from surface, along vector to camera
//targetPos = nodePos + glm::normalize(nodeToPrev) * (radius + height);
// OBS! TEMPORARY!! TODO: fix so that a camera pose is optional in Waypoint
const double radius = WaypointNodeDetails::findValidBoundingSphere(targetNode, 10.0);
targetPos = targetNode->worldPosition() + 3 * radius * glm::dvec3(1.0, 0.0, 0.0);
}
Camera* camera = global::navigationHandler.camera();
glm::dmat4 lookAtMat = glm::lookAt(
targetPos,
targetNode->worldPosition(),
camera->lookUpVectorWorldSpace()
);
glm::dquat targetRot = glm::normalize(glm::inverse(glm::quat_cast(lookAtMat)));
Waypoint wp{ targetPos, targetRot, nodeIdentifier, 10.0 }; // TODO: make property for min valid boudnignsphere
return std::vector<Waypoint>({ wp });
}
NavigationStateInstruction::NavigationStateInstruction(
const ghoul::Dictionary& dictionary): Instruction(dictionary)
{
if (dictionary.hasValue<ghoul::Dictionary>(KeyNavigationState)) {
auto navStateDict = dictionary.value<ghoul::Dictionary>(KeyNavigationState);
try {
openspace::documentation::testSpecificationAndThrow(
interaction::NavigationHandler::NavigationState::Documentation(),
navStateDict,
"NavigationState"
);
}
catch (ghoul::RuntimeError& e) {
LERROR(fmt::format("Unable to generate navigation state instruction from dictionary. Does not match documentation: {}", e.message));
return;
}
openspace::documentation::testSpecificationAndThrow(
NavigationState::Documentation(),
navStateDict,
"NavigationState"
);
navState = interaction::NavigationHandler::NavigationState(navStateDict);
navigationState = NavigationState(navStateDict);
}
}
Instruction::Instruction(const ghoul::Dictionary& dictionary) {
// TODO: test against some documentation?
// Deduce the instruction type based on the fields in the dictionary
if (dictionary.hasValue<std::string>(KeyTarget)) {
type = InstructionType::TargetNode;
props = std::make_shared<TargetNodeInstructionProps>(dictionary);
}
else if (dictionary.hasValue<ghoul::Dictionary>(KeyNavigationState)) {
type = InstructionType::NavigationState;
props = std::make_shared<NavigationStateInstructionProps>(dictionary);
}
else {
throw ghoul::RuntimeError(
"Could not deduce instruction type."
);
}
std::vector<Waypoint> NavigationStateInstruction::getWaypoints() const {
Waypoint wp{ navigationState, 10.0 }; // TODO: make property for min valid boudnignsphere
return std::vector<Waypoint>({ wp });
}
} // namespace openspace::autonavigation
+29 -18
View File
@@ -25,42 +25,53 @@
#ifndef __OPENSPACE_MODULE___PATHINSTRUCTION___H__
#define __OPENSPACE_MODULE___PATHINSTRUCTION___H__
#include <modules/autonavigation/waypoint.h>
#include <openspace/interaction/navigationhandler.h>
#include <optional>
namespace openspace::autonavigation {
enum class InstructionType { TargetNode, NavigationState };
struct Instruction {
Instruction() = default;
Instruction(const ghoul::Dictionary& dictionary);
virtual ~Instruction();
struct InstructionProps {
InstructionProps() = default;
InstructionProps(const ghoul::Dictionary& dictionary);
virtual ~InstructionProps() = 0;
virtual std::vector<Waypoint> getWaypoints() const = 0;
// TODO
//static documentation::Documentation Documentation();
std::optional<double> duration;
// TODO: include pause information
};
struct TargetNodeInstructionProps : public InstructionProps {
TargetNodeInstructionProps(const ghoul::Dictionary& dictionary);
struct TargetNodeInstruction : public Instruction {
TargetNodeInstruction(const ghoul::Dictionary& dictionary);
std::string targetNode;
std::vector<Waypoint> getWaypoints() const override;
// TODO
//static documentation::Documentation Documentation();
std::string nodeIdentifier;
std::optional<glm::dvec3> position; // relative to target node (model space)
std::optional<double> height;
};
struct NavigationStateInstructionProps : public InstructionProps {
NavigationStateInstructionProps(const ghoul::Dictionary& dictionary);
struct NavigationStateInstruction : public Instruction {
using NavigationState = interaction::NavigationHandler::NavigationState;
interaction::NavigationHandler::NavigationState navState;
NavigationStateInstruction(const ghoul::Dictionary& dictionary);
std::vector<Waypoint> getWaypoints() const override;
// TODO
//static documentation::Documentation Documentation();
NavigationState navigationState;
};
struct Instruction {
Instruction() = default;
Instruction(const ghoul::Dictionary& dictionary);
InstructionType type;
std::shared_ptr<InstructionProps> props;
};
} // namespace openspace::autonavigation
+68 -26
View File
@@ -35,66 +35,76 @@ namespace {
constexpr const char* KeyStopAtTargets = "StopAtTargets";
constexpr const char* KeyStartState = "StartState";
// Instruction Types
constexpr const char* KeyType = "Type";
constexpr const char* KeyTypeTargetNode = "Node";
constexpr const char* KeyTypeNavigationState = "NavigationState";
} // namespace
namespace openspace::autonavigation {
PathSpecification::PathSpecification(const ghoul::Dictionary& dictionary) {
try {
documentation::testSpecificationAndThrow(
Documentation(),
dictionary,
"Path Specification"
);
}
catch (ghoul::RuntimeError& e) {
LERROR(fmt::format("Unable to generate path specification from dictionary. Does not match documentation: {}", e.message));
return;
}
using NavigationState = interaction::NavigationHandler::NavigationState;
// Read instructions from dictionary
ghoul::Dictionary instructions = dictionary.value<ghoul::Dictionary>(KeyInstructions);
PathSpecification::PathSpecification(const ghoul::Dictionary& dictionary) {
documentation::testSpecificationAndThrow(
Documentation(),
dictionary,
"Path Specification"
);
ghoul::Dictionary instructions =
dictionary.value<ghoul::Dictionary>(KeyInstructions);
for (size_t i = 1; i <= instructions.size(); ++i) {
ghoul::Dictionary insDict = instructions.value<ghoul::Dictionary>(std::to_string(i));
ghoul::Dictionary insDict =
instructions.value<ghoul::Dictionary>(std::to_string(i));
_instructions.push_back(Instruction{ insDict });
if (!insDict.hasValue<std::string>(KeyType)) {
throw ghoul::RuntimeError(
"Each instruction must have a specified type."
);
}
std::string type = insDict.value<std::string>(KeyType);
tryReadInstruction(i, type, insDict);
}
// Read stop at targets flag
if (dictionary.hasValue<bool>(KeyStopAtTargets)) {
_stopAtTargets = dictionary.value<bool>(KeyStopAtTargets);
}
// Read start state
if (dictionary.hasValue<ghoul::Dictionary>(KeyStartState)) {
auto navStateDict = dictionary.value<ghoul::Dictionary>(KeyStartState);
try {
openspace::documentation::testSpecificationAndThrow(
interaction::NavigationHandler::NavigationState::Documentation(),
NavigationState::Documentation(),
navStateDict,
"NavigationState"
);
}
catch (ghoul::RuntimeError& e) {
LERROR(fmt::format("Unable to read start navigation state. Does not match documentation: {}", e.message));
LERROR(fmt::format("Unable to read start navigation state. {}", e.message));
return;
}
_startState = interaction::NavigationHandler::NavigationState(navStateDict);
_startState = NavigationState(navStateDict);
}
}
PathSpecification::PathSpecification(const Instruction instruction) {
_instructions.push_back(instruction);
_stopAtTargets = false;
PathSpecification::PathSpecification(const TargetNodeInstruction instruction) {
_instructions.push_back(std::make_unique<TargetNodeInstruction>(instruction));
}
const std::vector<Instruction>* PathSpecification::instructions() const {
const std::vector<std::unique_ptr<Instruction>>* PathSpecification::instructions() const {
return &_instructions;
}
const Instruction* PathSpecification::instruction(int i) const {
return (_instructions.size() > i) ? _instructions[i].get() : nullptr;
}
const bool PathSpecification::stopAtTargets() const {
return _stopAtTargets.value();
}
@@ -103,7 +113,7 @@ const bool PathSpecification::stopAtTargetsSpecified() const {
return _stopAtTargets.has_value();
}
const interaction::NavigationHandler::NavigationState& PathSpecification::startState() const {
const NavigationState& PathSpecification::startState() const {
return _startState.value();
}
@@ -140,4 +150,36 @@ documentation::Documentation PathSpecification::Documentation() {
};
}
void PathSpecification::tryReadInstruction(int index, std::string type,
ghoul::Dictionary& dictionary)
{
// create correct type of instruction and present and throw error with useful
// error message if we failed.
if (type == KeyTypeTargetNode) {
try {
_instructions.push_back(std::make_unique<TargetNodeInstruction>(dictionary));
}
catch (ghoul::RuntimeError& e) {
throw ghoul::RuntimeError(
fmt::format("Failed reading instruction {}: {}", index, e.message));
}
}
else if (type == KeyTypeNavigationState) {
try {
_instructions.push_back(
std::make_unique<NavigationStateInstruction>(dictionary));
}
catch (ghoul::RuntimeError& e) {
throw ghoul::RuntimeError(
fmt::format("Failed reading instruction {}: {}", index, e.message));
}
}
else {
throw ghoul::RuntimeError(fmt::format(
"Failed reading instruction {}: Uknown instruction type '{}'",
index, type)
);
}
}
} // namespace openspace::autonavigation
+6 -3
View File
@@ -39,19 +39,22 @@ class PathSpecification {
public:
PathSpecification() = default;
PathSpecification(const ghoul::Dictionary& dictionary);
PathSpecification(const Instruction instruction);
PathSpecification(const TargetNodeInstruction instruction);
static documentation::Documentation Documentation();
// Accessors
const std::vector<Instruction>* instructions() const;
const std::vector<std::unique_ptr<Instruction>>* instructions() const;
const Instruction* instruction(int i) const;
const bool stopAtTargets() const;
const bool stopAtTargetsSpecified() const;
const NavigationState& startState() const;
const bool hasStartState() const;
private:
std::vector<Instruction> _instructions;
void tryReadInstruction(int index, std::string type, ghoul::Dictionary& dictionary);
std::vector<std::unique_ptr<Instruction>> _instructions;
std::optional<bool> _stopAtTargets;
std::optional<NavigationState> _startState;