celestia/src/celephem/scriptrotation.cpp

210 lines
6.6 KiB
C++

// scriptrotation.cpp
//
// Copyright (C) 2006, Chris Laurel <claurel@shatters.net>
//
// Interface for a Celestia rotation model implemented via a Lua script.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
#include <celutil/logger.h>
#include "scriptobject.h"
#include "scriptrotation.h"
using namespace Eigen;
using namespace std;
using celestia::util::GetLogger;
/*! Initialize the script rotation
* moduleName is the name of a module that contains the rotation factory
* function. The module will be loaded with Lua's require function before
* creating the Lua rotation object.
*
* funcName is the name of some factory function in the specified luaState
* that will produce a Lua rotation object from the parameter list.
*
* The Lua factory function accepts a single table parameter containg
* all the rotation properties. It returns a table with the following
* properties:
*
* period - A number giving the period of the rotation. If not present,
* the rotation is assumed to be aperiodic.
* beginDate, endDate - optional values that specify the time span over
* which the rotation model is valid. If not given, the rotation model
* is assumed to be valid over all time. The rotation model is invalid
* if end < begin.
* orientation(time) - The orientation function takes a time value as
* input (TDB Julian day) and returns three values which are the the
* quaternion (w, x, y, z).
*/
bool
ScriptedRotation::initialize(const std::string& moduleName,
const std::string& funcName,
Hash* parameters)
{
if (parameters == nullptr)
return false;
luaState = GetScriptedObjectContext();
if (luaState == nullptr)
{
GetLogger()->warn("ScriptedRotations are currently disabled.\n");
return false;
}
if (!moduleName.empty())
{
lua_getglobal(luaState, "require");
if (!lua_isfunction(luaState, -1))
{
GetLogger()->error("Cannot load ScriptedRotation package: 'require' function is unavailable\n");
lua_pop(luaState, 1);
return false;
}
lua_pushstring(luaState, moduleName.c_str());
if (lua_pcall(luaState, 1, 1, 0) != 0)
{
GetLogger()->error("Failed to load module for ScriptedRotation: {}\n", lua_tostring(luaState, -1));
lua_pop(luaState, 1);
return false;
}
}
// Get the rotation generator function
lua_getglobal(luaState, funcName.c_str());
if (lua_isfunction(luaState, -1) == 0)
{
// No function with the requested name; pop whatever value we
// did receive along with the table of arguments.
lua_pop(luaState, 1);
GetLogger()->error("No Lua function named {} found.\n", funcName);
return false;
}
// Construct the table that we'll pass to the rotation generator function
lua_newtable(luaState);
SetLuaVariables(luaState, parameters);
// Call the generator function
if (lua_pcall(luaState, 1, 1, 0) != 0)
{
// Some sort of error occurred--the error message is atop the stack
GetLogger()->error("Error calling ScriptedRotation generator function: {}\n",
lua_tostring(luaState, -1));
lua_pop(luaState, 1);
return false;
}
if (lua_istable(luaState, -1) == 0)
{
// We have an object, but it's not a table. Pop it off the
// stack and report failure.
GetLogger()->error("ScriptedRotation generator function returned bad value.\n");
lua_pop(luaState, 1);
return false;
}
luaRotationObjectName = GenerateScriptObjectName();
// Attach the name to the script rotation
lua_pushvalue(luaState, -1); // dup the rotation object on top of stack
lua_setglobal(luaState, luaRotationObjectName.c_str());
// Get the rest of the rotation parameters; they are all optional.
period = SafeGetLuaNumber(luaState, -1, "period", 0.0);
validRangeBegin = SafeGetLuaNumber(luaState, -1, "beginDate", 0.0);
validRangeEnd = SafeGetLuaNumber(luaState, -1, "endDate", 0.0);
// Pop the rotations object off the stack
lua_pop(luaState, 1);
// Perform some sanity checks on the rotation parameters
if (validRangeEnd < validRangeBegin)
{
GetLogger()->error("Bad script rotation: valid range end < begin\n");
return false;
}
return true;
}
// Call the position method of the ScriptedRotation object
Quaterniond
ScriptedRotation::spin(double tjd) const
{
if (tjd != lastTime || !cacheable)
{
lua_getglobal(luaState, luaRotationObjectName.c_str());
if (lua_istable(luaState, -1))
{
lua_pushstring(luaState, "orientation");
lua_gettable(luaState, -2);
if (lua_isfunction(luaState, -1))
{
lua_pushvalue(luaState, -2); // push 'self' on stack
lua_pushnumber(luaState, tjd);
if (lua_pcall(luaState, 2, 4, 0) == 0)
{
lastOrientation = Quaterniond(lua_tonumber(luaState, -4),
lua_tonumber(luaState, -3),
lua_tonumber(luaState, -2),
lua_tonumber(luaState, -1));
lua_pop(luaState, 4);
lastTime = tjd;
}
else
{
// Function call failed for some reason
GetLogger()->warn("ScriptedRotation failed: {}\n", lua_tostring(luaState, -1));
lua_pop(luaState, 1);
}
}
else
{
// Bad orientation function
lua_pop(luaState, 1);
}
}
else
{
// The script rotation object disappeared. OOPS.
}
// Pop the script rotation object
lua_pop(luaState, 1);
}
return lastOrientation;
}
double
ScriptedRotation::getPeriod() const
{
if (period == 0.0)
return validRangeEnd - validRangeBegin;
return period;
}
bool
ScriptedRotation::isPeriodic() const
{
return period != 0.0;
}
void
ScriptedRotation::getValidRange(double& begin, double& end) const
{
begin = validRangeBegin;
end = validRangeEnd;
}