Celestia/Celx Scripting/CELX Lua Methods

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.

It was started to document the available functionality during development celestia v1.3.1pre11, and was then extended as new functionality was contributed.

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.

It's also a good idea to read at least the first sections of the Lua-Documentation, available here:

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() function, 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 or quit the script - it was completely blocked. As from version 1.3.2, Celestia periodically checks if the scripts has exceeded the maximum allowed time to execute (5 seconds), 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.

Lua commands can be terminated either by a textual line terminator or by a semicolon. If you want to include several Lua commands on a single line of text, separate them by semi-colons (;), but if there is only a single Lua command on a line of text, no semi-colon is needed.

Write your CELX script by using a line editor like "Notepad", "Notepad++" or "Wordpad", without text formatting;

Short (singleline) comments start with two hyphens "--";

Long (multiline) comments use "--[[ comment ]]".

When you use the comment "-- Title: string:text" in the first line of the CELX script, the string:text title will be displayed in the Celestia/File/Scripts drop down menu instead of the filename.

Example:

-- Title: Example script
-- This is a short comment line.
-- [[ This
is
a
long
comment
line.]]
a = 5; b = 6 -- two Lua commands on a single line
c = 3 -- one Lua command on a line

Lua-variables don't have to be declared and don't have a type, but the content has a type.

There are eight basic types in Lua:

nil

Nil is the type of the value nil, whose main property is to be different from any other value; it usually represents the absence of a useful value.

boolean

Boolean is the type of the values false and true.
Both nil and false make a condition false; any other value makes it true.

number

Number represents real (double-precision floating-point) numbers.

string

String represents arrays of any 8-bit characters.

table

Tables can be used as arrays that can be indexed not only with numbers, but with any value (except nil).
Tables also can contain values of all types (except nil).

userdata

The objects to control Celestia are of this special type userdata.

function

The function type is an executable expression, compiled from a block of Lua and CELX code.

thread

Not further explained here.

Example:
The same variable "a" can have content of different type.

a = 1 -- a is 1, an integer number
a = a / 2 -- a is now 0.5, changed into a floatingpoint number
a = "Hello World" -- a contains a string now
a = a / 3 -- ERROR, because a string is divided by a number!
a = true -- a is true, a boolean
a = not a -- a is now false, a boolean

To operate on userdata, you can call methods for the 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 a script, the celestia object is automatically defined, holding a reference to the core-functionality of Celestia! To operate on this celestia object you can call methods which are defined on it by using the "celestia:" prefix, as shown in section celestia methods.

Example:

-- Get observer instance of the active view and store it in "obs"
obs = celestia:getobserver()
-- Find the celestial object representing Earth and store it in "earth"
earth = celestia:find("Sol/Earth")
-- Do something to confuse the reader, but possible in CELX
-- The object with name mars is made equal to the object with name earth
mars = earth
-- You can also use obs and earth like this.
obs:goto(earth)
-- This will start the goto-command, which moves the observer to Earth,
-- just as if you had pressed the [G] key in Celestia, after selecting Earth.

A function is an executable expression, compiled from a block of Lua and CELX code, whose value has type function.

A function can have arguments for input values and a function can also return zero, one or more values (see the Lua-documentation for details).

One or more functions can be defined at the beginning of your CELX script and they can be called once or several times from the main body of your CELX script.

Example:

-- Define function with name "add_one" and parameterlist "i" (a number).
function add_one(i)
-- use 'local' to declare variables local to function:
local j = i + 1
-- return the value of "j", a number.
return j
end
-- Define function with name "divide" and parameterlist "i, j" (two numbers).
function divide(i, j)
-- return the value of the division "i / j", a number.
return i / j
end

-- Start of the main body of your script
<... other script code ...>
-- and now call the functions:
a = add_one(1)
b = divide(a, 2)
<... other script code ...>

The function "wait(number:n)" is predefined in Celestia and waits number: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.

Notes:

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). See the methode celestia:requestsystemaccess() for more information.

Lua has the typical control-structures, like for, while, repeat and if.

