Celestia/Celx Scripting/CELX Lua Methods

From Wikibooks, the open-content textbooks collection

Jump to: navigation, search


This summary describes the support for Lua/CELX-scripting in Celestia and consists mostly of the CELX API. This documents as well as the CELX-support in Celestia probably contain a number of errors or bugs. Please report any problems you find.

Contents

[edit] Introduction

[edit] About this document

It was started to document the available functionality during development celestia v1.3.1pre11, and was then extended as I contributed new functionality. CELX uses the Lua Programming Language, so you are writing real programs. This is completely different than older CEL Celestia scripts, which are just sequences of fixed commands. So you probably should have a little experience in programming, otherwise this document won't help you much. And it's a good idea to read at least the first sections of the Lua-Documentation, available here: http://www.lua.org/manual/5.0/

[edit] How CELX works in Celestia

Celestia works roughly(!) by repeating this:

  • Check user input, change rendering settings accordingly (e.g. enable rendering of orbits, change observer position/orientation)
  • Update the simulation time
  • Update the observer's position if goto is active
  • Render all objects using the current settings (renderflags, time. positions)


If a CELX-script has been started, it is executed just before rendering begins. Then Celestia gives control to the Lua-interpreter, which continues to execute the script where he stopped the last time. When you call a CELX-method in a script, e.g. celestia:print(), the Lua-interpreter calls in fact a little C++ function which converts the arguments from Lua-types to C++-types, calls the right Celestia-methods to perform the action, and if necessary converts the C++ return value back to a Lua-value. Note that the Lua-interpreter called this C++ function, so when it returns the script continues, there is never a chance to return control back to the Celestia loop. To do this, the script has to call the "wait()" command, which makes the Lua-interpreter return control.


Forgetting to call wait() in Celestia 1.3.1 meant that Celestia never gained control again, and thus couldn't even handle the command to stop the script, or quit - it was completely blocked. Celestia v1.3.2 (and later versions) periodically checks if the scripts has exceeded the maximum allowed time to execute (5s), and if it has it terminates the script.


It should have become obvious that most actions don't really change anything immediately, instead they change a setting which is used later on during rendering, which from the point of view of the script happens while calling wait(). So if you change the position of the observer ten times without calling wait() in between, this will have no effect - only the last position will actually be used in rendering.

[edit] Syntax

For a complete description of the syntax of Lua, please read http://www.lua.org/manual/5.0/ Celestia version 1.5.0 uses version Lua version 5.1, which differs slightly from version 5.0 of Lua used in earlier Celestia releases. The Lua 5.1 manual can be found at http://www.lua.org/manual/5.1/ For details about what changed between versions 5.0 and 5.1, see http://www.lua.org/manual/5.1/manual.html#7


Some examples and quick notes:

You don't need anything like ";" to complete a line, simply write line after line. Blocks are enclosed by "begin ... end", short comments start with a "--" (two hyphens), long (multiline) comments use "--[[ comment ]]".

[edit] Variables & Types

Lua-variables don't have to be declared, and don't have a type. But the content has a type, so you can do:

a = 1 
a = a  / 2 -- a is now 0.5 
a = "Hello World" 
a = a / 2 -- This is an error! 
a = true 
a = not a -- a is now false 


Besides from numbers (which are doubles), booleans and strings there are tables, which can be used as arrays or hashes. A table is used like this:

t = {} 
t["key1"] = "value1" -- long form 
t.key2 = "value2" -- short form 
u = { key1="value1", key2="value2" } -- u is now the same as t 
v = { 1,2,3,4,5 } -- v[1] is now 1, v[2] is 2, ... 


Then there is one special type called userdata, this is what the objects to control celestia are. To operate on this data you can call methods for these objects. To use Lua for celestia, you mostly need to know only about the available objects, the methods defined on them and how to call them - everybody who already knows Object-Oriented-Programming should feel right at home.


At the beginning of your script, the global variable celestia is defined, holding a reference to the core-functionality of celestia. To call methods on it, use this syntax:

obs = celestia:getobserver() -- get the current observer, store it in "obs" 
earth = celestia:find("Earth") -- find the object representing Earth, store it in a variable "earth" 
mars = earth -- do something to confuse the reader 


These objects are mostly wrappers around objects which are really used in Celestia.


You then could use obs and earth like this:

obs:goto(earth) 


This will start the goto-command: it moves the observer to earth, just as if you had pressed "g" in celestia after selecting earth.


[edit] Function definition and call:

function add_one(i) 
  -- use 'local' to declare variables local to function: 
  local j = i + 1 
  return j 
end 
function divide(i, j) 
  return i / j 
end 
-- and now call them: 
a = add_one(1) 
b = divide(a, 2) 


Note: Lua can return multiple values at once, see the Lua-documentation for details.

