Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members

celx.cpp

Go to the documentation of this file.
00001 // celx.cpp
00002 //
00003 // Copyright (C) 2003, Chris Laurel <claurel@shatters.net>
00004 //
00005 // Lua script extensions for Celestia
00006 //
00007 // This program is free software; you can redistribute it and/or
00008 // modify it under the terms of the GNU General Public License
00009 // as published by the Free Software Foundation; either version 2
00010 // of the License, or (at your option) any later version.
00011 
00012 #include <cassert>
00013 #include <cstring>
00014 #include <cstdio>
00015 #include <map>
00016 #include <celengine/astro.h>
00017 #include <celengine/celestia.h>
00018 #include <celengine/cmdparser.h>
00019 #include <celengine/execenv.h>
00020 #include <celengine/execution.h>
00021 #include <celmath/vecmath.h>
00022 #include <celengine/gl.h>
00023 #include "imagecapture.h"
00024 
00025 // Ugh . . . the C++ standard says that stringstream should be in
00026 // sstream, but the GNU C++ compiler uses strstream instead.
00027 #ifdef HAVE_SSTREAM
00028 #include <sstream>
00029 #else
00030 #include <strstream>
00031 #endif // HAVE_SSTREAM
00032 
00033 #include "celx.h"
00034 #include "celestiacore.h"
00035 extern "C" {
00036 #include "lualib.h"
00037 }
00038 
00039 using namespace std;
00040 
00041 static const char* ClassNames[] =
00042 {
00043     "class_celestia",
00044     "class_observer",
00045     "class_object",
00046     "class_vec3",
00047     "class_matrix",
00048     "class_rotation",
00049     "class_position",
00050     "class_frame",
00051     "class_celscript",
00052 };
00053 
00054 static const int _Celestia = 0;
00055 static const int _Observer = 1;
00056 static const int _Object   = 2;
00057 static const int _Vec3     = 3;
00058 static const int _Matrix   = 4;
00059 static const int _Rotation = 5;
00060 static const int _Position = 6;
00061 static const int _Frame    = 7;
00062 static const int _CelScript= 8;
00063 
00064 #define CLASS(i) ClassNames[(i)]
00065 
00066 // Maximum timeslice a script may run without
00067 // returning control to celestia
00068 static const double MaxTimeslice = 5.0;
00069 
00070 // names of callback-functions in Lua:
00071 static const char* KbdCallback = "celestia_keyboard_callback";
00072 static const char* CleanupCallback = "celestia_cleanup_callback";
00073 
00074 typedef map<string, uint32> FlagMap; 
00075 
00076 static FlagMap RenderFlagMap;
00077 static FlagMap LabelFlagMap;
00078 static FlagMap LocationFlagMap;
00079 static FlagMap BodyTypeMap;
00080 static bool mapsInitialized = false;
00081 
00082 // select which type of error will be fatal (call lua_error) and
00083 // which will return a default value instead
00084 enum FatalErrors {
00085     NoErrors = 0,
00086     WrongType = 1,
00087     WrongArgc = 2,
00088     AllErrors = WrongType | WrongArgc,
00089 };
00090 
00091 
00092 // Initialize various maps from named keywords to numeric flags used within celestia:
00093 static void initRenderFlagMap()
00094 {
00095     RenderFlagMap["orbits"] = Renderer::ShowOrbits;
00096     RenderFlagMap["cloudmaps"] = Renderer::ShowCloudMaps;
00097     RenderFlagMap["constellations"] = Renderer::ShowDiagrams;
00098     RenderFlagMap["galaxies"] = Renderer::ShowGalaxies;
00099     RenderFlagMap["planets"] = Renderer::ShowPlanets;
00100     RenderFlagMap["stars"] = Renderer::ShowStars;
00101     RenderFlagMap["nightmaps"] = Renderer::ShowNightMaps;
00102     RenderFlagMap["eclipseshadows"] = Renderer::ShowEclipseShadows;
00103     RenderFlagMap["ringshadows"] = Renderer::ShowRingShadows;
00104     RenderFlagMap["comettails"] = Renderer::ShowCometTails;
00105     RenderFlagMap["boundaries"] = Renderer::ShowBoundaries;
00106     RenderFlagMap["markers"] = Renderer::ShowMarkers;
00107     RenderFlagMap["automag"] = Renderer::ShowAutoMag;
00108     RenderFlagMap["atmospheres"] = Renderer::ShowAtmospheres;
00109     RenderFlagMap["grid"] = Renderer::ShowCelestialSphere;
00110     RenderFlagMap["smoothlines"] = Renderer::ShowSmoothLines;
00111     RenderFlagMap["partialtrajectories"] = Renderer::ShowPartialTrajectories;
00112 }
00113 
00114 static void initLabelFlagMap()
00115 {
00116     LabelFlagMap["planets"] = Renderer::PlanetLabels;
00117     LabelFlagMap["moons"] = Renderer::MoonLabels;
00118     LabelFlagMap["spacecraft"] = Renderer::SpacecraftLabels;
00119     LabelFlagMap["asteroids"] = Renderer::AsteroidLabels;
00120     LabelFlagMap["comets"] = Renderer::CometLabels;
00121     LabelFlagMap["constellations"] = Renderer::ConstellationLabels;
00122     LabelFlagMap["stars"] = Renderer::StarLabels;
00123     LabelFlagMap["galaxies"] = Renderer::GalaxyLabels;
00124     LabelFlagMap["locations"] = Renderer::LocationLabels;
00125 }
00126 
00127 static void initBodyTypeMap()
00128 {
00129     BodyTypeMap["Planet"] = Body::Planet;
00130     BodyTypeMap["Moon"] = Body::Moon;
00131     BodyTypeMap["Asteroid"] = Body::Asteroid;
00132     BodyTypeMap["Comet"] = Body::Comet;
00133     BodyTypeMap["Spacecraft"] = Body::Spacecraft;
00134     BodyTypeMap["Invisible"] = Body::Invisible;
00135     BodyTypeMap["Unknown"] = Body::Unknown;
00136 }
00137 
00138 static void initLocationFlagMap()
00139 {
00140     LocationFlagMap["city"] = Location::City;
00141     LocationFlagMap["observatory"] = Location::Observatory;
00142     LocationFlagMap["landingsite"] = Location::LandingSite;
00143     LocationFlagMap["crater"] = Location::Crater;
00144     LocationFlagMap["vallis"] = Location::Vallis;
00145     LocationFlagMap["mons"] = Location::Mons;
00146     LocationFlagMap["planum"] = Location::Planum;
00147     LocationFlagMap["chasma"] = Location::Chasma;
00148     LocationFlagMap["patera"] = Location::Patera;
00149     LocationFlagMap["mare"] = Location::Mare;
00150     LocationFlagMap["rupes"] = Location::Rupes;
00151     LocationFlagMap["tessera"] = Location::Tessera;
00152     LocationFlagMap["regio"] = Location::Regio;
00153     LocationFlagMap["chaos"] = Location::Chaos;
00154     LocationFlagMap["terra"] = Location::Terra;
00155     LocationFlagMap["astrum"] = Location::Astrum;
00156     LocationFlagMap["corona"] = Location::Corona;
00157     LocationFlagMap["dorsum"] = Location::Dorsum;
00158     LocationFlagMap["fossa"] = Location::Fossa;
00159     LocationFlagMap["catena"] = Location::Catena;
00160     LocationFlagMap["mensa"] = Location::Mensa;
00161     LocationFlagMap["rima"] = Location::Rima;
00162     LocationFlagMap["undae"] = Location::Undae;
00163     LocationFlagMap["reticulum"] = Location::Reticulum;
00164     LocationFlagMap["planitia"] = Location::Planitia;
00165     LocationFlagMap["linea"] = Location::Linea;
00166     LocationFlagMap["fluctus"] = Location::Fluctus;
00167     LocationFlagMap["farrum"] = Location::Farrum;
00168     LocationFlagMap["other"] = Location::Other;
00169 }
00170 
00171 static void initMaps()
00172 {
00173     if (!mapsInitialized)
00174     {
00175         initRenderFlagMap();
00176         initLabelFlagMap();
00177         initBodyTypeMap();
00178         initLocationFlagMap();
00179     }
00180     mapsInitialized = true;
00181 }
00182 
00183 
00184 // Wrapper for a CEL-script, including the needed Execution Environment
00185 class CelScriptWrapper : public ExecutionEnvironment
00186 {
00187  public:
00188     CelScriptWrapper(CelestiaCore& appCore, istream& scriptfile): 
00189         script(NULL),
00190         core(appCore),
00191         cmdSequence(NULL),
00192         tickTime(0.0),
00193         errorMessage("")
00194     {
00195         CommandParser parser(scriptfile);
00196         cmdSequence = parser.parse();
00197         if (cmdSequence != NULL)
00198         {
00199             script = new Execution(*cmdSequence, *this);
00200         }
00201         else
00202         {
00203             const vector<string>* errors = parser.getErrors();
00204             if (errors->size() > 0)
00205                 errorMessage = "Error while parsing CEL-script: " + (*errors)[0];
00206             else
00207                 errorMessage = "Error while parsing CEL-script.";
00208         }
00209     }
00210     
00211     virtual ~CelScriptWrapper()
00212     {
00213         if (script != NULL)
00214             delete script;
00215         if (cmdSequence != NULL)
00216             delete cmdSequence;
00217     }
00218     
00219     string getErrorMessage() const
00220     {
00221         return errorMessage;
00222     }
00223     
00224     // tick the CEL-script. t is in seconds and doesn't have to start with zero
00225     bool tick(double t)
00226     {
00227         // use first tick to set the time
00228         if (tickTime == 0.0)
00229         {
00230             tickTime = t;
00231             return false;
00232         }
00233         double dt = t - tickTime;
00234         tickTime = t;
00235         return script->tick(dt);        
00236     }
00237 
00238     Simulation* getSimulation() const
00239     {
00240         return core.getSimulation();
00241     }
00242 
00243     Renderer* getRenderer() const
00244     {
00245         return core.getRenderer();
00246     }
00247 
00248     CelestiaCore* getCelestiaCore() const
00249     {
00250         return &core;         
00251     }
00252 
00253     void showText(string s, int horig, int vorig, int hoff, int voff,
00254                   double duration)
00255     {
00256         core.showText(s, horig, vorig, hoff, voff, duration);
00257     }
00258 
00259  private:
00260     Execution* script;
00261     CelestiaCore& core;
00262     CommandSequence* cmdSequence;
00263     double tickTime;
00264     string errorMessage;
00265 };
00266 
00267 
00268 // Push a class name onto the Lua stack
00269 static void PushClass(lua_State* l, int id)
00270 {
00271     lua_pushlstring(l, ClassNames[id], strlen(ClassNames[id]));
00272 }
00273 
00274 // Set the class (metatable) of the object on top of the stack
00275 static void SetClass(lua_State* l, int id)
00276 {
00277     PushClass(l, id);
00278     lua_rawget(l, LUA_REGISTRYINDEX);
00279     if (lua_type(l, -1) != LUA_TTABLE)
00280         cout << "Metatable for " << ClassNames[id] << " not found!\n";
00281     if (lua_setmetatable(l, -2) == 0)
00282         cout << "Error setting metatable for " << ClassNames[id] << '\n';
00283 }
00284 
00285 // Initialize the metatable for a class; sets the appropriate registry
00286 // entries and __index, leaving the metatable on the stack when done.
00287 static void CreateClassMetatable(lua_State* l, int id)
00288 {
00289     lua_newtable(l);
00290     PushClass(l, id);
00291     lua_pushvalue(l, -2);
00292     lua_rawset(l, LUA_REGISTRYINDEX); // registry.name = metatable
00293     lua_pushvalue(l, -1);
00294     PushClass(l, id);
00295     lua_rawset(l, LUA_REGISTRYINDEX); // registry.metatable = name
00296 
00297     lua_pushliteral(l, "__index");
00298     lua_pushvalue(l, -2);
00299     lua_rawset(l, -3);
00300 }
00301 
00302 // Register a class 'method' in the metatable (assumed to be on top of the stack)
00303 static void RegisterMethod(lua_State* l, const char* name, lua_CFunction fn)
00304 {
00305     lua_pushstring(l, name);
00306     lua_pushvalue(l, -2);
00307     lua_pushcclosure(l, fn, 1);
00308     lua_settable(l, -3);
00309 }
00310 
00311 
00312 // Verify that an object at location index on the stack is of the
00313 // specified class
00314 static bool istype(lua_State* l, int index, int id)
00315 {
00316     // get registry[metatable]
00317     if (!lua_getmetatable(l, index))
00318         return false;
00319     lua_rawget(l, LUA_REGISTRYINDEX);
00320 
00321     if (lua_type(l, -1) != LUA_TSTRING)
00322     {
00323         cout << "istype failed!  Unregistered class.\n";
00324         lua_pop(l, 1);
00325         return false;
00326     }
00327 
00328     const char* classname = lua_tostring(l, -1);
00329     if (classname != NULL && strcmp(classname, ClassNames[id]) == 0)
00330     {
00331         lua_pop(l, 1);
00332         return true;
00333     }
00334     lua_pop(l, 1);
00335     return false;
00336 }
00337 
00338 // Verify that an object at location index on the stack is of the
00339 // specified class and return pointer to userdata
00340 static void* CheckUserData(lua_State* l, int index, int id)
00341 {
00342     if (istype(l, index, id))
00343         return lua_touserdata(l, index);
00344     else
00345         return NULL;
00346 }
00347 
00348 
00349 // Return the CelestiaCore object stored in the globals table
00350 static CelestiaCore* getAppCore(lua_State* l, FatalErrors fatalErrors = NoErrors)
00351 {
00352     lua_pushstring(l, "celestia-appcore");
00353     lua_gettable(l, LUA_REGISTRYINDEX);
00354 
00355     if (!lua_islightuserdata(l, -1))
00356     {
00357         if (fatalErrors == NoErrors)
00358             return NULL;
00359         else
00360         {
00361             lua_pushstring(l, "internal error: invalid appCore");
00362             lua_error(l);
00363         }
00364     }
00365 
00366     CelestiaCore* appCore = static_cast<CelestiaCore*>(lua_touserdata(l, -1));
00367     lua_pop(l, 1);
00368     return appCore;
00369 }
00370 
00371 
00372 LuaState::LuaState() :
00373     timeout(MaxTimeslice),
00374     state(NULL),
00375     costate(NULL),
00376     alive(false),
00377     timer(NULL),
00378     scriptAwakenTime(0.1),
00379     ioMode(NoIO)
00380 {
00381     state = lua_open();
00382     timer = CreateTimer();
00383     screenshotCount = 0;
00384 }
00385 
00386 LuaState::~LuaState()
00387 {
00388     delete timer;
00389     if (state != NULL)
00390         lua_close(state);
00391 #if 0
00392     if (costate != NULL)
00393         lua_close(costate);
00394 #endif
00395 }
00396 
00397 lua_State* LuaState::getState() const
00398 {
00399     return state;
00400 }
00401 
00402 
00403 double LuaState::getTime() const
00404 {
00405     return timer->getTime();
00406 }
00407 
00408 
00409 // Check if the running script has exceeded its allowed timeslice
00410 // and terminate it if it has:
00411 static void checkTimeslice(lua_State *l, lua_Debug *ar)
00412 {
00413     lua_pushstring(l, "celestia-luastate");
00414     lua_gettable(l, LUA_REGISTRYINDEX);
00415     if (!lua_islightuserdata(l, -1))
00416     {
00417         lua_pushstring(l, "Internal Error: Invalid table entry in checkTimeslice");
00418         lua_error(l);
00419     }
00420     LuaState* luastate = static_cast<LuaState*>(lua_touserdata(l, -1));
00421     if (luastate == NULL)
00422     {
00423         lua_pushstring(l, "Internal Error: Invalid value in checkTimeslice");
00424         lua_error(l);
00425     }
00426 
00427     if (luastate->timesliceExpired())
00428     {
00429         const char* errormsg = "Timeout: script hasn't returned control to celestia (forgot to call wait()?)";
00430         cerr << errormsg << "\n";
00431         lua_pushstring(l, errormsg);
00432         lua_error(l);
00433     }
00434     return;
00435 }
00436 
00437 
00438 // allow the script to perform cleanup
00439 void LuaState::cleanup()
00440 {
00441     if (ioMode == Asking)
00442     {
00443         // Restore renderflags:
00444         CelestiaCore* appCore = getAppCore(costate, NoErrors);
00445         if (appCore != NULL)
00446         {
00447             lua_pushstring(state, "celestia-savedrenderflags");
00448             lua_gettable(state, LUA_REGISTRYINDEX);
00449             if (lua_isuserdata(state, -1))
00450             {
00451                 int* savedrenderflags = static_cast<int*>(lua_touserdata(state, -1));
00452                 appCore->getRenderer()->setRenderFlags(*savedrenderflags);
00453                 // now delete entry:
00454                 lua_pushstring(state, "celestia-savedrenderflags");
00455                 lua_pushnil(state);
00456                 lua_settable(state, LUA_REGISTRYINDEX);
00457             }
00458             lua_pop(state,1);
00459         }
00460     }
00461     lua_pushstring(costate, CleanupCallback);
00462     lua_gettable(costate, LUA_GLOBALSINDEX);
00463     if (lua_isnil(costate, -1))
00464     {
00465         return;
00466     }
00467     timeout = getTime() + 1.0;
00468     if (lua_pcall(costate, 0, 0, 0) != 0)
00469     {
00470         cerr << "Error while executing cleanup-callback: " << lua_tostring(costate, -1) << "\n";
00471     }
00472 }
00473 
00474 
00475 bool LuaState::createThread()
00476 {
00477     // Initialize the coroutine which wraps the script
00478     if (!(lua_isfunction(state, -1) && !lua_iscfunction(state, -1)))
00479     {
00480         // Should never happen; we manually set up the stack in C++
00481         assert(0);
00482         return false;
00483     }
00484     else
00485     {
00486         costate = lua_newthread(state);
00487         if (costate == NULL)
00488             return false;
00489         lua_sethook(costate, checkTimeslice, LUA_MASKCOUNT, 1000);
00490         lua_pushvalue(state, -2);
00491         lua_xmove(state, costate, 1);  /* move function from L to NL */
00492         alive = true;
00493         return true;
00494     }
00495 }
00496 
00497 
00498 string LuaState::getErrorMessage()
00499 {
00500     if (lua_gettop(state) > 0)
00501     {
00502         if (lua_isstring(state, -1))
00503             return lua_tostring(state, -1);
00504     }
00505     return "";
00506 }
00507 
00508 
00509 bool LuaState::timesliceExpired()
00510 {
00511     if (timeout < getTime())
00512     {
00513         // timeslice expired, make every instruction (including pcall) fail: 
00514         lua_sethook(costate, checkTimeslice, LUA_MASKCOUNT, 1);
00515         return true;
00516     }
00517     else
00518     {
00519         return false;
00520     }
00521 }
00522 
00523 
00524 static int auxresume(lua_State *L, lua_State *co, int narg)
00525 {
00526     int status;
00527 #if 0
00528     if (!lua_checkstack(co, narg))
00529         luaL_error(L, "too many arguments to resume");
00530 #endif
00531     lua_xmove(L, co, narg);
00532 
00533     status = lua_resume(co, narg);
00534     if (status == 0)
00535     {
00536         int nres = lua_gettop(co);
00537 #if 0
00538         if (!lua_checkstack(L, narg))
00539               luaL_error(L, "too many results to resume");
00540 #endif
00541         lua_xmove(co, L, nres);  // move yielded values
00542         return nres;
00543     }
00544     else
00545     {
00546         lua_xmove(co, L, 1);  // move error message
00547         return -1;            // error flag
00548     }
00549 }
00550 
00551 
00552 bool LuaState::isAlive() const
00553 {
00554     return alive;
00555 }
00556 
00557 
00558 struct ReadChunkInfo
00559 {
00560     char* buf;
00561     int bufSize;
00562     istream* in;
00563 };
00564 
00565 static const char* readStreamChunk(lua_State* state, void* udata, size_t* size)
00566 {
00567     assert(udata != NULL);
00568     if (udata == NULL)
00569         return NULL;
00570 
00571     ReadChunkInfo* info = reinterpret_cast<ReadChunkInfo*>(udata);
00572     assert(info->buf != NULL);
00573     assert(info->in != NULL);
00574 
00575     if (!info->in->good())
00576     {
00577         *size = 0;
00578         return NULL;
00579     }
00580 
00581     info->in->read(info->buf, info->bufSize);
00582     streamsize nread = info->in->gcount();
00583 
00584     *size = (size_t) nread;
00585     if (nread == 0)
00586         return NULL;
00587     else
00588         return info->buf;
00589 }
00590 
00591 
00592 // Callback for CelestiaCore::charEntered.
00593 // Returns true if keypress has been consumed 
00594 bool LuaState::charEntered(const char* c_p)
00595 {
00596     if (ioMode == Asking && getTime() > timeout)
00597     {
00598         int stackTop = lua_gettop(costate);
00599         if (strcmp(c_p, "y") == 0)
00600         {
00601             lua_iolibopen(costate);
00602             ioMode = IOAllowed;
00603         }
00604         else
00605         {
00606             ioMode = IODenied;
00607         }
00608         CelestiaCore* appCore = getAppCore(costate, NoErrors);
00609         if (appCore == NULL)
00610         {
00611             cerr << "ERROR: appCore not found\n";
00612             return true;
00613         }
00614         appCore->setTextEnterMode(appCore->getTextEnterMode() & ~CelestiaCore::KbPassToScript);
00615         appCore->showText("", 0, 0, 0, 0);
00616         // Restore renderflags:
00617         lua_pushstring(costate, "celestia-savedrenderflags");
00618         lua_gettable(costate, LUA_REGISTRYINDEX);
00619         if (lua_isuserdata(costate, -1))
00620         {
00621             int* savedrenderflags = static_cast<int*>(lua_touserdata(costate, -1));
00622             appCore->getRenderer()->setRenderFlags(*savedrenderflags);
00623             // now delete entry:
00624             lua_pushstring(costate, "celestia-savedrenderflags");
00625             lua_pushnil(costate);
00626             lua_settable(costate, LUA_REGISTRYINDEX);
00627         }
00628         else
00629         {
00630             cerr << "Oops, expected savedrenderflags to be userdata\n";
00631         }
00632         lua_settop(costate,stackTop);
00633         return true;
00634     }
00635     int stack_top = lua_gettop(costate);
00636     bool result = true;
00637     lua_pushstring(costate, KbdCallback);
00638     lua_gettable(costate, LUA_GLOBALSINDEX);
00639     lua_pushstring(costate, c_p);
00640     timeout = getTime() + 1.0;
00641     if (lua_pcall(costate, 1, 1, 0) != 0)
00642     {
00643         cerr << "Error while executing keyboard-callback: " << lua_tostring(costate, -1) << "\n";
00644         result = false;
00645     }
00646     else
00647     {
00648         if (lua_isboolean(costate, -1))
00649         {
00650             result = static_cast<bool>(lua_toboolean(costate, -1));
00651         }
00652     }
00653     // cleanup stack - is this necessary?
00654     lua_settop(costate, stack_top);
00655     return result;
00656 }
00657 
00658 
00659 int LuaState::loadScript(istream& in, const string& streamname)
00660 {
00661     char buf[4096];
00662     ReadChunkInfo info;
00663     info.buf = buf;
00664     info.bufSize = sizeof(buf);
00665     info.in = &in;
00666 
00667     if (streamname != "string")
00668     {
00669         lua_pushstring(state, "celestia-scriptpath");
00670         lua_pushstring(state, streamname.c_str());
00671         lua_settable(state, LUA_REGISTRYINDEX);
00672     }
00673 
00674     int status = lua_load(state, readStreamChunk, &info, streamname.c_str());
00675     if (status != 0)
00676         cout << "Error loading script: " << lua_tostring(state, -1) << '\n';
00677 
00678     return status;
00679 }
00680 
00681 int LuaState::loadScript(const string& s)
00682 {
00683 #ifdef HAVE_SSTREAM
00684     istringstream in(s);
00685 #else
00686     istrstream in(s.c_str());
00687 #endif
00688     return loadScript(in, "string");
00689 }
00690 
00691 int LuaState::resume()
00692 {
00693     assert(costate != NULL);
00694     if (costate == NULL)
00695         return false;
00696 
00697     lua_State* co = lua_tothread(state, -1);
00698     //assert(co == costate); // co can be NULL after error (top stack is errorstring)
00699     if (co != costate)
00700         return false;
00701 
00702     timeout = getTime() + MaxTimeslice;
00703     int nArgs = auxresume(state, co, 0);
00704     if (nArgs < 0)
00705     {
00706         alive = false;
00707 
00708         const char* errorMessage = lua_tostring(state, -1);
00709 
00710         // This is a nasty and hopefully temporary hack . . .  We continue
00711         // to resume the script until we get an error.  The
00712         // 'cannot resume dead coroutine' error appears when there were
00713         // no other errors, and execution terminates normally.  There
00714         // should be a better way to figure out whether the script ended
00715         // normally . . .
00716         if (strcmp(errorMessage, "cannot resume dead coroutine") != 0)
00717         {
00718             cout << "Error: " << errorMessage << '\n';
00719             CelestiaCore* appCore = getAppCore(co);
00720             if (appCore != NULL)
00721             {
00722                 CelestiaCore::Alerter* alerter = appCore->getAlerter();
00723                 alerter->fatalError(errorMessage);
00724             }
00725         }
00726 
00727         return 1; // just the error string
00728     }
00729     else
00730     {
00731         if (ioMode == Asking)
00732         {
00733             // timeout now is used to first only display warning, and 1s
00734             // later allow response to avoid accidental activation
00735             timeout = getTime() + 1.0;
00736         }
00737 
00738         return nArgs; // arguments from yield
00739     }
00740 }
00741 
00742 
00743 // get current linenumber of script and create
00744 // useful error-message
00745 static void doError(lua_State* l, const char* errorMsg)
00746 {
00747     lua_Debug debug;
00748     if (lua_getstack(l, 1, &debug))
00749     {
00750         char buf[1024];
00751         if (lua_getinfo(l, "l", &debug))
00752         {
00753             sprintf(buf, "In line %i: %s", debug.currentline, errorMsg);
00754             lua_pushstring(l, buf);
00755             lua_error(l);
00756         }
00757     }
00758     lua_pushstring(l, errorMsg);
00759     lua_error(l);
00760 }
00761 
00762 
00763 bool LuaState::tick(double dt)
00764 {
00765     // Due to the way CelestiaCore::tick is called (at least for KDE),
00766     // this method may be entered a second time when we show the error-alerter
00767     // Workaround: check if we are alive, return true(!) when we aren't anymore
00768     // this way the script isn't deleted after the second enter, but only
00769     // when we return from the first enter. OMG.
00770     
00771     // better Solution: defer showing the error-alterter to CelestiaCore, using
00772     // getErrorMessage()
00773     if (!isAlive())
00774         return false;
00775     
00776     if (ioMode == Asking)
00777     {
00778         CelestiaCore* appCore = getAppCore(costate, NoErrors);
00779         if (appCore == NULL)
00780         {
00781             cerr << "ERROR: appCore not found\n";
00782             return true;
00783         }
00784         lua_pushstring(state, "celestia-savedrenderflags");
00785         lua_gettable(state, LUA_REGISTRYINDEX);
00786         if (lua_isnil(state, -1))
00787         {
00788             lua_pushstring(state, "celestia-savedrenderflags");
00789             int* savedrenderflags = static_cast<int*>(lua_newuserdata(state, sizeof(int)));
00790             *savedrenderflags = appCore->getRenderer()->getRenderFlags();
00791             lua_settable(state, LUA_REGISTRYINDEX);
00792             appCore->getRenderer()->setRenderFlags(0);
00793         }
00794         // now pop result of gettable
00795         lua_pop(state, 1);
00796 
00797         if (getTime() > timeout)
00798         {
00799             appCore->showText("WARNING:\n\nThis script requests permission to read/write files\n"
00800                               "and execute external programs. Allowing this can be\n"
00801                               "dangerous.\n"
00802                               "Do you trust the script and want to allow this?\n\n"
00803                               "y = yes, ESC = cancel script, any other key = no",
00804                               0, 0,
00805                               -15, 5, 5);
00806             appCore->setTextEnterMode(appCore->getTextEnterMode() | CelestiaCore::KbPassToScript);
00807         }
00808         else
00809         {
00810             appCore->showText("WARNING:\n\nThis script requests permission to read/write files\n"
00811                               "and execute external programs. Allowing this can be\n"
00812                               "dangerous.\n"
00813                               "Do you trust the script and want to allow this?",
00814                               0, 0,
00815                               -15, 5, 5);
00816             appCore->setTextEnterMode(appCore->getTextEnterMode() & ~CelestiaCore::KbPassToScript);
00817         }
00818 
00819         return false;
00820     }
00821 
00822     if (dt == 0 || scriptAwakenTime > getTime())
00823         return false;
00824    
00825     int nArgs = resume();
00826     if (!isAlive())
00827     {
00828         // The script is complete
00829         return true;
00830     }
00831     else
00832     {
00833         // The script has returned control to us, but it is not completed.
00834         lua_State* state = getState();
00835 
00836         // The values on the stack indicate what event will wake up the
00837         // script.  For now, we just support wait()
00838         double delay;
00839         if (nArgs == 1 && lua_isnumber(state, -1))
00840             delay = lua_tonumber(state, -1);
00841         else
00842             delay = 0.0;
00843         scriptAwakenTime = getTime() + delay;
00844 
00845         // Clean up the stack
00846         lua_pop(state, nArgs);
00847         return false;
00848     }
00849 }
00850 
00851 
00852 void LuaState::requestIO()
00853 {
00854     // the script requested IO, set the mode
00855     // so we display the warning during tick
00856     // and can request keyboard. We can't do this now
00857     // because the script is still active and could
00858     // disable keyboard again.
00859     if (ioMode == NoIO)
00860     {
00861         CelestiaCore* appCore = getAppCore(state, AllErrors);
00862         string policy = appCore->getConfig()->scriptSystemAccessPolicy;
00863         if (policy == "allow")
00864         {
00865             //lua_iolibopen(costate);
00866             luaopen_io(costate);
00867             ioMode = IOAllowed;
00868         }
00869         else if (policy == "deny")
00870         {
00871             ioMode = IODenied;
00872         }
00873         else
00874         {
00875             ioMode = Asking;
00876         }
00877     }
00878 }
00879 
00880 // Check if the number of arguments on the stack matches
00881 // the allowed range [minArgs, maxArgs]. Cause an error if not. 
00882 static void checkArgs(lua_State* l,
00883                       int minArgs, int maxArgs, const char* errorMessage)
00884 {
00885     int argc = lua_gettop(l);
00886     if (argc < minArgs || argc > maxArgs)
00887     {
00888         doError(l, errorMessage);
00889     }
00890 }
00891 
00892 
00893 static astro::CoordinateSystem parseCoordSys(const string& name)
00894 {
00895     if (compareIgnoringCase(name, "universal") == 0)
00896         return astro::Universal;
00897     else if (compareIgnoringCase(name, "ecliptic") == 0)
00898         return astro::Ecliptical;
00899     else if (compareIgnoringCase(name, "equatorial") == 0)
00900         return astro::Equatorial;
00901     else if (compareIgnoringCase(name, "planetographic") == 0)
00902         return astro::Geographic;
00903     else if (compareIgnoringCase(name, "observer") == 0)
00904         return astro::ObserverLocal;
00905     else if (compareIgnoringCase(name, "lock") == 0)
00906         return astro::PhaseLock;
00907     else if (compareIgnoringCase(name, "chase") == 0)
00908         return astro::Chase;
00909     else
00910         return astro::Universal;
00911 }
00912 
00913 
00914 static Marker::Symbol parseMarkerSymbol(const string& name)
00915 {
00916     if (compareIgnoringCase(name, "diamond") == 0)
00917         return Marker::Diamond;
00918     else if (compareIgnoringCase(name, "triangle") == 0)
00919         return Marker::Triangle;
00920     else if (compareIgnoringCase(name, "square") == 0)
00921         return Marker::Square;
00922     else if (compareIgnoringCase(name, "plus") == 0)
00923         return Marker::Plus;
00924     else if (compareIgnoringCase(name, "x") == 0)
00925         return Marker::X;
00926     else
00927         return Marker::Diamond;
00928 }
00929 
00930 
00931 // Get a pointer to the LuaState-object from the registry:
00932 LuaState* getLuaStateObject(lua_State* l)
00933 {
00934     int stackSize = lua_gettop(l);
00935     lua_pushstring(l, "celestia-luastate");
00936     lua_gettable(l, LUA_REGISTRYINDEX);
00937 
00938     if (!lua_islightuserdata(l, -1))
00939     {
00940         doError(l, "Internal Error: Invalid table entry for LuaState-pointer");
00941     }
00942     LuaState* luastate_ptr = static_cast<LuaState*>(lua_touserdata(l, -1));
00943     if (luastate_ptr == NULL)
00944     {
00945         doError(l, "Internal Error: Invalid LuaState-pointer");
00946     }
00947     lua_settop(l, stackSize);
00948     return luastate_ptr;
00949 }
00950 
00951 
00952 // Map the observer to its View. Return NULL if no view exists 
00953 // for this observer (anymore).
00954 View* getViewByObserver(CelestiaCore* appCore, Observer* obs)
00955 {
00956     for (unsigned int i = 0; i < appCore->views.size(); i++)
00957     {
00958         if ((appCore->views[i])->observer == obs)
00959         {
00960             return appCore->views[i];
00961         }
00962     }
00963     return NULL;
00964 }
00965 
00966 // Fill list with all Observers
00967 void getObservers(CelestiaCore* appCore, vector<Observer*>& list)
00968 {
00969     for (unsigned int i = 0; i < appCore->views.size(); i++)
00970     {
00971         list.push_back(appCore->views[i]->observer);
00972     }
00973 }
00974 
00975 // ==================== Helpers ====================
00976 
00977 // safe wrapper for lua_tostring: fatal errors will terminate script by calling
00978 // lua_error with errorMsg.
00979 static const char* safeGetString(lua_State* l, int index, FatalErrors fatalErrors = AllErrors,
00980                                  const char* errorMsg = "String argument expected")
00981 {
00982     if (l == NULL)
00983     {
00984         cerr << "Error: LuaState invalid in safeGetString\n";
00985         cout << "Error: LuaState invalid in safeGetString\n";
00986         return NULL;
00987     }
00988     int argc = lua_gettop(l);
00989     if (index < 1 || index > argc)
00990     {
00991         if (fatalErrors & WrongArgc)
00992         {
00993             doError(l, errorMsg);
00994         }
00995         else
00996             return NULL;
00997     }
00998     if (!lua_isstring(l, index))
00999     {
01000         if (fatalErrors & WrongType)
01001         {
01002             doError(l, errorMsg);
01003         }
01004         else
01005             return NULL;
01006     }
01007     return lua_tostring(l, index);
01008 }
01009 
01010 // safe wrapper for lua_tonumber, c.f. safeGetString
01011 // Non-fatal errors will return  defaultValue.
01012 static lua_Number safeGetNumber(lua_State* l, int index, FatalErrors fatalErrors = AllErrors,
01013                                  const char* errorMsg = "Numeric argument expected",
01014                                  lua_Number defaultValue = 0.0)
01015 {
01016     if (l == NULL)
01017     {
01018         cerr << "Error: LuaState invalid in safeGetNumber\n";
01019         cout << "Error: LuaState invalid in safeGetNumber\n";
01020         return 0.0;
01021     }
01022     int argc = lua_gettop(l);
01023     if (index < 1 || index > argc)
01024     {
01025         if (fatalErrors & WrongArgc)
01026         {
01027             doError(l, errorMsg);
01028         }
01029         else
01030             return defaultValue;
01031     }
01032     if (!lua_isnumber(l, index))
01033     {
01034         if (fatalErrors & WrongType)
01035         {
01036             doError(l, errorMsg);
01037         }
01038         else
01039             return defaultValue;
01040     }
01041     return lua_tonumber(l, index);
01042 }
01043 
01044 
01045 // Add a field to the table on top of the stack
01046 static void setTable(lua_State* l, const char* field, lua_Number value)
01047 {
01048     lua_pushstring(l, field);
01049     lua_pushnumber(l, value);
01050     lua_settable(l, -3);
01051 }
01052 
01053 static void setTable(lua_State* l, const char* field, const char* value)
01054 {
01055     lua_pushstring(l, field);
01056     lua_pushstring(l, value);
01057     lua_settable(l, -3);
01058 }
01059 
01060 // ==================== forward declarations ====================
01061 
01062 // must be declared for vector_add:
01063 static UniversalCoord* to_position(lua_State* l, int index);
01064 static int position_new(lua_State* l, const UniversalCoord& uc);
01065 // for frame_getrefobject / gettargetobject
01066 static int object_new(lua_State* l, const Selection& sel);
01067 // for vector_mult
01068 static Quatd* to_rotation(lua_State* l, int index);
01069 static int rotation_new(lua_State* l, const Quatd& qd);
01070 
01071 // ==================== Vector ====================
01072 static int vector_new(lua_State* l, const Vec3d& v)
01073 {
01074     Vec3d* v3 = reinterpret_cast<Vec3d*>(lua_newuserdata(l, sizeof(Vec3d)));
01075     *v3 = v;
01076     SetClass(l, _Vec3);
01077 
01078     return 1;
01079 }
01080 
01081 static Vec3d* to_vector(lua_State* l, int index)
01082 {
01083     return static_cast<Vec3d*>(CheckUserData(l, index, _Vec3));
01084 }
01085 
01086 static Vec3d* this_vector(lua_State* l)
01087 {
01088     Vec3d* v3 = to_vector(l, 1);
01089     if (v3 == NULL)
01090     {
01091         doError(l, "Bad vector object!");
01092     }
01093 
01094     return v3;
01095 }
01096 
01097 
01098 static int vector_sub(lua_State* l)
01099 {
01100     checkArgs(l, 2, 2, "Need two operands for sub");
01101     Vec3d* op1 = to_vector(l, 1);
01102     Vec3d* op2 = to_vector(l, 2);
01103     if (op1 == NULL || op2 == NULL)
01104     {
01105         doError(l, "Subtraction only defined for two vectors");
01106     }
01107     else
01108     {
01109         Vec3d result = *op1 - *op2;
01110         vector_new(l, result);
01111     }
01112     return 1;
01113 }
01114 
01115 static int vector_get(lua_State* l)
01116 {
01117     checkArgs(l, 2, 2, "Invalid access of vector-component");
01118     Vec3d* v3 = this_vector(l);
01119     string key = safeGetString(l, 2, AllErrors, "Invalid key in vector-access");
01120     double value = 0.0;
01121     if (key == "x")
01122         value = v3->x;
01123     else if (key == "y")
01124         value = v3->y;
01125     else if (key == "z")
01126         value = v3->z;
01127     else
01128     {
01129         if (lua_getmetatable(l, 1))
01130         {
01131             lua_pushvalue(l, 2);
01132             lua_rawget(l, -2);
01133             return 1;
01134         }
01135         else
01136         {
01137             doError(l, "Internal error: couldn't get metatable");
01138         }
01139     }
01140     lua_pushnumber(l, (lua_Number)value);
01141     return 1;
01142 }
01143 
01144 static int vector_set(lua_State* l)
01145 {
01146     checkArgs(l, 3, 3, "Invalid access of vector-component");
01147     Vec3d* v3 = this_vector(l);
01148     string key = safeGetString(l, 2, AllErrors, "Invalid key in vector-access");
01149     double value = safeGetNumber(l, 3, AllErrors, "Vector components must be numbers");
01150     if (key == "x")
01151         v3->x = value;
01152     else if (key == "y")
01153         v3->y = value;
01154     else if (key == "z")
01155         v3->z = value;
01156     else
01157     {
01158         doError(l, "Invalid key in vector-access");
01159     }
01160     return 0;
01161 }
01162 
01163 static int vector_getx(lua_State* l)
01164 {
01165     checkArgs(l, 1, 1, "No arguments expected for vector:getx");
01166     Vec3d* v3 = this_vector(l);
01167     lua_Number x;
01168     x = static_cast<lua_Number>(v3->x);
01169     lua_pushnumber(l, x);
01170 
01171     return 1;
01172 }
01173 
01174 static int vector_gety(lua_State* l)
01175 {
01176     checkArgs(l, 1, 1, "No arguments expected for vector:gety");
01177     Vec3d* v3 = this_vector(l);
01178     lua_Number y;
01179     y = static_cast<lua_Number>(v3->y);
01180     lua_pushnumber(l, y);
01181 
01182     return 1;
01183 }
01184 
01185 static int vector_getz(lua_State* l)
01186 {
01187     checkArgs(l, 1, 1, "No arguments expected for vector:getz");
01188     Vec3d* v3 = this_vector(l);
01189     lua_Number z;
01190     z = static_cast<lua_Number>(v3->z);
01191     lua_pushnumber(l, z);
01192 
01193     return 1;
01194 }
01195 
01196 static int vector_normalize(lua_State* l)
01197 {
01198     checkArgs(l, 1, 1, "No arguments expected for vector:normalize");
01199     Vec3d* v = this_vector(l);
01200     Vec3d vn(*v);
01201     vn.normalize();
01202     vector_new(l, vn);
01203     return 1;
01204 }
01205 
01206 static int vector_length(lua_State* l)
01207 {
01208     checkArgs(l, 1, 1, "No arguments expected for vector:length");
01209     Vec3d* v = this_vector(l);
01210     double length = v->length();
01211     lua_pushnumber(l, (lua_Number)length);
01212     return 1;
01213 }
01214 
01215 static int vector_add(lua_State* l)
01216 {
01217     checkArgs(l, 2, 2, "Need two operands for addition");
01218     Vec3d* v1 = NULL;
01219     Vec3d* v2 = NULL;
01220     UniversalCoord* p = NULL;
01221 
01222     if (istype(l, 1, _Vec3) && istype(l, 2, _Vec3))
01223     {
01224         v1 = to_vector(l, 1);
01225         v2 = to_vector(l, 2);
01226         vector_new(l, *v1 + *v2);
01227     }
01228     else
01229     if (istype(l, 1, _Vec3) && istype(l, 2, _Position))
01230     {
01231         v1 = to_vector(l, 1);
01232         p = to_position(l, 2);
01233         position_new(l, *p + *v1);
01234     }
01235     else
01236     {
01237         doError(l, "Bad vector addition!");
01238     }
01239     return 1;
01240 }
01241 
01242 static int vector_mult(lua_State* l)
01243 {
01244     checkArgs(l, 2, 2, "Need two operands for multiplication");
01245     Vec3d* v1 = NULL;
01246     Vec3d* v2 = NULL;
01247     Quatd* q = NULL;
01248     lua_Number s = 0.0;
01249     if (istype(l, 1, _Vec3) && istype(l, 2, _Vec3))
01250     {
01251         v1 = to_vector(l, 1);
01252         v2 = to_vector(l, 2);
01253         lua_pushnumber(l, *v1 * *v2);
01254     }
01255     else
01256     if (istype(l, 1, _Vec3) && lua_isnumber(l, 2))
01257     {
01258         v1 = to_vector(l, 1);
01259         s = lua_tonumber(l, 2);
01260         vector_new(l, *v1 * s);
01261     }
01262     else
01263     if (istype(l, 1, _Vec3) && istype(l, 2, _Rotation))
01264     {
01265         v1 = to_vector(l, 1);
01266         q = to_rotation(l, 2);
01267         rotation_new(l, *v1 * *q);
01268     }
01269     else
01270     if (lua_isnumber(l, 1) && istype(l, 2, _Vec3))
01271     {
01272         s = lua_tonumber(l, 1);
01273         v1 = to_vector(l, 2);
01274         vector_new(l, *v1 * s);
01275     }
01276     else
01277     {
01278         doError(l, "Bad vector multiplication!");
01279     }
01280     return 1;
01281 }
01282 
01283 static int vector_cross(lua_State* l)
01284 {
01285     checkArgs(l, 2, 2, "Need two operands for multiplication");
01286     Vec3d* v1 = NULL;
01287     Vec3d* v2 = NULL;
01288     if (istype(l, 1, _Vec3) && istype(l, 2, _Vec3))
01289     {
01290         v1 = to_vector(l, 1);
01291         v2 = to_vector(l, 2);
01292         vector_new(l, *v1 ^ *v2);
01293     }
01294     else
01295     {
01296         doError(l, "Bad vector multiplication!");
01297     }
01298     return 1;
01299 
01300 }
01301 
01302 static int vector_tostring(lua_State* l)
01303 {
01304     lua_pushstring(l, "[Vector]");
01305     return 1;
01306 }
01307 
01308 static void CreateVectorMetaTable(lua_State* l)
01309 {
01310     CreateClassMetatable(l, _Vec3);
01311 
01312     RegisterMethod(l, "__tostring", vector_tostring);
01313     RegisterMethod(l, "__add", vector_add);
01314     RegisterMethod(l, "__sub", vector_sub);
01315     RegisterMethod(l, "__mul", vector_mult);
01316     RegisterMethod(l, "__pow", vector_cross);
01317     RegisterMethod(l, "__index", vector_get);
01318     RegisterMethod(l, "__newindex", vector_set);
01319     RegisterMethod(l, "getx", vector_getx);
01320     RegisterMethod(l, "gety", vector_gety);
01321     RegisterMethod(l, "getz", vector_getz);
01322     RegisterMethod(l, "normalize", vector_normalize);
01323     RegisterMethod(l, "length", vector_length);
01324 
01325     lua_pop(l, 1); // remove metatable from stack
01326 }
01327 
01328 // ==================== Rotation ====================
01329 static int rotation_new(lua_State* l, const Quatd& qd)
01330 {
01331     Quatd* q = reinterpret_cast<Quatd*>(lua_newuserdata(l, sizeof(Quatd)));
01332     *q = qd;
01333     SetClass(l, _Rotation);
01334 
01335     return 1;
01336 }
01337 
01338 static Quatd* to_rotation(lua_State* l, int index)
01339 {
01340     return static_cast<Quatd*>(CheckUserData(l, index, _Rotation));
01341 }
01342 
01343 static Quatd* this_rotation(lua_State* l)
01344 {
01345     Quatd* q = to_rotation(l, 1);
01346     if (q == NULL)
01347     {
01348         doError(l, "Bad rotation object!");
01349     }
01350 
01351     return q;
01352 }
01353 
01354 
01355 static int rotation_add(lua_State* l)
01356 {
01357     checkArgs(l, 2, 2, "Need two operands for add");
01358     Quatd* q1 = to_rotation(l, 1);
01359     Quatd* q2 = to_rotation(l, 2);
01360     if (q1 == NULL || q2 == NULL)
01361     {
01362         doError(l, "Addition only defined for two rotations");
01363     }
01364     else
01365     {
01366         Quatd result = *q1 + *q2;
01367         rotation_new(l, result);
01368     }
01369     return 1;
01370 }
01371 
01372 static int rotation_mult(lua_State* l)
01373 {
01374     checkArgs(l, 2, 2, "Need two operands for multiplication");
01375     Quatd* r1 = NULL;
01376     Quatd* r2 = NULL;
01377     Vec3d* v = NULL;
01378     lua_Number s = 0.0;
01379     if (istype(l, 1, _Rotation) && istype(l, 2, _Rotation))
01380     {
01381         r1 = to_rotation(l, 1);
01382         r2 = to_rotation(l, 2);
01383         rotation_new(l, *r1 * *r2);
01384     }
01385     else
01386     if (istype(l, 1, _Rotation) && lua_isnumber(l, 2))
01387     {
01388         r1 = to_rotation(l, 1);
01389         s = lua_tonumber(l, 2);
01390         rotation_new(l, *r1 * s);
01391     }
01392     else
01393     if (lua_isnumber(l, 1) && istype(l, 2, _Rotation))
01394     {
01395         s = lua_tonumber(l, 1);
01396         r1 = to_rotation(l, 2);
01397         rotation_new(l, *r1 * s);
01398     }
01399     else
01400     {
01401         doError(l, "Bad rotation multiplication!");
01402     }
01403     return 1;
01404 }
01405 
01406 static int rotation_imag(lua_State* l)
01407 {
01408     checkArgs(l, 1, 1, "No arguments expected for rotation_imag");
01409     Quatd* q = this_rotation(l);
01410     vector_new(l, imag(*q));
01411     return 1;
01412 }
01413 
01414 static int rotation_real(lua_State* l)
01415 {
01416     checkArgs(l, 1, 1, "No arguments expected for rotation_real");
01417     Quatd* q = this_rotation(l);
01418     lua_pushnumber(l, real(*q));
01419     return 1;
01420 }
01421 
01422 static int rotation_transform(lua_State* l)
01423 {
01424     checkArgs(l, 2, 2, "One argument expected for rotation:transform()");
01425     Quatd* q = this_rotation(l);
01426     Vec3d* v = to_vector(l, 2);
01427     if (v == NULL)
01428     {
01429         doError(l, "Argument to rotation:transform() must be a vector");
01430     }
01431     vector_new(l, *v * q->toMatrix3());
01432     return 1;
01433 }
01434 
01435 static int rotation_setaxisangle(lua_State* l)
01436 {
01437     checkArgs(l, 3, 3, "Two arguments expected for rotation:setaxisangle()");
01438     Quatd* q = this_rotation(l);
01439     Vec3d* v = to_vector(l, 2);
01440     if (v == NULL)
01441     {
01442         doError(l, "setaxisangle: first argument must be a vector");
01443     }
01444     double angle = safeGetNumber(l, 3, AllErrors, "second argument to rotation:setaxisangle must be a number");
01445     q->setAxisAngle(*v, angle);
01446     return 0;
01447 }
01448 
01449 static int rotation_slerp(lua_State* l)
01450 {
01451     checkArgs(l, 3, 3, "Two arguments expected for rotation:slerp()");
01452     Quatd* q1 = this_rotation(l);
01453     Quatd* q2 = to_rotation(l, 2);
01454     if (q2 == NULL)
01455     {
01456         doError(l, "slerp: first argument must be a rotation");
01457     }
01458     double t = safeGetNumber(l, 3, AllErrors, "second argument to rotation:slerp must be a number");
01459     rotation_new(l, Quatd::slerp(*q1, *q2, t));
01460     return 1;
01461 }
01462 
01463 static int rotation_get(lua_State* l)
01464 {
01465     checkArgs(l, 2, 2, "Invalid access of rotation-component");
01466     Quatd* q3 = this_rotation(l);
01467     string key = safeGetString(l, 2, AllErrors, "Invalid key in rotation-access");
01468     double value = 0.0;
01469     if (key == "x")
01470         value = imag(*q3).x;
01471     else if (key == "y")
01472         value = imag(*q3).y;
01473     else if (key == "z")
01474         value = imag(*q3).z;
01475     else if (key == "w")
01476         value = real(*q3);
01477     else
01478     {
01479         if (lua_getmetatable(l, 1))
01480         {
01481             lua_pushvalue(l, 2);
01482             lua_rawget(l, -2);
01483             return 1;
01484         }
01485         else
01486         {
01487             doError(l, "Internal error: couldn't get metatable");
01488         }
01489     }
01490     lua_pushnumber(l, (lua_Number)value);
01491     return 1;
01492 }
01493 
01494 static int rotation_set(lua_State* l)
01495 {
01496     checkArgs(l, 3, 3, "Invalid access of rotation-component");
01497     Quatd* q3 = this_rotation(l);
01498     string key = safeGetString(l, 2, AllErrors, "Invalid key in rotation-access");
01499     double value = safeGetNumber(l, 3, AllErrors, "Rotation components must be numbers");
01500     Vec3d v = imag(*q3);
01501     double w = real(*q3);
01502     if (key == "x")
01503         v.x = value;
01504     else if (key == "y")
01505         v.y = value;
01506     else if (key == "z")
01507         v.z = value;
01508     else if (key == "w")
01509         w = value;
01510     else
01511     {
01512         doError(l, "Invalid key in rotation-access");
01513     }
01514     *q3 = Quatd(w, v);
01515     return 0;
01516 }
01517 
01518 static int rotation_tostring(lua_State* l)
01519 {
01520     lua_pushstring(l, "[Rotation]");
01521     return 1;
01522 }
01523 
01524 static void CreateRotationMetaTable(lua_State* l)
01525 {
01526     CreateClassMetatable(l, _Rotation);
01527 
01528     RegisterMethod(l, "real", rotation_real);
01529     RegisterMethod(l, "imag", rotation_imag);
01530     RegisterMethod(l, "transform", rotation_transform);
01531     RegisterMethod(l, "setaxisangle", rotation_setaxisangle);
01532     RegisterMethod(l, "slerp", rotation_slerp);
01533     RegisterMethod(l, "__tostring", rotation_tostring);
01534     RegisterMethod(l, "__add", rotation_add);
01535     RegisterMethod(l, "__mul", rotation_mult);
01536     RegisterMethod(l, "__index", rotation_get);
01537     RegisterMethod(l, "__newindex", rotation_set);
01538 
01539     lua_pop(l, 1); // remove metatable from stack
01540 }
01541 
01542 // ==================== Position ====================
01543 // a 128-bit per component universal coordinate
01544 static int position_new(lua_State* l, const UniversalCoord& uc)
01545 {
01546     UniversalCoord* ud = reinterpret_cast<UniversalCoord*>(lua_newuserdata(l, sizeof(UniversalCoord)));
01547     *ud = uc;
01548 
01549     SetClass(l, _Position);
01550 
01551     return 1;
01552 }
01553 
01554 static UniversalCoord* to_position(lua_State* l, int index)
01555 {
01556     return static_cast<UniversalCoord*>(CheckUserData(l, index, _Position));
01557 }
01558 
01559 static UniversalCoord* this_position(lua_State* l)
01560 {
01561     UniversalCoord* uc = to_position(l, 1);
01562     if (uc == NULL)
01563     {
01564         doError(l, "Bad position object!");
01565     }
01566 
01567     return uc;
01568 }
01569 
01570 
01571 static int position_get(lua_State* l)
01572 {
01573     checkArgs(l, 2, 2, "Invalid access of position-component");
01574     UniversalCoord* uc = this_position(l);
01575     string key = safeGetString(l, 2, AllErrors, "Invalid key in position-access");
01576     double value = 0.0;
01577     if (key == "x")
01578         value = uc->x;
01579     else if (key == "y")
01580         value = uc->y;
01581     else if (key == "z")
01582         value = uc->z;
01583     else
01584     {
01585         if (lua_getmetatable(l, 1))
01586         {
01587             lua_pushvalue(l, 2);
01588             lua_rawget(l, -2);
01589             return 1;
01590         }
01591         else
01592         {
01593             doError(l, "Internal error: couldn't get metatable");
01594         }
01595     }
01596     lua_pushnumber(l, (lua_Number)value);
01597     return 1;
01598 }
01599 
01600 static int position_set(lua_State* l)
01601 {
01602     checkArgs(l, 3, 3, "Invalid access of position-component");
01603     UniversalCoord* uc = this_position(l);
01604     string key = safeGetString(l, 2, AllErrors, "Invalid key in position-access");
01605     double value = safeGetNumber(l, 3, AllErrors, "Position components must be numbers");
01606     if (key == "x")
01607         uc->x = value;
01608     else if (key == "y")
01609         uc->y = value;
01610     else if (key == "z")
01611         uc->z = value;
01612     else
01613     {
01614         doError(l, "Invalid key in position-access");
01615     }
01616     return 0;
01617 }
01618 
01619 static int position_getx(lua_State* l)
01620 {
01621     checkArgs(l, 1, 1, "No arguments expected for position:getx()");
01622 
01623     UniversalCoord* uc = this_position(l);
01624     lua_Number x;
01625     x = uc->x;
01626     lua_pushnumber(l, x);
01627 
01628     return 1;
01629 }
01630 
01631 static int position_gety(lua_State* l)
01632 {
01633     checkArgs(l, 1, 1, "No arguments expected for position:gety()");
01634 
01635     UniversalCoord* uc = this_position(l);
01636     lua_Number y;
01637     y = uc->y;
01638     lua_pushnumber(l, y);
01639 
01640     return 1;
01641 }
01642 
01643 static int position_getz(lua_State* l)
01644 {
01645     checkArgs(l, 1, 1, "No arguments expected for position:getz()");
01646 
01647     UniversalCoord* uc = this_position(l);
01648     lua_Number z;
01649     z = uc->z;
01650     lua_pushnumber(l, z);
01651 
01652     return 1;
01653 }
01654 
01655 static int position_vectorto(lua_State* l)
01656 {
01657     checkArgs(l, 2, 2, "One argument expected to position:vectorto");
01658 
01659     UniversalCoord* uc = this_position(l);
01660     UniversalCoord* uc2 = to_position(l, 2);
01661 
01662     if (uc2 == NULL)
01663     {
01664         doError(l, "Argument to position:vectorto must be a position");
01665     }
01666     Vec3d diff = *uc2 - *uc;
01667     vector_new(l, diff);
01668     return 1;
01669 }
01670 
01671 static int position_orientationto(lua_State* l)
01672 {
01673     checkArgs(l, 3, 3, "Two arguments expected for position:orientationto");
01674 
01675     UniversalCoord* src = this_position(l);
01676     UniversalCoord* target = to_position(l, 2);
01677 
01678     if (target == NULL)
01679     {
01680         doError(l, "First argument to position:orientationto must be a position");
01681     }
01682 
01683     Vec3d* upd = to_vector(l, 3);
01684     if (upd == NULL)
01685     {
01686         doError(l, "Second argument to position:orientationto must be a vector");
01687     }
01688 
01689     Vec3d src2target = *target - *src;
01690     src2target.normalize();
01691     Vec3d v = src2target ^ *upd;
01692     v.normalize();
01693     Vec3d u = v ^ src2target;
01694     Quatd qd = Quatd(Mat3d(v, u, -src2target));
01695     rotation_new(l, qd);
01696 
01697     return 1;
01698 }
01699 
01700 static int position_tostring(lua_State* l)
01701 {
01702     // TODO: print out the coordinate as it would appear in a cel:// URL
01703     lua_pushstring(l, "[Position]");
01704 
01705     return 1;
01706 }
01707 
01708 static int position_distanceto(lua_State* l)
01709 {
01710     checkArgs(l, 2, 2, "One argument expected to position:distanceto()");
01711 
01712     UniversalCoord* uc = this_position(l);
01713     UniversalCoord* uc2 = to_position(l, 2);
01714     if (uc2 == NULL)
01715     {
01716         doError(l, "Position expected as argument to position:distanceto");
01717     }
01718 
01719     Vec3d v = *uc2 - *uc;
01720     lua_pushnumber(l, astro::microLightYearsToKilometers(v.length()));
01721 
01722     return 1;
01723 }
01724 
01725 static int position_add(lua_State* l)
01726 {
01727     checkArgs(l, 2, 2, "Need two operands for addition");
01728     UniversalCoord* p1 = NULL;
01729     UniversalCoord* p2 = NULL;
01730     Vec3d* v2 = NULL;
01731 
01732     if (istype(l, 1, _Position) && istype(l, 2, _Position))
01733     {
01734         p1 = to_position(l, 1);
01735         p2 = to_position(l, 2);
01736         // this is not very intuitive, as p1-p2 is a vector
01737         position_new(l, *p1 + *p2);
01738     }
01739     else
01740     if (istype(l, 1, _Position) && istype(l, 2, _Vec3))
01741     {
01742         p1 = to_position(l, 1);
01743         v2 = to_vector(l, 2);
01744         position_new(l, *p1 + *v2);
01745     }
01746     else
01747     {
01748         doError(l, "Bad position addition!");
01749     }
01750     return 1;
01751 }
01752 
01753 static int position_sub(lua_State* l)
01754 {
01755     checkArgs(l, 2, 2, "Need two operands for subtraction");
01756     UniversalCoord* p1 = NULL;
01757     UniversalCoord* p2 = NULL;
01758     Vec3d* v2 = NULL;
01759 
01760     if (istype(l, 1, _Position) && istype(l, 2, _Position))
01761     {
01762         p1 = to_position(l, 1);
01763         p2 = to_position(l, 2);
01764         vector_new(l, *p1 - *p2);
01765     }
01766     else
01767     if (istype(l, 1, _Position) && istype(l, 2, _Vec3))
01768     {
01769         p1 = to_position(l, 1);
01770         v2 = to_vector(l, 2);
01771         position_new(l, *p1 - *v2);
01772     }
01773     else
01774     {
01775         doError(l, "Bad position subtraction!");
01776     }
01777     return 1;
01778 }
01779 
01780 static int position_addvector(lua_State* l)
01781 {
01782     checkArgs(l, 2, 2, "One argument expected to position:addvector()");
01783     UniversalCoord* uc = this_position(l);
01784     Vec3d* v3d = to_vector(l, 2);
01785     if (v3d == NULL)
01786     {
01787         doError(l, "Vector expected as argument to position:addvector");
01788     }
01789     else
01790     if (uc != NULL && v3d != NULL)
01791     {
01792         UniversalCoord ucnew = *uc + *v3d;
01793         position_new(l, ucnew);
01794     }
01795     return 1;
01796 }
01797 
01798 static void CreatePositionMetaTable(lua_State* l)
01799 {
01800     CreateClassMetatable(l, _Position);
01801 
01802     RegisterMethod(l, "__tostring", position_tostring);
01803     RegisterMethod(l, "distanceto", position_distanceto);
01804     RegisterMethod(l, "vectorto", position_vectorto);
01805     RegisterMethod(l, "orientationto", position_orientationto);
01806     RegisterMethod(l, "addvector", position_addvector);
01807     RegisterMethod(l, "__add", position_add);
01808     RegisterMethod(l, "__sub", position_sub);
01809     RegisterMethod(l, "__index", position_get);
01810     RegisterMethod(l, "__newindex", position_set);
01811     RegisterMethod(l, "getx", position_getx);
01812     RegisterMethod(l, "gety", position_gety);
01813     RegisterMethod(l, "getz", position_getz);
01814 
01815     lua_pop(l, 1); // remove metatable from stack
01816 }
01817 
01818 // ==================== FrameOfReference ====================
01819 static int frame_new(lua_State* l, const FrameOfReference& f)
01820 {
01821     FrameOfReference* ud = reinterpret_cast<FrameOfReference*>(lua_newuserdata(l, sizeof(FrameOfReference)));
01822     *ud = f;
01823 
01824     SetClass(l, _Frame);
01825 
01826     return 1;
01827 }
01828 
01829 static FrameOfReference* to_frame(lua_State* l, int index)
01830 {
01831     return static_cast<FrameOfReference*>(CheckUserData(l, index, _Frame));
01832 }
01833 
01834 static FrameOfReference* this_frame(lua_State* l)
01835 {
01836     FrameOfReference* f = to_frame(l, 1);
01837     if (f == NULL)
01838     {
01839         doError(l, "Bad frame object!");
01840     }
01841 
01842     return f;
01843 }
01844 
01845 
01846 // Convert from frame coordinates to universal.
01847 static int frame_from(lua_State* l)
01848 {
01849     checkArgs(l, 2, 3, "Two or three arguments required for frame:from");
01850 
01851     FrameOfReference* frame = this_frame(l);
01852     CelestiaCore* appCore = getAppCore(l, AllErrors);
01853 
01854     RigidTransform rt;
01855 
01856     UniversalCoord* uc = NULL;
01857     Quatd* q = NULL;
01858     double jd = 0.0;
01859 
01860     if (istype(l, 2, _Position))
01861     {
01862         uc = to_position(l, 2);
01863     }
01864     else if (istype(l, 2, _Rotation))
01865     {
01866         q = to_rotation(l, 2);
01867     }
01868     if (uc == NULL && q == NULL)
01869     {
01870         doError(l, "Position or rotation expected as second argument to frame:from()");
01871     }
01872 
01873     jd = safeGetNumber(l, 3, WrongType, "Second arg to frame:from must be a number", appCore->getSimulation()->getTime());
01874 
01875     if (uc != NULL)
01876     {
01877         rt.translation = *uc;
01878         rt = frame->toUniversal(rt, jd);
01879         position_new(l, rt.translation);
01880     }
01881     else
01882     {
01883         rt.rotation = *q;
01884         rt = frame->toUniversal(rt, jd);
01885         rotation_new(l, rt.rotation);
01886     }
01887 
01888     return 1;
01889 }
01890 
01891 // Convert from universal to frame coordinates.
01892 static int frame_to(lua_State* l)
01893 {
01894     checkArgs(l, 2, 3, "Two or three arguments required for frame:to");
01895 
01896     FrameOfReference* frame = this_frame(l);
01897     CelestiaCore* appCore = getAppCore(l, AllErrors);
01898 
01899     RigidTransform rt;
01900 
01901     UniversalCoord* uc = NULL;
01902     Quatd* q = NULL;
01903     double jd = 0.0;
01904 
01905     if (istype(l, 2, _Position))
01906     {
01907         uc = to_position(l, 2);
01908     }
01909     else
01910     if (istype(l, 2, _Rotation))
01911     {
01912         q = to_rotation(l, 2);
01913     }
01914 
01915     if (uc == NULL && q == NULL)
01916     {
01917         doError(l, "Position or rotation expected as second argument to frame:to()");
01918     }
01919 
01920     jd = safeGetNumber(l, 3, WrongType, "Second arg to frame:to must be a number", appCore->getSimulation()->getTime());
01921 
01922     if (uc != NULL)
01923     {
01924         rt.translation = *uc;
01925         rt = frame->fromUniversal(rt, jd);
01926         position_new(l, rt.translation);
01927     }
01928     else
01929     {
01930         rt.rotation = *q;
01931         rt = frame->fromUniversal(rt, jd);
01932         rotation_new(l, rt.rotation);
01933     }
01934 
01935     return 1;
01936 }
01937 
01938 static int frame_getrefobject(lua_State* l)
01939 {
01940     checkArgs(l, 1, 1, "No arguments expected for frame:getrefobject()");
01941 
01942     FrameOfReference* frame = this_frame(l);
01943     if (frame->refObject.getType() == Selection::Type_Nil)
01944     {
01945         lua_pushnil(l);
01946     }
01947     else
01948     {
01949         object_new(l, frame->refObject);
01950     }
01951     return 1;
01952 }
01953 
01954 static int frame_gettargetobject(lua_State* l)
01955 {
01956     checkArgs(l, 1, 1, "No arguments expected for frame:gettarget()");
01957 
01958     FrameOfReference* frame = this_frame(l);
01959     if (frame->targetObject.getType() == Selection::Type_Nil)
01960     {
01961         lua_pushnil(l);
01962     }
01963     else
01964     {
01965         object_new(l, frame->targetObject);
01966     }
01967     return 1;
01968 }
01969 
01970 static int frame_getcoordinatesystem(lua_State* l)
01971 {
01972     checkArgs(l, 1, 1, "No arguments expected for frame:getcoordinatesystem()");
01973 
01974     FrameOfReference* frame = this_frame(l);
01975     string coordsys;
01976     switch (frame->coordSys)
01977     {
01978     case astro::Universal: 
01979         coordsys = "universal"; break;
01980     case astro::Ecliptical: 
01981         coordsys = "ecliptic"; break;
01982     case astro::Equatorial: 
01983         coordsys = "equatorial"; break;
01984     case astro::Geographic: 
01985         coordsys = "planetographic"; break;
01986     case astro::ObserverLocal: 
01987         coordsys = "observer"; break;
01988     case astro::PhaseLock: 
01989         coordsys = "lock"; break;
01990     case astro::Chase: 
01991         coordsys = "chase"; break;
01992     default: 
01993         coordsys = "invalid";
01994     }
01995     lua_pushstring(l, coordsys.c_str());
01996     return 1;
01997 }
01998 
01999 static int frame_tostring(lua_State* l)
02000 {
02001     // TODO: print out the actual information about the frame
02002     lua_pushstring(l, "[Frame]");
02003 
02004     return 1;
02005 }
02006 
02007 static void CreateFrameMetaTable(lua_State* l)
02008 {
02009     CreateClassMetatable(l, _Frame);
02010 
02011     RegisterMethod(l, "__tostring", frame_tostring);
02012     RegisterMethod(l, "to", frame_to);
02013     RegisterMethod(l, "from", frame_from);
02014     RegisterMethod(l, "getcoordinatesystem", frame_getcoordinatesystem);
02015     RegisterMethod(l, "getrefobject", frame_getrefobject);
02016     RegisterMethod(l, "gettargetobject", frame_gettargetobject);
02017 
02018     lua_pop(l, 1); // remove metatable from stack
02019 }
02020 
02021 // ==================== Object ====================
02022 // star, planet, or deep-sky object
02023 static int object_new(lua_State* l, const Selection& sel)
02024 {
02025     Selection* ud = reinterpret_cast<Selection*>(lua_newuserdata(l, sizeof(Selection)));
02026     *ud = sel;
02027 
02028     SetClass(l, _Object);
02029 
02030     return 1;
02031 }
02032 
02033 static Selection* to_object(lua_State* l, int index)
02034 {
02035     return static_cast<Selection*>(CheckUserData(l, index, _Object));
02036 }
02037 
02038 static Selection* this_object(lua_State* l)
02039 {
02040     Selection* sel = to_object(l, 1);
02041     if (sel == NULL)
02042     {
02043         doError(l, "Bad position object!");
02044     }
02045 
02046     return sel;
02047 }
02048 
02049 
02050 static int object_tostring(lua_State* l)
02051 {
02052     lua_pushstring(l, "[Object]");
02053 
02054     return 1;
02055 }
02056 
02057 static int object_radius(lua_State* l)
02058 {
02059     checkArgs(l, 1, 1, "No arguments expected to function object:radius");
02060 
02061     Selection* sel = this_object(l);
02062     lua_pushnumber(l, sel->radius());
02063 
02064     return 1;
02065 }
02066 
02067 static int object_type(lua_State* l)
02068 {
02069     checkArgs(l, 1, 1, "No arguments expected to function object:type");
02070 
02071     Selection* sel = this_object(l);
02072     const char* tname = "unknown";
02073     switch (sel->getType())
02074     {
02075     case Selection::Type_Body:
02076         {
02077             int cl = sel->body()->getClassification();
02078             switch (cl)
02079             {
02080             case Body::Planet     : tname = "planet"; break;
02081             case Body::Moon       : tname = "moon"; break;
02082             case Body::Asteroid   : tname = "asteroid"; break;
02083             case Body::Comet      : tname = "comet"; break;
02084             case Body::Spacecraft : tname = "spacecraft"; break;
02085             case Body::Invisible  : tname = "invisible"; break;
02086             }
02087         }
02088         break;
02089 
02090     case Selection::Type_Star:
02091         tname = "star";
02092         break;
02093 
02094     case Selection::Type_DeepSky:
02095         // TODO: return cluster, galaxy, or nebula as appropriate
02096         tname = "deepsky";
02097         break;
02098 
02099     case Selection::Type_Location:
02100         tname = "location";
02101         break;
02102 
02103     case Selection::Type_Nil:
02104         tname = "null";
02105         break;
02106     }
02107 
02108     lua_pushstring(l, tname);
02109 
02110     return 1;
02111 }
02112 
02113 static int object_name(lua_State* l)
02114 {
02115     checkArgs(l, 1, 1, "No arguments expected to function object:name");
02116 
02117     Selection* sel = this_object(l);
02118     switch (sel->getType())
02119     {
02120     case Selection::Type_Body:
02121         lua_pushstring(l, sel->body()->getName().c_str());
02122         break;
02123     case Selection::Type_DeepSky:
02124         lua_pushstring(l, getAppCore(l, AllErrors)->getSimulation()->getUniverse()
02125                          ->getDSOCatalog()->getDSOName(sel->deepsky()).c_str());
02126         break;
02127     case Selection::Type_Star:
02128         lua_pushstring(l, getAppCore(l, AllErrors)->getSimulation()->getUniverse()
02129                        ->getStarCatalog()->getStarName(*(sel->star())).c_str());
02130         break;
02131     case Selection::Type_Location:
02132         lua_pushstring(l, sel->location()->getName().c_str());
02133         break;
02134     default:
02135         lua_pushstring(l, "?");
02136         break;
02137     }
02138 
02139     return 1;
02140 }
02141 
02142 static int object_spectraltype(lua_State* l)
02143 {
02144     checkArgs(l, 1, 1, "No arguments expected to function object:spectraltype");
02145 
02146     Selection* sel = this_object(l);
02147     if (sel->star() != NULL)
02148     {
02149         char buf[16];
02150         strncpy(buf, sel->star()->getSpectralType(), sizeof buf);
02151         buf[sizeof(buf) - 1] = '\0'; // make sure it's zero terminate
02152         lua_pushstring(l, buf);
02153     }
02154     else
02155     {
02156         lua_pushnil(l);
02157     }
02158 
02159     return 1;
02160 }
02161 
02162 static int object_getinfo(lua_State* l)
02163 {
02164     checkArgs(l, 1, 1, "No arguments expected to function object:getinfo");
02165 
02166     lua_newtable(l);
02167 
02168     Selection* sel = this_object(l);
02169     if (sel->star() != NULL)
02170     {
02171         Star* star = sel->star();
02172         setTable(l, "type", "star");
02173         setTable(l, "name", getAppCore(l, AllErrors)->getSimulation()->getUniverse()
02174                        ->getStarCatalog()->getStarName(*(sel->star())).c_str());
02175         setTable(l, "catalogNumber", star->getCatalogNumber());
02176         setTable(l, "stellarClass", star->getSpectralType());
02177         setTable(l, "absoluteMagnitude", (lua_Number)star->getAbsoluteMagnitude());
02178         setTable(l, "luminosity", (lua_Number)star->getLuminosity());
02179         setTable(l, "radius", (lua_Number)star->getRadius());
02180         setTable(l, "temperature", (lua_Number)star->getTemperature());
02181         setTable(l, "rotationPeriod", (lua_Number)star->getRotationElements().period);
02182         setTable(l, "bolometricMagnitude", (lua_Number)star->getBolometricMagnitude());
02183 
02184         if (star->getOrbitBarycenter() != NULL)
02185         {
02186             Selection parent((Star*)(star->getOrbitBarycenter()));
02187             lua_pushstring(l, "parent");
02188             object_new(l, parent);
02189             lua_settable(l, -3);
02190         }
02191     }
02192     else if (sel->body() != NULL)
02193     {
02194         Body* body = sel->body();
02195         const char* tname = "unknown";
02196         switch (body->getClassification())
02197         {
02198         case Body::Planet     : tname = "planet"; break;
02199         case Body::Moon       : tname = "moon"; break;
02200         case Body::Asteroid   : tname = "asteroid"; break;
02201         case Body::Comet      : tname = "comet"; break;
02202         case Body::Spacecraft : tname = "spacecraft"; break;
02203         case Body::Invisible  : tname = "invisible"; break;
02204         }
02205         setTable(l, "type", tname);
02206         setTable(l, "name", body->getName().c_str());
02207         setTable(l, "mass", (lua_Number)body->getMass());
02208         setTable(l, "oblateness", (lua_Number)body->getOblateness());
02209         setTable(l, "albedo", (lua_Number)body->getAlbedo());
02210         setTable(l, "infoURL", body->getInfoURL().c_str());
02211         setTable(l, "radius", (lua_Number)body->getRadius());
02212 
02213         double lifespanStart, lifespanEnd;
02214         body->getLifespan(lifespanStart, lifespanEnd);
02215         setTable(l, "lifespanStart", (lua_Number)lifespanStart);
02216         setTable(l, "lifespanEnd", (lua_Number)lifespanEnd);
02217         // TODO: atmosphere, surfaces ?
02218 
02219         PlanetarySystem* system = body->getSystem();
02220         if (system->getPrimaryBody() != NULL)
02221         {
02222             Selection parent(system->getPrimaryBody());
02223             lua_pushstring(l, "parent");
02224             object_new(l, parent);
02225             lua_settable(l, -3);
02226         }
02227         else
02228         {
02229             Selection parent(system->getStar());
02230             lua_pushstring(l, "parent");
02231             object_new(l, parent);
02232             lua_settable(l, -3);
02233         }
02234 
02235         lua_pushstring(l, "hasRings");
02236         lua_pushboolean(l, body->getRings() != NULL);
02237         lua_settable(l, -3);
02238         RotationElements re = body->getRotationElements();
02239         setTable(l, "rotationPeriod", (double)re.period);
02240         setTable(l, "rotationOffset", (double)re.offset);
02241         setTable(l, "rotationEpoch", re.epoch);
02242         setTable(l, "rotationObliquity", (double)re.obliquity);
02243         setTable(l, "rotationAscendingNode", (double)re.ascendingNode);
02244         setTable(l, "rotationPrecessionRate", (double)re.precessionRate);
02245         Orbit* orbit = body->getOrbit();
02246         setTable(l, "orbitPeriod", orbit->getPeriod());
02247         Atmosphere* atmosphere = body->getAtmosphere();
02248         if (atmosphere != NULL)
02249         {
02250             setTable(l, "atmosphereHeight", (double)atmosphere->height);
02251             setTable(l, "atmosphereCloudHeight", (double)atmosphere->cloudHeight);
02252             setTable(l, "atmosphereCloudSpeed", (double)atmosphere->cloudSpeed);
02253         }
02254     }
02255     else if (sel->deepsky() != NULL)
02256     {
02257         setTable(l, "type", "deepsky");
02258         DeepSkyObject* deepsky = sel->deepsky();
02259         setTable(l, "name", getAppCore(l, AllErrors)->getSimulation()->getUniverse()
02260                            ->getDSOCatalog()->getDSOName(sel->deepsky()).c_str());
02261         setTable(l, "radius", (lua_Number)deepsky->getRadius());
02262     }
02263     else if (sel->location() != NULL)
02264     {
02265         setTable(l, "type", "location");
02266         Location* location = sel->location();
02267         setTable(l, "name", location->getName().c_str());
02268         setTable(l, "size", (lua_Number)location->getSize());
02269         setTable(l, "importance", (lua_Number)location->getImportance());
02270         setTable(l, "infoURL", location->getInfoURL().c_str());
02271 
02272         uint32 featureType = location->getFeatureType();
02273         string featureName("Unknown");
02274         for (FlagMap::const_iterator it = LocationFlagMap.begin(); it != LocationFlagMap.end(); it++)
02275         {
02276             if (it->second == featureType)
02277             {
02278                 featureName = it->first;
02279                 break;
02280             }
02281         }
02282         setTable(l, "featureType", featureName.c_str());
02283 
02284         Body* parent = location->getParentBody();
02285         if (parent != NULL)
02286         {
02287             Selection selection(parent);
02288             lua_pushstring(l, "parent");
02289             object_new(l, selection);
02290             lua_settable(l, -3);
02291         }
02292     }
02293     else
02294     {
02295         setTable(l, "type", "null");
02296     }
02297     return 1;
02298 }
02299 
02300 static int object_absmag(lua_State* l)
02301 {
02302     checkArgs(l, 1, 1, "No arguments expected to function object:absmag");
02303 
02304     Selection* sel = this_object(l);
02305     if (sel->star() != NULL)
02306         lua_pushnumber(l, sel->star()->getAbsoluteMagnitude());
02307     else
02308         lua_pushnil(l);
02309 
02310     return 1;
02311 }
02312 
02313 static int object_mark(lua_State* l)
02314 {
02315     checkArgs(l, 1, 4, "No arguments expected to function object:mark");
02316 
02317     Selection* sel = this_object(l);
02318     CelestiaCore* appCore = getAppCore(l, AllErrors);
02319 
02320     Color markColor(0.0f, 1.0f, 0.0f);
02321     const char* colorString = safeGetString(l, 2, WrongType, "First argument to object:mark must be a string");
02322     if (colorString != NULL)
02323         Color::parse(colorString, markColor);
02324 
02325     Marker::Symbol markSymbol = Marker::Diamond;
02326     const char* markerString = safeGetString(l, 3, WrongType, "Second argument to object:mark must be a string");
02327     if (markerString != NULL)
02328         markSymbol = parseMarkerSymbol(markerString);
02329 
02330     float markSize = (float)safeGetNumber(l, 4, WrongType, "Third arg to object:mark must be a number", 10.0);
02331     if (markSize < 1.0f)
02332         markSize = 1.0f;
02333     else if (markSize > 10000.0f)
02334         markSize = 10000.0f;
02335 
02336     Simulation* sim = appCore->getSimulation();
02337     sim->getUniverse()->markObject(*sel, markSize,
02338                                    markColor, markSymbol, 1);
02339 
02340     return 0;
02341 }
02342 
02343 static int object_unmark(lua_State* l)
02344 {
02345     checkArgs(l, 1, 1, "No arguments expected to function object:unmark");
02346 
02347     Selection* sel = this_object(l);
02348     CelestiaCore* appCore = getAppCore(l, AllErrors);
02349 
02350     Simulation* sim = appCore->getSimulation();
02351     sim->getUniverse()->unmarkObject(*sel, 1);
02352 
02353     return 0;
02354 }
02355 
02356 // Return the object's current position.  A time argument is optional;
02357 // if not provided, the current master simulation time is used.
02358 static int object_getposition(lua_State* l)
02359 {
02360     checkArgs(l, 1, 2, "Expected no or one argument to object:getposition");
02361 
02362     Selection* sel = this_object(l);
02363     CelestiaCore* appCore = getAppCore(l, AllErrors);
02364 
02365     double t = safeGetNumber(l, 2, WrongType, "Time expected as argument to object:getposition",
02366                               appCore->getSimulation()->getTime());
02367     position_new(l, sel->getPosition(t));
02368 
02369     return 1;
02370 }
02371 
02372 static int object_getchildren(lua_State* l)
02373 {
02374     checkArgs(l, 1, 1, "No arguments expected for object:getchildren()");
02375 
02376     Selection* sel = this_object(l);
02377     CelestiaCore* appCore = getAppCore(l, AllErrors);
02378 
02379     Simulation* sim = appCore->getSimulation();
02380 
02381     lua_newtable(l);
02382     if (sel->star() != NULL)
02383     {
02384         SolarSystemCatalog* solarSystemCatalog = sim->getUniverse()->getSolarSystemCatalog();
02385         SolarSystemCatalog::iterator iter = solarSystemCatalog->find(sel->star()->getCatalogNumber());
02386         if (iter != solarSystemCatalog->end())
02387         {
02388             SolarSystem* solarSys = iter->second;
02389             for (int i = 0; i < solarSys->getPlanets()->getSystemSize(); i++)
02390             {
02391                 Body* body = solarSys->getPlanets()->getBody(i);
02392                 Selection satSel(body);
02393                 object_new(l, satSel);
02394                 lua_rawseti(l, -2, i + 1);
02395             }
02396         }
02397     }
02398     else if (sel->body() != NULL)
02399     {
02400         const PlanetarySystem* satellites = sel->body()->getSatellites();
02401         if (satellites != NULL && satellites->getSystemSize() != 0)
02402         {
02403             for (int i = 0; i < satellites->getSystemSize(); i++)
02404             {
02405                 Body* body = satellites->getBody(i);
02406                 Selection satSel(body);
02407                 object_new(l, satSel);
02408                 lua_rawseti(l, -2, i + 1);
02409             }
02410         }
02411     }
02412 
02413     return 1;
02414 }
02415 
02416 static int object_preloadtexture(lua_State* l)
02417 {
02418     checkArgs(l, 1, 1, "No argument expected to object:preloadtexture");
02419     CelestiaCore* appCore = getAppCore(l, AllErrors);
02420 
02421     Renderer* renderer = appCore->getRenderer();
02422     Selection* sel = this_object(l);
02423     if (sel->body() != NULL && renderer != NULL)
02424     {
02425         LuaState* luastate = getLuaStateObject(l);
02426         // make sure we don't timeout because of texture-loading:
02427         double timeToTimeout = luastate->timeout - luastate->getTime();
02428 
02429         renderer->loadTextures(sel->body());
02430 
02431         // no matter how long it really took, make it look like 0.1s:
02432         luastate->timeout = luastate->getTime() + timeToTimeout - 0.1;
02433     }
02434     return 0;
02435 }
02436 
02437 static void CreateObjectMetaTable(lua_State* l)
02438 {
02439     CreateClassMetatable(l, _Object);
02440 
02441     RegisterMethod(l, "__tostring", object_tostring);
02442     RegisterMethod(l, "radius", object_radius);
02443     RegisterMethod(l, "type", object_type);
02444     RegisterMethod(l, "spectraltype", object_spectraltype);
02445     RegisterMethod(l, "getinfo", object_getinfo);
02446     RegisterMethod(l, "absmag", object_absmag);
02447     RegisterMethod(l, "name", object_name);
02448     RegisterMethod(l, "mark", object_mark);
02449     RegisterMethod(l, "unmark", object_unmark);
02450     RegisterMethod(l, "getposition", object_getposition);
02451     RegisterMethod(l, "getchildren", object_getchildren);
02452     RegisterMethod(l, "preloadtexture", object_preloadtexture);
02453 
02454     lua_pop(l, 1); // pop metatable off the stack
02455 }
02456 
02457 // ==================== Observer ====================
02458 static int observer_new(lua_State* l, Observer* o)
02459 {
02460     Observer** ud = static_cast<Observer**>(lua_newuserdata(l, sizeof(Observer*)));
02461     *ud = o;
02462 
02463     SetClass(l, _Observer);
02464 
02465     return 1;
02466 }
02467 
02468 static Observer* to_observer(lua_State* l, int index)
02469 {
02470     Observer** o = static_cast<Observer**>(lua_touserdata(l, index));
02471     CelestiaCore* appCore = getAppCore(l, AllErrors);
02472 
02473     // Check if pointer is still valid, i.e. is used by a view:
02474     if (o != NULL && getViewByObserver(appCore, *o) != NULL)
02475     {
02476             return *o;
02477     }
02478     return NULL;
02479 }
02480 
02481 static Observer* this_observer(lua_State* l)
02482 {
02483     Observer* obs = to_observer(l, 1);
02484     if (obs == NULL)
02485     {
02486         doError(l, "Bad observer object (maybe tried to access a deleted view?)!");
02487     }
02488 
02489     return obs;
02490 }
02491 
02492 
02493 static int observer_isvalid(lua_State* l)
02494 {
02495     checkArgs(l, 1, 1, "No arguments expected for observer:isvalid()");
02496     lua_pushboolean(l, to_observer(l, 1) != NULL);
02497     return 1;
02498 }
02499 
02500 static int observer_tostring(lua_State* l)
02501 {
02502     lua_pushstring(l, "[Observer]");
02503 
02504     return 1;
02505 }
02506 
02507 static int observer_setposition(lua_State* l)
02508 {
02509     checkArgs(l, 2, 2, "One argument required for setpos");
02510 
02511     Observer* o = this_observer(l);
02512 
02513     UniversalCoord* uc = to_position(l,2);
02514     if (uc == NULL)
02515     {
02516         doError(l, "Argument to observer:setposition must be a position");
02517     }
02518     o->setPosition(*uc);
02519     return 0;
02520 }
02521 
02522 static int observer_setorientation(lua_State* l)
02523 {
02524     checkArgs(l, 2, 2, "One argument required for setorientation");
02525 
02526     Observer* o = this_observer(l);
02527 
02528     Quatd* q = to_rotation(l,2);
02529     if (q == NULL)
02530     {
02531         doError(l, "Argument to observer:setorientation must be a rotation");
02532     }
02533     o->setOrientation(*q);
02534     return 0;
02535 }
02536 
02537 static int observer_getorientation(lua_State* l)
02538 {
02539     checkArgs(l, 1, 1, "No arguments expected to observer:getorientation()");
02540 
02541     Observer* o = this_observer(l);
02542     Quatf q = o->getOrientation();
02543     rotation_new(l, Quatd(q.w, q.x, q.y, q.z));
02544 
02545     return 1;
02546 }
02547 
02548 static int observer_rotate(lua_State* l)
02549 {
02550     checkArgs(l, 2, 2, "One argument required for rotate");
02551 
02552     Observer* o = this_observer(l);
02553 
02554     Quatd* q = to_rotation(l,2);
02555     if (q == NULL)
02556     {
02557         doError(l, "Argument to observer:setpos must be a rotation");
02558     }
02559     Quatf qf((float) q->w, (float) q->x, (float) q->y, (float) q->z);
02560     o->rotate(qf);
02561     return 0;
02562 }
02563 
02564 static int observer_lookat(lua_State* l)
02565 {
02566     checkArgs(l, 3, 4, "Two or three arguments required for lookat");
02567     int argc = lua_gettop(l);
02568 
02569     Observer* o = this_observer(l);
02570 
02571     UniversalCoord* from = NULL;
02572     UniversalCoord* to = NULL;
02573     Vec3d* upd = NULL;
02574     if (argc == 3)
02575     {
02576         to = to_position(l, 2);
02577         upd = to_vector(l, 3);
02578         if (to == NULL)
02579         {
02580             doError(l, "Argument 1 (of 2) to observer:lookat must be of type position");
02581         }
02582     }
02583     else
02584     if (argc == 4)
02585     {
02586         from = to_position(l, 2);
02587         to = to_position(l, 3);
02588         upd = to_vector(l, 4);
02589 
02590         if (to == NULL || from == NULL)
02591         {
02592             doError(l, "Argument 1 and 2 (of 3) to observer:lookat must be of type position");
02593         }
02594     }
02595     if (upd == NULL)
02596     {
02597         doError(l, "Last argument to observer:lookat must be of type vector");
02598     }
02599     Vec3d nd;
02600     if (from == NULL)
02601     {
02602         nd = (*to) - o->getPosition();
02603     }
02604     else
02605     {
02606         nd = (*to) - (*from);
02607     }
02608     // need Vec3f instead:
02609     Vec3f up = Vec3f((float) upd->x, (float) upd->y, (float) upd->z);
02610     Vec3f n = Vec3f((float) nd.x, (float) nd.y, (float) nd.z);
02611 
02612     n.normalize();
02613     Vec3f v = n ^ up;
02614     v.normalize();
02615     Vec3f u = v ^ n;
02616     Quatf qf = Quatf(Mat3f(v, u, -n));
02617     o->setOrientation(qf);
02618     return 0;
02619 }
02620 
02621 static int observer_gototable(lua_State* l)
02622 {
02623     checkArgs(l, 2, 2, "Expected one table as argument to goto");
02624 
02625     Observer* o = this_observer(l);
02626     if (!lua_istable(l, 2))
02627     {
02628         lua_pushstring(l, "Argument to goto must be a table");
02629     }
02630 
02631     Observer::JourneyParams jparams;
02632     jparams.duration = 5.0;
02633     jparams.from = o->getSituation().translation;
02634     jparams.to = o->getSituation().translation;
02635     jparams.initialOrientation = o->getOrientation();
02636     jparams.finalOrientation = o->getOrientation();
02637     jparams.startInterpolation = 0.25;
02638     jparams.endInterpolation = 0.75;
02639     jparams.accelTime = 0.5;
02640     jparams.traj = Observer::Linear;
02641 
02642     lua_pushstring(l, "duration");
02643     lua_gettable(l, 2);
02644     jparams.duration = safeGetNumber(l, 3, NoErrors, "", 5.0);
02645     lua_settop(l, 2);
02646 
02647     lua_pushstring(l, "from");
02648     lua_gettable(l, 2);
02649     UniversalCoord* from = to_position(l, 3);
02650     if (from != NULL)
02651         jparams.from = *from;
02652     lua_settop(l, 2);
02653 
02654     lua_pushstring(l, "to");
02655     lua_gettable(l, 2);
02656     UniversalCoord* to = to_position(l, 3);
02657     if (to != NULL)
02658         jparams.to = *to;
02659     lua_settop(l, 2);
02660 
02661     lua_pushstring(l, "initialOrientation");
02662     lua_gettable(l, 2);
02663     Quatd* rot1 = to_rotation(l, 3);
02664     if (rot1 != NULL)
02665         jparams.initialOrientation = Quatf((float) rot1->w, (float) rot1->x, (float) rot1->y, (float) rot1->z);
02666     lua_settop(l, 2);
02667 
02668     lua_pushstring(l, "finalOrientation");
02669     lua_gettable(l, 2);
02670     Quatd* rot2 = to_rotation(l, 3);
02671     if (rot2 != NULL)
02672         jparams.finalOrientation = Quatf((float) rot2->w, (float) rot2->x, (float) rot2->y, (float) rot2->z);
02673     lua_settop(l, 2);
02674 
02675     lua_pushstring(l, "startInterpolation");
02676     lua_gettable(l, 2);
02677     jparams.startInterpolation = safeGetNumber(l, 3, NoErrors, "", 0.25);
02678     lua_settop(l, 2);
02679 
02680     lua_pushstring(l, "endInterpolation");
02681     lua_gettable(l, 2);
02682     jparams.endInterpolation = safeGetNumber(l, 3, NoErrors, "", 0.75);
02683     lua_settop(l, 2);
02684 
02685     lua_pushstring(l, "accelTime");
02686     lua_gettable(l, 2);
02687     jparams.accelTime = safeGetNumber(l, 3, NoErrors, "", 0.5);
02688     lua_settop(l, 2);
02689 
02690     jparams.duration = max(0.0, jparams.duration);
02691     jparams.accelTime = min(1.0, max(0.1, jparams.accelTime));
02692     jparams.startInterpolation = min(1.0, max(0.0, jparams.startInterpolation));
02693     jparams.endInterpolation = min(1.0, max(0.0, jparams.endInterpolation));
02694 
02695     // args are in universal coords, let setFrame handle conversion:
02696     FrameOfReference tmp = o->getFrame();
02697     o->setFrame(FrameOfReference());
02698     o->gotoJourney(jparams);
02699     o->setFrame(tmp);
02700     return 0;
02701 }
02702 
02703 // First argument is the target object or position; optional second argument
02704 // is the travel time
02705 static int observer_goto(lua_State* l)
02706 {
02707     if (lua_gettop(l) == 2 && lua_istable(l, 2))
02708     {
02709         // handle this in own function
02710         return observer_gototable(l);
02711     }
02712     checkArgs(l, 1, 5, "One to four arguments expected to observer:goto");
02713 
02714     Observer* o = this_observer(l);
02715 
02716     Selection* sel = to_object(l, 2);
02717     UniversalCoord* uc = to_position(l, 2);
02718     if (sel == NULL && uc == NULL)
02719     {
02720         doError(l, "First arg to observer:goto must be object or position");
02721     }
02722 
02723     double travelTime = safeGetNumber(l, 3, WrongType, "Second arg to observer:goto must be a number", 5.0);
02724     double startInter = safeGetNumber(l, 4, WrongType, "Third arg to observer:goto must be a number", 0.25);
02725     double endInter = safeGetNumber(l, 5, WrongType, "Fourth arg to observer:goto must be a number", 0.75);
02726     if (startInter < 0 || startInter > 1) startInter = 0.25;
02727     if (endInter < 0 || endInter > 1) startInter = 0.75;
02728 
02729     // The first argument may be either an object or a position
02730     if (sel != NULL)
02731     {
02732         o->gotoSelection(*sel, travelTime, startInter, endInter, Vec3f(0, 1, 0), astro::ObserverLocal);
02733     }
02734     else
02735     {
02736         RigidTransform rt = o->getSituation();
02737         rt.translation = *uc;
02738         o->gotoLocation(rt, travelTime);
02739     }
02740 
02741     return 0;
02742 }
02743 
02744 static int observer_gotolonglat(lua_State* l)
02745 {
02746     checkArgs(l, 2, 7, "One to five arguments expected to observer:gotolonglat");
02747 
02748     Observer* o = this_observer(l);
02749 
02750     Selection* sel = to_object(l, 2);
02751     if (sel == NULL)
02752     {
02753         doError(l, "First arg to observer:gotolonglat must be an object");
02754     }
02755     double defaultDistance = sel->radius() * 5.0;
02756 
02757     double longitude  = safeGetNumber(l, 3, WrongType, "Second arg to observer:gotolonglat must be a number", 0.0);
02758     double latitude   = safeGetNumber(l, 4, WrongType, "Third arg to observer:gotolonglat must be a number", 0.0);
02759     double distance   = safeGetNumber(l, 5, WrongType, "Fourth arg to observer:gotolonglat must be a number", defaultDistance);
02760     double travelTime = safeGetNumber(l, 6, WrongType, "Fifth arg to observer:gotolonglat must be a number", 5.0);
02761 
02762     distance = distance / KM_PER_LY;
02763 
02764     Vec3f up(0.0f, 1.0f, 0.0f);
02765     if (lua_gettop(l) >= 7)
02766     {
02767         Vec3d* uparg = to_vector(l, 7);
02768         if (uparg == NULL)
02769         {
02770             doError(l, "Sixth argument to observer:gotolonglat must be a vector");
02771         }
02772         up = Vec3f((float)uparg->x, (float)uparg->y, (float)uparg->z);
02773     }
02774     o->gotoSelectionLongLat(*sel, travelTime, distance, (float)longitude, (float)latitude, up);
02775 
02776     return 0;
02777 }
02778 
02779 // deprecated: wrong name, bad interface.
02780 static int observer_gotolocation(lua_State* l)
02781 {
02782     checkArgs(l, 2, 3,"Expected one or two arguments to observer:gotolocation");
02783 
02784     Observer* o = this_observer(l);
02785 
02786     double travelTime = safeGetNumber(l, 3, WrongType, "Second arg to observer:gotolocation must be a number", 5.0);
02787     if (travelTime < 0)
02788         travelTime = 0.0;
02789 
02790     UniversalCoord* uc = to_position(l, 2);
02791     if (uc != NULL)
02792     {
02793         RigidTransform rt = o->getSituation();
02794         rt.translation = *uc;
02795         o->gotoLocation(rt, travelTime);
02796     }
02797     else
02798     {
02799         doError(l, "First arg to observer:gotolocation must be a position");
02800     }
02801 
02802     return 0;
02803 }
02804 
02805 static int observer_gotodistance(lua_State* l)
02806 {
02807     checkArgs(l, 2, 5, "One to four arguments expected to observer:gotodistance");
02808 
02809     Observer* o = this_observer(l);
02810     Selection* sel = to_object(l, 2);
02811     if (sel == NULL)
02812     {
02813         doError(l, "First arg to observer:gotodistance must be object");
02814     }
02815 
02816     double distance = safeGetNumber(l, 3, WrongType, "Second arg to observer:gotodistance must be a number", 20000);
02817     double travelTime = safeGetNumber(l, 4, WrongType, "Third arg to observer:gotodistance must be a number", 5.0);
02818 
02819     Vec3f up(0,1,0);
02820     if (lua_gettop(l) > 4)
02821     {
02822         Vec3d* up_arg = to_vector(l, 5);
02823         if (up_arg == NULL)
02824         {
02825             doError(l, "Fourth arg to observer:gotodistance must be a vector");
02826         }
02827         up.x = (float)up_arg->x;
02828         up.y = (float)up_arg->y;
02829         up.z = (float)up_arg->z;
02830     }
02831 
02832     o->gotoSelection(*sel, travelTime, astro::kilometersToLightYears(distance), up, astro::Universal);
02833 
02834     return 0;
02835 }
02836 
02837 static int observer_gotosurface(lua_State* l)
02838 {
02839     checkArgs(l, 2, 3, "One to two arguments expected to observer:gotosurface");
02840 
02841     Observer* o = this_observer(l);
02842     Selection* sel = to_object(l, 2);
02843     if (sel == NULL)
02844     {
02845         doError(l, "First arg to observer:gotosurface must be object");
02846     }
02847 
02848     double travelTime = safeGetNumber(l, 3, WrongType, "Second arg to observer:gotosurface must be a number", 5.0);
02849 
02850     // This is needed because gotoSurface expects frame to be geosync:
02851     o->geosynchronousFollow(*sel);
02852     o->gotoSurface(*sel, travelTime);
02853 
02854     return 0;
02855 }
02856 
02857 static int observer_center(lua_State* l)
02858 {
02859     checkArgs(l, 2, 3, "Expected one or two arguments for to observer:center");
02860 
02861     Observer* o = this_observer(l);
02862     Selection* sel = to_object(l, 2);
02863     if (sel == NULL)
02864     {
02865         doError(l, "First argument to observer:center must be an object");
02866     }
02867     double travelTime = safeGetNumber(l, 3, WrongType, "Second arg to observer:center must be a number", 5.0);
02868 
02869     o->centerSelection(*sel, travelTime);
02870 
02871     return 0;
02872 }
02873 
02874 static int observer_centerorbit(lua_State* l)
02875 {
02876     checkArgs(l, 2, 3, "Expected one or two arguments for to observer:center");
02877 
02878     Observer* o = this_observer(l);
02879     Selection* sel = to_object(l, 2);
02880     if (sel == NULL)
02881     {
02882         doError(l, "First argument to observer:centerorbit must be an object");
02883     }
02884     double travelTime = safeGetNumber(l, 3, WrongType, "Second arg to observer:centerorbit must be a number", 5.0);
02885 
02886     o->centerSelectionCO(*sel, travelTime);
02887 
02888     return 0;
02889 }
02890 
02891 static int observer_cancelgoto(lua_State* l)
02892 {
02893     checkArgs(l, 1, 1, "Expected no arguments to observer:cancelgoto");
02894 
02895     Observer* o = this_observer(l);
02896     o->cancelMotion();
02897 
02898     return 0;
02899 }
02900 
02901 static int observer_follow(lua_State* l)
02902 {
02903     checkArgs(l, 2, 2, "One argument expected for observer:follow");
02904 
02905     Observer* o = this_observer(l);
02906     Selection* sel = to_object(l, 2);
02907     if (sel == NULL)
02908     {
02909         doError(l, "First argument to observer:follow must be an object");
02910     }
02911     o->follow(*sel);
02912 
02913     return 0;
02914 }
02915 
02916 static int observer_synchronous(lua_State* l)
02917 {
02918     checkArgs(l, 2, 2, "One argument expected for observer:synchronous");
02919 
02920     Observer* o = this_observer(l);
02921     Selection* sel = to_object(l, 2);
02922     if (sel == NULL)
02923     {
02924         doError(l, "First argument to observer:synchronous must be an object");
02925     }
02926     o->geosynchronousFollow(*sel);
02927 
02928     return 0;
02929 }
02930 
02931 static int observer_lock(lua_State* l)
02932 {
02933     checkArgs(l, 2, 2, "One argument expected for observer:lock");
02934 
02935     Observer* o = this_observer(l);
02936     Selection* sel = to_object(l, 2);
02937     if (sel == NULL)
02938     {
02939         doError(l, "First argument to observer:phaseLock must be an object");
02940     }
02941     o->phaseLock(*sel);
02942 
02943     return 0;
02944 }
02945 
02946 static int observer_chase(lua_State* l)
02947 {
02948     checkArgs(l, 2, 2, "One argument expected for observer:chase");
02949 
02950     Observer* o = this_observer(l);
02951     Selection* sel = to_object(l, 2);
02952     if (sel == NULL)
02953     {
02954         doError(l, "First argument to observer:chase must be an object");
02955     }
02956     o->chase(*sel);
02957 
02958     return 0;
02959 }
02960 
02961 static int observer_track(lua_State* l)
02962 {
02963     checkArgs(l, 2, 2, "One argument expected for observer:track");
02964 
02965     Observer* o = this_observer(l);
02966 
02967     // If the argument is nil, clear the tracked object
02968     if (lua_isnil(l, 2))
02969     {
02970         o->setTrackedObject(Selection());
02971     }
02972     else
02973     {
02974         // Otherwise, turn on tracking and set the tracked object
02975         Selection* sel = to_object(l, 2);
02976         if (sel == NULL)
02977         {
02978             doError(l, "First argument to observer:center must be an object");
02979         }
02980         o->setTrackedObject(*sel);
02981     }
02982 
02983     return 0;
02984 }
02985 
02986 // Return true if the observer is still moving as a result of a goto, center,
02987 // or similar command.
02988 static int observer_travelling(lua_State* l)
02989 {
02990     checkArgs(l, 1, 1, "No arguments expected to observer:travelling");
02991 
02992     Observer* o = this_observer(l);
02993     if (o->getMode() == Observer::Travelling)
02994         lua_pushboolean(l, 1);
02995     else
02996         lua_pushboolean(l, 0);
02997 
02998     return 1;
02999 }
03000 
03001 // Return the observer's current time as a Julian day number
03002 static int observer_gettime(lua_State* l)
03003 {
03004     checkArgs(l, 1, 1, "No arguments expected to observer:gettime");
03005 
03006     Observer* o = this_observer(l);
03007     lua_pushnumber(l, o->getTime());
03008 
03009     return 1;
03010 }
03011 
03012 // Return the observer's current position
03013 static int observer_getposition(lua_State* l)
03014 {
03015     checkArgs(l, 1, 1, "No arguments expected to observer:getposition");
03016 
03017     Observer* o = this_observer(l);
03018     position_new(l, o->getPosition());
03019 
03020     return 1;
03021 }
03022 
03023 static int observer_getsurface(lua_State* l)
03024 {
03025     checkArgs(l, 1, 1, "One argument expected to observer:getsurface()");
03026 
03027     Observer* obs = this_observer(l);
03028     lua_pushstring(l, obs->getDisplayedSurface().c_str());
03029 
03030     return 1;
03031 }
03032 
03033 static int observer_setsurface(lua_State* l)
03034 {
03035     checkArgs(l, 2, 2, "One argument expected to observer:setsurface()");
03036 
03037     Observer* obs = this_observer(l);
03038     const char* s = lua_tostring(l, 2);
03039 
03040     if (s == NULL)
03041         obs->setDisplayedSurface("");
03042     else
03043         obs->setDisplayedSurface(s);
03044 
03045     return 0;
03046 }
03047 
03048 static int observer_getframe(lua_State* l)
03049 {
03050     checkArgs(l, 1, 1, "No arguments expected for observer:getframe()");
03051 
03052     Observer* obs = this_observer(l);
03053 
03054     FrameOfReference frame = obs->getFrame();
03055     frame_new(l, frame);
03056     return 1;
03057 }
03058 
03059 static int observer_setframe(lua_State* l)
03060 {
03061     checkArgs(l, 2, 2, "One argument required for observer:setframe()");
03062 
03063     Observer* obs = this_observer(l);
03064 
03065     FrameOfReference* frame;
03066     frame = to_frame(l, 2);
03067     if (frame != NULL)
03068     {
03069         obs->setFrame(*frame);
03070     }
03071     else
03072     {
03073         doError(l, "Argument to observer:setframe must be a frame");
03074     }
03075     return 0;
03076 }
03077 
03078 static int observer_setspeed(lua_State* l)
03079 {
03080     checkArgs(l, 2, 2, "One argument required for observer:setspeed()");
03081 
03082     Observer* obs = this_observer(l);
03083 
03084     double speed = safeGetNumber(l, 2, AllErrors, "First argument to observer:setspeed must be a number");
03085     obs->setTargetSpeed((float)speed);
03086     return 0;
03087 }
03088 
03089 static int observer_getspeed(lua_State* l)
03090 {
03091     checkArgs(l, 1, 1, "No argument expected for observer:getspeed()");
03092 
03093     Observer* obs = this_observer(l);
03094 
03095     lua_pushnumber(l, (lua_Number)obs->getTargetSpeed());
03096     return 1;
03097 }
03098 
03099 static int observer_setfov(lua_State* l)
03100 {
03101     checkArgs(l, 2, 2, "One argument expected to observer:setfov()");
03102 
03103     Observer* obs = this_observer(l);
03104     double fov = safeGetNumber(l, 2, AllErrors, "Argument to observer:setfov() must be a number");
03105     if ((fov >= degToRad(0.001f)) && (fov <= degToRad(120.0f)))
03106     {
03107         obs->setFOV((float) fov);
03108         getAppCore(l, AllErrors)->setZoomFromFOV();
03109     }
03110     return 0;
03111 }
03112 
03113 static int observer_getfov(lua_State* l)
03114 {
03115     checkArgs(l, 1, 1, "No argument expected to observer:getfov()");
03116 
03117     Observer* obs = this_observer(l);
03118     lua_pushnumber(l, obs->getFOV());
03119     return 1;
03120 }
03121 
03122 static int observer_splitview(lua_State* l)
03123 {
03124     checkArgs(l, 2, 3, "One or two arguments expected for observer:splitview()");
03125 
03126     Observer* obs = this_observer(l);
03127     CelestiaCore* appCore = getAppCore(l, AllErrors);
03128     const char* splitType = safeGetString(l, 2, AllErrors, "First argument to observer:splitview() must be a string");
03129     View::Type type = (compareIgnoringCase(splitType, "h") == 0) ? View::HorizontalSplit : View::VerticalSplit;
03130     double splitPos = safeGetNumber(l, 3, WrongType, "Number expected as argument to observer:splitview()", 0.5);
03131     if (splitPos < 0.1)
03132         splitPos = 0.1;
03133     if (splitPos > 0.9)
03134         splitPos = 0.9;
03135     View* view = getViewByObserver(appCore, obs);
03136     appCore->splitView(type, view, (float)splitPos);
03137     return 0;
03138 }
03139 
03140 static int observer_deleteview(lua_State* l)
03141 {
03142     checkArgs(l, 1, 1, "No argument expected for observer:deleteview()");
03143 
03144     Observer* obs = this_observer(l);
03145     CelestiaCore* appCore = getAppCore(l, AllErrors);
03146     View* view = getViewByObserver(appCore, obs);
03147     appCore->deleteView(view);
03148     return 0;
03149 }
03150 
03151 static int observer_singleview(lua_State* l)
03152 {
03153     checkArgs(l, 1, 1, "No argument expected for observer:singleview()");
03154 
03155     Observer* obs = this_observer(l);
03156     CelestiaCore* appCore = getAppCore(l, AllErrors);
03157     View* view = getViewByObserver(appCore, obs);
03158     appCore->singleView(view);
03159     return 0;
03160 }
03161 
03162 static int observer_equal(lua_State* l)
03163 {
03164     checkArgs(l, 2, 2, "Wrong number of arguments for comparison!");
03165 
03166     Observer* o1 = this_observer(l);
03167     Observer* o2 = to_observer(l, 2);
03168 
03169     lua_pushboolean(l, (o1 == o2));
03170     return 1;
03171 }
03172 
03173 static int observer_setlocationflags(lua_State* l)
03174 {
03175     checkArgs(l, 2, 2, "One argument expected for observer:setlocationflags()");
03176     Observer* obs = this_observer(l);
03177     if (!lua_istable(l, 2))
03178     {
03179         doError(l, "Argument to observer:setlocationflags() must be a table");
03180     }
03181 
03182     lua_pushnil(l);
03183     int locationFlags = obs->getLocationFilter();
03184     while (lua_next(l, -2) != 0)
03185     {
03186         string key;
03187         bool value = false;
03188         if (lua_isstring(l, -2))
03189         {
03190             key = lua_tostring(l, -2);
03191         }
03192         else
03193         {
03194             doError(l, "Keys in table-argument to observer:setlocationflags() must be strings");
03195         }
03196         if (lua_isboolean(l, -1))
03197         {
03198             value = lua_toboolean(l, -1);
03199         }
03200         else
03201         {
03202             doError(l, "Values in table-argument to observer:setlocationflags() must be boolean");
03203         }
03204         if (LocationFlagMap.count(key) == 0)
03205         {
03206             cerr << "Unknown key: " << key << "\n";
03207         }
03208         else
03209         {
03210             int flag = LocationFlagMap[key];
03211             if (value)
03212             {
03213                 locationFlags |= flag;
03214             }
03215             else
03216             {
03217                 locationFlags &= ~flag;
03218             }
03219         }
03220         lua_pop(l,1);
03221     }
03222     obs->setLocationFilter(locationFlags);
03223     return 0;
03224 }
03225 
03226 static int observer_getlocationflags(lua_State* l)
03227 {
03228     checkArgs(l, 1, 1, "No arguments expected for observer:getlocationflags()");
03229     Observer* obs = this_observer(l);
03230     lua_newtable(l);
03231     FlagMap::const_iterator it = LocationFlagMap.begin();
03232     const int locationFlags = obs->getLocationFilter();
03233     while (it != LocationFlagMap.end())
03234     {
03235         string key = it->first;
03236         lua_pushstring(l, key.c_str());
03237         lua_pushboolean(l, (it->second & locationFlags) != 0);
03238         lua_settable(l,-3);
03239         it++;
03240     }
03241     return 1;
03242 }
03243 
03244 static void CreateObserverMetaTable(lua_State* l)
03245 {
03246     CreateClassMetatable(l, _Observer);
03247 
03248     RegisterMethod(l, "__tostring", observer_tostring);
03249     RegisterMethod(l, "isvalid", observer_isvalid);
03250     RegisterMethod(l, "goto", observer_goto);
03251     RegisterMethod(l, "gotolonglat", observer_gotolonglat);
03252     RegisterMethod(l, "gotolocation", observer_gotolocation);
03253     RegisterMethod(l, "gotodistance", observer_gotodistance);
03254     RegisterMethod(l, "gotosurface", observer_gotosurface);
03255     RegisterMethod(l, "cancelgoto", observer_cancelgoto);
03256     RegisterMethod(l, "setposition", observer_setposition);
03257     RegisterMethod(l, "lookat", observer_lookat);
03258     RegisterMethod(l, "setorientation", observer_setorientation);
03259     RegisterMethod(l, "getorientation", observer_getorientation);
03260     RegisterMethod(l, "getspeed", observer_getspeed);
03261     RegisterMethod(l, "setspeed", observer_setspeed);
03262     RegisterMethod(l, "getfov", observer_getfov);
03263     RegisterMethod(l, "setfov", observer_setfov);
03264     RegisterMethod(l, "rotate", observer_rotate);
03265     RegisterMethod(l, "center", observer_center);
03266     RegisterMethod(l, "centerorbit", observer_centerorbit);
03267     RegisterMethod(l, "follow", observer_follow);
03268     RegisterMethod(l, "synchronous", observer_synchronous);
03269     RegisterMethod(l, "chase", observer_chase);
03270     RegisterMethod(l, "lock", observer_lock);
03271     RegisterMethod(l, "track", observer_track);
03272     RegisterMethod(l, "travelling", observer_travelling);
03273     RegisterMethod(l, "getframe", observer_getframe);
03274     RegisterMethod(l, "setframe", observer_setframe);
03275     RegisterMethod(l, "gettime", observer_gettime);
03276     RegisterMethod(l, "getposition", observer_getposition);
03277     RegisterMethod(l, "getsurface", observer_getsurface);
03278     RegisterMethod(l, "setsurface", observer_setsurface);
03279     RegisterMethod(l, "splitview", observer_splitview);
03280     RegisterMethod(l, "deleteview", observer_deleteview);
03281     RegisterMethod(l, "singleview", observer_singleview);
03282     RegisterMethod(l, "getlocationflags", observer_getlocationflags);
03283     RegisterMethod(l, "setlocationflags", observer_setlocationflags);
03284     RegisterMethod(l, "__eq", observer_equal);
03285 
03286     lua_pop(l, 1); // remove metatable from stack
03287 }
03288 
03289 
03290 // ==================== Celscript-object ====================
03291 
03292 // create a CelScriptWrapper from a string:
03293 static int celscript_from_string(lua_State* l, string& script_text)
03294 {
03295 #ifdef HAVE_SSTREAM
03296     istringstream scriptfile(script_text);
03297 #else
03298     istrstream scriptfile(script_text.c_str());
03299 #endif
03300     CelestiaCore* appCore = getAppCore(l, AllErrors);
03301     CelScriptWrapper* celscript = new CelScriptWrapper(*appCore, scriptfile);
03302     if (celscript->getErrorMessage() != "")
03303     {
03304         string error = celscript->getErrorMessage();
03305         delete celscript;
03306         doError(l, error.c_str());
03307     }
03308     else
03309     {
03310         CelScriptWrapper** ud = reinterpret_cast<CelScriptWrapper**>(lua_newuserdata(l, sizeof(CelScriptWrapper*)));
03311         *ud = celscript;
03312         SetClass(l, _CelScript);
03313     }
03314 
03315     return 1;
03316 }
03317 
03318 static CelScriptWrapper* this_celscript(lua_State* l)
03319 {
03320     CelScriptWrapper** script = static_cast<CelScriptWrapper**>(CheckUserData(l, 1, _CelScript));
03321     if (script == NULL)
03322     {
03323         doError(l, "Bad CEL-script object!");
03324     }
03325     return *script;
03326 }
03327 
03328 static int celscript_tostring(lua_State* l)
03329 {
03330     lua_pushstring(l, "[Celscript]");
03331 
03332     return 1;
03333 }
03334 
03335 static int celscript_tick(lua_State* l)
03336 {
03337     CelScriptWrapper* script = this_celscript(l);
03338     LuaState* stateObject = getLuaStateObject(l);
03339     double t = stateObject->getTime();
03340     lua_pushboolean(l, !(script->tick(t)) );
03341     return 1;
03342 }
03343 
03344 static int celscript_gc(lua_State* l)
03345 {
03346     CelScriptWrapper* script = this_celscript(l);
03347     delete script;
03348     return 0;
03349 }
03350 
03351 
03352 static void CreateCelscriptMetaTable(lua_State* l)
03353 {
03354     CreateClassMetatable(l, _CelScript);
03355 
03356     RegisterMethod(l, "__tostring", celscript_tostring);
03357     RegisterMethod(l, "tick", celscript_tick);
03358     RegisterMethod(l, "__gc", celscript_gc);
03359 
03360     lua_pop(l, 1); // remove metatable from stack
03361 }
03362 
03363 
03364 // ==================== Celestia-object ====================
03365 static int celestia_new(lua_State* l, CelestiaCore* appCore)
03366 {
03367     CelestiaCore** ud = reinterpret_cast<CelestiaCore**>(lua_newuserdata(l, sizeof(CelestiaCore*)));
03368     *ud = appCore;
03369 
03370     SetClass(l, _Celestia);
03371 
03372     return 1;
03373 }
03374 
03375 static CelestiaCore* to_celestia(lua_State* l, int index)
03376 {
03377     CelestiaCore** appCore = static_cast<CelestiaCore**>(CheckUserData(l, index, _Celestia));
03378     if (appCore == NULL)
03379         return NULL;
03380     else
03381         return *appCore;
03382 }
03383 
03384 static CelestiaCore* this_celestia(lua_State* l)
03385 {
03386     CelestiaCore* appCore = to_celestia(l, 1);
03387     if (appCore == NULL)
03388     {
03389         doError(l, "Bad celestia object!");
03390     }
03391 
03392     return appCore;
03393 }
03394 
03395 
03396 static int celestia_flash(lua_State* l)
03397 {
03398     checkArgs(l, 2, 3, "One or two arguments expected to function celestia:flash");
03399 
03400     CelestiaCore* appCore = this_celestia(l);
03401     const char* s = safeGetString(l, 2, AllErrors, "First argument to celestia:flash must be a string");
03402     double duration = safeGetNumber(l, 3, WrongType, "Second argument to celestia:flash must be a number", 1.5);
03403     if (duration < 0.0)
03404     {
03405         duration = 1.5;
03406     }
03407 
03408     appCore->flash(s, duration);
03409 
03410     return 0;
03411 }
03412 
03413 static int celestia_print(lua_State* l)
03414 {
03415     checkArgs(l, 2, 7, "One to six arguments expected to function celestia:print");
03416 
03417     CelestiaCore* appCore = this_celestia(l);
03418     const char* s = safeGetString(l, 2, AllErrors, "First argument to celestia:print must be a string");
03419     double duration = safeGetNumber(l, 3, WrongType, "Second argument to celestia:print must be a number", 1.5);
03420     int horig = (int)safeGetNumber(l, 4, WrongType, "Third argument to celestia:print must be a number", -1.0);
03421     int vorig = (int)safeGetNumber(l, 5, WrongType, "Fourth argument to celestia:print must be a number", -1.0);
03422     int hoff = (int)safeGetNumber(l, 6, WrongType, "Fifth argument to celestia:print must be a number", 0.0);
03423     int voff = (int)safeGetNumber(l, 7, WrongType, "Sixth argument to celestia:print must be a number", 5.0);
03424 
03425     if (duration < 0.0)
03426     {
03427         duration = 1.5;
03428     }
03429 
03430     appCore->showText(s, horig, vorig, hoff, voff, duration);
03431 
03432     return 0;
03433 }
03434 
03435 static int celestia_gettextwidth(lua_State* l)
03436 {
03437     checkArgs(l, 2, 2, "One argument expected to function celestia:gettextwidth");
03438 
03439     CelestiaCore* appCore = this_celestia(l);
03440     const char* s = safeGetString(l, 2, AllErrors, "First argument to celestia:gettextwidth must be a string");
03441 
03442     lua_pushnumber(l, appCore->getTextWidth(s));
03443 
03444     return 1;
03445 }
03446 
03447 static int celestia_show(lua_State* l)
03448 {
03449     checkArgs(l, 1, 1000, "Wrong number of arguments to celestia:show");
03450     CelestiaCore* appCore = this_celestia(l);
03451 
03452     int argc = lua_gettop(l);
03453     int flags = 0;
03454     for (int i = 2; i <= argc; i++)
03455     {
03456         string renderFlag = safeGetString(l, i, AllErrors, "Arguments to celestia:show() must be strings");
03457         if (renderFlag == "lightdelay")
03458             appCore->setLightDelayActive(true);
03459         else
03460         if (RenderFlagMap.count(renderFlag) > 0)
03461             flags |= RenderFlagMap[renderFlag];
03462     }
03463 
03464     Renderer* r = appCore->getRenderer();
03465     r->setRenderFlags(r->getRenderFlags() | flags);
03466     appCore->notifyWatchers(CelestiaCore::RenderFlagsChanged);
03467 
03468     return 0;
03469 }
03470 
03471 static int celestia_hide(lua_State* l)
03472 {
03473     checkArgs(l, 1, 1000, "Wrong number of arguments to celestia:hide");
03474     CelestiaCore* appCore = this_celestia(l);
03475 
03476     int argc = lua_gettop(l);
03477     int flags = 0;
03478     for (int i = 2; i <= argc; i++)
03479     {
03480         string renderFlag = safeGetString(l, i, AllErrors, "Arguments to celestia:hide() must be strings"); 
03481         if (renderFlag == "lightdelay")
03482             appCore->setLightDelayActive(false);
03483         else
03484         if (RenderFlagMap.count(renderFlag) > 0)
03485             flags |= RenderFlagMap[renderFlag];
03486     }
03487 
03488     Renderer* r = appCore->getRenderer();
03489     r->setRenderFlags(r->getRenderFlags() & ~flags);
03490     appCore->notifyWatchers(CelestiaCore::RenderFlagsChanged);
03491 
03492     return 0;
03493 }
03494 
03495 static int celestia_setrenderflags(lua_State* l)
03496 {
03497     checkArgs(l, 2, 2, "One argument expected for celestia:setrenderflags()");
03498     CelestiaCore* appCore = this_celestia(l);
03499     if (!lua_istable(l, 2))
03500     {
03501         doError(l, "Argument to celestia:setrenderflags() must be a table");
03502     }
03503 
03504     int renderFlags = appCore->getRenderer()->getRenderFlags();
03505     lua_pushnil(l);
03506     while (lua_next(l, -2) != 0)
03507     {
03508         string key;
03509         bool value = false;
03510         if (lua_isstring(l, -2))
03511         {
03512             key = lua_tostring(l, -2);
03513         }
03514         else
03515         {
03516             doError(l, "Keys in table-argument to celestia:setrenderflags() must be strings");
03517         }
03518         if (lua_isboolean(l, -1))
03519         {
03520             value = lua_toboolean(l, -1);
03521         }
03522         else
03523         {
03524             doError(l, "Values in table-argument to celestia:setrenderflags() must be boolean");
03525         }
03526         if (key == "lightdelay")
03527         {
03528             appCore->setLightDelayActive(value);
03529         }
03530         else if (RenderFlagMap.count(key) > 0)
03531         {
03532             int flag = RenderFlagMap[key];
03533             if (value)
03534             {
03535                 renderFlags |= flag;
03536             }
03537             else
03538             {
03539                 renderFlags &= ~flag;
03540             }
03541         }
03542         else
03543         {
03544             cerr << "Unknown key: " << key << "\n";
03545         }
03546         lua_pop(l,1);
03547     }
03548     appCore->getRenderer()->setRenderFlags(renderFlags);
03549     appCore->notifyWatchers(CelestiaCore::RenderFlagsChanged);
03550 
03551     return 0;
03552 }
03553 
03554 static int celestia_getrenderflags(lua_State* l)
03555 {
03556     checkArgs(l, 1, 1, "No arguments expected for celestia:getrenderflags()");
03557     CelestiaCore* appCore = this_celestia(l);
03558     lua_newtable(l);
03559     FlagMap::const_iterator it = RenderFlagMap.begin();
03560     const int renderFlags = appCore->getRenderer()->getRenderFlags();
03561     while (it != RenderFlagMap.end())
03562     {
03563         string key = it->first;
03564         lua_pushstring(l, key.c_str());
03565         lua_pushboolean(l, (it->second & renderFlags) != 0);
03566         lua_settable(l,-3);
03567         it++;
03568     }
03569     lua_pushstring(l, "lightdelay");
03570     lua_pushboolean(l, appCore->getLightDelayActive());
03571     lua_settable(l, -3);
03572     return 1;
03573 }
03574 
03575 int celestia_getscreendimension(lua_State* l)
03576 {
03577     checkArgs(l, 1, 1, "No arguments expected for celestia:getscreendimension()");
03578     // error checking only:
03579     this_celestia(l);
03580     // Get the dimensions of the current viewport
03581     int viewport[4];
03582     glGetIntegerv(GL_VIEWPORT, viewport);
03583     lua_pushnumber(l, viewport[2]-viewport[0]);
03584     lua_pushnumber(l, viewport[3]-viewport[1]);
03585     return 2;
03586 }
03587 
03588 static int celestia_showlabel(lua_State* l)
03589 {
03590     checkArgs(l, 1, 1000, "Bad method call!");
03591     CelestiaCore* appCore = this_celestia(l);
03592 
03593     int argc = lua_gettop(l);
03594     int flags = 0;
03595     for (int i = 2; i <= argc; i++)
03596     {
03597         string labelFlag = safeGetString(l, i, AllErrors, "Arguments to celestia:showlabel() must be strings"); 
03598         if (LabelFlagMap.count(labelFlag) > 0)
03599             flags |= LabelFlagMap[labelFlag];
03600     }
03601 
03602     Renderer* r = appCore->getRenderer();
03603     r->setLabelMode(r->getLabelMode() | flags);
03604     appCore->notifyWatchers(CelestiaCore::LabelFlagsChanged);
03605 
03606     return 0;
03607 }
03608 
03609 static int celestia_hidelabel(lua_State* l)
03610 {
03611     checkArgs(l, 1, 1000, "Invalid number of arguments in celestia:hidelabel");
03612     CelestiaCore* appCore = this_celestia(l);
03613 
03614     int argc = lua_gettop(l);
03615     int flags = 0;
03616     for (int i = 2; i <= argc; i++)
03617     {
03618         string labelFlag = safeGetString(l, i, AllErrors, "Arguments to celestia:hidelabel() must be strings"); 
03619         if (LabelFlagMap.count(labelFlag) > 0)
03620             flags |= LabelFlagMap[labelFlag];
03621     }
03622 
03623     Renderer* r = appCore->getRenderer();
03624     r->setLabelMode(r->getLabelMode() & ~flags);
03625     appCore->notifyWatchers(CelestiaCore::LabelFlagsChanged);
03626 
03627     return 0;
03628 }
03629 
03630 static int celestia_setlabelflags(lua_State* l)
03631 {
03632     checkArgs(l, 2, 2, "One argument expected for celestia:setlabelflags()");
03633     CelestiaCore* appCore = this_celestia(l);
03634     if (!lua_istable(l, 2))
03635     {
03636         doError(l, "Argument to celestia:setlabelflags() must be a table");
03637     }
03638 
03639     int labelFlags = appCore->getRenderer()->getLabelMode();
03640     lua_pushnil(l);
03641     while (lua_next(l, -2) != 0)
03642     {
03643         string key;
03644         bool value = false;
03645         if (lua_isstring(l, -2))
03646         {
03647             key = lua_tostring(l, -2);
03648         }
03649         else
03650         {
03651             doError(l, "Keys in table-argument to celestia:setlabelflags() must be strings");
03652         }
03653         if (lua_isboolean(l, -1))
03654         {
03655             value = lua_toboolean(l, -1);
03656         }
03657         else
03658         {
03659             doError(l, "Values in table-argument to celestia:setlabelflags() must be boolean");
03660         }
03661         if (LabelFlagMap.count(key) == 0)
03662         {
03663             cerr << "Unknown key: " << key << "\n";
03664         }
03665         else
03666         {
03667             int flag = LabelFlagMap[key];
03668             if (value)
03669             {
03670                 labelFlags |= flag;
03671             }
03672             else
03673             {
03674                 labelFlags &= ~flag;
03675             }
03676         }
03677         lua_pop(l,1);
03678     }
03679     appCore->getRenderer()->setLabelMode(labelFlags);
03680     appCore->notifyWatchers(CelestiaCore::LabelFlagsChanged);
03681 
03682     return 0;
03683 }
03684 
03685 static int celestia_getlabelflags(lua_State* l)
03686 {
03687     checkArgs(l, 1, 1, "No arguments expected for celestia:getlabelflags()");
03688     CelestiaCore* appCore = this_celestia(l);
03689     lua_newtable(l);
03690     FlagMap::const_iterator it = LabelFlagMap.begin();
03691     const int labelFlags = appCore->getRenderer()->getLabelMode();
03692     while (it != LabelFlagMap.end())
03693     {
03694         string key = it->first;
03695         lua_pushstring(l, key.c_str());
03696         lua_pushboolean(l, (it->second & labelFlags) != 0);
03697         lua_settable(l,-3);
03698         it++;
03699     }
03700     return 1;
03701 }
03702 
03703 static int celestia_setorbitflags(lua_State* l)
03704 {
03705     checkArgs(l, 2, 2, "One argument expected for celestia:setorbitflags()");
03706     CelestiaCore* appCore = this_celestia(l);
03707     if (!lua_istable(l, 2))
03708     {
03709         doError(l, "Argument to celestia:setorbitflags() must be a table");
03710     }
03711 
03712     int orbitFlags = appCore->getRenderer()->getOrbitMask();
03713     lua_pushnil(l);
03714     while (lua_next(l, -2) != 0)
03715     {
03716         string key;
03717         bool value = false;
03718         if (lua_isstring(l, -2))
03719         {
03720             key = lua_tostring(l, -2);
03721         }
03722         else
03723         {
03724             doError(l, "Keys in table-argument to celestia:setorbitflags() must be strings");
03725         }
03726         if (lua_isboolean(l, -1))
03727         {
03728             value = lua_toboolean(l, -1);
03729         }
03730         else
03731         {
03732             doError(l, "Values in table-argument to celestia:setorbitflags() must be boolean");
03733         }
03734         if (BodyTypeMap.count(key) == 0)
03735         {
03736             cerr << "Unknown key: " << key << "\n";
03737         }
03738         else
03739         {
03740             int flag = BodyTypeMap[key];
03741             if (value)
03742             {
03743                 orbitFlags |= flag;
03744             }
03745             else
03746             {
03747                 orbitFlags &= ~flag;
03748             }
03749         }
03750         lua_pop(l,1);
03751     }
03752     appCore->getRenderer()->setOrbitMask(orbitFlags);
03753     return 0;
03754 }
03755 
03756 static int celestia_getorbitflags(lua_State* l)
03757 {
03758     checkArgs(l, 1, 1, "No arguments expected for celestia:getorbitflags()");
03759     CelestiaCore* appCore = this_celestia(l);
03760     lua_newtable(l);
03761     FlagMap::const_iterator it = BodyTypeMap.begin();
03762     const int orbitFlags = appCore->getRenderer()->getOrbitMask();
03763     while (it != BodyTypeMap.end())
03764     {
03765         string key = it->first;
03766         lua_pushstring(l, key.c_str());
03767         lua_pushboolean(l, (it->second & orbitFlags) != 0);
03768         lua_settable(l,-3);
03769         it++;
03770     }
03771     return 1;
03772 }
03773 
03774 static int celestia_setfaintestvisible(lua_State* l)
03775 {
03776     checkArgs(l, 2, 2, "One argument expected for celestia:setfaintestvisible()");
03777     CelestiaCore* appCore = this_celestia(l);
03778     float faintest = (float)safeGetNumber(l, 2, AllErrors, "Argument to celestia:setfaintestvisible() must be a number");
03779     if ((appCore->getRenderer()->getRenderFlags() & Renderer::ShowAutoMag) == 0)
03780     {
03781         faintest = min(15.0f, max(1.0f, faintest));
03782         appCore->setFaintest(faintest);
03783         appCore->notifyWatchers(CelestiaCore::FaintestChanged);
03784     }
03785     else
03786     {
03787         faintest = min(12.0f, max(6.0f, faintest));
03788         appCore->getRenderer()->setFaintestAM45deg(faintest);
03789         appCore->setFaintestAutoMag();
03790     }
03791     return 0;
03792 }
03793 
03794 static int celestia_getfaintestvisible(lua_State* l)
03795 {
03796     checkArgs(l, 1, 1, "No arguments expected for celestia:getfaintestvisible()");
03797     CelestiaCore* appCore = this_celestia(l);
03798     if ((appCore->getRenderer()->getRenderFlags() & Renderer::ShowAutoMag) == 0)
03799     {
03800         lua_pushnumber(l, appCore->getSimulation()->getFaintestVisible());
03801     }
03802     else
03803     {
03804         lua_pushnumber(l, appCore->getRenderer()->getFaintestAM45deg());
03805     }
03806     return 1;
03807 }
03808 
03809 static int celestia_setminfeaturesize(lua_State* l)
03810 {
03811     checkArgs(l, 2, 2, "One argument expected for celestia:setminfeaturesize()");
03812     CelestiaCore* appCore = this_celestia(l);
03813     float minFeatureSize = (float)safeGetNumber(l, 2, AllErrors, "Argument to celestia:setminfeaturesize() must be a number");
03814     minFeatureSize = max(0.0f, minFeatureSize);
03815     appCore->getRenderer()->setMinimumFeatureSize(minFeatureSize);
03816     return 0;
03817 }
03818 
03819 static int celestia_getminfeaturesize(lua_State* l)
03820 {
03821     checkArgs(l, 1, 1, "No arguments expected for celestia:getminfeaturesize()");
03822     CelestiaCore* appCore = this_celestia(l);
03823     lua_pushnumber(l, appCore->getRenderer()->getMinimumFeatureSize());
03824     return 1;
03825 }
03826 
03827 static int celestia_getobserver(lua_State* l)
03828 {
03829     checkArgs(l, 1, 1, "No arguments expected for celestia:getobserver()");
03830 
03831     CelestiaCore* appCore = this_celestia(l);
03832     Observer* o = appCore->getSimulation()->getActiveObserver();
03833     if (o == NULL)
03834         lua_pushnil(l);
03835     else
03836         observer_new(l, o);
03837 
03838     return 1;
03839 }
03840 
03841 static int celestia_getobservers(lua_State* l)
03842 {
03843     checkArgs(l, 1, 1, "No arguments expected for celestia:getobservers()");
03844     CelestiaCore* appCore = this_celestia(l);
03845 
03846     vector<Observer*> observer_list;
03847     getObservers(appCore, observer_list);
03848     lua_newtable(l);
03849     for (unsigned int i = 0; i < observer_list.size(); i++)
03850     {
03851         observer_new(l, observer_list[i]);
03852         lua_rawseti(l, -2, i + 1);
03853     }
03854 
03855     return 1;
03856 }
03857 
03858 static int celestia_getselection(lua_State* l)
03859 {
03860     checkArgs(l, 1, 1, "No arguments expected to celestia:getselection()");
03861     CelestiaCore* appCore = this_celestia(l);
03862     Selection sel = appCore->getSimulation()->getSelection();
03863     object_new(l, sel);
03864 
03865     return 1;
03866 }
03867 
03868 static int celestia_find(lua_State* l)
03869 {
03870     checkArgs(l, 2, 2, "One argument expected for function celestia:find()");
03871     if (!lua_isstring(l, 2))
03872     {
03873         doError(l, "Argument to find must be a string");
03874     }
03875 
03876     CelestiaCore* appCore = this_celestia(l);
03877     Simulation* sim = appCore->getSimulation();
03878     // Should use universe not simulation for finding objects
03879     Selection sel = sim->findObjectFromPath(lua_tostring(l, 2));
03880     object_new(l, sel);
03881 
03882     return 1;
03883 }
03884 
03885 static int celestia_select(lua_State* l)
03886 {
03887     checkArgs(l, 2, 2, "One argument expected for celestia:select()");
03888     CelestiaCore* appCore = this_celestia(l);
03889 
03890     Simulation* sim = appCore->getSimulation();
03891     Selection* sel = to_object(l, 2);
03892 
03893     // If the argument is an object, set the selection; if it's anything else
03894     // clear the selection.
03895     if (sel != NULL)
03896         sim->setSelection(*sel);
03897     else
03898         sim->setSelection(Selection());
03899 
03900     return 0;
03901 }
03902 
03903 static int celestia_mark(lua_State* l)
03904 {
03905     checkArgs(l, 2, 2, "One argument expected to function celestia:mark");
03906 
03907     CelestiaCore* appCore = this_celestia(l);
03908     Simulation* sim = appCore->getSimulation();
03909     Selection* sel = to_object(l, 2);
03910 
03911     if (sel != NULL)
03912     {
03913         sim->getUniverse()->markObject(*sel, 10.0f,
03914                                        Color(0.0f, 1.0f, 0.0f), Marker::Diamond, 1);
03915     }
03916     else
03917     {
03918         doError(l, "Argument to celestia:mark must be an object");
03919     }
03920 
03921     return 0;
03922 }
03923 
03924 static int celestia_unmark(lua_State* l)
03925 {
03926     checkArgs(l, 2, 2, "One argument expected to function celestia:unmark");
03927 
03928     CelestiaCore* appCore = this_celestia(l);
03929     Simulation* sim = appCore->getSimulation();
03930     Selection* sel = to_object(l, 2);
03931 
03932     if (sel != NULL)
03933     {
03934         sim->getUniverse()->unmarkObject(*sel, 1);
03935     }
03936     else
03937     {
03938         doError(l, "Argument to celestia:unmark must be an object");
03939     }
03940 
03941     return 0;
03942 }
03943 
03944 static int celestia_gettime(lua_State* l)
03945 {
03946     checkArgs(l, 1, 1, "No argument expected to function celestia:gettime");
03947 
03948     CelestiaCore* appCore = this_celestia(l);
03949     Simulation* sim = appCore->getSimulation();
03950     lua_pushnumber(l, sim->getTime());
03951 
03952     return 1;
03953 }
03954 
03955 static int celestia_gettimescale(lua_State* l)
03956 {
03957     checkArgs(l, 1, 1, "No argument expected to function celestia:gettimescale");
03958 
03959     CelestiaCore* appCore = this_celestia(l);
03960     lua_pushnumber(l, appCore->getSimulation()->getTimeScale());
03961 
03962     return 1;
03963 }
03964 
03965 static int celestia_settime(lua_State* l)
03966 {
03967     checkArgs(l, 2, 2, "One argument expected to function celestia:settime");
03968 
03969     CelestiaCore* appCore = this_celestia(l);
03970     double t = safeGetNumber(l, 2, AllErrors, "Second arg to celestia:settime must be a number");
03971     appCore->getSimulation()->setTime(t);
03972 
03973     return 0;
03974 }
03975 
03976 static int celestia_settimescale(lua_State* l)
03977 {
03978     checkArgs(l, 2, 2, "One argument expected to function celestia:settimescale");
03979 
03980     CelestiaCore* appCore = this_celestia(l);
03981     double t = safeGetNumber(l, 2, AllErrors, "Second arg to celestia:settimescale must be a number");
03982     appCore->getSimulation()->setTimeScale(t);
03983 
03984     return 0;
03985 }
03986 
03987 static int celestia_tojulianday(lua_State* l)
03988 {
03989     checkArgs(l, 2, 7, "Wrong number of arguments to function celestia:tojulianday");
03990 
03991     // for error checking only:
03992     this_celestia(l);
03993 
03994     int year = (int)safeGetNumber(l, 2, AllErrors, "First arg to celestia:tojulianday must be a number", 0.0);
03995     int month = (int)safeGetNumber(l, 3, WrongType, "Second arg to celestia:tojulianday must be a number", 1.0);
03996     int day = (int)safeGetNumber(l, 4, WrongType, "Third arg to celestia:tojulianday must be a number", 1.0);
03997     int hour = (int)safeGetNumber(l, 5, WrongType, "Fourth arg to celestia:tojulianday must be a number", 0.0);
03998     int minute = (int)safeGetNumber(l, 6, WrongType, "Fifth arg to celestia:tojulianday must be a number", 0.0);
03999     double seconds = safeGetNumber(l, 7, WrongType, "Sixth arg to celestia:tojulianday must be a number", 0.0);
04000 
04001     astro::Date date(year, month, day);
04002     date.hour = hour;
04003     date.minute = minute;
04004     date.seconds = seconds;
04005 
04006     double jd = (double) date;
04007 
04008     lua_pushnumber(l, jd);
04009 
04010     return 1;
04011 }
04012 
04013 static int celestia_fromjulianday(lua_State* l)
04014 {
04015     checkArgs(l, 2, 2, "Wrong number of arguments to function celestia:fromjulianday");
04016 
04017     // for error checking only:
04018     this_celestia(l);
04019 
04020     double jd = safeGetNumber(l, 2, AllErrors, "First arg to celestia:fromjulianday must be a number", 0.0);
04021     astro::Date date(jd);
04022 
04023     lua_newtable(l);
04024     setTable(l, "year", (double)date.year);
04025     setTable(l, "month", (double)date.month);
04026     setTable(l, "day", (double)date.day);
04027     setTable(l, "hour", (double)date.hour);
04028     setTable(l, "minute", (double)date.minute);
04029     setTable(l, "seconds", date.seconds);
04030 
04031     return 1;
04032 }
04033 
04034 static int celestia_unmarkall(lua_State* l)
04035 {
04036     checkArgs(l, 1, 1, "No arguments expected to function celestia:unmarkall");
04037 
04038     CelestiaCore* appCore = this_celestia(l);
04039     Simulation* sim = appCore->getSimulation();
04040     sim->getUniverse()->unmarkAll();
04041 
04042     return 0;
04043 }
04044 
04045 static int celestia_getstarcount(lua_State* l)
04046 {
04047     checkArgs(l, 1, 1, "No arguments expected to function celestia:getstarcount");
04048 
04049     CelestiaCore* appCore = this_celestia(l);
04050     Universe* u = appCore->getSimulation()->getUniverse();
04051     lua_pushnumber(l, u->getStarCatalog()->size());
04052 
04053     return 1;
04054 }
04055 
04056 static int celestia_setambient(lua_State* l)
04057 {
04058     checkArgs(l, 2, 2, "One argument expected in celestia:setambient");
04059     CelestiaCore* appCore = this_celestia(l);
04060 
04061     Renderer* renderer = appCore->getRenderer();
04062     double ambientLightLevel = safeGetNumber(l, 2, AllErrors, "Argument to celestia:setambient must be a number");
04063     if (ambientLightLevel > 1.0)
04064         ambientLightLevel = 1.0;
04065     if (ambientLightLevel < 0.0)
04066         ambientLightLevel = 0.0;
04067 
04068     if (renderer != NULL)
04069         renderer->setAmbientLightLevel((float)ambientLightLevel);
04070     appCore->notifyWatchers(CelestiaCore::AmbientLightChanged);
04071 
04072     return 0;
04073 }
04074 
04075 static int celestia_getambient(lua_State* l)
04076 {
04077     checkArgs(l, 1, 1, "No argument expected in celestia:setambient");
04078     CelestiaCore* appCore = this_celestia(l);
04079 
04080     Renderer* renderer = appCore->getRenderer();
04081     if (renderer == NULL)
04082     {
04083         doError(l, "Internal Error: renderer is NULL!");
04084     }
04085     else
04086     {
04087         lua_pushnumber(l, renderer->getAmbientLightLevel());
04088     }
04089     return 1;
04090 }
04091 
04092 static int celestia_setminorbitsize(lua_State* l)
04093 {
04094     checkArgs(l, 2, 2, "One argument expected in celestia:setminorbitsize");
04095     CelestiaCore* appCore = this_celestia(l);
04096 
04097     double orbitSize = safeGetNumber(l, 2, AllErrors, "Argument to celestia:setminorbitsize() must be a number");
04098     Renderer* renderer = appCore->getRenderer();
04099     if (renderer == NULL)
04100     {
04101         doError(l, "Internal Error: renderer is NULL!");
04102     }
04103     else
04104     {
04105         orbitSize = max(0.0, orbitSize);
04106         renderer->setMinimumOrbitSize((float)orbitSize);
04107     }
04108     return 0;
04109 }
04110 
04111 static int celestia_getminorbitsize(lua_State* l)
04112 {
04113     checkArgs(l, 1, 1, "No argument expected in celestia:getminorbitsize");
04114     CelestiaCore* appCore = this_celestia(l);
04115 
04116     Renderer* renderer = appCore->getRenderer();
04117     if (renderer == NULL)
04118     {
04119         doError(l, "Internal Error: renderer is NULL!");
04120     }
04121     else
04122     {
04123         lua_pushnumber(l, renderer->getMinimumOrbitSize());
04124     }
04125     return 1;
04126 }
04127 
04128 static int celestia_setstardistancelimit(lua_State* l)
04129 {
04130     checkArgs(l, 2, 2, "One argument expected in celestia:setstardistancelimit");
04131     CelestiaCore* appCore = this_celestia(l);
04132 
04133     double distanceLimit = safeGetNumber(l, 2, AllErrors, "Argument to celestia:setstardistancelimit() must be a number");
04134     Renderer* renderer = appCore->getRenderer();
04135     if (renderer == NULL)
04136     {
04137         doError(l, "Internal Error: renderer is NULL!");
04138     }
04139     else
04140     {
04141         renderer->setDistanceLimit((float)distanceLimit);
04142     }
04143     return 0;
04144 }
04145 
04146 static int celestia_getstardistancelimit(lua_State* l)
04147 {
04148     checkArgs(l, 1, 1, "No argument expected in celestia:getstardistancelimit");
04149     CelestiaCore* appCore = this_celestia(l);
04150 
04151     Renderer* renderer = appCore->getRenderer();
04152     if (renderer == NULL)
04153     {
04154         doError(l, "Internal Error: renderer is NULL!");
04155     }
04156     else
04157     {
04158         lua_pushnumber(l, renderer->getDistanceLimit());
04159     }
04160     return 1;
04161 }
04162 
04163 static int celestia_getstarstyle(lua_State* l)
04164 {
04165     checkArgs(l, 1, 1, "No argument expected in celestia:getstarstyle");
04166     CelestiaCore* appCore = this_celestia(l);
04167 
04168     Renderer* renderer = appCore->getRenderer();
04169     if (renderer == NULL)
04170     {
04171         doError(l, "Internal Error: renderer is NULL!");
04172     }
04173     else
04174     {
04175         Renderer::StarStyle starStyle = renderer->getStarStyle();
04176         switch (starStyle)
04177         {
04178         case Renderer::FuzzyPointStars: 
04179             lua_pushstring(l, "fuzzy"); break;
04180         case Renderer::PointStars: 
04181             lua_pushstring(l, "point"); break;
04182         case Renderer::ScaledDiscStars: 
04183             lua_pushstring(l, "disc"); break;
04184         default:
04185             lua_pushstring(l, "invalid starstyle");
04186         };
04187     }
04188     return 1;
04189 }
04190 
04191 static int celestia_setstarstyle(lua_State* l)
04192 {
04193     checkArgs(l, 2, 2, "One argument expected in celestia:setstarstyle");
04194     CelestiaCore* appCore = this_celestia(l);
04195 
04196     string starStyle = safeGetString(l, 2, AllErrors, "Argument to celestia:setstarstyle must be a string");
04197     Renderer* renderer = appCore->getRenderer();
04198     if (renderer == NULL)
04199     {
04200         doError(l, "Internal Error: renderer is NULL!");
04201     }
04202     else
04203     {
04204         if (starStyle == "fuzzy")
04205         {
04206             renderer->setStarStyle(Renderer::FuzzyPointStars);
04207         }
04208         else if (starStyle == "point")
04209         {
04210             renderer->setStarStyle(Renderer::PointStars);
04211         }
04212         else if (starStyle == "disc")
04213         {
04214             renderer->setStarStyle(Renderer::ScaledDiscStars);
04215         }
04216         else
04217         {
04218             doError(l, "Invalid starstyle");
04219         }
04220         appCore->notifyWatchers(CelestiaCore::RenderFlagsChanged);
04221         
04222     }
04223     return 0;
04224 }
04225 
04226 static int celestia_getstar(lua_State* l)
04227 {
04228     checkArgs(l, 2, 2, "One argument expected to function celestia:getstar");
04229 
04230     CelestiaCore* appCore = this_celestia(l);
04231     double starIndex = safeGetNumber(l, 2, AllErrors, "First arg to celestia:getstar must be a number");
04232     Universe* u = appCore->getSimulation()->getUniverse();
04233     Star* star = u->getStarCatalog()->getStar((uint32) starIndex);
04234     if (star == NULL)
04235         lua_pushnil(l);
04236     else
04237         object_new(l, Selection(star));
04238 
04239     return 1;
04240 }
04241 
04242 static int celestia_newvector(lua_State* l)
04243 {
04244     checkArgs(l, 4, 4, "Expected 3 arguments for celestia:newvector");
04245     // for error checking only:
04246     this_celestia(l);
04247     double x = safeGetNumber(l, 2, AllErrors, "First arg to celestia:newvector must be a number");
04248     double y = safeGetNumber(l, 3, AllErrors, "Second arg to celestia:newvector must be a number");
04249     double z = safeGetNumber(l, 4, AllErrors, "Third arg to celestia:newvector must be a number");
04250 
04251     vector_new(l, Vec3d(x,y,z));
04252 
04253     return 1;
04254 }
04255 
04256 static int celestia_newposition(lua_State* l)
04257 {
04258     checkArgs(l, 4, 4, "Expected 3 arguments for celestia:newposition");
04259     // for error checking only:
04260     this_celestia(l);
04261     BigFix components[3];
04262     for (int i = 0; i < 3; i++)
04263     {
04264         if (lua_isnumber(l, i+2))
04265         {
04266             double v = lua_tonumber(l, i+2);
04267             components[i] = BigFix(v);
04268         }
04269         else if (lua_isstring(l, i+2))
04270         {
04271             components[i] = BigFix(string(lua_tostring(l, i+2)));
04272         }
04273         else
04274         {
04275             doError(l, "Arguments to celestia:newposition must be either numbers or strings");
04276         }
04277     }
04278 
04279     position_new(l, UniversalCoord(components[0], components[1], components[2]));
04280 
04281     return 1;
04282 }
04283 
04284 static int celestia_newrotation(lua_State* l)
04285 {
04286     checkArgs(l, 3, 5, "Need 2 or 4 arguments for celestia:newrotation");
04287     // for error checking only:
04288     this_celestia(l);
04289 
04290     if (lua_gettop(l) > 3)
04291     {
04292         // if (lua_gettop == 4), safeGetNumber will catch the error
04293         double w = safeGetNumber(l, 2, AllErrors, "arguments to celestia:newrotation must either be (vec, number) or four numbers");
04294         double x = safeGetNumber(l, 3, AllErrors, "arguments to celestia:newrotation must either be (vec, number) or four numbers");
04295         double y = safeGetNumber(l, 4, AllErrors, "arguments to celestia:newrotation must either be (vec, number) or four numbers");
04296         double z = safeGetNumber(l, 5, AllErrors, "arguments to celestia:newrotation must either be (vec, number) or four numbers");
04297         Quatd q(w, x, y, z);
04298         rotation_new(l, q);
04299     }
04300     else
04301     {
04302         Vec3d* v = to_vector(l, 2);
04303         if (v == NULL)
04304         {
04305             doError(l, "newrotation: first argument must be a vector");
04306         }
04307         double angle = safeGetNumber(l, 3, AllErrors, "second argument to celestia:newrotation must be a number");
04308         Quatd q;
04309         q.setAxisAngle(*v, angle);
04310         rotation_new(l, q);
04311     }
04312     return 1;
04313 }
04314 
04315 static int celestia_getscripttime(lua_State* l)
04316 {
04317     checkArgs(l, 1, 1, "No arguments expected for celestia:getscripttime");
04318     // for error checking only:
04319     this_celestia(l);
04320 
04321     LuaState* luastate_ptr = getLuaStateObject(l);
04322     lua_pushnumber(l, luastate_ptr->getTime());
04323     return 1;
04324 }
04325 
04326 static int celestia_newframe(lua_State* l)
04327 {
04328     checkArgs(l, 2, 4, "One to three arguments expected for function celestia:newframe");
04329     int argc = lua_gettop(l);
04330 
04331     // for error checking only:
04332     this_celestia(l);
04333 
04334     const char* coordsysName = safeGetString(l, 2, AllErrors, "newframe: first argument must be a string");
04335     astro::CoordinateSystem coordSys = parseCoordSys(coordsysName);
04336     Selection* ref = NULL;
04337     Selection* target = NULL;
04338 
04339     if (coordSys == astro::Universal)
04340     {
04341         frame_new(l, FrameOfReference());
04342     }
04343     else if (coordSys == astro::PhaseLock)
04344     {
04345         if (argc >= 4)
04346         {
04347             ref = to_object(l, 3);
04348             target = to_object(l, 4);
04349         }
04350 
04351         if (ref == NULL || target == NULL)
04352         {
04353             doError(l, "newframe: two objects required for lock frame");
04354         }
04355 
04356         frame_new(l, FrameOfReference(coordSys, *ref, *target));
04357     }
04358     else
04359     {
04360         if (argc >= 3)
04361             ref = to_object(l, 3);
04362         if (ref == NULL)
04363         {
04364             doError(l, "newframe: one object argument required for frame");
04365         }
04366 
04367         frame_new(l, FrameOfReference(coordSys, *ref));
04368     }
04369 
04370     return 1;
04371 }
04372 
04373 static int celestia_requestkeyboard(lua_State* l)
04374 {
04375     checkArgs(l, 2, 2, "Need one arguments for celestia:requestkeyboard");
04376     CelestiaCore* appCore = this_celestia(l);
04377 
04378     if (!lua_isboolean(l, 2))
04379     {
04380         doError(l, "First argument for celestia:requestkeyboard must be a boolean");
04381     }
04382 
04383     int mode = appCore->getTextEnterMode();
04384 
04385     if (lua_toboolean(l, 2))
04386     {
04387         // Check for existence of charEntered:
04388         lua_pushstring(l, KbdCallback);
04389         lua_gettable(l, LUA_GLOBALSINDEX);
04390         if (lua_isnil(l, -1))
04391         {
04392             doError(l, "script requested keyboard, but did not provide callback");
04393         }
04394         lua_remove(l, -1);
04395 
04396         mode = mode | CelestiaCore::KbPassToScript;
04397     }
04398     else
04399     {
04400         mode = mode & ~CelestiaCore::KbPassToScript;
04401     }
04402     appCore->setTextEnterMode(mode);
04403 
04404     return 0;
04405 }
04406 
04407 static int celestia_takescreenshot(lua_State* l)
04408 {
04409     checkArgs(l, 1, 3, "Need 0 to 2 arguments for celestia:takescreenshot");
04410     CelestiaCore* appCore = this_celestia(l);
04411     LuaState* luastate = getLuaStateObject(l);
04412     // make sure we don't timeout because of taking a screenshot:
04413     double timeToTimeout = luastate->timeout - luastate->getTime();
04414 
04415     const char* filetype = safeGetString(l, 2, WrongType, "First argument to celestia:takescreenshot must be a string");
04416     if (filetype == NULL)
04417         filetype = "png";
04418 
04419     // Let the script safely contribute one part of the filename: 
04420     const char* fileid_ptr = safeGetString(l, 3, WrongType, "Second argument to celestia:takescreenshot must be a string");
04421     if (fileid_ptr == NULL)
04422         fileid_ptr = "";
04423     string fileid(fileid_ptr);
04424 
04425     // be paranoid about the fileid, make sure it only contains 'A-Za-z0-9_':
04426     for (unsigned int i = 0; i < fileid.length(); i++)
04427     {
04428         char ch = fileid[i];
04429         if (!((ch >= 'a' && ch <= 'z') || 
04430               (fileid[i] >= 'A' && ch <= 'Z') || 
04431               (ch >= '0' && ch <= '9') ) )
04432             fileid[i] = '_';
04433     }
04434     // limit length of string
04435     if (fileid.length() > 16)
04436         fileid = fileid.substr(0, 16);
04437     if (fileid.length() > 0)
04438         fileid.append("-");
04439 
04440     string path = appCore->getConfig()->scriptScreenshotDirectory;
04441     if (path.length() > 0 && 
04442         path[path.length()-1] != '/' && 
04443         path[path.length()-1] != '\\')
04444 
04445         path.append("/");
04446 
04447     luastate->screenshotCount++;
04448     bool success = false;
04449     char filenamestem[48];
04450     sprintf(filenamestem, "screenshot-%s%06i", fileid.c_str(), luastate->screenshotCount);
04451 
04452     // Get the dimensions of the current viewport
04453     int viewport[4];
04454     glGetIntegerv(GL_VIEWPORT, viewport);
04455 
04456 #ifndef MACOSX
04457     if (strncmp(filetype, "jpg", 3) == 0)
04458     {
04459         string filepath = path + filenamestem + ".jpg";
04460         success = CaptureGLBufferToJPEG(string(filepath),
04461                                        viewport[0], viewport[1],
04462                                        viewport[2], viewport[3]);
04463     }
04464     else
04465     {
04466         string filepath = path + filenamestem + ".png";
04467         success = CaptureGLBufferToPNG(string(filepath),
04468                                        viewport[0], viewport[1],
04469                                        viewport[2], viewport[3]);
04470     }
04471 #endif
04472     lua_pushboolean(l, success);
04473 
04474     // no matter how long it really took, make it look like 0.1s to timeout check:
04475     luastate->timeout = luastate->getTime() + timeToTimeout - 0.1;
04476     return 1;
04477 }
04478 
04479 static int celestia_createcelscript(lua_State* l)
04480 {
04481     checkArgs(l, 2, 2, "Need one argument for celestia:createcelscript()");
04482     string scripttext = safeGetString(l, 2, AllErrors, "Argument to celestia:createcelscript() must be a string");
04483     return celscript_from_string(l, scripttext);
04484 }
04485 
04486 static int celestia_requestsystemaccess(lua_State* l)
04487 {
04488     // ignore possible argument for future extensions
04489     checkArgs(l, 1, 2, "No argument expected for celestia:requestsystemaccess()");
04490     this_celestia(l);
04491     LuaState* luastate = getLuaStateObject(l);
04492     luastate->requestIO();
04493     return 0;
04494 }
04495 
04496 static int celestia_getscriptpath(lua_State* l)
04497 {
04498     // ignore possible argument for future extensions
04499     checkArgs(l, 1, 1, "No argument expected for celestia:requestsystemaccess()");
04500     this_celestia(l);
04501     lua_pushstring(l, "celestia-scriptpath");
04502     lua_gettable(l, LUA_REGISTRYINDEX);
04503     return 1;
04504 }
04505 
04506 static int celestia_tostring(lua_State* l)
04507 {
04508     lua_pushstring(l, "[Celestia]");
04509 
04510     return 1;
04511 }
04512 
04513 static void CreateCelestiaMetaTable(lua_State* l)
04514 {
04515     CreateClassMetatable(l, _Celestia);
04516 
04517     RegisterMethod(l, "__tostring", celestia_tostring);
04518     RegisterMethod(l, "flash", celestia_flash);
04519     RegisterMethod(l, "print", celestia_print);
04520     RegisterMethod(l, "gettextwidth", celestia_gettextwidth);
04521     RegisterMethod(l, "show", celestia_show);
04522     RegisterMethod(l, "hide", celestia_hide);
04523     RegisterMethod(l, "getrenderflags", celestia_getrenderflags);
04524     RegisterMethod(l, "setrenderflags", celestia_setrenderflags);
04525     RegisterMethod(l, "getscreendimension", celestia_getscreendimension);
04526     RegisterMethod(l, "showlabel", celestia_showlabel);
04527     RegisterMethod(l, "hidelabel", celestia_hidelabel);
04528     RegisterMethod(l, "getlabelflags", celestia_getlabelflags);
04529     RegisterMethod(l, "setlabelflags", celestia_setlabelflags);
04530     RegisterMethod(l, "getorbitflags", celestia_getorbitflags);
04531     RegisterMethod(l, "setorbitflags", celestia_setorbitflags);
04532     RegisterMethod(l, "getfaintestvisible", celestia_getfaintestvisible);
04533     RegisterMethod(l, "setfaintestvisible", celestia_setfaintestvisible);
04534     RegisterMethod(l, "setminfeaturesize", celestia_setminfeaturesize);
04535     RegisterMethod(l, "getminfeaturesize", celestia_getminfeaturesize);
04536     RegisterMethod(l, "getobserver", celestia_getobserver);
04537     RegisterMethod(l, "getobservers", celestia_getobservers);
04538     RegisterMethod(l, "getselection", celestia_getselection);
04539     RegisterMethod(l, "find", celestia_find);
04540     RegisterMethod(l, "select", celestia_select);
04541     RegisterMethod(l, "mark", celestia_mark);
04542     RegisterMethod(l, "unmark", celestia_unmark);
04543     RegisterMethod(l, "unmarkall", celestia_unmarkall);
04544     RegisterMethod(l, "gettime", celestia_gettime);
04545     RegisterMethod(l, "settime", celestia_settime);
04546     RegisterMethod(l, "gettimescale", celestia_gettimescale);
04547     RegisterMethod(l, "settimescale", celestia_settimescale);
04548     RegisterMethod(l, "getambient", celestia_getambient);
04549     RegisterMethod(l, "setambient", celestia_setambient);
04550     RegisterMethod(l, "getminorbitsize", celestia_getminorbitsize);
04551     RegisterMethod(l, "setminorbitsize", celestia_setminorbitsize);
04552     RegisterMethod(l, "getstardistancelimit", celestia_getstardistancelimit);
04553     RegisterMethod(l, "setstardistancelimit", celestia_setstardistancelimit);
04554     RegisterMethod(l, "getstarstyle", celestia_getstarstyle);
04555     RegisterMethod(l, "setstarstyle", celestia_setstarstyle);
04556     RegisterMethod(l, "tojulianday", celestia_tojulianday);
04557     RegisterMethod(l, "fromjulianday", celestia_fromjulianday);
04558     RegisterMethod(l, "getstarcount", celestia_getstarcount);
04559     RegisterMethod(l, "getstar", celestia_getstar);
04560     RegisterMethod(l, "newframe", celestia_newframe);
04561     RegisterMethod(l, "newvector", celestia_newvector);
04562     RegisterMethod(l, "newposition", celestia_newposition);
04563     RegisterMethod(l, "newrotation", celestia_newrotation);
04564     RegisterMethod(l, "getscripttime", celestia_getscripttime);
04565     RegisterMethod(l, "requestkeyboard", celestia_requestkeyboard);
04566     RegisterMethod(l, "takescreenshot", celestia_takescreenshot);
04567     RegisterMethod(l, "createcelscript", celestia_createcelscript);
04568     RegisterMethod(l, "requestsystemaccess", celestia_requestsystemaccess);
04569     RegisterMethod(l, "getscriptpath", celestia_getscriptpath);
04570 
04571     lua_pop(l, 1);
04572 }
04573 
04574 // ==================== Initialization ====================
04575 bool LuaState::init(CelestiaCore* appCore)
04576 {
04577     initMaps();
04578 
04579     // Import the base and math libraries
04580     lua_baselibopen(state);
04581     lua_mathlibopen(state);
04582     lua_tablibopen(state);
04583     lua_strlibopen(state);
04584 
04585     // Add an easy to use wait function, so that script writers can
04586     // live in ignorance of coroutines.  There will probably be a significant
04587     // library of useful functions that can be defined purely in Lua.
04588     // At that point, we'll want something a bit more robust than just
04589     // parsing the whole text of the library every time a script is launched
04590     if (loadScript("wait = function(x) coroutine.yield(x) end") != 0)
04591         return false;
04592     lua_pcall(state, 0, 0, 0); // execute it
04593 
04594     lua_pushstring(state, "KM_PER_MICROLY");
04595     lua_pushnumber(state, (lua_Number)KM_PER_LY/1e6);
04596     lua_settable(state, LUA_GLOBALSINDEX);
04597 
04598     CreateObjectMetaTable(state);
04599     CreateObserverMetaTable(state);
04600     CreateCelestiaMetaTable(state);
04601     CreatePositionMetaTable(state);
04602     CreateVectorMetaTable(state);
04603     CreateRotationMetaTable(state);
04604     CreateFrameMetaTable(state);
04605     CreateCelscriptMetaTable(state);
04606 
04607     // Create the celestia object
04608     lua_pushstring(state, "celestia");
04609     celestia_new(state, appCore);
04610     lua_settable(state, LUA_GLOBALSINDEX);
04611     // add reference to appCore in the registry
04612     lua_pushstring(state, "celestia-appcore");
04613     lua_pushlightuserdata(state, static_cast<void*>(appCore));
04614     lua_settable(state, LUA_REGISTRYINDEX);
04615     // add a reference to the LuaState-object in the registry
04616     lua_pushstring(state, "celestia-luastate");
04617     lua_pushlightuserdata(state, static_cast<void*>(this));
04618     lua_settable(state, LUA_REGISTRYINDEX);
04619 
04620 #if 0
04621     lua_pushstring(state, "dofile");
04622     lua_gettable(state, LUA_GLOBALSINDEX); // function "dofile" on stack
04623     lua_pushstring(state, "luainit.celx"); // parameter
04624     if (lua_pcall(state, 1, 0, 0) != 0) // execute it
04625     {
04626         CelestiaCore::Alerter* alerter = appCore->getAlerter();
04627         // copy string?!
04628         const char* errorMessage = lua_tostring(state, -1);
04629         cout << errorMessage << '\n'; cout.flush();
04630         alerter->fatalError(errorMessage);
04631         return false;
04632     }
04633 #endif
04634 
04635     return true;
04636 }

Generated on Sat Jan 14 22:30:30 2006 for Celestia by  doxygen 1.4.1