Files
OpenSpace/src/scripting/scriptscheduler.cpp
Alexander Bock a3c849843f Feature/new formatting (#547)
* Change to new logging format (closes #542)
 * Adds a screenshots folder that uses the application startup time
 * Creating focus nodes for VRT files if the info files contain location information
 * Fix issue with removing virtual properties
 * Add a method for returning the cartesian coordinates for a geolocation on a planet
2018-03-07 18:10:32 -05:00

299 lines
11 KiB
C++

/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2018 *
* *
* 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/scripting/scriptscheduler.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/scripting/scriptengine.h>
#include <openspace/util/time.h>
#include <ghoul/logging/logmanager.h>
//#include <ghoul/filesystem/filesystem>
#include <openspace/documentation/documentation.h>
#include <openspace/documentation/verifier.h>
namespace {
const char* KeyTime = "Time";
const char* KeyForwardScript = "ForwardScript";
const char* KeyBackwardScript = "BackwardScript";
const char* KeyUniversalScript = "Script";
} // namespace
#include "scriptscheduler_lua.inl"
namespace openspace::scripting {
documentation::Documentation ScriptScheduler::Documentation() {
using namespace openspace::documentation;
using TimeVerifier = StringVerifier;
using LuaScriptVerifier = StringVerifier;
return{
"Scheduled Scripts",
"core_scheduledscript",
{
{
"*",
new TableVerifier({
{
KeyTime,
new TimeVerifier,
Optional::No,
"The time at which, when the in game time passes it, the two "
"scripts will be executed. If the traversal is forwards (towards "
"+ infinity), the ForwardScript will be executed, otherwise the "
"BackwardScript will be executed instead."
},
{
KeyUniversalScript,
new LuaScriptVerifier,
Optional::Yes,
"The Lua script that will be executed when the specified time is "
"passed independent of its direction. This script will be "
"executed before the specific scripts if both versions are "
"specified"
},
{
KeyForwardScript,
new LuaScriptVerifier,
Optional::Yes,
"The Lua script that is executed when OpenSpace passes the time "
"in a forward direction."
},
{
KeyBackwardScript,
new LuaScriptVerifier,
Optional::Yes,
"The Lua script that is executed when OpenSpace passes the time "
"in a backward direction."
}
}),
Optional::No
}
}
};
}
ScriptScheduler::ScheduledScript::ScheduledScript(const ghoul::Dictionary& dictionary)
: time(-std::numeric_limits<double>::max())
{
std::string timeStr = dictionary.value<std::string>(KeyTime);
time = Time::convertTime(timeStr);
// If a universal script is specified, retrieve it and add a ; as a separator so that
// it can be added to the other scripts
std::string universal;
dictionary.getValue(KeyUniversalScript, universal);
if (!universal.empty()) {
universal += ";";
}
if (dictionary.hasKeyAndValue<std::string>(KeyForwardScript)) {
forwardScript =
universal + dictionary.value<std::string>(KeyForwardScript);
}
if (dictionary.hasKeyAndValue<std::string>(KeyBackwardScript)) {
backwardScript =
universal + dictionary.value<std::string>(KeyBackwardScript);
}
}
void ScriptScheduler::loadScripts(const ghoul::Dictionary& dictionary) {
// Check if all of the scheduled scripts are formed correctly
documentation::testSpecificationAndThrow(
Documentation(),
dictionary,
"ScriptScheduler"
);
// Create all the scheduled script first
std::vector<ScheduledScript> scheduledScripts;
for (size_t i = 1; i <= dictionary.size(); ++i) {
const ghoul::Dictionary& timedScriptDict = dictionary.value<ghoul::Dictionary>(
std::to_string(i)
);
scheduledScripts.emplace_back(timedScriptDict);
}
// Sort scripts by time; use a stable_sort as the user might have had an intention
// specifying multiple scripts for the same time in a specific order
std::stable_sort(
scheduledScripts.begin(),
scheduledScripts.end(),
[](const ScheduledScript& lhs, const ScheduledScript& rhs) {
return lhs.time < rhs.time;
}
);
// Move the scheduled scripts into their SOA alignment
// For the forward scripts, this is the forwards direction
// For the backward scripts, we insert them in the opposite order so that we can still
// return forward iterators to them in the progressTo method
for (ScheduledScript& script : scheduledScripts) {
_timings.push_back(script.time);
_forwardScripts.push_back(std::move(script.forwardScript));
_backwardScripts.insert(
_backwardScripts.begin(),
std::move(script.backwardScript)
);
}
// Ensure _currentIndex and _currentTime is accurate after new scripts was added
double lastTime = _currentTime;
rewind();
progressTo(lastTime);
ghoul_assert(
(_timings.size() == _forwardScripts.size()) &&
(_timings.size() == _backwardScripts.size()),
"The SOA data structure has been mistreated and has different number of values"
);
}
void ScriptScheduler::rewind() {
_currentIndex = 0;
_currentTime = -DBL_MAX;
}
void ScriptScheduler::clearSchedule() {
rewind();
_timings.clear();
_forwardScripts.clear();
_backwardScripts.clear();
}
std::pair<
std::vector<std::string>::const_iterator, std::vector<std::string>::const_iterator
>
ScriptScheduler::progressTo(double newTime)
{
if (newTime == _currentTime) {
return { _forwardScripts.end(), _forwardScripts.end() };
}
if (newTime > _currentTime) {
// Moving forward in time; we need to find the highest entry in the timings
// vector that is still smaller than the newTime
size_t prevIndex = _currentIndex;
auto it = std::upper_bound(
_timings.begin() + prevIndex, // We only need to start at the previous time
_timings.end(),
newTime
);
// How many values did we pass over?
ptrdiff_t n = std::distance(_timings.begin() + prevIndex, it);
_currentIndex = static_cast<int>(prevIndex + n);
// Update the new time
_currentTime = newTime;
return {
_forwardScripts.begin() + prevIndex,
_forwardScripts.begin() + _currentIndex
};
}
else {
// Moving backward in time; the need to find the lowest entry that is still bigger
// than the newTime
size_t prevIndex = _currentIndex;
auto it = std::lower_bound(
_timings.begin(),
_timings.begin() + prevIndex, // We can stop at the previous time
newTime
);
// How many values did we pass over?
ptrdiff_t n = std::distance(it, _timings.begin() + prevIndex);
_currentIndex = static_cast<int>(prevIndex - n);
// Update the new time
_currentTime = newTime;
return {
_backwardScripts.begin() + (_timings.size() - prevIndex),
_backwardScripts.begin() + (_timings.size() - _currentIndex)
};
}
}
double ScriptScheduler::currentTime() const {
return _currentTime;
}
std::vector<ScriptScheduler::ScheduledScript> ScriptScheduler::allScripts() const {
std::vector<ScheduledScript> result;
for (size_t i = 0; i < _timings.size(); ++i) {
ScheduledScript script;
script.time = _timings[i];
script.forwardScript = _forwardScripts[i];
script.backwardScript = _backwardScripts[i];
result.push_back(std::move(script));
}
return result;
}
LuaLibrary ScriptScheduler::luaLibrary() {
return {
"scriptScheduler",
{
{
"loadFile",
&luascriptfunctions::loadFile,
{},
"string",
"Load timed scripts from a Lua script file that returns a list of "
"scheduled scripts."
},
{
"loadScheduledScript",
&luascriptfunctions::loadScheduledScript,
{},
"string, string, (string, string)",
"Load a single scheduled script. The first argument is the time at which "
"the scheduled script is triggered, the second argument is the script "
"that is executed in the forward direction, the optional third argument "
"is the script executed in the backwards direction, and the optional "
"last argument is the universal script, executed in either direction."
},
{
"clear",
&luascriptfunctions::clear,
{},
"",
"Clears all scheduled scripts."
},
}
};
}
} // namespace openspace::scripting