The function "wait(number:n)" is predefined in celestia waits n seconds. It is special because it returns control to celestia, which you have to do to avoid blocking celestia. Many more functions are defined in Lua (see Lua-docs), such as mathematical functions or string operations. Note that not all Lua-libraries are loaded, you don't have access to io and debug-functions (which greatly reduces the danger of security-problems by "evil" scripts). But see the methode celestia:requestsystemaccess() for more information.


[edit] Control Structures

Lua has the typical control-structures, like while, if, for. They are used like this:

for i = 1, 10 do 
  ... 
end 
 
i = 1 
while i <= 11 do 
  ... 
i = i + 1 
end 
 
if i > j then 
  ... 
elsif i < j then 
  ... 
else 
  ... 
end 

To loop over the contents of a table "tbl", you can use this:

for key, value in pairs(tbl) do 
  -- tbl[key] == value 
end 
 
-- Use this for tabels used as arrays, i.e. indexed by numbers 1..n: 
for i, value in ipairs(tbl) do 
  -- tbl[i] == value 
end

[edit] Objects and Methods (The CELX API)

This sections documents the classes (types of objects) available in Lua-scripting. You can't create an object (i.e. an userdata) yourself, you must call some method to create one. As you only have the celestia-object (ignoring methods which are not Celestia related) available when the script is starting, you must use it to create other objects. For example, to call the method getposition() of observer, you have to get an observer-instance from celestia first and then call getposition on it:

o = celestia:getobserver() 
pos = o:getposition() 


Note that while I am using names for the various classes in this documentation, these names have no real meaning in a script. Even "celestia" is just a variable holding an object of type celestia and not really special. Each method is listed with it's signature:

return_value object:methodname(parameter, parameter[, optional parameter])

This is followed by a short description of what the method does, then a list of the parameters and what they are for, and possibly some notes about how to use this method, what it does exactly etc.

[edit] Object and method index:

[edit] celestia

[edit] observer

[edit] object

[edit] position

[edit] vector

[edit] rotation

[edit] frame

[edit] phase

1.6.0 Phases available only in Celestia 1.6.0 and later

[edit] celscript

[edit] Callbacks

Right now there are two callbacks, i.e. functions which are executed as a result of external events. Callbacks are limited in what you can do: the callback must complete within 1 second, or it will be terminated, and you can't use wait() in a callback.

[edit] Keyboard

The callback for keyboard input must have the name "celestia_keyboard_callback". After a script activates the handling keyboard-input by calling celestia:requestkeyboard(true), any keypress will result in a call to a method with this name.

The method should accept one parameter, a string, which holds the char of the key which has been pressed. Accented characters like ä or é will be passed as UTF-8 sequences, i.e. as a multibyte sequence - if you only want to handle ASCII you can safely ignore this. Keys like CTRL-A are passed as strings containing a char with ASCII-codes < 32, as are Enter (13) or Backspace (8). Some special keys like cursor-keys or ESC won't activate the callback.


The callback can return either true or false, indicating that it either has handled the keypress or that celestia should continue normal processing for this key. Not returning any value is the same as returning true. If an error occurs while processing the callback, the error message will be written to the console (visible with "~") and an implicit false is returned, i.e. celestia will continue processing for this key.

[edit] Cleanup

Whenever a Lua script terminates (because it ended, the user terminated it or an error occurred), Celestia will try to execute a function with the name "celestia_cleanup_callback". This function can be used to perform any cleanup actions, notably restoring settings to the values in use before the script started. Errors will only be reported in the console.

[edit] Notes

[edit] Typical Problems

  • Lua scripts are executed between Celestia's rendering phases. So if you do some lengthy calculation (or an endless loop), Celestia won't be able to update the screen or even check for key presses (i.e. you can't stop the script by pressing ESC). From the script's point of view, rendering and UI-handling only happen during calls to wait(). If the script doesn't return control to Celestia for more than 5 seconds, then the script is terminated.
  • If you want to move the observer smoothly, do this by setting its position and then call wait. But watch out: if "track" is activated on a body this can override any setorientation() in the script. A high time rate will change the position of planets significantly before they are rendered, which can result in a jerky movement. If necessary, check and reset time and timerate in your script.
  • On Windows wait() can return very quickly without doing any rendering, while on Linux it reliably enforces a rendering pass.
  • The user can change settings while a script is running, including which body is selected, position, orientation, speed, timerate, renderflags etc. Don't rely on these to stay constant, if necessary you have to continuously reset them.
  • Reference frames can be difficult to master at first. If you are trying to set the observer to a position within the solar system, but get distances to the sun of about 206 au instead, you probably got your reference-frame wrong. Before version 1.5.0, 206 AU is the distance between the point of origin [0,0,0] in universal coordinates and the position of the Sun. Starting with v 1.5.0, Celestia places the Solar System's barycenter at [0,0,0].
  • If your script turns your screen completely black or other weird effects occur, check for any usage of setorientation() or lookat(). You probably have your orientation set to an "invalid" quaternion (for example all zero), possibly by using lookat() with your up-vector parallel to the direction of view. If this happens, you should get celestia usable again by running a script which sets the orientation to some sensible value (or simply by restarting Celestia).