Index: src/celestia/url.cpp =================================================================== --- src/celestia/url.cpp (revision 4095) +++ src/celestia/url.cpp (working copy) @@ -52,27 +52,27 @@ if (!compareIgnoringCase(modeStr, std::string("Freeflight"))) { - mode = astro::Universal; + mode = ObserverFrame::Universal; nbBodies = 0; } else if (!compareIgnoringCase(modeStr, std::string("Follow"))) { - mode = astro::Ecliptical; + mode = ObserverFrame::Ecliptical; nbBodies = 1; } else if (!compareIgnoringCase(modeStr, std::string("SyncOrbit"))) { - mode = astro::Geographic; + mode = ObserverFrame::BodyFixed; nbBodies = 1; } else if (!compareIgnoringCase(modeStr, std::string("Chase"))) { - mode = astro::Chase; + mode = ObserverFrame::Chase; nbBodies = 1; } else if (!compareIgnoringCase(modeStr, std::string("PhaseLock"))) { - mode = astro::PhaseLock; + mode = ObserverFrame::PhaseLock; nbBodies = 2; } else if (!compareIgnoringCase(modeStr, std::string("Settings"))) @@ -125,9 +125,9 @@ return; // Number of bodies in Url doesn't match Mode } - if (nbBodies == 0) ref = FrameOfReference(); - if (nbBodies == 1) ref = FrameOfReference(mode, bodies[0]); - if (nbBodies == 2) ref = FrameOfReference(mode, bodies[0], bodies[1]); + if (nbBodies == 0) ref = ObserverFrame(); + if (nbBodies == 1) ref = ObserverFrame(mode, bodies[0]); + if (nbBodies == 2) ref = ObserverFrame(mode, bodies[0], bodies[1]); fromString = true; std::string time=""; @@ -218,15 +218,15 @@ this->type = type; - modeStr = getCoordSysName(sim->getFrame().coordSys); + modeStr = getCoordSysName(sim->getFrame()->getCoordinateSystem()); if (type == Settings) modeStr = "Settings"; - ref = sim->getFrame(); + ref = *sim->getFrame(); urlStr += "cel://" + modeStr; - if (type != Settings && sim->getFrame().coordSys != astro::Universal) { - body1 = getSelectionName(sim->getFrame().refObject); + if (type != Settings && sim->getFrame()->getCoordinateSystem() != ObserverFrame::Universal) { + body1 = getSelectionName(sim->getFrame()->getRefObject()); urlStr += "/" + body1; - if (sim->getFrame().coordSys == astro::PhaseLock) { - body2 = getSelectionName(sim->getFrame().targetObject); + if (sim->getFrame()->getCoordinateSystem() == ObserverFrame::PhaseLock) { + body2 = getSelectionName(sim->getFrame()->getTargetObject()); urlStr += "/" + body2; } } @@ -245,7 +245,7 @@ urlStr += "&y=" + coord.y.toString(); urlStr += "&z=" + coord.z.toString(); - orientation = sim->getObserver().getOrientation(); + orientation = sim->getObserver().getOrientationf(); sprintf(buff, "&ow=%f&ox=%f&oy=%f&oz=%f", orientation.w, orientation.x, orientation.y, orientation.z); urlStr += buff; break; @@ -371,23 +371,23 @@ } -std::string Url::getCoordSysName(astro::CoordinateSystem mode) const +std::string Url::getCoordSysName(ObserverFrame::CoordinateSystem mode) const { switch (mode) { - case astro::Universal: + case ObserverFrame::Universal: return "Freeflight"; - case astro::Ecliptical: + case ObserverFrame::Ecliptical: return "Follow"; - case astro::Geographic: + case ObserverFrame::BodyFixed: return "SyncOrbit"; - case astro::Chase: + case ObserverFrame::Chase: return "Chase"; - case astro::PhaseLock: + case ObserverFrame::PhaseLock: return "PhaseLock"; - case astro::Equatorial: + case ObserverFrame::Equatorial: return "Unknown"; - case astro::ObserverLocal: + case ObserverFrame::ObserverLocal: return "Unknown"; } return "Unknown"; @@ -469,7 +469,7 @@ switch(type) { case Absolute:// Intentional Fall-Through case Relative: - sim->setFrame(ref); + sim->setFrame(ref.getCoordinateSystem(), ref.getRefObject(), ref.getTargetObject()); sim->getActiveObserver()->setFOV(degToRad(fieldOfView)); appCore->setZoomFromFOV(); sim->setTimeScale(timeScale); Index: src/celestia/favorites.cpp =================================================================== --- src/celestia/favorites.cpp (revision 4095) +++ src/celestia/favorites.cpp (working copy) @@ -88,13 +88,13 @@ string coordSysName; favParams->getString("coordsys", coordSysName); if (coordSysName == "ecliptical") - fav->coordSys = astro::Ecliptical; + fav->coordSys = ObserverFrame::Ecliptical; else if (coordSysName == "equatorial") - fav->coordSys = astro::Equatorial; + fav->coordSys = ObserverFrame::Equatorial; else if (coordSysName == "geographic") - fav->coordSys = astro::Geographic; + fav->coordSys = ObserverFrame::BodyFixed; else - fav->coordSys = astro::Universal; + fav->coordSys = ObserverFrame::Universal; favorites->insert(favorites->end(), fav); } @@ -137,19 +137,19 @@ out << "\tcoordsys \""; switch (fav->coordSys) { - case astro::Universal: + case ObserverFrame::Universal: out << "universal"; break; - case astro::Ecliptical: + case ObserverFrame::Ecliptical: out << "ecliptical"; break; - case astro::Equatorial: + case ObserverFrame::Equatorial: out << "equatorial"; break; - case astro::Geographic: + case ObserverFrame::BodyFixed: out << "geographic"; break; - case astro::ObserverLocal: + case ObserverFrame::ObserverLocal: out << "local"; break; - case astro::PhaseLock: + case ObserverFrame::PhaseLock: out << "phaselock"; break; - case astro::Chase: + case ObserverFrame::Chase: out << "chase"; break; } out << "\"\n"; Index: src/celestia/celestiacore.cpp =================================================================== --- src/celestia/celestiacore.cpp (revision 4095) +++ src/celestia/celestiacore.cpp (working copy) @@ -140,7 +140,7 @@ { float coarseness = 1.5f; - Selection selection = sim.getActiveObserver()->getFrame().refObject; + Selection selection = sim.getActiveObserver()->getFrame()->getRefObject(); if (selection.getType() == Selection::Type_Star || selection.getType() == Selection::Type_Body) { @@ -449,7 +449,7 @@ FavoritesEntry* fav = new FavoritesEntry(); fav->jd = sim->getTime(); fav->position = sim->getObserver().getPosition(); - fav->orientation = sim->getObserver().getOrientation(); + fav->orientation = sim->getObserver().getOrientationf(); fav->name = name; fav->isFolder = false; fav->parentFolder = parentFolder; @@ -460,7 +460,7 @@ else fav->selectionName = sel.getName(); - fav->coordSys = sim->getFrame().coordSys; + fav->coordSys = sim->getFrame()->getCoordinateSystem(); favorites->insert(pos, fav); } @@ -937,11 +937,12 @@ { Observer& observer = sim->getObserver(); Vec3d v = Vec3d(0, 0, dx * -MouseRotationSensitivity); - RigidTransform rt = observer.getSituation(); - Quatd dr = 0.5 * (v * rt.rotation); - rt.rotation += dr; - rt.rotation.normalize(); - observer.setSituation(rt); + + Quatd obsOrientation = observer.getOrientation(); + Quatd dr = 0.5 * (v * obsOrientation); + obsOrientation += dr; + obsOrientation.normalize(); + observer.setOrientation(obsOrientation); } } else if (checkMask(modifiers, LeftButton | ShiftKey)) @@ -1571,7 +1572,7 @@ if (sim->getObserverMode() == Observer::Travelling) sim->setObserverMode(Observer::Free); else - sim->setFrame(astro::Universal, Selection()); + sim->setFrame(ObserverFrame::Universal, Selection()); if (!sim->getTrackedObject().empty()) sim->setTrackedObject(Selection()); } @@ -1853,9 +1854,9 @@ case 'G': addToHistory(); - if (sim->getFrame().coordSys == astro::Universal) + if (sim->getFrame()->getCoordinateSystem() == ObserverFrame::Universal) sim->follow(); - sim->gotoSelection(5.0, Vec3f(0, 1, 0), astro::ObserverLocal); + sim->gotoSelection(5.0, Vec3f(0, 1, 0), ObserverFrame::ObserverLocal); break; case 'H': @@ -2266,7 +2267,7 @@ av = av * (float) exp(-dt * RotationDecay); float fov = sim->getActiveObserver()->getFOV() / stdFOV; - FrameOfReference frame = sim->getFrame(); + Selection refObject = sim->getFrame()->getRefObject(); // Handle arrow keys; disable them when the log console is displayed, // because then they're used to scroll up and down. @@ -2285,11 +2286,11 @@ } else { - if (!frame.refObject.empty()) + if (!refObject.empty()) { - Quatf orientation = sim->getObserver().getOrientation(); + Quatf orientation = sim->getObserver().getOrientationf(); Vec3d upd = sim->getObserver().getPosition() - - frame.refObject.getPosition(sim->getTime()); + refObject.getPosition(sim->getTime()); upd.normalize(); Vec3f up((float) upd.x, (float) upd.y, (float) upd.z); @@ -2359,7 +2360,7 @@ sim->setTargetSpeed(sim->getTargetSpeed()); } - if (!frame.refObject.empty()) + if (!refObject.empty()) { Quatf q(1.0f); float coarseness = ComputeRotationCoarseness(*sim); @@ -3452,32 +3453,34 @@ *overlay << '\n'; { - FrameOfReference frame = sim->getFrame(); + //FrameOfReference frame = sim->getFrame(); + Selection refObject = sim->getFrame()->getRefObject(); + ObserverFrame::CoordinateSystem coordSys = sim->getFrame()->getCoordinateSystem(); - switch (frame.coordSys) + switch (coordSys) { - case astro::Ecliptical: + case ObserverFrame::Ecliptical: *overlay << _("Follow "); - displaySelectionName(*overlay, frame.refObject, + displaySelectionName(*overlay, refObject, *sim->getUniverse()); break; - case astro::Geographic: + case ObserverFrame::BodyFixed: *overlay << _("Sync Orbit "); - displaySelectionName(*overlay, frame.refObject, + displaySelectionName(*overlay, refObject, *sim->getUniverse()); break; - case astro::PhaseLock: + case ObserverFrame::PhaseLock: *overlay << _("Lock "); - displaySelectionName(*overlay, frame.refObject, + displaySelectionName(*overlay, refObject, *sim->getUniverse()); *overlay << " -> "; - displaySelectionName(*overlay, frame.targetObject, + displaySelectionName(*overlay, sim->getFrame()->getTargetObject(), *sim->getUniverse()); break; - case astro::Chase: + case ObserverFrame::Chase: *overlay << _("Chase "); - displaySelectionName(*overlay, frame.refObject, + displaySelectionName(*overlay, refObject, *sim->getUniverse()); break; @@ -3582,7 +3585,7 @@ // Display RA/Dec for the selection, but only when the observer is near // the Earth. - Selection refObject = sim->getFrame().refObject; + Selection refObject = sim->getFrame()->getRefObject(); if (refObject.body() && refObject.body()->getName() == "Earth") { Body* earth = refObject.body(); Index: src/celestia/favorites.h =================================================================== --- src/celestia/favorites.h (revision 4095) +++ src/celestia/favorites.h (working copy) @@ -15,7 +15,7 @@ #include #include #include -#include +#include struct FavoritesEntry @@ -29,7 +29,7 @@ bool isFolder; std::string parentFolder; - astro::CoordinateSystem coordSys; + ObserverFrame::CoordinateSystem coordSys; }; typedef std::vector FavoritesList; Index: src/celestia/url.h =================================================================== --- src/celestia/url.h (revision 4095) +++ src/celestia/url.h (working copy) @@ -52,11 +52,11 @@ CelestiaCore *appCore; - FrameOfReference ref; + ObserverFrame ref; Selection selected; Selection tracked; - astro::CoordinateSystem mode; + ObserverFrame::CoordinateSystem mode; int nbBodies; float fieldOfView; float timeScale; @@ -66,7 +66,7 @@ bool pauseState; std::map parseUrlParams(const std::string& url) const; - std::string getCoordSysName(astro::CoordinateSystem mode) const; + std::string getCoordSysName(ObserverFrame::CoordinateSystem mode) const; std::string getSelectionName(const Selection& selection) const; std::string getBodyShortName(const std::string& body) const; static std::string decode_string(const std::string& str); Index: src/celestia/celx.cpp =================================================================== --- src/celestia/celx.cpp (revision 4095) +++ src/celestia/celx.cpp (working copy) @@ -1,6702 +1,6700 @@ -// celx.cpp -// -// Copyright (C) 2003, Chris Laurel -// -// Lua script extensions for Celestia -// -// 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "imagecapture.h" - -// Older gcc versions used instead of . -// This has been corrected in GCC 3.2, but name clashing must -// be avoided -#ifdef __GNUC__ -#undef min -#undef max -#endif -#include - -#include "celx.h" -#include "celestiacore.h" - -using namespace std; - -static const char* ClassNames[] = -{ - "class_celestia", - "class_observer", - "class_object", - "class_vec3", - "class_matrix", - "class_rotation", - "class_position", - "class_frame", - "class_celscript", - "class_font", - "class_image", - "class_texture", -}; - -static const int _Celestia = 0; -static const int _Observer = 1; -static const int _Object = 2; -static const int _Vec3 = 3; -static const int _Matrix = 4; -static const int _Rotation = 5; -static const int _Position = 6; -static const int _Frame = 7; -static const int _CelScript= 8; -static const int _Font = 9; -static const int _Image = 10; -static const int _Texture = 11; - -#define CLASS(i) ClassNames[(i)] - -// Maximum timeslice a script may run without -// returning control to celestia -static const double MaxTimeslice = 5.0; - -// names of callback-functions in Lua: -static const char* KbdCallback = "celestia_keyboard_callback"; -static const char* CleanupCallback = "celestia_cleanup_callback"; - -static const char* EventHandlers = "celestia_event_handlers"; - -static const char* KeyHandler = "key"; -static const char* TickHandler = "tick"; -static const char* MouseDownHandler = "mousedown"; -static const char* MouseUpHandler = "mouseup"; - -typedef map FlagMap; -typedef map ColorMap; - -static FlagMap RenderFlagMap; -static FlagMap LabelFlagMap; -static FlagMap LocationFlagMap; -static FlagMap BodyTypeMap; -static FlagMap OverlayElementMap; -static FlagMap OrbitVisibilityMap; -static ColorMap LineColorMap; -static ColorMap LabelColorMap; -static bool mapsInitialized = false; - -// select which type of error will be fatal (call lua_error) and -// which will return a default value instead -enum FatalErrors { - NoErrors = 0, - WrongType = 1, - WrongArgc = 2, - AllErrors = WrongType | WrongArgc, -}; - - -// Initialize various maps from named keywords to numeric flags used within celestia: -static void initRenderFlagMap() -{ - RenderFlagMap["orbits"] = Renderer::ShowOrbits; - RenderFlagMap["cloudmaps"] = Renderer::ShowCloudMaps; - RenderFlagMap["constellations"] = Renderer::ShowDiagrams; - RenderFlagMap["galaxies"] = Renderer::ShowGalaxies; - RenderFlagMap["planets"] = Renderer::ShowPlanets; - RenderFlagMap["stars"] = Renderer::ShowStars; - RenderFlagMap["nightmaps"] = Renderer::ShowNightMaps; - RenderFlagMap["eclipseshadows"] = Renderer::ShowEclipseShadows; - RenderFlagMap["ringshadows"] = Renderer::ShowRingShadows; - RenderFlagMap["comettails"] = Renderer::ShowCometTails; - RenderFlagMap["boundaries"] = Renderer::ShowBoundaries; - RenderFlagMap["markers"] = Renderer::ShowMarkers; - RenderFlagMap["automag"] = Renderer::ShowAutoMag; - RenderFlagMap["atmospheres"] = Renderer::ShowAtmospheres; - RenderFlagMap["grid"] = Renderer::ShowCelestialSphere; - RenderFlagMap["smoothlines"] = Renderer::ShowSmoothLines; - RenderFlagMap["partialtrajectories"] = Renderer::ShowPartialTrajectories; - RenderFlagMap["nebulae"] = Renderer::ShowNebulae; - RenderFlagMap["openclusters"] = Renderer::ShowOpenClusters; - RenderFlagMap["cloudshadows"] = Renderer::ShowCloudShadows; -} - -static void initLabelFlagMap() -{ - LabelFlagMap["planets"] = Renderer::PlanetLabels; - LabelFlagMap["moons"] = Renderer::MoonLabels; - LabelFlagMap["spacecraft"] = Renderer::SpacecraftLabels; - LabelFlagMap["asteroids"] = Renderer::AsteroidLabels; - LabelFlagMap["comets"] = Renderer::CometLabels; - LabelFlagMap["constellations"] = Renderer::ConstellationLabels; - LabelFlagMap["stars"] = Renderer::StarLabels; - LabelFlagMap["galaxies"] = Renderer::GalaxyLabels; - LabelFlagMap["locations"] = Renderer::LocationLabels; - LabelFlagMap["nebulae"] = Renderer::NebulaLabels; - LabelFlagMap["openclusters"] = Renderer::OpenClusterLabels; - LabelFlagMap["i18nconstellations"] = Renderer::I18nConstellationLabels; -} - -static void initBodyTypeMap() -{ - BodyTypeMap["Planet"] = Body::Planet; - BodyTypeMap["Moon"] = Body::Moon; - BodyTypeMap["Asteroid"] = Body::Asteroid; - BodyTypeMap["Comet"] = Body::Comet; - BodyTypeMap["Spacecraft"] = Body::Spacecraft; - BodyTypeMap["Invisible"] = Body::Invisible; - BodyTypeMap["Star"] = Body::Stellar; - BodyTypeMap["Unknown"] = Body::Unknown; -} - -static void initLocationFlagMap() -{ - LocationFlagMap["city"] = Location::City; - LocationFlagMap["observatory"] = Location::Observatory; - LocationFlagMap["landingsite"] = Location::LandingSite; - LocationFlagMap["crater"] = Location::Crater; - LocationFlagMap["vallis"] = Location::Vallis; - LocationFlagMap["mons"] = Location::Mons; - LocationFlagMap["planum"] = Location::Planum; - LocationFlagMap["chasma"] = Location::Chasma; - LocationFlagMap["patera"] = Location::Patera; - LocationFlagMap["mare"] = Location::Mare; - LocationFlagMap["rupes"] = Location::Rupes; - LocationFlagMap["tessera"] = Location::Tessera; - LocationFlagMap["regio"] = Location::Regio; - LocationFlagMap["chaos"] = Location::Chaos; - LocationFlagMap["terra"] = Location::Terra; - LocationFlagMap["astrum"] = Location::Astrum; - LocationFlagMap["corona"] = Location::Corona; - LocationFlagMap["dorsum"] = Location::Dorsum; - LocationFlagMap["fossa"] = Location::Fossa; - LocationFlagMap["catena"] = Location::Catena; - LocationFlagMap["mensa"] = Location::Mensa; - LocationFlagMap["rima"] = Location::Rima; - LocationFlagMap["undae"] = Location::Undae; - LocationFlagMap["reticulum"] = Location::Reticulum; - LocationFlagMap["planitia"] = Location::Planitia; - LocationFlagMap["linea"] = Location::Linea; - LocationFlagMap["fluctus"] = Location::Fluctus; - LocationFlagMap["farrum"] = Location::Farrum; - LocationFlagMap["other"] = Location::Other; -} - -static void initOverlayElementMap() -{ - OverlayElementMap["Time"] = CelestiaCore::ShowTime; - OverlayElementMap["Velocity"] = CelestiaCore::ShowVelocity; - OverlayElementMap["Selection"] = CelestiaCore::ShowSelection; - OverlayElementMap["Frame"] = CelestiaCore::ShowFrame; -} - -static void initOrbitVisibilityMap() -{ - OrbitVisibilityMap["never"] = Body::NeverVisible; - OrbitVisibilityMap["normal"] = Body::UseClassVisibility; - OrbitVisibilityMap["always"] = Body::AlwaysVisible; -} - - -static void initLabelColorMap() -{ - LabelColorMap["stars"] = &Renderer::StarLabelColor; - LabelColorMap["planets"] = &Renderer::PlanetLabelColor; - LabelColorMap["moons"] = &Renderer::MoonLabelColor; - LabelColorMap["asteroids"] = &Renderer::AsteroidLabelColor; - LabelColorMap["comets"] = &Renderer::CometLabelColor; - LabelColorMap["spacecraft"] = &Renderer::SpacecraftLabelColor; - LabelColorMap["locations"] = &Renderer::LocationLabelColor; - LabelColorMap["galaxies"] = &Renderer::GalaxyLabelColor; - LabelColorMap["nebulae"] = &Renderer::NebulaLabelColor; - LabelColorMap["openclusters"] = &Renderer::OpenClusterLabelColor; - LabelColorMap["constellations"] = &Renderer::ConstellationLabelColor; - LabelColorMap["equatorialgrid"] = &Renderer::EquatorialGridLabelColor; -} - -static void initLineColorMap() -{ - LineColorMap["starorbits"] = &Renderer::StarOrbitColor; - LineColorMap["planetorbits"] = &Renderer::PlanetOrbitColor; - LineColorMap["moonorbits"] = &Renderer::MoonOrbitColor; - LineColorMap["asteroidorbits"] = &Renderer::AsteroidOrbitColor; - LineColorMap["cometorbits"] = &Renderer::CometOrbitColor; - LineColorMap["spacecraftorbits"] = &Renderer::SpacecraftOrbitColor; - LineColorMap["constellations"] = &Renderer::ConstellationColor; - LineColorMap["boundaries"] = &Renderer::BoundaryColor; - LineColorMap["equatorialgrid"] = &Renderer::EquatorialGridColor; -} - - -#if LUA_VER >= 0x050100 -// Load a Lua library--in Lua 5.1, the luaopen_* functions cannot be called -// directly. They most be invoked through the Lua state. -static void openLuaLibrary(lua_State* l, - const char* name, - lua_CFunction func) -{ - lua_pushcfunction(l, func); - lua_pushstring(l, name); - lua_call(l, 1, 0); -} -#endif - - -static void initMaps() -{ - if (!mapsInitialized) - { - initRenderFlagMap(); - initLabelFlagMap(); - initBodyTypeMap(); - initLocationFlagMap(); - initOverlayElementMap(); - initOrbitVisibilityMap(); - initLabelColorMap(); - initLineColorMap(); - } - mapsInitialized = true; -} - - -static void getField(lua_State* l, int index, const char* key) -{ - // When we move to Lua 5.1, this will be replaced by: - // lua_getfield(l, index, key); - lua_pushstring(l, key); - - if (index != LUA_GLOBALSINDEX && index != LUA_REGISTRYINDEX) - lua_gettable(l, index - 1); - else - lua_gettable(l, index); -} - - -// Wrapper for a CEL-script, including the needed Execution Environment -class CelScriptWrapper : public ExecutionEnvironment -{ - public: - CelScriptWrapper(CelestiaCore& appCore, istream& scriptfile): - script(NULL), - core(appCore), - cmdSequence(NULL), - tickTime(0.0), - errorMessage("") - { - CommandParser parser(scriptfile); - cmdSequence = parser.parse(); - if (cmdSequence != NULL) - { - script = new Execution(*cmdSequence, *this); - } - else - { - const vector* errors = parser.getErrors(); - if (errors->size() > 0) - errorMessage = "Error while parsing CEL-script: " + (*errors)[0]; - else - errorMessage = "Error while parsing CEL-script."; - } - } - - virtual ~CelScriptWrapper() - { - if (script != NULL) - delete script; - if (cmdSequence != NULL) - delete cmdSequence; - } - - string getErrorMessage() const - { - return errorMessage; - } - - // tick the CEL-script. t is in seconds and doesn't have to start with zero - bool tick(double t) - { - // use first tick to set the time - if (tickTime == 0.0) - { - tickTime = t; - return false; - } - double dt = t - tickTime; - tickTime = t; - return script->tick(dt); - } - - Simulation* getSimulation() const - { - return core.getSimulation(); - } - - Renderer* getRenderer() const - { - return core.getRenderer(); - } - - CelestiaCore* getCelestiaCore() const - { - return &core; - } - - void showText(string s, int horig, int vorig, int hoff, int voff, - double duration) - { - core.showText(s, horig, vorig, hoff, voff, duration); - } - - private: - Execution* script; - CelestiaCore& core; - CommandSequence* cmdSequence; - double tickTime; - string errorMessage; -}; - - -// Push a class name onto the Lua stack -static void PushClass(lua_State* l, int id) -{ - lua_pushlstring(l, ClassNames[id], strlen(ClassNames[id])); -} - -// Set the class (metatable) of the object on top of the stack -static void SetClass(lua_State* l, int id) -{ - PushClass(l, id); - lua_rawget(l, LUA_REGISTRYINDEX); - if (lua_type(l, -1) != LUA_TTABLE) - cout << "Metatable for " << ClassNames[id] << " not found!\n"; - if (lua_setmetatable(l, -2) == 0) - cout << "Error setting metatable for " << ClassNames[id] << '\n'; -} - -// Initialize the metatable for a class; sets the appropriate registry -// entries and __index, leaving the metatable on the stack when done. -static void CreateClassMetatable(lua_State* l, int id) -{ - lua_newtable(l); - PushClass(l, id); - lua_pushvalue(l, -2); - lua_rawset(l, LUA_REGISTRYINDEX); // registry.name = metatable - lua_pushvalue(l, -1); - PushClass(l, id); - lua_rawset(l, LUA_REGISTRYINDEX); // registry.metatable = name - - lua_pushliteral(l, "__index"); - lua_pushvalue(l, -2); - lua_rawset(l, -3); -} - -// Register a class 'method' in the metatable (assumed to be on top of the stack) -static void RegisterMethod(lua_State* l, const char* name, lua_CFunction fn) -{ - lua_pushstring(l, name); - lua_pushvalue(l, -2); - lua_pushcclosure(l, fn, 1); - lua_settable(l, -3); -} - - -// Verify that an object at location index on the stack is of the -// specified class -static bool istype(lua_State* l, int index, int id) -{ - // get registry[metatable] - if (!lua_getmetatable(l, index)) - return false; - lua_rawget(l, LUA_REGISTRYINDEX); - - if (lua_type(l, -1) != LUA_TSTRING) - { - cout << "istype failed! Unregistered class.\n"; - lua_pop(l, 1); - return false; - } - - const char* classname = lua_tostring(l, -1); - if (classname != NULL && strcmp(classname, ClassNames[id]) == 0) - { - lua_pop(l, 1); - return true; - } - lua_pop(l, 1); - return false; -} - -// Verify that an object at location index on the stack is of the -// specified class and return pointer to userdata -static void* CheckUserData(lua_State* l, int index, int id) -{ - if (istype(l, index, id)) - return lua_touserdata(l, index); - else - return NULL; -} - - -// Return the CelestiaCore object stored in the globals table -static CelestiaCore* getAppCore(lua_State* l, FatalErrors fatalErrors = NoErrors) -{ - lua_pushstring(l, "celestia-appcore"); - lua_gettable(l, LUA_REGISTRYINDEX); - - if (!lua_islightuserdata(l, -1)) - { - if (fatalErrors == NoErrors) - return NULL; - else - { - lua_pushstring(l, "internal error: invalid appCore"); - lua_error(l); - } - } - - CelestiaCore* appCore = static_cast(lua_touserdata(l, -1)); - lua_pop(l, 1); - return appCore; -} - - -LuaState::LuaState() : - timeout(MaxTimeslice), - state(NULL), - costate(NULL), - alive(false), - timer(NULL), - scriptAwakenTime(0.0), - ioMode(NoIO), - eventHandlerEnabled(false) -{ - state = lua_open(); - timer = CreateTimer(); - screenshotCount = 0; -} - -LuaState::~LuaState() -{ - delete timer; - if (state != NULL) - lua_close(state); -#if 0 - if (costate != NULL) - lua_close(costate); -#endif -} - -lua_State* LuaState::getState() const -{ - return state; -} - - -double LuaState::getTime() const -{ - return timer->getTime(); -} - - -// Check if the running script has exceeded its allowed timeslice -// and terminate it if it has: -static void checkTimeslice(lua_State* l, lua_Debug* /*ar*/) -{ - lua_pushstring(l, "celestia-luastate"); - lua_gettable(l, LUA_REGISTRYINDEX); - if (!lua_islightuserdata(l, -1)) - { - lua_pushstring(l, "Internal Error: Invalid table entry in checkTimeslice"); - lua_error(l); - } - LuaState* luastate = static_cast(lua_touserdata(l, -1)); - if (luastate == NULL) - { - lua_pushstring(l, "Internal Error: Invalid value in checkTimeslice"); - lua_error(l); - } - - if (luastate->timesliceExpired()) - { - const char* errormsg = "Timeout: script hasn't returned control to celestia (forgot to call wait()?)"; - cerr << errormsg << "\n"; - lua_pushstring(l, errormsg); - lua_error(l); - } - return; -} - - -// allow the script to perform cleanup -void LuaState::cleanup() -{ - if (ioMode == Asking) - { - // Restore renderflags: - CelestiaCore* appCore = getAppCore(costate, NoErrors); - if (appCore != NULL) - { - lua_pushstring(state, "celestia-savedrenderflags"); - lua_gettable(state, LUA_REGISTRYINDEX); - if (lua_isuserdata(state, -1)) - { - int* savedrenderflags = static_cast(lua_touserdata(state, -1)); - appCore->getRenderer()->setRenderFlags(*savedrenderflags); - // now delete entry: - lua_pushstring(state, "celestia-savedrenderflags"); - lua_pushnil(state); - lua_settable(state, LUA_REGISTRYINDEX); - } - lua_pop(state,1); - } - } - lua_pushstring(costate, CleanupCallback); - lua_gettable(costate, LUA_GLOBALSINDEX); - if (lua_isnil(costate, -1)) - { - return; - } - timeout = getTime() + 1.0; - if (lua_pcall(costate, 0, 0, 0) != 0) - { - cerr << "Error while executing cleanup-callback: " << lua_tostring(costate, -1) << "\n"; - } -} - - -bool LuaState::createThread() -{ - // Initialize the coroutine which wraps the script - if (!(lua_isfunction(state, -1) && !lua_iscfunction(state, -1))) - { - // Should never happen; we manually set up the stack in C++ - assert(0); - return false; - } - else - { - costate = lua_newthread(state); - if (costate == NULL) - return false; - lua_sethook(costate, checkTimeslice, LUA_MASKCOUNT, 1000); - lua_pushvalue(state, -2); - lua_xmove(state, costate, 1); /* move function from L to NL */ - alive = true; - return true; - } -} - - -string LuaState::getErrorMessage() -{ - if (lua_gettop(state) > 0) - { - if (lua_isstring(state, -1)) - return lua_tostring(state, -1); - } - return ""; -} - - -bool LuaState::timesliceExpired() -{ - if (timeout < getTime()) - { - // timeslice expired, make every instruction (including pcall) fail: - lua_sethook(costate, checkTimeslice, LUA_MASKCOUNT, 1); - return true; - } - else - { - return false; - } -} - - -static int resumeLuaThread(lua_State *L, lua_State *co, int narg) -{ - int status; - - //if (!lua_checkstack(co, narg)) - // luaL_error(L, "too many arguments to resume"); - lua_xmove(L, co, narg); - - status = lua_resume(co, narg); -#if LUA_VER >= 0x050100 - if (status == 0 || status == LUA_YIELD) -#else - if (status == 0) -#endif - { - int nres = lua_gettop(co); - //if (!lua_checkstack(L, narg)) - // luaL_error(L, "too many results to resume"); - lua_xmove(co, L, nres); // move yielded values - return nres; - } - else - { - lua_xmove(co, L, 1); // move error message - return -1; // error flag - } -} - - -bool LuaState::isAlive() const -{ - return alive; -} - - -struct ReadChunkInfo -{ - char* buf; - int bufSize; - istream* in; -}; - -static const char* readStreamChunk(lua_State*, void* udata, size_t* size) -{ - assert(udata != NULL); - if (udata == NULL) - return NULL; - - ReadChunkInfo* info = reinterpret_cast(udata); - assert(info->buf != NULL); - assert(info->in != NULL); - - if (!info->in->good()) - { - *size = 0; - return NULL; - } - - info->in->read(info->buf, info->bufSize); - streamsize nread = info->in->gcount(); - - *size = (size_t) nread; - if (nread == 0) - return NULL; - else - return info->buf; -} - - -// Callback for CelestiaCore::charEntered. -// Returns true if keypress has been consumed -bool LuaState::charEntered(const char* c_p) -{ - if (ioMode == Asking && getTime() > timeout) - { - int stackTop = lua_gettop(costate); - if (strcmp(c_p, "y") == 0) - { -#if LUA_VER >= 0x050100 - openLuaLibrary(costate, LUA_LOADLIBNAME, luaopen_package); - openLuaLibrary(costate, LUA_IOLIBNAME, luaopen_io); -#else - lua_iolibopen(costate); -#endif - ioMode = IOAllowed; - } - else - { - ioMode = IODenied; - } - CelestiaCore* appCore = getAppCore(costate, NoErrors); - if (appCore == NULL) - { - cerr << "ERROR: appCore not found\n"; - return true; - } - appCore->setTextEnterMode(appCore->getTextEnterMode() & ~CelestiaCore::KbPassToScript); - appCore->showText("", 0, 0, 0, 0); - // Restore renderflags: - lua_pushstring(costate, "celestia-savedrenderflags"); - lua_gettable(costate, LUA_REGISTRYINDEX); - if (lua_isuserdata(costate, -1)) - { - int* savedrenderflags = static_cast(lua_touserdata(costate, -1)); - appCore->getRenderer()->setRenderFlags(*savedrenderflags); - // now delete entry: - lua_pushstring(costate, "celestia-savedrenderflags"); - lua_pushnil(costate); - lua_settable(costate, LUA_REGISTRYINDEX); - } - else - { - cerr << "Oops, expected savedrenderflags to be userdata\n"; - } - lua_settop(costate,stackTop); - return true; - } - int stack_top = lua_gettop(costate); - bool result = true; - lua_pushstring(costate, KbdCallback); - lua_gettable(costate, LUA_GLOBALSINDEX); - lua_pushstring(costate, c_p); - timeout = getTime() + 1.0; - if (lua_pcall(costate, 1, 1, 0) != 0) - { - cerr << "Error while executing keyboard-callback: " << lua_tostring(costate, -1) << "\n"; - result = false; - } - else - { - if (lua_isboolean(costate, -1)) - { - result = (lua_toboolean(costate, -1) != 0); - } - } - -#if LUA_VER < 0x050100 - // cleanup stack - is this necessary? - lua_settop(costate, stack_top); -#endif - return result; -} - - -// Returns true if a handler is registered for the key -bool LuaState::handleKeyEvent(const char* key) -{ - CelestiaCore* appCore = getAppCore(costate, NoErrors); - if (appCore == NULL) - { - return false; - } - - // get the registered event table - getField(costate, LUA_REGISTRYINDEX, EventHandlers); - if (!lua_istable(costate, -1)) - { - cerr << "Missing event handler table"; - lua_pop(costate, 1); - return false; - } - - bool handled = false; - getField(costate, -1, KeyHandler); - if (lua_isfunction(costate, -1)) - { - lua_remove(costate, -2); // remove the key event table from the stack - - lua_newtable(costate); - lua_pushstring(costate, "char"); - lua_pushstring(costate, key); // the default key handler accepts the key name as an argument - lua_settable(costate, -3); - - timeout = getTime() + 1.0; - if (lua_pcall(costate, 1, 1, 0) != 0) - { - cerr << "Error while executing keyboard callback: " << lua_tostring(costate, -1) << "\n"; - } - else - { - handled = lua_toboolean(costate, -1) == 1 ? true : false; - } - lua_pop(costate, 1); // pop the return value - } - else - { - lua_pop(costate, 2); - } - - return handled; -} - - -// Returns true if a handler is registered for the button event -bool LuaState::handleMouseButtonEvent(float x, float y, int button, bool down) -{ - CelestiaCore* appCore = getAppCore(costate, NoErrors); - if (appCore == NULL) - { - return false; - } - - // get the registered event table - getField(costate, LUA_REGISTRYINDEX, EventHandlers); - if (!lua_istable(costate, -1)) - { - cerr << "Missing event handler table"; - lua_pop(costate, 1); - return false; - } - - bool handled = false; - getField(costate, -1, down ? MouseDownHandler : MouseUpHandler); - if (lua_isfunction(costate, -1)) - { - lua_remove(costate, -2); // remove the key event table from the stack - - lua_newtable(costate); - lua_pushstring(costate, "button"); - lua_pushnumber(costate, button); - lua_settable(costate, -3); - lua_pushstring(costate, "x"); - lua_pushnumber(costate, x); - lua_settable(costate, -3); - lua_pushstring(costate, "y"); - lua_pushnumber(costate, y); - lua_settable(costate, -3); - - timeout = getTime() + 1.0; - if (lua_pcall(costate, 1, 1, 0) != 0) - { - cerr << "Error while executing keyboard callback: " << lua_tostring(costate, -1) << "\n"; - } - else - { - handled = lua_toboolean(costate, -1) == 1 ? true : false; - } - lua_pop(costate, 1); // pop the return value - } - else - { - lua_pop(costate, 2); - } - - return handled; -} - - -// Returns true if a handler is registered for the tick event -bool LuaState::handleTickEvent(double dt) -{ - CelestiaCore* appCore = getAppCore(costate, NoErrors); - if (appCore == NULL) - { - return false; - } - - // get the registered event table - getField(costate, LUA_REGISTRYINDEX, EventHandlers); - if (!lua_istable(costate, -1)) - { - cerr << "Missing event handler table"; - lua_pop(costate, 1); - return false; - } - - bool handled = false; - getField(costate, -1, TickHandler); - if (lua_isfunction(costate, -1)) - { - lua_remove(costate, -2); // remove the key event table from the stack - - lua_newtable(costate); - lua_pushstring(costate, "dt"); - lua_pushnumber(costate, dt); // the default key handler accepts the key name as an argument - lua_settable(costate, -3); - - timeout = getTime() + 1.0; - if (lua_pcall(costate, 1, 1, 0) != 0) - { - cerr << "Error while executing tick callback: " << lua_tostring(costate, -1) << "\n"; - } - else - { - handled = lua_toboolean(costate, -1) == 1 ? true : false; - } - lua_pop(costate, 1); // pop the return value - } - else - { - lua_pop(costate, 2); - } - - return handled; -} - - -int LuaState::loadScript(istream& in, const string& streamname) -{ - char buf[4096]; - ReadChunkInfo info; - info.buf = buf; - info.bufSize = sizeof(buf); - info.in = ∈ - - if (streamname != "string") - { - lua_pushstring(state, "celestia-scriptpath"); - lua_pushstring(state, streamname.c_str()); - lua_settable(state, LUA_REGISTRYINDEX); - } - - int status = lua_load(state, readStreamChunk, &info, streamname.c_str()); - if (status != 0) - cout << "Error loading script: " << lua_tostring(state, -1) << '\n'; - - return status; -} - -int LuaState::loadScript(const string& s) -{ - istringstream in(s); - return loadScript(in, "string"); -} - - -// Resume a thread; if the thread completes, the status is set to !alive -int LuaState::resume() -{ - assert(costate != NULL); - if (costate == NULL) - return false; - - lua_State* co = lua_tothread(state, -1); - //assert(co == costate); // co can be NULL after error (top stack is errorstring) - if (co != costate) - return false; - - timeout = getTime() + MaxTimeslice; - int nArgs = resumeLuaThread(state, co, 0); - if (nArgs < 0) - { - alive = false; - - const char* errorMessage = lua_tostring(state, -1); - if (errorMessage == NULL) - errorMessage = "Unknown script error"; - -#if LUA_VER < 0x050100 - // This is a nasty hack required in Lua 5.0, where there's no - // way to distinguish between a thread returning because it completed - // or yielded. Thus, we continue to resume the script until we get - // an error. The 'cannot resume dead coroutine' error appears when - // there were no other errors, and execution terminates normally. - // In Lua 5.1, we can simply check the thread status to find out - // if it's done executing. - if (strcmp(errorMessage, "cannot resume dead coroutine") != 0) -#endif - { - cout << "Error: " << errorMessage << '\n'; - CelestiaCore* appCore = getAppCore(co); - if (appCore != NULL) - { - CelestiaCore::Alerter* alerter = appCore->getAlerter(); - alerter->fatalError(errorMessage); - } - } - - return 1; // just the error string - } - else - { - if (ioMode == Asking) - { - // timeout now is used to first only display warning, and 1s - // later allow response to avoid accidental activation - timeout = getTime() + 1.0; - } - -#if LUA_VER >= 0x050100 - // The thread status is zero if it has terminated normally - if (lua_status(co) == 0) - { - alive = false; - } -#endif - - return nArgs; // arguments from yield - } -} - - -// get current linenumber of script and create -// useful error-message -static void doError(lua_State* l, const char* errorMsg) -{ - lua_Debug debug; - if (lua_getstack(l, 1, &debug)) - { - char buf[1024]; - if (lua_getinfo(l, "l", &debug)) - { - sprintf(buf, "In line %i: %s", debug.currentline, errorMsg); - lua_pushstring(l, buf); - lua_error(l); - } - } - lua_pushstring(l, errorMsg); - lua_error(l); -} - - -bool LuaState::tick(double dt) -{ - // Due to the way CelestiaCore::tick is called (at least for KDE), - // this method may be entered a second time when we show the error-alerter - // Workaround: check if we are alive, return true(!) when we aren't anymore - // this way the script isn't deleted after the second enter, but only - // when we return from the first enter. OMG. - - // better Solution: defer showing the error-alterter to CelestiaCore, using - // getErrorMessage() - if (!isAlive()) - return false; - - if (ioMode == Asking) - { - CelestiaCore* appCore = getAppCore(costate, NoErrors); - if (appCore == NULL) - { - cerr << "ERROR: appCore not found\n"; - return true; - } - lua_pushstring(state, "celestia-savedrenderflags"); - lua_gettable(state, LUA_REGISTRYINDEX); - if (lua_isnil(state, -1)) - { - lua_pushstring(state, "celestia-savedrenderflags"); - int* savedrenderflags = static_cast(lua_newuserdata(state, sizeof(int))); - *savedrenderflags = appCore->getRenderer()->getRenderFlags(); - lua_settable(state, LUA_REGISTRYINDEX); - appCore->getRenderer()->setRenderFlags(0); - } - // now pop result of gettable - lua_pop(state, 1); - - if (getTime() > timeout) - { - appCore->showText("WARNING:\n\nThis script requests permission to read/write files\n" - "and execute external programs. Allowing this can be\n" - "dangerous.\n" - "Do you trust the script and want to allow this?\n\n" - "y = yes, ESC = cancel script, any other key = no", - 0, 0, - -15, 5, 5); - appCore->setTextEnterMode(appCore->getTextEnterMode() | CelestiaCore::KbPassToScript); - } - else - { - appCore->showText("WARNING:\n\nThis script requests permission to read/write files\n" - "and execute external programs. Allowing this can be\n" - "dangerous.\n" - "Do you trust the script and want to allow this?", - 0, 0, - -15, 5, 5); - appCore->setTextEnterMode(appCore->getTextEnterMode() & ~CelestiaCore::KbPassToScript); - } - - return false; - } - - if (dt == 0 || scriptAwakenTime > getTime()) - return false; - - int nArgs = resume(); - if (!isAlive()) - { - // The script is complete - return true; - } - else - { - // The script has returned control to us, but it is not completed. - lua_State* state = getState(); - - // The values on the stack indicate what event will wake up the - // script. For now, we just support wait() - double delay; - if (nArgs == 1 && lua_isnumber(state, -1)) - delay = lua_tonumber(state, -1); - else - delay = 0.0; - scriptAwakenTime = getTime() + delay; - - // Clean up the stack - lua_pop(state, nArgs); - return false; - } -} - - -void LuaState::requestIO() -{ - // the script requested IO, set the mode - // so we display the warning during tick - // and can request keyboard. We can't do this now - // because the script is still active and could - // disable keyboard again. - if (ioMode == NoIO) - { - CelestiaCore* appCore = getAppCore(state, AllErrors); - string policy = appCore->getConfig()->scriptSystemAccessPolicy; - if (policy == "allow") - { -#if LUA_VER >= 0x050100 - openLuaLibrary(costate, LUA_LOADLIBNAME, luaopen_package); - openLuaLibrary(costate, LUA_IOLIBNAME, luaopen_io); - openLuaLibrary(costate, LUA_OSLIBNAME, luaopen_os); -#else - lua_iolibopen(costate); -#endif - //luaopen_io(costate); - ioMode = IOAllowed; - } - else if (policy == "deny") - { - ioMode = IODenied; - } - else - { - ioMode = Asking; - } - } -} - -// Check if the number of arguments on the stack matches -// the allowed range [minArgs, maxArgs]. Cause an error if not. -static void checkArgs(lua_State* l, - int minArgs, int maxArgs, const char* errorMessage) -{ - int argc = lua_gettop(l); - if (argc < minArgs || argc > maxArgs) - { - doError(l, errorMessage); - } -} - - -static astro::CoordinateSystem parseCoordSys(const string& name) -{ - if (compareIgnoringCase(name, "universal") == 0) - return astro::Universal; - else if (compareIgnoringCase(name, "ecliptic") == 0) - return astro::Ecliptical; - else if (compareIgnoringCase(name, "equatorial") == 0) - return astro::Equatorial; - else if (compareIgnoringCase(name, "planetographic") == 0) - return astro::Geographic; - else if (compareIgnoringCase(name, "observer") == 0) - return astro::ObserverLocal; - else if (compareIgnoringCase(name, "lock") == 0) - return astro::PhaseLock; - else if (compareIgnoringCase(name, "chase") == 0) - return astro::Chase; - else - return astro::Universal; -} - - -static Marker::Symbol parseMarkerSymbol(const string& name) -{ - if (compareIgnoringCase(name, "diamond") == 0) - return Marker::Diamond; - else if (compareIgnoringCase(name, "triangle") == 0) - return Marker::Triangle; - else if (compareIgnoringCase(name, "square") == 0) - return Marker::Square; - else if (compareIgnoringCase(name, "filledsquare") == 0) - return Marker::FilledSquare; - else if (compareIgnoringCase(name, "plus") == 0) - return Marker::Plus; - else if (compareIgnoringCase(name, "x") == 0) - return Marker::X; - else if (compareIgnoringCase(name, "leftarrow") == 0) - return Marker::LeftArrow; - else if (compareIgnoringCase(name, "rightarrow") == 0) - return Marker::RightArrow; - else if (compareIgnoringCase(name, "uparrow") == 0) - return Marker::UpArrow; - else if (compareIgnoringCase(name, "downarrow") == 0) - return Marker::DownArrow; - else if (compareIgnoringCase(name, "circle") == 0) - return Marker::Circle; - else if (compareIgnoringCase(name, "disk") == 0) - return Marker::Disk; - else - return Marker::Diamond; -} - - -// Get a pointer to the LuaState-object from the registry: -LuaState* getLuaStateObject(lua_State* l) -{ - int stackSize = lua_gettop(l); - lua_pushstring(l, "celestia-luastate"); - lua_gettable(l, LUA_REGISTRYINDEX); - - if (!lua_islightuserdata(l, -1)) - { - doError(l, "Internal Error: Invalid table entry for LuaState-pointer"); - } - LuaState* luastate_ptr = static_cast(lua_touserdata(l, -1)); - if (luastate_ptr == NULL) - { - doError(l, "Internal Error: Invalid LuaState-pointer"); - } - lua_settop(l, stackSize); - return luastate_ptr; -} - - -// Map the observer to its View. Return NULL if no view exists -// for this observer (anymore). -View* getViewByObserver(CelestiaCore* appCore, Observer* obs) -{ - for (unsigned int i = 0; i < appCore->views.size(); i++) - { - if ((appCore->views[i])->observer == obs) - { - return appCore->views[i]; - } - } - return NULL; -} - -// Fill list with all Observers -void getObservers(CelestiaCore* appCore, vector& list) -{ - for (unsigned int i = 0; i < appCore->views.size(); i++) - { - list.push_back(appCore->views[i]->observer); - } -} - - -// ==================== Helpers ==================== - -// safe wrapper for lua_tostring: fatal errors will terminate script by calling -// lua_error with errorMsg. -static const char* safeGetString(lua_State* l, int index, FatalErrors fatalErrors = AllErrors, - const char* errorMsg = "String argument expected") -{ - if (l == NULL) - { - cerr << "Error: LuaState invalid in safeGetString\n"; - cout << "Error: LuaState invalid in safeGetString\n"; - return NULL; - } - int argc = lua_gettop(l); - if (index < 1 || index > argc) - { - if (fatalErrors & WrongArgc) - { - doError(l, errorMsg); - } - else - return NULL; - } - if (!lua_isstring(l, index)) - { - if (fatalErrors & WrongType) - { - doError(l, errorMsg); - } - else - return NULL; - } - return lua_tostring(l, index); -} - -// safe wrapper for lua_tonumber, c.f. safeGetString -// Non-fatal errors will return defaultValue. -static lua_Number safeGetNumber(lua_State* l, int index, FatalErrors fatalErrors = AllErrors, - const char* errorMsg = "Numeric argument expected", - lua_Number defaultValue = 0.0) -{ - if (l == NULL) - { - cerr << "Error: LuaState invalid in safeGetNumber\n"; - cout << "Error: LuaState invalid in safeGetNumber\n"; - return 0.0; - } - int argc = lua_gettop(l); - if (index < 1 || index > argc) - { - if (fatalErrors & WrongArgc) - { - doError(l, errorMsg); - } - else - return defaultValue; - } - if (!lua_isnumber(l, index)) - { - if (fatalErrors & WrongType) - { - doError(l, errorMsg); - } - else - return defaultValue; - } - return lua_tonumber(l, index); -} - -// Safe wrapper for lua_tobool, c.f. safeGetString -// Non-fata errors will return defaultValue -static bool safeGetBoolean(lua_State* l, - int index, - FatalErrors fatalErrors = AllErrors, - const char* errorMsg = "Boolean argument expected", - bool defaultValue = false) -{ - int argc = lua_gettop(l); - if (index < 1 || index > argc) - { - if (fatalErrors & WrongArgc) - { - doError(l, errorMsg); - } - else - { - return defaultValue; - } - } - - if (!lua_isboolean(l, index)) - { - if (fatalErrors & WrongType) - { - doError(l, errorMsg); - } - else - { - return defaultValue; - } - } - - return lua_toboolean(l, index) != 0; -} - - -// Add a field to the table on top of the stack -static void setTable(lua_State* l, const char* field, lua_Number value) -{ - lua_pushstring(l, field); - lua_pushnumber(l, value); - lua_settable(l, -3); -} - -static void setTable(lua_State* l, const char* field, const char* value) -{ - lua_pushstring(l, field); - lua_pushstring(l, value); - lua_settable(l, -3); -} - -// ==================== forward declarations ==================== - -// must be declared for vector_add: -static UniversalCoord* to_position(lua_State* l, int index); -static int position_new(lua_State* l, const UniversalCoord& uc); -// for frame_getrefobject / gettargetobject -static int object_new(lua_State* l, const Selection& sel); -// for vector_mult -static Quatd* to_rotation(lua_State* l, int index); -static int rotation_new(lua_State* l, const Quatd& qd); - -// ==================== Vector ==================== -static int vector_new(lua_State* l, const Vec3d& v) -{ - Vec3d* v3 = reinterpret_cast(lua_newuserdata(l, sizeof(Vec3d))); - *v3 = v; - SetClass(l, _Vec3); - - return 1; -} - -static Vec3d* to_vector(lua_State* l, int index) -{ - return static_cast(CheckUserData(l, index, _Vec3)); -} - -static Vec3d* this_vector(lua_State* l) -{ - Vec3d* v3 = to_vector(l, 1); - if (v3 == NULL) - { - doError(l, "Bad vector object!"); - } - - return v3; -} - - -static int vector_sub(lua_State* l) -{ - checkArgs(l, 2, 2, "Need two operands for sub"); - Vec3d* op1 = to_vector(l, 1); - Vec3d* op2 = to_vector(l, 2); - if (op1 == NULL || op2 == NULL) - { - doError(l, "Subtraction only defined for two vectors"); - } - else - { - Vec3d result = *op1 - *op2; - vector_new(l, result); - } - return 1; -} - -static int vector_get(lua_State* l) -{ - checkArgs(l, 2, 2, "Invalid access of vector-component"); - Vec3d* v3 = this_vector(l); - string key = safeGetString(l, 2, AllErrors, "Invalid key in vector-access"); - double value = 0.0; - if (key == "x") - value = v3->x; - else if (key == "y") - value = v3->y; - else if (key == "z") - value = v3->z; - else - { - if (lua_getmetatable(l, 1)) - { - lua_pushvalue(l, 2); - lua_rawget(l, -2); - return 1; - } - else - { - doError(l, "Internal error: couldn't get metatable"); - } - } - lua_pushnumber(l, (lua_Number)value); - return 1; -} - -static int vector_set(lua_State* l) -{ - checkArgs(l, 3, 3, "Invalid access of vector-component"); - Vec3d* v3 = this_vector(l); - string key = safeGetString(l, 2, AllErrors, "Invalid key in vector-access"); - double value = safeGetNumber(l, 3, AllErrors, "Vector components must be numbers"); - if (key == "x") - v3->x = value; - else if (key == "y") - v3->y = value; - else if (key == "z") - v3->z = value; - else - { - doError(l, "Invalid key in vector-access"); - } - return 0; -} - -static int vector_getx(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for vector:getx"); - Vec3d* v3 = this_vector(l); - lua_Number x; - x = static_cast(v3->x); - lua_pushnumber(l, x); - - return 1; -} - -static int vector_gety(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for vector:gety"); - Vec3d* v3 = this_vector(l); - lua_Number y; - y = static_cast(v3->y); - lua_pushnumber(l, y); - - return 1; -} - -static int vector_getz(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for vector:getz"); - Vec3d* v3 = this_vector(l); - lua_Number z; - z = static_cast(v3->z); - lua_pushnumber(l, z); - - return 1; -} - -static int vector_normalize(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for vector:normalize"); - Vec3d* v = this_vector(l); - Vec3d vn(*v); - vn.normalize(); - vector_new(l, vn); - return 1; -} - -static int vector_length(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for vector:length"); - Vec3d* v = this_vector(l); - double length = v->length(); - lua_pushnumber(l, (lua_Number)length); - return 1; -} - -static int vector_add(lua_State* l) -{ - checkArgs(l, 2, 2, "Need two operands for addition"); - Vec3d* v1 = NULL; - Vec3d* v2 = NULL; - UniversalCoord* p = NULL; - - if (istype(l, 1, _Vec3) && istype(l, 2, _Vec3)) - { - v1 = to_vector(l, 1); - v2 = to_vector(l, 2); - vector_new(l, *v1 + *v2); - } - else - if (istype(l, 1, _Vec3) && istype(l, 2, _Position)) - { - v1 = to_vector(l, 1); - p = to_position(l, 2); - position_new(l, *p + *v1); - } - else - { - doError(l, "Bad vector addition!"); - } - return 1; -} - -static int vector_mult(lua_State* l) -{ - checkArgs(l, 2, 2, "Need two operands for multiplication"); - Vec3d* v1 = NULL; - Vec3d* v2 = NULL; - Quatd* q = NULL; - lua_Number s = 0.0; - if (istype(l, 1, _Vec3) && istype(l, 2, _Vec3)) - { - v1 = to_vector(l, 1); - v2 = to_vector(l, 2); - lua_pushnumber(l, *v1 * *v2); - } - else - if (istype(l, 1, _Vec3) && lua_isnumber(l, 2)) - { - v1 = to_vector(l, 1); - s = lua_tonumber(l, 2); - vector_new(l, *v1 * s); - } - else - if (istype(l, 1, _Vec3) && istype(l, 2, _Rotation)) - { - v1 = to_vector(l, 1); - q = to_rotation(l, 2); - rotation_new(l, *v1 * *q); - } - else - if (lua_isnumber(l, 1) && istype(l, 2, _Vec3)) - { - s = lua_tonumber(l, 1); - v1 = to_vector(l, 2); - vector_new(l, *v1 * s); - } - else - { - doError(l, "Bad vector multiplication!"); - } - return 1; -} - -static int vector_cross(lua_State* l) -{ - checkArgs(l, 2, 2, "Need two operands for multiplication"); - Vec3d* v1 = NULL; - Vec3d* v2 = NULL; - if (istype(l, 1, _Vec3) && istype(l, 2, _Vec3)) - { - v1 = to_vector(l, 1); - v2 = to_vector(l, 2); - vector_new(l, *v1 ^ *v2); - } - else - { - doError(l, "Bad vector multiplication!"); - } - return 1; - -} - -static int vector_tostring(lua_State* l) -{ - lua_pushstring(l, "[Vector]"); - return 1; -} - -static void CreateVectorMetaTable(lua_State* l) -{ - CreateClassMetatable(l, _Vec3); - - RegisterMethod(l, "__tostring", vector_tostring); - RegisterMethod(l, "__add", vector_add); - RegisterMethod(l, "__sub", vector_sub); - RegisterMethod(l, "__mul", vector_mult); - RegisterMethod(l, "__pow", vector_cross); - RegisterMethod(l, "__index", vector_get); - RegisterMethod(l, "__newindex", vector_set); - RegisterMethod(l, "getx", vector_getx); - RegisterMethod(l, "gety", vector_gety); - RegisterMethod(l, "getz", vector_getz); - RegisterMethod(l, "normalize", vector_normalize); - RegisterMethod(l, "length", vector_length); - - lua_pop(l, 1); // remove metatable from stack -} - -// ==================== Rotation ==================== -static int rotation_new(lua_State* l, const Quatd& qd) -{ - Quatd* q = reinterpret_cast(lua_newuserdata(l, sizeof(Quatd))); - *q = qd; - SetClass(l, _Rotation); - - return 1; -} - -static Quatd* to_rotation(lua_State* l, int index) -{ - return static_cast(CheckUserData(l, index, _Rotation)); -} - -static Quatd* this_rotation(lua_State* l) -{ - Quatd* q = to_rotation(l, 1); - if (q == NULL) - { - doError(l, "Bad rotation object!"); - } - - return q; -} - - -static int rotation_add(lua_State* l) -{ - checkArgs(l, 2, 2, "Need two operands for add"); - Quatd* q1 = to_rotation(l, 1); - Quatd* q2 = to_rotation(l, 2); - if (q1 == NULL || q2 == NULL) - { - doError(l, "Addition only defined for two rotations"); - } - else - { - Quatd result = *q1 + *q2; - rotation_new(l, result); - } - return 1; -} - -static int rotation_mult(lua_State* l) -{ - checkArgs(l, 2, 2, "Need two operands for multiplication"); - Quatd* r1 = NULL; - Quatd* r2 = NULL; - //Vec3d* v = NULL; - lua_Number s = 0.0; - if (istype(l, 1, _Rotation) && istype(l, 2, _Rotation)) - { - r1 = to_rotation(l, 1); - r2 = to_rotation(l, 2); - rotation_new(l, *r1 * *r2); - } - else - if (istype(l, 1, _Rotation) && lua_isnumber(l, 2)) - { - r1 = to_rotation(l, 1); - s = lua_tonumber(l, 2); - rotation_new(l, *r1 * s); - } - else - if (lua_isnumber(l, 1) && istype(l, 2, _Rotation)) - { - s = lua_tonumber(l, 1); - r1 = to_rotation(l, 2); - rotation_new(l, *r1 * s); - } - else - { - doError(l, "Bad rotation multiplication!"); - } - return 1; -} - -static int rotation_imag(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for rotation_imag"); - Quatd* q = this_rotation(l); - vector_new(l, imag(*q)); - return 1; -} - -static int rotation_real(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for rotation_real"); - Quatd* q = this_rotation(l); - lua_pushnumber(l, real(*q)); - return 1; -} - -static int rotation_transform(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected for rotation:transform()"); - Quatd* q = this_rotation(l); - Vec3d* v = to_vector(l, 2); - if (v == NULL) - { - doError(l, "Argument to rotation:transform() must be a vector"); - } - vector_new(l, *v * q->toMatrix3()); - return 1; -} - -static int rotation_setaxisangle(lua_State* l) -{ - checkArgs(l, 3, 3, "Two arguments expected for rotation:setaxisangle()"); - Quatd* q = this_rotation(l); - Vec3d* v = to_vector(l, 2); - if (v == NULL) - { - doError(l, "setaxisangle: first argument must be a vector"); - } - double angle = safeGetNumber(l, 3, AllErrors, "second argument to rotation:setaxisangle must be a number"); - q->setAxisAngle(*v, angle); - return 0; -} - -static int rotation_slerp(lua_State* l) -{ - checkArgs(l, 3, 3, "Two arguments expected for rotation:slerp()"); - Quatd* q1 = this_rotation(l); - Quatd* q2 = to_rotation(l, 2); - if (q2 == NULL) - { - doError(l, "slerp: first argument must be a rotation"); - } - double t = safeGetNumber(l, 3, AllErrors, "second argument to rotation:slerp must be a number"); - rotation_new(l, Quatd::slerp(*q1, *q2, t)); - return 1; -} - -static int rotation_get(lua_State* l) -{ - checkArgs(l, 2, 2, "Invalid access of rotation-component"); - Quatd* q3 = this_rotation(l); - string key = safeGetString(l, 2, AllErrors, "Invalid key in rotation-access"); - double value = 0.0; - if (key == "x") - value = imag(*q3).x; - else if (key == "y") - value = imag(*q3).y; - else if (key == "z") - value = imag(*q3).z; - else if (key == "w") - value = real(*q3); - else - { - if (lua_getmetatable(l, 1)) - { - lua_pushvalue(l, 2); - lua_rawget(l, -2); - return 1; - } - else - { - doError(l, "Internal error: couldn't get metatable"); - } - } - lua_pushnumber(l, (lua_Number)value); - return 1; -} - -static int rotation_set(lua_State* l) -{ - checkArgs(l, 3, 3, "Invalid access of rotation-component"); - Quatd* q3 = this_rotation(l); - string key = safeGetString(l, 2, AllErrors, "Invalid key in rotation-access"); - double value = safeGetNumber(l, 3, AllErrors, "Rotation components must be numbers"); - Vec3d v = imag(*q3); - double w = real(*q3); - if (key == "x") - v.x = value; - else if (key == "y") - v.y = value; - else if (key == "z") - v.z = value; - else if (key == "w") - w = value; - else - { - doError(l, "Invalid key in rotation-access"); - } - *q3 = Quatd(w, v); - return 0; -} - -static int rotation_tostring(lua_State* l) -{ - lua_pushstring(l, "[Rotation]"); - return 1; -} - -static void CreateRotationMetaTable(lua_State* l) -{ - CreateClassMetatable(l, _Rotation); - - RegisterMethod(l, "real", rotation_real); - RegisterMethod(l, "imag", rotation_imag); - RegisterMethod(l, "transform", rotation_transform); - RegisterMethod(l, "setaxisangle", rotation_setaxisangle); - RegisterMethod(l, "slerp", rotation_slerp); - RegisterMethod(l, "__tostring", rotation_tostring); - RegisterMethod(l, "__add", rotation_add); - RegisterMethod(l, "__mul", rotation_mult); - RegisterMethod(l, "__index", rotation_get); - RegisterMethod(l, "__newindex", rotation_set); - - lua_pop(l, 1); // remove metatable from stack -} - -// ==================== Position ==================== -// a 128-bit per component universal coordinate -static int position_new(lua_State* l, const UniversalCoord& uc) -{ - UniversalCoord* ud = reinterpret_cast(lua_newuserdata(l, sizeof(UniversalCoord))); - *ud = uc; - - SetClass(l, _Position); - - return 1; -} - -static UniversalCoord* to_position(lua_State* l, int index) -{ - return static_cast(CheckUserData(l, index, _Position)); -} - -static UniversalCoord* this_position(lua_State* l) -{ - UniversalCoord* uc = to_position(l, 1); - if (uc == NULL) - { - doError(l, "Bad position object!"); - } - - return uc; -} - - -static int position_get(lua_State* l) -{ - checkArgs(l, 2, 2, "Invalid access of position-component"); - UniversalCoord* uc = this_position(l); - string key = safeGetString(l, 2, AllErrors, "Invalid key in position-access"); - double value = 0.0; - if (key == "x") - value = uc->x; - else if (key == "y") - value = uc->y; - else if (key == "z") - value = uc->z; - else - { - if (lua_getmetatable(l, 1)) - { - lua_pushvalue(l, 2); - lua_rawget(l, -2); - return 1; - } - else - { - doError(l, "Internal error: couldn't get metatable"); - } - } - lua_pushnumber(l, (lua_Number)value); - return 1; -} - -static int position_set(lua_State* l) -{ - checkArgs(l, 3, 3, "Invalid access of position-component"); - UniversalCoord* uc = this_position(l); - string key = safeGetString(l, 2, AllErrors, "Invalid key in position-access"); - double value = safeGetNumber(l, 3, AllErrors, "Position components must be numbers"); - if (key == "x") - uc->x = value; - else if (key == "y") - uc->y = value; - else if (key == "z") - uc->z = value; - else - { - doError(l, "Invalid key in position-access"); - } - return 0; -} - -static int position_getx(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for position:getx()"); - - UniversalCoord* uc = this_position(l); - lua_Number x; - x = uc->x; - lua_pushnumber(l, x); - - return 1; -} - -static int position_gety(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for position:gety()"); - - UniversalCoord* uc = this_position(l); - lua_Number y; - y = uc->y; - lua_pushnumber(l, y); - - return 1; -} - -static int position_getz(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for position:getz()"); - - UniversalCoord* uc = this_position(l); - lua_Number z; - z = uc->z; - lua_pushnumber(l, z); - - return 1; -} - -static int position_vectorto(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected to position:vectorto"); - - UniversalCoord* uc = this_position(l); - UniversalCoord* uc2 = to_position(l, 2); - - if (uc2 == NULL) - { - doError(l, "Argument to position:vectorto must be a position"); - } - Vec3d diff = *uc2 - *uc; - vector_new(l, diff); - return 1; -} - -static int position_orientationto(lua_State* l) -{ - checkArgs(l, 3, 3, "Two arguments expected for position:orientationto"); - - UniversalCoord* src = this_position(l); - UniversalCoord* target = to_position(l, 2); - - if (target == NULL) - { - doError(l, "First argument to position:orientationto must be a position"); - } - - Vec3d* upd = to_vector(l, 3); - if (upd == NULL) - { - doError(l, "Second argument to position:orientationto must be a vector"); - } - - Vec3d src2target = *target - *src; - src2target.normalize(); - Vec3d v = src2target ^ *upd; - v.normalize(); - Vec3d u = v ^ src2target; - Quatd qd = Quatd(Mat3d(v, u, -src2target)); - rotation_new(l, qd); - - return 1; -} - -static int position_tostring(lua_State* l) -{ - // TODO: print out the coordinate as it would appear in a cel:// URL - lua_pushstring(l, "[Position]"); - - return 1; -} - -static int position_distanceto(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected to position:distanceto()"); - - UniversalCoord* uc = this_position(l); - UniversalCoord* uc2 = to_position(l, 2); - if (uc2 == NULL) - { - doError(l, "Position expected as argument to position:distanceto"); - } - - Vec3d v = *uc2 - *uc; - lua_pushnumber(l, astro::microLightYearsToKilometers(v.length())); - - return 1; -} - -static int position_add(lua_State* l) -{ - checkArgs(l, 2, 2, "Need two operands for addition"); - UniversalCoord* p1 = NULL; - UniversalCoord* p2 = NULL; - Vec3d* v2 = NULL; - - if (istype(l, 1, _Position) && istype(l, 2, _Position)) - { - p1 = to_position(l, 1); - p2 = to_position(l, 2); - // this is not very intuitive, as p1-p2 is a vector - position_new(l, *p1 + *p2); - } - else - if (istype(l, 1, _Position) && istype(l, 2, _Vec3)) - { - p1 = to_position(l, 1); - v2 = to_vector(l, 2); - position_new(l, *p1 + *v2); - } - else - { - doError(l, "Bad position addition!"); - } - return 1; -} - -static int position_sub(lua_State* l) -{ - checkArgs(l, 2, 2, "Need two operands for subtraction"); - UniversalCoord* p1 = NULL; - UniversalCoord* p2 = NULL; - Vec3d* v2 = NULL; - - if (istype(l, 1, _Position) && istype(l, 2, _Position)) - { - p1 = to_position(l, 1); - p2 = to_position(l, 2); - vector_new(l, *p1 - *p2); - } - else - if (istype(l, 1, _Position) && istype(l, 2, _Vec3)) - { - p1 = to_position(l, 1); - v2 = to_vector(l, 2); - position_new(l, *p1 - *v2); - } - else - { - doError(l, "Bad position subtraction!"); - } - return 1; -} - -static int position_addvector(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected to position:addvector()"); - UniversalCoord* uc = this_position(l); - Vec3d* v3d = to_vector(l, 2); - if (v3d == NULL) - { - doError(l, "Vector expected as argument to position:addvector"); - } - else - if (uc != NULL && v3d != NULL) - { - UniversalCoord ucnew = *uc + *v3d; - position_new(l, ucnew); - } - return 1; -} - -static void CreatePositionMetaTable(lua_State* l) -{ - CreateClassMetatable(l, _Position); - - RegisterMethod(l, "__tostring", position_tostring); - RegisterMethod(l, "distanceto", position_distanceto); - RegisterMethod(l, "vectorto", position_vectorto); - RegisterMethod(l, "orientationto", position_orientationto); - RegisterMethod(l, "addvector", position_addvector); - RegisterMethod(l, "__add", position_add); - RegisterMethod(l, "__sub", position_sub); - RegisterMethod(l, "__index", position_get); - RegisterMethod(l, "__newindex", position_set); - RegisterMethod(l, "getx", position_getx); - RegisterMethod(l, "gety", position_gety); - RegisterMethod(l, "getz", position_getz); - - lua_pop(l, 1); // remove metatable from stack -} - -// ==================== FrameOfReference ==================== -static int frame_new(lua_State* l, const FrameOfReference& f) -{ - FrameOfReference* ud = reinterpret_cast(lua_newuserdata(l, sizeof(FrameOfReference))); - *ud = f; - - SetClass(l, _Frame); - - return 1; -} - -static FrameOfReference* to_frame(lua_State* l, int index) -{ - return static_cast(CheckUserData(l, index, _Frame)); -} - -static FrameOfReference* this_frame(lua_State* l) -{ - FrameOfReference* f = to_frame(l, 1); - if (f == NULL) - { - doError(l, "Bad frame object!"); - } - - return f; -} - - -// Convert from frame coordinates to universal. -static int frame_from(lua_State* l) -{ - checkArgs(l, 2, 3, "Two or three arguments required for frame:from"); - - FrameOfReference* frame = this_frame(l); - CelestiaCore* appCore = getAppCore(l, AllErrors); - - RigidTransform rt; - - UniversalCoord* uc = NULL; - Quatd* q = NULL; - double jd = 0.0; - - if (istype(l, 2, _Position)) - { - uc = to_position(l, 2); - } - else if (istype(l, 2, _Rotation)) - { - q = to_rotation(l, 2); - } - if (uc == NULL && q == NULL) - { - doError(l, "Position or rotation expected as second argument to frame:from()"); - } - - jd = safeGetNumber(l, 3, WrongType, "Second arg to frame:from must be a number", appCore->getSimulation()->getTime()); - - if (uc != NULL) - { - rt.translation = *uc; - rt = frame->toUniversal(rt, jd); - position_new(l, rt.translation); - } - else - { - rt.rotation = *q; - rt = frame->toUniversal(rt, jd); - rotation_new(l, rt.rotation); - } - - return 1; -} - -// Convert from universal to frame coordinates. -static int frame_to(lua_State* l) -{ - checkArgs(l, 2, 3, "Two or three arguments required for frame:to"); - - FrameOfReference* frame = this_frame(l); - CelestiaCore* appCore = getAppCore(l, AllErrors); - - RigidTransform rt; - - UniversalCoord* uc = NULL; - Quatd* q = NULL; - double jd = 0.0; - - if (istype(l, 2, _Position)) - { - uc = to_position(l, 2); - } - else - if (istype(l, 2, _Rotation)) - { - q = to_rotation(l, 2); - } - - if (uc == NULL && q == NULL) - { - doError(l, "Position or rotation expected as second argument to frame:to()"); - } - - jd = safeGetNumber(l, 3, WrongType, "Second arg to frame:to must be a number", appCore->getSimulation()->getTime()); - - if (uc != NULL) - { - rt.translation = *uc; - rt = frame->fromUniversal(rt, jd); - position_new(l, rt.translation); - } - else - { - rt.rotation = *q; - rt = frame->fromUniversal(rt, jd); - rotation_new(l, rt.rotation); - } - - return 1; -} - -static int frame_getrefobject(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for frame:getrefobject()"); - - FrameOfReference* frame = this_frame(l); - if (frame->refObject.getType() == Selection::Type_Nil) - { - lua_pushnil(l); - } - else - { - object_new(l, frame->refObject); - } - return 1; -} - -static int frame_gettargetobject(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for frame:gettarget()"); - - FrameOfReference* frame = this_frame(l); - if (frame->targetObject.getType() == Selection::Type_Nil) - { - lua_pushnil(l); - } - else - { - object_new(l, frame->targetObject); - } - return 1; -} - -static int frame_getcoordinatesystem(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for frame:getcoordinatesystem()"); - - FrameOfReference* frame = this_frame(l); - string coordsys; - switch (frame->coordSys) - { - case astro::Universal: - coordsys = "universal"; break; - case astro::Ecliptical: - coordsys = "ecliptic"; break; - case astro::Equatorial: - coordsys = "equatorial"; break; - case astro::Geographic: - coordsys = "planetographic"; break; - case astro::ObserverLocal: - coordsys = "observer"; break; - case astro::PhaseLock: - coordsys = "lock"; break; - case astro::Chase: - coordsys = "chase"; break; - default: - coordsys = "invalid"; - } - lua_pushstring(l, coordsys.c_str()); - return 1; -} - -static int frame_tostring(lua_State* l) -{ - // TODO: print out the actual information about the frame - lua_pushstring(l, "[Frame]"); - - return 1; -} - -static void CreateFrameMetaTable(lua_State* l) -{ - CreateClassMetatable(l, _Frame); - - RegisterMethod(l, "__tostring", frame_tostring); - RegisterMethod(l, "to", frame_to); - RegisterMethod(l, "from", frame_from); - RegisterMethod(l, "getcoordinatesystem", frame_getcoordinatesystem); - RegisterMethod(l, "getrefobject", frame_getrefobject); - RegisterMethod(l, "gettargetobject", frame_gettargetobject); - - lua_pop(l, 1); // remove metatable from stack -} - -// ==================== Object ==================== -// star, planet, or deep-sky object -static int object_new(lua_State* l, const Selection& sel) -{ - Selection* ud = reinterpret_cast(lua_newuserdata(l, sizeof(Selection))); - *ud = sel; - - SetClass(l, _Object); - - return 1; -} - -static Selection* to_object(lua_State* l, int index) -{ - return static_cast(CheckUserData(l, index, _Object)); -} - -static Selection* this_object(lua_State* l) -{ - Selection* sel = to_object(l, 1); - if (sel == NULL) - { - doError(l, "Bad position object!"); - } - - return sel; -} - - -static int object_tostring(lua_State* l) -{ - lua_pushstring(l, "[Object]"); - - return 1; -} - - -// Return true if the object is visible, false if not. -static int object_visible(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected to function object:visible"); - - Selection* sel = this_object(l); - lua_pushboolean(l, sel->isVisible()); - - return 1; -} - - -// Set the object visibility flag. -static int object_setvisible(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected to object:setvisible()"); - - Selection* sel = this_object(l); - bool visible = safeGetBoolean(l, 2, AllErrors, "Argument to object:setvisible() must be a boolean"); - if (sel->body() != NULL) - { - sel->body()->setVisible(visible); - } - - return 0; -} - - -static int object_setorbitcolor(lua_State* l) -{ - checkArgs(l, 4, 4, "Red, green, and blue color values exepected for object:setorbitcolor()"); - - Selection* sel = this_object(l); - float r = (float) safeGetNumber(l, 2, WrongType, "Argument 1 to object:setorbitcolor() must be a number", 0.0); - float g = (float) safeGetNumber(l, 3, WrongType, "Argument 2 to object:setorbitcolor() must be a number", 0.0); - float b = (float) safeGetNumber(l, 4, WrongType, "Argument 3 to object:setorbitcolor() must be a number", 0.0); - Color orbitColor(r, g, b); - - if (sel->body() != NULL) - { - sel->body()->setOrbitColor(orbitColor); - } - - return 0; -} - - -static int object_orbitcoloroverridden(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected to object:orbitcoloroverridden"); - - bool isOverridden = false; - Selection* sel = this_object(l); - if (sel->body() != NULL) - { - isOverridden = sel->body()->isOrbitColorOverridden(); - } - - lua_pushboolean(l, isOverridden); - - return 1; -} - - -static int object_setorbitcoloroverridden(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected to object:setorbitcoloroverridden"); - - Selection* sel = this_object(l); - bool override = safeGetBoolean(l, 2, AllErrors, "Argument to object:setorbitcoloroverridden() must be a boolean"); - - if (sel->body() != NULL) - { - sel->body()->setOrbitColorOverridden(override); - } - - return 0; -} - - -static int object_orbitvisibility(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected to object:orbitvisibility"); - - Body::VisibilityPolicy visibility = Body::UseClassVisibility; - - Selection* sel = this_object(l); - if (sel->body() != NULL) - { - visibility = sel->body()->getOrbitVisibility(); - } - - char* s = "normal"; - if (visibility == Body::AlwaysVisible) - s = "always"; - else if (visibility == Body::NeverVisible) - s = "never"; - - lua_pushstring(l, s); - - return 1; -} - - -static int object_setorbitvisibility(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected to object:setorbitcoloroverridden"); - - if (!lua_isstring(l, 2)) - { - doError(l, "First argument to object:setorbitvisibility() must be a string"); - } - - Selection* sel = this_object(l); - - string key; - key = lua_tostring(l, 2); - - if (OrbitVisibilityMap.count(key) == 0) - { - cerr << "Unknown visibility policy: " << key << endl; - } - else - { - Body::VisibilityPolicy visibility = static_cast(OrbitVisibilityMap[key]); - - if (sel->body() != NULL) - { - sel->body()->setOrbitVisibility(visibility); - } - } - - return 0; -} - - -static int object_radius(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected to function object:radius"); - - Selection* sel = this_object(l); - lua_pushnumber(l, sel->radius()); - - return 1; -} - -static int object_setradius(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected to object:setradius()"); - - Selection* sel = this_object(l); - if (sel->body() != NULL) - { - Body* body = sel->body(); - float iradius = body->getRadius(); - double radius = safeGetNumber(l, 2, AllErrors, "Argument to object:setradius() must be a number"); - if ((radius > 0)) - { - body->setSemiAxes(body->getSemiAxes() * ((float) radius / iradius)); - } - - if (body->getRings() != NULL) - { - RingSystem rings(0.0f, 0.0f); - rings = *body->getRings(); - float inner = rings.innerRadius; - float outer = rings.outerRadius; - rings.innerRadius = inner * (float) radius / iradius; - rings.outerRadius = outer * (float) radius / iradius; - body->setRings(rings); - } - } - - return 0; -} - -static int object_type(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected to function object:type"); - - Selection* sel = this_object(l); - const char* tname = "unknown"; - switch (sel->getType()) - { - case Selection::Type_Body: - { - int cl = sel->body()->getClassification(); - switch (cl) - { - case Body::Planet : tname = "planet"; break; - case Body::Moon : tname = "moon"; break; - case Body::Asteroid : tname = "asteroid"; break; - case Body::Comet : tname = "comet"; break; - case Body::Spacecraft : tname = "spacecraft"; break; - case Body::Invisible : tname = "invisible"; break; - } - } - break; - - case Selection::Type_Star: - tname = "star"; - break; - - case Selection::Type_DeepSky: - tname = sel->deepsky()->getObjTypeName(); - break; - - case Selection::Type_Location: - tname = "location"; - break; - - case Selection::Type_Nil: - tname = "null"; - break; - } - - lua_pushstring(l, tname); - - return 1; -} - -static int object_name(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected to function object:name"); - - Selection* sel = this_object(l); - switch (sel->getType()) - { - case Selection::Type_Body: - lua_pushstring(l, sel->body()->getName().c_str()); - break; - case Selection::Type_DeepSky: - lua_pushstring(l, getAppCore(l, AllErrors)->getSimulation()->getUniverse() - ->getDSOCatalog()->getDSOName(sel->deepsky()).c_str()); - break; - case Selection::Type_Star: - lua_pushstring(l, getAppCore(l, AllErrors)->getSimulation()->getUniverse() - ->getStarCatalog()->getStarName(*(sel->star())).c_str()); - break; - case Selection::Type_Location: - lua_pushstring(l, sel->location()->getName().c_str()); - break; - default: - lua_pushstring(l, "?"); - break; - } - - return 1; -} - -static int object_localname(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected to function object:localname"); - - Selection* sel = this_object(l); - switch (sel->getType()) - { - case Selection::Type_Body: - lua_pushstring(l, sel->body()->getName(true).c_str()); - break; - default: - lua_pushstring(l, "?"); - break; - } - - return 1; -} - -static int object_spectraltype(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected to function object:spectraltype"); - - Selection* sel = this_object(l); - if (sel->star() != NULL) - { - char buf[16]; - strncpy(buf, sel->star()->getSpectralType(), sizeof buf); - buf[sizeof(buf) - 1] = '\0'; // make sure it's zero terminate - lua_pushstring(l, buf); - } - else - { - lua_pushnil(l); - } - - return 1; -} - -static int object_getinfo(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected to function object:getinfo"); - - lua_newtable(l); - - Selection* sel = this_object(l); - if (sel->star() != NULL) - { - Star* star = sel->star(); - setTable(l, "type", "star"); - setTable(l, "name", getAppCore(l, AllErrors)->getSimulation()->getUniverse() - ->getStarCatalog()->getStarName(*(sel->star())).c_str()); - setTable(l, "catalogNumber", star->getCatalogNumber()); - setTable(l, "stellarClass", star->getSpectralType()); - setTable(l, "absoluteMagnitude", (lua_Number)star->getAbsoluteMagnitude()); - setTable(l, "luminosity", (lua_Number)star->getLuminosity()); - setTable(l, "radius", (lua_Number)star->getRadius()); - setTable(l, "temperature", (lua_Number)star->getTemperature()); - setTable(l, "rotationPeriod", (lua_Number)star->getRotationModel()->getPeriod()); - setTable(l, "bolometricMagnitude", (lua_Number)star->getBolometricMagnitude()); - - if (star->getOrbitBarycenter() != NULL) - { - Selection parent((Star*)(star->getOrbitBarycenter())); - lua_pushstring(l, "parent"); - object_new(l, parent); - lua_settable(l, -3); - } - } - else if (sel->body() != NULL) - { - Body* body = sel->body(); - const char* tname = "unknown"; - switch (body->getClassification()) - { - case Body::Planet : tname = "planet"; break; - case Body::Moon : tname = "moon"; break; - case Body::Asteroid : tname = "asteroid"; break; - case Body::Comet : tname = "comet"; break; - case Body::Spacecraft : tname = "spacecraft"; break; - case Body::Invisible : tname = "invisible"; break; - } - - setTable(l, "type", tname); - setTable(l, "name", body->getName().c_str()); - setTable(l, "mass", (lua_Number)body->getMass()); - setTable(l, "albedo", (lua_Number)body->getAlbedo()); - setTable(l, "infoURL", body->getInfoURL().c_str()); - setTable(l, "radius", (lua_Number)body->getRadius()); - - // TODO: add method to return semiaxes - Vec3f semiAxes = body->getSemiAxes(); - // Note: oblateness is an obsolete field, replaced by semiaxes; - // it's only here for backward compatibility. - float polarRadius = semiAxes.y; - float eqRadius = max(semiAxes.x, semiAxes.z); - setTable(l, "oblateness", (eqRadius - polarRadius) / eqRadius); - - double lifespanStart, lifespanEnd; - body->getLifespan(lifespanStart, lifespanEnd); - setTable(l, "lifespanStart", (lua_Number)lifespanStart); - setTable(l, "lifespanEnd", (lua_Number)lifespanEnd); - // TODO: atmosphere, surfaces ? - - PlanetarySystem* system = body->getSystem(); - if (system->getPrimaryBody() != NULL) - { - Selection parent(system->getPrimaryBody()); - lua_pushstring(l, "parent"); - object_new(l, parent); - lua_settable(l, -3); - } - else - { - Selection parent(system->getStar()); - lua_pushstring(l, "parent"); - object_new(l, parent); - lua_settable(l, -3); - } - - lua_pushstring(l, "hasRings"); - lua_pushboolean(l, body->getRings() != NULL); - lua_settable(l, -3); - - const RotationModel* rm = body->getRotationModel(); - setTable(l, "rotationPeriod", (double) rm->getPeriod()); - - Orbit* orbit = body->getOrbit(); - setTable(l, "orbitPeriod", orbit->getPeriod()); - Atmosphere* atmosphere = body->getAtmosphere(); - if (atmosphere != NULL) - { - setTable(l, "atmosphereHeight", (double)atmosphere->height); - setTable(l, "atmosphereCloudHeight", (double)atmosphere->cloudHeight); - setTable(l, "atmosphereCloudSpeed", (double)atmosphere->cloudSpeed); - } - } - else if (sel->deepsky() != NULL) - { - DeepSkyObject* deepsky = sel->deepsky(); - const char* objTypeName = deepsky->getObjTypeName(); - setTable(l, "type", objTypeName); - - setTable(l, "name", getAppCore(l, AllErrors)->getSimulation()->getUniverse() - ->getDSOCatalog()->getDSOName(deepsky).c_str()); - setTable(l, "catalogNumber", deepsky->getCatalogNumber()); - - if (objTypeName == "galaxy") - setTable(l, "hubbleType", deepsky->getType()); - - setTable(l, "absoluteMagnitude", (lua_Number)deepsky->getAbsoluteMagnitude()); - setTable(l, "radius", (lua_Number)deepsky->getRadius()); - } - else if (sel->location() != NULL) - { - setTable(l, "type", "location"); - Location* location = sel->location(); - setTable(l, "name", location->getName().c_str()); - setTable(l, "size", (lua_Number)location->getSize()); - setTable(l, "importance", (lua_Number)location->getImportance()); - setTable(l, "infoURL", location->getInfoURL().c_str()); - - uint32 featureType = location->getFeatureType(); - string featureName("Unknown"); - for (FlagMap::const_iterator it = LocationFlagMap.begin(); it != LocationFlagMap.end(); it++) - { - if (it->second == featureType) - { - featureName = it->first; - break; - } - } - setTable(l, "featureType", featureName.c_str()); - - Body* parent = location->getParentBody(); - if (parent != NULL) - { - Selection selection(parent); - lua_pushstring(l, "parent"); - object_new(l, selection); - lua_settable(l, -3); - } - } - else - { - setTable(l, "type", "null"); - } - return 1; -} - -static int object_absmag(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected to function object:absmag"); - - Selection* sel = this_object(l); - if (sel->star() != NULL) - lua_pushnumber(l, sel->star()->getAbsoluteMagnitude()); - else - lua_pushnil(l); - - return 1; -} - -static int object_mark(lua_State* l) -{ - checkArgs(l, 1, 6, "Need 0 to 5 arguments for object:mark"); - - Selection* sel = this_object(l); - CelestiaCore* appCore = getAppCore(l, AllErrors); - - Color markColor(0.0f, 1.0f, 0.0f); - const char* colorString = safeGetString(l, 2, WrongType, "First argument to object:mark must be a string"); - if (colorString != NULL) - Color::parse(colorString, markColor); - - Marker::Symbol markSymbol = Marker::Diamond; - const char* markerString = safeGetString(l, 3, WrongType, "Second argument to object:mark must be a string"); - if (markerString != NULL) - markSymbol = parseMarkerSymbol(markerString); - - float markSize = (float)safeGetNumber(l, 4, WrongType, "Third arg to object:mark must be a number", 10.0); - if (markSize < 1.0f) - markSize = 1.0f; - else if (markSize > 10000.0f) - markSize = 10000.0f; - - float markAlpha = (float)safeGetNumber(l, 5, WrongType, "Fourth arg to object:mark must be a number", 0.9); - if (markAlpha < 0.0f) - markAlpha = 0.0f; - else if (markAlpha > 1.0f) - markAlpha = 1.0f; - - Color markColorAlpha(0.0f, 1.0f, 0.0f, 0.9f); - markColorAlpha = Color::Color(markColor, markAlpha); - - const char* markLabel = safeGetString(l, 6, WrongType, "Fifth argument to object:mark must be a string"); - if (markLabel == NULL) - markLabel = ""; - - Simulation* sim = appCore->getSimulation(); - sim->getUniverse()->markObject(*sel, markSize, - markColorAlpha, markSymbol, 1, markLabel); - - return 0; -} - -static int object_unmark(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected to function object:unmark"); - - Selection* sel = this_object(l); - CelestiaCore* appCore = getAppCore(l, AllErrors); - - Simulation* sim = appCore->getSimulation(); - sim->getUniverse()->unmarkObject(*sel, 1); - - return 0; -} - -// Return the object's current position. A time argument is optional; -// if not provided, the current master simulation time is used. -static int object_getposition(lua_State* l) -{ - checkArgs(l, 1, 2, "Expected no or one argument to object:getposition"); - - Selection* sel = this_object(l); - CelestiaCore* appCore = getAppCore(l, AllErrors); - - double t = safeGetNumber(l, 2, WrongType, "Time expected as argument to object:getposition", - appCore->getSimulation()->getTime()); - position_new(l, sel->getPosition(t)); - - return 1; -} - -static int object_getchildren(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for object:getchildren()"); - - Selection* sel = this_object(l); - CelestiaCore* appCore = getAppCore(l, AllErrors); - - Simulation* sim = appCore->getSimulation(); - - lua_newtable(l); - if (sel->star() != NULL) - { - SolarSystemCatalog* solarSystemCatalog = sim->getUniverse()->getSolarSystemCatalog(); - SolarSystemCatalog::iterator iter = solarSystemCatalog->find(sel->star()->getCatalogNumber()); - if (iter != solarSystemCatalog->end()) - { - SolarSystem* solarSys = iter->second; - for (int i = 0; i < solarSys->getPlanets()->getSystemSize(); i++) - { - Body* body = solarSys->getPlanets()->getBody(i); - Selection satSel(body); - object_new(l, satSel); - lua_rawseti(l, -2, i + 1); - } - } - } - else if (sel->body() != NULL) - { - const PlanetarySystem* satellites = sel->body()->getSatellites(); - if (satellites != NULL && satellites->getSystemSize() != 0) - { - for (int i = 0; i < satellites->getSystemSize(); i++) - { - Body* body = satellites->getBody(i); - Selection satSel(body); - object_new(l, satSel); - lua_rawseti(l, -2, i + 1); - } - } - } - - return 1; -} - -static int object_preloadtexture(lua_State* l) -{ - checkArgs(l, 1, 1, "No argument expected to object:preloadtexture"); - CelestiaCore* appCore = getAppCore(l, AllErrors); - - Renderer* renderer = appCore->getRenderer(); - Selection* sel = this_object(l); - if (sel->body() != NULL && renderer != NULL) - { - LuaState* luastate = getLuaStateObject(l); - // make sure we don't timeout because of texture-loading: - double timeToTimeout = luastate->timeout - luastate->getTime(); - - renderer->loadTextures(sel->body()); - - // no matter how long it really took, make it look like 0.1s: - luastate->timeout = luastate->getTime() + timeToTimeout - 0.1; - } - return 0; -} - -static void CreateObjectMetaTable(lua_State* l) -{ - CreateClassMetatable(l, _Object); - - RegisterMethod(l, "__tostring", object_tostring); - RegisterMethod(l, "visible", object_visible); - RegisterMethod(l, "setvisible", object_setvisible); - RegisterMethod(l, "orbitcoloroverridden", object_orbitcoloroverridden); - RegisterMethod(l, "setorbitcoloroverridden", object_setorbitcoloroverridden); - RegisterMethod(l, "setorbitcolor", object_setorbitcolor); - RegisterMethod(l, "orbitvisibility", object_orbitvisibility); - RegisterMethod(l, "setorbitvisibility", object_setorbitvisibility); - RegisterMethod(l, "radius", object_radius); - RegisterMethod(l, "setradius", object_setradius); - RegisterMethod(l, "type", object_type); - RegisterMethod(l, "spectraltype", object_spectraltype); - RegisterMethod(l, "getinfo", object_getinfo); - RegisterMethod(l, "absmag", object_absmag); - RegisterMethod(l, "name", object_name); - RegisterMethod(l, "localname", object_localname); - RegisterMethod(l, "mark", object_mark); - RegisterMethod(l, "unmark", object_unmark); - RegisterMethod(l, "getposition", object_getposition); - RegisterMethod(l, "getchildren", object_getchildren); - RegisterMethod(l, "preloadtexture", object_preloadtexture); - - lua_pop(l, 1); // pop metatable off the stack -} - -// ==================== Observer ==================== -static int observer_new(lua_State* l, Observer* o) -{ - Observer** ud = static_cast(lua_newuserdata(l, sizeof(Observer*))); - *ud = o; - - SetClass(l, _Observer); - - return 1; -} - -static Observer* to_observer(lua_State* l, int index) -{ - Observer** o = static_cast(lua_touserdata(l, index)); - CelestiaCore* appCore = getAppCore(l, AllErrors); - - // Check if pointer is still valid, i.e. is used by a view: - if (o != NULL && getViewByObserver(appCore, *o) != NULL) - { - return *o; - } - return NULL; -} - -static Observer* this_observer(lua_State* l) -{ - Observer* obs = to_observer(l, 1); - if (obs == NULL) - { - doError(l, "Bad observer object (maybe tried to access a deleted view?)!"); - } - - return obs; -} - - -static int observer_isvalid(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for observer:isvalid()"); - lua_pushboolean(l, to_observer(l, 1) != NULL); - return 1; -} - -static int observer_tostring(lua_State* l) -{ - lua_pushstring(l, "[Observer]"); - - return 1; -} - -static int observer_setposition(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument required for setpos"); - - Observer* o = this_observer(l); - - UniversalCoord* uc = to_position(l,2); - if (uc == NULL) - { - doError(l, "Argument to observer:setposition must be a position"); - } - o->setPosition(*uc); - return 0; -} - -static int observer_setorientation(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument required for setorientation"); - - Observer* o = this_observer(l); - - Quatd* q = to_rotation(l,2); - if (q == NULL) - { - doError(l, "Argument to observer:setorientation must be a rotation"); - } - o->setOrientation(*q); - return 0; -} - -static int observer_getorientation(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected to observer:getorientation()"); - - Observer* o = this_observer(l); - Quatf q = o->getOrientation(); - rotation_new(l, Quatd(q.w, q.x, q.y, q.z)); - - return 1; -} - -static int observer_rotate(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument required for rotate"); - - Observer* o = this_observer(l); - - Quatd* q = to_rotation(l,2); - if (q == NULL) - { - doError(l, "Argument to observer:setpos must be a rotation"); - } - Quatf qf((float) q->w, (float) q->x, (float) q->y, (float) q->z); - o->rotate(qf); - return 0; -} - -static int observer_lookat(lua_State* l) -{ - checkArgs(l, 3, 4, "Two or three arguments required for lookat"); - int argc = lua_gettop(l); - - Observer* o = this_observer(l); - - UniversalCoord* from = NULL; - UniversalCoord* to = NULL; - Vec3d* upd = NULL; - if (argc == 3) - { - to = to_position(l, 2); - upd = to_vector(l, 3); - if (to == NULL) - { - doError(l, "Argument 1 (of 2) to observer:lookat must be of type position"); - } - } - else - if (argc == 4) - { - from = to_position(l, 2); - to = to_position(l, 3); - upd = to_vector(l, 4); - - if (to == NULL || from == NULL) - { - doError(l, "Argument 1 and 2 (of 3) to observer:lookat must be of type position"); - } - } - if (upd == NULL) - { - doError(l, "Last argument to observer:lookat must be of type vector"); - } - Vec3d nd; - if (from == NULL) - { - nd = (*to) - o->getPosition(); - } - else - { - nd = (*to) - (*from); - } - // need Vec3f instead: - Vec3f up = Vec3f((float) upd->x, (float) upd->y, (float) upd->z); - Vec3f n = Vec3f((float) nd.x, (float) nd.y, (float) nd.z); - - n.normalize(); - Vec3f v = n ^ up; - v.normalize(); - Vec3f u = v ^ n; - Quatf qf = Quatf(Mat3f(v, u, -n)); - o->setOrientation(qf); - return 0; -} - -static int observer_gototable(lua_State* l) -{ - checkArgs(l, 2, 2, "Expected one table as argument to goto"); - - Observer* o = this_observer(l); - if (!lua_istable(l, 2)) - { - lua_pushstring(l, "Argument to goto must be a table"); - } - - Observer::JourneyParams jparams; - jparams.duration = 5.0; - jparams.from = o->getSituation().translation; - jparams.to = o->getSituation().translation; - jparams.initialOrientation = o->getOrientation(); - jparams.finalOrientation = o->getOrientation(); - jparams.startInterpolation = 0.25; - jparams.endInterpolation = 0.75; - jparams.accelTime = 0.5; - jparams.traj = Observer::Linear; - - lua_pushstring(l, "duration"); - lua_gettable(l, 2); - jparams.duration = safeGetNumber(l, 3, NoErrors, "", 5.0); - lua_settop(l, 2); - - lua_pushstring(l, "from"); - lua_gettable(l, 2); - UniversalCoord* from = to_position(l, 3); - if (from != NULL) - jparams.from = *from; - lua_settop(l, 2); - - lua_pushstring(l, "to"); - lua_gettable(l, 2); - UniversalCoord* to = to_position(l, 3); - if (to != NULL) - jparams.to = *to; - lua_settop(l, 2); - - lua_pushstring(l, "initialOrientation"); - lua_gettable(l, 2); - Quatd* rot1 = to_rotation(l, 3); - if (rot1 != NULL) - jparams.initialOrientation = Quatf((float) rot1->w, (float) rot1->x, (float) rot1->y, (float) rot1->z); - lua_settop(l, 2); - - lua_pushstring(l, "finalOrientation"); - lua_gettable(l, 2); - Quatd* rot2 = to_rotation(l, 3); - if (rot2 != NULL) - jparams.finalOrientation = Quatf((float) rot2->w, (float) rot2->x, (float) rot2->y, (float) rot2->z); - lua_settop(l, 2); - - lua_pushstring(l, "startInterpolation"); - lua_gettable(l, 2); - jparams.startInterpolation = safeGetNumber(l, 3, NoErrors, "", 0.25); - lua_settop(l, 2); - - lua_pushstring(l, "endInterpolation"); - lua_gettable(l, 2); - jparams.endInterpolation = safeGetNumber(l, 3, NoErrors, "", 0.75); - lua_settop(l, 2); - - lua_pushstring(l, "accelTime"); - lua_gettable(l, 2); - jparams.accelTime = safeGetNumber(l, 3, NoErrors, "", 0.5); - lua_settop(l, 2); - - jparams.duration = max(0.0, jparams.duration); - jparams.accelTime = min(1.0, max(0.1, jparams.accelTime)); - jparams.startInterpolation = min(1.0, max(0.0, jparams.startInterpolation)); - jparams.endInterpolation = min(1.0, max(0.0, jparams.endInterpolation)); - - // args are in universal coords, let setFrame handle conversion: - FrameOfReference tmp = o->getFrame(); - o->setFrame(FrameOfReference()); - o->gotoJourney(jparams); - o->setFrame(tmp); - return 0; -} - -// First argument is the target object or position; optional second argument -// is the travel time -static int observer_goto(lua_State* l) -{ - if (lua_gettop(l) == 2 && lua_istable(l, 2)) - { - // handle this in own function - return observer_gototable(l); - } - checkArgs(l, 1, 5, "One to four arguments expected to observer:goto"); - - Observer* o = this_observer(l); - - Selection* sel = to_object(l, 2); - UniversalCoord* uc = to_position(l, 2); - if (sel == NULL && uc == NULL) - { - doError(l, "First arg to observer:goto must be object or position"); - } - - double travelTime = safeGetNumber(l, 3, WrongType, "Second arg to observer:goto must be a number", 5.0); - double startInter = safeGetNumber(l, 4, WrongType, "Third arg to observer:goto must be a number", 0.25); - double endInter = safeGetNumber(l, 5, WrongType, "Fourth arg to observer:goto must be a number", 0.75); - if (startInter < 0 || startInter > 1) startInter = 0.25; - if (endInter < 0 || endInter > 1) startInter = 0.75; - - // The first argument may be either an object or a position - if (sel != NULL) - { - o->gotoSelection(*sel, travelTime, startInter, endInter, Vec3f(0, 1, 0), astro::ObserverLocal); - } - else - { - RigidTransform rt = o->getSituation(); - rt.translation = *uc; - o->gotoLocation(rt, travelTime); - } - - return 0; -} - -static int observer_gotolonglat(lua_State* l) -{ - checkArgs(l, 2, 7, "One to five arguments expected to observer:gotolonglat"); - - Observer* o = this_observer(l); - - Selection* sel = to_object(l, 2); - if (sel == NULL) - { - doError(l, "First arg to observer:gotolonglat must be an object"); - } - double defaultDistance = sel->radius() * 5.0; - - double longitude = safeGetNumber(l, 3, WrongType, "Second arg to observer:gotolonglat must be a number", 0.0); - double latitude = safeGetNumber(l, 4, WrongType, "Third arg to observer:gotolonglat must be a number", 0.0); - double distance = safeGetNumber(l, 5, WrongType, "Fourth arg to observer:gotolonglat must be a number", defaultDistance); - double travelTime = safeGetNumber(l, 6, WrongType, "Fifth arg to observer:gotolonglat must be a number", 5.0); - - distance = distance / KM_PER_LY; - - Vec3f up(0.0f, 1.0f, 0.0f); - if (lua_gettop(l) >= 7) - { - Vec3d* uparg = to_vector(l, 7); - if (uparg == NULL) - { - doError(l, "Sixth argument to observer:gotolonglat must be a vector"); - } - up = Vec3f((float)uparg->x, (float)uparg->y, (float)uparg->z); - } - o->gotoSelectionLongLat(*sel, travelTime, distance, (float)longitude, (float)latitude, up); - - return 0; -} - -// deprecated: wrong name, bad interface. -static int observer_gotolocation(lua_State* l) -{ - checkArgs(l, 2, 3,"Expected one or two arguments to observer:gotolocation"); - - Observer* o = this_observer(l); - - double travelTime = safeGetNumber(l, 3, WrongType, "Second arg to observer:gotolocation must be a number", 5.0); - if (travelTime < 0) - travelTime = 0.0; - - UniversalCoord* uc = to_position(l, 2); - if (uc != NULL) - { - RigidTransform rt = o->getSituation(); - rt.translation = *uc; - o->gotoLocation(rt, travelTime); - } - else - { - doError(l, "First arg to observer:gotolocation must be a position"); - } - - return 0; -} - -static int observer_gotodistance(lua_State* l) -{ - checkArgs(l, 2, 5, "One to four arguments expected to observer:gotodistance"); - - Observer* o = this_observer(l); - Selection* sel = to_object(l, 2); - if (sel == NULL) - { - doError(l, "First arg to observer:gotodistance must be object"); - } - - double distance = safeGetNumber(l, 3, WrongType, "Second arg to observer:gotodistance must be a number", 20000); - double travelTime = safeGetNumber(l, 4, WrongType, "Third arg to observer:gotodistance must be a number", 5.0); - - Vec3f up(0,1,0); - if (lua_gettop(l) > 4) - { - Vec3d* up_arg = to_vector(l, 5); - if (up_arg == NULL) - { - doError(l, "Fourth arg to observer:gotodistance must be a vector"); - } - up.x = (float)up_arg->x; - up.y = (float)up_arg->y; - up.z = (float)up_arg->z; - } - - o->gotoSelection(*sel, travelTime, astro::kilometersToLightYears(distance), up, astro::Universal); - - return 0; -} - -static int observer_gotosurface(lua_State* l) -{ - checkArgs(l, 2, 3, "One to two arguments expected to observer:gotosurface"); - - Observer* o = this_observer(l); - Selection* sel = to_object(l, 2); - if (sel == NULL) - { - doError(l, "First arg to observer:gotosurface must be object"); - } - - double travelTime = safeGetNumber(l, 3, WrongType, "Second arg to observer:gotosurface must be a number", 5.0); - - // This is needed because gotoSurface expects frame to be geosync: - o->geosynchronousFollow(*sel); - o->gotoSurface(*sel, travelTime); - - return 0; -} - -static int observer_center(lua_State* l) -{ - checkArgs(l, 2, 3, "Expected one or two arguments for to observer:center"); - - Observer* o = this_observer(l); - Selection* sel = to_object(l, 2); - if (sel == NULL) - { - doError(l, "First argument to observer:center must be an object"); - } - double travelTime = safeGetNumber(l, 3, WrongType, "Second arg to observer:center must be a number", 5.0); - - o->centerSelection(*sel, travelTime); - - return 0; -} - -static int observer_centerorbit(lua_State* l) -{ - checkArgs(l, 2, 3, "Expected one or two arguments for to observer:center"); - - Observer* o = this_observer(l); - Selection* sel = to_object(l, 2); - if (sel == NULL) - { - doError(l, "First argument to observer:centerorbit must be an object"); - } - double travelTime = safeGetNumber(l, 3, WrongType, "Second arg to observer:centerorbit must be a number", 5.0); - - o->centerSelectionCO(*sel, travelTime); - - return 0; -} - -static int observer_cancelgoto(lua_State* l) -{ - checkArgs(l, 1, 1, "Expected no arguments to observer:cancelgoto"); - - Observer* o = this_observer(l); - o->cancelMotion(); - - return 0; -} - -static int observer_follow(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected for observer:follow"); - - Observer* o = this_observer(l); - Selection* sel = to_object(l, 2); - if (sel == NULL) - { - doError(l, "First argument to observer:follow must be an object"); - } - o->follow(*sel); - - return 0; -} - -static int observer_synchronous(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected for observer:synchronous"); - - Observer* o = this_observer(l); - Selection* sel = to_object(l, 2); - if (sel == NULL) - { - doError(l, "First argument to observer:synchronous must be an object"); - } - o->geosynchronousFollow(*sel); - - return 0; -} - -static int observer_lock(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected for observer:lock"); - - Observer* o = this_observer(l); - Selection* sel = to_object(l, 2); - if (sel == NULL) - { - doError(l, "First argument to observer:phaseLock must be an object"); - } - o->phaseLock(*sel); - - return 0; -} - -static int observer_chase(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected for observer:chase"); - - Observer* o = this_observer(l); - Selection* sel = to_object(l, 2); - if (sel == NULL) - { - doError(l, "First argument to observer:chase must be an object"); - } - o->chase(*sel); - - return 0; -} - -static int observer_track(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected for observer:track"); - - Observer* o = this_observer(l); - - // If the argument is nil, clear the tracked object - if (lua_isnil(l, 2)) - { - o->setTrackedObject(Selection()); - } - else - { - // Otherwise, turn on tracking and set the tracked object - Selection* sel = to_object(l, 2); - if (sel == NULL) - { - doError(l, "First argument to observer:center must be an object"); - } - o->setTrackedObject(*sel); - } - - return 0; -} - -static int observer_gettrackedobject(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected to observer:gettrackedobject"); - - Observer* o = this_observer(l); - object_new(l, o->getTrackedObject()); - - return 1; -} - -// Return true if the observer is still moving as a result of a goto, center, -// or similar command. -static int observer_travelling(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected to observer:travelling"); - - Observer* o = this_observer(l); - if (o->getMode() == Observer::Travelling) - lua_pushboolean(l, 1); - else - lua_pushboolean(l, 0); - - return 1; -} - -// Return the observer's current time as a Julian day number -static int observer_gettime(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected to observer:gettime"); - - Observer* o = this_observer(l); - lua_pushnumber(l, o->getTime()); - - return 1; -} - -// Return the observer's current position -static int observer_getposition(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected to observer:getposition"); - - Observer* o = this_observer(l); - position_new(l, o->getPosition()); - - return 1; -} - -static int observer_getsurface(lua_State* l) -{ - checkArgs(l, 1, 1, "One argument expected to observer:getsurface()"); - - Observer* obs = this_observer(l); - lua_pushstring(l, obs->getDisplayedSurface().c_str()); - - return 1; -} - -static int observer_setsurface(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected to observer:setsurface()"); - - Observer* obs = this_observer(l); - const char* s = lua_tostring(l, 2); - - if (s == NULL) - obs->setDisplayedSurface(""); - else - obs->setDisplayedSurface(s); - - return 0; -} - -static int observer_getframe(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for observer:getframe()"); - - Observer* obs = this_observer(l); - - FrameOfReference frame = obs->getFrame(); - frame_new(l, frame); - return 1; -} - -static int observer_setframe(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument required for observer:setframe()"); - - Observer* obs = this_observer(l); - - FrameOfReference* frame; - frame = to_frame(l, 2); - if (frame != NULL) - { - obs->setFrame(*frame); - } - else - { - doError(l, "Argument to observer:setframe must be a frame"); - } - return 0; -} - -static int observer_setspeed(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument required for observer:setspeed()"); - - Observer* obs = this_observer(l); - - double speed = safeGetNumber(l, 2, AllErrors, "First argument to observer:setspeed must be a number"); - obs->setTargetSpeed((float)speed); - return 0; -} - -static int observer_getspeed(lua_State* l) -{ - checkArgs(l, 1, 1, "No argument expected for observer:getspeed()"); - - Observer* obs = this_observer(l); - - lua_pushnumber(l, (lua_Number)obs->getTargetSpeed()); - return 1; -} - -static int observer_setfov(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected to observer:setfov()"); - - Observer* obs = this_observer(l); - double fov = safeGetNumber(l, 2, AllErrors, "Argument to observer:setfov() must be a number"); - if ((fov >= degToRad(0.001f)) && (fov <= degToRad(120.0f))) - { - obs->setFOV((float) fov); - getAppCore(l, AllErrors)->setZoomFromFOV(); - } - return 0; -} - -static int observer_getfov(lua_State* l) -{ - checkArgs(l, 1, 1, "No argument expected to observer:getfov()"); - - Observer* obs = this_observer(l); - lua_pushnumber(l, obs->getFOV()); - return 1; -} - -static int observer_splitview(lua_State* l) -{ - checkArgs(l, 2, 3, "One or two arguments expected for observer:splitview()"); - - Observer* obs = this_observer(l); - CelestiaCore* appCore = getAppCore(l, AllErrors); - const char* splitType = safeGetString(l, 2, AllErrors, "First argument to observer:splitview() must be a string"); - View::Type type = (compareIgnoringCase(splitType, "h") == 0) ? View::HorizontalSplit : View::VerticalSplit; - double splitPos = safeGetNumber(l, 3, WrongType, "Number expected as argument to observer:splitview()", 0.5); - if (splitPos < 0.1) - splitPos = 0.1; - if (splitPos > 0.9) - splitPos = 0.9; - View* view = getViewByObserver(appCore, obs); - appCore->splitView(type, view, (float)splitPos); - return 0; -} - -static int observer_deleteview(lua_State* l) -{ - checkArgs(l, 1, 1, "No argument expected for observer:deleteview()"); - - Observer* obs = this_observer(l); - CelestiaCore* appCore = getAppCore(l, AllErrors); - View* view = getViewByObserver(appCore, obs); - appCore->deleteView(view); - return 0; -} - -static int observer_singleview(lua_State* l) -{ - checkArgs(l, 1, 1, "No argument expected for observer:singleview()"); - - Observer* obs = this_observer(l); - CelestiaCore* appCore = getAppCore(l, AllErrors); - View* view = getViewByObserver(appCore, obs); - appCore->singleView(view); - return 0; -} - -static int observer_equal(lua_State* l) -{ - checkArgs(l, 2, 2, "Wrong number of arguments for comparison!"); - - Observer* o1 = this_observer(l); - Observer* o2 = to_observer(l, 2); - - lua_pushboolean(l, (o1 == o2)); - return 1; -} - -static int observer_setlocationflags(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected for observer:setlocationflags()"); - Observer* obs = this_observer(l); - if (!lua_istable(l, 2)) - { - doError(l, "Argument to observer:setlocationflags() must be a table"); - } - - lua_pushnil(l); - int locationFlags = obs->getLocationFilter(); - while (lua_next(l, -2) != 0) - { - string key; - bool value = false; - if (lua_isstring(l, -2)) - { - key = lua_tostring(l, -2); - } - else - { - doError(l, "Keys in table-argument to observer:setlocationflags() must be strings"); - } - if (lua_isboolean(l, -1)) - { - value = lua_toboolean(l, -1) != 0; - } - else - { - doError(l, "Values in table-argument to observer:setlocationflags() must be boolean"); - } - if (LocationFlagMap.count(key) == 0) - { - cerr << "Unknown key: " << key << "\n"; - } - else - { - int flag = LocationFlagMap[key]; - if (value) - { - locationFlags |= flag; - } - else - { - locationFlags &= ~flag; - } - } - lua_pop(l,1); - } - obs->setLocationFilter(locationFlags); - return 0; -} - -static int observer_getlocationflags(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for observer:getlocationflags()"); - Observer* obs = this_observer(l); - lua_newtable(l); - FlagMap::const_iterator it = LocationFlagMap.begin(); - const int locationFlags = obs->getLocationFilter(); - while (it != LocationFlagMap.end()) - { - string key = it->first; - lua_pushstring(l, key.c_str()); - lua_pushboolean(l, (it->second & locationFlags) != 0); - lua_settable(l,-3); - it++; - } - return 1; -} - -static void CreateObserverMetaTable(lua_State* l) -{ - CreateClassMetatable(l, _Observer); - - RegisterMethod(l, "__tostring", observer_tostring); - RegisterMethod(l, "isvalid", observer_isvalid); - RegisterMethod(l, "goto", observer_goto); - RegisterMethod(l, "gotolonglat", observer_gotolonglat); - RegisterMethod(l, "gotolocation", observer_gotolocation); - RegisterMethod(l, "gotodistance", observer_gotodistance); - RegisterMethod(l, "gotosurface", observer_gotosurface); - RegisterMethod(l, "cancelgoto", observer_cancelgoto); - RegisterMethod(l, "setposition", observer_setposition); - RegisterMethod(l, "lookat", observer_lookat); - RegisterMethod(l, "setorientation", observer_setorientation); - RegisterMethod(l, "getorientation", observer_getorientation); - RegisterMethod(l, "getspeed", observer_getspeed); - RegisterMethod(l, "setspeed", observer_setspeed); - RegisterMethod(l, "getfov", observer_getfov); - RegisterMethod(l, "setfov", observer_setfov); - RegisterMethod(l, "rotate", observer_rotate); - RegisterMethod(l, "center", observer_center); - RegisterMethod(l, "centerorbit", observer_centerorbit); - RegisterMethod(l, "follow", observer_follow); - RegisterMethod(l, "synchronous", observer_synchronous); - RegisterMethod(l, "chase", observer_chase); - RegisterMethod(l, "lock", observer_lock); - RegisterMethod(l, "track", observer_track); - RegisterMethod(l, "gettrackedobject", observer_gettrackedobject); - RegisterMethod(l, "travelling", observer_travelling); - RegisterMethod(l, "getframe", observer_getframe); - RegisterMethod(l, "setframe", observer_setframe); - RegisterMethod(l, "gettime", observer_gettime); - RegisterMethod(l, "getposition", observer_getposition); - RegisterMethod(l, "getsurface", observer_getsurface); - RegisterMethod(l, "setsurface", observer_setsurface); - RegisterMethod(l, "splitview", observer_splitview); - RegisterMethod(l, "deleteview", observer_deleteview); - RegisterMethod(l, "singleview", observer_singleview); - RegisterMethod(l, "getlocationflags", observer_getlocationflags); - RegisterMethod(l, "setlocationflags", observer_setlocationflags); - RegisterMethod(l, "__eq", observer_equal); - - lua_pop(l, 1); // remove metatable from stack -} - - -// ==================== Celscript-object ==================== - -// create a CelScriptWrapper from a string: -static int celscript_from_string(lua_State* l, string& script_text) -{ - istringstream scriptfile(script_text); - - CelestiaCore* appCore = getAppCore(l, AllErrors); - CelScriptWrapper* celscript = new CelScriptWrapper(*appCore, scriptfile); - if (celscript->getErrorMessage() != "") - { - string error = celscript->getErrorMessage(); - delete celscript; - doError(l, error.c_str()); - } - else - { - CelScriptWrapper** ud = reinterpret_cast(lua_newuserdata(l, sizeof(CelScriptWrapper*))); - *ud = celscript; - SetClass(l, _CelScript); - } - - return 1; -} - -static CelScriptWrapper* this_celscript(lua_State* l) -{ - CelScriptWrapper** script = static_cast(CheckUserData(l, 1, _CelScript)); - if (script == NULL) - { - doError(l, "Bad CEL-script object!"); - } - return *script; -} - -static int celscript_tostring(lua_State* l) -{ - lua_pushstring(l, "[Celscript]"); - - return 1; -} - -static int celscript_tick(lua_State* l) -{ - CelScriptWrapper* script = this_celscript(l); - LuaState* stateObject = getLuaStateObject(l); - double t = stateObject->getTime(); - lua_pushboolean(l, !(script->tick(t)) ); - return 1; -} - -static int celscript_gc(lua_State* l) -{ - CelScriptWrapper* script = this_celscript(l); - delete script; - return 0; -} - - -static void CreateCelscriptMetaTable(lua_State* l) -{ - CreateClassMetatable(l, _CelScript); - - RegisterMethod(l, "__tostring", celscript_tostring); - RegisterMethod(l, "tick", celscript_tick); - RegisterMethod(l, "__gc", celscript_gc); - - lua_pop(l, 1); // remove metatable from stack -} - - -// ==================== Celestia-object ==================== -static int celestia_new(lua_State* l, CelestiaCore* appCore) -{ - CelestiaCore** ud = reinterpret_cast(lua_newuserdata(l, sizeof(CelestiaCore*))); - *ud = appCore; - - SetClass(l, _Celestia); - - return 1; -} - -static CelestiaCore* to_celestia(lua_State* l, int index) -{ - CelestiaCore** appCore = static_cast(CheckUserData(l, index, _Celestia)); - if (appCore == NULL) - return NULL; - else - return *appCore; -} - -static CelestiaCore* this_celestia(lua_State* l) -{ - CelestiaCore* appCore = to_celestia(l, 1); - if (appCore == NULL) - { - doError(l, "Bad celestia object!"); - } - - return appCore; -} - - -static int celestia_flash(lua_State* l) -{ - checkArgs(l, 2, 3, "One or two arguments expected to function celestia:flash"); - - CelestiaCore* appCore = this_celestia(l); - const char* s = safeGetString(l, 2, AllErrors, "First argument to celestia:flash must be a string"); - double duration = safeGetNumber(l, 3, WrongType, "Second argument to celestia:flash must be a number", 1.5); - if (duration < 0.0) - { - duration = 1.5; - } - - appCore->flash(s, duration); - - return 0; -} - -static int celestia_print(lua_State* l) -{ - checkArgs(l, 2, 7, "One to six arguments expected to function celestia:print"); - - CelestiaCore* appCore = this_celestia(l); - const char* s = safeGetString(l, 2, AllErrors, "First argument to celestia:print must be a string"); - double duration = safeGetNumber(l, 3, WrongType, "Second argument to celestia:print must be a number", 1.5); - int horig = (int)safeGetNumber(l, 4, WrongType, "Third argument to celestia:print must be a number", -1.0); - int vorig = (int)safeGetNumber(l, 5, WrongType, "Fourth argument to celestia:print must be a number", -1.0); - int hoff = (int)safeGetNumber(l, 6, WrongType, "Fifth argument to celestia:print must be a number", 0.0); - int voff = (int)safeGetNumber(l, 7, WrongType, "Sixth argument to celestia:print must be a number", 5.0); - - if (duration < 0.0) - { - duration = 1.5; - } - - appCore->showText(s, horig, vorig, hoff, voff, duration); - - return 0; -} - -static int celestia_gettextwidth(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected to function celestia:gettextwidth"); - - CelestiaCore* appCore = this_celestia(l); - const char* s = safeGetString(l, 2, AllErrors, "First argument to celestia:gettextwidth must be a string"); - - lua_pushnumber(l, appCore->getTextWidth(s)); - - return 1; -} - -static int celestia_getaltazimuthmode(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for celestia:getaltazimuthmode()"); - - CelestiaCore* appCore = this_celestia(l); - lua_pushboolean(l, appCore->getAltAzimuthMode()); - - return 1; -} - -static int celestia_setaltazimuthmode(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected to function celestia:setaltazimuthmode"); - bool enable = false; - if (lua_isboolean(l, -1)) - { - enable = lua_toboolean(l, -1) != 0; - } - else - { - doError(l, "Argument for celestia:setaltazimuthmode must be a boolean"); - } - - CelestiaCore* appCore = this_celestia(l); - appCore->setAltAzimuthMode(enable); - lua_pop(l, 1); - - return 0; -} - -static int celestia_show(lua_State* l) -{ - checkArgs(l, 1, 1000, "Wrong number of arguments to celestia:show"); - CelestiaCore* appCore = this_celestia(l); - - int argc = lua_gettop(l); - int flags = 0; - for (int i = 2; i <= argc; i++) - { - string renderFlag = safeGetString(l, i, AllErrors, "Arguments to celestia:show() must be strings"); - if (renderFlag == "lightdelay") - appCore->setLightDelayActive(true); - else - if (RenderFlagMap.count(renderFlag) > 0) - flags |= RenderFlagMap[renderFlag]; - } - - Renderer* r = appCore->getRenderer(); - r->setRenderFlags(r->getRenderFlags() | flags); - appCore->notifyWatchers(CelestiaCore::RenderFlagsChanged); - - return 0; -} - -static int celestia_hide(lua_State* l) -{ - checkArgs(l, 1, 1000, "Wrong number of arguments to celestia:hide"); - CelestiaCore* appCore = this_celestia(l); - - int argc = lua_gettop(l); - int flags = 0; - for (int i = 2; i <= argc; i++) - { - string renderFlag = safeGetString(l, i, AllErrors, "Arguments to celestia:hide() must be strings"); - if (renderFlag == "lightdelay") - appCore->setLightDelayActive(false); - else - if (RenderFlagMap.count(renderFlag) > 0) - flags |= RenderFlagMap[renderFlag]; - } - - Renderer* r = appCore->getRenderer(); - r->setRenderFlags(r->getRenderFlags() & ~flags); - appCore->notifyWatchers(CelestiaCore::RenderFlagsChanged); - - return 0; -} - -static int celestia_setrenderflags(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected for celestia:setrenderflags()"); - CelestiaCore* appCore = this_celestia(l); - if (!lua_istable(l, 2)) - { - doError(l, "Argument to celestia:setrenderflags() must be a table"); - } - - int renderFlags = appCore->getRenderer()->getRenderFlags(); - lua_pushnil(l); - while (lua_next(l, -2) != 0) - { - string key; - bool value = false; - if (lua_isstring(l, -2)) - { - key = lua_tostring(l, -2); - } - else - { - doError(l, "Keys in table-argument to celestia:setrenderflags() must be strings"); - } - if (lua_isboolean(l, -1)) - { - value = lua_toboolean(l, -1) != 0; - } - else - { - doError(l, "Values in table-argument to celestia:setrenderflags() must be boolean"); - } - if (key == "lightdelay") - { - appCore->setLightDelayActive(value); - } - else if (RenderFlagMap.count(key) > 0) - { - int flag = RenderFlagMap[key]; - if (value) - { - renderFlags |= flag; - } - else - { - renderFlags &= ~flag; - } - } - else - { - cerr << "Unknown key: " << key << "\n"; - } - lua_pop(l,1); - } - appCore->getRenderer()->setRenderFlags(renderFlags); - appCore->notifyWatchers(CelestiaCore::RenderFlagsChanged); - - return 0; -} - -static int celestia_getrenderflags(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for celestia:getrenderflags()"); - CelestiaCore* appCore = this_celestia(l); - lua_newtable(l); - FlagMap::const_iterator it = RenderFlagMap.begin(); - const int renderFlags = appCore->getRenderer()->getRenderFlags(); - while (it != RenderFlagMap.end()) - { - string key = it->first; - lua_pushstring(l, key.c_str()); - lua_pushboolean(l, (it->second & renderFlags) != 0); - lua_settable(l,-3); - it++; - } - lua_pushstring(l, "lightdelay"); - lua_pushboolean(l, appCore->getLightDelayActive()); - lua_settable(l, -3); - return 1; -} - -int celestia_getscreendimension(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for celestia:getscreendimension()"); - // error checking only: - this_celestia(l); - // Get the dimensions of the current viewport - int viewport[4]; - glGetIntegerv(GL_VIEWPORT, viewport); - lua_pushnumber(l, viewport[2]-viewport[0]); - lua_pushnumber(l, viewport[3]-viewport[1]); - return 2; -} - -static int celestia_showlabel(lua_State* l) -{ - checkArgs(l, 1, 1000, "Bad method call!"); - CelestiaCore* appCore = this_celestia(l); - - int argc = lua_gettop(l); - int flags = 0; - for (int i = 2; i <= argc; i++) - { - string labelFlag = safeGetString(l, i, AllErrors, "Arguments to celestia:showlabel() must be strings"); - if (LabelFlagMap.count(labelFlag) > 0) - flags |= LabelFlagMap[labelFlag]; - } - - Renderer* r = appCore->getRenderer(); - r->setLabelMode(r->getLabelMode() | flags); - appCore->notifyWatchers(CelestiaCore::LabelFlagsChanged); - - return 0; -} - -static int celestia_hidelabel(lua_State* l) -{ - checkArgs(l, 1, 1000, "Invalid number of arguments in celestia:hidelabel"); - CelestiaCore* appCore = this_celestia(l); - - int argc = lua_gettop(l); - int flags = 0; - for (int i = 2; i <= argc; i++) - { - string labelFlag = safeGetString(l, i, AllErrors, "Arguments to celestia:hidelabel() must be strings"); - if (LabelFlagMap.count(labelFlag) > 0) - flags |= LabelFlagMap[labelFlag]; - } - - Renderer* r = appCore->getRenderer(); - r->setLabelMode(r->getLabelMode() & ~flags); - appCore->notifyWatchers(CelestiaCore::LabelFlagsChanged); - - return 0; -} - -static int celestia_setlabelflags(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected for celestia:setlabelflags()"); - CelestiaCore* appCore = this_celestia(l); - if (!lua_istable(l, 2)) - { - doError(l, "Argument to celestia:setlabelflags() must be a table"); - } - - int labelFlags = appCore->getRenderer()->getLabelMode(); - lua_pushnil(l); - while (lua_next(l, -2) != 0) - { - string key; - bool value = false; - if (lua_isstring(l, -2)) - { - key = lua_tostring(l, -2); - } - else - { - doError(l, "Keys in table-argument to celestia:setlabelflags() must be strings"); - } - if (lua_isboolean(l, -1)) - { - value = lua_toboolean(l, -1) != 0; - } - else - { - doError(l, "Values in table-argument to celestia:setlabelflags() must be boolean"); - } - if (LabelFlagMap.count(key) == 0) - { - cerr << "Unknown key: " << key << "\n"; - } - else - { - int flag = LabelFlagMap[key]; - if (value) - { - labelFlags |= flag; - } - else - { - labelFlags &= ~flag; - } - } - lua_pop(l,1); - } - appCore->getRenderer()->setLabelMode(labelFlags); - appCore->notifyWatchers(CelestiaCore::LabelFlagsChanged); - - return 0; -} - -static int celestia_getlabelflags(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for celestia:getlabelflags()"); - CelestiaCore* appCore = this_celestia(l); - lua_newtable(l); - FlagMap::const_iterator it = LabelFlagMap.begin(); - const int labelFlags = appCore->getRenderer()->getLabelMode(); - while (it != LabelFlagMap.end()) - { - string key = it->first; - lua_pushstring(l, key.c_str()); - lua_pushboolean(l, (it->second & labelFlags) != 0); - lua_settable(l,-3); - it++; - } - return 1; -} - -static int celestia_setorbitflags(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected for celestia:setorbitflags()"); - CelestiaCore* appCore = this_celestia(l); - if (!lua_istable(l, 2)) - { - doError(l, "Argument to celestia:setorbitflags() must be a table"); - } - - int orbitFlags = appCore->getRenderer()->getOrbitMask(); - lua_pushnil(l); - while (lua_next(l, -2) != 0) - { - string key; - bool value = false; - if (lua_isstring(l, -2)) - { - key = lua_tostring(l, -2); - } - else - { - doError(l, "Keys in table-argument to celestia:setorbitflags() must be strings"); - } - if (lua_isboolean(l, -1)) - { - value = lua_toboolean(l, -1) != 0; - } - else - { - doError(l, "Values in table-argument to celestia:setorbitflags() must be boolean"); - } - if (BodyTypeMap.count(key) == 0) - { - cerr << "Unknown key: " << key << "\n"; - } - else - { - int flag = BodyTypeMap[key]; - if (value) - { - orbitFlags |= flag; - } - else - { - orbitFlags &= ~flag; - } - } - lua_pop(l,1); - } - appCore->getRenderer()->setOrbitMask(orbitFlags); - return 0; -} - -static int celestia_getorbitflags(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for celestia:getorbitflags()"); - CelestiaCore* appCore = this_celestia(l); - lua_newtable(l); - FlagMap::const_iterator it = BodyTypeMap.begin(); - const int orbitFlags = appCore->getRenderer()->getOrbitMask(); - while (it != BodyTypeMap.end()) - { - string key = it->first; - lua_pushstring(l, key.c_str()); - lua_pushboolean(l, (it->second & orbitFlags) != 0); - lua_settable(l,-3); - it++; - } - return 1; -} - -static int celestia_setoverlayelements(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected for celestia:setoverlayelements()"); - CelestiaCore* appCore = this_celestia(l); - if (!lua_istable(l, 2)) - { - doError(l, "Argument to celestia:setoverlayelements() must be a table"); - } - - int overlayElements = appCore->getOverlayElements(); - lua_pushnil(l); - while (lua_next(l, -2) != 0) - { - string key; - bool value = false; - if (lua_isstring(l, -2)) - { - key = lua_tostring(l, -2); - } - else - { - doError(l, "Keys in table-argument to celestia:setoverlayelements() must be strings"); - } - if (lua_isboolean(l, -1)) - { - value = lua_toboolean(l, -1) != 0; - } - else - { - doError(l, "Values in table-argument to celestia:setoverlayelements() must be boolean"); - } - if (OverlayElementMap.count(key) == 0) - { - cerr << "Unknown key: " << key << "\n"; - } - else - { - int element = OverlayElementMap[key]; - if (value) - { - overlayElements |= element; - } - else - { - overlayElements &= ~element; - } - } - lua_pop(l,1); - } - appCore->setOverlayElements(overlayElements); - return 0; -} - -static int celestia_getoverlayelements(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for celestia:getoverlayelements()"); - CelestiaCore* appCore = this_celestia(l); - lua_newtable(l); - FlagMap::const_iterator it = OverlayElementMap.begin(); - const int overlayElements = appCore->getOverlayElements(); - while (it != OverlayElementMap.end()) - { - string key = it->first; - lua_pushstring(l, key.c_str()); - lua_pushboolean(l, (it->second & overlayElements) != 0); - lua_settable(l,-3); - it++; - } - return 1; -} - -static int celestia_setlabelcolor(lua_State* l) -{ - checkArgs(l, 5, 5, "Four arguments expected for celestia:setlabelcolor()"); - if (!lua_isstring(l, 2)) - { - doError(l, "First argument to celestia:setlabelstyle() must be a string"); - } - - Color* color = NULL; - string key; - key = lua_tostring(l, 2); - if (LabelColorMap.count(key) == 0) - { - cerr << "Unknown label style: " << key << "\n"; - } - else - { - color = LabelColorMap[key]; - } - - double red = safeGetNumber(l, 3, AllErrors, "setlabelcolor: color values must be numbers"); - double green = safeGetNumber(l, 4, AllErrors, "setlabelcolor: color values must be numbers"); - double blue = safeGetNumber(l, 5, AllErrors, "setlabelcolor: color values must be numbers"); - - // opacity currently not settable - double opacity = 1.0; - - if (color != NULL) - { - *color = Color((float) red, (float) green, (float) blue, (float) opacity); - } - - return 1; -} - - -static int celestia_setlinecolor(lua_State* l) -{ - checkArgs(l, 5, 5, "Four arguments expected for celestia:setlinecolor()"); - if (!lua_isstring(l, 2)) - { - doError(l, "First argument to celestia:setlinecolor() must be a string"); - } - - Color* color = NULL; - string key; - key = lua_tostring(l, 2); - if (LineColorMap.count(key) == 0) - { - cerr << "Unknown line style: " << key << "\n"; - } - else - { - color = LineColorMap[key]; - } - - double red = safeGetNumber(l, 3, AllErrors, "setlinecolor: color values must be numbers"); - double green = safeGetNumber(l, 4, AllErrors, "setlinecolor: color values must be numbers"); - double blue = safeGetNumber(l, 5, AllErrors, "setlinecolor: color values must be numbers"); - - // opacity currently not settable - double opacity = 1.0; - - if (color != NULL) - { - *color = Color((float) red, (float) green, (float) blue, (float) opacity); - } - - return 1; -} - - -static int celestia_setfaintestvisible(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected for celestia:setfaintestvisible()"); - CelestiaCore* appCore = this_celestia(l); - float faintest = (float)safeGetNumber(l, 2, AllErrors, "Argument to celestia:setfaintestvisible() must be a number"); - if ((appCore->getRenderer()->getRenderFlags() & Renderer::ShowAutoMag) == 0) - { - faintest = min(15.0f, max(1.0f, faintest)); - appCore->setFaintest(faintest); - appCore->notifyWatchers(CelestiaCore::FaintestChanged); - } - else - { - faintest = min(12.0f, max(6.0f, faintest)); - appCore->getRenderer()->setFaintestAM45deg(faintest); - appCore->setFaintestAutoMag(); - } - return 0; -} - -static int celestia_getfaintestvisible(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for celestia:getfaintestvisible()"); - CelestiaCore* appCore = this_celestia(l); - if ((appCore->getRenderer()->getRenderFlags() & Renderer::ShowAutoMag) == 0) - { - lua_pushnumber(l, appCore->getSimulation()->getFaintestVisible()); - } - else - { - lua_pushnumber(l, appCore->getRenderer()->getFaintestAM45deg()); - } - return 1; -} - -static int celestia_setgalaxylightgain(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected for celestia:setgalaxylightgain()"); - float lightgain = (float)safeGetNumber(l, 2, AllErrors, "Argument to celestia:setgalaxylightgain() must be a number"); - lightgain = min(1.0f, max(0.0f, lightgain)); - Galaxy::setLightGain(lightgain); - - return 0; -} - -static int celestia_getgalaxylightgain(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for celestia:getgalaxylightgain()"); - lua_pushnumber(l, Galaxy::getLightGain()); - - return 1; -} - -static int celestia_setminfeaturesize(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected for celestia:setminfeaturesize()"); - CelestiaCore* appCore = this_celestia(l); - float minFeatureSize = (float)safeGetNumber(l, 2, AllErrors, "Argument to celestia:setminfeaturesize() must be a number"); - minFeatureSize = max(0.0f, minFeatureSize); - appCore->getRenderer()->setMinimumFeatureSize(minFeatureSize); - return 0; -} - -static int celestia_getminfeaturesize(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for celestia:getminfeaturesize()"); - CelestiaCore* appCore = this_celestia(l); - lua_pushnumber(l, appCore->getRenderer()->getMinimumFeatureSize()); - return 1; -} - -static int celestia_getobserver(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for celestia:getobserver()"); - - CelestiaCore* appCore = this_celestia(l); - Observer* o = appCore->getSimulation()->getActiveObserver(); - if (o == NULL) - lua_pushnil(l); - else - observer_new(l, o); - - return 1; -} - -static int celestia_getobservers(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for celestia:getobservers()"); - CelestiaCore* appCore = this_celestia(l); - - vector observer_list; - getObservers(appCore, observer_list); - lua_newtable(l); - for (unsigned int i = 0; i < observer_list.size(); i++) - { - observer_new(l, observer_list[i]); - lua_rawseti(l, -2, i + 1); - } - - return 1; -} - -static int celestia_getselection(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected to celestia:getselection()"); - CelestiaCore* appCore = this_celestia(l); - Selection sel = appCore->getSimulation()->getSelection(); - object_new(l, sel); - - return 1; -} - -static int celestia_find(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected for function celestia:find()"); - if (!lua_isstring(l, 2)) - { - doError(l, "Argument to find must be a string"); - } - - CelestiaCore* appCore = this_celestia(l); - Simulation* sim = appCore->getSimulation(); - // Should use universe not simulation for finding objects - Selection sel = sim->findObjectFromPath(lua_tostring(l, 2)); - object_new(l, sel); - - return 1; -} - -static int celestia_select(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected for celestia:select()"); - CelestiaCore* appCore = this_celestia(l); - - Simulation* sim = appCore->getSimulation(); - Selection* sel = to_object(l, 2); - - // If the argument is an object, set the selection; if it's anything else - // clear the selection. - if (sel != NULL) - sim->setSelection(*sel); - else - sim->setSelection(Selection()); - - return 0; -} - -static int celestia_mark(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected to function celestia:mark"); - - CelestiaCore* appCore = this_celestia(l); - Simulation* sim = appCore->getSimulation(); - Selection* sel = to_object(l, 2); - - if (sel != NULL) - { - sim->getUniverse()->markObject(*sel, 10.0f, - Color(0.0f, 1.0f, 0.0f), Marker::Diamond, 1, ""); - } - else - { - doError(l, "Argument to celestia:mark must be an object"); - } - - return 0; -} - -static int celestia_unmark(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected to function celestia:unmark"); - - CelestiaCore* appCore = this_celestia(l); - Simulation* sim = appCore->getSimulation(); - Selection* sel = to_object(l, 2); - - if (sel != NULL) - { - sim->getUniverse()->unmarkObject(*sel, 1); - } - else - { - doError(l, "Argument to celestia:unmark must be an object"); - } - - return 0; -} - -static int celestia_gettime(lua_State* l) -{ - checkArgs(l, 1, 1, "No argument expected to function celestia:gettime"); - - CelestiaCore* appCore = this_celestia(l); - Simulation* sim = appCore->getSimulation(); - lua_pushnumber(l, sim->getTime()); - - return 1; -} - -static int celestia_gettimescale(lua_State* l) -{ - checkArgs(l, 1, 1, "No argument expected to function celestia:gettimescale"); - - CelestiaCore* appCore = this_celestia(l); - lua_pushnumber(l, appCore->getSimulation()->getTimeScale()); - - return 1; -} - -static int celestia_settime(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected to function celestia:settime"); - - CelestiaCore* appCore = this_celestia(l); - double t = safeGetNumber(l, 2, AllErrors, "First arg to celestia:settime must be a number"); - appCore->getSimulation()->setTime(t); - - return 0; -} - -static int celestia_settimescale(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected to function celestia:settimescale"); - - CelestiaCore* appCore = this_celestia(l); - double t = safeGetNumber(l, 2, AllErrors, "Second arg to celestia:settimescale must be a number"); - appCore->getSimulation()->setTimeScale(t); - - return 0; -} - -static int celestia_tojulianday(lua_State* l) -{ - checkArgs(l, 2, 7, "Wrong number of arguments to function celestia:tojulianday"); - - // for error checking only: - this_celestia(l); - - int year = (int)safeGetNumber(l, 2, AllErrors, "First arg to celestia:tojulianday must be a number", 0.0); - int month = (int)safeGetNumber(l, 3, WrongType, "Second arg to celestia:tojulianday must be a number", 1.0); - int day = (int)safeGetNumber(l, 4, WrongType, "Third arg to celestia:tojulianday must be a number", 1.0); - int hour = (int)safeGetNumber(l, 5, WrongType, "Fourth arg to celestia:tojulianday must be a number", 0.0); - int minute = (int)safeGetNumber(l, 6, WrongType, "Fifth arg to celestia:tojulianday must be a number", 0.0); - double seconds = safeGetNumber(l, 7, WrongType, "Sixth arg to celestia:tojulianday must be a number", 0.0); - - astro::Date date(year, month, day); - date.hour = hour; - date.minute = minute; - date.seconds = seconds; - - double jd = (double) date; - - lua_pushnumber(l, jd); - - return 1; -} - -static int celestia_fromjulianday(lua_State* l) -{ - checkArgs(l, 2, 2, "Wrong number of arguments to function celestia:fromjulianday"); - - // for error checking only: - this_celestia(l); - - double jd = safeGetNumber(l, 2, AllErrors, "First arg to celestia:fromjulianday must be a number", 0.0); - astro::Date date(jd); - - lua_newtable(l); - setTable(l, "year", (double)date.year); - setTable(l, "month", (double)date.month); - setTable(l, "day", (double)date.day); - setTable(l, "hour", (double)date.hour); - setTable(l, "minute", (double)date.minute); - setTable(l, "seconds", date.seconds); - - return 1; -} - - -// Convert a UTC Julian date to a TDB Julian day -// TODO: also support a single table argument of the form output by -// celestia_tdbtoutc. -static int celestia_utctotdb(lua_State* l) -{ - checkArgs(l, 2, 7, "Wrong number of arguments to function celestia:utctotdb"); - - // for error checking only: - this_celestia(l); - - int year = (int) safeGetNumber(l, 2, AllErrors, "First arg to celestia:utctotdb must be a number", 0.0); - int month = (int) safeGetNumber(l, 3, WrongType, "Second arg to celestia:utctotdb must be a number", 1.0); - int day = (int) safeGetNumber(l, 4, WrongType, "Third arg to celestia:utctotdb must be a number", 1.0); - int hour = (int)safeGetNumber(l, 5, WrongType, "Fourth arg to celestia:utctotdb must be a number", 0.0); - int minute = (int)safeGetNumber(l, 6, WrongType, "Fifth arg to celestia:utctotdb must be a number", 0.0); - double seconds = safeGetNumber(l, 7, WrongType, "Sixth arg to celestia:utctotdb must be a number", 0.0); - - astro::Date date(year, month, day); - date.hour = hour; - date.minute = minute; - date.seconds = seconds; - - double jd = astro::UTCtoTDB(date); - - lua_pushnumber(l, jd); - - return 1; -} - - -// Convert a TDB Julian day to a UTC Julian date (table format) -static int celestia_tdbtoutc(lua_State* l) -{ - checkArgs(l, 2, 2, "Wrong number of arguments to function celestia:tdbtoutc"); - - // for error checking only: - this_celestia(l); - - double jd = safeGetNumber(l, 2, AllErrors, "First arg to celestia:tdbtoutc must be a number", 0.0); - astro::Date date = astro::TDBtoUTC(jd); - - lua_newtable(l); - setTable(l, "year", (double)date.year); - setTable(l, "month", (double)date.month); - setTable(l, "day", (double)date.day); - setTable(l, "hour", (double)date.hour); - setTable(l, "minute", (double)date.minute); - setTable(l, "seconds", date.seconds); - - return 1; -} - - -static int celestia_unmarkall(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected to function celestia:unmarkall"); - - CelestiaCore* appCore = this_celestia(l); - Simulation* sim = appCore->getSimulation(); - sim->getUniverse()->unmarkAll(); - - return 0; -} - -static int celestia_getstarcount(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected to function celestia:getstarcount"); - - CelestiaCore* appCore = this_celestia(l); - Universe* u = appCore->getSimulation()->getUniverse(); - lua_pushnumber(l, u->getStarCatalog()->size()); - - return 1; -} - - -// Stars iterator function; two upvalues expected -static int celestia_stars_iter(lua_State* l) -{ - CelestiaCore* appCore = to_celestia(l, lua_upvalueindex(1)); - if (appCore == NULL) - { - doError(l, "Bad celestia object!"); - return 0; - } - - uint32 i = (uint32) lua_tonumber(l, lua_upvalueindex(2)); - Universe* u = appCore->getSimulation()->getUniverse(); - - if (i < u->getStarCatalog()->size()) - { - // Increment the counter - lua_pushnumber(l, i + 1); - lua_replace(l, lua_upvalueindex(2)); - - Star* star = u->getStarCatalog()->getStar(i); - if (star == NULL) - lua_pushnil(l); - else - object_new(l, Selection(star)); - - return 1; - } - else - { - // Return nil when we've enumerated all the stars - return 0; - } -} - - -static int celestia_stars(lua_State* l) -{ - // Push a closure with two upvalues: the celestia object and a - // counter. - lua_pushvalue(l, 1); // Celestia object - lua_pushnumber(l, 0); // counter - lua_pushcclosure(l, celestia_stars_iter, 2); - - return 1; -} - - -static int celestia_getdsocount(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected to function celestia:getdsocount"); - - CelestiaCore* appCore = this_celestia(l); - Universe* u = appCore->getSimulation()->getUniverse(); - lua_pushnumber(l, u->getDSOCatalog()->size()); - - return 1; -} - - -// DSOs iterator function; two upvalues expected -static int celestia_dsos_iter(lua_State* l) -{ - CelestiaCore* appCore = to_celestia(l, lua_upvalueindex(1)); - if (appCore == NULL) - { - doError(l, "Bad celestia object!"); - return 0; - } - - uint32 i = (uint32) lua_tonumber(l, lua_upvalueindex(2)); - Universe* u = appCore->getSimulation()->getUniverse(); - - if (i < u->getDSOCatalog()->size()) - { - // Increment the counter - lua_pushnumber(l, i + 1); - lua_replace(l, lua_upvalueindex(2)); - - DeepSkyObject* dso = u->getDSOCatalog()->getDSO(i); - if (dso == NULL) - lua_pushnil(l); - else - object_new(l, Selection(dso)); - - return 1; - } - else - { - // Return nil when we've enumerated all the DSOs - return 0; - } -} - - -static int celestia_dsos(lua_State* l) -{ - // Push a closure with two upvalues: the celestia object and a - // counter. - lua_pushvalue(l, 1); // Celestia object - lua_pushnumber(l, 0); // counter - lua_pushcclosure(l, celestia_dsos_iter, 2); - - return 1; -} - -static int celestia_setambient(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected in celestia:setambient"); - CelestiaCore* appCore = this_celestia(l); - - Renderer* renderer = appCore->getRenderer(); - double ambientLightLevel = safeGetNumber(l, 2, AllErrors, "Argument to celestia:setambient must be a number"); - if (ambientLightLevel > 1.0) - ambientLightLevel = 1.0; - if (ambientLightLevel < 0.0) - ambientLightLevel = 0.0; - - if (renderer != NULL) - renderer->setAmbientLightLevel((float)ambientLightLevel); - appCore->notifyWatchers(CelestiaCore::AmbientLightChanged); - - return 0; -} - -static int celestia_getambient(lua_State* l) -{ - checkArgs(l, 1, 1, "No argument expected in celestia:setambient"); - CelestiaCore* appCore = this_celestia(l); - - Renderer* renderer = appCore->getRenderer(); - if (renderer == NULL) - { - doError(l, "Internal Error: renderer is NULL!"); - } - else - { - lua_pushnumber(l, renderer->getAmbientLightLevel()); - } - return 1; -} - -static int celestia_setminorbitsize(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected in celestia:setminorbitsize"); - CelestiaCore* appCore = this_celestia(l); - - double orbitSize = safeGetNumber(l, 2, AllErrors, "Argument to celestia:setminorbitsize() must be a number"); - Renderer* renderer = appCore->getRenderer(); - if (renderer == NULL) - { - doError(l, "Internal Error: renderer is NULL!"); - } - else - { - orbitSize = max(0.0, orbitSize); - renderer->setMinimumOrbitSize((float)orbitSize); - } - return 0; -} - -static int celestia_getminorbitsize(lua_State* l) -{ - checkArgs(l, 1, 1, "No argument expected in celestia:getminorbitsize"); - CelestiaCore* appCore = this_celestia(l); - - Renderer* renderer = appCore->getRenderer(); - if (renderer == NULL) - { - doError(l, "Internal Error: renderer is NULL!"); - } - else - { - lua_pushnumber(l, renderer->getMinimumOrbitSize()); - } - return 1; -} - -static int celestia_setstardistancelimit(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected in celestia:setstardistancelimit"); - CelestiaCore* appCore = this_celestia(l); - - double distanceLimit = safeGetNumber(l, 2, AllErrors, "Argument to celestia:setstardistancelimit() must be a number"); - Renderer* renderer = appCore->getRenderer(); - if (renderer == NULL) - { - doError(l, "Internal Error: renderer is NULL!"); - } - else - { - renderer->setDistanceLimit((float)distanceLimit); - } - return 0; -} - -static int celestia_getstardistancelimit(lua_State* l) -{ - checkArgs(l, 1, 1, "No argument expected in celestia:getstardistancelimit"); - CelestiaCore* appCore = this_celestia(l); - - Renderer* renderer = appCore->getRenderer(); - if (renderer == NULL) - { - doError(l, "Internal Error: renderer is NULL!"); - } - else - { - lua_pushnumber(l, renderer->getDistanceLimit()); - } - return 1; -} - -static int celestia_getstarstyle(lua_State* l) -{ - checkArgs(l, 1, 1, "No argument expected in celestia:getstarstyle"); - CelestiaCore* appCore = this_celestia(l); - - Renderer* renderer = appCore->getRenderer(); - if (renderer == NULL) - { - doError(l, "Internal Error: renderer is NULL!"); - } - else - { - Renderer::StarStyle starStyle = renderer->getStarStyle(); - switch (starStyle) - { - case Renderer::FuzzyPointStars: - lua_pushstring(l, "fuzzy"); break; - case Renderer::PointStars: - lua_pushstring(l, "point"); break; - case Renderer::ScaledDiscStars: - lua_pushstring(l, "disc"); break; - default: - lua_pushstring(l, "invalid starstyle"); - }; - } - return 1; -} - -static int celestia_setstarstyle(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected in celestia:setstarstyle"); - CelestiaCore* appCore = this_celestia(l); - - string starStyle = safeGetString(l, 2, AllErrors, "Argument to celestia:setstarstyle must be a string"); - Renderer* renderer = appCore->getRenderer(); - if (renderer == NULL) - { - doError(l, "Internal Error: renderer is NULL!"); - } - else - { - if (starStyle == "fuzzy") - { - renderer->setStarStyle(Renderer::FuzzyPointStars); - } - else if (starStyle == "point") - { - renderer->setStarStyle(Renderer::PointStars); - } - else if (starStyle == "disc") - { - renderer->setStarStyle(Renderer::ScaledDiscStars); - } - else - { - doError(l, "Invalid starstyle"); - } - appCore->notifyWatchers(CelestiaCore::RenderFlagsChanged); - - } - return 0; -} - -static int celestia_getstar(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected to function celestia:getstar"); - - CelestiaCore* appCore = this_celestia(l); - double starIndex = safeGetNumber(l, 2, AllErrors, "First arg to celestia:getstar must be a number"); - Universe* u = appCore->getSimulation()->getUniverse(); - Star* star = u->getStarCatalog()->getStar((uint32) starIndex); - if (star == NULL) - lua_pushnil(l); - else - object_new(l, Selection(star)); - - return 1; -} - -static int celestia_getdso(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected to function celestia:getdso"); - - CelestiaCore* appCore = this_celestia(l); - double dsoIndex = safeGetNumber(l, 2, AllErrors, "First arg to celestia:getdso must be a number"); - Universe* u = appCore->getSimulation()->getUniverse(); - DeepSkyObject* dso = u->getDSOCatalog()->getDSO((uint32) dsoIndex); - if (dso == NULL) - lua_pushnil(l); - else - object_new(l, Selection(dso)); - - return 1; -} - - -static int celestia_newvector(lua_State* l) -{ - checkArgs(l, 4, 4, "Expected 3 arguments for celestia:newvector"); - // for error checking only: - this_celestia(l); - double x = safeGetNumber(l, 2, AllErrors, "First arg to celestia:newvector must be a number"); - double y = safeGetNumber(l, 3, AllErrors, "Second arg to celestia:newvector must be a number"); - double z = safeGetNumber(l, 4, AllErrors, "Third arg to celestia:newvector must be a number"); - - vector_new(l, Vec3d(x,y,z)); - - return 1; -} - -static int celestia_newposition(lua_State* l) -{ - checkArgs(l, 4, 4, "Expected 3 arguments for celestia:newposition"); - // for error checking only: - this_celestia(l); - BigFix components[3]; - for (int i = 0; i < 3; i++) - { - if (lua_isnumber(l, i+2)) - { - double v = lua_tonumber(l, i+2); - components[i] = BigFix(v); - } - else if (lua_isstring(l, i+2)) - { - components[i] = BigFix(string(lua_tostring(l, i+2))); - } - else - { - doError(l, "Arguments to celestia:newposition must be either numbers or strings"); - } - } - - position_new(l, UniversalCoord(components[0], components[1], components[2])); - - return 1; -} - -static int celestia_newrotation(lua_State* l) -{ - checkArgs(l, 3, 5, "Need 2 or 4 arguments for celestia:newrotation"); - // for error checking only: - this_celestia(l); - - if (lua_gettop(l) > 3) - { - // if (lua_gettop == 4), safeGetNumber will catch the error - double w = safeGetNumber(l, 2, AllErrors, "arguments to celestia:newrotation must either be (vec, number) or four numbers"); - double x = safeGetNumber(l, 3, AllErrors, "arguments to celestia:newrotation must either be (vec, number) or four numbers"); - double y = safeGetNumber(l, 4, AllErrors, "arguments to celestia:newrotation must either be (vec, number) or four numbers"); - double z = safeGetNumber(l, 5, AllErrors, "arguments to celestia:newrotation must either be (vec, number) or four numbers"); - Quatd q(w, x, y, z); - rotation_new(l, q); - } - else - { - Vec3d* v = to_vector(l, 2); - if (v == NULL) - { - doError(l, "newrotation: first argument must be a vector"); - } - double angle = safeGetNumber(l, 3, AllErrors, "second argument to celestia:newrotation must be a number"); - Quatd q; - q.setAxisAngle(*v, angle); - rotation_new(l, q); - } - return 1; -} - -static int celestia_getscripttime(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for celestia:getscripttime"); - // for error checking only: - this_celestia(l); - - LuaState* luastate_ptr = getLuaStateObject(l); - lua_pushnumber(l, luastate_ptr->getTime()); - return 1; -} - -static int celestia_newframe(lua_State* l) -{ - checkArgs(l, 2, 4, "One to three arguments expected for function celestia:newframe"); - int argc = lua_gettop(l); - - // for error checking only: - this_celestia(l); - - const char* coordsysName = safeGetString(l, 2, AllErrors, "newframe: first argument must be a string"); - astro::CoordinateSystem coordSys = parseCoordSys(coordsysName); - Selection* ref = NULL; - Selection* target = NULL; - - if (coordSys == astro::Universal) - { - frame_new(l, FrameOfReference()); - } - else if (coordSys == astro::PhaseLock) - { - if (argc >= 4) - { - ref = to_object(l, 3); - target = to_object(l, 4); - } - - if (ref == NULL || target == NULL) - { - doError(l, "newframe: two objects required for lock frame"); - } - - frame_new(l, FrameOfReference(coordSys, *ref, *target)); - } - else - { - if (argc >= 3) - ref = to_object(l, 3); - if (ref == NULL) - { - doError(l, "newframe: one object argument required for frame"); - } - - frame_new(l, FrameOfReference(coordSys, *ref)); - } - - return 1; -} - -static int celestia_requestkeyboard(lua_State* l) -{ - checkArgs(l, 2, 2, "Need one arguments for celestia:requestkeyboard"); - CelestiaCore* appCore = this_celestia(l); - - if (!lua_isboolean(l, 2)) - { - doError(l, "First argument for celestia:requestkeyboard must be a boolean"); - } - - int mode = appCore->getTextEnterMode(); - - if (lua_toboolean(l, 2)) - { - // Check for existence of charEntered: - lua_pushstring(l, KbdCallback); - lua_gettable(l, LUA_GLOBALSINDEX); - if (lua_isnil(l, -1)) - { - doError(l, "script requested keyboard, but did not provide callback"); - } - lua_remove(l, -1); - - mode = mode | CelestiaCore::KbPassToScript; - } - else - { - mode = mode & ~CelestiaCore::KbPassToScript; - } - appCore->setTextEnterMode(mode); - - return 0; -} - -static int celestia_registereventhandler(lua_State* l) -{ - checkArgs(l, 3, 3, "Two arguments required for celestia:registereventhandler"); - //CelestiaCore* appCore = this_celestia(l); - - if (!lua_isstring(l, 2)) - { - doError(l, "First argument for celestia:registereventhandler must be a string"); - } - - if (!lua_isfunction(l, 3) && !lua_isnil(l, 3)) - { - doError(l, "Second argument for celestia:registereventhandler must be a function or nil"); - } - - lua_pushstring(l, EventHandlers); - lua_gettable(l, LUA_REGISTRYINDEX); - if (lua_isnil(l, -1)) - { - // This should never happen--the table should be created when a new Celestia Lua - // state is initialized. - doError(l, "Event handler table not created"); - } - - lua_pushvalue(l, 2); - lua_pushvalue(l, 3); - - lua_settable(l, -3); - - return 0; -} - -static int celestia_geteventhandler(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected for celestia:registereventhandler"); - //CelestiaCore* appCore = this_celestia(l); - - if (!lua_isstring(l, 2)) - { - doError(l, "Argument to celestia:geteventhandler must be a string"); - } - - lua_pushstring(l, EventHandlers); - lua_gettable(l, LUA_REGISTRYINDEX); - if (lua_isnil(l, -1)) - { - // This should never happen--the table should be created when a new Celestia Lua - // state is initialized. - doError(l, "Event handler table not created"); - } - - lua_pushvalue(l, 2); - lua_gettable(l, -2); - - return 1; -} - -static int celestia_takescreenshot(lua_State* l) -{ - checkArgs(l, 1, 3, "Need 0 to 2 arguments for celestia:takescreenshot"); - CelestiaCore* appCore = this_celestia(l); - LuaState* luastate = getLuaStateObject(l); - // make sure we don't timeout because of taking a screenshot: - double timeToTimeout = luastate->timeout - luastate->getTime(); - - const char* filetype = safeGetString(l, 2, WrongType, "First argument to celestia:takescreenshot must be a string"); - if (filetype == NULL) - filetype = "png"; - - // Let the script safely contribute one part of the filename: - const char* fileid_ptr = safeGetString(l, 3, WrongType, "Second argument to celestia:takescreenshot must be a string"); - if (fileid_ptr == NULL) - fileid_ptr = ""; - string fileid(fileid_ptr); - - // be paranoid about the fileid, make sure it only contains 'A-Za-z0-9_': - for (unsigned int i = 0; i < fileid.length(); i++) - { - char ch = fileid[i]; - if (!((ch >= 'a' && ch <= 'z') || - (fileid[i] >= 'A' && ch <= 'Z') || - (ch >= '0' && ch <= '9') ) ) - fileid[i] = '_'; - } - // limit length of string - if (fileid.length() > 16) - fileid = fileid.substr(0, 16); - if (fileid.length() > 0) - fileid.append("-"); - - string path = appCore->getConfig()->scriptScreenshotDirectory; - if (path.length() > 0 && - path[path.length()-1] != '/' && - path[path.length()-1] != '\\') - - path.append("/"); - - luastate->screenshotCount++; - bool success = false; - char filenamestem[48]; - sprintf(filenamestem, "screenshot-%s%06i", fileid.c_str(), luastate->screenshotCount); - - // Get the dimensions of the current viewport - int viewport[4]; - glGetIntegerv(GL_VIEWPORT, viewport); - -#ifndef TARGET_OS_MAC - if (strncmp(filetype, "jpg", 3) == 0) - { - string filepath = path + filenamestem + ".jpg"; - success = CaptureGLBufferToJPEG(string(filepath), - viewport[0], viewport[1], - viewport[2], viewport[3]); - } - else - { - string filepath = path + filenamestem + ".png"; - success = CaptureGLBufferToPNG(string(filepath), - viewport[0], viewport[1], - viewport[2], viewport[3]); - } -#endif - lua_pushboolean(l, success); - - // no matter how long it really took, make it look like 0.1s to timeout check: - luastate->timeout = luastate->getTime() + timeToTimeout - 0.1; - return 1; -} - -static int celestia_createcelscript(lua_State* l) -{ - checkArgs(l, 2, 2, "Need one argument for celestia:createcelscript()"); - string scripttext = safeGetString(l, 2, AllErrors, "Argument to celestia:createcelscript() must be a string"); - return celscript_from_string(l, scripttext); -} - -static int celestia_requestsystemaccess(lua_State* l) -{ - // ignore possible argument for future extensions - checkArgs(l, 1, 2, "No argument expected for celestia:requestsystemaccess()"); - this_celestia(l); - LuaState* luastate = getLuaStateObject(l); - luastate->requestIO(); - return 0; -} - -static int celestia_getscriptpath(lua_State* l) -{ - // ignore possible argument for future extensions - checkArgs(l, 1, 1, "No argument expected for celestia:requestsystemaccess()"); - this_celestia(l); - lua_pushstring(l, "celestia-scriptpath"); - lua_gettable(l, LUA_REGISTRYINDEX); - return 1; -} - -static int celestia_tostring(lua_State* l) -{ - lua_pushstring(l, "[Celestia]"); - - return 1; -} - -static void CreateCelestiaMetaTable(lua_State* l) -{ - CreateClassMetatable(l, _Celestia); - - RegisterMethod(l, "__tostring", celestia_tostring); - RegisterMethod(l, "flash", celestia_flash); - RegisterMethod(l, "print", celestia_print); - RegisterMethod(l, "gettextwidth", celestia_gettextwidth); - RegisterMethod(l, "show", celestia_show); - RegisterMethod(l, "setaltazimuthmode", celestia_setaltazimuthmode); - RegisterMethod(l, "getaltazimuthmode", celestia_getaltazimuthmode); - RegisterMethod(l, "hide", celestia_hide); - RegisterMethod(l, "getrenderflags", celestia_getrenderflags); - RegisterMethod(l, "setrenderflags", celestia_setrenderflags); - RegisterMethod(l, "getscreendimension", celestia_getscreendimension); - RegisterMethod(l, "showlabel", celestia_showlabel); - RegisterMethod(l, "hidelabel", celestia_hidelabel); - RegisterMethod(l, "getlabelflags", celestia_getlabelflags); - RegisterMethod(l, "setlabelflags", celestia_setlabelflags); - RegisterMethod(l, "getorbitflags", celestia_getorbitflags); - RegisterMethod(l, "setorbitflags", celestia_setorbitflags); - RegisterMethod(l, "setlabelcolor", celestia_setlabelcolor); - RegisterMethod(l, "setlinecolor", celestia_setlinecolor); - RegisterMethod(l, "getoverlayelements", celestia_getoverlayelements); - RegisterMethod(l, "setoverlayelements", celestia_setoverlayelements); - RegisterMethod(l, "getfaintestvisible", celestia_getfaintestvisible); - RegisterMethod(l, "setfaintestvisible", celestia_setfaintestvisible); - RegisterMethod(l, "getgalaxylightgain", celestia_getgalaxylightgain); - RegisterMethod(l, "setgalaxylightgain", celestia_setgalaxylightgain); - RegisterMethod(l, "setminfeaturesize", celestia_setminfeaturesize); - RegisterMethod(l, "getminfeaturesize", celestia_getminfeaturesize); - RegisterMethod(l, "getobserver", celestia_getobserver); - RegisterMethod(l, "getobservers", celestia_getobservers); - RegisterMethod(l, "getselection", celestia_getselection); - RegisterMethod(l, "find", celestia_find); - RegisterMethod(l, "select", celestia_select); - RegisterMethod(l, "mark", celestia_mark); - RegisterMethod(l, "unmark", celestia_unmark); - RegisterMethod(l, "unmarkall", celestia_unmarkall); - RegisterMethod(l, "gettime", celestia_gettime); - RegisterMethod(l, "settime", celestia_settime); - RegisterMethod(l, "gettimescale", celestia_gettimescale); - RegisterMethod(l, "settimescale", celestia_settimescale); - RegisterMethod(l, "getambient", celestia_getambient); - RegisterMethod(l, "setambient", celestia_setambient); - RegisterMethod(l, "getminorbitsize", celestia_getminorbitsize); - RegisterMethod(l, "setminorbitsize", celestia_setminorbitsize); - RegisterMethod(l, "getstardistancelimit", celestia_getstardistancelimit); - RegisterMethod(l, "setstardistancelimit", celestia_setstardistancelimit); - RegisterMethod(l, "getstarstyle", celestia_getstarstyle); - RegisterMethod(l, "setstarstyle", celestia_setstarstyle); - RegisterMethod(l, "tojulianday", celestia_tojulianday); - RegisterMethod(l, "fromjulianday", celestia_fromjulianday); - RegisterMethod(l, "utctotdb", celestia_utctotdb); - RegisterMethod(l, "tdbtoutc", celestia_tdbtoutc); - RegisterMethod(l, "getstarcount", celestia_getstarcount); - RegisterMethod(l, "getdsocount", celestia_getdsocount); - RegisterMethod(l, "getstar", celestia_getstar); - RegisterMethod(l, "getdso", celestia_getdso); - RegisterMethod(l, "newframe", celestia_newframe); - RegisterMethod(l, "newvector", celestia_newvector); - RegisterMethod(l, "newposition", celestia_newposition); - RegisterMethod(l, "newrotation", celestia_newrotation); - RegisterMethod(l, "getscripttime", celestia_getscripttime); - RegisterMethod(l, "requestkeyboard", celestia_requestkeyboard); - RegisterMethod(l, "takescreenshot", celestia_takescreenshot); - RegisterMethod(l, "createcelscript", celestia_createcelscript); - RegisterMethod(l, "requestsystemaccess", celestia_requestsystemaccess); - RegisterMethod(l, "getscriptpath", celestia_getscriptpath); - RegisterMethod(l, "registereventhandler", celestia_registereventhandler); - RegisterMethod(l, "geteventhandler", celestia_geteventhandler); - RegisterMethod(l, "stars", celestia_stars); - RegisterMethod(l, "dsos", celestia_dsos); - - lua_pop(l, 1); -} - -static void loadLuaLibs(lua_State* state); - -// ==================== Initialization ==================== -bool LuaState::init(CelestiaCore* appCore) -{ - initMaps(); - - // Import the base, table, string, and math libraries -#if LUA_VER >= 0x050100 - openLuaLibrary(state, "", luaopen_base); - openLuaLibrary(state, LUA_MATHLIBNAME, luaopen_math); - openLuaLibrary(state, LUA_TABLIBNAME, luaopen_table); - openLuaLibrary(state, LUA_STRLIBNAME, luaopen_string); -#else - lua_baselibopen(state); - lua_mathlibopen(state); - lua_tablibopen(state); - lua_strlibopen(state); -#endif - - // Add an easy to use wait function, so that script writers can - // live in ignorance of coroutines. There will probably be a significant - // library of useful functions that can be defined purely in Lua. - // At that point, we'll want something a bit more robust than just - // parsing the whole text of the library every time a script is launched - if (loadScript("wait = function(x) coroutine.yield(x) end") != 0) - return false; - - // Execute the script fragment to define the wait function - if (lua_pcall(state, 0, 0, 0) != 0) - { - cout << "Error running script initialization fragment.\n"; - return false; - } - - lua_pushstring(state, "KM_PER_MICROLY"); - lua_pushnumber(state, (lua_Number)KM_PER_LY/1e6); - lua_settable(state, LUA_GLOBALSINDEX); - - loadLuaLibs(state); - - // Create the celestia object - lua_pushstring(state, "celestia"); - celestia_new(state, appCore); - lua_settable(state, LUA_GLOBALSINDEX); - // add reference to appCore in the registry - lua_pushstring(state, "celestia-appcore"); - lua_pushlightuserdata(state, static_cast(appCore)); - lua_settable(state, LUA_REGISTRYINDEX); - // add a reference to the LuaState-object in the registry - lua_pushstring(state, "celestia-luastate"); - lua_pushlightuserdata(state, static_cast(this)); - lua_settable(state, LUA_REGISTRYINDEX); - - lua_pushstring(state, EventHandlers); - lua_newtable(state); - lua_settable(state, LUA_REGISTRYINDEX); - -#if 0 - lua_pushstring(state, "dofile"); - lua_gettable(state, LUA_GLOBALSINDEX); // function "dofile" on stack - lua_pushstring(state, "luainit.celx"); // parameter - if (lua_pcall(state, 1, 0, 0) != 0) // execute it - { - CelestiaCore::Alerter* alerter = appCore->getAlerter(); - // copy string?! - const char* errorMessage = lua_tostring(state, -1); - cout << errorMessage << '\n'; cout.flush(); - alerter->fatalError(errorMessage); - return false; - } -#endif - - return true; -} - - -void LuaState::setLuaPath(const string& s) -{ -#if LUA_VER >= 0x050100 - lua_getfield(state, LUA_GLOBALSINDEX, "package"); - lua_pushstring(state, s.c_str()); - lua_setfield(state, -2, "path"); - lua_pop(state, 1); -#else - lua_pushstring(state, "LUA_PATH"); - lua_pushstring(state, s.c_str()); - lua_settable(state, LUA_GLOBALSINDEX); -#endif -} - - -// ==================== OpenGL ==================== - -static int glu_LookAt(lua_State* l) -{ - checkArgs(l, 9, 9, "Nine arguments expected for glu.LookAt()"); - float ix = (float)safeGetNumber(l, 1, WrongType, "argument 1 to gl.Frustum must be a number", 0.0); - float iy = (float)safeGetNumber(l, 2, WrongType, "argument 2 to gl.Frustum must be a number", 0.0); - float iz = (float)safeGetNumber(l, 3, WrongType, "argument 3 to gl.Frustum must be a number", 0.0); - float cx = (float)safeGetNumber(l, 4, WrongType, "argument 4 to gl.Frustum must be a number", 0.0); - float cy = (float)safeGetNumber(l, 5, WrongType, "argument 5 to gl.Frustum must be a number", 0.0); - float cz = (float)safeGetNumber(l, 6, WrongType, "argument 6 to gl.Frustum must be a number", 0.0); - float ux = (float)safeGetNumber(l, 7, WrongType, "argument 4 to gl.Frustum must be a number", 0.0); - float uy = (float)safeGetNumber(l, 8, WrongType, "argument 5 to gl.Frustum must be a number", 0.0); - float uz = (float)safeGetNumber(l, 9, WrongType, "argument 6 to gl.Frustum must be a number", 0.0); - gluLookAt(ix,iy,iz,cx,cy,cz,ux,uy,uz); - return 0; -} - -static int gl_Frustum(lua_State* l) -{ - checkArgs(l, 6, 6, "Six arguments expected for gl.Frustum()"); - float ll = (float)safeGetNumber(l, 1, WrongType, "argument 1 to gl.Frustum must be a number", 0.0); - float r = (float)safeGetNumber(l, 2, WrongType, "argument 2 to gl.Frustum must be a number", 0.0); - float b = (float)safeGetNumber(l, 3, WrongType, "argument 3 to gl.Frustum must be a number", 0.0); - float t = (float)safeGetNumber(l, 4, WrongType, "argument 4 to gl.Frustum must be a number", 0.0); - float n = (float)safeGetNumber(l, 5, WrongType, "argument 5 to gl.Frustum must be a number", 0.0); - float f = (float)safeGetNumber(l, 6, WrongType, "argument 6 to gl.Frustum must be a number", 0.0); - glFrustum(ll,r,b,t,n,f); - return 0; -} - -static int gl_Ortho(lua_State* l) -{ - checkArgs(l, 6, 6, "Six arguments expected for gl.Ortho()"); - float ll = (float)safeGetNumber(l, 1, WrongType, "argument 1 to gl.Ortho must be a number", 0.0); - float r = (float)safeGetNumber(l, 2, WrongType, "argument 2 to gl.Ortho must be a number", 0.0); - float b = (float)safeGetNumber(l, 3, WrongType, "argument 3 to gl.Ortho must be a number", 0.0); - float t = (float)safeGetNumber(l, 4, WrongType, "argument 4 to gl.Ortho must be a number", 0.0); - float n = (float)safeGetNumber(l, 5, WrongType, "argument 5 to gl.Ortho must be a number", 0.0); - float f = (float)safeGetNumber(l, 6, WrongType, "argument 6 to gl.Ortho must be a number", 0.0); - glOrtho(ll,r,b,t,n,f); - return 0; -} - -static int glu_Ortho2D(lua_State* l) -{ - checkArgs(l, 4, 4, "Six arguments expected for gl.Ortho2D()"); - float ll = (float)safeGetNumber(l, 1, WrongType, "argument 1 to gl.Ortho must be a number", 0.0); - float r = (float)safeGetNumber(l, 2, WrongType, "argument 2 to gl.Ortho must be a number", 0.0); - float b = (float)safeGetNumber(l, 3, WrongType, "argument 3 to gl.Ortho must be a number", 0.0); - float t = (float)safeGetNumber(l, 4, WrongType, "argument 4 to gl.Ortho must be a number", 0.0); - gluOrtho2D(ll,r,b,t); - return 0; -} - -static int gl_TexCoord(lua_State* l) -{ - checkArgs(l, 2, 2, "Two arguments expected for gl.TexCoord()"); - float x = (float)safeGetNumber(l, 1, WrongType, "argument 1 to gl.TexCoord must be a number", 0.0); - float y = (float)safeGetNumber(l, 2, WrongType, "argument 2 to gl.TexCoord must be a number", 0.0); - glTexCoord2f(x,y); - return 0; -} - -static int gl_TexParameter(lua_State* l) -{ - checkArgs(l, 3, 3, "Three arguments expected for gl.TexParameter()"); - - // TODO: Need to ensure that these values are integers, or better yet use - // names. - float x = (float)safeGetNumber(l, 1, WrongType, "argument 1 to gl.TexParameter must be a number", 0.0); - float y = (float)safeGetNumber(l, 2, WrongType, "argument 2 to gl.TexParameter must be a number", 0.0); - float z = (float)safeGetNumber(l, 3, WrongType, "argument 3 to gl.TexParameter must be a number", 0.0); - glTexParameteri((GLint) x, (GLenum) y, (GLenum) z); - return 0; -} - -static int gl_Vertex(lua_State* l) -{ - checkArgs(l, 2, 2, "Two arguments expected for gl.Vertex()"); - float x = (float)safeGetNumber(l, 1, WrongType, "argument 1 to gl.Vertex must be a number", 0.0); - float y = (float)safeGetNumber(l, 2, WrongType, "argument 2 to gl.Vertex must be a number", 0.0); - glVertex2f(x,y); - return 0; -} - -static int gl_Color(lua_State* l) -{ - checkArgs(l, 4, 4, "Four arguments expected for gl.Color()"); - float r = (float)safeGetNumber(l, 1, WrongType, "argument 1 to gl.Color must be a number", 0.0); - float g = (float)safeGetNumber(l, 2, WrongType, "argument 2 to gl.Color must be a number", 0.0); - float b = (float)safeGetNumber(l, 3, WrongType, "argument 3 to gl.Color must be a number", 0.0); - float a = (float)safeGetNumber(l, 4, WrongType, "argument 4 to gl.Color must be a number", 0.0); - glColor4f(r,g,b,a); -// glColor4f(0.8f, 0.5f, 0.5f, 1.0f); - return 0; -} - -static int gl_LineWidth(lua_State* l) -{ - checkArgs(l, 1, 1, "One argument expected for gl.LineWidth()"); - float n = (float)safeGetNumber(l, 1, WrongType, "argument 1 to gl.LineWidth must be a number", 1.0); - glLineWidth(n); - return 0; -} - -static int gl_Translate(lua_State* l) -{ - checkArgs(l, 2, 2, "Two arguments expected for gl.Translate()"); - float x = (float)safeGetNumber(l, 1, WrongType, "argument 1 to gl.Translate must be a number", 0.0); - float y = (float)safeGetNumber(l, 2, WrongType, "argument 2 to gl.Translate must be a number", 0.0); - glTranslatef(x,y,0.0f); - return 0; -} - -static int gl_BlendFunc(lua_State* l) -{ - checkArgs(l, 2, 2, "Two arguments expected for gl.BlendFunc()"); - int i = (int)safeGetNumber(l, 1, WrongType, "argument 1 to gl.BlendFunc must be a number", 0.0); - int j = (int)safeGetNumber(l, 2, WrongType, "argument 2 to gl.BlendFunc must be a number", 0.0); - glBlendFunc(i,j); - return 0; -} - -static int gl_Begin(lua_State* l) -{ - checkArgs(l, 1, 1, "One argument expected for gl.Begin()"); - int i = (int)safeGetNumber(l, 1, WrongType, "argument 1 to gl.Begin must be a number", 0.0); - glBegin(i); - return 0; -} - -static int gl_End(lua_State* l) -{ - checkArgs(l, 0, 0, "No arguments expected for gl.PopMatrix()"); - glEnd(); - return 0; -} - -static int gl_Enable(lua_State* l) -{ - checkArgs(l, 1, 1, "One argument expected for gl.Enable()"); - int i = (int)safeGetNumber(l, 1, WrongType, "argument 1 to gl.Enable must be a number", 0.0); - glEnable(i); - return 0; -} - -static int gl_Disable(lua_State* l) -{ - checkArgs(l, 1, 1, "One argument expected for gl.Disable()"); - int i = (int)safeGetNumber(l, 1, WrongType, "argument 1 to gl.Disable must be a number", 0.0); - glDisable(i); - return 0; -} - -static int gl_MatrixMode(lua_State* l) -{ - checkArgs(l, 1, 1, "One argument expected for gl.MatrixMode()"); - int i = (int)safeGetNumber(l, 1, WrongType, "argument 1 to gl.MatrixMode must be a number", 0.0); - glMatrixMode(i); - return 0; -} - -static int gl_PopMatrix(lua_State* l) -{ - checkArgs(l, 0, 0, "No arguments expected for gl.PopMatrix()"); - glPopMatrix(); - return 0; -} - -static int gl_LoadIdentity(lua_State* l) -{ - checkArgs(l, 0, 0, "No arguments expected for gl.LoadIdentity()"); - glLoadIdentity(); - return 0; -} - -static int gl_PushMatrix(lua_State* l) -{ - checkArgs(l, 0, 0, "No arguments expected for gl.PushMatrix()"); - glPushMatrix(); - return 0; -} - -static void RegisterValue(lua_State* l, const char* name, float n) -{ - lua_pushstring(l, name); - lua_pushnumber(l, n); - lua_settable(l, -3); -} - -static void gl_loadlib(lua_State* l) -{ - lua_pushstring(l, "gl"); - lua_newtable(l); - - RegisterMethod(l, "Frustum", gl_Frustum); - RegisterMethod(l, "Ortho", gl_Ortho); - RegisterMethod(l, "Color", gl_Color); - RegisterMethod(l, "LineWidth", gl_LineWidth); - RegisterMethod(l, "TexCoord", gl_TexCoord); - RegisterMethod(l, "TexParameter", gl_TexParameter); - RegisterMethod(l, "Vertex", gl_Vertex); - RegisterMethod(l, "Translate", gl_Translate); - RegisterMethod(l, "BlendFunc", gl_BlendFunc); - RegisterMethod(l, "Begin", gl_Begin); - RegisterMethod(l, "End", gl_End); - RegisterMethod(l, "Enable", gl_Enable); - RegisterMethod(l, "Disable", gl_Disable); - RegisterMethod(l, "MatrixMode", gl_MatrixMode); - RegisterMethod(l, "PopMatrix", gl_PopMatrix); - RegisterMethod(l, "LoadIdentity", gl_LoadIdentity); - RegisterMethod(l, "PushMatrix", gl_PushMatrix); - - RegisterValue(l, "QUADS", GL_QUADS); - RegisterValue(l, "LIGHTING", GL_LIGHTING); - RegisterValue(l, "POINTS", GL_POINTS); - RegisterValue(l, "LINES", GL_LINES); - RegisterValue(l, "LINE_LOOP", GL_LINE_LOOP); - RegisterValue(l, "LINE_SMOOTH", GL_LINE_SMOOTH); - RegisterValue(l, "POLYGON", GL_POLYGON); - RegisterValue(l, "PROJECTION", GL_PROJECTION); - RegisterValue(l, "MODELVIEW", GL_MODELVIEW); - RegisterValue(l, "BLEND", GL_BLEND); - RegisterValue(l, "TEXTURE_2D", GL_TEXTURE_2D); - RegisterValue(l, "TEXTURE_MAG_FILTER", GL_TEXTURE_MAG_FILTER); - RegisterValue(l, "TEXTURE_MIN_FILTER", GL_TEXTURE_MIN_FILTER); - RegisterValue(l, "LINEAR", GL_LINEAR); - RegisterValue(l, "NEAREST", GL_NEAREST); - RegisterValue(l, "SRC_ALPHA", GL_SRC_ALPHA); - RegisterValue(l, "ONE_MINUS_SRC_ALPHA", GL_ONE_MINUS_SRC_ALPHA); - lua_settable(l, LUA_GLOBALSINDEX); - - lua_pushstring(l, "glu"); - lua_newtable(l); - RegisterMethod(l, "LookAt", glu_LookAt); - RegisterMethod(l, "Ortho2D", glu_Ortho2D); - lua_settable(l, LUA_GLOBALSINDEX); -} - -// ==================== Font Object ==================== - -static int font_new(lua_State* l, TextureFont* f) -{ - TextureFont** ud = static_cast(lua_newuserdata(l, sizeof(TextureFont*))); - *ud = f; - - SetClass(l, _Font); - - return 1; -} - -static TextureFont* to_font(lua_State* l, int index) -{ - TextureFont** f = static_cast(lua_touserdata(l, index)); - - // Check if pointer is valid - if (f != NULL ) - { - return *f; - } - return NULL; -} - -static TextureFont* this_font(lua_State* l) -{ - TextureFont* f = to_font(l, 1); - if (f == NULL) - { - doError(l, "Bad font object!"); - } - - return f; -} - - -static int font_bind(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for font:bind()"); - - TextureFont* font = this_font(l); - font->bind(); - return 0; -} - -static int font_render(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument required for font:render"); - - const char* s = safeGetString(l, 2, AllErrors, "First argument to font:render must be a string"); - TextureFont* font = this_font(l); - font->render(s); - - return 0; -} - -static int font_getwidth(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected for font:getwidth"); - const char* s = safeGetString(l, 2, AllErrors, "Argument to font:getwidth must be a string"); - TextureFont* font = this_font(l); - lua_pushnumber(l, font->getWidth(s)); - return 1; -} - -static int font_getheight(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for font:getheight()"); - - TextureFont* font = this_font(l); - lua_pushnumber(l, font->getHeight()); - return 1; -} - -static int font_tostring(lua_State* l) -{ - // TODO: print out the actual information about the font - lua_pushstring(l, "[Font]"); - - return 1; -} - -static void CreateFontMetaTable(lua_State* l) -{ - CreateClassMetatable(l, _Font); - - RegisterMethod(l, "__tostring", font_tostring); - RegisterMethod(l, "bind", font_bind); - RegisterMethod(l, "render", font_render); - RegisterMethod(l, "getwidth", font_getwidth); - RegisterMethod(l, "getheight", font_getheight); - - lua_pop(l, 1); // remove metatable from stack -} - -// ==================== Image ============================================= -#if 0 -static int image_new(lua_State* l, Image* i) -{ - Image** ud = static_cast(lua_newuserdata(l, sizeof(Image*))); - *ud = i; - - SetClass(l, _Image); - - return 1; -} -#endif - -static Image* to_image(lua_State* l, int index) -{ - Image** image = static_cast(lua_touserdata(l, index)); - - // Check if pointer is valid - if (image != NULL ) - { - return *image; - } - return NULL; -} - -static Image* this_image(lua_State* l) -{ - Image* image = to_image(l,1); - if (image == NULL) - { - doError(l, "Bad image object!"); - } - - return image; -} - -static int image_getheight(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for image:getheight()"); - - Image* image = this_image(l); - lua_pushnumber(l, image->getHeight()); - return 1; -} - -static int image_getwidth(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for image:getwidth()"); - - Image* image = this_image(l); - lua_pushnumber(l, image->getWidth()); - return 1; -} - -static int image_tostring(lua_State* l) -{ - // TODO: print out the actual information about the image - lua_pushstring(l, "[Image]"); - - return 1; -} - -static void CreateImageMetaTable(lua_State* l) -{ - CreateClassMetatable(l, _Image); - - RegisterMethod(l, "__tostring", image_tostring); - RegisterMethod(l, "getheight", image_getheight); - RegisterMethod(l, "getwidth", image_getwidth); - - lua_pop(l, 1); // remove metatable from stack -} - -// ==================== Texture ============================================ - -static int texture_new(lua_State* l, Texture* t) -{ - Texture** ud = static_cast(lua_newuserdata(l, sizeof(Texture*))); - *ud = t; - - SetClass(l, _Texture); - - return 1; -} - -static Texture* to_texture(lua_State* l, int index) -{ - Texture** texture = static_cast(lua_touserdata(l, index)); - - // Check if pointer is valid - if (texture != NULL ) - { - return *texture; - } - return NULL; -} - -static Texture* this_texture(lua_State* l) -{ - Texture* texture = to_texture(l,1); - if (texture == NULL) - { - doError(l, "Bad texture object!"); - } - - return texture; -} - -static int texture_bind(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for texture:bind()"); - - Texture* texture = this_texture(l); - texture->bind(); - return 0; -} - -static int texture_getheight(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for texture:getheight()"); - - Texture* texture = this_texture(l); - lua_pushnumber(l, texture->getHeight()); - return 1; -} - -static int texture_getwidth(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected for texture:getwidth()"); - - Texture* texture = this_texture(l); - lua_pushnumber(l, texture->getWidth()); - return 1; -} - -static int texture_tostring(lua_State* l) -{ - // TODO: print out the actual information about the texture - lua_pushstring(l, "[Texture]"); - - return 1; -} - -static void CreateTextureMetaTable(lua_State* l) -{ - CreateClassMetatable(l, _Texture); - - RegisterMethod(l, "__tostring", texture_tostring); - RegisterMethod(l, "getheight", texture_getheight); - RegisterMethod(l, "getwidth", texture_getwidth); - RegisterMethod(l, "bind", texture_bind); - - lua_pop(l, 1); // remove metatable from stack -} - -// ==================== object extensions ==================== - -// TODO: This should be replaced by an actual Atmosphere object -static int object_setatmosphere(lua_State* l) -{ - checkArgs(l, 23, 23, "22 arguments (!) expected to function object:setatmosphere"); - - Selection* sel = this_object(l); - //CelestiaCore* appCore = getAppCore(l, AllErrors); - - if (sel->body() != NULL) - { - Body* body = sel->body(); - Atmosphere* atmosphere = body->getAtmosphere(); - if (atmosphere != NULL) - { - float r = (float) safeGetNumber(l, 2, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); - float g = (float) safeGetNumber(l, 3, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); - float b = (float) safeGetNumber(l, 4, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); - // Color testColor(0.0f, 1.0f, 0.0f); - Color testColor(r, g, b); - atmosphere->lowerColor = testColor; - r = (float) safeGetNumber(l, 5, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); - g = (float) safeGetNumber(l, 6, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); - b = (float) safeGetNumber(l, 7, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); - atmosphere->upperColor = Color(r, g, b); - r = (float) safeGetNumber(l, 8, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); - g = (float) safeGetNumber(l, 9, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); - b = (float) safeGetNumber(l, 10, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); - atmosphere->skyColor = Color(r, g, b); - r = (float) safeGetNumber(l, 11, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); - g = (float) safeGetNumber(l, 12, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); - b = (float) safeGetNumber(l, 13, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); - atmosphere->sunsetColor = Color(r, g, b); - r = (float) safeGetNumber(l, 14, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); - g = (float) safeGetNumber(l, 15, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); - b = (float) safeGetNumber(l, 16, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); - //HWR atmosphere->rayleighCoeff = Vector3(r, g, b); - r = (float) safeGetNumber(l, 17, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); - g = (float) safeGetNumber(l, 18, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); - b = (float) safeGetNumber(l, 19, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); - //HWR atmosphere->absorptionCoeff = Vector3(r, g, b); - b = (float) safeGetNumber(l, 20, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); - atmosphere->mieCoeff = b; - b = (float) safeGetNumber(l, 21, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); - atmosphere->mieScaleHeight = b; - b = (float) safeGetNumber(l, 22, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); - atmosphere->miePhaseAsymmetry = b; - b = (float) safeGetNumber(l, 23, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); - atmosphere->rayleighScaleHeight = b; - - body->setAtmosphere(*atmosphere); - cout << "set atmosphere\n"; - } - } - - return 0; -} - -static void ExtendObjectMetaTable(lua_State* l) -{ - PushClass(l, _Object); - lua_rawget(l, LUA_REGISTRYINDEX); - if (lua_type(l, -1) != LUA_TTABLE) - cout << "Metatable for " << ClassNames[_Object] << " not found!\n"; - RegisterMethod(l, "setatmosphere", object_setatmosphere); - lua_pop(l, 1); -} -// ==================== celestia extensions ==================== - -static int celestia_log(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected to function celestia:log"); - - const char* s = safeGetString(l, 2, AllErrors, "First argument to celestia:log must be a string"); - clog << s << "\n"; clog.flush(); - return 0; -} - -static int celestia_getparamstring(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument expected to celestia:getparamstring()"); - CelestiaCore* appCore = this_celestia(l); - const char* s = safeGetString(l, 2, AllErrors, "Argument to celestia:getparamstring must be a string"); - std::string paramString; // HWR - CelestiaConfig* config = appCore->getConfig(); - config->configParams->getString(s, paramString); - lua_pushstring(l,paramString.c_str()); - return 1; -} - -static int celestia_loadtexture(lua_State* l) -{ - checkArgs(l, 2, 2, "Need one argument for celestia:loadtexture()"); - string s = safeGetString(l, 2, AllErrors, "Argument to celestia:loadtexture() must be a string"); - lua_Debug ar; - lua_getstack(l, 1, &ar); - lua_getinfo(l, "S", &ar); - string base_dir = ar.source; // Lua file from which we are called - if (base_dir[0] == '@') base_dir = base_dir.substr(1); - base_dir = base_dir.substr(0, base_dir.rfind('/')) + '/'; - Texture* t = LoadTextureFromFile(base_dir + s); - if (t == NULL) return 0; - texture_new(l, t); - return 1; -} - -static int celestia_loadfont(lua_State* l) -{ - checkArgs(l, 2, 2, "Need one argument for celestia:loadtexture()"); - string s = safeGetString(l, 2, AllErrors, "Argument to celestia:loadfont() must be a string"); - TextureFont* font = LoadTextureFont(s); - if (font == NULL) return 0; - font->buildTexture(); - font_new(l, font); - return 1; -} - -TextureFont* getFont(CelestiaCore* appCore) -{ - return appCore->font; -} - -static int celestia_getfont(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected to function celestia:getTitleFont"); - - CelestiaCore* appCore = getAppCore(l, AllErrors); - TextureFont* font = getFont(appCore); - if (font == NULL) return 0; - font_new(l, font); - return 1; -} - -TextureFont* getTitleFont(CelestiaCore* appCore) -{ - return appCore->titleFont; -} - -static int celestia_gettitlefont(lua_State* l) -{ - checkArgs(l, 1, 1, "No arguments expected to function celestia:getTitleFont"); - - CelestiaCore* appCore = getAppCore(l, AllErrors); - TextureFont* font = getTitleFont(appCore); - if (font == NULL) return 0; - font_new(l, font); - return 1; -} - -static int celestia_settimeslice(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument required for celestia:settimeslice"); - //CelestiaCore* appCore = this_celestia(l); - - if (!lua_isnumber(l, 2) && !lua_isnil(l, 2)) - { - doError(l, "Argument for celestia:settimeslice must be a number"); - } - double timeslice = safeGetNumber(l, 2, AllErrors, "Argument to celestia:settimeslice must be a number"); - if (timeslice == 0.0) - timeslice = 0.1; - - LuaState* luastate = getLuaStateObject(l); - luastate->timeout = luastate->getTime() + timeslice; - - return 0; -} - -static int celestia_setluahook(lua_State* l) -{ - checkArgs(l, 2, 2, "One argument required for celestia:setluahook"); - CelestiaCore* appCore = this_celestia(l); - - if (!lua_istable(l, 2) && !lua_isnil(l, 2)) - { - doError(l, "Argument for celestia:setluahook must be a table or nil"); - return 0; - } - - LuaState* luastate = getLuaStateObject(l); - if (luastate != NULL) - { - luastate->setLuaHookEventHandlerEnabled(lua_istable(l, 2)); - } - - lua_pushlightuserdata(l, appCore); - lua_pushvalue(l, -2); - lua_settable(l, LUA_REGISTRYINDEX); - - return 0; -} - -static void ExtendCelestiaMetaTable(lua_State* l) -{ - PushClass(l, _Celestia); - lua_rawget(l, LUA_REGISTRYINDEX); - if (lua_type(l, -1) != LUA_TTABLE) - cout << "Metatable for " << ClassNames[_Celestia] << " not found!\n"; - RegisterMethod(l, "log", celestia_log); - RegisterMethod(l, "settimeslice", celestia_settimeslice); - RegisterMethod(l, "setluahook", celestia_setluahook); - RegisterMethod(l, "getparamstring", celestia_getparamstring); - RegisterMethod(l, "getfont", celestia_getfont); - RegisterMethod(l, "gettitlefont", celestia_gettitlefont); - RegisterMethod(l, "loadtexture", celestia_loadtexture); - RegisterMethod(l, "loadfont", celestia_loadfont); - lua_pop(l, 1); -} - - -#if LUA_VER < 0x050100 -// ======================== loadlib =================================== -/* -* This is an implementation of loadlib based on the dlfcn interface. -* The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD, -* NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least -* as an emulation layer on top of native functions. -*/ - -#ifndef _WIN32 -extern "C" { -#include -/* #include */ -#include -} - -#if 0 -static int x_loadlib(lua_State *L) -{ -/* temp -- don't have lauxlib - const char *path=luaL_checkstring(L,1); - const char *init=luaL_checkstring(L,2); -*/ -cout << "loading lua lib\n"; cout.flush(); - - const char *path=lua_tostring(L,1); - const char *init=lua_tostring(L,2); - - void *lib=dlopen(path,RTLD_NOW); - if (lib!=NULL) - { - lua_CFunction f=(lua_CFunction) dlsym(lib,init); - if (f!=NULL) - { - lua_pushlightuserdata(L,lib); - lua_pushcclosure(L,f,1); - return 1; - } - } - /* else return appropriate error messages */ - lua_pushnil(L); - lua_pushstring(L,dlerror()); - lua_pushstring(L,(lib!=NULL) ? "init" : "open"); - if (lib!=NULL) dlclose(lib); - return 3; -} -#endif -#endif // _WIN32 -#endif // LUA_VER < 0x050100 - -// ==================== Load Libraries ================================================ - -static void loadLuaLibs(lua_State* state) -{ -#if LUA_VER >= 0x050100 - openLuaLibrary(state, LUA_DBLIBNAME, luaopen_debug); -#else - luaopen_debug(state); -#endif - - // TODO: Not required with Lua 5.1 -#if 0 -#ifndef _WIN32 - lua_pushstring(state, "xloadlib"); - lua_pushcfunction(state, x_loadlib); - lua_settable(state, LUA_GLOBALSINDEX); -#endif -#endif - - CreateObjectMetaTable(state); - CreateObserverMetaTable(state); - CreateCelestiaMetaTable(state); - CreatePositionMetaTable(state); - CreateVectorMetaTable(state); - CreateRotationMetaTable(state); - CreateFrameMetaTable(state); - CreateCelscriptMetaTable(state); - CreateFontMetaTable(state); - CreateImageMetaTable(state); - CreateTextureMetaTable(state); - ExtendCelestiaMetaTable(state); - ExtendObjectMetaTable(state); - - gl_loadlib(state); -} - - -void LuaState::allowSystemAccess() -{ -#if LUA_VER >= 0x050100 - openLuaLibrary(state, LUA_LOADLIBNAME, luaopen_package); - openLuaLibrary(state, LUA_IOLIBNAME, luaopen_io); - openLuaLibrary(state, LUA_OSLIBNAME, luaopen_os); -#else - luaopen_io(state); -#endif - ioMode = IOAllowed; -} - - -// Permit access to the package library, but prohibit use of the loadlib -// function. -void LuaState::allowLuaPackageAccess() -{ -#if LUA_VER >= 0x050100 - openLuaLibrary(state, LUA_LOADLIBNAME, luaopen_package); - - // Disallow loadlib - lua_getfield(state, LUA_GLOBALSINDEX, "package"); - lua_pushnil(state); - lua_setfield(state, -2, "loadlib"); - lua_pop(state, 1); -#endif -} - - -// ==================== Lua Hook Methods ================================================ - -void LuaState::setLuaHookEventHandlerEnabled(bool enable) -{ - eventHandlerEnabled = enable; -} - - -bool LuaState::callLuaHook(void* obj, const char* method) -{ - if (!eventHandlerEnabled) - return false; - - lua_pushlightuserdata(costate, obj); - lua_gettable(costate, LUA_REGISTRYINDEX); - if (!lua_istable(costate, -1)) - { - lua_pop(costate, 1); - return false; - } - bool handled = false; - - lua_pushstring(costate, method); - lua_gettable(costate, -2); - if (lua_isfunction(costate, -1)) - { - lua_pushvalue(costate, -2); // push the Lua object the stack - lua_remove(costate, -3); // remove the Lua object from the stack - - timeout = getTime() + 1.0; - if (lua_pcall(costate, 1, 1, 0) != 0) - { - cerr << "Error while executing Lua Hook: " << lua_tostring(costate, -1) << "\n"; - } - else - { - handled = lua_toboolean(costate, -1) == 1 ? true : false; - } - lua_pop(costate, 1); // pop the return value - } - else - { - lua_pop(costate, 2); - } - - return handled; -} - - -bool LuaState::callLuaHook(void* obj, const char* method, const char ch) -{ - if (!eventHandlerEnabled) - return false; - - lua_pushlightuserdata(costate, obj); - lua_gettable(costate, LUA_REGISTRYINDEX); - if (!lua_istable(costate, -1)) - { - lua_pop(costate, 1); - return false; - } - bool handled = false; - - lua_pushstring(costate, method); - lua_gettable(costate, -2); - if (lua_isfunction(costate, -1)) - { - lua_pushvalue(costate, -2); // push the Lua object onto the stack - lua_remove(costate, -3); // remove the Lua object from the stack - - lua_pushlstring(costate, &ch, 1); // push the char onto the stack - - timeout = getTime() + 1.0; - if (lua_pcall(costate, 2, 1, 0) != 0) - { - cerr << "Error while executing Lua Hook: " << lua_tostring(costate, -1) << "\n"; - } - else - { - handled = lua_toboolean(costate, -1) == 1 ? true : false; - } - lua_pop(costate, 1); // pop the return value - } - else - { - lua_pop(costate, 2); - } - - return handled; -} - - -bool LuaState::callLuaHook(void* obj, const char* method, float x, float y) -{ - if (!eventHandlerEnabled) - return false; - - lua_pushlightuserdata(costate, obj); - lua_gettable(costate, LUA_REGISTRYINDEX); - if (!lua_istable(costate, -1)) - { - lua_pop(costate, 1); - return false; - } - bool handled = false; - - lua_pushstring(costate, method); - lua_gettable(costate, -2); - if (lua_isfunction(costate, -1)) - { - lua_pushvalue(costate, -2); // push the Lua object onto the stack - lua_remove(costate, -3); // remove the Lua object from the stack - - lua_pushnumber(costate, x); // push x onto the stack - lua_pushnumber(costate, y); // push y onto the stack - - timeout = getTime() + 1.0; - if (lua_pcall(costate, 3, 1, 0) != 0) - { - cerr << "Error while executing Lua Hook: " << lua_tostring(costate, -1) << "\n"; - } - else - { - handled = lua_toboolean(costate, -1) == 1 ? true : false; - } - lua_pop(costate, 1); // pop the return value - } - else - { - lua_pop(costate, 2); - } - - return handled; -} - - -bool LuaState::callLuaHook(void* obj, const char* method, float x, float y, int b) -{ - if (!eventHandlerEnabled) - return false; - - lua_pushlightuserdata(costate, obj); - lua_gettable(costate, LUA_REGISTRYINDEX); - if (!lua_istable(costate, -1)) - { - lua_pop(costate, 1); - return false; - } - bool handled = false; - - lua_pushstring(costate, method); - lua_gettable(costate, -2); - if (lua_isfunction(costate, -1)) - { - lua_pushvalue(costate, -2); // push the Lua object onto the stack - lua_remove(costate, -3); // remove the Lua object from the stack - - lua_pushnumber(costate, x); // push x onto the stack - lua_pushnumber(costate, y); // push y onto the stack - lua_pushnumber(costate, b); // push b onto the stack - - timeout = getTime() + 1.0; - if (lua_pcall(costate, 4, 1, 0) != 0) - { - cerr << "Error while executing Lua Hook: " << lua_tostring(costate, -1) << "\n"; - } - else - { - handled = lua_toboolean(costate, -1) == 1 ? true : false; - } - lua_pop(costate, 1); // pop the return value - } - else - { - lua_pop(costate, 2); - } - - return handled; -} - - -bool LuaState::callLuaHook(void* obj, const char* method, double dt) -{ - if (!eventHandlerEnabled) - return false; - - lua_pushlightuserdata(costate, obj); - lua_gettable(costate, LUA_REGISTRYINDEX); - if (!lua_istable(costate, -1)) - { - lua_pop(costate, 1); - return false; - } - bool handled = false; - - lua_pushstring(costate, method); - lua_gettable(costate, -2); - if (lua_isfunction(costate, -1)) - { - lua_pushvalue(costate, -2); // push the Lua object onto the stack - lua_remove(costate, -3); // remove the Lua object from the stack - lua_pushnumber(costate, dt); - - timeout = getTime() + 1.0; - if (lua_pcall(costate, 2, 1, 0) != 0) - { - cerr << "Error while executing Lua Hook: " << lua_tostring(costate, -1) << "\n"; - } - else - { - handled = lua_toboolean(costate, -1) == 1 ? true : false; - } - lua_pop(costate, 1); // pop the return value - } - else - { - lua_pop(costate, 2); - } - - return handled; -} +// celx.cpp +// +// Copyright (C) 2003, Chris Laurel +// +// Lua script extensions for Celestia +// +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "imagecapture.h" + +// Older gcc versions used instead of . +// This has been corrected in GCC 3.2, but name clashing must +// be avoided +#ifdef __GNUC__ +#undef min +#undef max +#endif +#include + +#include "celx.h" +#include "celestiacore.h" + +using namespace std; + +static const char* ClassNames[] = +{ + "class_celestia", + "class_observer", + "class_object", + "class_vec3", + "class_matrix", + "class_rotation", + "class_position", + "class_frame", + "class_celscript", + "class_font", + "class_image", + "class_texture", +}; + +static const int _Celestia = 0; +static const int _Observer = 1; +static const int _Object = 2; +static const int _Vec3 = 3; +static const int _Matrix = 4; +static const int _Rotation = 5; +static const int _Position = 6; +static const int _Frame = 7; +static const int _CelScript= 8; +static const int _Font = 9; +static const int _Image = 10; +static const int _Texture = 11; + +#define CLASS(i) ClassNames[(i)] + +// Maximum timeslice a script may run without +// returning control to celestia +static const double MaxTimeslice = 5.0; + +// names of callback-functions in Lua: +static const char* KbdCallback = "celestia_keyboard_callback"; +static const char* CleanupCallback = "celestia_cleanup_callback"; + +static const char* EventHandlers = "celestia_event_handlers"; + +static const char* KeyHandler = "key"; +static const char* TickHandler = "tick"; +static const char* MouseDownHandler = "mousedown"; +static const char* MouseUpHandler = "mouseup"; + +typedef map FlagMap; +typedef map ColorMap; + +static FlagMap RenderFlagMap; +static FlagMap LabelFlagMap; +static FlagMap LocationFlagMap; +static FlagMap BodyTypeMap; +static FlagMap OverlayElementMap; +static FlagMap OrbitVisibilityMap; +static ColorMap LineColorMap; +static ColorMap LabelColorMap; +static bool mapsInitialized = false; + +// select which type of error will be fatal (call lua_error) and +// which will return a default value instead +enum FatalErrors { + NoErrors = 0, + WrongType = 1, + WrongArgc = 2, + AllErrors = WrongType | WrongArgc, +}; + + +// Initialize various maps from named keywords to numeric flags used within celestia: +static void initRenderFlagMap() +{ + RenderFlagMap["orbits"] = Renderer::ShowOrbits; + RenderFlagMap["cloudmaps"] = Renderer::ShowCloudMaps; + RenderFlagMap["constellations"] = Renderer::ShowDiagrams; + RenderFlagMap["galaxies"] = Renderer::ShowGalaxies; + RenderFlagMap["planets"] = Renderer::ShowPlanets; + RenderFlagMap["stars"] = Renderer::ShowStars; + RenderFlagMap["nightmaps"] = Renderer::ShowNightMaps; + RenderFlagMap["eclipseshadows"] = Renderer::ShowEclipseShadows; + RenderFlagMap["ringshadows"] = Renderer::ShowRingShadows; + RenderFlagMap["comettails"] = Renderer::ShowCometTails; + RenderFlagMap["boundaries"] = Renderer::ShowBoundaries; + RenderFlagMap["markers"] = Renderer::ShowMarkers; + RenderFlagMap["automag"] = Renderer::ShowAutoMag; + RenderFlagMap["atmospheres"] = Renderer::ShowAtmospheres; + RenderFlagMap["grid"] = Renderer::ShowCelestialSphere; + RenderFlagMap["smoothlines"] = Renderer::ShowSmoothLines; + RenderFlagMap["partialtrajectories"] = Renderer::ShowPartialTrajectories; + RenderFlagMap["nebulae"] = Renderer::ShowNebulae; + RenderFlagMap["openclusters"] = Renderer::ShowOpenClusters; + RenderFlagMap["cloudshadows"] = Renderer::ShowCloudShadows; +} + +static void initLabelFlagMap() +{ + LabelFlagMap["planets"] = Renderer::PlanetLabels; + LabelFlagMap["moons"] = Renderer::MoonLabels; + LabelFlagMap["spacecraft"] = Renderer::SpacecraftLabels; + LabelFlagMap["asteroids"] = Renderer::AsteroidLabels; + LabelFlagMap["comets"] = Renderer::CometLabels; + LabelFlagMap["constellations"] = Renderer::ConstellationLabels; + LabelFlagMap["stars"] = Renderer::StarLabels; + LabelFlagMap["galaxies"] = Renderer::GalaxyLabels; + LabelFlagMap["locations"] = Renderer::LocationLabels; + LabelFlagMap["nebulae"] = Renderer::NebulaLabels; + LabelFlagMap["openclusters"] = Renderer::OpenClusterLabels; + LabelFlagMap["i18nconstellations"] = Renderer::I18nConstellationLabels; +} + +static void initBodyTypeMap() +{ + BodyTypeMap["Planet"] = Body::Planet; + BodyTypeMap["Moon"] = Body::Moon; + BodyTypeMap["Asteroid"] = Body::Asteroid; + BodyTypeMap["Comet"] = Body::Comet; + BodyTypeMap["Spacecraft"] = Body::Spacecraft; + BodyTypeMap["Invisible"] = Body::Invisible; + BodyTypeMap["Star"] = Body::Stellar; + BodyTypeMap["Unknown"] = Body::Unknown; +} + +static void initLocationFlagMap() +{ + LocationFlagMap["city"] = Location::City; + LocationFlagMap["observatory"] = Location::Observatory; + LocationFlagMap["landingsite"] = Location::LandingSite; + LocationFlagMap["crater"] = Location::Crater; + LocationFlagMap["vallis"] = Location::Vallis; + LocationFlagMap["mons"] = Location::Mons; + LocationFlagMap["planum"] = Location::Planum; + LocationFlagMap["chasma"] = Location::Chasma; + LocationFlagMap["patera"] = Location::Patera; + LocationFlagMap["mare"] = Location::Mare; + LocationFlagMap["rupes"] = Location::Rupes; + LocationFlagMap["tessera"] = Location::Tessera; + LocationFlagMap["regio"] = Location::Regio; + LocationFlagMap["chaos"] = Location::Chaos; + LocationFlagMap["terra"] = Location::Terra; + LocationFlagMap["astrum"] = Location::Astrum; + LocationFlagMap["corona"] = Location::Corona; + LocationFlagMap["dorsum"] = Location::Dorsum; + LocationFlagMap["fossa"] = Location::Fossa; + LocationFlagMap["catena"] = Location::Catena; + LocationFlagMap["mensa"] = Location::Mensa; + LocationFlagMap["rima"] = Location::Rima; + LocationFlagMap["undae"] = Location::Undae; + LocationFlagMap["reticulum"] = Location::Reticulum; + LocationFlagMap["planitia"] = Location::Planitia; + LocationFlagMap["linea"] = Location::Linea; + LocationFlagMap["fluctus"] = Location::Fluctus; + LocationFlagMap["farrum"] = Location::Farrum; + LocationFlagMap["other"] = Location::Other; +} + +static void initOverlayElementMap() +{ + OverlayElementMap["Time"] = CelestiaCore::ShowTime; + OverlayElementMap["Velocity"] = CelestiaCore::ShowVelocity; + OverlayElementMap["Selection"] = CelestiaCore::ShowSelection; + OverlayElementMap["Frame"] = CelestiaCore::ShowFrame; +} + +static void initOrbitVisibilityMap() +{ + OrbitVisibilityMap["never"] = Body::NeverVisible; + OrbitVisibilityMap["normal"] = Body::UseClassVisibility; + OrbitVisibilityMap["always"] = Body::AlwaysVisible; +} + + +static void initLabelColorMap() +{ + LabelColorMap["stars"] = &Renderer::StarLabelColor; + LabelColorMap["planets"] = &Renderer::PlanetLabelColor; + LabelColorMap["moons"] = &Renderer::MoonLabelColor; + LabelColorMap["asteroids"] = &Renderer::AsteroidLabelColor; + LabelColorMap["comets"] = &Renderer::CometLabelColor; + LabelColorMap["spacecraft"] = &Renderer::SpacecraftLabelColor; + LabelColorMap["locations"] = &Renderer::LocationLabelColor; + LabelColorMap["galaxies"] = &Renderer::GalaxyLabelColor; + LabelColorMap["nebulae"] = &Renderer::NebulaLabelColor; + LabelColorMap["openclusters"] = &Renderer::OpenClusterLabelColor; + LabelColorMap["constellations"] = &Renderer::ConstellationLabelColor; + LabelColorMap["equatorialgrid"] = &Renderer::EquatorialGridLabelColor; +} + +static void initLineColorMap() +{ + LineColorMap["starorbits"] = &Renderer::StarOrbitColor; + LineColorMap["planetorbits"] = &Renderer::PlanetOrbitColor; + LineColorMap["moonorbits"] = &Renderer::MoonOrbitColor; + LineColorMap["asteroidorbits"] = &Renderer::AsteroidOrbitColor; + LineColorMap["cometorbits"] = &Renderer::CometOrbitColor; + LineColorMap["spacecraftorbits"] = &Renderer::SpacecraftOrbitColor; + LineColorMap["constellations"] = &Renderer::ConstellationColor; + LineColorMap["boundaries"] = &Renderer::BoundaryColor; + LineColorMap["equatorialgrid"] = &Renderer::EquatorialGridColor; +} + + +#if LUA_VER >= 0x050100 +// Load a Lua library--in Lua 5.1, the luaopen_* functions cannot be called +// directly. They most be invoked through the Lua state. +static void openLuaLibrary(lua_State* l, + const char* name, + lua_CFunction func) +{ + lua_pushcfunction(l, func); + lua_pushstring(l, name); + lua_call(l, 1, 0); +} +#endif + + +static void initMaps() +{ + if (!mapsInitialized) + { + initRenderFlagMap(); + initLabelFlagMap(); + initBodyTypeMap(); + initLocationFlagMap(); + initOverlayElementMap(); + initOrbitVisibilityMap(); + initLabelColorMap(); + initLineColorMap(); + } + mapsInitialized = true; +} + + +static void getField(lua_State* l, int index, const char* key) +{ + // When we move to Lua 5.1, this will be replaced by: + // lua_getfield(l, index, key); + lua_pushstring(l, key); + + if (index != LUA_GLOBALSINDEX && index != LUA_REGISTRYINDEX) + lua_gettable(l, index - 1); + else + lua_gettable(l, index); +} + + +// Wrapper for a CEL-script, including the needed Execution Environment +class CelScriptWrapper : public ExecutionEnvironment +{ + public: + CelScriptWrapper(CelestiaCore& appCore, istream& scriptfile): + script(NULL), + core(appCore), + cmdSequence(NULL), + tickTime(0.0), + errorMessage("") + { + CommandParser parser(scriptfile); + cmdSequence = parser.parse(); + if (cmdSequence != NULL) + { + script = new Execution(*cmdSequence, *this); + } + else + { + const vector* errors = parser.getErrors(); + if (errors->size() > 0) + errorMessage = "Error while parsing CEL-script: " + (*errors)[0]; + else + errorMessage = "Error while parsing CEL-script."; + } + } + + virtual ~CelScriptWrapper() + { + if (script != NULL) + delete script; + if (cmdSequence != NULL) + delete cmdSequence; + } + + string getErrorMessage() const + { + return errorMessage; + } + + // tick the CEL-script. t is in seconds and doesn't have to start with zero + bool tick(double t) + { + // use first tick to set the time + if (tickTime == 0.0) + { + tickTime = t; + return false; + } + double dt = t - tickTime; + tickTime = t; + return script->tick(dt); + } + + Simulation* getSimulation() const + { + return core.getSimulation(); + } + + Renderer* getRenderer() const + { + return core.getRenderer(); + } + + CelestiaCore* getCelestiaCore() const + { + return &core; + } + + void showText(string s, int horig, int vorig, int hoff, int voff, + double duration) + { + core.showText(s, horig, vorig, hoff, voff, duration); + } + + private: + Execution* script; + CelestiaCore& core; + CommandSequence* cmdSequence; + double tickTime; + string errorMessage; +}; + + +// Push a class name onto the Lua stack +static void PushClass(lua_State* l, int id) +{ + lua_pushlstring(l, ClassNames[id], strlen(ClassNames[id])); +} + +// Set the class (metatable) of the object on top of the stack +static void SetClass(lua_State* l, int id) +{ + PushClass(l, id); + lua_rawget(l, LUA_REGISTRYINDEX); + if (lua_type(l, -1) != LUA_TTABLE) + cout << "Metatable for " << ClassNames[id] << " not found!\n"; + if (lua_setmetatable(l, -2) == 0) + cout << "Error setting metatable for " << ClassNames[id] << '\n'; +} + +// Initialize the metatable for a class; sets the appropriate registry +// entries and __index, leaving the metatable on the stack when done. +static void CreateClassMetatable(lua_State* l, int id) +{ + lua_newtable(l); + PushClass(l, id); + lua_pushvalue(l, -2); + lua_rawset(l, LUA_REGISTRYINDEX); // registry.name = metatable + lua_pushvalue(l, -1); + PushClass(l, id); + lua_rawset(l, LUA_REGISTRYINDEX); // registry.metatable = name + + lua_pushliteral(l, "__index"); + lua_pushvalue(l, -2); + lua_rawset(l, -3); +} + +// Register a class 'method' in the metatable (assumed to be on top of the stack) +static void RegisterMethod(lua_State* l, const char* name, lua_CFunction fn) +{ + lua_pushstring(l, name); + lua_pushvalue(l, -2); + lua_pushcclosure(l, fn, 1); + lua_settable(l, -3); +} + + +// Verify that an object at location index on the stack is of the +// specified class +static bool istype(lua_State* l, int index, int id) +{ + // get registry[metatable] + if (!lua_getmetatable(l, index)) + return false; + lua_rawget(l, LUA_REGISTRYINDEX); + + if (lua_type(l, -1) != LUA_TSTRING) + { + cout << "istype failed! Unregistered class.\n"; + lua_pop(l, 1); + return false; + } + + const char* classname = lua_tostring(l, -1); + if (classname != NULL && strcmp(classname, ClassNames[id]) == 0) + { + lua_pop(l, 1); + return true; + } + lua_pop(l, 1); + return false; +} + +// Verify that an object at location index on the stack is of the +// specified class and return pointer to userdata +static void* CheckUserData(lua_State* l, int index, int id) +{ + if (istype(l, index, id)) + return lua_touserdata(l, index); + else + return NULL; +} + + +// Return the CelestiaCore object stored in the globals table +static CelestiaCore* getAppCore(lua_State* l, FatalErrors fatalErrors = NoErrors) +{ + lua_pushstring(l, "celestia-appcore"); + lua_gettable(l, LUA_REGISTRYINDEX); + + if (!lua_islightuserdata(l, -1)) + { + if (fatalErrors == NoErrors) + return NULL; + else + { + lua_pushstring(l, "internal error: invalid appCore"); + lua_error(l); + } + } + + CelestiaCore* appCore = static_cast(lua_touserdata(l, -1)); + lua_pop(l, 1); + return appCore; +} + + +LuaState::LuaState() : + timeout(MaxTimeslice), + state(NULL), + costate(NULL), + alive(false), + timer(NULL), + scriptAwakenTime(0.0), + ioMode(NoIO), + eventHandlerEnabled(false) +{ + state = lua_open(); + timer = CreateTimer(); + screenshotCount = 0; +} + +LuaState::~LuaState() +{ + delete timer; + if (state != NULL) + lua_close(state); +#if 0 + if (costate != NULL) + lua_close(costate); +#endif +} + +lua_State* LuaState::getState() const +{ + return state; +} + + +double LuaState::getTime() const +{ + return timer->getTime(); +} + + +// Check if the running script has exceeded its allowed timeslice +// and terminate it if it has: +static void checkTimeslice(lua_State* l, lua_Debug* /*ar*/) +{ + lua_pushstring(l, "celestia-luastate"); + lua_gettable(l, LUA_REGISTRYINDEX); + if (!lua_islightuserdata(l, -1)) + { + lua_pushstring(l, "Internal Error: Invalid table entry in checkTimeslice"); + lua_error(l); + } + LuaState* luastate = static_cast(lua_touserdata(l, -1)); + if (luastate == NULL) + { + lua_pushstring(l, "Internal Error: Invalid value in checkTimeslice"); + lua_error(l); + } + + if (luastate->timesliceExpired()) + { + const char* errormsg = "Timeout: script hasn't returned control to celestia (forgot to call wait()?)"; + cerr << errormsg << "\n"; + lua_pushstring(l, errormsg); + lua_error(l); + } + return; +} + + +// allow the script to perform cleanup +void LuaState::cleanup() +{ + if (ioMode == Asking) + { + // Restore renderflags: + CelestiaCore* appCore = getAppCore(costate, NoErrors); + if (appCore != NULL) + { + lua_pushstring(state, "celestia-savedrenderflags"); + lua_gettable(state, LUA_REGISTRYINDEX); + if (lua_isuserdata(state, -1)) + { + int* savedrenderflags = static_cast(lua_touserdata(state, -1)); + appCore->getRenderer()->setRenderFlags(*savedrenderflags); + // now delete entry: + lua_pushstring(state, "celestia-savedrenderflags"); + lua_pushnil(state); + lua_settable(state, LUA_REGISTRYINDEX); + } + lua_pop(state,1); + } + } + lua_pushstring(costate, CleanupCallback); + lua_gettable(costate, LUA_GLOBALSINDEX); + if (lua_isnil(costate, -1)) + { + return; + } + timeout = getTime() + 1.0; + if (lua_pcall(costate, 0, 0, 0) != 0) + { + cerr << "Error while executing cleanup-callback: " << lua_tostring(costate, -1) << "\n"; + } +} + + +bool LuaState::createThread() +{ + // Initialize the coroutine which wraps the script + if (!(lua_isfunction(state, -1) && !lua_iscfunction(state, -1))) + { + // Should never happen; we manually set up the stack in C++ + assert(0); + return false; + } + else + { + costate = lua_newthread(state); + if (costate == NULL) + return false; + lua_sethook(costate, checkTimeslice, LUA_MASKCOUNT, 1000); + lua_pushvalue(state, -2); + lua_xmove(state, costate, 1); /* move function from L to NL */ + alive = true; + return true; + } +} + + +string LuaState::getErrorMessage() +{ + if (lua_gettop(state) > 0) + { + if (lua_isstring(state, -1)) + return lua_tostring(state, -1); + } + return ""; +} + + +bool LuaState::timesliceExpired() +{ + if (timeout < getTime()) + { + // timeslice expired, make every instruction (including pcall) fail: + lua_sethook(costate, checkTimeslice, LUA_MASKCOUNT, 1); + return true; + } + else + { + return false; + } +} + + +static int resumeLuaThread(lua_State *L, lua_State *co, int narg) +{ + int status; + + //if (!lua_checkstack(co, narg)) + // luaL_error(L, "too many arguments to resume"); + lua_xmove(L, co, narg); + + status = lua_resume(co, narg); +#if LUA_VER >= 0x050100 + if (status == 0 || status == LUA_YIELD) +#else + if (status == 0) +#endif + { + int nres = lua_gettop(co); + //if (!lua_checkstack(L, narg)) + // luaL_error(L, "too many results to resume"); + lua_xmove(co, L, nres); // move yielded values + return nres; + } + else + { + lua_xmove(co, L, 1); // move error message + return -1; // error flag + } +} + + +bool LuaState::isAlive() const +{ + return alive; +} + + +struct ReadChunkInfo +{ + char* buf; + int bufSize; + istream* in; +}; + +static const char* readStreamChunk(lua_State*, void* udata, size_t* size) +{ + assert(udata != NULL); + if (udata == NULL) + return NULL; + + ReadChunkInfo* info = reinterpret_cast(udata); + assert(info->buf != NULL); + assert(info->in != NULL); + + if (!info->in->good()) + { + *size = 0; + return NULL; + } + + info->in->read(info->buf, info->bufSize); + streamsize nread = info->in->gcount(); + + *size = (size_t) nread; + if (nread == 0) + return NULL; + else + return info->buf; +} + + +// Callback for CelestiaCore::charEntered. +// Returns true if keypress has been consumed +bool LuaState::charEntered(const char* c_p) +{ + if (ioMode == Asking && getTime() > timeout) + { + int stackTop = lua_gettop(costate); + if (strcmp(c_p, "y") == 0) + { +#if LUA_VER >= 0x050100 + openLuaLibrary(costate, LUA_LOADLIBNAME, luaopen_package); + openLuaLibrary(costate, LUA_IOLIBNAME, luaopen_io); +#else + lua_iolibopen(costate); +#endif + ioMode = IOAllowed; + } + else + { + ioMode = IODenied; + } + CelestiaCore* appCore = getAppCore(costate, NoErrors); + if (appCore == NULL) + { + cerr << "ERROR: appCore not found\n"; + return true; + } + appCore->setTextEnterMode(appCore->getTextEnterMode() & ~CelestiaCore::KbPassToScript); + appCore->showText("", 0, 0, 0, 0); + // Restore renderflags: + lua_pushstring(costate, "celestia-savedrenderflags"); + lua_gettable(costate, LUA_REGISTRYINDEX); + if (lua_isuserdata(costate, -1)) + { + int* savedrenderflags = static_cast(lua_touserdata(costate, -1)); + appCore->getRenderer()->setRenderFlags(*savedrenderflags); + // now delete entry: + lua_pushstring(costate, "celestia-savedrenderflags"); + lua_pushnil(costate); + lua_settable(costate, LUA_REGISTRYINDEX); + } + else + { + cerr << "Oops, expected savedrenderflags to be userdata\n"; + } + lua_settop(costate,stackTop); + return true; + } + int stack_top = lua_gettop(costate); + bool result = true; + lua_pushstring(costate, KbdCallback); + lua_gettable(costate, LUA_GLOBALSINDEX); + lua_pushstring(costate, c_p); + timeout = getTime() + 1.0; + if (lua_pcall(costate, 1, 1, 0) != 0) + { + cerr << "Error while executing keyboard-callback: " << lua_tostring(costate, -1) << "\n"; + result = false; + } + else + { + if (lua_isboolean(costate, -1)) + { + result = (lua_toboolean(costate, -1) != 0); + } + } + +#if LUA_VER < 0x050100 + // cleanup stack - is this necessary? + lua_settop(costate, stack_top); +#endif + return result; +} + + +// Returns true if a handler is registered for the key +bool LuaState::handleKeyEvent(const char* key) +{ + CelestiaCore* appCore = getAppCore(costate, NoErrors); + if (appCore == NULL) + { + return false; + } + + // get the registered event table + getField(costate, LUA_REGISTRYINDEX, EventHandlers); + if (!lua_istable(costate, -1)) + { + cerr << "Missing event handler table"; + lua_pop(costate, 1); + return false; + } + + bool handled = false; + getField(costate, -1, KeyHandler); + if (lua_isfunction(costate, -1)) + { + lua_remove(costate, -2); // remove the key event table from the stack + + lua_newtable(costate); + lua_pushstring(costate, "char"); + lua_pushstring(costate, key); // the default key handler accepts the key name as an argument + lua_settable(costate, -3); + + timeout = getTime() + 1.0; + if (lua_pcall(costate, 1, 1, 0) != 0) + { + cerr << "Error while executing keyboard callback: " << lua_tostring(costate, -1) << "\n"; + } + else + { + handled = lua_toboolean(costate, -1) == 1 ? true : false; + } + lua_pop(costate, 1); // pop the return value + } + else + { + lua_pop(costate, 2); + } + + return handled; +} + + +// Returns true if a handler is registered for the button event +bool LuaState::handleMouseButtonEvent(float x, float y, int button, bool down) +{ + CelestiaCore* appCore = getAppCore(costate, NoErrors); + if (appCore == NULL) + { + return false; + } + + // get the registered event table + getField(costate, LUA_REGISTRYINDEX, EventHandlers); + if (!lua_istable(costate, -1)) + { + cerr << "Missing event handler table"; + lua_pop(costate, 1); + return false; + } + + bool handled = false; + getField(costate, -1, down ? MouseDownHandler : MouseUpHandler); + if (lua_isfunction(costate, -1)) + { + lua_remove(costate, -2); // remove the key event table from the stack + + lua_newtable(costate); + lua_pushstring(costate, "button"); + lua_pushnumber(costate, button); + lua_settable(costate, -3); + lua_pushstring(costate, "x"); + lua_pushnumber(costate, x); + lua_settable(costate, -3); + lua_pushstring(costate, "y"); + lua_pushnumber(costate, y); + lua_settable(costate, -3); + + timeout = getTime() + 1.0; + if (lua_pcall(costate, 1, 1, 0) != 0) + { + cerr << "Error while executing keyboard callback: " << lua_tostring(costate, -1) << "\n"; + } + else + { + handled = lua_toboolean(costate, -1) == 1 ? true : false; + } + lua_pop(costate, 1); // pop the return value + } + else + { + lua_pop(costate, 2); + } + + return handled; +} + + +// Returns true if a handler is registered for the tick event +bool LuaState::handleTickEvent(double dt) +{ + CelestiaCore* appCore = getAppCore(costate, NoErrors); + if (appCore == NULL) + { + return false; + } + + // get the registered event table + getField(costate, LUA_REGISTRYINDEX, EventHandlers); + if (!lua_istable(costate, -1)) + { + cerr << "Missing event handler table"; + lua_pop(costate, 1); + return false; + } + + bool handled = false; + getField(costate, -1, TickHandler); + if (lua_isfunction(costate, -1)) + { + lua_remove(costate, -2); // remove the key event table from the stack + + lua_newtable(costate); + lua_pushstring(costate, "dt"); + lua_pushnumber(costate, dt); // the default key handler accepts the key name as an argument + lua_settable(costate, -3); + + timeout = getTime() + 1.0; + if (lua_pcall(costate, 1, 1, 0) != 0) + { + cerr << "Error while executing tick callback: " << lua_tostring(costate, -1) << "\n"; + } + else + { + handled = lua_toboolean(costate, -1) == 1 ? true : false; + } + lua_pop(costate, 1); // pop the return value + } + else + { + lua_pop(costate, 2); + } + + return handled; +} + + +int LuaState::loadScript(istream& in, const string& streamname) +{ + char buf[4096]; + ReadChunkInfo info; + info.buf = buf; + info.bufSize = sizeof(buf); + info.in = ∈ + + if (streamname != "string") + { + lua_pushstring(state, "celestia-scriptpath"); + lua_pushstring(state, streamname.c_str()); + lua_settable(state, LUA_REGISTRYINDEX); + } + + int status = lua_load(state, readStreamChunk, &info, streamname.c_str()); + if (status != 0) + cout << "Error loading script: " << lua_tostring(state, -1) << '\n'; + + return status; +} + +int LuaState::loadScript(const string& s) +{ + istringstream in(s); + return loadScript(in, "string"); +} + + +// Resume a thread; if the thread completes, the status is set to !alive +int LuaState::resume() +{ + assert(costate != NULL); + if (costate == NULL) + return false; + + lua_State* co = lua_tothread(state, -1); + //assert(co == costate); // co can be NULL after error (top stack is errorstring) + if (co != costate) + return false; + + timeout = getTime() + MaxTimeslice; + int nArgs = resumeLuaThread(state, co, 0); + if (nArgs < 0) + { + alive = false; + + const char* errorMessage = lua_tostring(state, -1); + if (errorMessage == NULL) + errorMessage = "Unknown script error"; + +#if LUA_VER < 0x050100 + // This is a nasty hack required in Lua 5.0, where there's no + // way to distinguish between a thread returning because it completed + // or yielded. Thus, we continue to resume the script until we get + // an error. The 'cannot resume dead coroutine' error appears when + // there were no other errors, and execution terminates normally. + // In Lua 5.1, we can simply check the thread status to find out + // if it's done executing. + if (strcmp(errorMessage, "cannot resume dead coroutine") != 0) +#endif + { + cout << "Error: " << errorMessage << '\n'; + CelestiaCore* appCore = getAppCore(co); + if (appCore != NULL) + { + CelestiaCore::Alerter* alerter = appCore->getAlerter(); + alerter->fatalError(errorMessage); + } + } + + return 1; // just the error string + } + else + { + if (ioMode == Asking) + { + // timeout now is used to first only display warning, and 1s + // later allow response to avoid accidental activation + timeout = getTime() + 1.0; + } + +#if LUA_VER >= 0x050100 + // The thread status is zero if it has terminated normally + if (lua_status(co) == 0) + { + alive = false; + } +#endif + + return nArgs; // arguments from yield + } +} + + +// get current linenumber of script and create +// useful error-message +static void doError(lua_State* l, const char* errorMsg) +{ + lua_Debug debug; + if (lua_getstack(l, 1, &debug)) + { + char buf[1024]; + if (lua_getinfo(l, "l", &debug)) + { + sprintf(buf, "In line %i: %s", debug.currentline, errorMsg); + lua_pushstring(l, buf); + lua_error(l); + } + } + lua_pushstring(l, errorMsg); + lua_error(l); +} + + +bool LuaState::tick(double dt) +{ + // Due to the way CelestiaCore::tick is called (at least for KDE), + // this method may be entered a second time when we show the error-alerter + // Workaround: check if we are alive, return true(!) when we aren't anymore + // this way the script isn't deleted after the second enter, but only + // when we return from the first enter. OMG. + + // better Solution: defer showing the error-alterter to CelestiaCore, using + // getErrorMessage() + if (!isAlive()) + return false; + + if (ioMode == Asking) + { + CelestiaCore* appCore = getAppCore(costate, NoErrors); + if (appCore == NULL) + { + cerr << "ERROR: appCore not found\n"; + return true; + } + lua_pushstring(state, "celestia-savedrenderflags"); + lua_gettable(state, LUA_REGISTRYINDEX); + if (lua_isnil(state, -1)) + { + lua_pushstring(state, "celestia-savedrenderflags"); + int* savedrenderflags = static_cast(lua_newuserdata(state, sizeof(int))); + *savedrenderflags = appCore->getRenderer()->getRenderFlags(); + lua_settable(state, LUA_REGISTRYINDEX); + appCore->getRenderer()->setRenderFlags(0); + } + // now pop result of gettable + lua_pop(state, 1); + + if (getTime() > timeout) + { + appCore->showText("WARNING:\n\nThis script requests permission to read/write files\n" + "and execute external programs. Allowing this can be\n" + "dangerous.\n" + "Do you trust the script and want to allow this?\n\n" + "y = yes, ESC = cancel script, any other key = no", + 0, 0, + -15, 5, 5); + appCore->setTextEnterMode(appCore->getTextEnterMode() | CelestiaCore::KbPassToScript); + } + else + { + appCore->showText("WARNING:\n\nThis script requests permission to read/write files\n" + "and execute external programs. Allowing this can be\n" + "dangerous.\n" + "Do you trust the script and want to allow this?", + 0, 0, + -15, 5, 5); + appCore->setTextEnterMode(appCore->getTextEnterMode() & ~CelestiaCore::KbPassToScript); + } + + return false; + } + + if (dt == 0 || scriptAwakenTime > getTime()) + return false; + + int nArgs = resume(); + if (!isAlive()) + { + // The script is complete + return true; + } + else + { + // The script has returned control to us, but it is not completed. + lua_State* state = getState(); + + // The values on the stack indicate what event will wake up the + // script. For now, we just support wait() + double delay; + if (nArgs == 1 && lua_isnumber(state, -1)) + delay = lua_tonumber(state, -1); + else + delay = 0.0; + scriptAwakenTime = getTime() + delay; + + // Clean up the stack + lua_pop(state, nArgs); + return false; + } +} + + +void LuaState::requestIO() +{ + // the script requested IO, set the mode + // so we display the warning during tick + // and can request keyboard. We can't do this now + // because the script is still active and could + // disable keyboard again. + if (ioMode == NoIO) + { + CelestiaCore* appCore = getAppCore(state, AllErrors); + string policy = appCore->getConfig()->scriptSystemAccessPolicy; + if (policy == "allow") + { +#if LUA_VER >= 0x050100 + openLuaLibrary(costate, LUA_LOADLIBNAME, luaopen_package); + openLuaLibrary(costate, LUA_IOLIBNAME, luaopen_io); + openLuaLibrary(costate, LUA_OSLIBNAME, luaopen_os); +#else + lua_iolibopen(costate); +#endif + //luaopen_io(costate); + ioMode = IOAllowed; + } + else if (policy == "deny") + { + ioMode = IODenied; + } + else + { + ioMode = Asking; + } + } +} + +// Check if the number of arguments on the stack matches +// the allowed range [minArgs, maxArgs]. Cause an error if not. +static void checkArgs(lua_State* l, + int minArgs, int maxArgs, const char* errorMessage) +{ + int argc = lua_gettop(l); + if (argc < minArgs || argc > maxArgs) + { + doError(l, errorMessage); + } +} + + +static ObserverFrame::CoordinateSystem parseCoordSys(const string& name) +{ + if (compareIgnoringCase(name, "universal") == 0) + return ObserverFrame::Universal; + else if (compareIgnoringCase(name, "ecliptic") == 0) + return ObserverFrame::Ecliptical; + else if (compareIgnoringCase(name, "equatorial") == 0) + return ObserverFrame::Equatorial; + else if (compareIgnoringCase(name, "planetographic") == 0) + return ObserverFrame::BodyFixed; + else if (compareIgnoringCase(name, "observer") == 0) + return ObserverFrame::ObserverLocal; + else if (compareIgnoringCase(name, "lock") == 0) + return ObserverFrame::PhaseLock; + else if (compareIgnoringCase(name, "chase") == 0) + return ObserverFrame::Chase; + else + return ObserverFrame::Universal; +} + + +static Marker::Symbol parseMarkerSymbol(const string& name) +{ + if (compareIgnoringCase(name, "diamond") == 0) + return Marker::Diamond; + else if (compareIgnoringCase(name, "triangle") == 0) + return Marker::Triangle; + else if (compareIgnoringCase(name, "square") == 0) + return Marker::Square; + else if (compareIgnoringCase(name, "filledsquare") == 0) + return Marker::FilledSquare; + else if (compareIgnoringCase(name, "plus") == 0) + return Marker::Plus; + else if (compareIgnoringCase(name, "x") == 0) + return Marker::X; + else if (compareIgnoringCase(name, "leftarrow") == 0) + return Marker::LeftArrow; + else if (compareIgnoringCase(name, "rightarrow") == 0) + return Marker::RightArrow; + else if (compareIgnoringCase(name, "uparrow") == 0) + return Marker::UpArrow; + else if (compareIgnoringCase(name, "downarrow") == 0) + return Marker::DownArrow; + else if (compareIgnoringCase(name, "circle") == 0) + return Marker::Circle; + else if (compareIgnoringCase(name, "disk") == 0) + return Marker::Disk; + else + return Marker::Diamond; +} + + +// Get a pointer to the LuaState-object from the registry: +LuaState* getLuaStateObject(lua_State* l) +{ + int stackSize = lua_gettop(l); + lua_pushstring(l, "celestia-luastate"); + lua_gettable(l, LUA_REGISTRYINDEX); + + if (!lua_islightuserdata(l, -1)) + { + doError(l, "Internal Error: Invalid table entry for LuaState-pointer"); + } + LuaState* luastate_ptr = static_cast(lua_touserdata(l, -1)); + if (luastate_ptr == NULL) + { + doError(l, "Internal Error: Invalid LuaState-pointer"); + } + lua_settop(l, stackSize); + return luastate_ptr; +} + + +// Map the observer to its View. Return NULL if no view exists +// for this observer (anymore). +View* getViewByObserver(CelestiaCore* appCore, Observer* obs) +{ + for (unsigned int i = 0; i < appCore->views.size(); i++) + { + if ((appCore->views[i])->observer == obs) + { + return appCore->views[i]; + } + } + return NULL; +} + +// Fill list with all Observers +void getObservers(CelestiaCore* appCore, vector& list) +{ + for (unsigned int i = 0; i < appCore->views.size(); i++) + { + list.push_back(appCore->views[i]->observer); + } +} + + +// ==================== Helpers ==================== + +// safe wrapper for lua_tostring: fatal errors will terminate script by calling +// lua_error with errorMsg. +static const char* safeGetString(lua_State* l, int index, FatalErrors fatalErrors = AllErrors, + const char* errorMsg = "String argument expected") +{ + if (l == NULL) + { + cerr << "Error: LuaState invalid in safeGetString\n"; + cout << "Error: LuaState invalid in safeGetString\n"; + return NULL; + } + int argc = lua_gettop(l); + if (index < 1 || index > argc) + { + if (fatalErrors & WrongArgc) + { + doError(l, errorMsg); + } + else + return NULL; + } + if (!lua_isstring(l, index)) + { + if (fatalErrors & WrongType) + { + doError(l, errorMsg); + } + else + return NULL; + } + return lua_tostring(l, index); +} + +// safe wrapper for lua_tonumber, c.f. safeGetString +// Non-fatal errors will return defaultValue. +static lua_Number safeGetNumber(lua_State* l, int index, FatalErrors fatalErrors = AllErrors, + const char* errorMsg = "Numeric argument expected", + lua_Number defaultValue = 0.0) +{ + if (l == NULL) + { + cerr << "Error: LuaState invalid in safeGetNumber\n"; + cout << "Error: LuaState invalid in safeGetNumber\n"; + return 0.0; + } + int argc = lua_gettop(l); + if (index < 1 || index > argc) + { + if (fatalErrors & WrongArgc) + { + doError(l, errorMsg); + } + else + return defaultValue; + } + if (!lua_isnumber(l, index)) + { + if (fatalErrors & WrongType) + { + doError(l, errorMsg); + } + else + return defaultValue; + } + return lua_tonumber(l, index); +} + +// Safe wrapper for lua_tobool, c.f. safeGetString +// Non-fata errors will return defaultValue +static bool safeGetBoolean(lua_State* l, + int index, + FatalErrors fatalErrors = AllErrors, + const char* errorMsg = "Boolean argument expected", + bool defaultValue = false) +{ + int argc = lua_gettop(l); + if (index < 1 || index > argc) + { + if (fatalErrors & WrongArgc) + { + doError(l, errorMsg); + } + else + { + return defaultValue; + } + } + + if (!lua_isboolean(l, index)) + { + if (fatalErrors & WrongType) + { + doError(l, errorMsg); + } + else + { + return defaultValue; + } + } + + return lua_toboolean(l, index) != 0; +} + + +// Add a field to the table on top of the stack +static void setTable(lua_State* l, const char* field, lua_Number value) +{ + lua_pushstring(l, field); + lua_pushnumber(l, value); + lua_settable(l, -3); +} + +static void setTable(lua_State* l, const char* field, const char* value) +{ + lua_pushstring(l, field); + lua_pushstring(l, value); + lua_settable(l, -3); +} + +// ==================== forward declarations ==================== + +// must be declared for vector_add: +static UniversalCoord* to_position(lua_State* l, int index); +static int position_new(lua_State* l, const UniversalCoord& uc); +// for frame_getrefobject / gettargetobject +static int object_new(lua_State* l, const Selection& sel); +// for vector_mult +static Quatd* to_rotation(lua_State* l, int index); +static int rotation_new(lua_State* l, const Quatd& qd); + +// ==================== Vector ==================== +static int vector_new(lua_State* l, const Vec3d& v) +{ + Vec3d* v3 = reinterpret_cast(lua_newuserdata(l, sizeof(Vec3d))); + *v3 = v; + SetClass(l, _Vec3); + + return 1; +} + +static Vec3d* to_vector(lua_State* l, int index) +{ + return static_cast(CheckUserData(l, index, _Vec3)); +} + +static Vec3d* this_vector(lua_State* l) +{ + Vec3d* v3 = to_vector(l, 1); + if (v3 == NULL) + { + doError(l, "Bad vector object!"); + } + + return v3; +} + + +static int vector_sub(lua_State* l) +{ + checkArgs(l, 2, 2, "Need two operands for sub"); + Vec3d* op1 = to_vector(l, 1); + Vec3d* op2 = to_vector(l, 2); + if (op1 == NULL || op2 == NULL) + { + doError(l, "Subtraction only defined for two vectors"); + } + else + { + Vec3d result = *op1 - *op2; + vector_new(l, result); + } + return 1; +} + +static int vector_get(lua_State* l) +{ + checkArgs(l, 2, 2, "Invalid access of vector-component"); + Vec3d* v3 = this_vector(l); + string key = safeGetString(l, 2, AllErrors, "Invalid key in vector-access"); + double value = 0.0; + if (key == "x") + value = v3->x; + else if (key == "y") + value = v3->y; + else if (key == "z") + value = v3->z; + else + { + if (lua_getmetatable(l, 1)) + { + lua_pushvalue(l, 2); + lua_rawget(l, -2); + return 1; + } + else + { + doError(l, "Internal error: couldn't get metatable"); + } + } + lua_pushnumber(l, (lua_Number)value); + return 1; +} + +static int vector_set(lua_State* l) +{ + checkArgs(l, 3, 3, "Invalid access of vector-component"); + Vec3d* v3 = this_vector(l); + string key = safeGetString(l, 2, AllErrors, "Invalid key in vector-access"); + double value = safeGetNumber(l, 3, AllErrors, "Vector components must be numbers"); + if (key == "x") + v3->x = value; + else if (key == "y") + v3->y = value; + else if (key == "z") + v3->z = value; + else + { + doError(l, "Invalid key in vector-access"); + } + return 0; +} + +static int vector_getx(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for vector:getx"); + Vec3d* v3 = this_vector(l); + lua_Number x; + x = static_cast(v3->x); + lua_pushnumber(l, x); + + return 1; +} + +static int vector_gety(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for vector:gety"); + Vec3d* v3 = this_vector(l); + lua_Number y; + y = static_cast(v3->y); + lua_pushnumber(l, y); + + return 1; +} + +static int vector_getz(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for vector:getz"); + Vec3d* v3 = this_vector(l); + lua_Number z; + z = static_cast(v3->z); + lua_pushnumber(l, z); + + return 1; +} + +static int vector_normalize(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for vector:normalize"); + Vec3d* v = this_vector(l); + Vec3d vn(*v); + vn.normalize(); + vector_new(l, vn); + return 1; +} + +static int vector_length(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for vector:length"); + Vec3d* v = this_vector(l); + double length = v->length(); + lua_pushnumber(l, (lua_Number)length); + return 1; +} + +static int vector_add(lua_State* l) +{ + checkArgs(l, 2, 2, "Need two operands for addition"); + Vec3d* v1 = NULL; + Vec3d* v2 = NULL; + UniversalCoord* p = NULL; + + if (istype(l, 1, _Vec3) && istype(l, 2, _Vec3)) + { + v1 = to_vector(l, 1); + v2 = to_vector(l, 2); + vector_new(l, *v1 + *v2); + } + else + if (istype(l, 1, _Vec3) && istype(l, 2, _Position)) + { + v1 = to_vector(l, 1); + p = to_position(l, 2); + position_new(l, *p + *v1); + } + else + { + doError(l, "Bad vector addition!"); + } + return 1; +} + +static int vector_mult(lua_State* l) +{ + checkArgs(l, 2, 2, "Need two operands for multiplication"); + Vec3d* v1 = NULL; + Vec3d* v2 = NULL; + Quatd* q = NULL; + lua_Number s = 0.0; + if (istype(l, 1, _Vec3) && istype(l, 2, _Vec3)) + { + v1 = to_vector(l, 1); + v2 = to_vector(l, 2); + lua_pushnumber(l, *v1 * *v2); + } + else + if (istype(l, 1, _Vec3) && lua_isnumber(l, 2)) + { + v1 = to_vector(l, 1); + s = lua_tonumber(l, 2); + vector_new(l, *v1 * s); + } + else + if (istype(l, 1, _Vec3) && istype(l, 2, _Rotation)) + { + v1 = to_vector(l, 1); + q = to_rotation(l, 2); + rotation_new(l, *v1 * *q); + } + else + if (lua_isnumber(l, 1) && istype(l, 2, _Vec3)) + { + s = lua_tonumber(l, 1); + v1 = to_vector(l, 2); + vector_new(l, *v1 * s); + } + else + { + doError(l, "Bad vector multiplication!"); + } + return 1; +} + +static int vector_cross(lua_State* l) +{ + checkArgs(l, 2, 2, "Need two operands for multiplication"); + Vec3d* v1 = NULL; + Vec3d* v2 = NULL; + if (istype(l, 1, _Vec3) && istype(l, 2, _Vec3)) + { + v1 = to_vector(l, 1); + v2 = to_vector(l, 2); + vector_new(l, *v1 ^ *v2); + } + else + { + doError(l, "Bad vector multiplication!"); + } + return 1; + +} + +static int vector_tostring(lua_State* l) +{ + lua_pushstring(l, "[Vector]"); + return 1; +} + +static void CreateVectorMetaTable(lua_State* l) +{ + CreateClassMetatable(l, _Vec3); + + RegisterMethod(l, "__tostring", vector_tostring); + RegisterMethod(l, "__add", vector_add); + RegisterMethod(l, "__sub", vector_sub); + RegisterMethod(l, "__mul", vector_mult); + RegisterMethod(l, "__pow", vector_cross); + RegisterMethod(l, "__index", vector_get); + RegisterMethod(l, "__newindex", vector_set); + RegisterMethod(l, "getx", vector_getx); + RegisterMethod(l, "gety", vector_gety); + RegisterMethod(l, "getz", vector_getz); + RegisterMethod(l, "normalize", vector_normalize); + RegisterMethod(l, "length", vector_length); + + lua_pop(l, 1); // remove metatable from stack +} + +// ==================== Rotation ==================== +static int rotation_new(lua_State* l, const Quatd& qd) +{ + Quatd* q = reinterpret_cast(lua_newuserdata(l, sizeof(Quatd))); + *q = qd; + SetClass(l, _Rotation); + + return 1; +} + +static Quatd* to_rotation(lua_State* l, int index) +{ + return static_cast(CheckUserData(l, index, _Rotation)); +} + +static Quatd* this_rotation(lua_State* l) +{ + Quatd* q = to_rotation(l, 1); + if (q == NULL) + { + doError(l, "Bad rotation object!"); + } + + return q; +} + + +static int rotation_add(lua_State* l) +{ + checkArgs(l, 2, 2, "Need two operands for add"); + Quatd* q1 = to_rotation(l, 1); + Quatd* q2 = to_rotation(l, 2); + if (q1 == NULL || q2 == NULL) + { + doError(l, "Addition only defined for two rotations"); + } + else + { + Quatd result = *q1 + *q2; + rotation_new(l, result); + } + return 1; +} + +static int rotation_mult(lua_State* l) +{ + checkArgs(l, 2, 2, "Need two operands for multiplication"); + Quatd* r1 = NULL; + Quatd* r2 = NULL; + //Vec3d* v = NULL; + lua_Number s = 0.0; + if (istype(l, 1, _Rotation) && istype(l, 2, _Rotation)) + { + r1 = to_rotation(l, 1); + r2 = to_rotation(l, 2); + rotation_new(l, *r1 * *r2); + } + else + if (istype(l, 1, _Rotation) && lua_isnumber(l, 2)) + { + r1 = to_rotation(l, 1); + s = lua_tonumber(l, 2); + rotation_new(l, *r1 * s); + } + else + if (lua_isnumber(l, 1) && istype(l, 2, _Rotation)) + { + s = lua_tonumber(l, 1); + r1 = to_rotation(l, 2); + rotation_new(l, *r1 * s); + } + else + { + doError(l, "Bad rotation multiplication!"); + } + return 1; +} + +static int rotation_imag(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for rotation_imag"); + Quatd* q = this_rotation(l); + vector_new(l, imag(*q)); + return 1; +} + +static int rotation_real(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for rotation_real"); + Quatd* q = this_rotation(l); + lua_pushnumber(l, real(*q)); + return 1; +} + +static int rotation_transform(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected for rotation:transform()"); + Quatd* q = this_rotation(l); + Vec3d* v = to_vector(l, 2); + if (v == NULL) + { + doError(l, "Argument to rotation:transform() must be a vector"); + } + vector_new(l, *v * q->toMatrix3()); + return 1; +} + +static int rotation_setaxisangle(lua_State* l) +{ + checkArgs(l, 3, 3, "Two arguments expected for rotation:setaxisangle()"); + Quatd* q = this_rotation(l); + Vec3d* v = to_vector(l, 2); + if (v == NULL) + { + doError(l, "setaxisangle: first argument must be a vector"); + } + double angle = safeGetNumber(l, 3, AllErrors, "second argument to rotation:setaxisangle must be a number"); + q->setAxisAngle(*v, angle); + return 0; +} + +static int rotation_slerp(lua_State* l) +{ + checkArgs(l, 3, 3, "Two arguments expected for rotation:slerp()"); + Quatd* q1 = this_rotation(l); + Quatd* q2 = to_rotation(l, 2); + if (q2 == NULL) + { + doError(l, "slerp: first argument must be a rotation"); + } + double t = safeGetNumber(l, 3, AllErrors, "second argument to rotation:slerp must be a number"); + rotation_new(l, Quatd::slerp(*q1, *q2, t)); + return 1; +} + +static int rotation_get(lua_State* l) +{ + checkArgs(l, 2, 2, "Invalid access of rotation-component"); + Quatd* q3 = this_rotation(l); + string key = safeGetString(l, 2, AllErrors, "Invalid key in rotation-access"); + double value = 0.0; + if (key == "x") + value = imag(*q3).x; + else if (key == "y") + value = imag(*q3).y; + else if (key == "z") + value = imag(*q3).z; + else if (key == "w") + value = real(*q3); + else + { + if (lua_getmetatable(l, 1)) + { + lua_pushvalue(l, 2); + lua_rawget(l, -2); + return 1; + } + else + { + doError(l, "Internal error: couldn't get metatable"); + } + } + lua_pushnumber(l, (lua_Number)value); + return 1; +} + +static int rotation_set(lua_State* l) +{ + checkArgs(l, 3, 3, "Invalid access of rotation-component"); + Quatd* q3 = this_rotation(l); + string key = safeGetString(l, 2, AllErrors, "Invalid key in rotation-access"); + double value = safeGetNumber(l, 3, AllErrors, "Rotation components must be numbers"); + Vec3d v = imag(*q3); + double w = real(*q3); + if (key == "x") + v.x = value; + else if (key == "y") + v.y = value; + else if (key == "z") + v.z = value; + else if (key == "w") + w = value; + else + { + doError(l, "Invalid key in rotation-access"); + } + *q3 = Quatd(w, v); + return 0; +} + +static int rotation_tostring(lua_State* l) +{ + lua_pushstring(l, "[Rotation]"); + return 1; +} + +static void CreateRotationMetaTable(lua_State* l) +{ + CreateClassMetatable(l, _Rotation); + + RegisterMethod(l, "real", rotation_real); + RegisterMethod(l, "imag", rotation_imag); + RegisterMethod(l, "transform", rotation_transform); + RegisterMethod(l, "setaxisangle", rotation_setaxisangle); + RegisterMethod(l, "slerp", rotation_slerp); + RegisterMethod(l, "__tostring", rotation_tostring); + RegisterMethod(l, "__add", rotation_add); + RegisterMethod(l, "__mul", rotation_mult); + RegisterMethod(l, "__index", rotation_get); + RegisterMethod(l, "__newindex", rotation_set); + + lua_pop(l, 1); // remove metatable from stack +} + +// ==================== Position ==================== +// a 128-bit per component universal coordinate +static int position_new(lua_State* l, const UniversalCoord& uc) +{ + UniversalCoord* ud = reinterpret_cast(lua_newuserdata(l, sizeof(UniversalCoord))); + *ud = uc; + + SetClass(l, _Position); + + return 1; +} + +static UniversalCoord* to_position(lua_State* l, int index) +{ + return static_cast(CheckUserData(l, index, _Position)); +} + +static UniversalCoord* this_position(lua_State* l) +{ + UniversalCoord* uc = to_position(l, 1); + if (uc == NULL) + { + doError(l, "Bad position object!"); + } + + return uc; +} + + +static int position_get(lua_State* l) +{ + checkArgs(l, 2, 2, "Invalid access of position-component"); + UniversalCoord* uc = this_position(l); + string key = safeGetString(l, 2, AllErrors, "Invalid key in position-access"); + double value = 0.0; + if (key == "x") + value = uc->x; + else if (key == "y") + value = uc->y; + else if (key == "z") + value = uc->z; + else + { + if (lua_getmetatable(l, 1)) + { + lua_pushvalue(l, 2); + lua_rawget(l, -2); + return 1; + } + else + { + doError(l, "Internal error: couldn't get metatable"); + } + } + lua_pushnumber(l, (lua_Number)value); + return 1; +} + +static int position_set(lua_State* l) +{ + checkArgs(l, 3, 3, "Invalid access of position-component"); + UniversalCoord* uc = this_position(l); + string key = safeGetString(l, 2, AllErrors, "Invalid key in position-access"); + double value = safeGetNumber(l, 3, AllErrors, "Position components must be numbers"); + if (key == "x") + uc->x = value; + else if (key == "y") + uc->y = value; + else if (key == "z") + uc->z = value; + else + { + doError(l, "Invalid key in position-access"); + } + return 0; +} + +static int position_getx(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for position:getx()"); + + UniversalCoord* uc = this_position(l); + lua_Number x; + x = uc->x; + lua_pushnumber(l, x); + + return 1; +} + +static int position_gety(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for position:gety()"); + + UniversalCoord* uc = this_position(l); + lua_Number y; + y = uc->y; + lua_pushnumber(l, y); + + return 1; +} + +static int position_getz(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for position:getz()"); + + UniversalCoord* uc = this_position(l); + lua_Number z; + z = uc->z; + lua_pushnumber(l, z); + + return 1; +} + +static int position_vectorto(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected to position:vectorto"); + + UniversalCoord* uc = this_position(l); + UniversalCoord* uc2 = to_position(l, 2); + + if (uc2 == NULL) + { + doError(l, "Argument to position:vectorto must be a position"); + } + Vec3d diff = *uc2 - *uc; + vector_new(l, diff); + return 1; +} + +static int position_orientationto(lua_State* l) +{ + checkArgs(l, 3, 3, "Two arguments expected for position:orientationto"); + + UniversalCoord* src = this_position(l); + UniversalCoord* target = to_position(l, 2); + + if (target == NULL) + { + doError(l, "First argument to position:orientationto must be a position"); + } + + Vec3d* upd = to_vector(l, 3); + if (upd == NULL) + { + doError(l, "Second argument to position:orientationto must be a vector"); + } + + Vec3d src2target = *target - *src; + src2target.normalize(); + Vec3d v = src2target ^ *upd; + v.normalize(); + Vec3d u = v ^ src2target; + Quatd qd = Quatd(Mat3d(v, u, -src2target)); + rotation_new(l, qd); + + return 1; +} + +static int position_tostring(lua_State* l) +{ + // TODO: print out the coordinate as it would appear in a cel:// URL + lua_pushstring(l, "[Position]"); + + return 1; +} + +static int position_distanceto(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected to position:distanceto()"); + + UniversalCoord* uc = this_position(l); + UniversalCoord* uc2 = to_position(l, 2); + if (uc2 == NULL) + { + doError(l, "Position expected as argument to position:distanceto"); + } + + Vec3d v = *uc2 - *uc; + lua_pushnumber(l, astro::microLightYearsToKilometers(v.length())); + + return 1; +} + +static int position_add(lua_State* l) +{ + checkArgs(l, 2, 2, "Need two operands for addition"); + UniversalCoord* p1 = NULL; + UniversalCoord* p2 = NULL; + Vec3d* v2 = NULL; + + if (istype(l, 1, _Position) && istype(l, 2, _Position)) + { + p1 = to_position(l, 1); + p2 = to_position(l, 2); + // this is not very intuitive, as p1-p2 is a vector + position_new(l, *p1 + *p2); + } + else + if (istype(l, 1, _Position) && istype(l, 2, _Vec3)) + { + p1 = to_position(l, 1); + v2 = to_vector(l, 2); + position_new(l, *p1 + *v2); + } + else + { + doError(l, "Bad position addition!"); + } + return 1; +} + +static int position_sub(lua_State* l) +{ + checkArgs(l, 2, 2, "Need two operands for subtraction"); + UniversalCoord* p1 = NULL; + UniversalCoord* p2 = NULL; + Vec3d* v2 = NULL; + + if (istype(l, 1, _Position) && istype(l, 2, _Position)) + { + p1 = to_position(l, 1); + p2 = to_position(l, 2); + vector_new(l, *p1 - *p2); + } + else + if (istype(l, 1, _Position) && istype(l, 2, _Vec3)) + { + p1 = to_position(l, 1); + v2 = to_vector(l, 2); + position_new(l, *p1 - *v2); + } + else + { + doError(l, "Bad position subtraction!"); + } + return 1; +} + +static int position_addvector(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected to position:addvector()"); + UniversalCoord* uc = this_position(l); + Vec3d* v3d = to_vector(l, 2); + if (v3d == NULL) + { + doError(l, "Vector expected as argument to position:addvector"); + } + else + if (uc != NULL && v3d != NULL) + { + UniversalCoord ucnew = *uc + *v3d; + position_new(l, ucnew); + } + return 1; +} + +static void CreatePositionMetaTable(lua_State* l) +{ + CreateClassMetatable(l, _Position); + + RegisterMethod(l, "__tostring", position_tostring); + RegisterMethod(l, "distanceto", position_distanceto); + RegisterMethod(l, "vectorto", position_vectorto); + RegisterMethod(l, "orientationto", position_orientationto); + RegisterMethod(l, "addvector", position_addvector); + RegisterMethod(l, "__add", position_add); + RegisterMethod(l, "__sub", position_sub); + RegisterMethod(l, "__index", position_get); + RegisterMethod(l, "__newindex", position_set); + RegisterMethod(l, "getx", position_getx); + RegisterMethod(l, "gety", position_gety); + RegisterMethod(l, "getz", position_getz); + + lua_pop(l, 1); // remove metatable from stack +} + +// ==================== FrameOfReference ==================== +static int frame_new(lua_State* l, const ObserverFrame& f) +{ + ObserverFrame* ud = reinterpret_cast(lua_newuserdata(l, sizeof(ObserverFrame))); + *ud = f; + + SetClass(l, _Frame); + + return 1; +} + +static ObserverFrame* to_frame(lua_State* l, int index) +{ + return static_cast(CheckUserData(l, index, _Frame)); +} + +static ObserverFrame* this_frame(lua_State* l) +{ + ObserverFrame* f = to_frame(l, 1); + if (f == NULL) + { + doError(l, "Bad frame object!"); + } + + return f; +} + + +// Convert from frame coordinates to universal. +static int frame_from(lua_State* l) +{ + checkArgs(l, 2, 3, "Two or three arguments required for frame:from"); + + ObserverFrame* frame = this_frame(l); + CelestiaCore* appCore = getAppCore(l, AllErrors); + + UniversalCoord* uc = NULL; + Quatd* q = NULL; + double jd = 0.0; + + if (istype(l, 2, _Position)) + { + uc = to_position(l, 2); + } + else if (istype(l, 2, _Rotation)) + { + q = to_rotation(l, 2); + } + if (uc == NULL && q == NULL) + { + doError(l, "Position or rotation expected as second argument to frame:from()"); + } + + jd = safeGetNumber(l, 3, WrongType, "Second arg to frame:from must be a number", appCore->getSimulation()->getTime()); + + if (uc != NULL) + { + UniversalCoord uc1 = frame->convertToUniversal(*uc, jd); + position_new(l, uc1); + } + else + { + Quatd q1 = frame->convertToUniversal(*q, jd); + rotation_new(l, q1); + } + + return 1; +} + +// Convert from universal to frame coordinates. +static int frame_to(lua_State* l) +{ + checkArgs(l, 2, 3, "Two or three arguments required for frame:to"); + + ObserverFrame* frame = this_frame(l); + CelestiaCore* appCore = getAppCore(l, AllErrors); + + UniversalCoord* uc = NULL; + Quatd* q = NULL; + double jd = 0.0; + + if (istype(l, 2, _Position)) + { + uc = to_position(l, 2); + } + else + if (istype(l, 2, _Rotation)) + { + q = to_rotation(l, 2); + } + + if (uc == NULL && q == NULL) + { + doError(l, "Position or rotation expected as second argument to frame:to()"); + } + + jd = safeGetNumber(l, 3, WrongType, "Second arg to frame:to must be a number", appCore->getSimulation()->getTime()); + + if (uc != NULL) + { + UniversalCoord uc1 = frame->convertFromUniversal(*uc, jd); + position_new(l, uc1); + } + else + { + Quatd q1 = frame->convertFromUniversal(*q, jd); + rotation_new(l, q1); + } + + return 1; +} + +static int frame_getrefobject(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for frame:getrefobject()"); + + ObserverFrame* frame = this_frame(l); + if (frame->getRefObject().getType() == Selection::Type_Nil) + { + lua_pushnil(l); + } + else + { + object_new(l, frame->getRefObject()); + } + return 1; +} + +static int frame_gettargetobject(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for frame:gettarget()"); + + ObserverFrame* frame = this_frame(l); + if (frame->getTargetObject().getType() == Selection::Type_Nil) + { + lua_pushnil(l); + } + else + { + object_new(l, frame->getTargetObject()); + } + return 1; +} + +static int frame_getcoordinatesystem(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for frame:getcoordinatesystem()"); + + ObserverFrame* frame = this_frame(l); + string coordsys; + switch (frame->getCoordinateSystem()) + { + case ObserverFrame::Universal: + coordsys = "universal"; break; + case ObserverFrame::Ecliptical: + coordsys = "ecliptic"; break; + case ObserverFrame::Equatorial: + coordsys = "equatorial"; break; + case ObserverFrame::BodyFixed: + coordsys = "planetographic"; break; + case ObserverFrame::ObserverLocal: + coordsys = "observer"; break; + case ObserverFrame::PhaseLock: + coordsys = "lock"; break; + case ObserverFrame::Chase: + coordsys = "chase"; break; + default: + coordsys = "invalid"; + } + lua_pushstring(l, coordsys.c_str()); + return 1; +} + +static int frame_tostring(lua_State* l) +{ + // TODO: print out the actual information about the frame + lua_pushstring(l, "[Frame]"); + + return 1; +} + +static void CreateFrameMetaTable(lua_State* l) +{ + CreateClassMetatable(l, _Frame); + + RegisterMethod(l, "__tostring", frame_tostring); + RegisterMethod(l, "to", frame_to); + RegisterMethod(l, "from", frame_from); + RegisterMethod(l, "getcoordinatesystem", frame_getcoordinatesystem); + RegisterMethod(l, "getrefobject", frame_getrefobject); + RegisterMethod(l, "gettargetobject", frame_gettargetobject); + + lua_pop(l, 1); // remove metatable from stack +} + +// ==================== Object ==================== +// star, planet, or deep-sky object +static int object_new(lua_State* l, const Selection& sel) +{ + Selection* ud = reinterpret_cast(lua_newuserdata(l, sizeof(Selection))); + *ud = sel; + + SetClass(l, _Object); + + return 1; +} + +static Selection* to_object(lua_State* l, int index) +{ + return static_cast(CheckUserData(l, index, _Object)); +} + +static Selection* this_object(lua_State* l) +{ + Selection* sel = to_object(l, 1); + if (sel == NULL) + { + doError(l, "Bad position object!"); + } + + return sel; +} + + +static int object_tostring(lua_State* l) +{ + lua_pushstring(l, "[Object]"); + + return 1; +} + + +// Return true if the object is visible, false if not. +static int object_visible(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected to function object:visible"); + + Selection* sel = this_object(l); + lua_pushboolean(l, sel->isVisible()); + + return 1; +} + + +// Set the object visibility flag. +static int object_setvisible(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected to object:setvisible()"); + + Selection* sel = this_object(l); + bool visible = safeGetBoolean(l, 2, AllErrors, "Argument to object:setvisible() must be a boolean"); + if (sel->body() != NULL) + { + sel->body()->setVisible(visible); + } + + return 0; +} + + +static int object_setorbitcolor(lua_State* l) +{ + checkArgs(l, 4, 4, "Red, green, and blue color values exepected for object:setorbitcolor()"); + + Selection* sel = this_object(l); + float r = (float) safeGetNumber(l, 2, WrongType, "Argument 1 to object:setorbitcolor() must be a number", 0.0); + float g = (float) safeGetNumber(l, 3, WrongType, "Argument 2 to object:setorbitcolor() must be a number", 0.0); + float b = (float) safeGetNumber(l, 4, WrongType, "Argument 3 to object:setorbitcolor() must be a number", 0.0); + Color orbitColor(r, g, b); + + if (sel->body() != NULL) + { + sel->body()->setOrbitColor(orbitColor); + } + + return 0; +} + + +static int object_orbitcoloroverridden(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected to object:orbitcoloroverridden"); + + bool isOverridden = false; + Selection* sel = this_object(l); + if (sel->body() != NULL) + { + isOverridden = sel->body()->isOrbitColorOverridden(); + } + + lua_pushboolean(l, isOverridden); + + return 1; +} + + +static int object_setorbitcoloroverridden(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected to object:setorbitcoloroverridden"); + + Selection* sel = this_object(l); + bool override = safeGetBoolean(l, 2, AllErrors, "Argument to object:setorbitcoloroverridden() must be a boolean"); + + if (sel->body() != NULL) + { + sel->body()->setOrbitColorOverridden(override); + } + + return 0; +} + + +static int object_orbitvisibility(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected to object:orbitvisibility"); + + Body::VisibilityPolicy visibility = Body::UseClassVisibility; + + Selection* sel = this_object(l); + if (sel->body() != NULL) + { + visibility = sel->body()->getOrbitVisibility(); + } + + char* s = "normal"; + if (visibility == Body::AlwaysVisible) + s = "always"; + else if (visibility == Body::NeverVisible) + s = "never"; + + lua_pushstring(l, s); + + return 1; +} + + +static int object_setorbitvisibility(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected to object:setorbitcoloroverridden"); + + if (!lua_isstring(l, 2)) + { + doError(l, "First argument to object:setorbitvisibility() must be a string"); + } + + Selection* sel = this_object(l); + + string key; + key = lua_tostring(l, 2); + + if (OrbitVisibilityMap.count(key) == 0) + { + cerr << "Unknown visibility policy: " << key << endl; + } + else + { + Body::VisibilityPolicy visibility = static_cast(OrbitVisibilityMap[key]); + + if (sel->body() != NULL) + { + sel->body()->setOrbitVisibility(visibility); + } + } + + return 0; +} + + +static int object_radius(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected to function object:radius"); + + Selection* sel = this_object(l); + lua_pushnumber(l, sel->radius()); + + return 1; +} + +static int object_setradius(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected to object:setradius()"); + + Selection* sel = this_object(l); + if (sel->body() != NULL) + { + Body* body = sel->body(); + float iradius = body->getRadius(); + double radius = safeGetNumber(l, 2, AllErrors, "Argument to object:setradius() must be a number"); + if ((radius > 0)) + { + body->setSemiAxes(body->getSemiAxes() * ((float) radius / iradius)); + } + + if (body->getRings() != NULL) + { + RingSystem rings(0.0f, 0.0f); + rings = *body->getRings(); + float inner = rings.innerRadius; + float outer = rings.outerRadius; + rings.innerRadius = inner * (float) radius / iradius; + rings.outerRadius = outer * (float) radius / iradius; + body->setRings(rings); + } + } + + return 0; +} + +static int object_type(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected to function object:type"); + + Selection* sel = this_object(l); + const char* tname = "unknown"; + switch (sel->getType()) + { + case Selection::Type_Body: + { + int cl = sel->body()->getClassification(); + switch (cl) + { + case Body::Planet : tname = "planet"; break; + case Body::Moon : tname = "moon"; break; + case Body::Asteroid : tname = "asteroid"; break; + case Body::Comet : tname = "comet"; break; + case Body::Spacecraft : tname = "spacecraft"; break; + case Body::Invisible : tname = "invisible"; break; + } + } + break; + + case Selection::Type_Star: + tname = "star"; + break; + + case Selection::Type_DeepSky: + tname = sel->deepsky()->getObjTypeName(); + break; + + case Selection::Type_Location: + tname = "location"; + break; + + case Selection::Type_Nil: + tname = "null"; + break; + } + + lua_pushstring(l, tname); + + return 1; +} + +static int object_name(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected to function object:name"); + + Selection* sel = this_object(l); + switch (sel->getType()) + { + case Selection::Type_Body: + lua_pushstring(l, sel->body()->getName().c_str()); + break; + case Selection::Type_DeepSky: + lua_pushstring(l, getAppCore(l, AllErrors)->getSimulation()->getUniverse() + ->getDSOCatalog()->getDSOName(sel->deepsky()).c_str()); + break; + case Selection::Type_Star: + lua_pushstring(l, getAppCore(l, AllErrors)->getSimulation()->getUniverse() + ->getStarCatalog()->getStarName(*(sel->star())).c_str()); + break; + case Selection::Type_Location: + lua_pushstring(l, sel->location()->getName().c_str()); + break; + default: + lua_pushstring(l, "?"); + break; + } + + return 1; +} + +static int object_localname(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected to function object:localname"); + + Selection* sel = this_object(l); + switch (sel->getType()) + { + case Selection::Type_Body: + lua_pushstring(l, sel->body()->getName(true).c_str()); + break; + default: + lua_pushstring(l, "?"); + break; + } + + return 1; +} + +static int object_spectraltype(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected to function object:spectraltype"); + + Selection* sel = this_object(l); + if (sel->star() != NULL) + { + char buf[16]; + strncpy(buf, sel->star()->getSpectralType(), sizeof buf); + buf[sizeof(buf) - 1] = '\0'; // make sure it's zero terminate + lua_pushstring(l, buf); + } + else + { + lua_pushnil(l); + } + + return 1; +} + +static int object_getinfo(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected to function object:getinfo"); + + lua_newtable(l); + + Selection* sel = this_object(l); + if (sel->star() != NULL) + { + Star* star = sel->star(); + setTable(l, "type", "star"); + setTable(l, "name", getAppCore(l, AllErrors)->getSimulation()->getUniverse() + ->getStarCatalog()->getStarName(*(sel->star())).c_str()); + setTable(l, "catalogNumber", star->getCatalogNumber()); + setTable(l, "stellarClass", star->getSpectralType()); + setTable(l, "absoluteMagnitude", (lua_Number)star->getAbsoluteMagnitude()); + setTable(l, "luminosity", (lua_Number)star->getLuminosity()); + setTable(l, "radius", (lua_Number)star->getRadius()); + setTable(l, "temperature", (lua_Number)star->getTemperature()); + setTable(l, "rotationPeriod", (lua_Number)star->getRotationModel()->getPeriod()); + setTable(l, "bolometricMagnitude", (lua_Number)star->getBolometricMagnitude()); + + if (star->getOrbitBarycenter() != NULL) + { + Selection parent((Star*)(star->getOrbitBarycenter())); + lua_pushstring(l, "parent"); + object_new(l, parent); + lua_settable(l, -3); + } + } + else if (sel->body() != NULL) + { + Body* body = sel->body(); + const char* tname = "unknown"; + switch (body->getClassification()) + { + case Body::Planet : tname = "planet"; break; + case Body::Moon : tname = "moon"; break; + case Body::Asteroid : tname = "asteroid"; break; + case Body::Comet : tname = "comet"; break; + case Body::Spacecraft : tname = "spacecraft"; break; + case Body::Invisible : tname = "invisible"; break; + } + + setTable(l, "type", tname); + setTable(l, "name", body->getName().c_str()); + setTable(l, "mass", (lua_Number)body->getMass()); + setTable(l, "albedo", (lua_Number)body->getAlbedo()); + setTable(l, "infoURL", body->getInfoURL().c_str()); + setTable(l, "radius", (lua_Number)body->getRadius()); + + // TODO: add method to return semiaxes + Vec3f semiAxes = body->getSemiAxes(); + // Note: oblateness is an obsolete field, replaced by semiaxes; + // it's only here for backward compatibility. + float polarRadius = semiAxes.y; + float eqRadius = max(semiAxes.x, semiAxes.z); + setTable(l, "oblateness", (eqRadius - polarRadius) / eqRadius); + + double lifespanStart, lifespanEnd; + body->getLifespan(lifespanStart, lifespanEnd); + setTable(l, "lifespanStart", (lua_Number)lifespanStart); + setTable(l, "lifespanEnd", (lua_Number)lifespanEnd); + // TODO: atmosphere, surfaces ? + + PlanetarySystem* system = body->getSystem(); + if (system->getPrimaryBody() != NULL) + { + Selection parent(system->getPrimaryBody()); + lua_pushstring(l, "parent"); + object_new(l, parent); + lua_settable(l, -3); + } + else + { + Selection parent(system->getStar()); + lua_pushstring(l, "parent"); + object_new(l, parent); + lua_settable(l, -3); + } + + lua_pushstring(l, "hasRings"); + lua_pushboolean(l, body->getRings() != NULL); + lua_settable(l, -3); + + const RotationModel* rm = body->getRotationModel(); + setTable(l, "rotationPeriod", (double) rm->getPeriod()); + + Orbit* orbit = body->getOrbit(); + setTable(l, "orbitPeriod", orbit->getPeriod()); + Atmosphere* atmosphere = body->getAtmosphere(); + if (atmosphere != NULL) + { + setTable(l, "atmosphereHeight", (double)atmosphere->height); + setTable(l, "atmosphereCloudHeight", (double)atmosphere->cloudHeight); + setTable(l, "atmosphereCloudSpeed", (double)atmosphere->cloudSpeed); + } + } + else if (sel->deepsky() != NULL) + { + DeepSkyObject* deepsky = sel->deepsky(); + const char* objTypeName = deepsky->getObjTypeName(); + setTable(l, "type", objTypeName); + + setTable(l, "name", getAppCore(l, AllErrors)->getSimulation()->getUniverse() + ->getDSOCatalog()->getDSOName(deepsky).c_str()); + setTable(l, "catalogNumber", deepsky->getCatalogNumber()); + + if (objTypeName == "galaxy") + setTable(l, "hubbleType", deepsky->getType()); + + setTable(l, "absoluteMagnitude", (lua_Number)deepsky->getAbsoluteMagnitude()); + setTable(l, "radius", (lua_Number)deepsky->getRadius()); + } + else if (sel->location() != NULL) + { + setTable(l, "type", "location"); + Location* location = sel->location(); + setTable(l, "name", location->getName().c_str()); + setTable(l, "size", (lua_Number)location->getSize()); + setTable(l, "importance", (lua_Number)location->getImportance()); + setTable(l, "infoURL", location->getInfoURL().c_str()); + + uint32 featureType = location->getFeatureType(); + string featureName("Unknown"); + for (FlagMap::const_iterator it = LocationFlagMap.begin(); it != LocationFlagMap.end(); it++) + { + if (it->second == featureType) + { + featureName = it->first; + break; + } + } + setTable(l, "featureType", featureName.c_str()); + + Body* parent = location->getParentBody(); + if (parent != NULL) + { + Selection selection(parent); + lua_pushstring(l, "parent"); + object_new(l, selection); + lua_settable(l, -3); + } + } + else + { + setTable(l, "type", "null"); + } + return 1; +} + +static int object_absmag(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected to function object:absmag"); + + Selection* sel = this_object(l); + if (sel->star() != NULL) + lua_pushnumber(l, sel->star()->getAbsoluteMagnitude()); + else + lua_pushnil(l); + + return 1; +} + +static int object_mark(lua_State* l) +{ + checkArgs(l, 1, 6, "Need 0 to 5 arguments for object:mark"); + + Selection* sel = this_object(l); + CelestiaCore* appCore = getAppCore(l, AllErrors); + + Color markColor(0.0f, 1.0f, 0.0f); + const char* colorString = safeGetString(l, 2, WrongType, "First argument to object:mark must be a string"); + if (colorString != NULL) + Color::parse(colorString, markColor); + + Marker::Symbol markSymbol = Marker::Diamond; + const char* markerString = safeGetString(l, 3, WrongType, "Second argument to object:mark must be a string"); + if (markerString != NULL) + markSymbol = parseMarkerSymbol(markerString); + + float markSize = (float)safeGetNumber(l, 4, WrongType, "Third arg to object:mark must be a number", 10.0); + if (markSize < 1.0f) + markSize = 1.0f; + else if (markSize > 10000.0f) + markSize = 10000.0f; + + float markAlpha = (float)safeGetNumber(l, 5, WrongType, "Fourth arg to object:mark must be a number", 0.9); + if (markAlpha < 0.0f) + markAlpha = 0.0f; + else if (markAlpha > 1.0f) + markAlpha = 1.0f; + + Color markColorAlpha(0.0f, 1.0f, 0.0f, 0.9f); + markColorAlpha = Color::Color(markColor, markAlpha); + + const char* markLabel = safeGetString(l, 6, WrongType, "Fifth argument to object:mark must be a string"); + if (markLabel == NULL) + markLabel = ""; + + Simulation* sim = appCore->getSimulation(); + sim->getUniverse()->markObject(*sel, markSize, + markColorAlpha, markSymbol, 1, markLabel); + + return 0; +} + +static int object_unmark(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected to function object:unmark"); + + Selection* sel = this_object(l); + CelestiaCore* appCore = getAppCore(l, AllErrors); + + Simulation* sim = appCore->getSimulation(); + sim->getUniverse()->unmarkObject(*sel, 1); + + return 0; +} + +// Return the object's current position. A time argument is optional; +// if not provided, the current master simulation time is used. +static int object_getposition(lua_State* l) +{ + checkArgs(l, 1, 2, "Expected no or one argument to object:getposition"); + + Selection* sel = this_object(l); + CelestiaCore* appCore = getAppCore(l, AllErrors); + + double t = safeGetNumber(l, 2, WrongType, "Time expected as argument to object:getposition", + appCore->getSimulation()->getTime()); + position_new(l, sel->getPosition(t)); + + return 1; +} + +static int object_getchildren(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for object:getchildren()"); + + Selection* sel = this_object(l); + CelestiaCore* appCore = getAppCore(l, AllErrors); + + Simulation* sim = appCore->getSimulation(); + + lua_newtable(l); + if (sel->star() != NULL) + { + SolarSystemCatalog* solarSystemCatalog = sim->getUniverse()->getSolarSystemCatalog(); + SolarSystemCatalog::iterator iter = solarSystemCatalog->find(sel->star()->getCatalogNumber()); + if (iter != solarSystemCatalog->end()) + { + SolarSystem* solarSys = iter->second; + for (int i = 0; i < solarSys->getPlanets()->getSystemSize(); i++) + { + Body* body = solarSys->getPlanets()->getBody(i); + Selection satSel(body); + object_new(l, satSel); + lua_rawseti(l, -2, i + 1); + } + } + } + else if (sel->body() != NULL) + { + const PlanetarySystem* satellites = sel->body()->getSatellites(); + if (satellites != NULL && satellites->getSystemSize() != 0) + { + for (int i = 0; i < satellites->getSystemSize(); i++) + { + Body* body = satellites->getBody(i); + Selection satSel(body); + object_new(l, satSel); + lua_rawseti(l, -2, i + 1); + } + } + } + + return 1; +} + +static int object_preloadtexture(lua_State* l) +{ + checkArgs(l, 1, 1, "No argument expected to object:preloadtexture"); + CelestiaCore* appCore = getAppCore(l, AllErrors); + + Renderer* renderer = appCore->getRenderer(); + Selection* sel = this_object(l); + if (sel->body() != NULL && renderer != NULL) + { + LuaState* luastate = getLuaStateObject(l); + // make sure we don't timeout because of texture-loading: + double timeToTimeout = luastate->timeout - luastate->getTime(); + + renderer->loadTextures(sel->body()); + + // no matter how long it really took, make it look like 0.1s: + luastate->timeout = luastate->getTime() + timeToTimeout - 0.1; + } + return 0; +} + +static void CreateObjectMetaTable(lua_State* l) +{ + CreateClassMetatable(l, _Object); + + RegisterMethod(l, "__tostring", object_tostring); + RegisterMethod(l, "visible", object_visible); + RegisterMethod(l, "setvisible", object_setvisible); + RegisterMethod(l, "orbitcoloroverridden", object_orbitcoloroverridden); + RegisterMethod(l, "setorbitcoloroverridden", object_setorbitcoloroverridden); + RegisterMethod(l, "setorbitcolor", object_setorbitcolor); + RegisterMethod(l, "orbitvisibility", object_orbitvisibility); + RegisterMethod(l, "setorbitvisibility", object_setorbitvisibility); + RegisterMethod(l, "radius", object_radius); + RegisterMethod(l, "setradius", object_setradius); + RegisterMethod(l, "type", object_type); + RegisterMethod(l, "spectraltype", object_spectraltype); + RegisterMethod(l, "getinfo", object_getinfo); + RegisterMethod(l, "absmag", object_absmag); + RegisterMethod(l, "name", object_name); + RegisterMethod(l, "localname", object_localname); + RegisterMethod(l, "mark", object_mark); + RegisterMethod(l, "unmark", object_unmark); + RegisterMethod(l, "getposition", object_getposition); + RegisterMethod(l, "getchildren", object_getchildren); + RegisterMethod(l, "preloadtexture", object_preloadtexture); + + lua_pop(l, 1); // pop metatable off the stack +} + +// ==================== Observer ==================== +static int observer_new(lua_State* l, Observer* o) +{ + Observer** ud = static_cast(lua_newuserdata(l, sizeof(Observer*))); + *ud = o; + + SetClass(l, _Observer); + + return 1; +} + +static Observer* to_observer(lua_State* l, int index) +{ + Observer** o = static_cast(lua_touserdata(l, index)); + CelestiaCore* appCore = getAppCore(l, AllErrors); + + // Check if pointer is still valid, i.e. is used by a view: + if (o != NULL && getViewByObserver(appCore, *o) != NULL) + { + return *o; + } + return NULL; +} + +static Observer* this_observer(lua_State* l) +{ + Observer* obs = to_observer(l, 1); + if (obs == NULL) + { + doError(l, "Bad observer object (maybe tried to access a deleted view?)!"); + } + + return obs; +} + + +static int observer_isvalid(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for observer:isvalid()"); + lua_pushboolean(l, to_observer(l, 1) != NULL); + return 1; +} + +static int observer_tostring(lua_State* l) +{ + lua_pushstring(l, "[Observer]"); + + return 1; +} + +static int observer_setposition(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument required for setpos"); + + Observer* o = this_observer(l); + + UniversalCoord* uc = to_position(l,2); + if (uc == NULL) + { + doError(l, "Argument to observer:setposition must be a position"); + } + o->setPosition(*uc); + return 0; +} + +static int observer_setorientation(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument required for setorientation"); + + Observer* o = this_observer(l); + + Quatd* q = to_rotation(l,2); + if (q == NULL) + { + doError(l, "Argument to observer:setorientation must be a rotation"); + } + o->setOrientation(*q); + return 0; +} + +static int observer_getorientation(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected to observer:getorientation()"); + + Observer* o = this_observer(l); + rotation_new(l, o->getOrientation()); + + return 1; +} + +static int observer_rotate(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument required for rotate"); + + Observer* o = this_observer(l); + + Quatd* q = to_rotation(l,2); + if (q == NULL) + { + doError(l, "Argument to observer:setpos must be a rotation"); + } + Quatf qf((float) q->w, (float) q->x, (float) q->y, (float) q->z); + o->rotate(qf); + return 0; +} + +static int observer_lookat(lua_State* l) +{ + checkArgs(l, 3, 4, "Two or three arguments required for lookat"); + int argc = lua_gettop(l); + + Observer* o = this_observer(l); + + UniversalCoord* from = NULL; + UniversalCoord* to = NULL; + Vec3d* upd = NULL; + if (argc == 3) + { + to = to_position(l, 2); + upd = to_vector(l, 3); + if (to == NULL) + { + doError(l, "Argument 1 (of 2) to observer:lookat must be of type position"); + } + } + else + if (argc == 4) + { + from = to_position(l, 2); + to = to_position(l, 3); + upd = to_vector(l, 4); + + if (to == NULL || from == NULL) + { + doError(l, "Argument 1 and 2 (of 3) to observer:lookat must be of type position"); + } + } + if (upd == NULL) + { + doError(l, "Last argument to observer:lookat must be of type vector"); + } + Vec3d nd; + if (from == NULL) + { + nd = (*to) - o->getPosition(); + } + else + { + nd = (*to) - (*from); + } + // need Vec3f instead: + Vec3f up = Vec3f((float) upd->x, (float) upd->y, (float) upd->z); + Vec3f n = Vec3f((float) nd.x, (float) nd.y, (float) nd.z); + + n.normalize(); + Vec3f v = n ^ up; + v.normalize(); + Vec3f u = v ^ n; + Quatf qf = Quatf(Mat3f(v, u, -n)); + o->setOrientation(qf); + return 0; +} + +static int observer_gototable(lua_State* l) +{ + checkArgs(l, 2, 2, "Expected one table as argument to goto"); + + Observer* o = this_observer(l); + if (!lua_istable(l, 2)) + { + lua_pushstring(l, "Argument to goto must be a table"); + } + + Observer::JourneyParams jparams; + jparams.duration = 5.0; + jparams.from = o->getPosition(); + jparams.to = o->getPosition(); + jparams.initialOrientation = o->getOrientation(); + jparams.finalOrientation = o->getOrientation(); + jparams.startInterpolation = 0.25; + jparams.endInterpolation = 0.75; + jparams.accelTime = 0.5; + jparams.traj = Observer::Linear; + + lua_pushstring(l, "duration"); + lua_gettable(l, 2); + jparams.duration = safeGetNumber(l, 3, NoErrors, "", 5.0); + lua_settop(l, 2); + + lua_pushstring(l, "from"); + lua_gettable(l, 2); + UniversalCoord* from = to_position(l, 3); + if (from != NULL) + jparams.from = *from; + lua_settop(l, 2); + + lua_pushstring(l, "to"); + lua_gettable(l, 2); + UniversalCoord* to = to_position(l, 3); + if (to != NULL) + jparams.to = *to; + lua_settop(l, 2); + + lua_pushstring(l, "initialOrientation"); + lua_gettable(l, 2); + Quatd* rot1 = to_rotation(l, 3); + if (rot1 != NULL) + jparams.initialOrientation = *rot1; + lua_settop(l, 2); + + lua_pushstring(l, "finalOrientation"); + lua_gettable(l, 2); + Quatd* rot2 = to_rotation(l, 3); + if (rot2 != NULL) + jparams.finalOrientation = *rot2; + lua_settop(l, 2); + + lua_pushstring(l, "startInterpolation"); + lua_gettable(l, 2); + jparams.startInterpolation = safeGetNumber(l, 3, NoErrors, "", 0.25); + lua_settop(l, 2); + + lua_pushstring(l, "endInterpolation"); + lua_gettable(l, 2); + jparams.endInterpolation = safeGetNumber(l, 3, NoErrors, "", 0.75); + lua_settop(l, 2); + + lua_pushstring(l, "accelTime"); + lua_gettable(l, 2); + jparams.accelTime = safeGetNumber(l, 3, NoErrors, "", 0.5); + lua_settop(l, 2); + + jparams.duration = max(0.0, jparams.duration); + jparams.accelTime = min(1.0, max(0.1, jparams.accelTime)); + jparams.startInterpolation = min(1.0, max(0.0, jparams.startInterpolation)); + jparams.endInterpolation = min(1.0, max(0.0, jparams.endInterpolation)); + + // args are in universal coords, let setFrame handle conversion: + ObserverFrame tmp = *(o->getFrame()); + o->setFrame(ObserverFrame::Universal, Selection()); + o->gotoJourney(jparams); + o->setFrame(tmp); + + return 0; +} + +// First argument is the target object or position; optional second argument +// is the travel time +static int observer_goto(lua_State* l) +{ + if (lua_gettop(l) == 2 && lua_istable(l, 2)) + { + // handle this in own function + return observer_gototable(l); + } + checkArgs(l, 1, 5, "One to four arguments expected to observer:goto"); + + Observer* o = this_observer(l); + + Selection* sel = to_object(l, 2); + UniversalCoord* uc = to_position(l, 2); + if (sel == NULL && uc == NULL) + { + doError(l, "First arg to observer:goto must be object or position"); + } + + double travelTime = safeGetNumber(l, 3, WrongType, "Second arg to observer:goto must be a number", 5.0); + double startInter = safeGetNumber(l, 4, WrongType, "Third arg to observer:goto must be a number", 0.25); + double endInter = safeGetNumber(l, 5, WrongType, "Fourth arg to observer:goto must be a number", 0.75); + if (startInter < 0 || startInter > 1) startInter = 0.25; + if (endInter < 0 || endInter > 1) startInter = 0.75; + + // The first argument may be either an object or a position + if (sel != NULL) + { + o->gotoSelection(*sel, travelTime, startInter, endInter, Vec3f(0, 1, 0), ObserverFrame::ObserverLocal); + } + else + { +#if 0 + RigidTransform rt = o->getSituation(); + rt.translation = *uc; + o->gotoLocation(rt, travelTime); +#endif + o->gotoLocation(*uc, o->getOrientation(), travelTime); + } + + return 0; +} + +static int observer_gotolonglat(lua_State* l) +{ + checkArgs(l, 2, 7, "One to five arguments expected to observer:gotolonglat"); + + Observer* o = this_observer(l); + + Selection* sel = to_object(l, 2); + if (sel == NULL) + { + doError(l, "First arg to observer:gotolonglat must be an object"); + } + double defaultDistance = sel->radius() * 5.0; + + double longitude = safeGetNumber(l, 3, WrongType, "Second arg to observer:gotolonglat must be a number", 0.0); + double latitude = safeGetNumber(l, 4, WrongType, "Third arg to observer:gotolonglat must be a number", 0.0); + double distance = safeGetNumber(l, 5, WrongType, "Fourth arg to observer:gotolonglat must be a number", defaultDistance); + double travelTime = safeGetNumber(l, 6, WrongType, "Fifth arg to observer:gotolonglat must be a number", 5.0); + + distance = distance / KM_PER_LY; + + Vec3f up(0.0f, 1.0f, 0.0f); + if (lua_gettop(l) >= 7) + { + Vec3d* uparg = to_vector(l, 7); + if (uparg == NULL) + { + doError(l, "Sixth argument to observer:gotolonglat must be a vector"); + } + up = Vec3f((float)uparg->x, (float)uparg->y, (float)uparg->z); + } + o->gotoSelectionLongLat(*sel, travelTime, distance, (float)longitude, (float)latitude, up); + + return 0; +} + +// deprecated: wrong name, bad interface. +static int observer_gotolocation(lua_State* l) +{ + checkArgs(l, 2, 3,"Expected one or two arguments to observer:gotolocation"); + + Observer* o = this_observer(l); + + double travelTime = safeGetNumber(l, 3, WrongType, "Second arg to observer:gotolocation must be a number", 5.0); + if (travelTime < 0) + travelTime = 0.0; + + UniversalCoord* uc = to_position(l, 2); + if (uc != NULL) + { +#if 0 + RigidTransform rt = o->getSituation(); + rt.translation = *uc; + o->gotoLocation(rt, travelTime); +#endif + o->gotoLocation(*uc, o->getOrientation(), travelTime); + } + else + { + doError(l, "First arg to observer:gotolocation must be a position"); + } + + return 0; +} + +static int observer_gotodistance(lua_State* l) +{ + checkArgs(l, 2, 5, "One to four arguments expected to observer:gotodistance"); + + Observer* o = this_observer(l); + Selection* sel = to_object(l, 2); + if (sel == NULL) + { + doError(l, "First arg to observer:gotodistance must be object"); + } + + double distance = safeGetNumber(l, 3, WrongType, "Second arg to observer:gotodistance must be a number", 20000); + double travelTime = safeGetNumber(l, 4, WrongType, "Third arg to observer:gotodistance must be a number", 5.0); + + Vec3f up(0,1,0); + if (lua_gettop(l) > 4) + { + Vec3d* up_arg = to_vector(l, 5); + if (up_arg == NULL) + { + doError(l, "Fourth arg to observer:gotodistance must be a vector"); + } + up.x = (float)up_arg->x; + up.y = (float)up_arg->y; + up.z = (float)up_arg->z; + } + + o->gotoSelection(*sel, travelTime, astro::kilometersToLightYears(distance), up, ObserverFrame::Universal); + + return 0; +} + +static int observer_gotosurface(lua_State* l) +{ + checkArgs(l, 2, 3, "One to two arguments expected to observer:gotosurface"); + + Observer* o = this_observer(l); + Selection* sel = to_object(l, 2); + if (sel == NULL) + { + doError(l, "First arg to observer:gotosurface must be object"); + } + + double travelTime = safeGetNumber(l, 3, WrongType, "Second arg to observer:gotosurface must be a number", 5.0); + + // This is needed because gotoSurface expects frame to be geosync: + o->geosynchronousFollow(*sel); + o->gotoSurface(*sel, travelTime); + + return 0; +} + +static int observer_center(lua_State* l) +{ + checkArgs(l, 2, 3, "Expected one or two arguments for to observer:center"); + + Observer* o = this_observer(l); + Selection* sel = to_object(l, 2); + if (sel == NULL) + { + doError(l, "First argument to observer:center must be an object"); + } + double travelTime = safeGetNumber(l, 3, WrongType, "Second arg to observer:center must be a number", 5.0); + + o->centerSelection(*sel, travelTime); + + return 0; +} + +static int observer_centerorbit(lua_State* l) +{ + checkArgs(l, 2, 3, "Expected one or two arguments for to observer:center"); + + Observer* o = this_observer(l); + Selection* sel = to_object(l, 2); + if (sel == NULL) + { + doError(l, "First argument to observer:centerorbit must be an object"); + } + double travelTime = safeGetNumber(l, 3, WrongType, "Second arg to observer:centerorbit must be a number", 5.0); + + o->centerSelectionCO(*sel, travelTime); + + return 0; +} + +static int observer_cancelgoto(lua_State* l) +{ + checkArgs(l, 1, 1, "Expected no arguments to observer:cancelgoto"); + + Observer* o = this_observer(l); + o->cancelMotion(); + + return 0; +} + +static int observer_follow(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected for observer:follow"); + + Observer* o = this_observer(l); + Selection* sel = to_object(l, 2); + if (sel == NULL) + { + doError(l, "First argument to observer:follow must be an object"); + } + o->follow(*sel); + + return 0; +} + +static int observer_synchronous(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected for observer:synchronous"); + + Observer* o = this_observer(l); + Selection* sel = to_object(l, 2); + if (sel == NULL) + { + doError(l, "First argument to observer:synchronous must be an object"); + } + o->geosynchronousFollow(*sel); + + return 0; +} + +static int observer_lock(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected for observer:lock"); + + Observer* o = this_observer(l); + Selection* sel = to_object(l, 2); + if (sel == NULL) + { + doError(l, "First argument to observer:phaseLock must be an object"); + } + o->phaseLock(*sel); + + return 0; +} + +static int observer_chase(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected for observer:chase"); + + Observer* o = this_observer(l); + Selection* sel = to_object(l, 2); + if (sel == NULL) + { + doError(l, "First argument to observer:chase must be an object"); + } + o->chase(*sel); + + return 0; +} + +static int observer_track(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected for observer:track"); + + Observer* o = this_observer(l); + + // If the argument is nil, clear the tracked object + if (lua_isnil(l, 2)) + { + o->setTrackedObject(Selection()); + } + else + { + // Otherwise, turn on tracking and set the tracked object + Selection* sel = to_object(l, 2); + if (sel == NULL) + { + doError(l, "First argument to observer:center must be an object"); + } + o->setTrackedObject(*sel); + } + + return 0; +} + +static int observer_gettrackedobject(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected to observer:gettrackedobject"); + + Observer* o = this_observer(l); + object_new(l, o->getTrackedObject()); + + return 1; +} + +// Return true if the observer is still moving as a result of a goto, center, +// or similar command. +static int observer_travelling(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected to observer:travelling"); + + Observer* o = this_observer(l); + if (o->getMode() == Observer::Travelling) + lua_pushboolean(l, 1); + else + lua_pushboolean(l, 0); + + return 1; +} + +// Return the observer's current time as a Julian day number +static int observer_gettime(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected to observer:gettime"); + + Observer* o = this_observer(l); + lua_pushnumber(l, o->getTime()); + + return 1; +} + +// Return the observer's current position +static int observer_getposition(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected to observer:getposition"); + + Observer* o = this_observer(l); + position_new(l, o->getPosition()); + + return 1; +} + +static int observer_getsurface(lua_State* l) +{ + checkArgs(l, 1, 1, "One argument expected to observer:getsurface()"); + + Observer* obs = this_observer(l); + lua_pushstring(l, obs->getDisplayedSurface().c_str()); + + return 1; +} + +static int observer_setsurface(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected to observer:setsurface()"); + + Observer* obs = this_observer(l); + const char* s = lua_tostring(l, 2); + + if (s == NULL) + obs->setDisplayedSurface(""); + else + obs->setDisplayedSurface(s); + + return 0; +} + +static int observer_getframe(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for observer:getframe()"); + + Observer* obs = this_observer(l); + + ObserverFrame* frame = obs->getFrame(); + frame_new(l, *frame); + return 1; +} + +static int observer_setframe(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument required for observer:setframe()"); + + Observer* obs = this_observer(l); + + ObserverFrame* frame; + frame = to_frame(l, 2); + if (frame != NULL) + { + obs->setFrame(*frame); + } + else + { + doError(l, "Argument to observer:setframe must be a frame"); + } + return 0; +} + +static int observer_setspeed(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument required for observer:setspeed()"); + + Observer* obs = this_observer(l); + + double speed = safeGetNumber(l, 2, AllErrors, "First argument to observer:setspeed must be a number"); + obs->setTargetSpeed((float)speed); + return 0; +} + +static int observer_getspeed(lua_State* l) +{ + checkArgs(l, 1, 1, "No argument expected for observer:getspeed()"); + + Observer* obs = this_observer(l); + + lua_pushnumber(l, (lua_Number)obs->getTargetSpeed()); + return 1; +} + +static int observer_setfov(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected to observer:setfov()"); + + Observer* obs = this_observer(l); + double fov = safeGetNumber(l, 2, AllErrors, "Argument to observer:setfov() must be a number"); + if ((fov >= degToRad(0.001f)) && (fov <= degToRad(120.0f))) + { + obs->setFOV((float) fov); + getAppCore(l, AllErrors)->setZoomFromFOV(); + } + return 0; +} + +static int observer_getfov(lua_State* l) +{ + checkArgs(l, 1, 1, "No argument expected to observer:getfov()"); + + Observer* obs = this_observer(l); + lua_pushnumber(l, obs->getFOV()); + return 1; +} + +static int observer_splitview(lua_State* l) +{ + checkArgs(l, 2, 3, "One or two arguments expected for observer:splitview()"); + + Observer* obs = this_observer(l); + CelestiaCore* appCore = getAppCore(l, AllErrors); + const char* splitType = safeGetString(l, 2, AllErrors, "First argument to observer:splitview() must be a string"); + View::Type type = (compareIgnoringCase(splitType, "h") == 0) ? View::HorizontalSplit : View::VerticalSplit; + double splitPos = safeGetNumber(l, 3, WrongType, "Number expected as argument to observer:splitview()", 0.5); + if (splitPos < 0.1) + splitPos = 0.1; + if (splitPos > 0.9) + splitPos = 0.9; + View* view = getViewByObserver(appCore, obs); + appCore->splitView(type, view, (float)splitPos); + return 0; +} + +static int observer_deleteview(lua_State* l) +{ + checkArgs(l, 1, 1, "No argument expected for observer:deleteview()"); + + Observer* obs = this_observer(l); + CelestiaCore* appCore = getAppCore(l, AllErrors); + View* view = getViewByObserver(appCore, obs); + appCore->deleteView(view); + return 0; +} + +static int observer_singleview(lua_State* l) +{ + checkArgs(l, 1, 1, "No argument expected for observer:singleview()"); + + Observer* obs = this_observer(l); + CelestiaCore* appCore = getAppCore(l, AllErrors); + View* view = getViewByObserver(appCore, obs); + appCore->singleView(view); + return 0; +} + +static int observer_equal(lua_State* l) +{ + checkArgs(l, 2, 2, "Wrong number of arguments for comparison!"); + + Observer* o1 = this_observer(l); + Observer* o2 = to_observer(l, 2); + + lua_pushboolean(l, (o1 == o2)); + return 1; +} + +static int observer_setlocationflags(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected for observer:setlocationflags()"); + Observer* obs = this_observer(l); + if (!lua_istable(l, 2)) + { + doError(l, "Argument to observer:setlocationflags() must be a table"); + } + + lua_pushnil(l); + int locationFlags = obs->getLocationFilter(); + while (lua_next(l, -2) != 0) + { + string key; + bool value = false; + if (lua_isstring(l, -2)) + { + key = lua_tostring(l, -2); + } + else + { + doError(l, "Keys in table-argument to observer:setlocationflags() must be strings"); + } + if (lua_isboolean(l, -1)) + { + value = lua_toboolean(l, -1) != 0; + } + else + { + doError(l, "Values in table-argument to observer:setlocationflags() must be boolean"); + } + if (LocationFlagMap.count(key) == 0) + { + cerr << "Unknown key: " << key << "\n"; + } + else + { + int flag = LocationFlagMap[key]; + if (value) + { + locationFlags |= flag; + } + else + { + locationFlags &= ~flag; + } + } + lua_pop(l,1); + } + obs->setLocationFilter(locationFlags); + return 0; +} + +static int observer_getlocationflags(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for observer:getlocationflags()"); + Observer* obs = this_observer(l); + lua_newtable(l); + FlagMap::const_iterator it = LocationFlagMap.begin(); + const int locationFlags = obs->getLocationFilter(); + while (it != LocationFlagMap.end()) + { + string key = it->first; + lua_pushstring(l, key.c_str()); + lua_pushboolean(l, (it->second & locationFlags) != 0); + lua_settable(l,-3); + it++; + } + return 1; +} + +static void CreateObserverMetaTable(lua_State* l) +{ + CreateClassMetatable(l, _Observer); + + RegisterMethod(l, "__tostring", observer_tostring); + RegisterMethod(l, "isvalid", observer_isvalid); + RegisterMethod(l, "goto", observer_goto); + RegisterMethod(l, "gotolonglat", observer_gotolonglat); + RegisterMethod(l, "gotolocation", observer_gotolocation); + RegisterMethod(l, "gotodistance", observer_gotodistance); + RegisterMethod(l, "gotosurface", observer_gotosurface); + RegisterMethod(l, "cancelgoto", observer_cancelgoto); + RegisterMethod(l, "setposition", observer_setposition); + RegisterMethod(l, "lookat", observer_lookat); + RegisterMethod(l, "setorientation", observer_setorientation); + RegisterMethod(l, "getorientation", observer_getorientation); + RegisterMethod(l, "getspeed", observer_getspeed); + RegisterMethod(l, "setspeed", observer_setspeed); + RegisterMethod(l, "getfov", observer_getfov); + RegisterMethod(l, "setfov", observer_setfov); + RegisterMethod(l, "rotate", observer_rotate); + RegisterMethod(l, "center", observer_center); + RegisterMethod(l, "centerorbit", observer_centerorbit); + RegisterMethod(l, "follow", observer_follow); + RegisterMethod(l, "synchronous", observer_synchronous); + RegisterMethod(l, "chase", observer_chase); + RegisterMethod(l, "lock", observer_lock); + RegisterMethod(l, "track", observer_track); + RegisterMethod(l, "gettrackedobject", observer_gettrackedobject); + RegisterMethod(l, "travelling", observer_travelling); + RegisterMethod(l, "getframe", observer_getframe); + RegisterMethod(l, "setframe", observer_setframe); + RegisterMethod(l, "gettime", observer_gettime); + RegisterMethod(l, "getposition", observer_getposition); + RegisterMethod(l, "getsurface", observer_getsurface); + RegisterMethod(l, "setsurface", observer_setsurface); + RegisterMethod(l, "splitview", observer_splitview); + RegisterMethod(l, "deleteview", observer_deleteview); + RegisterMethod(l, "singleview", observer_singleview); + RegisterMethod(l, "getlocationflags", observer_getlocationflags); + RegisterMethod(l, "setlocationflags", observer_setlocationflags); + RegisterMethod(l, "__eq", observer_equal); + + lua_pop(l, 1); // remove metatable from stack +} + + +// ==================== Celscript-object ==================== + +// create a CelScriptWrapper from a string: +static int celscript_from_string(lua_State* l, string& script_text) +{ + istringstream scriptfile(script_text); + + CelestiaCore* appCore = getAppCore(l, AllErrors); + CelScriptWrapper* celscript = new CelScriptWrapper(*appCore, scriptfile); + if (celscript->getErrorMessage() != "") + { + string error = celscript->getErrorMessage(); + delete celscript; + doError(l, error.c_str()); + } + else + { + CelScriptWrapper** ud = reinterpret_cast(lua_newuserdata(l, sizeof(CelScriptWrapper*))); + *ud = celscript; + SetClass(l, _CelScript); + } + + return 1; +} + +static CelScriptWrapper* this_celscript(lua_State* l) +{ + CelScriptWrapper** script = static_cast(CheckUserData(l, 1, _CelScript)); + if (script == NULL) + { + doError(l, "Bad CEL-script object!"); + } + return *script; +} + +static int celscript_tostring(lua_State* l) +{ + lua_pushstring(l, "[Celscript]"); + + return 1; +} + +static int celscript_tick(lua_State* l) +{ + CelScriptWrapper* script = this_celscript(l); + LuaState* stateObject = getLuaStateObject(l); + double t = stateObject->getTime(); + lua_pushboolean(l, !(script->tick(t)) ); + return 1; +} + +static int celscript_gc(lua_State* l) +{ + CelScriptWrapper* script = this_celscript(l); + delete script; + return 0; +} + + +static void CreateCelscriptMetaTable(lua_State* l) +{ + CreateClassMetatable(l, _CelScript); + + RegisterMethod(l, "__tostring", celscript_tostring); + RegisterMethod(l, "tick", celscript_tick); + RegisterMethod(l, "__gc", celscript_gc); + + lua_pop(l, 1); // remove metatable from stack +} + + +// ==================== Celestia-object ==================== +static int celestia_new(lua_State* l, CelestiaCore* appCore) +{ + CelestiaCore** ud = reinterpret_cast(lua_newuserdata(l, sizeof(CelestiaCore*))); + *ud = appCore; + + SetClass(l, _Celestia); + + return 1; +} + +static CelestiaCore* to_celestia(lua_State* l, int index) +{ + CelestiaCore** appCore = static_cast(CheckUserData(l, index, _Celestia)); + if (appCore == NULL) + return NULL; + else + return *appCore; +} + +static CelestiaCore* this_celestia(lua_State* l) +{ + CelestiaCore* appCore = to_celestia(l, 1); + if (appCore == NULL) + { + doError(l, "Bad celestia object!"); + } + + return appCore; +} + + +static int celestia_flash(lua_State* l) +{ + checkArgs(l, 2, 3, "One or two arguments expected to function celestia:flash"); + + CelestiaCore* appCore = this_celestia(l); + const char* s = safeGetString(l, 2, AllErrors, "First argument to celestia:flash must be a string"); + double duration = safeGetNumber(l, 3, WrongType, "Second argument to celestia:flash must be a number", 1.5); + if (duration < 0.0) + { + duration = 1.5; + } + + appCore->flash(s, duration); + + return 0; +} + +static int celestia_print(lua_State* l) +{ + checkArgs(l, 2, 7, "One to six arguments expected to function celestia:print"); + + CelestiaCore* appCore = this_celestia(l); + const char* s = safeGetString(l, 2, AllErrors, "First argument to celestia:print must be a string"); + double duration = safeGetNumber(l, 3, WrongType, "Second argument to celestia:print must be a number", 1.5); + int horig = (int)safeGetNumber(l, 4, WrongType, "Third argument to celestia:print must be a number", -1.0); + int vorig = (int)safeGetNumber(l, 5, WrongType, "Fourth argument to celestia:print must be a number", -1.0); + int hoff = (int)safeGetNumber(l, 6, WrongType, "Fifth argument to celestia:print must be a number", 0.0); + int voff = (int)safeGetNumber(l, 7, WrongType, "Sixth argument to celestia:print must be a number", 5.0); + + if (duration < 0.0) + { + duration = 1.5; + } + + appCore->showText(s, horig, vorig, hoff, voff, duration); + + return 0; +} + +static int celestia_gettextwidth(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected to function celestia:gettextwidth"); + + CelestiaCore* appCore = this_celestia(l); + const char* s = safeGetString(l, 2, AllErrors, "First argument to celestia:gettextwidth must be a string"); + + lua_pushnumber(l, appCore->getTextWidth(s)); + + return 1; +} + +static int celestia_getaltazimuthmode(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for celestia:getaltazimuthmode()"); + + CelestiaCore* appCore = this_celestia(l); + lua_pushboolean(l, appCore->getAltAzimuthMode()); + + return 1; +} + +static int celestia_setaltazimuthmode(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected to function celestia:setaltazimuthmode"); + bool enable = false; + if (lua_isboolean(l, -1)) + { + enable = lua_toboolean(l, -1) != 0; + } + else + { + doError(l, "Argument for celestia:setaltazimuthmode must be a boolean"); + } + + CelestiaCore* appCore = this_celestia(l); + appCore->setAltAzimuthMode(enable); + lua_pop(l, 1); + + return 0; +} + +static int celestia_show(lua_State* l) +{ + checkArgs(l, 1, 1000, "Wrong number of arguments to celestia:show"); + CelestiaCore* appCore = this_celestia(l); + + int argc = lua_gettop(l); + int flags = 0; + for (int i = 2; i <= argc; i++) + { + string renderFlag = safeGetString(l, i, AllErrors, "Arguments to celestia:show() must be strings"); + if (renderFlag == "lightdelay") + appCore->setLightDelayActive(true); + else + if (RenderFlagMap.count(renderFlag) > 0) + flags |= RenderFlagMap[renderFlag]; + } + + Renderer* r = appCore->getRenderer(); + r->setRenderFlags(r->getRenderFlags() | flags); + appCore->notifyWatchers(CelestiaCore::RenderFlagsChanged); + + return 0; +} + +static int celestia_hide(lua_State* l) +{ + checkArgs(l, 1, 1000, "Wrong number of arguments to celestia:hide"); + CelestiaCore* appCore = this_celestia(l); + + int argc = lua_gettop(l); + int flags = 0; + for (int i = 2; i <= argc; i++) + { + string renderFlag = safeGetString(l, i, AllErrors, "Arguments to celestia:hide() must be strings"); + if (renderFlag == "lightdelay") + appCore->setLightDelayActive(false); + else + if (RenderFlagMap.count(renderFlag) > 0) + flags |= RenderFlagMap[renderFlag]; + } + + Renderer* r = appCore->getRenderer(); + r->setRenderFlags(r->getRenderFlags() & ~flags); + appCore->notifyWatchers(CelestiaCore::RenderFlagsChanged); + + return 0; +} + +static int celestia_setrenderflags(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected for celestia:setrenderflags()"); + CelestiaCore* appCore = this_celestia(l); + if (!lua_istable(l, 2)) + { + doError(l, "Argument to celestia:setrenderflags() must be a table"); + } + + int renderFlags = appCore->getRenderer()->getRenderFlags(); + lua_pushnil(l); + while (lua_next(l, -2) != 0) + { + string key; + bool value = false; + if (lua_isstring(l, -2)) + { + key = lua_tostring(l, -2); + } + else + { + doError(l, "Keys in table-argument to celestia:setrenderflags() must be strings"); + } + if (lua_isboolean(l, -1)) + { + value = lua_toboolean(l, -1) != 0; + } + else + { + doError(l, "Values in table-argument to celestia:setrenderflags() must be boolean"); + } + if (key == "lightdelay") + { + appCore->setLightDelayActive(value); + } + else if (RenderFlagMap.count(key) > 0) + { + int flag = RenderFlagMap[key]; + if (value) + { + renderFlags |= flag; + } + else + { + renderFlags &= ~flag; + } + } + else + { + cerr << "Unknown key: " << key << "\n"; + } + lua_pop(l,1); + } + appCore->getRenderer()->setRenderFlags(renderFlags); + appCore->notifyWatchers(CelestiaCore::RenderFlagsChanged); + + return 0; +} + +static int celestia_getrenderflags(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for celestia:getrenderflags()"); + CelestiaCore* appCore = this_celestia(l); + lua_newtable(l); + FlagMap::const_iterator it = RenderFlagMap.begin(); + const int renderFlags = appCore->getRenderer()->getRenderFlags(); + while (it != RenderFlagMap.end()) + { + string key = it->first; + lua_pushstring(l, key.c_str()); + lua_pushboolean(l, (it->second & renderFlags) != 0); + lua_settable(l,-3); + it++; + } + lua_pushstring(l, "lightdelay"); + lua_pushboolean(l, appCore->getLightDelayActive()); + lua_settable(l, -3); + return 1; +} + +int celestia_getscreendimension(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for celestia:getscreendimension()"); + // error checking only: + this_celestia(l); + // Get the dimensions of the current viewport + int viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + lua_pushnumber(l, viewport[2]-viewport[0]); + lua_pushnumber(l, viewport[3]-viewport[1]); + return 2; +} + +static int celestia_showlabel(lua_State* l) +{ + checkArgs(l, 1, 1000, "Bad method call!"); + CelestiaCore* appCore = this_celestia(l); + + int argc = lua_gettop(l); + int flags = 0; + for (int i = 2; i <= argc; i++) + { + string labelFlag = safeGetString(l, i, AllErrors, "Arguments to celestia:showlabel() must be strings"); + if (LabelFlagMap.count(labelFlag) > 0) + flags |= LabelFlagMap[labelFlag]; + } + + Renderer* r = appCore->getRenderer(); + r->setLabelMode(r->getLabelMode() | flags); + appCore->notifyWatchers(CelestiaCore::LabelFlagsChanged); + + return 0; +} + +static int celestia_hidelabel(lua_State* l) +{ + checkArgs(l, 1, 1000, "Invalid number of arguments in celestia:hidelabel"); + CelestiaCore* appCore = this_celestia(l); + + int argc = lua_gettop(l); + int flags = 0; + for (int i = 2; i <= argc; i++) + { + string labelFlag = safeGetString(l, i, AllErrors, "Arguments to celestia:hidelabel() must be strings"); + if (LabelFlagMap.count(labelFlag) > 0) + flags |= LabelFlagMap[labelFlag]; + } + + Renderer* r = appCore->getRenderer(); + r->setLabelMode(r->getLabelMode() & ~flags); + appCore->notifyWatchers(CelestiaCore::LabelFlagsChanged); + + return 0; +} + +static int celestia_setlabelflags(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected for celestia:setlabelflags()"); + CelestiaCore* appCore = this_celestia(l); + if (!lua_istable(l, 2)) + { + doError(l, "Argument to celestia:setlabelflags() must be a table"); + } + + int labelFlags = appCore->getRenderer()->getLabelMode(); + lua_pushnil(l); + while (lua_next(l, -2) != 0) + { + string key; + bool value = false; + if (lua_isstring(l, -2)) + { + key = lua_tostring(l, -2); + } + else + { + doError(l, "Keys in table-argument to celestia:setlabelflags() must be strings"); + } + if (lua_isboolean(l, -1)) + { + value = lua_toboolean(l, -1) != 0; + } + else + { + doError(l, "Values in table-argument to celestia:setlabelflags() must be boolean"); + } + if (LabelFlagMap.count(key) == 0) + { + cerr << "Unknown key: " << key << "\n"; + } + else + { + int flag = LabelFlagMap[key]; + if (value) + { + labelFlags |= flag; + } + else + { + labelFlags &= ~flag; + } + } + lua_pop(l,1); + } + appCore->getRenderer()->setLabelMode(labelFlags); + appCore->notifyWatchers(CelestiaCore::LabelFlagsChanged); + + return 0; +} + +static int celestia_getlabelflags(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for celestia:getlabelflags()"); + CelestiaCore* appCore = this_celestia(l); + lua_newtable(l); + FlagMap::const_iterator it = LabelFlagMap.begin(); + const int labelFlags = appCore->getRenderer()->getLabelMode(); + while (it != LabelFlagMap.end()) + { + string key = it->first; + lua_pushstring(l, key.c_str()); + lua_pushboolean(l, (it->second & labelFlags) != 0); + lua_settable(l,-3); + it++; + } + return 1; +} + +static int celestia_setorbitflags(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected for celestia:setorbitflags()"); + CelestiaCore* appCore = this_celestia(l); + if (!lua_istable(l, 2)) + { + doError(l, "Argument to celestia:setorbitflags() must be a table"); + } + + int orbitFlags = appCore->getRenderer()->getOrbitMask(); + lua_pushnil(l); + while (lua_next(l, -2) != 0) + { + string key; + bool value = false; + if (lua_isstring(l, -2)) + { + key = lua_tostring(l, -2); + } + else + { + doError(l, "Keys in table-argument to celestia:setorbitflags() must be strings"); + } + if (lua_isboolean(l, -1)) + { + value = lua_toboolean(l, -1) != 0; + } + else + { + doError(l, "Values in table-argument to celestia:setorbitflags() must be boolean"); + } + if (BodyTypeMap.count(key) == 0) + { + cerr << "Unknown key: " << key << "\n"; + } + else + { + int flag = BodyTypeMap[key]; + if (value) + { + orbitFlags |= flag; + } + else + { + orbitFlags &= ~flag; + } + } + lua_pop(l,1); + } + appCore->getRenderer()->setOrbitMask(orbitFlags); + return 0; +} + +static int celestia_getorbitflags(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for celestia:getorbitflags()"); + CelestiaCore* appCore = this_celestia(l); + lua_newtable(l); + FlagMap::const_iterator it = BodyTypeMap.begin(); + const int orbitFlags = appCore->getRenderer()->getOrbitMask(); + while (it != BodyTypeMap.end()) + { + string key = it->first; + lua_pushstring(l, key.c_str()); + lua_pushboolean(l, (it->second & orbitFlags) != 0); + lua_settable(l,-3); + it++; + } + return 1; +} + +static int celestia_setoverlayelements(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected for celestia:setoverlayelements()"); + CelestiaCore* appCore = this_celestia(l); + if (!lua_istable(l, 2)) + { + doError(l, "Argument to celestia:setoverlayelements() must be a table"); + } + + int overlayElements = appCore->getOverlayElements(); + lua_pushnil(l); + while (lua_next(l, -2) != 0) + { + string key; + bool value = false; + if (lua_isstring(l, -2)) + { + key = lua_tostring(l, -2); + } + else + { + doError(l, "Keys in table-argument to celestia:setoverlayelements() must be strings"); + } + if (lua_isboolean(l, -1)) + { + value = lua_toboolean(l, -1) != 0; + } + else + { + doError(l, "Values in table-argument to celestia:setoverlayelements() must be boolean"); + } + if (OverlayElementMap.count(key) == 0) + { + cerr << "Unknown key: " << key << "\n"; + } + else + { + int element = OverlayElementMap[key]; + if (value) + { + overlayElements |= element; + } + else + { + overlayElements &= ~element; + } + } + lua_pop(l,1); + } + appCore->setOverlayElements(overlayElements); + return 0; +} + +static int celestia_getoverlayelements(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for celestia:getoverlayelements()"); + CelestiaCore* appCore = this_celestia(l); + lua_newtable(l); + FlagMap::const_iterator it = OverlayElementMap.begin(); + const int overlayElements = appCore->getOverlayElements(); + while (it != OverlayElementMap.end()) + { + string key = it->first; + lua_pushstring(l, key.c_str()); + lua_pushboolean(l, (it->second & overlayElements) != 0); + lua_settable(l,-3); + it++; + } + return 1; +} + +static int celestia_setlabelcolor(lua_State* l) +{ + checkArgs(l, 5, 5, "Four arguments expected for celestia:setlabelcolor()"); + if (!lua_isstring(l, 2)) + { + doError(l, "First argument to celestia:setlabelstyle() must be a string"); + } + + Color* color = NULL; + string key; + key = lua_tostring(l, 2); + if (LabelColorMap.count(key) == 0) + { + cerr << "Unknown label style: " << key << "\n"; + } + else + { + color = LabelColorMap[key]; + } + + double red = safeGetNumber(l, 3, AllErrors, "setlabelcolor: color values must be numbers"); + double green = safeGetNumber(l, 4, AllErrors, "setlabelcolor: color values must be numbers"); + double blue = safeGetNumber(l, 5, AllErrors, "setlabelcolor: color values must be numbers"); + + // opacity currently not settable + double opacity = 1.0; + + if (color != NULL) + { + *color = Color((float) red, (float) green, (float) blue, (float) opacity); + } + + return 1; +} + + +static int celestia_setlinecolor(lua_State* l) +{ + checkArgs(l, 5, 5, "Four arguments expected for celestia:setlinecolor()"); + if (!lua_isstring(l, 2)) + { + doError(l, "First argument to celestia:setlinecolor() must be a string"); + } + + Color* color = NULL; + string key; + key = lua_tostring(l, 2); + if (LineColorMap.count(key) == 0) + { + cerr << "Unknown line style: " << key << "\n"; + } + else + { + color = LineColorMap[key]; + } + + double red = safeGetNumber(l, 3, AllErrors, "setlinecolor: color values must be numbers"); + double green = safeGetNumber(l, 4, AllErrors, "setlinecolor: color values must be numbers"); + double blue = safeGetNumber(l, 5, AllErrors, "setlinecolor: color values must be numbers"); + + // opacity currently not settable + double opacity = 1.0; + + if (color != NULL) + { + *color = Color((float) red, (float) green, (float) blue, (float) opacity); + } + + return 1; +} + + +static int celestia_setfaintestvisible(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected for celestia:setfaintestvisible()"); + CelestiaCore* appCore = this_celestia(l); + float faintest = (float)safeGetNumber(l, 2, AllErrors, "Argument to celestia:setfaintestvisible() must be a number"); + if ((appCore->getRenderer()->getRenderFlags() & Renderer::ShowAutoMag) == 0) + { + faintest = min(15.0f, max(1.0f, faintest)); + appCore->setFaintest(faintest); + appCore->notifyWatchers(CelestiaCore::FaintestChanged); + } + else + { + faintest = min(12.0f, max(6.0f, faintest)); + appCore->getRenderer()->setFaintestAM45deg(faintest); + appCore->setFaintestAutoMag(); + } + return 0; +} + +static int celestia_getfaintestvisible(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for celestia:getfaintestvisible()"); + CelestiaCore* appCore = this_celestia(l); + if ((appCore->getRenderer()->getRenderFlags() & Renderer::ShowAutoMag) == 0) + { + lua_pushnumber(l, appCore->getSimulation()->getFaintestVisible()); + } + else + { + lua_pushnumber(l, appCore->getRenderer()->getFaintestAM45deg()); + } + return 1; +} + +static int celestia_setgalaxylightgain(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected for celestia:setgalaxylightgain()"); + float lightgain = (float)safeGetNumber(l, 2, AllErrors, "Argument to celestia:setgalaxylightgain() must be a number"); + lightgain = min(1.0f, max(0.0f, lightgain)); + Galaxy::setLightGain(lightgain); + + return 0; +} + +static int celestia_getgalaxylightgain(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for celestia:getgalaxylightgain()"); + lua_pushnumber(l, Galaxy::getLightGain()); + + return 1; +} + +static int celestia_setminfeaturesize(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected for celestia:setminfeaturesize()"); + CelestiaCore* appCore = this_celestia(l); + float minFeatureSize = (float)safeGetNumber(l, 2, AllErrors, "Argument to celestia:setminfeaturesize() must be a number"); + minFeatureSize = max(0.0f, minFeatureSize); + appCore->getRenderer()->setMinimumFeatureSize(minFeatureSize); + return 0; +} + +static int celestia_getminfeaturesize(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for celestia:getminfeaturesize()"); + CelestiaCore* appCore = this_celestia(l); + lua_pushnumber(l, appCore->getRenderer()->getMinimumFeatureSize()); + return 1; +} + +static int celestia_getobserver(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for celestia:getobserver()"); + + CelestiaCore* appCore = this_celestia(l); + Observer* o = appCore->getSimulation()->getActiveObserver(); + if (o == NULL) + lua_pushnil(l); + else + observer_new(l, o); + + return 1; +} + +static int celestia_getobservers(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for celestia:getobservers()"); + CelestiaCore* appCore = this_celestia(l); + + vector observer_list; + getObservers(appCore, observer_list); + lua_newtable(l); + for (unsigned int i = 0; i < observer_list.size(); i++) + { + observer_new(l, observer_list[i]); + lua_rawseti(l, -2, i + 1); + } + + return 1; +} + +static int celestia_getselection(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected to celestia:getselection()"); + CelestiaCore* appCore = this_celestia(l); + Selection sel = appCore->getSimulation()->getSelection(); + object_new(l, sel); + + return 1; +} + +static int celestia_find(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected for function celestia:find()"); + if (!lua_isstring(l, 2)) + { + doError(l, "Argument to find must be a string"); + } + + CelestiaCore* appCore = this_celestia(l); + Simulation* sim = appCore->getSimulation(); + // Should use universe not simulation for finding objects + Selection sel = sim->findObjectFromPath(lua_tostring(l, 2)); + object_new(l, sel); + + return 1; +} + +static int celestia_select(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected for celestia:select()"); + CelestiaCore* appCore = this_celestia(l); + + Simulation* sim = appCore->getSimulation(); + Selection* sel = to_object(l, 2); + + // If the argument is an object, set the selection; if it's anything else + // clear the selection. + if (sel != NULL) + sim->setSelection(*sel); + else + sim->setSelection(Selection()); + + return 0; +} + +static int celestia_mark(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected to function celestia:mark"); + + CelestiaCore* appCore = this_celestia(l); + Simulation* sim = appCore->getSimulation(); + Selection* sel = to_object(l, 2); + + if (sel != NULL) + { + sim->getUniverse()->markObject(*sel, 10.0f, + Color(0.0f, 1.0f, 0.0f), Marker::Diamond, 1, ""); + } + else + { + doError(l, "Argument to celestia:mark must be an object"); + } + + return 0; +} + +static int celestia_unmark(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected to function celestia:unmark"); + + CelestiaCore* appCore = this_celestia(l); + Simulation* sim = appCore->getSimulation(); + Selection* sel = to_object(l, 2); + + if (sel != NULL) + { + sim->getUniverse()->unmarkObject(*sel, 1); + } + else + { + doError(l, "Argument to celestia:unmark must be an object"); + } + + return 0; +} + +static int celestia_gettime(lua_State* l) +{ + checkArgs(l, 1, 1, "No argument expected to function celestia:gettime"); + + CelestiaCore* appCore = this_celestia(l); + Simulation* sim = appCore->getSimulation(); + lua_pushnumber(l, sim->getTime()); + + return 1; +} + +static int celestia_gettimescale(lua_State* l) +{ + checkArgs(l, 1, 1, "No argument expected to function celestia:gettimescale"); + + CelestiaCore* appCore = this_celestia(l); + lua_pushnumber(l, appCore->getSimulation()->getTimeScale()); + + return 1; +} + +static int celestia_settime(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected to function celestia:settime"); + + CelestiaCore* appCore = this_celestia(l); + double t = safeGetNumber(l, 2, AllErrors, "First arg to celestia:settime must be a number"); + appCore->getSimulation()->setTime(t); + + return 0; +} + +static int celestia_settimescale(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected to function celestia:settimescale"); + + CelestiaCore* appCore = this_celestia(l); + double t = safeGetNumber(l, 2, AllErrors, "Second arg to celestia:settimescale must be a number"); + appCore->getSimulation()->setTimeScale(t); + + return 0; +} + +static int celestia_tojulianday(lua_State* l) +{ + checkArgs(l, 2, 7, "Wrong number of arguments to function celestia:tojulianday"); + + // for error checking only: + this_celestia(l); + + int year = (int)safeGetNumber(l, 2, AllErrors, "First arg to celestia:tojulianday must be a number", 0.0); + int month = (int)safeGetNumber(l, 3, WrongType, "Second arg to celestia:tojulianday must be a number", 1.0); + int day = (int)safeGetNumber(l, 4, WrongType, "Third arg to celestia:tojulianday must be a number", 1.0); + int hour = (int)safeGetNumber(l, 5, WrongType, "Fourth arg to celestia:tojulianday must be a number", 0.0); + int minute = (int)safeGetNumber(l, 6, WrongType, "Fifth arg to celestia:tojulianday must be a number", 0.0); + double seconds = safeGetNumber(l, 7, WrongType, "Sixth arg to celestia:tojulianday must be a number", 0.0); + + astro::Date date(year, month, day); + date.hour = hour; + date.minute = minute; + date.seconds = seconds; + + double jd = (double) date; + + lua_pushnumber(l, jd); + + return 1; +} + +static int celestia_fromjulianday(lua_State* l) +{ + checkArgs(l, 2, 2, "Wrong number of arguments to function celestia:fromjulianday"); + + // for error checking only: + this_celestia(l); + + double jd = safeGetNumber(l, 2, AllErrors, "First arg to celestia:fromjulianday must be a number", 0.0); + astro::Date date(jd); + + lua_newtable(l); + setTable(l, "year", (double)date.year); + setTable(l, "month", (double)date.month); + setTable(l, "day", (double)date.day); + setTable(l, "hour", (double)date.hour); + setTable(l, "minute", (double)date.minute); + setTable(l, "seconds", date.seconds); + + return 1; +} + + +// Convert a UTC Julian date to a TDB Julian day +// TODO: also support a single table argument of the form output by +// celestia_tdbtoutc. +static int celestia_utctotdb(lua_State* l) +{ + checkArgs(l, 2, 7, "Wrong number of arguments to function celestia:utctotdb"); + + // for error checking only: + this_celestia(l); + + int year = (int) safeGetNumber(l, 2, AllErrors, "First arg to celestia:utctotdb must be a number", 0.0); + int month = (int) safeGetNumber(l, 3, WrongType, "Second arg to celestia:utctotdb must be a number", 1.0); + int day = (int) safeGetNumber(l, 4, WrongType, "Third arg to celestia:utctotdb must be a number", 1.0); + int hour = (int)safeGetNumber(l, 5, WrongType, "Fourth arg to celestia:utctotdb must be a number", 0.0); + int minute = (int)safeGetNumber(l, 6, WrongType, "Fifth arg to celestia:utctotdb must be a number", 0.0); + double seconds = safeGetNumber(l, 7, WrongType, "Sixth arg to celestia:utctotdb must be a number", 0.0); + + astro::Date date(year, month, day); + date.hour = hour; + date.minute = minute; + date.seconds = seconds; + + double jd = astro::UTCtoTDB(date); + + lua_pushnumber(l, jd); + + return 1; +} + + +// Convert a TDB Julian day to a UTC Julian date (table format) +static int celestia_tdbtoutc(lua_State* l) +{ + checkArgs(l, 2, 2, "Wrong number of arguments to function celestia:tdbtoutc"); + + // for error checking only: + this_celestia(l); + + double jd = safeGetNumber(l, 2, AllErrors, "First arg to celestia:tdbtoutc must be a number", 0.0); + astro::Date date = astro::TDBtoUTC(jd); + + lua_newtable(l); + setTable(l, "year", (double)date.year); + setTable(l, "month", (double)date.month); + setTable(l, "day", (double)date.day); + setTable(l, "hour", (double)date.hour); + setTable(l, "minute", (double)date.minute); + setTable(l, "seconds", date.seconds); + + return 1; +} + + +static int celestia_unmarkall(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected to function celestia:unmarkall"); + + CelestiaCore* appCore = this_celestia(l); + Simulation* sim = appCore->getSimulation(); + sim->getUniverse()->unmarkAll(); + + return 0; +} + +static int celestia_getstarcount(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected to function celestia:getstarcount"); + + CelestiaCore* appCore = this_celestia(l); + Universe* u = appCore->getSimulation()->getUniverse(); + lua_pushnumber(l, u->getStarCatalog()->size()); + + return 1; +} + + +// Stars iterator function; two upvalues expected +static int celestia_stars_iter(lua_State* l) +{ + CelestiaCore* appCore = to_celestia(l, lua_upvalueindex(1)); + if (appCore == NULL) + { + doError(l, "Bad celestia object!"); + return 0; + } + + uint32 i = (uint32) lua_tonumber(l, lua_upvalueindex(2)); + Universe* u = appCore->getSimulation()->getUniverse(); + + if (i < u->getStarCatalog()->size()) + { + // Increment the counter + lua_pushnumber(l, i + 1); + lua_replace(l, lua_upvalueindex(2)); + + Star* star = u->getStarCatalog()->getStar(i); + if (star == NULL) + lua_pushnil(l); + else + object_new(l, Selection(star)); + + return 1; + } + else + { + // Return nil when we've enumerated all the stars + return 0; + } +} + + +static int celestia_stars(lua_State* l) +{ + // Push a closure with two upvalues: the celestia object and a + // counter. + lua_pushvalue(l, 1); // Celestia object + lua_pushnumber(l, 0); // counter + lua_pushcclosure(l, celestia_stars_iter, 2); + + return 1; +} + + +static int celestia_getdsocount(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected to function celestia:getdsocount"); + + CelestiaCore* appCore = this_celestia(l); + Universe* u = appCore->getSimulation()->getUniverse(); + lua_pushnumber(l, u->getDSOCatalog()->size()); + + return 1; +} + + +// DSOs iterator function; two upvalues expected +static int celestia_dsos_iter(lua_State* l) +{ + CelestiaCore* appCore = to_celestia(l, lua_upvalueindex(1)); + if (appCore == NULL) + { + doError(l, "Bad celestia object!"); + return 0; + } + + uint32 i = (uint32) lua_tonumber(l, lua_upvalueindex(2)); + Universe* u = appCore->getSimulation()->getUniverse(); + + if (i < u->getDSOCatalog()->size()) + { + // Increment the counter + lua_pushnumber(l, i + 1); + lua_replace(l, lua_upvalueindex(2)); + + DeepSkyObject* dso = u->getDSOCatalog()->getDSO(i); + if (dso == NULL) + lua_pushnil(l); + else + object_new(l, Selection(dso)); + + return 1; + } + else + { + // Return nil when we've enumerated all the DSOs + return 0; + } +} + + +static int celestia_dsos(lua_State* l) +{ + // Push a closure with two upvalues: the celestia object and a + // counter. + lua_pushvalue(l, 1); // Celestia object + lua_pushnumber(l, 0); // counter + lua_pushcclosure(l, celestia_dsos_iter, 2); + + return 1; +} + +static int celestia_setambient(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected in celestia:setambient"); + CelestiaCore* appCore = this_celestia(l); + + Renderer* renderer = appCore->getRenderer(); + double ambientLightLevel = safeGetNumber(l, 2, AllErrors, "Argument to celestia:setambient must be a number"); + if (ambientLightLevel > 1.0) + ambientLightLevel = 1.0; + if (ambientLightLevel < 0.0) + ambientLightLevel = 0.0; + + if (renderer != NULL) + renderer->setAmbientLightLevel((float)ambientLightLevel); + appCore->notifyWatchers(CelestiaCore::AmbientLightChanged); + + return 0; +} + +static int celestia_getambient(lua_State* l) +{ + checkArgs(l, 1, 1, "No argument expected in celestia:setambient"); + CelestiaCore* appCore = this_celestia(l); + + Renderer* renderer = appCore->getRenderer(); + if (renderer == NULL) + { + doError(l, "Internal Error: renderer is NULL!"); + } + else + { + lua_pushnumber(l, renderer->getAmbientLightLevel()); + } + return 1; +} + +static int celestia_setminorbitsize(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected in celestia:setminorbitsize"); + CelestiaCore* appCore = this_celestia(l); + + double orbitSize = safeGetNumber(l, 2, AllErrors, "Argument to celestia:setminorbitsize() must be a number"); + Renderer* renderer = appCore->getRenderer(); + if (renderer == NULL) + { + doError(l, "Internal Error: renderer is NULL!"); + } + else + { + orbitSize = max(0.0, orbitSize); + renderer->setMinimumOrbitSize((float)orbitSize); + } + return 0; +} + +static int celestia_getminorbitsize(lua_State* l) +{ + checkArgs(l, 1, 1, "No argument expected in celestia:getminorbitsize"); + CelestiaCore* appCore = this_celestia(l); + + Renderer* renderer = appCore->getRenderer(); + if (renderer == NULL) + { + doError(l, "Internal Error: renderer is NULL!"); + } + else + { + lua_pushnumber(l, renderer->getMinimumOrbitSize()); + } + return 1; +} + +static int celestia_setstardistancelimit(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected in celestia:setstardistancelimit"); + CelestiaCore* appCore = this_celestia(l); + + double distanceLimit = safeGetNumber(l, 2, AllErrors, "Argument to celestia:setstardistancelimit() must be a number"); + Renderer* renderer = appCore->getRenderer(); + if (renderer == NULL) + { + doError(l, "Internal Error: renderer is NULL!"); + } + else + { + renderer->setDistanceLimit((float)distanceLimit); + } + return 0; +} + +static int celestia_getstardistancelimit(lua_State* l) +{ + checkArgs(l, 1, 1, "No argument expected in celestia:getstardistancelimit"); + CelestiaCore* appCore = this_celestia(l); + + Renderer* renderer = appCore->getRenderer(); + if (renderer == NULL) + { + doError(l, "Internal Error: renderer is NULL!"); + } + else + { + lua_pushnumber(l, renderer->getDistanceLimit()); + } + return 1; +} + +static int celestia_getstarstyle(lua_State* l) +{ + checkArgs(l, 1, 1, "No argument expected in celestia:getstarstyle"); + CelestiaCore* appCore = this_celestia(l); + + Renderer* renderer = appCore->getRenderer(); + if (renderer == NULL) + { + doError(l, "Internal Error: renderer is NULL!"); + } + else + { + Renderer::StarStyle starStyle = renderer->getStarStyle(); + switch (starStyle) + { + case Renderer::FuzzyPointStars: + lua_pushstring(l, "fuzzy"); break; + case Renderer::PointStars: + lua_pushstring(l, "point"); break; + case Renderer::ScaledDiscStars: + lua_pushstring(l, "disc"); break; + default: + lua_pushstring(l, "invalid starstyle"); + }; + } + return 1; +} + +static int celestia_setstarstyle(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected in celestia:setstarstyle"); + CelestiaCore* appCore = this_celestia(l); + + string starStyle = safeGetString(l, 2, AllErrors, "Argument to celestia:setstarstyle must be a string"); + Renderer* renderer = appCore->getRenderer(); + if (renderer == NULL) + { + doError(l, "Internal Error: renderer is NULL!"); + } + else + { + if (starStyle == "fuzzy") + { + renderer->setStarStyle(Renderer::FuzzyPointStars); + } + else if (starStyle == "point") + { + renderer->setStarStyle(Renderer::PointStars); + } + else if (starStyle == "disc") + { + renderer->setStarStyle(Renderer::ScaledDiscStars); + } + else + { + doError(l, "Invalid starstyle"); + } + appCore->notifyWatchers(CelestiaCore::RenderFlagsChanged); + + } + return 0; +} + +static int celestia_getstar(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected to function celestia:getstar"); + + CelestiaCore* appCore = this_celestia(l); + double starIndex = safeGetNumber(l, 2, AllErrors, "First arg to celestia:getstar must be a number"); + Universe* u = appCore->getSimulation()->getUniverse(); + Star* star = u->getStarCatalog()->getStar((uint32) starIndex); + if (star == NULL) + lua_pushnil(l); + else + object_new(l, Selection(star)); + + return 1; +} + +static int celestia_getdso(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected to function celestia:getdso"); + + CelestiaCore* appCore = this_celestia(l); + double dsoIndex = safeGetNumber(l, 2, AllErrors, "First arg to celestia:getdso must be a number"); + Universe* u = appCore->getSimulation()->getUniverse(); + DeepSkyObject* dso = u->getDSOCatalog()->getDSO((uint32) dsoIndex); + if (dso == NULL) + lua_pushnil(l); + else + object_new(l, Selection(dso)); + + return 1; +} + + +static int celestia_newvector(lua_State* l) +{ + checkArgs(l, 4, 4, "Expected 3 arguments for celestia:newvector"); + // for error checking only: + this_celestia(l); + double x = safeGetNumber(l, 2, AllErrors, "First arg to celestia:newvector must be a number"); + double y = safeGetNumber(l, 3, AllErrors, "Second arg to celestia:newvector must be a number"); + double z = safeGetNumber(l, 4, AllErrors, "Third arg to celestia:newvector must be a number"); + + vector_new(l, Vec3d(x,y,z)); + + return 1; +} + +static int celestia_newposition(lua_State* l) +{ + checkArgs(l, 4, 4, "Expected 3 arguments for celestia:newposition"); + // for error checking only: + this_celestia(l); + BigFix components[3]; + for (int i = 0; i < 3; i++) + { + if (lua_isnumber(l, i+2)) + { + double v = lua_tonumber(l, i+2); + components[i] = BigFix(v); + } + else if (lua_isstring(l, i+2)) + { + components[i] = BigFix(string(lua_tostring(l, i+2))); + } + else + { + doError(l, "Arguments to celestia:newposition must be either numbers or strings"); + } + } + + position_new(l, UniversalCoord(components[0], components[1], components[2])); + + return 1; +} + +static int celestia_newrotation(lua_State* l) +{ + checkArgs(l, 3, 5, "Need 2 or 4 arguments for celestia:newrotation"); + // for error checking only: + this_celestia(l); + + if (lua_gettop(l) > 3) + { + // if (lua_gettop == 4), safeGetNumber will catch the error + double w = safeGetNumber(l, 2, AllErrors, "arguments to celestia:newrotation must either be (vec, number) or four numbers"); + double x = safeGetNumber(l, 3, AllErrors, "arguments to celestia:newrotation must either be (vec, number) or four numbers"); + double y = safeGetNumber(l, 4, AllErrors, "arguments to celestia:newrotation must either be (vec, number) or four numbers"); + double z = safeGetNumber(l, 5, AllErrors, "arguments to celestia:newrotation must either be (vec, number) or four numbers"); + Quatd q(w, x, y, z); + rotation_new(l, q); + } + else + { + Vec3d* v = to_vector(l, 2); + if (v == NULL) + { + doError(l, "newrotation: first argument must be a vector"); + } + double angle = safeGetNumber(l, 3, AllErrors, "second argument to celestia:newrotation must be a number"); + Quatd q; + q.setAxisAngle(*v, angle); + rotation_new(l, q); + } + return 1; +} + +static int celestia_getscripttime(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for celestia:getscripttime"); + // for error checking only: + this_celestia(l); + + LuaState* luastate_ptr = getLuaStateObject(l); + lua_pushnumber(l, luastate_ptr->getTime()); + return 1; +} + +static int celestia_newframe(lua_State* l) +{ + checkArgs(l, 2, 4, "One to three arguments expected for function celestia:newframe"); + int argc = lua_gettop(l); + + // for error checking only: + this_celestia(l); + + const char* coordsysName = safeGetString(l, 2, AllErrors, "newframe: first argument must be a string"); + ObserverFrame::CoordinateSystem coordSys = parseCoordSys(coordsysName); + Selection* ref = NULL; + Selection* target = NULL; + + if (coordSys == ObserverFrame::Universal) + { + frame_new(l, ObserverFrame()); + } + else if (coordSys == ObserverFrame::PhaseLock) + { + if (argc >= 4) + { + ref = to_object(l, 3); + target = to_object(l, 4); + } + + if (ref == NULL || target == NULL) + { + doError(l, "newframe: two objects required for lock frame"); + } + + frame_new(l, ObserverFrame(coordSys, *ref, *target)); + } + else + { + if (argc >= 3) + ref = to_object(l, 3); + if (ref == NULL) + { + doError(l, "newframe: one object argument required for frame"); + } + + frame_new(l, ObserverFrame(coordSys, *ref)); + } + + return 1; +} + +static int celestia_requestkeyboard(lua_State* l) +{ + checkArgs(l, 2, 2, "Need one arguments for celestia:requestkeyboard"); + CelestiaCore* appCore = this_celestia(l); + + if (!lua_isboolean(l, 2)) + { + doError(l, "First argument for celestia:requestkeyboard must be a boolean"); + } + + int mode = appCore->getTextEnterMode(); + + if (lua_toboolean(l, 2)) + { + // Check for existence of charEntered: + lua_pushstring(l, KbdCallback); + lua_gettable(l, LUA_GLOBALSINDEX); + if (lua_isnil(l, -1)) + { + doError(l, "script requested keyboard, but did not provide callback"); + } + lua_remove(l, -1); + + mode = mode | CelestiaCore::KbPassToScript; + } + else + { + mode = mode & ~CelestiaCore::KbPassToScript; + } + appCore->setTextEnterMode(mode); + + return 0; +} + +static int celestia_registereventhandler(lua_State* l) +{ + checkArgs(l, 3, 3, "Two arguments required for celestia:registereventhandler"); + //CelestiaCore* appCore = this_celestia(l); + + if (!lua_isstring(l, 2)) + { + doError(l, "First argument for celestia:registereventhandler must be a string"); + } + + if (!lua_isfunction(l, 3) && !lua_isnil(l, 3)) + { + doError(l, "Second argument for celestia:registereventhandler must be a function or nil"); + } + + lua_pushstring(l, EventHandlers); + lua_gettable(l, LUA_REGISTRYINDEX); + if (lua_isnil(l, -1)) + { + // This should never happen--the table should be created when a new Celestia Lua + // state is initialized. + doError(l, "Event handler table not created"); + } + + lua_pushvalue(l, 2); + lua_pushvalue(l, 3); + + lua_settable(l, -3); + + return 0; +} + +static int celestia_geteventhandler(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected for celestia:registereventhandler"); + //CelestiaCore* appCore = this_celestia(l); + + if (!lua_isstring(l, 2)) + { + doError(l, "Argument to celestia:geteventhandler must be a string"); + } + + lua_pushstring(l, EventHandlers); + lua_gettable(l, LUA_REGISTRYINDEX); + if (lua_isnil(l, -1)) + { + // This should never happen--the table should be created when a new Celestia Lua + // state is initialized. + doError(l, "Event handler table not created"); + } + + lua_pushvalue(l, 2); + lua_gettable(l, -2); + + return 1; +} + +static int celestia_takescreenshot(lua_State* l) +{ + checkArgs(l, 1, 3, "Need 0 to 2 arguments for celestia:takescreenshot"); + CelestiaCore* appCore = this_celestia(l); + LuaState* luastate = getLuaStateObject(l); + // make sure we don't timeout because of taking a screenshot: + double timeToTimeout = luastate->timeout - luastate->getTime(); + + const char* filetype = safeGetString(l, 2, WrongType, "First argument to celestia:takescreenshot must be a string"); + if (filetype == NULL) + filetype = "png"; + + // Let the script safely contribute one part of the filename: + const char* fileid_ptr = safeGetString(l, 3, WrongType, "Second argument to celestia:takescreenshot must be a string"); + if (fileid_ptr == NULL) + fileid_ptr = ""; + string fileid(fileid_ptr); + + // be paranoid about the fileid, make sure it only contains 'A-Za-z0-9_': + for (unsigned int i = 0; i < fileid.length(); i++) + { + char ch = fileid[i]; + if (!((ch >= 'a' && ch <= 'z') || + (fileid[i] >= 'A' && ch <= 'Z') || + (ch >= '0' && ch <= '9') ) ) + fileid[i] = '_'; + } + // limit length of string + if (fileid.length() > 16) + fileid = fileid.substr(0, 16); + if (fileid.length() > 0) + fileid.append("-"); + + string path = appCore->getConfig()->scriptScreenshotDirectory; + if (path.length() > 0 && + path[path.length()-1] != '/' && + path[path.length()-1] != '\\') + + path.append("/"); + + luastate->screenshotCount++; + bool success = false; + char filenamestem[48]; + sprintf(filenamestem, "screenshot-%s%06i", fileid.c_str(), luastate->screenshotCount); + + // Get the dimensions of the current viewport + int viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + +#ifndef TARGET_OS_MAC + if (strncmp(filetype, "jpg", 3) == 0) + { + string filepath = path + filenamestem + ".jpg"; + success = CaptureGLBufferToJPEG(string(filepath), + viewport[0], viewport[1], + viewport[2], viewport[3]); + } + else + { + string filepath = path + filenamestem + ".png"; + success = CaptureGLBufferToPNG(string(filepath), + viewport[0], viewport[1], + viewport[2], viewport[3]); + } +#endif + lua_pushboolean(l, success); + + // no matter how long it really took, make it look like 0.1s to timeout check: + luastate->timeout = luastate->getTime() + timeToTimeout - 0.1; + return 1; +} + +static int celestia_createcelscript(lua_State* l) +{ + checkArgs(l, 2, 2, "Need one argument for celestia:createcelscript()"); + string scripttext = safeGetString(l, 2, AllErrors, "Argument to celestia:createcelscript() must be a string"); + return celscript_from_string(l, scripttext); +} + +static int celestia_requestsystemaccess(lua_State* l) +{ + // ignore possible argument for future extensions + checkArgs(l, 1, 2, "No argument expected for celestia:requestsystemaccess()"); + this_celestia(l); + LuaState* luastate = getLuaStateObject(l); + luastate->requestIO(); + return 0; +} + +static int celestia_getscriptpath(lua_State* l) +{ + // ignore possible argument for future extensions + checkArgs(l, 1, 1, "No argument expected for celestia:requestsystemaccess()"); + this_celestia(l); + lua_pushstring(l, "celestia-scriptpath"); + lua_gettable(l, LUA_REGISTRYINDEX); + return 1; +} + +static int celestia_tostring(lua_State* l) +{ + lua_pushstring(l, "[Celestia]"); + + return 1; +} + +static void CreateCelestiaMetaTable(lua_State* l) +{ + CreateClassMetatable(l, _Celestia); + + RegisterMethod(l, "__tostring", celestia_tostring); + RegisterMethod(l, "flash", celestia_flash); + RegisterMethod(l, "print", celestia_print); + RegisterMethod(l, "gettextwidth", celestia_gettextwidth); + RegisterMethod(l, "show", celestia_show); + RegisterMethod(l, "setaltazimuthmode", celestia_setaltazimuthmode); + RegisterMethod(l, "getaltazimuthmode", celestia_getaltazimuthmode); + RegisterMethod(l, "hide", celestia_hide); + RegisterMethod(l, "getrenderflags", celestia_getrenderflags); + RegisterMethod(l, "setrenderflags", celestia_setrenderflags); + RegisterMethod(l, "getscreendimension", celestia_getscreendimension); + RegisterMethod(l, "showlabel", celestia_showlabel); + RegisterMethod(l, "hidelabel", celestia_hidelabel); + RegisterMethod(l, "getlabelflags", celestia_getlabelflags); + RegisterMethod(l, "setlabelflags", celestia_setlabelflags); + RegisterMethod(l, "getorbitflags", celestia_getorbitflags); + RegisterMethod(l, "setorbitflags", celestia_setorbitflags); + RegisterMethod(l, "setlabelcolor", celestia_setlabelcolor); + RegisterMethod(l, "setlinecolor", celestia_setlinecolor); + RegisterMethod(l, "getoverlayelements", celestia_getoverlayelements); + RegisterMethod(l, "setoverlayelements", celestia_setoverlayelements); + RegisterMethod(l, "getfaintestvisible", celestia_getfaintestvisible); + RegisterMethod(l, "setfaintestvisible", celestia_setfaintestvisible); + RegisterMethod(l, "getgalaxylightgain", celestia_getgalaxylightgain); + RegisterMethod(l, "setgalaxylightgain", celestia_setgalaxylightgain); + RegisterMethod(l, "setminfeaturesize", celestia_setminfeaturesize); + RegisterMethod(l, "getminfeaturesize", celestia_getminfeaturesize); + RegisterMethod(l, "getobserver", celestia_getobserver); + RegisterMethod(l, "getobservers", celestia_getobservers); + RegisterMethod(l, "getselection", celestia_getselection); + RegisterMethod(l, "find", celestia_find); + RegisterMethod(l, "select", celestia_select); + RegisterMethod(l, "mark", celestia_mark); + RegisterMethod(l, "unmark", celestia_unmark); + RegisterMethod(l, "unmarkall", celestia_unmarkall); + RegisterMethod(l, "gettime", celestia_gettime); + RegisterMethod(l, "settime", celestia_settime); + RegisterMethod(l, "gettimescale", celestia_gettimescale); + RegisterMethod(l, "settimescale", celestia_settimescale); + RegisterMethod(l, "getambient", celestia_getambient); + RegisterMethod(l, "setambient", celestia_setambient); + RegisterMethod(l, "getminorbitsize", celestia_getminorbitsize); + RegisterMethod(l, "setminorbitsize", celestia_setminorbitsize); + RegisterMethod(l, "getstardistancelimit", celestia_getstardistancelimit); + RegisterMethod(l, "setstardistancelimit", celestia_setstardistancelimit); + RegisterMethod(l, "getstarstyle", celestia_getstarstyle); + RegisterMethod(l, "setstarstyle", celestia_setstarstyle); + RegisterMethod(l, "tojulianday", celestia_tojulianday); + RegisterMethod(l, "fromjulianday", celestia_fromjulianday); + RegisterMethod(l, "utctotdb", celestia_utctotdb); + RegisterMethod(l, "tdbtoutc", celestia_tdbtoutc); + RegisterMethod(l, "getstarcount", celestia_getstarcount); + RegisterMethod(l, "getdsocount", celestia_getdsocount); + RegisterMethod(l, "getstar", celestia_getstar); + RegisterMethod(l, "getdso", celestia_getdso); + RegisterMethod(l, "newframe", celestia_newframe); + RegisterMethod(l, "newvector", celestia_newvector); + RegisterMethod(l, "newposition", celestia_newposition); + RegisterMethod(l, "newrotation", celestia_newrotation); + RegisterMethod(l, "getscripttime", celestia_getscripttime); + RegisterMethod(l, "requestkeyboard", celestia_requestkeyboard); + RegisterMethod(l, "takescreenshot", celestia_takescreenshot); + RegisterMethod(l, "createcelscript", celestia_createcelscript); + RegisterMethod(l, "requestsystemaccess", celestia_requestsystemaccess); + RegisterMethod(l, "getscriptpath", celestia_getscriptpath); + RegisterMethod(l, "registereventhandler", celestia_registereventhandler); + RegisterMethod(l, "geteventhandler", celestia_geteventhandler); + RegisterMethod(l, "stars", celestia_stars); + RegisterMethod(l, "dsos", celestia_dsos); + + lua_pop(l, 1); +} + +static void loadLuaLibs(lua_State* state); + +// ==================== Initialization ==================== +bool LuaState::init(CelestiaCore* appCore) +{ + initMaps(); + + // Import the base, table, string, and math libraries +#if LUA_VER >= 0x050100 + openLuaLibrary(state, "", luaopen_base); + openLuaLibrary(state, LUA_MATHLIBNAME, luaopen_math); + openLuaLibrary(state, LUA_TABLIBNAME, luaopen_table); + openLuaLibrary(state, LUA_STRLIBNAME, luaopen_string); +#else + lua_baselibopen(state); + lua_mathlibopen(state); + lua_tablibopen(state); + lua_strlibopen(state); +#endif + + // Add an easy to use wait function, so that script writers can + // live in ignorance of coroutines. There will probably be a significant + // library of useful functions that can be defined purely in Lua. + // At that point, we'll want something a bit more robust than just + // parsing the whole text of the library every time a script is launched + if (loadScript("wait = function(x) coroutine.yield(x) end") != 0) + return false; + + // Execute the script fragment to define the wait function + if (lua_pcall(state, 0, 0, 0) != 0) + { + cout << "Error running script initialization fragment.\n"; + return false; + } + + lua_pushstring(state, "KM_PER_MICROLY"); + lua_pushnumber(state, (lua_Number)KM_PER_LY/1e6); + lua_settable(state, LUA_GLOBALSINDEX); + + loadLuaLibs(state); + + // Create the celestia object + lua_pushstring(state, "celestia"); + celestia_new(state, appCore); + lua_settable(state, LUA_GLOBALSINDEX); + // add reference to appCore in the registry + lua_pushstring(state, "celestia-appcore"); + lua_pushlightuserdata(state, static_cast(appCore)); + lua_settable(state, LUA_REGISTRYINDEX); + // add a reference to the LuaState-object in the registry + lua_pushstring(state, "celestia-luastate"); + lua_pushlightuserdata(state, static_cast(this)); + lua_settable(state, LUA_REGISTRYINDEX); + + lua_pushstring(state, EventHandlers); + lua_newtable(state); + lua_settable(state, LUA_REGISTRYINDEX); + +#if 0 + lua_pushstring(state, "dofile"); + lua_gettable(state, LUA_GLOBALSINDEX); // function "dofile" on stack + lua_pushstring(state, "luainit.celx"); // parameter + if (lua_pcall(state, 1, 0, 0) != 0) // execute it + { + CelestiaCore::Alerter* alerter = appCore->getAlerter(); + // copy string?! + const char* errorMessage = lua_tostring(state, -1); + cout << errorMessage << '\n'; cout.flush(); + alerter->fatalError(errorMessage); + return false; + } +#endif + + return true; +} + + +void LuaState::setLuaPath(const string& s) +{ +#if LUA_VER >= 0x050100 + lua_getfield(state, LUA_GLOBALSINDEX, "package"); + lua_pushstring(state, s.c_str()); + lua_setfield(state, -2, "path"); + lua_pop(state, 1); +#else + lua_pushstring(state, "LUA_PATH"); + lua_pushstring(state, s.c_str()); + lua_settable(state, LUA_GLOBALSINDEX); +#endif +} + + +// ==================== OpenGL ==================== + +static int glu_LookAt(lua_State* l) +{ + checkArgs(l, 9, 9, "Nine arguments expected for glu.LookAt()"); + float ix = (float)safeGetNumber(l, 1, WrongType, "argument 1 to gl.Frustum must be a number", 0.0); + float iy = (float)safeGetNumber(l, 2, WrongType, "argument 2 to gl.Frustum must be a number", 0.0); + float iz = (float)safeGetNumber(l, 3, WrongType, "argument 3 to gl.Frustum must be a number", 0.0); + float cx = (float)safeGetNumber(l, 4, WrongType, "argument 4 to gl.Frustum must be a number", 0.0); + float cy = (float)safeGetNumber(l, 5, WrongType, "argument 5 to gl.Frustum must be a number", 0.0); + float cz = (float)safeGetNumber(l, 6, WrongType, "argument 6 to gl.Frustum must be a number", 0.0); + float ux = (float)safeGetNumber(l, 7, WrongType, "argument 4 to gl.Frustum must be a number", 0.0); + float uy = (float)safeGetNumber(l, 8, WrongType, "argument 5 to gl.Frustum must be a number", 0.0); + float uz = (float)safeGetNumber(l, 9, WrongType, "argument 6 to gl.Frustum must be a number", 0.0); + gluLookAt(ix,iy,iz,cx,cy,cz,ux,uy,uz); + return 0; +} + +static int gl_Frustum(lua_State* l) +{ + checkArgs(l, 6, 6, "Six arguments expected for gl.Frustum()"); + float ll = (float)safeGetNumber(l, 1, WrongType, "argument 1 to gl.Frustum must be a number", 0.0); + float r = (float)safeGetNumber(l, 2, WrongType, "argument 2 to gl.Frustum must be a number", 0.0); + float b = (float)safeGetNumber(l, 3, WrongType, "argument 3 to gl.Frustum must be a number", 0.0); + float t = (float)safeGetNumber(l, 4, WrongType, "argument 4 to gl.Frustum must be a number", 0.0); + float n = (float)safeGetNumber(l, 5, WrongType, "argument 5 to gl.Frustum must be a number", 0.0); + float f = (float)safeGetNumber(l, 6, WrongType, "argument 6 to gl.Frustum must be a number", 0.0); + glFrustum(ll,r,b,t,n,f); + return 0; +} + +static int gl_Ortho(lua_State* l) +{ + checkArgs(l, 6, 6, "Six arguments expected for gl.Ortho()"); + float ll = (float)safeGetNumber(l, 1, WrongType, "argument 1 to gl.Ortho must be a number", 0.0); + float r = (float)safeGetNumber(l, 2, WrongType, "argument 2 to gl.Ortho must be a number", 0.0); + float b = (float)safeGetNumber(l, 3, WrongType, "argument 3 to gl.Ortho must be a number", 0.0); + float t = (float)safeGetNumber(l, 4, WrongType, "argument 4 to gl.Ortho must be a number", 0.0); + float n = (float)safeGetNumber(l, 5, WrongType, "argument 5 to gl.Ortho must be a number", 0.0); + float f = (float)safeGetNumber(l, 6, WrongType, "argument 6 to gl.Ortho must be a number", 0.0); + glOrtho(ll,r,b,t,n,f); + return 0; +} + +static int glu_Ortho2D(lua_State* l) +{ + checkArgs(l, 4, 4, "Six arguments expected for gl.Ortho2D()"); + float ll = (float)safeGetNumber(l, 1, WrongType, "argument 1 to gl.Ortho must be a number", 0.0); + float r = (float)safeGetNumber(l, 2, WrongType, "argument 2 to gl.Ortho must be a number", 0.0); + float b = (float)safeGetNumber(l, 3, WrongType, "argument 3 to gl.Ortho must be a number", 0.0); + float t = (float)safeGetNumber(l, 4, WrongType, "argument 4 to gl.Ortho must be a number", 0.0); + gluOrtho2D(ll,r,b,t); + return 0; +} + +static int gl_TexCoord(lua_State* l) +{ + checkArgs(l, 2, 2, "Two arguments expected for gl.TexCoord()"); + float x = (float)safeGetNumber(l, 1, WrongType, "argument 1 to gl.TexCoord must be a number", 0.0); + float y = (float)safeGetNumber(l, 2, WrongType, "argument 2 to gl.TexCoord must be a number", 0.0); + glTexCoord2f(x,y); + return 0; +} + +static int gl_TexParameter(lua_State* l) +{ + checkArgs(l, 3, 3, "Three arguments expected for gl.TexParameter()"); + + // TODO: Need to ensure that these values are integers, or better yet use + // names. + float x = (float)safeGetNumber(l, 1, WrongType, "argument 1 to gl.TexParameter must be a number", 0.0); + float y = (float)safeGetNumber(l, 2, WrongType, "argument 2 to gl.TexParameter must be a number", 0.0); + float z = (float)safeGetNumber(l, 3, WrongType, "argument 3 to gl.TexParameter must be a number", 0.0); + glTexParameteri((GLint) x, (GLenum) y, (GLenum) z); + return 0; +} + +static int gl_Vertex(lua_State* l) +{ + checkArgs(l, 2, 2, "Two arguments expected for gl.Vertex()"); + float x = (float)safeGetNumber(l, 1, WrongType, "argument 1 to gl.Vertex must be a number", 0.0); + float y = (float)safeGetNumber(l, 2, WrongType, "argument 2 to gl.Vertex must be a number", 0.0); + glVertex2f(x,y); + return 0; +} + +static int gl_Color(lua_State* l) +{ + checkArgs(l, 4, 4, "Four arguments expected for gl.Color()"); + float r = (float)safeGetNumber(l, 1, WrongType, "argument 1 to gl.Color must be a number", 0.0); + float g = (float)safeGetNumber(l, 2, WrongType, "argument 2 to gl.Color must be a number", 0.0); + float b = (float)safeGetNumber(l, 3, WrongType, "argument 3 to gl.Color must be a number", 0.0); + float a = (float)safeGetNumber(l, 4, WrongType, "argument 4 to gl.Color must be a number", 0.0); + glColor4f(r,g,b,a); +// glColor4f(0.8f, 0.5f, 0.5f, 1.0f); + return 0; +} + +static int gl_LineWidth(lua_State* l) +{ + checkArgs(l, 1, 1, "One argument expected for gl.LineWidth()"); + float n = (float)safeGetNumber(l, 1, WrongType, "argument 1 to gl.LineWidth must be a number", 1.0); + glLineWidth(n); + return 0; +} + +static int gl_Translate(lua_State* l) +{ + checkArgs(l, 2, 2, "Two arguments expected for gl.Translate()"); + float x = (float)safeGetNumber(l, 1, WrongType, "argument 1 to gl.Translate must be a number", 0.0); + float y = (float)safeGetNumber(l, 2, WrongType, "argument 2 to gl.Translate must be a number", 0.0); + glTranslatef(x,y,0.0f); + return 0; +} + +static int gl_BlendFunc(lua_State* l) +{ + checkArgs(l, 2, 2, "Two arguments expected for gl.BlendFunc()"); + int i = (int)safeGetNumber(l, 1, WrongType, "argument 1 to gl.BlendFunc must be a number", 0.0); + int j = (int)safeGetNumber(l, 2, WrongType, "argument 2 to gl.BlendFunc must be a number", 0.0); + glBlendFunc(i,j); + return 0; +} + +static int gl_Begin(lua_State* l) +{ + checkArgs(l, 1, 1, "One argument expected for gl.Begin()"); + int i = (int)safeGetNumber(l, 1, WrongType, "argument 1 to gl.Begin must be a number", 0.0); + glBegin(i); + return 0; +} + +static int gl_End(lua_State* l) +{ + checkArgs(l, 0, 0, "No arguments expected for gl.PopMatrix()"); + glEnd(); + return 0; +} + +static int gl_Enable(lua_State* l) +{ + checkArgs(l, 1, 1, "One argument expected for gl.Enable()"); + int i = (int)safeGetNumber(l, 1, WrongType, "argument 1 to gl.Enable must be a number", 0.0); + glEnable(i); + return 0; +} + +static int gl_Disable(lua_State* l) +{ + checkArgs(l, 1, 1, "One argument expected for gl.Disable()"); + int i = (int)safeGetNumber(l, 1, WrongType, "argument 1 to gl.Disable must be a number", 0.0); + glDisable(i); + return 0; +} + +static int gl_MatrixMode(lua_State* l) +{ + checkArgs(l, 1, 1, "One argument expected for gl.MatrixMode()"); + int i = (int)safeGetNumber(l, 1, WrongType, "argument 1 to gl.MatrixMode must be a number", 0.0); + glMatrixMode(i); + return 0; +} + +static int gl_PopMatrix(lua_State* l) +{ + checkArgs(l, 0, 0, "No arguments expected for gl.PopMatrix()"); + glPopMatrix(); + return 0; +} + +static int gl_LoadIdentity(lua_State* l) +{ + checkArgs(l, 0, 0, "No arguments expected for gl.LoadIdentity()"); + glLoadIdentity(); + return 0; +} + +static int gl_PushMatrix(lua_State* l) +{ + checkArgs(l, 0, 0, "No arguments expected for gl.PushMatrix()"); + glPushMatrix(); + return 0; +} + +static void RegisterValue(lua_State* l, const char* name, float n) +{ + lua_pushstring(l, name); + lua_pushnumber(l, n); + lua_settable(l, -3); +} + +static void gl_loadlib(lua_State* l) +{ + lua_pushstring(l, "gl"); + lua_newtable(l); + + RegisterMethod(l, "Frustum", gl_Frustum); + RegisterMethod(l, "Ortho", gl_Ortho); + RegisterMethod(l, "Color", gl_Color); + RegisterMethod(l, "LineWidth", gl_LineWidth); + RegisterMethod(l, "TexCoord", gl_TexCoord); + RegisterMethod(l, "TexParameter", gl_TexParameter); + RegisterMethod(l, "Vertex", gl_Vertex); + RegisterMethod(l, "Translate", gl_Translate); + RegisterMethod(l, "BlendFunc", gl_BlendFunc); + RegisterMethod(l, "Begin", gl_Begin); + RegisterMethod(l, "End", gl_End); + RegisterMethod(l, "Enable", gl_Enable); + RegisterMethod(l, "Disable", gl_Disable); + RegisterMethod(l, "MatrixMode", gl_MatrixMode); + RegisterMethod(l, "PopMatrix", gl_PopMatrix); + RegisterMethod(l, "LoadIdentity", gl_LoadIdentity); + RegisterMethod(l, "PushMatrix", gl_PushMatrix); + + RegisterValue(l, "QUADS", GL_QUADS); + RegisterValue(l, "LIGHTING", GL_LIGHTING); + RegisterValue(l, "POINTS", GL_POINTS); + RegisterValue(l, "LINES", GL_LINES); + RegisterValue(l, "LINE_LOOP", GL_LINE_LOOP); + RegisterValue(l, "LINE_SMOOTH", GL_LINE_SMOOTH); + RegisterValue(l, "POLYGON", GL_POLYGON); + RegisterValue(l, "PROJECTION", GL_PROJECTION); + RegisterValue(l, "MODELVIEW", GL_MODELVIEW); + RegisterValue(l, "BLEND", GL_BLEND); + RegisterValue(l, "TEXTURE_2D", GL_TEXTURE_2D); + RegisterValue(l, "TEXTURE_MAG_FILTER", GL_TEXTURE_MAG_FILTER); + RegisterValue(l, "TEXTURE_MIN_FILTER", GL_TEXTURE_MIN_FILTER); + RegisterValue(l, "LINEAR", GL_LINEAR); + RegisterValue(l, "NEAREST", GL_NEAREST); + RegisterValue(l, "SRC_ALPHA", GL_SRC_ALPHA); + RegisterValue(l, "ONE_MINUS_SRC_ALPHA", GL_ONE_MINUS_SRC_ALPHA); + lua_settable(l, LUA_GLOBALSINDEX); + + lua_pushstring(l, "glu"); + lua_newtable(l); + RegisterMethod(l, "LookAt", glu_LookAt); + RegisterMethod(l, "Ortho2D", glu_Ortho2D); + lua_settable(l, LUA_GLOBALSINDEX); +} + +// ==================== Font Object ==================== + +static int font_new(lua_State* l, TextureFont* f) +{ + TextureFont** ud = static_cast(lua_newuserdata(l, sizeof(TextureFont*))); + *ud = f; + + SetClass(l, _Font); + + return 1; +} + +static TextureFont* to_font(lua_State* l, int index) +{ + TextureFont** f = static_cast(lua_touserdata(l, index)); + + // Check if pointer is valid + if (f != NULL ) + { + return *f; + } + return NULL; +} + +static TextureFont* this_font(lua_State* l) +{ + TextureFont* f = to_font(l, 1); + if (f == NULL) + { + doError(l, "Bad font object!"); + } + + return f; +} + + +static int font_bind(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for font:bind()"); + + TextureFont* font = this_font(l); + font->bind(); + return 0; +} + +static int font_render(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument required for font:render"); + + const char* s = safeGetString(l, 2, AllErrors, "First argument to font:render must be a string"); + TextureFont* font = this_font(l); + font->render(s); + + return 0; +} + +static int font_getwidth(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected for font:getwidth"); + const char* s = safeGetString(l, 2, AllErrors, "Argument to font:getwidth must be a string"); + TextureFont* font = this_font(l); + lua_pushnumber(l, font->getWidth(s)); + return 1; +} + +static int font_getheight(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for font:getheight()"); + + TextureFont* font = this_font(l); + lua_pushnumber(l, font->getHeight()); + return 1; +} + +static int font_tostring(lua_State* l) +{ + // TODO: print out the actual information about the font + lua_pushstring(l, "[Font]"); + + return 1; +} + +static void CreateFontMetaTable(lua_State* l) +{ + CreateClassMetatable(l, _Font); + + RegisterMethod(l, "__tostring", font_tostring); + RegisterMethod(l, "bind", font_bind); + RegisterMethod(l, "render", font_render); + RegisterMethod(l, "getwidth", font_getwidth); + RegisterMethod(l, "getheight", font_getheight); + + lua_pop(l, 1); // remove metatable from stack +} + +// ==================== Image ============================================= +#if 0 +static int image_new(lua_State* l, Image* i) +{ + Image** ud = static_cast(lua_newuserdata(l, sizeof(Image*))); + *ud = i; + + SetClass(l, _Image); + + return 1; +} +#endif + +static Image* to_image(lua_State* l, int index) +{ + Image** image = static_cast(lua_touserdata(l, index)); + + // Check if pointer is valid + if (image != NULL ) + { + return *image; + } + return NULL; +} + +static Image* this_image(lua_State* l) +{ + Image* image = to_image(l,1); + if (image == NULL) + { + doError(l, "Bad image object!"); + } + + return image; +} + +static int image_getheight(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for image:getheight()"); + + Image* image = this_image(l); + lua_pushnumber(l, image->getHeight()); + return 1; +} + +static int image_getwidth(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for image:getwidth()"); + + Image* image = this_image(l); + lua_pushnumber(l, image->getWidth()); + return 1; +} + +static int image_tostring(lua_State* l) +{ + // TODO: print out the actual information about the image + lua_pushstring(l, "[Image]"); + + return 1; +} + +static void CreateImageMetaTable(lua_State* l) +{ + CreateClassMetatable(l, _Image); + + RegisterMethod(l, "__tostring", image_tostring); + RegisterMethod(l, "getheight", image_getheight); + RegisterMethod(l, "getwidth", image_getwidth); + + lua_pop(l, 1); // remove metatable from stack +} + +// ==================== Texture ============================================ + +static int texture_new(lua_State* l, Texture* t) +{ + Texture** ud = static_cast(lua_newuserdata(l, sizeof(Texture*))); + *ud = t; + + SetClass(l, _Texture); + + return 1; +} + +static Texture* to_texture(lua_State* l, int index) +{ + Texture** texture = static_cast(lua_touserdata(l, index)); + + // Check if pointer is valid + if (texture != NULL ) + { + return *texture; + } + return NULL; +} + +static Texture* this_texture(lua_State* l) +{ + Texture* texture = to_texture(l,1); + if (texture == NULL) + { + doError(l, "Bad texture object!"); + } + + return texture; +} + +static int texture_bind(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for texture:bind()"); + + Texture* texture = this_texture(l); + texture->bind(); + return 0; +} + +static int texture_getheight(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for texture:getheight()"); + + Texture* texture = this_texture(l); + lua_pushnumber(l, texture->getHeight()); + return 1; +} + +static int texture_getwidth(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected for texture:getwidth()"); + + Texture* texture = this_texture(l); + lua_pushnumber(l, texture->getWidth()); + return 1; +} + +static int texture_tostring(lua_State* l) +{ + // TODO: print out the actual information about the texture + lua_pushstring(l, "[Texture]"); + + return 1; +} + +static void CreateTextureMetaTable(lua_State* l) +{ + CreateClassMetatable(l, _Texture); + + RegisterMethod(l, "__tostring", texture_tostring); + RegisterMethod(l, "getheight", texture_getheight); + RegisterMethod(l, "getwidth", texture_getwidth); + RegisterMethod(l, "bind", texture_bind); + + lua_pop(l, 1); // remove metatable from stack +} + +// ==================== object extensions ==================== + +// TODO: This should be replaced by an actual Atmosphere object +static int object_setatmosphere(lua_State* l) +{ + checkArgs(l, 23, 23, "22 arguments (!) expected to function object:setatmosphere"); + + Selection* sel = this_object(l); + //CelestiaCore* appCore = getAppCore(l, AllErrors); + + if (sel->body() != NULL) + { + Body* body = sel->body(); + Atmosphere* atmosphere = body->getAtmosphere(); + if (atmosphere != NULL) + { + float r = (float) safeGetNumber(l, 2, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); + float g = (float) safeGetNumber(l, 3, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); + float b = (float) safeGetNumber(l, 4, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); + // Color testColor(0.0f, 1.0f, 0.0f); + Color testColor(r, g, b); + atmosphere->lowerColor = testColor; + r = (float) safeGetNumber(l, 5, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); + g = (float) safeGetNumber(l, 6, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); + b = (float) safeGetNumber(l, 7, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); + atmosphere->upperColor = Color(r, g, b); + r = (float) safeGetNumber(l, 8, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); + g = (float) safeGetNumber(l, 9, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); + b = (float) safeGetNumber(l, 10, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); + atmosphere->skyColor = Color(r, g, b); + r = (float) safeGetNumber(l, 11, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); + g = (float) safeGetNumber(l, 12, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); + b = (float) safeGetNumber(l, 13, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); + atmosphere->sunsetColor = Color(r, g, b); + r = (float) safeGetNumber(l, 14, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); + g = (float) safeGetNumber(l, 15, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); + b = (float) safeGetNumber(l, 16, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); + //HWR atmosphere->rayleighCoeff = Vector3(r, g, b); + r = (float) safeGetNumber(l, 17, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); + g = (float) safeGetNumber(l, 18, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); + b = (float) safeGetNumber(l, 19, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); + //HWR atmosphere->absorptionCoeff = Vector3(r, g, b); + b = (float) safeGetNumber(l, 20, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); + atmosphere->mieCoeff = b; + b = (float) safeGetNumber(l, 21, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); + atmosphere->mieScaleHeight = b; + b = (float) safeGetNumber(l, 22, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); + atmosphere->miePhaseAsymmetry = b; + b = (float) safeGetNumber(l, 23, AllErrors, "Arguments to observer:setatmosphere() must be numbers"); + atmosphere->rayleighScaleHeight = b; + + body->setAtmosphere(*atmosphere); + cout << "set atmosphere\n"; + } + } + + return 0; +} + +static void ExtendObjectMetaTable(lua_State* l) +{ + PushClass(l, _Object); + lua_rawget(l, LUA_REGISTRYINDEX); + if (lua_type(l, -1) != LUA_TTABLE) + cout << "Metatable for " << ClassNames[_Object] << " not found!\n"; + RegisterMethod(l, "setatmosphere", object_setatmosphere); + lua_pop(l, 1); +} +// ==================== celestia extensions ==================== + +static int celestia_log(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected to function celestia:log"); + + const char* s = safeGetString(l, 2, AllErrors, "First argument to celestia:log must be a string"); + clog << s << "\n"; clog.flush(); + return 0; +} + +static int celestia_getparamstring(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument expected to celestia:getparamstring()"); + CelestiaCore* appCore = this_celestia(l); + const char* s = safeGetString(l, 2, AllErrors, "Argument to celestia:getparamstring must be a string"); + std::string paramString; // HWR + CelestiaConfig* config = appCore->getConfig(); + config->configParams->getString(s, paramString); + lua_pushstring(l,paramString.c_str()); + return 1; +} + +static int celestia_loadtexture(lua_State* l) +{ + checkArgs(l, 2, 2, "Need one argument for celestia:loadtexture()"); + string s = safeGetString(l, 2, AllErrors, "Argument to celestia:loadtexture() must be a string"); + lua_Debug ar; + lua_getstack(l, 1, &ar); + lua_getinfo(l, "S", &ar); + string base_dir = ar.source; // Lua file from which we are called + if (base_dir[0] == '@') base_dir = base_dir.substr(1); + base_dir = base_dir.substr(0, base_dir.rfind('/')) + '/'; + Texture* t = LoadTextureFromFile(base_dir + s); + if (t == NULL) return 0; + texture_new(l, t); + return 1; +} + +static int celestia_loadfont(lua_State* l) +{ + checkArgs(l, 2, 2, "Need one argument for celestia:loadtexture()"); + string s = safeGetString(l, 2, AllErrors, "Argument to celestia:loadfont() must be a string"); + TextureFont* font = LoadTextureFont(s); + if (font == NULL) return 0; + font->buildTexture(); + font_new(l, font); + return 1; +} + +TextureFont* getFont(CelestiaCore* appCore) +{ + return appCore->font; +} + +static int celestia_getfont(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected to function celestia:getTitleFont"); + + CelestiaCore* appCore = getAppCore(l, AllErrors); + TextureFont* font = getFont(appCore); + if (font == NULL) return 0; + font_new(l, font); + return 1; +} + +TextureFont* getTitleFont(CelestiaCore* appCore) +{ + return appCore->titleFont; +} + +static int celestia_gettitlefont(lua_State* l) +{ + checkArgs(l, 1, 1, "No arguments expected to function celestia:getTitleFont"); + + CelestiaCore* appCore = getAppCore(l, AllErrors); + TextureFont* font = getTitleFont(appCore); + if (font == NULL) return 0; + font_new(l, font); + return 1; +} + +static int celestia_settimeslice(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument required for celestia:settimeslice"); + //CelestiaCore* appCore = this_celestia(l); + + if (!lua_isnumber(l, 2) && !lua_isnil(l, 2)) + { + doError(l, "Argument for celestia:settimeslice must be a number"); + } + double timeslice = safeGetNumber(l, 2, AllErrors, "Argument to celestia:settimeslice must be a number"); + if (timeslice == 0.0) + timeslice = 0.1; + + LuaState* luastate = getLuaStateObject(l); + luastate->timeout = luastate->getTime() + timeslice; + + return 0; +} + +static int celestia_setluahook(lua_State* l) +{ + checkArgs(l, 2, 2, "One argument required for celestia:setluahook"); + CelestiaCore* appCore = this_celestia(l); + + if (!lua_istable(l, 2) && !lua_isnil(l, 2)) + { + doError(l, "Argument for celestia:setluahook must be a table or nil"); + return 0; + } + + LuaState* luastate = getLuaStateObject(l); + if (luastate != NULL) + { + luastate->setLuaHookEventHandlerEnabled(lua_istable(l, 2)); + } + + lua_pushlightuserdata(l, appCore); + lua_pushvalue(l, -2); + lua_settable(l, LUA_REGISTRYINDEX); + + return 0; +} + +static void ExtendCelestiaMetaTable(lua_State* l) +{ + PushClass(l, _Celestia); + lua_rawget(l, LUA_REGISTRYINDEX); + if (lua_type(l, -1) != LUA_TTABLE) + cout << "Metatable for " << ClassNames[_Celestia] << " not found!\n"; + RegisterMethod(l, "log", celestia_log); + RegisterMethod(l, "settimeslice", celestia_settimeslice); + RegisterMethod(l, "setluahook", celestia_setluahook); + RegisterMethod(l, "getparamstring", celestia_getparamstring); + RegisterMethod(l, "getfont", celestia_getfont); + RegisterMethod(l, "gettitlefont", celestia_gettitlefont); + RegisterMethod(l, "loadtexture", celestia_loadtexture); + RegisterMethod(l, "loadfont", celestia_loadfont); + lua_pop(l, 1); +} + + +#if LUA_VER < 0x050100 +// ======================== loadlib =================================== +/* +* This is an implementation of loadlib based on the dlfcn interface. +* The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD, +* NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least +* as an emulation layer on top of native functions. +*/ + +#ifndef _WIN32 +extern "C" { +#include +/* #include */ +#include +} + +#if 0 +static int x_loadlib(lua_State *L) +{ +/* temp -- don't have lauxlib + const char *path=luaL_checkstring(L,1); + const char *init=luaL_checkstring(L,2); +*/ +cout << "loading lua lib\n"; cout.flush(); + + const char *path=lua_tostring(L,1); + const char *init=lua_tostring(L,2); + + void *lib=dlopen(path,RTLD_NOW); + if (lib!=NULL) + { + lua_CFunction f=(lua_CFunction) dlsym(lib,init); + if (f!=NULL) + { + lua_pushlightuserdata(L,lib); + lua_pushcclosure(L,f,1); + return 1; + } + } + /* else return appropriate error messages */ + lua_pushnil(L); + lua_pushstring(L,dlerror()); + lua_pushstring(L,(lib!=NULL) ? "init" : "open"); + if (lib!=NULL) dlclose(lib); + return 3; +} +#endif +#endif // _WIN32 +#endif // LUA_VER < 0x050100 + +// ==================== Load Libraries ================================================ + +static void loadLuaLibs(lua_State* state) +{ +#if LUA_VER >= 0x050100 + openLuaLibrary(state, LUA_DBLIBNAME, luaopen_debug); +#else + luaopen_debug(state); +#endif + + // TODO: Not required with Lua 5.1 +#if 0 +#ifndef _WIN32 + lua_pushstring(state, "xloadlib"); + lua_pushcfunction(state, x_loadlib); + lua_settable(state, LUA_GLOBALSINDEX); +#endif +#endif + + CreateObjectMetaTable(state); + CreateObserverMetaTable(state); + CreateCelestiaMetaTable(state); + CreatePositionMetaTable(state); + CreateVectorMetaTable(state); + CreateRotationMetaTable(state); + CreateFrameMetaTable(state); + CreateCelscriptMetaTable(state); + CreateFontMetaTable(state); + CreateImageMetaTable(state); + CreateTextureMetaTable(state); + ExtendCelestiaMetaTable(state); + ExtendObjectMetaTable(state); + + gl_loadlib(state); +} + + +void LuaState::allowSystemAccess() +{ +#if LUA_VER >= 0x050100 + openLuaLibrary(state, LUA_LOADLIBNAME, luaopen_package); + openLuaLibrary(state, LUA_IOLIBNAME, luaopen_io); + openLuaLibrary(state, LUA_OSLIBNAME, luaopen_os); +#else + luaopen_io(state); +#endif + ioMode = IOAllowed; +} + + +// Permit access to the package library, but prohibit use of the loadlib +// function. +void LuaState::allowLuaPackageAccess() +{ +#if LUA_VER >= 0x050100 + openLuaLibrary(state, LUA_LOADLIBNAME, luaopen_package); + + // Disallow loadlib + lua_getfield(state, LUA_GLOBALSINDEX, "package"); + lua_pushnil(state); + lua_setfield(state, -2, "loadlib"); + lua_pop(state, 1); +#endif +} + + +// ==================== Lua Hook Methods ================================================ + +void LuaState::setLuaHookEventHandlerEnabled(bool enable) +{ + eventHandlerEnabled = enable; +} + + +bool LuaState::callLuaHook(void* obj, const char* method) +{ + if (!eventHandlerEnabled) + return false; + + lua_pushlightuserdata(costate, obj); + lua_gettable(costate, LUA_REGISTRYINDEX); + if (!lua_istable(costate, -1)) + { + lua_pop(costate, 1); + return false; + } + bool handled = false; + + lua_pushstring(costate, method); + lua_gettable(costate, -2); + if (lua_isfunction(costate, -1)) + { + lua_pushvalue(costate, -2); // push the Lua object the stack + lua_remove(costate, -3); // remove the Lua object from the stack + + timeout = getTime() + 1.0; + if (lua_pcall(costate, 1, 1, 0) != 0) + { + cerr << "Error while executing Lua Hook: " << lua_tostring(costate, -1) << "\n"; + } + else + { + handled = lua_toboolean(costate, -1) == 1 ? true : false; + } + lua_pop(costate, 1); // pop the return value + } + else + { + lua_pop(costate, 2); + } + + return handled; +} + + +bool LuaState::callLuaHook(void* obj, const char* method, const char ch) +{ + if (!eventHandlerEnabled) + return false; + + lua_pushlightuserdata(costate, obj); + lua_gettable(costate, LUA_REGISTRYINDEX); + if (!lua_istable(costate, -1)) + { + lua_pop(costate, 1); + return false; + } + bool handled = false; + + lua_pushstring(costate, method); + lua_gettable(costate, -2); + if (lua_isfunction(costate, -1)) + { + lua_pushvalue(costate, -2); // push the Lua object onto the stack + lua_remove(costate, -3); // remove the Lua object from the stack + + lua_pushlstring(costate, &ch, 1); // push the char onto the stack + + timeout = getTime() + 1.0; + if (lua_pcall(costate, 2, 1, 0) != 0) + { + cerr << "Error while executing Lua Hook: " << lua_tostring(costate, -1) << "\n"; + } + else + { + handled = lua_toboolean(costate, -1) == 1 ? true : false; + } + lua_pop(costate, 1); // pop the return value + } + else + { + lua_pop(costate, 2); + } + + return handled; +} + + +bool LuaState::callLuaHook(void* obj, const char* method, float x, float y) +{ + if (!eventHandlerEnabled) + return false; + + lua_pushlightuserdata(costate, obj); + lua_gettable(costate, LUA_REGISTRYINDEX); + if (!lua_istable(costate, -1)) + { + lua_pop(costate, 1); + return false; + } + bool handled = false; + + lua_pushstring(costate, method); + lua_gettable(costate, -2); + if (lua_isfunction(costate, -1)) + { + lua_pushvalue(costate, -2); // push the Lua object onto the stack + lua_remove(costate, -3); // remove the Lua object from the stack + + lua_pushnumber(costate, x); // push x onto the stack + lua_pushnumber(costate, y); // push y onto the stack + + timeout = getTime() + 1.0; + if (lua_pcall(costate, 3, 1, 0) != 0) + { + cerr << "Error while executing Lua Hook: " << lua_tostring(costate, -1) << "\n"; + } + else + { + handled = lua_toboolean(costate, -1) == 1 ? true : false; + } + lua_pop(costate, 1); // pop the return value + } + else + { + lua_pop(costate, 2); + } + + return handled; +} + + +bool LuaState::callLuaHook(void* obj, const char* method, float x, float y, int b) +{ + if (!eventHandlerEnabled) + return false; + + lua_pushlightuserdata(costate, obj); + lua_gettable(costate, LUA_REGISTRYINDEX); + if (!lua_istable(costate, -1)) + { + lua_pop(costate, 1); + return false; + } + bool handled = false; + + lua_pushstring(costate, method); + lua_gettable(costate, -2); + if (lua_isfunction(costate, -1)) + { + lua_pushvalue(costate, -2); // push the Lua object onto the stack + lua_remove(costate, -3); // remove the Lua object from the stack + + lua_pushnumber(costate, x); // push x onto the stack + lua_pushnumber(costate, y); // push y onto the stack + lua_pushnumber(costate, b); // push b onto the stack + + timeout = getTime() + 1.0; + if (lua_pcall(costate, 4, 1, 0) != 0) + { + cerr << "Error while executing Lua Hook: " << lua_tostring(costate, -1) << "\n"; + } + else + { + handled = lua_toboolean(costate, -1) == 1 ? true : false; + } + lua_pop(costate, 1); // pop the return value + } + else + { + lua_pop(costate, 2); + } + + return handled; +} + + +bool LuaState::callLuaHook(void* obj, const char* method, double dt) +{ + if (!eventHandlerEnabled) + return false; + + lua_pushlightuserdata(costate, obj); + lua_gettable(costate, LUA_REGISTRYINDEX); + if (!lua_istable(costate, -1)) + { + lua_pop(costate, 1); + return false; + } + bool handled = false; + + lua_pushstring(costate, method); + lua_gettable(costate, -2); + if (lua_isfunction(costate, -1)) + { + lua_pushvalue(costate, -2); // push the Lua object onto the stack + lua_remove(costate, -3); // remove the Lua object from the stack + lua_pushnumber(costate, dt); + + timeout = getTime() + 1.0; + if (lua_pcall(costate, 2, 1, 0) != 0) + { + cerr << "Error while executing Lua Hook: " << lua_tostring(costate, -1) << "\n"; + } + else + { + handled = lua_toboolean(costate, -1) == 1 ? true : false; + } + lua_pop(costate, 1); // pop the return value + } + else + { + lua_pop(costate, 2); + } + + return handled; +} Index: src/celengine/body.cpp =================================================================== --- src/celengine/body.cpp (revision 4095) +++ src/celengine/body.cpp (working copy) @@ -359,7 +359,7 @@ if (orbitFrame != NULL) { - Point3d p = orbitFrame->convertFromAstrocentric(pos, tjd); + Point3d p = orbitFrame->convertToAstrocentric(pos, tjd); // Temporary hack; won't be necessary post-1.5.0 when this function // is redefined to return position with respect to frame root object @@ -405,7 +405,7 @@ if (orbitFrame != NULL) { - Point3d p = orbitFrame->convertFromAstrocentric(pos, tjd); + Point3d p = orbitFrame->convertToAstrocentric(pos, tjd); // Temporary hack; won't be necessary post-1.5.0 when this function // is redefined to return position with respect to frame root object Index: src/celengine/simulation.cpp =================================================================== --- src/celengine/simulation.cpp (revision 4095) +++ src/celengine/simulation.cpp (working copy) @@ -166,7 +166,7 @@ Selection Simulation::pickObject(Vec3f pickRay, int renderFlags, float tolerance) { return universe->pick(activeObserver->getPosition(), - pickRay * activeObserver->getOrientation().toMatrix4(), + pickRay * activeObserver->getOrientationf().toMatrix4(), activeObserver->getTime(), renderFlags, faintestVisible, @@ -236,18 +236,20 @@ activeObserver->setMode(mode); } -void Simulation::setFrame(astro::CoordinateSystem coordSys, - const Selection& sel) +void Simulation::setFrame(ObserverFrame::CoordinateSystem coordSys, + const Selection& refObject, + const Selection& targetObject) { - activeObserver->setFrame(FrameOfReference(coordSys, sel)); + activeObserver->setFrame(coordSys, refObject, targetObject); } -void Simulation::setFrame(const FrameOfReference& _frame) +void Simulation::setFrame(ObserverFrame::CoordinateSystem coordSys, + const Selection& refObject) { - activeObserver->setFrame(_frame); + activeObserver->setFrame(coordSys, refObject); } -FrameOfReference Simulation::getFrame() const +const ObserverFrame* Simulation::getFrame() const { return activeObserver->getFrame(); } @@ -286,7 +288,7 @@ void Simulation::gotoSelection(double gotoTime, Vec3f up, - astro::CoordinateSystem upFrame) + ObserverFrame::CoordinateSystem upFrame) { if (selection.getType() == Selection::Type_Location) { @@ -303,9 +305,9 @@ void Simulation::gotoSelection(double gotoTime, double distance, Vec3f up, - astro::CoordinateSystem upFrame) + ObserverFrame::CoordinateSystem upCoordSys) { - activeObserver->gotoSelection(selection, gotoTime, distance, up, upFrame); + activeObserver->gotoSelection(selection, gotoTime, distance, up, upCoordSys); } void Simulation::gotoSelectionLongLat(double gotoTime, @@ -319,10 +321,11 @@ } -void Simulation::gotoLocation(const RigidTransform& transform, +void Simulation::gotoLocation(const UniversalCoord& position, + const Quatd& orientation, double duration) { - activeObserver->gotoLocation(transform, duration); + activeObserver->gotoLocation(position, orientation, duration); } Index: src/celengine/render.cpp =================================================================== --- src/celengine/render.cpp (revision 4095) +++ src/celengine/render.cpp (working copy) @@ -2163,7 +2163,7 @@ // Highlight the selected object highlightObject = sel; - Quatf cameraOrientation = observer.getOrientation(); + Quatf cameraOrientation = observer.getOrientationf(); // Set up the camera for star rendering; the units of this phase // are light years. @@ -2445,7 +2445,7 @@ Frustum frustum(degToRad(fov), (float) windowWidth / (float) windowHeight, MinNearPlaneDistance); - Mat3f viewMat = conjugate(observer.getOrientation()).toMatrix3(); + Mat3f viewMat = conjugate(observer.getOrientationf()).toMatrix3(); // Remove objects from the render list that lie completely outside the // view frustum. @@ -2898,7 +2898,7 @@ renderMarkers(*universe.getMarkers(), observer.getPosition(), - observer.getOrientation(), + observer.getOrientationf(), now); if ((renderFlags & ShowSmoothLines) != 0) @@ -7159,7 +7159,7 @@ Point3f starPos = sun.getPosition(); Point3d observerPos = astrocentricPosition(observer.getPosition(), sun, now); - Mat3f viewMat = observer.getOrientation().toMatrix3(); + Mat3f viewMat = observer.getOrientationf().toMatrix3(); Vec3f viewMatZ(viewMat[2][0], viewMat[2][1], viewMat[2][2]); Body* lastPrimary = NULL; @@ -7334,7 +7334,7 @@ } #endif - if (showLabels && (pos * conjugate(observer.getOrientation()).toMatrix3()).z < 0) + if (showLabels && (pos * conjugate(observer.getOrientationf()).toMatrix3()).z < 0) { float boundingRadiusSize = (float) (body->getOrbit()->getBoundingRadius() / distanceFromObserver) / pixelSize; if (boundingRadiusSize > minOrbitSize) @@ -7552,7 +7552,7 @@ if ((renderFlags & ShowOrbits) != 0 && ((orbitMask & Body::Stellar) != 0 || highlightObject.star() == &star)) { - Mat3f viewMat = observer.getOrientation().toMatrix3(); + Mat3f viewMat = observer.getOrientationf().toMatrix3(); Vec3f viewMatZ(viewMat[2][0], viewMat[2][1], viewMat[2][2]); if (star.getOrbit() != NULL) @@ -7830,7 +7830,7 @@ } else { - Mat3f viewMat = observer->getOrientation().toMatrix3(); + Mat3f viewMat = observer->getOrientationf().toMatrix3(); Vec3f viewMatZ(viewMat[2][0], viewMat[2][1], viewMat[2][2]); RenderListEntry rle; @@ -8022,7 +8022,7 @@ } else { - Mat3f viewMat = observer->getOrientation().toMatrix3(); + Mat3f viewMat = observer->getOrientationf().toMatrix3(); Vec3f viewMatZ(viewMat[2][0], viewMat[2][1], viewMat[2][2]); RenderListEntry rle; @@ -8075,7 +8075,7 @@ starRenderer.starDB = &starDB; starRenderer.observer = &observer; starRenderer.obsPos = obsPos; - starRenderer.viewNormal = Vec3f(0, 0, -1) * observer.getOrientation().toMatrix3(); + starRenderer.viewNormal = Vec3f(0, 0, -1) * observer.getOrientationf().toMatrix3(); starRenderer.glareParticles = &glareParticles; starRenderer.renderList = &renderList; starRenderer.starVertexBuffer = starVertexBuffer; @@ -8119,7 +8119,7 @@ glareParticles.clear(); - starVertexBuffer->setBillboardOrientation(observer.getOrientation()); + starVertexBuffer->setBillboardOrientation(observer.getOrientationf()); glEnable(GL_TEXTURE_2D); @@ -8142,7 +8142,7 @@ } starDB.findVisibleStars(starRenderer, obsPos, - observer.getOrientation(), + observer.getOrientationf(), degToRad(fov), (float) windowWidth / (float) windowHeight, faintestMagNight); @@ -8153,7 +8153,7 @@ starRenderer.starVertexBuffer->finish(); gaussianGlareTex->bind(); - renderParticles(glareParticles, observer.getOrientation()); + renderParticles(glareParticles, observer.getOrientationf()); } @@ -8169,7 +8169,7 @@ starRenderer.starDB = &starDB; starRenderer.observer = &observer; starRenderer.obsPos = obsPos; - starRenderer.viewNormal = Vec3f(0, 0, -1) * observer.getOrientation().toMatrix3(); + starRenderer.viewNormal = Vec3f(0, 0, -1) * observer.getOrientationf().toMatrix3(); starRenderer.renderList = &renderList; starRenderer.starVertexBuffer = pointStarVertexBuffer; starRenderer.glareVertexBuffer = glareVertexBuffer; @@ -8216,7 +8216,7 @@ starDB.findVisibleStars(starRenderer, obsPos, - observer.getOrientation(), + observer.getOrientationf(), degToRad(fov), (float) windowWidth / (float) windowHeight, faintestMagNight); @@ -8334,7 +8334,7 @@ dso->render(*context, relPos, - observer->getOrientation(), + observer->getOrientationf(), (float) brightness, pixelSize); glPopMatrix(); @@ -8419,10 +8419,10 @@ dsoRenderer.context = context; dsoRenderer.renderer = this; dsoRenderer.dsoDB = dsoDB; - dsoRenderer.orientationMatrix = conjugate(observer.getOrientation()).toMatrix3(); + dsoRenderer.orientationMatrix = conjugate(observer.getOrientationf()).toMatrix3(); dsoRenderer.observer = &observer; dsoRenderer.obsPos = obsPos; - dsoRenderer.viewNormal = Vec3f(0, 0, -1) * observer.getOrientation().toMatrix3(); + dsoRenderer.viewNormal = Vec3f(0, 0, -1) * observer.getOrientationf().toMatrix3(); dsoRenderer.fov = fov; // size/pixelSize =0.86 at 120deg, 1.43 at 45deg and 1.6 at 0deg. dsoRenderer.size = pixelSize * 1.6f / corrFac; @@ -8456,7 +8456,7 @@ dsoDB->findVisibleDSOs(dsoRenderer, obsPos, - observer.getOrientation(), + observer.getOrientationf(), degToRad(fov), (float) windowWidth / (float) windowHeight, 2 * faintestMagNight); @@ -8514,7 +8514,7 @@ glLineWidth(1.0f); - Mat3f m = conjugate(observer.getOrientation()).toMatrix3(); + Mat3f m = conjugate(observer.getOrientationf()).toMatrix3(); // Show the declination labels for (i = 0; i < DecLabelCount; i++) @@ -8573,7 +8573,7 @@ Vec3f rpos = Point3f(avg.x, avg.y, avg.z) - observerPos; - if ((observer.getOrientation().toMatrix3() * rpos).z < 0) + if ((observer.getOrientationf().toMatrix3() * rpos).z < 0) { // We'll linearly fade the labels as a function of the // observer's distance to the origin of coordinates: Index: src/celengine/frame.cpp =================================================================== --- src/celengine/frame.cpp (revision 4095) +++ src/celengine/frame.cpp (working copy) @@ -13,266 +13,120 @@ using namespace std; -RigidTransform FrameOfReference::toUniversal(const RigidTransform& xform, - double t) const -{ - // Handle the easy case . . . - if (coordSys == astro::Universal) - return xform; - UniversalCoord origin = refObject.getPosition(t); +/*** ReferenceFrame ***/ - if (coordSys == astro::Geographic) +ReferenceFrame::ReferenceFrame(Selection center) : + centerObject(center), + refCount(0) { - Quatd rotation(1, 0, 0, 0); - switch (refObject.getType()) - { - case Selection::Type_Body: - rotation = refObject.body()->getEclipticalToBodyFixed(t); - break; - case Selection::Type_Star: - rotation = refObject.star()->getRotationModel()->orientationAtTime(t); - break; - case Selection::Type_Location: - if (refObject.location()->getParentBody() != NULL) - rotation = refObject.location()->getParentBody()->getEclipticalToBodyFixed(t); - break; - default: - break; } - Point3d p = (Point3d) xform.translation * rotation.toMatrix4(); - return RigidTransform(origin + Vec3d(p.x, p.y, p.z), - xform.rotation * rotation); - } - else if (coordSys == astro::PhaseLock) - { - Mat3d m; - Vec3d lookDir = refObject.getPosition(t) - targetObject.getPosition(t); - lookDir.normalize(); - switch (refObject.getType()) +int +ReferenceFrame::addRef() const { - case Selection::Type_Body: - { - Body* body = refObject.body(); - Vec3d axisDir = Vec3d(0, 1, 0) * body->getEclipticalToEquatorial(t).toMatrix3(); - Vec3d v = axisDir ^ lookDir; - v.normalize(); - Vec3d u = lookDir ^ v; - m = Mat3d(v, u, lookDir); + return ++refCount; } - break; - case Selection::Type_Star: - { - Star* star = refObject.star(); - Vec3d axisDir = Vec3d(0, 1, 0) * star->getRotationModel()->equatorOrientationAtTime(t).toMatrix3(); - Vec3d v = axisDir ^ lookDir; - v.normalize(); - Vec3d u = lookDir ^ v; - m = Mat3d(v, u, lookDir); - } - default: - return xform; - } - Point3d p = (Point3d) xform.translation * m; - return RigidTransform(origin + Vec3d(p.x, p.y, p.z), - xform.rotation * Quatd(m)); - } - else if (coordSys == astro::Chase) +int +ReferenceFrame::release() const { - Mat3d m; + --refCount; + assert(refCount >= 0); + if (refCount <= 0) + delete this; - switch (refObject.getType()) - { - case Selection::Type_Body: - { - Body* body = refObject.body(); - Vec3d lookDir = body->getOrbit()->positionAtTime(t) - - body->getOrbit()->positionAtTime(t - 1.0 / 1440.0); - Vec3d axisDir = Vec3d(0, 1, 0) * body->getEclipticalToEquatorial(t).toMatrix3(); - lookDir.normalize(); - Vec3d v = lookDir ^ axisDir; - v.normalize(); - Vec3d u = v ^ lookDir; - m = Mat3d(v, u, -lookDir); + return refCount; } - break; - default: - return xform; - } - Point3d p = (Point3d) xform.translation * m; - return RigidTransform(origin + Vec3d(p.x, p.y, p.z), - xform.rotation * Quatd(m)); - } - else +// High-precision rotation using 64.64 fixed point path. Rotate uc by +// the rotation specified by unit quaternion q. +static UniversalCoord rotate(const UniversalCoord& uc, const Quatd& q) { - return RigidTransform(origin + xform.translation, xform.rotation); - } -} + Mat3d r = q.toMatrix3(); + UniversalCoord uc1; + uc1.x = uc.x * BigFix(r[0].x) + uc.y * BigFix(r[1].x) + uc.z * BigFix(r[2].x); + uc1.y = uc.x * BigFix(r[0].y) + uc.y * BigFix(r[1].y) + uc.z * BigFix(r[2].y); + uc1.z = uc.x * BigFix(r[0].z) + uc.y * BigFix(r[1].z) + uc.z * BigFix(r[2].z); -RigidTransform FrameOfReference::fromUniversal(const RigidTransform& xform, - double t) const -{ - // Handle the easy case . . . - if (coordSys == astro::Universal) - return xform; - UniversalCoord origin = refObject.getPosition(t); - - if (coordSys == astro::Geographic) - { - Quatd rotation(1, 0, 0, 0); - switch (refObject.getType()) - { - case Selection::Type_Body: - rotation = refObject.body()->getEclipticalToBodyFixed(t); - break; - case Selection::Type_Star: - rotation = refObject.star()->getRotationModel()->orientationAtTime(t); - break; - case Selection::Type_Location: - if (refObject.location()->getParentBody() != NULL) - rotation = refObject.location()->getParentBody()->getEclipticalToBodyFixed(t); - break; - default: - break; + return uc1; } - Vec3d v = (xform.translation - origin) * (~rotation).toMatrix4(); - return RigidTransform(UniversalCoord(v.x, v.y, v.z), - xform.rotation * ~rotation); - } - else if (coordSys == astro::PhaseLock) - { - Mat3d m; - Vec3d lookDir = refObject.getPosition(t) - targetObject.getPosition(t); - lookDir.normalize(); - switch (refObject.getType()) +/*! Convert from universal coordinates to frame coordinates. This method + * uses 64.64 fixed point arithmetic in conversion, and is thus /much/ slower + * than convertFromAstrocentric(), which works with double precision + * floating points values. For cases when the bodies are all in the same + * solar system, convertFromAstrocentric() should be used. + */ +UniversalCoord +ReferenceFrame::convertFromUniversal(const UniversalCoord& uc, double tjd) const { - case Selection::Type_Body: - { - Body* body = refObject.body(); - Vec3d axisDir = Vec3d(0, 1, 0) * body->getEclipticalToEquatorial(t).toMatrix3(); - Vec3d v = axisDir ^ lookDir; - v.normalize(); - Vec3d u = lookDir ^ v; - m = Mat3d(v, u, lookDir); + UniversalCoord uc1 = uc.difference(centerObject.getPosition(tjd)); + return rotate(uc1, conjugate(getOrientation(tjd))); } - break; - case Selection::Type_Star: - { - Star* star = refObject.star(); - Vec3d axisDir = Vec3d(0, 1, 0) * star->getRotationModel()->equatorOrientationAtTime(t).toMatrix3(); - Vec3d v = axisDir ^ lookDir; - v.normalize(); - Vec3d u = lookDir ^ v; - m = Mat3d(v, u, lookDir); - } - default: - return xform; - } - - Vec3d v = (xform.translation - origin) * m.transpose(); - - return RigidTransform(UniversalCoord(v.x, v.y, v.z), - xform.rotation * ~Quatd(m)); - } - else if (coordSys == astro::Chase) +Quatd +ReferenceFrame::convertFromUniversal(const Quatd& q, double tjd) const { - Mat3d m; - - switch (refObject.getType()) - { - case Selection::Type_Body: - { - Body* body = refObject.body(); - Vec3d lookDir = body->getOrbit()->positionAtTime(t) - - body->getOrbit()->positionAtTime(t - 1.0 / 1440.0); - Vec3d axisDir = Vec3d(0, 1, 0) * body->getEclipticalToEquatorial(t).toMatrix3(); - lookDir.normalize(); - Vec3d v = lookDir ^ axisDir; - v.normalize(); - Vec3d u = v ^ lookDir; - m = Mat3d(v, u, -lookDir); + return q * conjugate(getOrientation(tjd)); } - break; - default: - return xform; - } - Vec3d v = (xform.translation - origin) * m.transpose(); - - return RigidTransform(UniversalCoord(v.x, v.y, v.z), - xform.rotation * ~Quatd(m)); - } - else +/*! Convert from local coordinates to universal coordinates. This method + * uses 64.64 fixed point arithmetic in conversion, and is thus /much/ slower + * than convertFromAstrocentric(), which works with double precision + * floating points values. For cases when the bodies are all in the same + * solar system, convertFromAstrocentric() should be used. + * + * To get the position of a solar system object in universal coordinates, + * it usually suffices to get the astrocentric position and then add that + * to the position of the star in universal coordinates. This avoids any + * expensive high-precision multiplication. + */ +UniversalCoord +ReferenceFrame::convertToUniversal(const UniversalCoord& uc, double tjd) const { - return RigidTransform(xform.translation.difference(origin), - xform.rotation); + return centerObject.getPosition(tjd) + rotate(uc, getOrientation(tjd)); } -} -/*** ReferenceFrame ***/ - -ReferenceFrame::ReferenceFrame(Selection center) : - centerObject(center), - refCount(0) +Quatd +ReferenceFrame::convertToUniversal(const Quatd& q, double tjd) const { + return q * getOrientation(tjd); } -int -ReferenceFrame::addRef() const +Point3d +ReferenceFrame::convertFromAstrocentric(const Point3d& p, double tjd) const { - return ++refCount; -} - - -int -ReferenceFrame::release() const + Point3d center; + if (centerObject.getType() == Selection::Type_Body) { - --refCount; - assert(refCount >= 0); - if (refCount <= 0) - delete this; - - return refCount; + Point3d center = centerObject.body()->getHeliocentricPosition(tjd); + return Point3d(0.0, 0.0, 0.0) + (p - center) * conjugate(getOrientation(tjd)).toMatrix3(); } - - -// TODO: Not correct; requires BigFix * double multiplication -UniversalCoord -ReferenceFrame::convertFrom(const UniversalCoord& uc, double tjd) const + else if (centerObject.getType() == Selection::Type_Star) { - UniversalCoord center = centerObject.getPosition(tjd); - Vec3d relative = uc - center; - - return center + relative * getOrientation(tjd).toMatrix3(); + return p * conjugate(getOrientation(tjd)).toMatrix3(); } - - -// TODO: Not correct; requires BigFix * double multiplication -UniversalCoord -ReferenceFrame::convertTo(const UniversalCoord& uc, double tjd) const + else { - UniversalCoord center = centerObject.getPosition(tjd); - Vec3d relative = uc - center; - - return center + relative * conjugate(getOrientation(tjd)).toMatrix3(); + // TODO: + // bad if the center object is a galaxy + // what about locations? + return Point3d(0.0, 0.0, 0.0); } +} Point3d -ReferenceFrame::convertFromAstrocentric(const Point3d& p, double tjd) const +ReferenceFrame::convertToAstrocentric(const Point3d& p, double tjd) const { Point3d center; if (centerObject.getType() == Selection::Type_Body) Index: src/celengine/observer.h =================================================================== --- src/celengine/observer.h (revision 4095) +++ src/celengine/observer.h (working copy) @@ -20,35 +20,102 @@ #include +class ObserverFrame +{ +public: + enum CoordinateSystem + { + Universal = 0, + Ecliptical = 1, + Equatorial = 2, + BodyFixed = 3, + PhaseLock = 5, + Chase = 6, + + // Previous versions of PhaseLock and Chase used the + // spin axis of the reference object as a secondary + // vector for the coordinate system. + PhaseLock_Old = 100, + Chase_Old = 101, + + // ObserverLocal is not a real frame; it's an optional + // way to specify view vectors. Eventually, there will + // be some other way to accomplish this and ObserverLocal + // will go away. + ObserverLocal = 200, + + Unknown = 1000, + }; + + ObserverFrame(); + ObserverFrame(CoordinateSystem cs, + const Selection& _refObject, + const Selection& _targetObj = Selection()); + ObserverFrame(const ObserverFrame&); + + ~ObserverFrame(); + + ObserverFrame& operator=(const ObserverFrame& f); + + CoordinateSystem getCoordinateSystem() const; + Selection getRefObject() const; + Selection getTargetObject() const; + + const ReferenceFrame* getFrame() const; + + UniversalCoord convertFromUniversal(const UniversalCoord& uc, double tjd) const; + UniversalCoord convertToUniversal(const UniversalCoord& uc, double tjd) const; + Quatd convertFromUniversal(const Quatd& q, double tjd) const; + Quatd convertToUniversal(const Quatd& q, double tjd) const; + + static UniversalCoord convert(const ObserverFrame* fromFrame, + const ObserverFrame* toFrame, + const UniversalCoord& uc, + double t); + static Quatd convert(const ObserverFrame* fromFrame, + const ObserverFrame* toFrame, + const Quatd& q, + double t); + +private: + ReferenceFrame* createFrame(CoordinateSystem _coordSys, + const Selection& _refObject, + const Selection& _targetObject); + +private: + CoordinateSystem coordSys; + ReferenceFrame* frame; + Selection targetObject; +}; + + +/*! ObserverFrame is a wrapper class for ReferenceFrame which adds some + * annotation data. The goal is to place some restrictions on what reference + * frame can be set for an observer. General reference frames can be + * arbitrarily complex, with multiple levels of nesting. This makes it + * difficult to store them in a cel:// URL or display information about + * them for the user. The restricted set of reference frames wrapped by + * the ObserverFrame class does not suffer from such problems. + */ class Observer { public: Observer(); - // The getPosition method returns the observer's position in micro light - // years. UniversalCoord getPosition() const; + void setPosition(const UniversalCoord&); + void setPosition(const Point3d&); - // getRelativePosition returns in units of kilometers the difference - // between the position of the observer and a location specified in - // light years. - Point3d getRelativePosition(const Point3d&) const; - - Quatf getOrientation() const; + Quatd getOrientation() const; + Quatf getOrientationf() const; void setOrientation(const Quatf&); void setOrientation(const Quatd&); + Vec3d getVelocity() const; void setVelocity(const Vec3d&); Vec3f getAngularVelocity() const; void setAngularVelocity(const Vec3f&); - void setPosition(BigFix x, BigFix y, BigFix z); - void setPosition(const UniversalCoord&); - void setPosition(const Point3d&); - - RigidTransform getSituation() const; - void setSituation(const RigidTransform&); - float getFOV() const; void setFOV(float); @@ -75,30 +142,31 @@ void gotoSelection(const Selection&, double gotoTime, Vec3f up, - astro::CoordinateSystem upFrame); + ObserverFrame::CoordinateSystem upFrame); void gotoSelection(const Selection&, double gotoTime, double startInter, double endInter, Vec3f up, - astro::CoordinateSystem upFrame); + ObserverFrame::CoordinateSystem upFrame); void gotoSelectionGC(const Selection&, double gotoTime, double startInter, double endInter, Vec3f up, - astro::CoordinateSystem upFrame); + ObserverFrame::CoordinateSystem upFrame); void gotoSelection(const Selection&, double gotoTime, double distance, Vec3f up, - astro::CoordinateSystem upFrame); + ObserverFrame::CoordinateSystem upFrame); void gotoSelectionLongLat(const Selection&, double gotoTime, double distance, float longitude, float latitude, Vec3f up); - void gotoLocation(const RigidTransform& transform, + void gotoLocation(const UniversalCoord& toPosition, + const Quatd& toOrientation, double duration); void getSelectionLongLat(const Selection&, double& distance, @@ -108,7 +176,7 @@ double gotoTime, double distance, Vec3f up, - astro::CoordinateSystem upFrame); + ObserverFrame::CoordinateSystem upFrame); void gotoSurface(const Selection&, double duration); void centerSelection(const Selection&, double centerTime = 0.5); void centerSelectionCO(const Selection&, double centerTime = 0.5); @@ -120,15 +188,19 @@ void reverseOrientation(); - void setFrame(const FrameOfReference&); - FrameOfReference getFrame() const; + void setFrame(ObserverFrame::CoordinateSystem cs, const Selection& refObj, const Selection& targetObj); + void setFrame(ObserverFrame::CoordinateSystem cs, const Selection& refObj); + void setFrame(const ObserverFrame& f); + const ObserverFrame* getFrame() const; + double getArrivalTime() const; double getTime() const; void setTime(double); - enum ObserverMode { + enum ObserverMode + { Free = 0, Travelling = 1, }; @@ -136,7 +208,8 @@ ObserverMode getMode() const; void setMode(ObserverMode); - enum TrajectoryType { + enum TrajectoryType + { Linear = 0, GreatCircle = 1, CircularOrbit = 2, @@ -148,13 +221,13 @@ double startTime; UniversalCoord from; UniversalCoord to; - Quatf initialOrientation; - Quatf finalOrientation; + Quatd initialOrientation; + Quatd finalOrientation; double startInterpolation; // start of orientation interpolation phase [0-1] double endInterpolation; // end of orientation interpolation phase [0-1] double expFactor; double accelTime; - Quatf rotation1; // rotation on the CircularOrbit around centerObject + Quatd rotation1; // rotation on the CircularOrbit around centerObject Selection centerObject; @@ -171,18 +244,18 @@ double startInter, double endInter, Vec3d offset, - astro::CoordinateSystem offsetFrame, + ObserverFrame::CoordinateSystem offsetFrame, Vec3f up, - astro::CoordinateSystem upFrame); + ObserverFrame::CoordinateSystem upFrame); void computeGotoParametersGC(const Selection& sel, JourneyParams& jparams, double gotoTime, double startInter, double endInter, Vec3d offset, - astro::CoordinateSystem offsetFrame, + ObserverFrame::CoordinateSystem offsetFrame, Vec3f up, - astro::CoordinateSystem upFrame, + ObserverFrame::CoordinateSystem upFrame, const Selection& centerObj); void computeCenterParameters(const Selection& sel, JourneyParams& jparams, @@ -191,13 +264,25 @@ JourneyParams& jparams, double centerTime); + void updateUniversal(); + void convertFrameCoordinates(const ObserverFrame* newFrame); + private: double simTime; - RigidTransform situation; + // Position, orientation, and velocity in the observer's reference frame + UniversalCoord position; + Quatd orientation; Vec3d velocity; Vec3f angularVelocity; + // Position and orientation in universal coordinates, derived from the + // equivalent quantities in the observer reference frame. + UniversalCoord positionUniv; + Quatd orientationUniv; + + ObserverFrame* frame; + double realTime; double targetSpeed; @@ -207,10 +292,9 @@ ObserverMode observerMode; JourneyParams journey; - FrameOfReference frame; Selection trackObject; - Quatf trackingOrientation; // orientation prior to selecting tracking + Quatd trackingOrientation; // orientation prior to selecting tracking float fov; bool reverseFlag; Index: src/celengine/astro.h =================================================================== --- src/celengine/astro.h (revision 4095) +++ src/celengine/astro.h (working copy) @@ -60,18 +60,6 @@ bool parseDate(const std::string&, Date&); - enum CoordinateSystem - { - Universal = 0, - Ecliptical = 1, - Equatorial = 2, - Geographic = 3, - ObserverLocal = 4, - PhaseLock = 5, - Chase = 6, - }; - - // Time scale conversions // UTC - Coordinated Universal Time // TAI - International Atomic Time Index: src/celengine/cmdparser.cpp =================================================================== --- src/celengine/cmdparser.cpp (revision 4095) +++ src/celengine/cmdparser.cpp (working copy) @@ -116,24 +116,24 @@ } -static astro::CoordinateSystem parseCoordinateSystem(const string& name) +static ObserverFrame::CoordinateSystem parseCoordinateSystem(const string& name) { if (compareIgnoringCase(name, "observer") == 0) - return astro::ObserverLocal; + return ObserverFrame::ObserverLocal; else if (compareIgnoringCase(name, "geographic") == 0) - return astro::Geographic; + return ObserverFrame::BodyFixed; else if (compareIgnoringCase(name, "equatorial") == 0) - return astro::Equatorial; + return ObserverFrame::Equatorial; else if (compareIgnoringCase(name, "ecliptical") == 0) - return astro::Ecliptical; + return ObserverFrame::Ecliptical; else if (compareIgnoringCase(name, "universal") == 0) - return astro::Universal; + return ObserverFrame::Universal; else if (compareIgnoringCase(name, "lock") == 0) - return astro::PhaseLock; + return ObserverFrame::PhaseLock; else if (compareIgnoringCase(name, "chase") == 0) - return astro::Chase; + return ObserverFrame::Chase; else - return astro::ObserverLocal; + return ObserverFrame::ObserverLocal; } @@ -198,7 +198,7 @@ string targetName; paramList->getString("target", targetName); string coordSysName; - astro::CoordinateSystem coordSys = astro::Universal; + ObserverFrame::CoordinateSystem coordSys = ObserverFrame::Universal; if (paramList->getString("coordsys", coordSysName)) coordSys = parseCoordinateSystem(coordSysName); @@ -217,7 +217,7 @@ double distance = 5.0; paramList->getNumber("distance", distance); - astro::CoordinateSystem upFrame = astro::ObserverLocal; + ObserverFrame::CoordinateSystem upFrame = ObserverFrame::ObserverLocal; string frameString; if (paramList->getString("upframe", frameString)) upFrame = parseCoordinateSystem(frameString); Index: src/celengine/observer.cpp =================================================================== --- src/celengine/observer.cpp (revision 4095) +++ src/celengine/observer.cpp (working copy) @@ -1,6 +1,6 @@ // observer.cpp // -// Copyright (C) 2001-2006, Chris Laurel +// Copyright (C) 2001-2008, Chris Laurel // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -18,7 +18,6 @@ using namespace std; -#define LY 9466411842000.000 #define VELOCITY_CHANGE_TIME 0.25f @@ -40,10 +39,26 @@ } +/*! Notes on the Observer class + * The values position and orientation are in observer's reference frame. positionUniv + * and orientationUniv are the equivalent values in the universal coordinate system. + * They must be kept in sync. Generally, it's position and orientation that are modified; + * after they're changed, the method updateUniversal is called. However, when the observer + * frame is changed, positionUniv and orientationUniv are not changed, but the position + * and orientation within the frame /do/ change. Thus, a 'reverse' update is necessary. + * + * There are two types of 'automatic' updates to position and orientation that may + * occur when the observer's update method is called: updates from free travel, and + * updates due to an active goto operation. + */ + Observer::Observer() : simTime(0.0), + position(0.0, 0.0, 0.0), + orientation(1.0), velocity(0.0, 0.0, 0.0), angularVelocity(0.0f, 0.0f, 0.0f), + frame(NULL), realTime(0.0), targetSpeed(0.0), targetVelocity(0.0, 0.0, 0.0), @@ -55,70 +70,106 @@ reverseFlag(false), locationFilter(~0u) { + frame = new ObserverFrame(); + updateUniversal(); } +/*! Get the current simulation time. The time returned is a Julian date, + * and the time standard is TDB. + */ double Observer::getTime() const { return simTime; }; + +/*! Set the simulation time (Julian date, TDB time standard) +*/ void Observer::setTime(double jd) { simTime = jd; } +/*! Return the position of the observer in universal coordinates. The origin + * The origin of this coordinate system is the Solar System Barycenter, and + * axes are defined by the J2000 ecliptic and equinox. + */ UniversalCoord Observer::getPosition() const { - // TODO: Optimize this! Dirty bit should be set by Simulation::setTime and - // by Observer::update(), Observer::setOrientation(), Observer::setPosition() - return frame.toUniversal(situation, getTime()).translation; + return positionUniv; } -Point3d Observer::getRelativePosition(const Point3d& p) const +// TODO: Low-precision set position that should be removed +void Observer::setPosition(const Point3d& p) { - BigFix x(p.x); - BigFix y(p.y); - BigFix z(p.z); + setPosition(UniversalCoord(p)); +} - UniversalCoord position = getPosition(); - double dx = (double) (position.x - x); - double dy = (double) (position.y - y); - double dz = (double) (position.z - z); - return Point3d(dx / LY, dy / LY, dz / LY); +/*! Set the position of the observer; position is specified in the universal + * coordinate system. + */ +void Observer::setPosition(const UniversalCoord& p) +{ + positionUniv = p; + position = frame->convertFromUniversal(p, getTime()); } -Quatf Observer::getOrientation() const +/*! Return the orientation of the observer in the universal coordinate + * system. + */ +Quatd Observer::getOrientation() const { - Quatd q = frame.toUniversal(situation, getTime()).rotation; + return orientationUniv; +} + +/*! Reduced precision version of getOrientation() + */ +Quatf Observer::getOrientationf() const +{ + Quatd q = getOrientation(); return Quatf((float) q.w, (float) q.x, (float) q.y, (float) q.z); } +/* Set the orientation of the observer. The orientation is specified in + * the universal coordinate system. + */ void Observer::setOrientation(const Quatf& q) { + /* RigidTransform rt = frame.toUniversal(situation, getTime()); rt.rotation = Quatd(q.w, q.x, q.y, q.z); situation = frame.fromUniversal(rt, getTime()); + */ + setOrientation(Quatd(q.w, q.x, q.y, q.z)); } +/*! Set the orientation of the observer. The orientation is specified in + * the universal coordinate system. + */ void Observer::setOrientation(const Quatd& q) { - setOrientation(Quatf((float) q.w, (float) q.x, (float) q.y, (float) q.z)); + orientationUniv = q; + orientation = frame->convertFromUniversal(q, getTime()); } +/*! Get the velocity of the observer within the observer's reference frame. + */ Vec3d Observer::getVelocity() const { return velocity; } +/*! Set the velocity of the observer within the observer's reference frame. +*/ void Observer::setVelocity(const Vec3d& v) { velocity = v; @@ -137,76 +188,6 @@ } -void Observer::setPosition(const Point3d& p) -{ - setPosition(UniversalCoord(p)); -} - - -void Observer::setPosition(const UniversalCoord& p) -{ - RigidTransform rt = frame.toUniversal(situation, getTime()); - rt.translation = p; - situation = frame.fromUniversal(rt, getTime()); -} - - -RigidTransform Observer::getSituation() const -{ - return frame.toUniversal(situation, getTime()); -} - - - -void Observer::setSituation(const RigidTransform& xform) -{ - situation = frame.fromUniversal(xform, getTime()); -} - - -Vec3d toUniversal(const Vec3d& v, - const Observer& observer, - const Selection& sel, - double t, - astro::CoordinateSystem frame) -{ - switch (frame) - { - case astro::ObserverLocal: - { - Quatf q = observer.getOrientation(); - Quatd qd(q.w, q.x, q.y, q.z); - return v * qd.toMatrix3(); - } - - - case astro::Geographic: - if (sel.getType() != Selection::Type_Body) - return v; - else - return v * sel.body()->getBodyFixedToHeliocentric(t); - - case astro::Equatorial: - if (sel.getType() != Selection::Type_Body) - return v; - else - return v * sel.body()->getLocalToHeliocentric(t); - - case astro::Ecliptical: - // TODO: Multiply this by the planetary system's ecliptic orientation, - // once this field is added. - return v; - - case astro::Universal: - return v; - - default: - // assert(0); - return v; - } -} - - /*! Determine an orientation that will make the negative z-axis point from * from the observer to the target, with the y-axis pointing in direction * of the component of 'up' that is orthogonal to the z-axis. @@ -235,7 +216,10 @@ } -// Tick the simulation by dt seconds +/*! Tick the simulation by dt seconds. Update the observer position + * and orientation due to an active goto command or non-zero velocity + * or angular velocity. + */ void Observer::update(double dt, double timeScale) { realTime += dt; @@ -294,7 +278,7 @@ } else if (journey.traj == GreatCircle) { - Selection centerObj = frame.refObject; + Selection centerObj = frame->getRefObject(); if (centerObj.body() != NULL) { Body* body = centerObj.body(); @@ -307,8 +291,8 @@ } } - UniversalCoord ufrom = frame.toUniversal(RigidTransform(journey.from, Quatf(1.0f)), simTime).translation; - UniversalCoord uto = frame.toUniversal(RigidTransform(journey.to, Quatf(1.0f)), simTime).translation; + UniversalCoord ufrom = frame->convertToUniversal(journey.from, simTime); + UniversalCoord uto = frame->convertToUniversal(journey.to, simTime); UniversalCoord origin = centerObj.getPosition(simTime); Vec3d v0 = ufrom - origin; Vec3d v1 = uto - origin; @@ -327,16 +311,15 @@ else v = slerp(x, v1, v0); - p = origin + v; - p = frame.fromUniversal(RigidTransform(p, Quatf(1.0f)), simTime).translation; + p = frame->convertFromUniversal(origin + v, simTime); } } else if (journey.traj == CircularOrbit) { - Selection centerObj = frame.refObject; + Selection centerObj = frame->getRefObject(); - UniversalCoord ufrom = frame.toUniversal(RigidTransform(journey.from, Quatf(1.0f)), simTime).translation; - UniversalCoord uto = frame.toUniversal(RigidTransform(journey.to, Quatf(1.0f)), simTime).translation; + UniversalCoord ufrom = frame->convertToUniversal(journey.from, simTime); + UniversalCoord uto = frame->convertToUniversal(journey.to, simTime); UniversalCoord origin = centerObj.getPosition(simTime); Vec3d v0 = ufrom - origin; Vec3d v1 = uto - origin; @@ -352,16 +335,14 @@ Quatd q1(journey.rotation1.w, journey.rotation1.x, journey.rotation1.y, journey.rotation1.z); p = origin + v0 * Quatd::slerp(q0, q1, t).toMatrix3(); - - p = frame.fromUniversal(RigidTransform(p, Quatf(1.0f)), - simTime).translation; + p = frame->convertFromUniversal(p, simTime); } } } // Spherically interpolate the orientation over the first half // of the journey. - Quatf orientation; + Quatd q; if (t >= journey.startInterpolation && t < journey.endInterpolation ) { // Smooth out the interpolation to avoid jarring changes in @@ -383,31 +364,36 @@ if (norm(journey.initialOrientation - journey.finalOrientation) < norm(journey.initialOrientation + journey.finalOrientation)) { - orientation = Quatf::slerp(journey.initialOrientation, - journey.finalOrientation, (float)v); + q = Quatd::slerp(journey.initialOrientation, + journey.finalOrientation, v); } else { - orientation = Quatf::slerp(journey.initialOrientation, - -journey.finalOrientation,(float)v); + q = Quatd::slerp(journey.initialOrientation, + -journey.finalOrientation, v); } } else if (t < journey.startInterpolation) { - orientation = journey.initialOrientation; + q = journey.initialOrientation; } else // t >= endInterpolation { - orientation = journey.finalOrientation; + q = journey.finalOrientation; } - situation = RigidTransform(p, orientation); + position = p; + orientation = q; // If the journey's complete, reset to manual control if (t == 1.0f) { if (journey.traj != CircularOrbit) - situation = RigidTransform(journey.to, journey.finalOrientation); + { + //situation = RigidTransform(journey.to, journey.finalOrientation); + position = journey.to; + orientation = journey.finalOrientation; + } observerMode = Free; setVelocity(Vec3d(0, 0, 0)); // targetVelocity = Vec3d(0, 0, 0); @@ -427,27 +413,29 @@ } // Update the position - situation.translation = situation.translation + getVelocity() * dt; + position = position + getVelocity() * dt; if (observerMode == Free) { // Update the observer's orientation Vec3f fAV = getAngularVelocity(); Vec3d AV(fAV.x, fAV.y, fAV.z); - Quatd dr = 0.5 * (AV * situation.rotation); - situation.rotation += dt * dr; - situation.rotation.normalize(); + Quatd dr = 0.5 * (AV * orientation); + orientation += dt * dr; + orientation.normalize(); } if (!trackObject.empty()) { - Vec3f up = Vec3f(0, 1, 0) * getOrientation().toMatrix3(); + Vec3d up = Vec3d(0, 1, 0) * getOrientation().toMatrix3(); Vec3d viewDir = trackObject.getPosition(getTime()) - getPosition(); setOrientation(lookAt(Point3d(0, 0, 0), Point3d(viewDir.x, viewDir.y, viewDir.z), - Vec3d(up.x, up.y, up.z))); + up)); } + + updateUniversal(); } @@ -489,8 +477,8 @@ void Observer::reverseOrientation() { - Quatf q = getOrientation(); - q.yrotate((float) PI); + Quatd q = getOrientation(); + q.yrotate(PI); setOrientation(q); reverseFlag = !reverseFlag; } @@ -517,17 +505,18 @@ double startInter, double endInter, Vec3d offset, - astro::CoordinateSystem offsetFrame, + ObserverFrame::CoordinateSystem offsetCoordSys, Vec3f up, - astro::CoordinateSystem upFrame) + ObserverFrame::CoordinateSystem upCoordSys) { - if (frame.coordSys == astro::PhaseLock) + if (frame->getCoordinateSystem() == ObserverFrame::PhaseLock) { - setFrame(FrameOfReference(astro::Ecliptical, destination)); + //setFrame(FrameOfReference(astro::Ecliptical, destination)); + setFrame(ObserverFrame::Ecliptical, destination); } else { - setFrame(FrameOfReference(frame.coordSys, destination)); + setFrame(frame->getCoordinateSystem(), destination); } UniversalCoord targetPosition = destination.getPosition(getTime()); @@ -541,17 +530,32 @@ // Right where we are now . . . jparams.from = getPosition(); - offset = toUniversal(offset, *this, destination, getTime(), offsetFrame); + if (offsetCoordSys == ObserverFrame::ObserverLocal) + { + offset = offset * orientationUniv.toMatrix3(); + } + else + { + ObserverFrame offsetFrame(offsetCoordSys, destination); + offset = offset * offsetFrame.getFrame()->getOrientation(getTime()).toMatrix3(); + } jparams.to = targetPosition + offset; Vec3d upd(up.x, up.y, up.z); - upd = toUniversal(upd, *this, destination, getTime(), upFrame); - Vec3f upf = Vec3f((float) upd.x, (float) upd.y, (float) upd.z); + if (upCoordSys == ObserverFrame::ObserverLocal) + { + upd = upd * orientationUniv.toMatrix3(); + } + else + { + ObserverFrame upFrame(upCoordSys, destination); + upd = upd * upFrame.getFrame()->getOrientation(getTime()).toMatrix3(); + } jparams.initialOrientation = getOrientation(); Vec3d vn = targetPosition - jparams.to; - Point3f focus((float) vn.x, (float) vn.y, (float) vn.z); - jparams.finalOrientation = lookAt(Point3f(0, 0, 0), focus, upf); + Point3d focus(vn.x, vn.y, vn.z); + jparams.finalOrientation = lookAt(Point3d(0, 0, 0), focus, upd); jparams.startInterpolation = min(startInter, endInter); jparams.endInterpolation = max(startInter, endInter); @@ -563,20 +567,10 @@ jparams.expFactor = sol.first; // Convert to frame coordinates - RigidTransform from(jparams.from, jparams.initialOrientation); - from = frame.fromUniversal(from, getTime()); - jparams.from = from.translation; - jparams.initialOrientation= Quatf((float) from.rotation.w, - (float) from.rotation.x, - (float) from.rotation.y, - (float) from.rotation.z); - RigidTransform to(jparams.to, jparams.finalOrientation); - to = frame.fromUniversal(to, getTime()); - jparams.to = to.translation; - jparams.finalOrientation= Quatf((float) to.rotation.w, - (float) to.rotation.x, - (float) to.rotation.y, - (float) to.rotation.z); + jparams.from = frame->convertFromUniversal(jparams.from, getTime()); + jparams.initialOrientation = frame->convertFromUniversal(jparams.initialOrientation, getTime()); + jparams.to = frame->convertFromUniversal(jparams.to, getTime()); + jparams.finalOrientation = frame->convertFromUniversal(jparams.finalOrientation, getTime()); } @@ -586,12 +580,12 @@ double startInter, double endInter, Vec3d offset, - astro::CoordinateSystem offsetFrame, + ObserverFrame::CoordinateSystem offsetCoordSys, Vec3f up, - astro::CoordinateSystem upFrame, + ObserverFrame::CoordinateSystem upCoordSys, const Selection& centerObj) { - setFrame(FrameOfReference(frame.coordSys, destination)); + setFrame(frame->getCoordinateSystem(), destination); UniversalCoord targetPosition = destination.getPosition(getTime()); Vec3d v = targetPosition - getPosition(); @@ -606,17 +600,26 @@ // Right where we are now . . . jparams.from = getPosition(); - offset = toUniversal(offset, *this, destination, getTime(), offsetFrame); + ObserverFrame offsetFrame(offsetCoordSys, destination); + offset = offset * offsetFrame.getFrame()->getOrientation(getTime()).toMatrix3(); + jparams.to = targetPosition + offset; Vec3d upd(up.x, up.y, up.z); - upd = toUniversal(upd, *this, destination, getTime(), upFrame); - Vec3f upf = Vec3f((float) upd.x, (float) upd.y, (float) upd.z); + if (upCoordSys == ObserverFrame::ObserverLocal) + { + upd = upd * orientationUniv.toMatrix3(); + } + else + { + ObserverFrame upFrame(upCoordSys, destination); + upd = upd * upFrame.getFrame()->getOrientation(getTime()).toMatrix3(); + } jparams.initialOrientation = getOrientation(); Vec3d vn = targetPosition - jparams.to; - Point3f focus((float) vn.x, (float) vn.y, (float) vn.z); - jparams.finalOrientation = lookAt(Point3f(0, 0, 0), focus, upf); + Point3d focus(vn.x, vn.y, vn.z); + jparams.finalOrientation = lookAt(Point3d(0, 0, 0), focus, upd); jparams.startInterpolation = min(startInter, endInter); jparams.endInterpolation = max(startInter, endInter); @@ -628,20 +631,10 @@ jparams.expFactor = sol.first; // Convert to frame coordinates - RigidTransform from(jparams.from, jparams.initialOrientation); - from = frame.fromUniversal(from, getTime()); - jparams.from = from.translation; - jparams.initialOrientation= Quatf((float) from.rotation.w, - (float) from.rotation.x, - (float) from.rotation.y, - (float) from.rotation.z); - RigidTransform to(jparams.to, jparams.finalOrientation); - to = frame.fromUniversal(to, getTime()); - jparams.to = to.translation; - jparams.finalOrientation= Quatf((float) to.rotation.w, - (float) to.rotation.x, - (float) to.rotation.y, - (float) to.rotation.z); + jparams.from = frame->convertFromUniversal(jparams.from, getTime()); + jparams.initialOrientation = frame->convertFromUniversal(jparams.initialOrientation, getTime()); + jparams.to = frame->convertFromUniversal(jparams.to, getTime()); + jparams.finalOrientation = frame->convertFromUniversal(jparams.finalOrientation, getTime()); } @@ -659,12 +652,12 @@ jparams.from = getPosition(); jparams.to = jparams.from; - Vec3f up = Vec3f(0, 1, 0) * getOrientation().toMatrix4(); + Vec3d up = Vec3d(0, 1, 0) * getOrientation().toMatrix3(); jparams.initialOrientation = getOrientation(); Vec3d vn = targetPosition - jparams.to; - Point3f focus((float) vn.x, (float) vn.y, (float) vn.z); - jparams.finalOrientation = lookAt(Point3f(0, 0, 0), focus, up); + Point3d focus(vn.x, vn.y, vn.z); + jparams.finalOrientation = lookAt(Point3d(0, 0, 0), focus, up); jparams.startInterpolation = 0; jparams.endInterpolation = 1; @@ -673,21 +666,10 @@ jparams.expFactor = 0; // Convert to frame coordinates - RigidTransform from(jparams.from, jparams.initialOrientation); - from = frame.fromUniversal(from, getTime()); - jparams.from = from.translation; - jparams.initialOrientation= Quatf((float) from.rotation.w, - (float) from.rotation.x, - (float) from.rotation.y, - (float) from.rotation.z); - - RigidTransform to(jparams.to, jparams.finalOrientation); - to = frame.fromUniversal(to, getTime()); - jparams.to = to.translation; - jparams.finalOrientation= Quatf((float) to.rotation.w, - (float) to.rotation.x, - (float) to.rotation.y, - (float) to.rotation.z); + jparams.from = frame->convertFromUniversal(jparams.from, getTime()); + jparams.initialOrientation = frame->convertFromUniversal(jparams.initialOrientation, getTime()); + jparams.to = frame->convertFromUniversal(jparams.to, getTime()); + jparams.finalOrientation = frame->convertFromUniversal(jparams.finalOrientation, getTime()); } @@ -699,23 +681,21 @@ jparams.startTime = realTime; jparams.traj = CircularOrbit; - jparams.centerObject = frame.refObject; + jparams.centerObject = frame->getRefObject(); jparams.expFactor = 0.5; Vec3d v = destination.getPosition(getTime()) - getPosition(); - Vec3f wf = Vec3f(0.0, 0.0, -1.0) * getOrientation().toMatrix3(); - Vec3d w(wf.x, wf.y, wf.z); + Vec3d w = Vec3d(0.0, 0.0, -1.0) * getOrientation().toMatrix3(); v.normalize(); - Selection centerObj = frame.refObject; + Selection centerObj = frame->getRefObject(); UniversalCoord centerPos = centerObj.getPosition(getTime()); UniversalCoord targetPosition = destination.getPosition(getTime()); - Quatd qd = Quatd::vecToVecRotation(v, w); - Quatf q((float) qd.w, (float) qd.x, (float) qd.y, (float) qd.z); + Quatd q = Quatd::vecToVecRotation(v, w); jparams.from = getPosition(); - jparams.to = centerPos + ((getPosition() - centerPos) * qd.toMatrix3()); + jparams.to = centerPos + ((getPosition() - centerPos) * q.toMatrix3()); jparams.initialOrientation = getOrientation(); jparams.finalOrientation = getOrientation() * q; @@ -725,35 +705,19 @@ jparams.rotation1 = q; // Convert to frame coordinates - RigidTransform from(jparams.from, jparams.initialOrientation); - from = frame.fromUniversal(from, getTime()); - jparams.from = from.translation; - jparams.initialOrientation= Quatf((float) from.rotation.w, - (float) from.rotation.x, - (float) from.rotation.y, - (float) from.rotation.z); - - RigidTransform to(jparams.to, jparams.finalOrientation); - to = frame.fromUniversal(to, getTime()); - jparams.to = to.translation; - jparams.finalOrientation= Quatf((float) to.rotation.w, - (float) to.rotation.x, - (float) to.rotation.y, - (float) to.rotation.z); + jparams.from = frame->convertFromUniversal(jparams.from, getTime()); + jparams.initialOrientation = frame->convertFromUniversal(jparams.initialOrientation, getTime()); + jparams.to = frame->convertFromUniversal(jparams.to, getTime()); + jparams.finalOrientation = frame->convertFromUniversal(jparams.finalOrientation, getTime()); } -Observer::ObserverMode Observer::getMode() const -{ - return observerMode; -} - - -// Center the selection by moving on a circular orbit arround -// the primary body (refObject). +/*! Center the selection by moving on a circular orbit arround +* the primary body (refObject). +*/ void Observer::centerSelectionCO(const Selection& selection, double centerTime) { - if (!selection.empty() && !frame.refObject.empty()) + if (!selection.empty() && !frame->getRefObject().empty()) { computeCenterCOParameters(selection, journey, centerTime); observerMode = Travelling; @@ -761,67 +725,112 @@ } +Observer::ObserverMode Observer::getMode() const +{ + return observerMode; +} + + void Observer::setMode(Observer::ObserverMode mode) { observerMode = mode; } -void Observer::setFrame(const FrameOfReference& _frame) +// Private method to convert coordinates when a new observer frame is set. +// Universal coordinates remain the same. All frame coordinates get updated, including +// the goto parameters. +void Observer::convertFrameCoordinates(const ObserverFrame* newFrame) { - RigidTransform transform = frame.toUniversal(situation, getTime()); - if (observerMode == Travelling) + double now = getTime(); + + // Universal coordinates don't change. + // Convert frame coordinates to the new frame. + position = newFrame->convertFromUniversal(positionUniv, now); + orientation = newFrame->convertFromUniversal(orientationUniv, now); + + // Convert goto parameters to the new frame + journey.from = ObserverFrame::convert(frame, newFrame, journey.from, now); + journey.initialOrientation = ObserverFrame::convert(frame, newFrame, journey.initialOrientation, now); + journey.to = ObserverFrame::convert(frame, newFrame, journey.to, now); + journey.finalOrientation = ObserverFrame::convert(frame, newFrame, journey.finalOrientation, now); +} + + +/*! Set the observer's reference frame. The position of the observer in +* universal coordinates will not change. +*/ +void Observer::setFrame(ObserverFrame::CoordinateSystem cs, const Selection& refObj, const Selection& targetObj) { - RigidTransform from = frame.toUniversal(RigidTransform(journey.from, journey.initialOrientation), getTime()); - RigidTransform to = frame.toUniversal(RigidTransform(journey.to, journey.finalOrientation), getTime()); + ObserverFrame* newFrame = new ObserverFrame(cs, refObj, targetObj); + if (newFrame != NULL) + { + convertFrameCoordinates(newFrame); + delete frame; + frame = newFrame; + } +} - frame = _frame; - situation = frame.fromUniversal(transform, getTime()); - from = frame.fromUniversal(from, getTime()); - journey.from = from.translation; - journey.initialOrientation = Quatf((float) from.rotation.w, - (float) from.rotation.x, - (float) from.rotation.y, - (float) from.rotation.z); - to = frame.fromUniversal(to, getTime()); - journey.to = to.translation; - journey.finalOrientation = Quatf((float) to.rotation.w, - (float) to.rotation.x, - (float) to.rotation.y, - (float) to.rotation.z); +/*! Set the observer's reference frame. The position of the observer in +* universal coordinates will not change. +*/ +void Observer::setFrame(ObserverFrame::CoordinateSystem cs, const Selection& refObj) +{ + setFrame(cs, refObj, Selection()); } - else + + +/*! Set the observer's reference frame. The position of the observer in + * universal coordinates will not change. + */ +void Observer::setFrame(const ObserverFrame& f) { - frame = _frame; - situation = frame.fromUniversal(transform, getTime()); + if (frame != &f) + { + ObserverFrame* newFrame = new ObserverFrame(f); + + if (newFrame != NULL) + { + convertFrameCoordinates(newFrame); + delete frame; + frame = newFrame; } } +} -FrameOfReference Observer::getFrame() const +/*! Get the current reference frame for the observer. + */ +const ObserverFrame* Observer::getFrame() const { return frame; } -// Rotate the observer about its center. +/*! Rotate the observer about its center. + */ void Observer::rotate(Quatf q) { Quatd qd(q.w, q.x, q.y, q.z); - situation.rotation = qd * situation.rotation; + orientation = qd * orientation; + updateUniversal(); } -// Orbit around the reference object (if there is one.) This involves changing -// both the observer's position and orientation. +/*! Orbit around the reference object (if there is one.) This involves changing + * both the observer's position and orientation. If there is no current center + * object, the specified selection will be used as the center of rotation, and + * the observer reference frame will be modified. + */ void Observer::orbit(const Selection& selection, Quatf q) { - Selection center = frame.refObject; + Selection center = frame->getRefObject(); if (center.empty() && !selection.empty()) { + // Automatically set the center of the reference frame center = selection; - setFrame(FrameOfReference(frame.coordSys, center)); + setFrame(frame->getCoordinateSystem(), center); } if (!center.empty()) @@ -831,10 +840,12 @@ // frames of reference, it's important to work in frame // coordinates. UniversalCoord focusPosition = center.getPosition(getTime()); - focusPosition = frame.fromUniversal(RigidTransform(focusPosition), getTime()).translation; + //focusPosition = frame.fromUniversal(RigidTransform(focusPosition), getTime()).translation; + focusPosition = frame->convertFromUniversal(focusPosition, getTime()); // v = the vector from the observer's position to the focus - Vec3d v = situation.translation - focusPosition; + //Vec3d v = situation.translation - focusPosition; + Vec3d v = position - focusPosition; // Get a double precision version of the rotation Quatd qd(q.w, q.x, q.y, q.z); @@ -844,7 +855,7 @@ // which we apply transformations later on, we can't pre-multiply. // To get around this, we compute a rotation q2 such // that q1 * r = r * q2. - Quatd qd2 = ~situation.rotation * qd * situation.rotation; + Quatd qd2 = ~orientation * qd * orientation; qd2.normalize(); // Roundoff errors will accumulate and cause the distance between @@ -855,21 +866,25 @@ v.normalize(); v *= distance; - situation.rotation = situation.rotation * qd2; - situation.translation = focusPosition + v; + //situation.rotation = situation.rotation * qd2; + //situation.translation = focusPosition + v; + orientation = orientation * qd2; + position = focusPosition + v; + updateUniversal(); } } -// Exponential camera dolly--move toward or away from the selected object -// at a rate dependent on the observer's distance from the object. +/*! Exponential camera dolly--move toward or away from the selected object + * at a rate dependent on the observer's distance from the object. + */ void Observer::changeOrbitDistance(const Selection& selection, float d) { - Selection center = frame.refObject; + Selection center = frame->getRefObject(); if (center.empty() && !selection.empty()) { center = selection; - setFrame(FrameOfReference(frame.coordSys, center)); + setFrame(frame->getCoordinateSystem(), center); } if (!center.empty()) @@ -887,7 +902,6 @@ Vec3d v = getPosition() - focusPosition; double currentDistance = v.length(); - // TODO: This is sketchy . . . if (currentDistance < minOrbitDistance) minOrbitDistance = currentDistance * 0.5; @@ -896,9 +910,9 @@ double r = (currentDistance - minOrbitDistance) / naturalOrbitDistance; double newDistance = minOrbitDistance + naturalOrbitDistance * exp(log(r) + d); v = v * (newDistance / currentDistance); - RigidTransform framePos = frame.fromUniversal(RigidTransform(focusPosition + v), - getTime()); - situation.translation = framePos.translation; + + position = frame->convertFromUniversal(focusPosition + v, getTime()); + updateUniversal(); } } } @@ -907,7 +921,7 @@ void Observer::setTargetSpeed(float s) { targetSpeed = s; - Vec3f v; + Vec3d v; if (reverseFlag) s = -s; if (trackObject.empty()) @@ -915,15 +929,15 @@ trackingOrientation = getOrientation(); // Generate vector for velocity using current orientation // and specified speed. - v = Vec3f(0, 0, -s) * getOrientation().toMatrix4(); + v = Vec3d(0, 0, -s) * getOrientation().toMatrix4(); } else { // Use tracking orientation vector to generate target velocity - v = Vec3f(0, 0, -s) * trackingOrientation.toMatrix4(); + v = Vec3d(0, 0, -s) * trackingOrientation.toMatrix4(); } - targetVelocity = Vec3d(v.x, v.y, v.z); + targetVelocity = v; initialVelocity = getVelocity(); beginAccelTime = realTime; } @@ -950,7 +964,7 @@ void Observer::gotoSelection(const Selection& selection, double gotoTime, Vec3f up, - astro::CoordinateSystem upFrame) + ObserverFrame::CoordinateSystem upFrame) { gotoSelection(selection, gotoTime, 0.0, 0.5, up, upFrame); } @@ -1023,7 +1037,7 @@ double startInter, double endInter, Vec3f up, - astro::CoordinateSystem upFrame) + ObserverFrame::CoordinateSystem upFrame) { if (!selection.empty()) { @@ -1036,22 +1050,23 @@ computeGotoParameters(selection, journey, gotoTime, startInter, endInter, v * -(orbitDistance / distance), - astro::Universal, + ObserverFrame::Universal, up, upFrame); observerMode = Travelling; } } -// Like normal goto, except we'll follow a great circle trajectory. Useful -// for travelling between surface locations, where we'd rather not go straight -// through the middle of a planet. +/*! Like normal goto, except we'll follow a great circle trajectory. Useful + * for travelling between surface locations, where we'd rather not go straight + * through the middle of a planet. + */ void Observer::gotoSelectionGC(const Selection& selection, double gotoTime, double /*startInter*/, //TODO: remove parameter?? double /*endInter*/, //TODO: remove parameter?? Vec3f up, - astro::CoordinateSystem upFrame) + ObserverFrame::CoordinateSystem upFrame) { if (!selection.empty()) { @@ -1081,7 +1096,7 @@ //startInter, endInter, 0.25, 0.75, v * (orbitDistance / distanceToCenter), - astro::Universal, + ObserverFrame::Universal, up, upFrame, centerObj); observerMode = Travelling; @@ -1093,7 +1108,7 @@ double gotoTime, double distance, Vec3f up, - astro::CoordinateSystem upFrame) + ObserverFrame::CoordinateSystem upFrame) { if (!selection.empty()) { @@ -1104,7 +1119,7 @@ v.normalize(); computeGotoParameters(selection, journey, gotoTime, 0.25, 0.75, - v * -distance * 1e6, astro::Universal, + v * -distance * 1e6, ObserverFrame::Universal, up, upFrame); observerMode = Travelling; } @@ -1115,7 +1130,7 @@ double gotoTime, double distance, Vec3f up, - astro::CoordinateSystem upFrame) + ObserverFrame::CoordinateSystem upFrame) { if (!selection.empty()) { @@ -1128,7 +1143,7 @@ // The destination position lies along a line extended from the center // object to the target object computeGotoParametersGC(selection, journey, gotoTime, 0.25, 0.75, - v * -distance * 1e6, astro::Universal, + v * -distance * 1e6, ObserverFrame::Universal, up, upFrame, centerObj); observerMode = Travelling; @@ -1151,34 +1166,28 @@ double y = cos(phi); double z = -sin(theta) * sin(phi); computeGotoParameters(selection, journey, gotoTime, 0.25, 0.75, - Vec3d(x, y, z) * distance * 1e6, astro::Geographic, - up, astro::Geographic); + Vec3d(x, y, z) * distance * 1e6, ObserverFrame::BodyFixed, + up, ObserverFrame::BodyFixed); observerMode = Travelling; } } -void Observer::gotoLocation(const RigidTransform& transform, +void Observer::gotoLocation(const UniversalCoord& toPosition, + const Quatd& toOrientation, double duration) { journey.startTime = realTime; journey.duration = duration; - RigidTransform from(getPosition(), getOrientation()); - from = frame.fromUniversal(from, getTime()); - journey.from = from.translation; - journey.initialOrientation= Quatf((float) from.rotation.w, (float) from.rotation.x, - (float) from.rotation.y, (float) from.rotation.z); + journey.from = position; + journey.initialOrientation = orientation; + journey.to = toPosition; + journey.finalOrientation = toOrientation; - journey.to = transform.translation; - journey.finalOrientation = Quatf((float) transform.rotation.w, - (float) transform.rotation.x, - (float) transform.rotation.y, - (float) transform.rotation.z); journey.startInterpolation = 0.25f; journey.endInterpolation = 0.75f; - journey.accelTime = 0.5; double distance = astro::microLightYearsToKilometers(journey.from.distanceTo(journey.to)) / 2.0; pair sol = solve_bisection(TravelExpFunc(distance, journey.accelTime), @@ -1199,16 +1208,13 @@ // respect to currently selected object. if (!selection.empty()) { - FrameOfReference refFrame(astro::Geographic, selection); - RigidTransform xform = refFrame.fromUniversal(RigidTransform(getPosition(), getOrientation()), - getTime()); + ObserverFrame frame(ObserverFrame::BodyFixed, selection); + Point3d bfPos = (Point3d) frame.convertFromUniversal(positionUniv, getTime()); - Point3d pos = (Point3d) xform.translation; + distance = bfPos.distanceFromOrigin(); + longitude = -radToDeg(atan2(-bfPos.z, -bfPos.x)); + latitude = radToDeg(PI/2 - acos(bfPos.y / distance)); - distance = pos.distanceFromOrigin(); - longitude = -radToDeg(atan2(-pos.z, -pos.x)); - latitude = radToDeg(PI/2 - acos(pos.y / distance)); - // Convert distance from light years to kilometers. distance = astro::microLightYearsToKilometers(distance); } @@ -1217,31 +1223,28 @@ void Observer::gotoSurface(const Selection& sel, double duration) { - Vec3d vd = getPosition() - sel.getPosition(getTime()); - Vec3f vf((float) vd.x, (float) vd.y, (float) vd.z); - vf.normalize(); - Vec3f viewDir = Vec3f(0, 0, -1) * getOrientation().toMatrix3(); - Vec3f up = Vec3f(0, 1, 0) * getOrientation().toMatrix3(); - Quatf q = getOrientation(); - if (vf * viewDir < 0.0f) + Vec3d v = getPosition() - sel.getPosition(getTime()); + v.normalize(); + + Vec3d viewDir = Vec3d(0, 0, -1) * orientationUniv.toMatrix3(); + Vec3d up = Vec3d(0, 1, 0) * orientationUniv.toMatrix3(); + Quatd q = orientationUniv; + if (v * viewDir < 0.0) { - q = lookAt(Point3f(0, 0, 0), Point3f(0.0f, 0.0f, 0.0f) + up, vf); + q = lookAt(Point3d(0.0, 0.0, 0.0), Point3d(0.0, 0.0, 0.0) + up, v); } - else - { - } - FrameOfReference frame(astro::Geographic, sel); - RigidTransform rt = frame.fromUniversal(RigidTransform(getPosition(), q), - getTime()); + ObserverFrame frame(ObserverFrame::BodyFixed, sel); + UniversalCoord bfPos = frame.convertFromUniversal(positionUniv, getTime()); + q = frame.convertFromUniversal(q, getTime()); double height = 1.0001 * astro::kilometersToMicroLightYears(sel.radius()); - Vec3d dir = rt.translation - Point3d(0.0, 0.0, 0.0); + Vec3d dir = bfPos - Point3d(0.0, 0.0, 0.0); dir.normalize(); dir *= height; + UniversalCoord nearSurfacePoint(dir.x, dir.y, dir.z); - rt.translation = UniversalCoord(dir.x, dir.y, dir.z); - gotoLocation(rt, duration); + gotoLocation(nearSurfacePoint, q, duration); }; @@ -1263,11 +1266,8 @@ void Observer::follow(const Selection& selection) { - if (!selection.empty()) - { - setFrame(FrameOfReference(astro::Ecliptical, selection)); + setFrame(ObserverFrame::Ecliptical, selection); } -} void Observer::geosynchronousFollow(const Selection& selection) @@ -1276,45 +1276,41 @@ selection.location() != NULL || selection.star() != NULL) { - setFrame(FrameOfReference(astro::Geographic, selection)); + setFrame(ObserverFrame::BodyFixed, selection); } } void Observer::phaseLock(const Selection& selection) { - if (frame.refObject.body() != NULL) + Selection refObject = frame->getRefObject(); + + if (selection != refObject) { - if (selection == frame.refObject) + if (refObject.body() != NULL || refObject.star() != NULL) { - setFrame(FrameOfReference(astro::PhaseLock, selection, - Selection(selection.body()->getSystem()->getStar()))); + setFrame(ObserverFrame::PhaseLock, refObject, selection); } + } else { - setFrame(FrameOfReference(astro::PhaseLock, frame.refObject, selection)); - } - } - else if (frame.refObject.star() != NULL) + // Selection and reference object are identical, so the frame is undefined. + // We'll instead use the object's star as the target object. + if (selection.body() != NULL) { - if (selection != frame.refObject) - { - setFrame(FrameOfReference(astro::PhaseLock, frame.refObject, selection)); + setFrame(ObserverFrame::PhaseLock, selection, Selection(selection.body()->getSystem()->getStar())); } } } + void Observer::chase(const Selection& selection) { - if (selection.body() != NULL) + if (selection.body() != NULL || selection.star() != NULL) { - setFrame(FrameOfReference(astro::Chase, selection)); + setFrame(ObserverFrame::Chase, selection); } - else if (selection.star() != NULL) - { - setFrame(FrameOfReference(astro::Chase, selection)); } -} float Observer::getFOV() const @@ -1338,3 +1334,214 @@ return pickDirection; } + + +// Internal method to update the position and orientation of the observer in +// universal coordinates. +void Observer::updateUniversal() +{ + positionUniv = frame->convertToUniversal(position, simTime); + orientationUniv = frame->convertToUniversal(orientation, simTime); +} + + +/*! Create the default 'universal' observer frame, with a center at the + * Solar System barycenter and coordinate axes of the J200Ecliptic + * reference frame. + */ +ObserverFrame::ObserverFrame() : + coordSys(Universal), + frame(NULL) +{ + frame = createFrame(Universal, Selection(), Selection()); + frame->addRef(); +} + + +ObserverFrame::ObserverFrame(CoordinateSystem _coordSys, + const Selection& _refObject, + const Selection& _targetObject) : + coordSys(_coordSys), + frame(NULL), + targetObject(_targetObject) +{ + frame = createFrame(_coordSys, _refObject, _targetObject); + frame->addRef(); +} + + +ObserverFrame::ObserverFrame(const ObserverFrame& f) : + coordSys(f.coordSys), + frame(f.frame), + targetObject(f.targetObject) +{ + frame->addRef(); +} + + +ObserverFrame& ObserverFrame::operator=(const ObserverFrame& f) +{ + coordSys = f.coordSys; + targetObject = f.targetObject; + + // In case frames are the same, make sure we addref before releasing + f.frame->addRef(); + frame->release(); + frame = f.frame; + + return *this; +} + + +ObserverFrame::~ObserverFrame() +{ + if (frame != NULL) + frame->release(); +} + + +ObserverFrame::CoordinateSystem +ObserverFrame::getCoordinateSystem() const +{ + return coordSys; +} + + +Selection +ObserverFrame::getRefObject() const +{ + return frame->getCenter(); +} + + +Selection +ObserverFrame::getTargetObject() const +{ + return targetObject; +} + + +const ReferenceFrame* +ObserverFrame::getFrame() const +{ + return frame; +} + + +UniversalCoord +ObserverFrame::convertFromUniversal(const UniversalCoord& uc, double tjd) const +{ + return frame->convertFromUniversal(uc, tjd); +} + + +UniversalCoord +ObserverFrame::convertToUniversal(const UniversalCoord& uc, double tjd) const +{ + return frame->convertToUniversal(uc, tjd); +} + + +Quatd +ObserverFrame::convertFromUniversal(const Quatd& q, double tjd) const +{ + return frame->convertFromUniversal(q, tjd); +} + + +Quatd +ObserverFrame::convertToUniversal(const Quatd& q, double tjd) const +{ + return frame->convertToUniversal(q, tjd); +} + + +/*! Convert a position from one frame to another. + */ +UniversalCoord +ObserverFrame::convert(const ObserverFrame* fromFrame, + const ObserverFrame* toFrame, + const UniversalCoord& uc, + double t) +{ + // Perform the conversion fromFrame -> universal -> toFrame + return toFrame->convertFromUniversal(fromFrame->convertToUniversal(uc, t), t); +} + + +/*! Convert an orientation from one frame to another. +*/ +Quatd +ObserverFrame::convert(const ObserverFrame* fromFrame, + const ObserverFrame* toFrame, + const Quatd& q, + double t) +{ + // Perform the conversion fromFrame -> universal -> toFrame + return toFrame->convertFromUniversal(fromFrame->convertToUniversal(q, t), t); +} + + +// Create the ReferenceFrame for the specified observer frame parameters. +ReferenceFrame* +ObserverFrame::createFrame(CoordinateSystem _coordSys, + const Selection& _refObject, + const Selection& _targetObject) +{ + switch (_coordSys) + { + case Universal: + return new J2000EclipticFrame(Selection()); + + case Ecliptical: + return new J2000EclipticFrame(_refObject); + + case Equatorial: + return new BodyMeanEquatorFrame(_refObject, _refObject); + + case BodyFixed: + return new BodyFixedFrame(_refObject, _refObject); + + case PhaseLock: + { + return new TwoVectorFrame(_refObject, + FrameVector::createRelativePositionVector(_refObject, _targetObject), 1, + FrameVector::createRelativeVelocityVector(_refObject, _targetObject), 2); + } + + case Chase: + { + return new TwoVectorFrame(_refObject, + FrameVector::createRelativeVelocityVector(_refObject, _refObject.parent()), 1, + FrameVector::createRelativePositionVector(_refObject, _refObject.parent()), 2); + } + + case PhaseLock_Old: + { + FrameVector rotAxis(FrameVector::createConstantVector(Vec3d(0, 1, 0), + new BodyMeanEquatorFrame(_refObject, _refObject))); + return new TwoVectorFrame(_refObject, + FrameVector::createRelativePositionVector(_refObject, _targetObject), 3, + rotAxis, 2); + } + + case Chase_Old: + { + FrameVector rotAxis(FrameVector::createConstantVector(Vec3d(0, 1, 0), + new BodyMeanEquatorFrame(_refObject, _refObject))); + + return new TwoVectorFrame(_refObject, + FrameVector::createRelativeVelocityVector(_refObject.parent(), _refObject), 3, + rotAxis, 2); + } + + case ObserverLocal: + // TODO: This is only used for computing up vectors for orientation; it does + // define a proper frame for the observer position orientation. + return new J2000EclipticFrame(Selection()); + + default: + return new J2000EclipticFrame(_refObject); + } +} + Index: src/celengine/frame.h =================================================================== --- src/celengine/frame.h (revision 4095) +++ src/celengine/frame.h (working copy) @@ -16,72 +16,12 @@ #include -struct RigidTransform -{ - RigidTransform() : - translation(0.0, 0.0, 0.0), rotation(1.0, 0.0, 0.0, 0.0) {}; - RigidTransform(const UniversalCoord& uc) : - translation(uc), rotation(1.0f) {}; - RigidTransform(const UniversalCoord& uc, const Quatd& q) : - translation(uc), rotation(q) {}; - RigidTransform(const UniversalCoord& uc, const Quatf& q) : - translation(uc), rotation(q.w, q.x, q.y, q.z) {}; - UniversalCoord translation; - Quatd rotation; -}; - - -struct FrameOfReference -{ - FrameOfReference() : - coordSys(astro::Universal) {}; - FrameOfReference(astro::CoordinateSystem _coordSys, Body* _body) : - coordSys(_coordSys), refObject(_body) {}; - FrameOfReference(astro::CoordinateSystem _coordSys, Star* _star) : - coordSys(_coordSys), refObject(_star) {}; - FrameOfReference(astro::CoordinateSystem _coordSys, DeepSkyObject* _deepsky) : - coordSys(_coordSys), refObject(_deepsky) {}; - FrameOfReference(astro::CoordinateSystem _coordSys, const Selection& sel) : - coordSys(_coordSys), refObject(sel) {}; - FrameOfReference(astro::CoordinateSystem _coordSys, const Selection& ref, - const Selection& target) : - coordSys(_coordSys), refObject(ref), targetObject(target) {}; - - RigidTransform toUniversal(const RigidTransform& xform, double t) const; - RigidTransform fromUniversal(const RigidTransform& xform, double t) const; - - astro::CoordinateSystem coordSys; - Selection refObject; - Selection targetObject; -}; - - -// class RefCountedObject - -/*! -Frame -{ - Center "Sol" - # Orientation "J2000" - # Orientation "J2000Ecliptic" - TwoAxis - { - Primary - { - Axis "+X" - Observer "Earth" - Target "Sun" - } - - Secondary - { - Axis "+Y" - } - } -} +/*! A ReferenceFrame object has a center and set of orthogonal axes. + * + * Subclasses of ReferenceFrame must override the getOrientation method + * (which specifies the coordinate axes at a given time) and the + * nestingDepth() method (which is used to check for recursive frames.) */ - - class ReferenceFrame { public: @@ -91,10 +31,13 @@ int addRef() const; int release() const; - UniversalCoord convertFrom(const UniversalCoord& uc, double tjd) const; - UniversalCoord convertTo(const UniversalCoord& uc, double tjd) const; + UniversalCoord convertFromUniversal(const UniversalCoord& uc, double tjd) const; + UniversalCoord convertToUniversal(const UniversalCoord& uc, double tjd) const; + Quatd convertFromUniversal(const Quatd& q, double tjd) const; + Quatd convertToUniversal(const Quatd& q, double tjd) const; Point3d convertFromAstrocentric(const Point3d& p, double tjd) const; + Point3d convertToAstrocentric(const Point3d& p, double tjd) const; Selection getCenter() const; Index: src/celengine/command.cpp =================================================================== --- src/celengine/command.cpp (revision 4095) +++ src/celengine/command.cpp (working copy) @@ -60,7 +60,7 @@ CommandGoto::CommandGoto(double t, double dist, Vec3f _up, - astro::CoordinateSystem _upFrame) : + ObserverFrame::CoordinateSystem _upFrame) : gotoTime(t), distance(dist), up(_up), upFrame(_upFrame) { } @@ -124,10 +124,9 @@ void CommandGotoLocation::process(ExecutionEnvironment& env) { - RigidTransform to; - to.rotation = Quatd(rotation.w, rotation.x, rotation.y, rotation.z); - to.translation = translation; - env.getSimulation()->gotoLocation(to, gotoTime); + Quatd toOrientation = Quatd(rotation.w, rotation.x, rotation.y, rotation.z); + UniversalCoord toPosition = translation; + env.getSimulation()->gotoLocation(toPosition, toOrientation, gotoTime); } ///////////////////////////// @@ -231,7 +230,7 @@ //////////////// // Setframe command -CommandSetFrame::CommandSetFrame(astro::CoordinateSystem _coordSys, +CommandSetFrame::CommandSetFrame(ObserverFrame::CoordinateSystem _coordSys, const string& refName, const string& targetName) : coordSys(_coordSys), refObjectName(refName), targetObjectName(targetName) @@ -242,9 +241,9 @@ { Selection ref = env.getSimulation()->findObjectFromPath(refObjectName); Selection target; - if (coordSys == astro::PhaseLock) + if (coordSys == ObserverFrame::PhaseLock) target = env.getSimulation()->findObjectFromPath(targetObjectName); - env.getSimulation()->setFrame(FrameOfReference(coordSys, ref, target)); + env.getSimulation()->setFrame(coordSys, ref, target); } @@ -273,7 +272,7 @@ void CommandCancel::process(ExecutionEnvironment& env) { env.getSimulation()->cancelMotion(); - env.getSimulation()->setFrame(FrameOfReference()); + env.getSimulation()->setFrame(ObserverFrame::Universal, Selection()); env.getSimulation()->setTrackedObject(Selection()); } Index: src/celengine/command.h =================================================================== --- src/celengine/command.h (revision 4095) +++ src/celengine/command.h (working copy) @@ -79,7 +79,7 @@ { public: CommandGoto(double t, double dist, - Vec3f _up, astro::CoordinateSystem _upFrame); + Vec3f _up, ObserverFrame::CoordinateSystem _upFrame); ~CommandGoto(); void process(ExecutionEnvironment&); @@ -87,7 +87,7 @@ double gotoTime; double distance; Vec3f up; - astro::CoordinateSystem upFrame; + ObserverFrame::CoordinateSystem upFrame; }; @@ -205,12 +205,12 @@ class CommandSetFrame : public InstantaneousCommand { public: - CommandSetFrame(astro::CoordinateSystem, + CommandSetFrame(ObserverFrame::CoordinateSystem, const std::string&, const std::string&); void process(ExecutionEnvironment&); private: - astro::CoordinateSystem coordSys; + ObserverFrame::CoordinateSystem coordSys; std::string refObjectName; std::string targetObjectName; }; Index: src/celengine/simulation.h =================================================================== --- src/celengine/simulation.h (revision 4095) +++ src/celengine/simulation.h (working copy) @@ -61,14 +61,14 @@ Selection findObjectFromPath(std::string s, bool i18n = false); std::vector getObjectCompletion(std::string s, bool withLocations = false); void gotoSelection(double gotoTime, - Vec3f up, astro::CoordinateSystem upFrame); + Vec3f up, ObserverFrame::CoordinateSystem upFrame); void gotoSelection(double gotoTime, double distance, - Vec3f up, astro::CoordinateSystem upFrame); + Vec3f up, ObserverFrame::CoordinateSystem upFrame); void gotoSelectionLongLat(double gotoTime, double distance, float longitude, float latitude, Vec3f up); - void gotoLocation(const RigidTransform& transform, double duration); + void gotoLocation(const UniversalCoord& toPosition, const Quatd& toOrientation, double duration); void getSelectionLongLat(double& distance, double& longitude, double& latitude); @@ -107,9 +107,9 @@ void setObserverMode(Observer::ObserverMode); Observer::ObserverMode getObserverMode() const; - void setFrame(astro::CoordinateSystem, const Selection&); - void setFrame(const FrameOfReference&); - FrameOfReference getFrame() const; + void setFrame(ObserverFrame::CoordinateSystem, const Selection& refObject, const Selection& targetObject); + void setFrame(ObserverFrame::CoordinateSystem, const Selection& refObject); + const ObserverFrame* getFrame() const; private: SolarSystem* getSolarSystem(const Star* star); Index: macosx/CelestiaSimulation.mm =================================================================== --- macosx/CelestiaSimulation.mm (revision 4095) +++ macosx/CelestiaSimulation.mm (working copy) @@ -144,7 +144,7 @@ [self simulation]->gotoSelection( [gotoTime doubleValue], [up vec3f], - (astro::CoordinateSystem)[[Astro coordinateSystem:csysName] intValue]); + (ObserverFrame::CoordinateSystem)[[Astro coordinateSystem:csysName] intValue]); } @@ -154,7 +154,7 @@ [gotoTime doubleValue], [distance doubleValue], [up vec3f], - (astro::CoordinateSystem)[[Astro coordinateSystem:csysName] intValue]); + (ObserverFrame::CoordinateSystem)[[Astro coordinateSystem:csysName] intValue]); } -(void)gotoSelection:(NSNumber*)gotoTime distance:(NSNumber*)distance longitude:(NSNumber*)longitude latitude:(NSNumber*)latitude up:(CelestiaVector*)up @@ -225,7 +225,7 @@ -(void)setFrame:(NSString*)cs selection:(CelestiaSelection*)sel { - [self simulation]->setFrame((astro::CoordinateSystem)[[Astro coordinateSystem:cs] intValue], [sel selection]); + [self simulation]->setFrame((ObserverFrame::CoordinateSystem)[[Astro coordinateSystem:cs] intValue], [sel selection]); } @end \ No newline at end of file Index: macosx/Astro.mm =================================================================== --- macosx/Astro.mm (revision 4095) +++ macosx/Astro.mm (working copy) @@ -8,6 +8,7 @@ #import "Astro.h" #import "Astro_PrivateAPI.h" +#import "Observer.h" #import "CelestiaUniversalCoord_PrivateAPI.h" #import "CelestiaVector_PrivateAPI.h" @@ -68,7 +69,7 @@ +(void)initialize { // compiler macro would be prettier I guess - coordinateDict = [[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:astro::Universal],@"Universal",[NSNumber numberWithInt:astro::Ecliptical],@"Ecliptical",[NSNumber numberWithInt:astro::Equatorial],@"Equatorial",[NSNumber numberWithInt:astro::Geographic],@"Geographic",[NSNumber numberWithInt:astro::ObserverLocal],@"ObserverLocal",[NSNumber numberWithInt:astro::PhaseLock],@"PhaseLock",[NSNumber numberWithInt:astro::Chase],@"Chase",nil,nil] retain]; + coordinateDict = [[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:ObserverFrame::Universal],@"Universal",[NSNumber numberWithInt:ObserverFrame::Ecliptical],@"Ecliptical",[NSNumber numberWithInt:ObserverFrame::Equatorial],@"Equatorial",[NSNumber numberWithInt:ObserverFrame::BodyFixed],@"Geographic",[NSNumber numberWithInt:ObserverFrame::ObserverLocal],@"ObserverLocal",[NSNumber numberWithInt:ObserverFrame::PhaseLock],@"PhaseLock",[NSNumber numberWithInt:ObserverFrame::Chase],@"Chase",nil,nil] retain]; } +(NSNumber*)sphereIlluminationFraction:(CelestiaVector*)spherePos viewerPosition:(CelestiaVector*)viewerPos { Index: macosx/Celestia