Examples:

-- Execute the block of codelines (...) 10 times after each other,
-- where "i" is incremented with 1 during each loop.
for i = 1, 10 do
...
end

-- Execute the block of codelines (...)
-- as long as "i" is smaller than or equal to 11.
i = 1
while i <= 11 do
...
i = i + 1
end

-- Execute the block of codelines (...)
-- until "i" is equal to "j".
i = 1
j = 22
repeat
...
i = i + 1
until i == j

-- Compare "i" and "j" with each other and execute a
-- certain block of codelines (...1, ...2 or ...3),
-- depending on the result of this comparizon.
if i > j then
...1
elsif i < j then
...2
else
...3
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

This section documents the userdata classes (object types) available in CELX Lua scripting.

Besides the predefined celestia object, there are also other celestia related objects to control Celestia. You can't create an object (i.e. 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.

The following celestia related objects can be used within a CELX script:

Observer object:
An observer object is used to access properties specific to a view, such as viewer position, viewer orientation, frame of reference, and tracking status. To operate on an observer objects, you can call observer methods which are defined on them, as shown in section observer methods.

Object objects:
An "object" object in CELX refers to a celestial object like a planet or a star. To operate on "object" objects, you can call "object" methods which are defined on them, as shown in section object methods.

Position objects:
A position object contains the exact coordinates of a point in space. To operate on position objects, you can call position methods which are defined on them, as shown in section position methods.

Vector objects:
A vector object is a geometric object that has both a length and direction [X,Y,Z] in a 3-dimensional Coordinate System. To operate on vector objects, you can call vector methods which are defined on them, as shown in section vector methods.

Rotation objects:
A rotation object is internally a Quaternion, which is one possibility to mathematically describe a rotation in 3 dimensions (i.e. it can be converted to a rotation matrix). A rotation can also be used to describe the orientation of objects or the observer (i.e. where the observer is looking to, and where "up" is). To operate on rotation objects, you can call rotation methods which are defined on them, as shown in section rotation methods.

Frame objects:
The frame object describes the celestia coordinate cystem and tells how the X, Y, and Z, axes of each 3-dimensional coordinate system are aligned. Coordinate systems are well documented in the available Celestia .Cel Scripting Guide v1-0g by Don Goyette, in the chapter: "Coordinate Systems". To operate on frame objects, you can call frame methods which are defined on them, as shown in section frame methods.

Phase objects:
The timeline of an object can be subdivided into one or more phases. Each phase object has its own trajectory, rotation model, orbit frame, and body frame. To operate on phase objects, you can call phase methods which are defined on them, as shown in section phase methods.

CELscript objects:
A CELscript object contains a string with a valid CEL script, which can be imbedded in a CELX script by using the "celestia:createcelscript()" method. To operate on CELscript objects, you can call CELscript methods which are defined on them, as shown in section CELscript methods.

Example:
To call the method getposition() of observer, you have to get an observer-instance from celestia first and then call getposition() on it:

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

Notes:

While 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 is not really special.

The following section contains an index of the available CEL commands. By clicking on a specific command, you will be routed to the explanation on its functionality and how that CEL command can be migrated to equivalent CELX objects and methods.

The CEL commands below are organized following the sequence and explanation of CEL script commands in Celestia .Cel Scripting Guide v1-0g by Don Goyette. The explanations also contain many of Don’s examples and syntax descriptions to aid in the migration from CEL to CELX.

Within this section, parameter values for CEL commands and CELX methods are printed like <type> or <name>. These parameter values need to be replaced when actually using these commands or methods.

Example:

<string> "Sol/Earth"
<duration> 1.5
<distance> 20000

Since Celestia version 1.6.0 and 1.6.1 the original listing of only 35 Cel commands has been expanded to 52 Cel commands. The additional commands are indicated with 1.6.0 and 1.6.1.

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] key). 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 observer: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 version 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 observer:setorientation() or observer:lookat(). You probably have your orientation set to an "invalid" quaternion (for example all zero), possibly by using observer: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).