// scriptrotation.cpp // // Copyright (C) 2006, Chris Laurel // // 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 #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; }