Add your own plugin for both Lua Plug-ins and Lua Edu Tools

All tutorials about Celestia go in here. For Celestia itself, add-ons, textures, scripting, etc.
Avatar
Topic author
jogad
Posts: 458
Joined: 17.09.2008
With us: 16 years 2 months
Location: Paris France

Add your own plugin for both Lua Plug-ins and Lua Edu Tools

Post #1by jogad » 11.11.2012, 17:15

When I began this tutorial it was only for Lua Plug-ins.
But scripting for Lua Plug-ins and Lua Edu Tools is very similar.
So I complete the tutorial to point out the differences and try to make your script compatible with both Lua Plug-ins and Lua Edu Tools.

From chapter 9, you have always the entire working code of the previous step. It may help if you have missed something or if you don't want to start from the beginning.
And if you are only interested in a tool to measure angles without worrying about how it is done, you can just use it. It works very well!

Table of contents
I) intro to Lua Plug-ins

In this thread , there were a request for a simple tool: How to get the angular separation for to celestial bodies.

I propose to show how to use a lua script in conjunction with the Lua Plug-ins to make this tool.
The Lua Plug-ins are indeed very well suited for this purpose.

I'm obviously not the most qualified to do this tutorial, and I apologize if my explanations are not clear.
But I think it's better than nothing at all.
There is indeed enough documentation about celx scripts or lua progrmming but the documentation about how to use the Lua Plug-ins is indeed quite inexistent. Thus this small introduction.

This tuto is intended for those who have at least a basic knowledge of scripts or programming but have never tried to use the improved capabilities offered by the Lua Plug-ins.
Even if you have never made a script, if you follow the steps, you will end with a working script :D
Programming language is the same than for the celx scripts, i.e lua.

Rather of another "Hello world" program, let's try to do something useful.
It is rather ambitious of me but at the end we will have a functional tool.

Here is what I want:
(click to enlarge)
sepangle.jpg

- When I select an object it is automatically marked.
- When I have selected two objects, the angular separation is displayed.
- If I move, the angular separation is automatically updated

In the example above I have selected Jupiter then the Sun.
If I select another object, I can resume as I want.

The tool is activated or inactivated by typing the [shift+A] key

If not yet done, install the Lua Plug-ins program.
You can find it here
viewtopic.php?f=23&t=15705

I reserve some posts for the continuation of this tutorial

:mrgreen:
Last edited by jogad on 08.01.2013, 18:12, edited 6 times in total.

Avatar
Topic author
jogad
Posts: 458
Joined: 17.09.2008
With us: 16 years 2 months
Location: Paris France

Re: How to make a little tool for Lua Plug-ins

Post #2by jogad » 11.11.2012, 17:15

II) A minimal program for Lua Plug-ins

A lua plug-ins tool works with boxes.
The boxes are frames where you can
    - display things (texts, images...)
    - execute automatically some code
    - react to mouse actions
    etc...
I decide to call my tool "sepAnglesBox" (for separation angles box)
The program file name must be "sepAnglesBox.lua" (you can put it everywhere in the "celestia/extras" directory tree structure.
The best is probably to make a directory "extras/separation angles" and put your "sepAnglesBox.lua" at this place.

The Lua Plug-ins require that a box with the same name than the program exists (even if it does nothing!)
We create this box with the instruction CXBox:new()
Note the special syntax of this instruction, followed with some functions to initialize the box.
The box definition begins by:
:init(0,0,0,0)
We'll see the other functions later.

For the purpose of our tool we need another box. (You can name it as you want)
It is in this box that we'll display the calculated separation angle.
for instance, you can only see its existence by the presence of a white border around it.
This is the reason of the :bordercolor() function in its definition.

The initial values:
They determine the width and the position of this box.
The instruction that actually place the box is the :attach(...) procedure.

And lastly we have a function "toggleAnglesDisplay" that hides or displays "sepAnglesFrame" (angle display)
This function is logically bound to the [shif + A] key.

Here is the result.

Code: Select all

----------------------------------------------
-- Set initial values
----------------------------------------------

-- sx and sy are provided by Lua Plug-ins as the width ant the height of the screen
local lspace = normalfont:getheight() + 1
local boxW, boxH = 120,lspace * 2  -- display width = 120 pixels, display height = 2 lines
local posX, posT = 0, 170  -- near the left margin, 170 pixels from the top of the screen.

----------------------------------------------
-- Set up and Draw the boxes
----------------------------------------------
sepAnglesBox = CXBox:new()
    :init(0, 0, 0, 0)
    :movable(false)
    :attach(screenBox, sx, sy, 0, 0);

sepAnglesFrame = CXBox:new()
    :init(0, 0, 0, 0)
   :bordercolor({1,1,1,0.7})
    :movable(false)
    :clickable(true)
    :visible(false)
    :attach(screenBox, posX, sy-posT-boxH, sx-boxW-posX, posT)

local toggleAnglesDisplay = function()
   sepAnglesFrame.Visible = not sepAnglesFrame.Visible
end

keymap["A"] = toggleAnglesDisplay

This is a very minimal but functional program.

You must do one last thing to make it active in celestia.

Go to the lua_plugins directory and open the file "config.lua" in a plain text edidor.
find the "plugins" section and add a line like this at the end of the section

Code: Select all

plugins =
    {
        "compassBox",
        "coordinateBox",
        "KeplerParamBox",
        "pictureBox",
        "HRBox",
      "sepAnglesBox",   --  <-- ligne to add
    }

That's all! (for the time being)

- Launch Celestia and press [shift+A].
sepangle2.jpg

The box is at the expected place.
- Press [shift+A] again and it disappears... and so on.

Well! In the next part we'll learn more about the box and and fix another issue...
Try to resize the celestia window and observe the result.
Horror! it is a bug! :evil:

:mrgreen:
Last edited by jogad on 27.12.2012, 15:46, edited 4 times in total.

Avatar
Topic author
jogad
Posts: 458
Joined: 17.09.2008
With us: 16 years 2 months
Location: Paris France

Re: How to make a little tool for Lua Plug-ins

Post #3by jogad » 11.11.2012, 17:16

III) Fix the position of the boxes

With the lua plug-ins, when a box is visible, if you define a function which name is "Customdraw" associated to this box, the code inside this function is automatically executed each time Celestia refreshes the screen.
The code is not executed when the box is not visible.

We begin to use this interesting property to fix the bug of the previous post.

To associate a "Customdraw" function to our "sepAnglesFrame" the syntax is

Code: Select all

sepAnglesFrame.Customdraw = function(this)
   ...
end

"this" stands for "sepAnglesFrame"
As the parameters of the :attach() function are bound to the size of the screen, we have just to attach again the box with its new values.

Here is the resulting "sepAnglesBox.lua"

Code: Select all

----------------------------------------------
-- Set initial values
----------------------------------------------

-- sx and sy are provided by Lua Plug-ins as the width ant the height of the screen
local lspace = normalfont:getheight() + 1
local boxW, boxH = 120,lspace * 2  -- display width = 120 pixels, display height = 2 lines
local posX, posT = 0, 170  -- near the left margin, 200 pixels from the top of the screen.

----------------------------------------------
-- Set up and Draw the boxes
----------------------------------------------
sepAnglesBox = CXBox:new()
    :init(0, 0, 0, 0)
    :movable(false)
    :attach(screenBox, sx, sy, 0, 0);

sepAnglesFrame = CXBox:new()
    :init(0, 0, 0, 0)
   :bordercolor({1,1,1,0.7})
    :movable(false)
    :clickable(true)
    :visible(false)
    :attach(screenBox, posX, sy-posT-boxH, sx-boxW-posX, posT)
   
sepAnglesFrame.Customdraw = function(this)

   sepAnglesFrame:attach(screenBox, posX, sy-posT-boxH, sx-boxW-posX, posT)
end;


local toggleAnglesDisplay = function()
   sepAnglesFrame.Visible = not sepAnglesFrame.Visible
end

keymap["A"] = toggleAnglesDisplay

But what are exactly the parameters of the box:attach(parent,left,bottom,right,top) function?
There are 5 parameters:
A box is attached to another box. Here "screenBox" is the parent box of "sepAnglesFrame"
"screenBox" is a hidden box that represents the whole screen.

left,bottom,right,top are the sizes of the borders from the edges of the screen to the box in these directions.

Because margins' sizes are continuously recalculated, we can now resize the Celestia windows as we want without errors. :)

:arrow: Save your file, restart Celestia and try again to see the result.
Last edited by jogad on 11.11.2012, 17:55, edited 1 time in total.

Avatar
Topic author
jogad
Posts: 458
Joined: 17.09.2008
With us: 16 years 2 months
Location: Paris France

Re: How to make a little tool for Lua Plug-ins

Post #4by jogad » 11.11.2012, 17:16

IV) Writing text in the boxes

The celestia:print() method is available but is not well suited to our purpose.

The Lua Plug-ins offer two specific way to do that.

1) the :text() method

The easiest is to use the "Text" property of the box.
Here is how to use it. You must define
    the used font. (the available ones are "titlefont", "normalfont" and "smallfont")
    the color of the font in the format {red, green, blue, opacity). Each parameter is a number in the range 0 to 1
    and obviously the text itself.
Modify the sepAnglesFrame box like this

Code: Select all

sepAnglesFrame = CXBox:new()
   :init(0, 0, 0, 0)
   :bordercolor({1,1,1,0.7})
   :textfont(normalfont)
   :textcolor({0.7, 0.7, 1.0, 0.9})
   :text("Angular separation")
   :clickable(true)
   :visible(false)
   :attach(screenBox, posX, sy-posT-boxH, sx-boxW-posX, posT)

:arrow: Try it!
sepangle4.jpg

It is not so bad but I'd like to have the text a little more left and up.
Don't think the text is centered vertically. :( It is just by chance!
To fix that, add a line just after the :text("Angular separation") command.

Code: Select all

   :textcolor({0.7, 0.7, 1.0, 0.9})
   :text("Angular separation")
   :movetext(-5,4)  -- line to add

the parameters x and y of :movetext(x,y) represent the horizontal position (from left to the right) and the vertical position (from the bottom to the top). The weird numbers are because the origin is not the top (nor bottom) left corner of the box. You have often some trial and error to do before you find the right place.
Another possibility is to use the instruction :setpos("center") instead of :movetext(x,y). The text is then automatically centered vertically and horizontally in the box. But it is not what we want here.

2) the textlayout method.

The previous method is simple and efficient but allows to display only one line in the box.
To be able to display several lines we must use the "textlayout" object and its associate methods.

You must use textlayout in a Customdraw function.
Let's use it to display the second line of our box. (At this point we havn't done any calculations so we'll be happy to display 0° 00' 00'')

Transform the sepAnglesFrame.Customdraw function like that:

Code: Select all

sepAnglesFrame.Customdraw = function(this)

   textlayout:setfont(normalfont)
   textlayout:setfontcolor({0.7, 0.7, 1, 0.9})
   textlayout:setlinespacing(lspace)
   textlayout:setpos(this.lb,this.tb-lspace*2+2) -- position of the second line
   textlayout:println("0° 00' 00''")

   sepAnglesFrame:attach(screenBox, posX, sy-posT-boxH, sx-boxW-posX, posT)
end;

:arrow: Try it again!

You can see the corresponding methods with respect to the previous ones:

textfont --> setfont
textcolor --> setfontcolor
movetext --> setpos


As we can display several lines,
- :setlinespacing defines the interline.
- :println places the text at the defined place and jump to the next line so you can write several lines without redoing all the initializations.

Since we have now the two lines at the right place, we can remove the :bordercolor instruction in the sepAnglesFrame box definition.
snap4c.jpg

:mrgreen:
Last edited by jogad on 11.11.2012, 18:46, edited 2 times in total.

Avatar
Topic author
jogad
Posts: 458
Joined: 17.09.2008
With us: 16 years 2 months
Location: Paris France

Re: How to make a little tool for Lua Plug-ins

Post #5by jogad » 11.11.2012, 17:16

 
V) Some clarification and a few tips

Before we go on, let's take a pause and see some considerations about the boxes functions and properties and other stuff.

When we set a box, we use functions ex:
    textcolor({0.7, 0.7, 1.0, 0.9})
    text("Angular separation")
    visible(false)
The first letter of such a function is a lower case letters.
Besides these functions, there are most often properties with the same name but beginning with a upper case letter:
    Textcolor
    Text
    Visible
The initialization of a box requires that you use the functions.
But otherwise, you can use freely one or the other form.
ex:
    box:visible(true) is equivalent to box.Visible = true
    box:text("Hello") is equivalent to box.Text = "Hello"
Note the ":" before a function and "." before a property.

Boxes are global variables. This is because Lua Plug-ins must know about them in order to process them.
The consequence of that is that you must name them carefully with a name enough specific so then don't conflict with other global variables in other plugins.
For the same reason you should avoid to use global variables if it is not absolutely necessary.
This is reason of the directive local before the name of the variables at the beginning of the program.
The exception is for the variable sx and sy. But it is a BAD idea to use these global variables. If Vincent decides to change their names in a next version, your program doesn't work anymore. And if you make a global variable like sx, sy (you cannot know the name of all global variables) your code seem correct but nothing in all the plugins works well.
The correct way is to declare sx and sy as local and create yourself the necessary code to give them the right values.

Just declare sx and sx as local

Code: Select all

local sx, sy = celestia:getscreendimension()
At the beginning of the program,

and the line

Code: Select all

sx, sy = celestia:getscreendimension()
before adjusting the place of the box.
at the end of the sepAnglesFrames.Customdraw function

At this point here is our program.

Code: Select all

----------------------------------------------
-- Set initial values
----------------------------------------------

local sx, sy = celestia:getscreendimension()
local lspace = normalfont:getheight() + 1
local boxW, boxH = 120,lspace * 2  -- display width = 120 pixels, display height = 2 lines
local posX, posT = 0, 170  -- near the left margin, 200 pixels from the top of the screen.

----------------------------------------------
-- Set up and Draw the boxes
----------------------------------------------
sepAnglesBox = CXBox:new()
    :init(0, 0, 0, 0)
    :movable(false)
    :attach(screenBox, sx, sy, 0, 0);

sepAnglesFrame = CXBox:new()
    :init(0, 0, 0, 0)
   --:bordercolor({1,1,1,0.7})
    :textfont(normalfont)
    :textcolor({0.7, 0.7, 1.0, 0.9})
   :text("Angular separation")
    :movetext(-5,4)
    :movable(false)
    :clickable(true)
    :visible(false)
    :attach(screenBox, posX, sy-posT-boxH, sx-boxW-posX, posT)
   
sepAnglesFrame.Customdraw = function(this)

   textlayout:setfont(normalfont)
   textlayout:setfontcolor({0.7, 0.7, 1, 0.9})
   textlayout:setlinespacing(lspace);
   textlayout:setpos(this.lb,this.tb-lspace*2+2) -- second line of the box
   textlayout:println("0° 00' 00''")

   sx, sy = celestia:getscreendimension()
   sepAnglesFrame:attach(screenBox, posX, sy-posT-boxH, sx-boxW-posX, posT)

end;

local toggleAnglesDisplay = function()
   sepAnglesFrame.Visible = not sepAnglesFrame.Visible
end

keymap["A"] = toggleAnglesDisplay

This is all for the specific programming with Lua Plug-ins.

The remaining job is standard lua scripts programming.
So I'll explain more briefly.
:mrgreen:
Last edited by jogad on 25.12.2012, 14:07, edited 4 times in total.

Avatar
Topic author
jogad
Posts: 458
Joined: 17.09.2008
With us: 16 years 2 months
Location: Paris France

FINAL PROGRAM

Post #6by jogad » 11.11.2012, 17:17

 
VI) the final program

Now I need 2 bodies to calculte the angle between them.
I create 2 new variables in the "set intial values" section:
The first selected body will be sel1 and the second sel2
If a body is not selected the value of its variable will be nil.

I add also three lines to ensure that it is possible to uses markers.

Code: Select all

local sel1, sel2 = nil, nil -- no object selected

local renderflags = celestia:getrenderflags()
renderflags["markers"] = true
celestia:setrenderflags(renderflags)

We have now to test if a new selection is made and in this case update sel1 and sel2 and mark (or unmark) them.

I make a function for that whose name is getnewselection()
Because we must continuously test if a new selection is made, we call this function from sepAnglesFrame.Customdraw.

I need also a function that calculates the angle and returns it as a string to be displayed by the textlayout.println() function. The name of this function is angle().

The sepAnglesFrame.Customdraw becomes:

Code: Select all

sepAnglesFrame.Customdraw = function(this)
   getnewselection()  ----- line to add
   
   textlayout:setfont(normalfont)
   textlayout:setfontcolor({0.7, 0.7, 1, 0.9})
   textlayout:setlinespacing(lspace);
   textlayout:setpos(this.lb,this.tb-lspace*2+2) -- second line of the box
   textlayout:println(angle())  ------ line to edit

   sx, sy = celestia:getscreendimension()
   sepAnglesFrame:attach(screenBox, posX, sy-posT-boxH, sx-boxW-posX, posT)
end

I place the functions getnewselection() and angle() just after the setting of the initial values, and before the definitions of the boxes.
Here they are:

Code: Select all

---------------------
-- local functions --
---------------------

local getnewselection = function()
   local obj = celestia:getselection()
   -- test if there is a new selection
   local sel = true 
   if obj:type() == "null" then
      sel = false
   elseif sel2 and sel2:name() == obj:name() then
      sel = false
   elseif sel1 and sel1:name() == obj:name() then
      sel = false
   end
   
    -- record and mark selection
   if sel then
      if sel2 then
         sel1:unmark(); sel2:unmark()
         sel1=obj; sel2 = nil
         sel1:mark()
      elseif sel1 then
         sel2=obj; sel2:mark()
      else
         sel1=obj; sel1:mark()
      end
   end
end

local angle = function()
   if sel1 and sel2 then
      local obs = celestia:getobserver()
      local a= sel1:getposition() - obs:getposition()
      local b= sel2:getposition() - obs:getposition()
      local sepangle = math.acos(a:normalize() * b:normalize())*180/math.pi
      return string.format(" %0.2f°",sepangle)
   else
      return "- - - - - -"
   end
end

Note that these fonctions are defined as local variables.
It is also important that they are defined before they are used.

At last we must add 3 lines to the toggleAnglesDisplay() function
This is the final version of this function:

Code: Select all

local toggleAnglesDisplay = function()
   sepAnglesFrame.Visible = not sepAnglesFrame.Visible
   celestia:unmarkall()
   sel1=nil
   sel2=nil
end


That's all!!!
The program is done and works as expected!
:) :D :D
:mrgreen:
Last edited by jogad on 12.11.2012, 16:43, edited 1 time in total.

Avatar
Topic author
jogad
Posts: 458
Joined: 17.09.2008
With us: 16 years 2 months
Location: Paris France

Re: How to make a little tool for Lua Plug-ins

Post #7by jogad » 11.11.2012, 17:17

 
VII) Personalization

This little program works very well. (And for me that is enough.)
The installation is very simple (one line to add in the file config.lua in the lua_plug_ins directory)
But you may want more..
- You display or hide it with [shift+A]. Maybe you want another key :?:
You can always tell the user to change the last line of your program but it is not a safe method!
- When you launch Celestia the tool is hidden and you have to press the hot key to show it. Maybe you want let the user choice if it is visible or not at launch :?:

Let's suppose the user wants [shift+Z] as hotkey and the tool visible when Celestia starts.
The procedure is
:arrow: open the config.lua file

:arrow: to install the plugin, find the section "plugins = " and add a line
     "sepAnglesBox",

:arrow: to say to show at startup find the "enable_plugin = " section and add a line
    sepAnglesBox = true,

:arrow: to change the hot key find the "key_shortcut = " section and add a line
    sepAnglesBox = "Z", -- or whatever letter you want

Of course this doesn't work :lol: ! You must add adapt your code to enable theses possibilities.

In the "Set initial values" add this line

Code: Select all

local display_angle = enable_plugin["sepAnglesBox"] or false

and in the definition of the "sepAnglesFrame" box, replace the line

Code: Select all

    :visible(false)
with

Code: Select all

    :visible(display_angle)


At the end of the program replace the line:

Code: Select all

keymap["A"] = toggleAnglesDisplay
By these two lines:

Code: Select all

local shortcut = key_shortcut["sepAnglesBox"] or "A"
keymap[shortcut] = toggleAnglesDisplay

This way , if the user doesn't modify these entries the program works with the default values (no display at startup, and "A" as shortkey) .

:mrgreen:
Last edited by jogad on 12.11.2012, 17:03, edited 1 time in total.

Avatar
Topic author
jogad
Posts: 458
Joined: 17.09.2008
With us: 16 years 2 months
Location: Paris France

Re: How to make a little tool for Lua Plug-ins

Post #8by jogad » 11.11.2012, 17:17

 
VIII) summary of some useful boxes functions and properties

CXBox:new()
    create a new box
:init(0,0,0,0,)
    (never seen other values) sets up a new box
:bordercolor(color)
    sets the color border of the box.
    "color" is a table of (red, green, blue, opacity) withe value from 0 to 1
:fillcolor(color)
    sets the background color of the box. "color" as above
:fillimage(texture)
    fills the box with an image. You get the texture with the instruction celestia:loadtexture(name_of_the_texture)
:textfont(font)
    text font. Standard values for font are "titlefont", "normalfont", "smallfont"
:textcolor(color)
    Try to guess!
:text(string)
    Text bound to the box. Multiline texts are not allowed
:moveto(x,y)
    places the Text property at these coordinates. As the point 0,0 is somewhere other than expected, you may have to try to get the exact position.
:textpos("center")
    (I don't know about other possible values for this parameter) Places the text centered vertically and horizontally in the box.
:clickable(boolean)
    "boolean" is "true" or "false"
    If true this means you can click through the box and your click has no effect on the box.
:movable(false)
    The box can be moved with the mouse. To be able to use the mouse with this box, you must also declare clickable(false)
:visible(false)
    Makes the box visible or not. If a "Customdraw" function is defined, it is executed if the box is visible, otherwhise the function is not executed.
:active(boolean)
    default is :active(false). If set to "true" and an "Action" function if defined it is executed when you clik on the box.
    If you want an active button, you must also declare clickable(false)
:attach(parent, leftmargin, bottommargin, rightmargin, topmargin)
    fixes the position of the box, with respect to the position of its parent box.
    The name of the box that represents the entire screen is "screenBox"
    You can get the actual values of the margin with the property
    .la, .ba, .ra and .ta of the box.
    .lb, .bb, .rb and .tb give the actual coordinates of these margins.
    you can read these values, but use the "attach" function to set a new position.
:orderfront()
    useful function to place a box above the others. For example if an active box is below another box, you can't click on it anymore.

Et voil? ! c'est fini ! :mrgreen:

Avatar
PlutonianEmpire M
Posts: 1374
Joined: 09.09.2004
Age: 40
With us: 20 years 2 months
Location: MinneSNOWta
Contact:

Re: How to make a little tool for Lua Plug-ins

Post #9by PlutonianEmpire » 25.12.2012, 01:19

I have a few issues here:

1: Here, you give individual code, "getdimension", and when I loaded it up in Celestia, the tool didn't load. But then you gave "getscreendimension" in the big code box, and it worked. Mix-up, perhaps?

2. In Celestia, a ? shows up instead of ° . Quite irritating.

3. If i wanted it to show degrees minutes and seconds as well instead of decimal degrees, how might I go about doing that?
Terraformed Pluto: Now with New Horizons maps! :D

Avatar
Topic author
jogad
Posts: 458
Joined: 17.09.2008
With us: 16 years 2 months
Location: Paris France

Re: How to make a little tool for Lua Plug-ins

Post #10by jogad » 25.12.2012, 14:12

PlutonianEmpire wrote:1: Here, you give individual code, "getdimension", and when I loaded it up in Celestia, the tool didn't load. But then you gave "getscreendimension" in the big code box, and it worked. Mix-up, perhaps?
I have updated the tutorial.
The right command is celestia:getscreendimension()

PlutonianEmpire wrote:2. In Celestia, a ? shows up instead of ° . Quite irritating.
Just pay attention to save your script encoded as UTF-8 (without BOM)

PlutonianEmpire wrote:3. If i wanted it to show degrees minutes and seconds as well instead of decimal degrees, how might I go about doing that?
You have to write some additional code.
This example is focused on the specific code for Lua Plug-ins.
For general programing with LUA, see the lua documentations
Here is the on-ligne Lua 5.1 reference manual
http://www.lua.org/manual/5.1/manual.html#5.6

About your specific question here is an example of a function that converts a positive angle in decimal degrees to degrees, minutes and seconds.

Code: Select all

local degreesToDMS = function(angle) -- positive angle in degrees
   local degr, mn, sec
   degr, mn = math.modf(angle)
   mn, sec =  math.modf(mn * 60)
   sec = sec * 60
   return string.format("%d° %02d' %05.2f''", degr, mn, sec)
end

Avatar
Marco Klunder
Posts: 181
Joined: 20.02.2008
Age: 62
With us: 16 years 9 months
Location: The Netherlands

Re: How to make a little tool for Lua Plug-ins

Post #11by Marco Klunder » 26.12.2012, 21:08

Jogad,

Thanks for this explenation.
I learned a lot from it. :D

Now I was experimenting with the :movable(true) and :clickable(false).
But in the example sepAnglesFrame.Customdraw = function(this)
the box is repositioned again to its original place each time. :(

In this case, how can I read-out the left, bottom, right, top, when they are meanwhile adjusted by the mouse :?:

Marco
Marco Klunder
email: marco.klunder@xs4all.nl
Windows10 PD 3.0 GHz, 2 GB of RAM, Nvidia GeForce 6700 XL
Celestia161 / SVN + Lua Edu Tools v1.2 Beta9, Celestia160-ED and Celestia1621

Avatar
Topic author
jogad
Posts: 458
Joined: 17.09.2008
With us: 16 years 2 months
Location: Paris France

Re: How to make a little tool for Lua Plug-ins

Post #12by jogad » 27.12.2012, 16:04

Marco,

The first thing to move a box is to set :movable(true) and :clickable(false) as you already did.

The position of the box is set by the instruction :attach
As the Customdraw function is performed quasi continuously, if you execute the :attach function each time in the Customdraw function with always the same parameters, the position is fixed at the same place forever.
A workaround is to execute the ":attach" instruction ONLY when the screen changes dimension.
This is a simple way to do that. You need two more variables.

Your initializations become:

Code: Select all

----------------------------------------------
-- Set initial values
----------------------------------------------

local sx, sy = celestia:getscreendimension()
local oldsx, oldsy = 0, 0
local lspace = normalfont:getheight() + 1
local boxW, boxH = 120,lspace * 2  -- display width = 120 pixels, display height = 2 lines
local posX, posT = 2, 170  -- near the left margin, 170 pixels from the top of the screen.

You have added the variables oldsx and oldsy and they are initialized to zero.
Because of a bug in Lua Plug-ins (and also Lua Edu tool) there is a problem to move a box when its initial distance from the screen edge is less than 2 or 3
So don't forget to modify the initial posX value from 0 to 2

Now in the Customdraw function,
replace

Code: Select all

   sx, sy = celestia:getscreendimension()
   sepAnglesFrame:attach(screenBox, posX, sy-posT-boxH, sx-boxW-posX, posT)

with

Code: Select all

   oldsx, oldsy = sx, sy
   sx, sy = celestia:getscreendimension()
   if sx ~= oldsx or sy ~= oldsy then
      sepAnglesFrame:attach(screenBox, posX, sy-posT-boxH, sx-boxW-posX, posT)
   end

This is a minimal management that is enough for a simple tool like this.

Marco Klunder wrote: how can I read-out the left, bottom, right, top, when they are meanwhile adjusted by the mouse :?:
You have to use the .la, .ba, .ra or .ta properties of the box which are described in the post about boxes functions and properties.

For example, suppose that you want to leave the box at the same position relative to the left and top margin while you are resizing the Celestia window.
Instead of repositioning the box at the default posX and posT positions, simply actualize continuously these variables in the sepAnglesFrame.Customdraw() function.
As the bottom and right margins are changing, you have also to test if the box always fits in the Celestia windows.

In this case the whole sepAnglesFrame.Customdraw() function. becomes

Code: Select all

sepAnglesFrame.Customdraw = function(this)
   getnewselection()
   
   textlayout:setfont(normalfont)
   textlayout:setfontcolor(textcolor)
   textlayout:setlinespacing(lspace)
   textlayout:setpos(this.lb,this.tb-lspace*2+2) -- second line of the box
   textlayout:println(angle())

   oldsx, oldsy = sx, sy
   sx, sy = celestia:getscreendimension()
   if sx ~= oldsx or sy ~= oldsy then
      -- 2 lines to force the box to stay in the celestia window
      if posT >= sy - boxH - 2 then posT = sy - boxH - 2 end
      if posX >= sx - boxW - 2 then posX = sx - boxW - 2 end
      ----
      sepAnglesFrame:attach(screenBox, posX, sy-posT-boxH, sx-boxW-posX, posT)
   end
   -- recording the actual position
   posX = this.la
   posT = this.ta
end

My method to go on when I have a problem is to take example of one of the allready existing tools for the Lua Plugin.
This tuto is just a help to start.
:mrgreen:
Last edited by jogad on 22.03.2013, 20:14, edited 1 time in total.

Avatar
Topic author
jogad
Posts: 458
Joined: 17.09.2008
With us: 16 years 2 months
Location: Paris France

Re: How to make a little tool for Lua Plug-ins

Post #13by jogad » 01.01.2013, 10:54

Hello,

I plan to continue this tutorial very soon with details on the differences between Lua Plug-ins and Lua Edu Tools.
The goal is to make a plugin compatible with both environments and that can install and uninstall (almost) as easily as any addon.

I am of course at your disposal for questions / suggestions / criticisms, and I will answer to the best of my ability.
But please do not PM for it.

I noticed that the last chapters contain several changes that might be not so easy to follow.
For an overview, here is the full functional program from Chapter VII) of this tutorial.

Code: Select all

----------------------------------------------
-- Set initial values
----------------------------------------------

local sx, sy = celestia:getscreendimension()
local lspace = normalfont:getheight() + 1
local boxW, boxH = 120,lspace * 2  -- display width = 120 pixels, display height = 2 lines
local posX, posT = 0, 170  -- near the left margin, 170 pixels from the top of the screen.

local sel1, sel2 = nil, nil -- no object selected

local renderflags = celestia:getrenderflags()
renderflags["markers"] = true
celestia:setrenderflags(renderflags)

local display_angle = enable_plugin["sepAnglesBox"] or false

----------------------------------------------
-- local functions
----------------------------------------------

local getnewselection = function()
   local obj = celestia:getselection()
   -- test if there is a new selection
   local sel = true 
   if obj:type() == "null" then
      sel = false
   elseif sel2 and sel2:name() == obj:name() then
      sel = false
   elseif sel1 and sel1:name() == obj:name() then
      sel = false
   end
   
    -- record and mark selection
   if sel then
      if sel2 then
         sel1:unmark(); sel2:unmark()
         sel1=obj; sel2 = nil
         sel1:mark()
      elseif sel1 then
         sel2=obj; sel2:mark()
      else
         sel1=obj; sel1:mark()
      end
   end
end

local angle = function()
   if sel1 and sel2 then
      local obs = celestia:getobserver()
      local a= sel1:getposition() - obs:getposition()
      local b= sel2:getposition() - obs:getposition()
      local sepangle = math.acos(a:normalize() * b:normalize())*180/math.pi
      return string.format(" %0.2f°",sepangle)
   else
      return "- - - - - -"
   end
end

----------------------------------------------
-- Set up and Draw the boxes
----------------------------------------------
sepAnglesBox = CXBox:new()
    :init(0, 0, 0, 0)
    :movable(false)
    :attach(screenBox, sx, sy, 0, 0);

sepAnglesFrame = CXBox:new()
    :init(0, 0, 0, 0)
    :textfont(normalfont)
    :textcolor({0.7, 0.7, 1.0, 0.9})
   :text("Angular separation")
    :movetext(-5,4)
    :movable(false)
    :clickable(true)
    :visible(display_angle)
    :attach(screenBox, posX, sy-posT-boxH, sx-boxW-posX, posT)
   
sepAnglesFrame.Customdraw = function(this)
   getnewselection()
   
   textlayout:setfont(normalfont)
   textlayout:setfontcolor({0.7, 0.7, 1, 0.9})
   textlayout:setlinespacing(lspace);
   textlayout:setpos(this.lb,this.tb-lspace*2+2) -- second line of the box
   textlayout:println(angle())

   sx, sy = celestia:getscreendimension()
   sepAnglesFrame:attach(screenBox, posX, sy-posT-boxH, sx-boxW-posX, posT)
end;

local toggleAnglesDisplay = function()
   sepAnglesFrame.Visible = not sepAnglesFrame.Visible
   celestia:unmarkall()
   sel1=nil
   sel2=nil
end

-- show or hide the tool
local shortcut = key_shortcut["sepAnglesBox"] or "A"
keymap[shortcut] = toggleAnglesDisplay

If you are not interested in how it is built, you can always copy it and use it as is. :wink:
It is a small useful program that works very well.
For now it is only compatible with Lua Plug-ins (not with Lua Edu Tools)

I wish you a happy new year :D

:mrgreen:

Avatar
Topic author
jogad
Posts: 458
Joined: 17.09.2008
With us: 16 years 2 months
Location: Paris France

Re: How to make a little tool for Lua Plug-ins

Post #14by jogad » 02.01.2013, 06:30

 
IX) The Lua Edu Tools version

Making a tool for Lua Edu Tools from scratch is not the purpose here.
You can have a look at this tutorial by Vincent for that:
http://www.shatters.net/forum/viewtopic.php?f=21&t=12133&sid=edb02749544bb74f46e4b1af9188c3b5#p101161


Let's start with the version completed for Lua Plug-ins, before the personalization stage.
Here is the full code of the Lua Plug-ins version

Code: Select all

----------------------------------------------
-- Set initial values
----------------------------------------------

local sx, sy = celestia:getscreendimension()
local lspace = normalfont:getheight() + 1
local boxW, boxH = 120,lspace * 2  -- display width = 120 pixels, display height = 2 lines
local posX, posT = 0, 170  -- near the left margin, 170 pixels from the top of the screen.

local sel1, sel2 = nil, nil -- no object selected

local renderflags = celestia:getrenderflags()
renderflags["markers"] = true
celestia:setrenderflags(renderflags)

----------------------------------------------
-- local functions
----------------------------------------------

local getnewselection = function()
   local obj = celestia:getselection()
   -- test if there is a new selection
   local sel = true 
   if obj:type() == "null" then
      sel = false
   elseif sel2 and sel2:name() == obj:name() then
      sel = false
   elseif sel1 and sel1:name() == obj:name() then
      sel = false
   end
   
    -- record and mark selection
   if sel then
      if sel2 then
         sel1:unmark(); sel2:unmark()
         sel1=obj; sel2 = nil
         sel1:mark()
      elseif sel1 then
         sel2=obj; sel2:mark()
      else
         sel1=obj; sel1:mark()
      end
   end
end

local angle = function()
   if sel1 and sel2 then
      local obs = celestia:getobserver()
      local a= sel1:getposition() - obs:getposition()
      local b= sel2:getposition() - obs:getposition()
      local sepangle = math.acos(a:normalize() * b:normalize())*180/math.pi
      return string.format(" %0.2f°",sepangle)
   else
      return "- - - - - -"
   end
end

----------------------------------------------
-- Set up and Draw the boxes
----------------------------------------------
sepAnglesBox = CXBox:new()
    :init(0, 0, 0, 0)
    :movable(false)
    :attach(screenBox, sx, sy, 0, 0);

sepAnglesFrame = CXBox:new()
    :init(0, 0, 0, 0)
    :textfont(normalfont)
    :textcolor({0.7, 0.7, 1.0, 0.9})
   :text("Angular separation")
    :movetext(-5,4)
    :movable(false)
    :clickable(true)
    :visible(false)
    :attach(screenBox, posX, sy-posT-boxH, sx-boxW-posX, posT)
   
sepAnglesFrame.Customdraw = function(this)
   getnewselection()
   
   textlayout:setfont(normalfont)
   textlayout:setfontcolor({0.7, 0.7, 1, 0.9})
   textlayout:setlinespacing(lspace);
   textlayout:setpos(this.lb,this.tb-lspace*2+2) -- second line of the box
   textlayout:println(angle())

   sx, sy = celestia:getscreendimension()
   sepAnglesFrame:attach(screenBox, posX, sy-posT-boxH, sx-boxW-posX, posT)
end;

local toggleAnglesDisplay = function()
   sepAnglesFrame.Visible = not sepAnglesFrame.Visible
   celestia:unmarkall()
   sel1=nil
   sel2=nil
end

-- show or hide the tool
keymap["A"] = toggleAnglesDisplay

Now we want the Lua Edu Tools version
Here is the first conspicuous difference:

sepcheck.jpg

Instead of a key, we have now a box with the name of the tool (a little bit shortened) and a checkbox to activate or deactivate it.

Good new! the box with de name of the tool already exists!
In Lua Plug-ins whe had:

Code: Select all

sepAnglesBox = CXBox:new()
    :init(0, 0, 0, 0)
    :movable(false)
    :attach(screenBox, sx, sy, 0, 0);

With no text and its size of 0 x 0 pixel, it was invisible with Lua Plug-ins.

To use it with Lua Edu Tools, the only thing to do is to complete it to show the text.
Don't bother with the position and the size of this box. It is automatically managed by Lua Edu Tools.

The sepAnglesBox becomes:

Code: Select all

sepAnglesBox = CXBox:new()
    :init(0, 0, 0, 0)
    :movable(false)
   :textfont(normalfont)
   :textcolor(ctext)
   :movetext(20, 4)
   :text("sep. angles")
    :attach(screenBox, sx, sy, 0, 0)

Unfortunately the checkbox doesn't exist.
Create it just after the "sepAnglesBox"

Code: Select all

sepAnglesCheck = CXBox:new()
   :init(0, 0, 0, 0)
   :bordercolor(cbubordoff)
   :textfont(smallfont)
   :textcolor(ctext)
   :textpos("center")
   :movetext(0, 10)
   :text("")
   :movable(false)
   :active(true)
   :attach(sepAnglesBox, boxWidth - 40, 4, 29, 5)

The text of the checkbox is either "" (nothing) at startup or when the tool is hidden, or "x" when the tool is visible.
This box is active so you can click on it to hide or show the tool (and switch its display between "x" and ""

Clicking the checkbox in Lua Edu Tools has exactly the same function as typing a key in Lua Plug-ins.
The counterpart of the keymap statement is a function which is called when you click the "sepAnglesCheck" checkbox.
It is the "Action" function for "sepAnglesCheck" and its name must be sepAnglesCheck.Action (beware the capitalized letters)
The only difference is that it must also update its own display ( "" or "x")

[b]Juste replace [/b]

Code: Select all

keymap["A"] = toggleAnglesDisplay

with the Action function:

Code: Select all

sepAnglesCheck.Action = function(this)
   toggleAnglesDisplay()
   if sepAnglesFrame.Visible then
      this:text("x")
   else
      this:text("")
   end
end

The argument (this) of the function stands for the box itself, i.e. "sepAnglesCheck"
Note also the syntax of the "Action" function which is simpler than in most of the existing plug-ins.

As you can see there are only very few and simple changes to do to make a Lua Edu Tools plug-in from a Lua Plus-ins program.

If you respect the standard size of the tool (one line) in the Lua Edu Tools toolbox (the tools panel on the right of the screen), the ONLY thing you have to do to install the tool is to add the declaration:
"sepAnglesBox",
in the "toolset" section of the file config.lua as explained in Vincent's tutorial
viewtopic.php?f=21&t=12133&sid=edb02749544bb74f46e4b1af9188c3b5#p101161

As the personalization is different for Lua Plug-ins and Lua Edu Tool, I don't talk about it now.
Rather than having to manage two systems, I will explain later a way to do which is suitable for both Lua Lua Edu Tools and Plug-ins and has the advantage of the least possible impact on the key files of these two systems.

Avatar
Topic author
jogad
Posts: 458
Joined: 17.09.2008
With us: 16 years 2 months
Location: Paris France

Add your own plugin for both Lua Plug-ins and Lua Edu Tools

Post #15by jogad » 02.01.2013, 18:06

 
X) A version that works with Lua Plug-ins AND Lua Edu Tools

We now have two versions of our program.
We have clearly identified the differences so it is now easy to merge the two versions. :wink:
According to the Lua Edu Tools or Lua Plug-ins environment, the specific part of code will be executed.

For that you can test if the variables lua_edu_tools or lua_plugins exist or not. (Just test, don't try to modify!)
If we are running Lua Edu Tools then lua_edu_tools exists (but no lua_plugins)
if we are running Lua Plug-ins then lua_plugins exists and lua_edu_tools does not.
If one of these variable does not exist, then the other must exist.

for the definition of sepAnglesBox and sepAnglesCheck we get:

Code: Select all

if lua_edu_tools then   
   sepAnglesBox = CXBox:new()
      :init(0, 0, 0, 0)
      :movable(false)
      :textfont(normalfont)
      :textcolor(ctext)
      :movetext(20, 4)
      :text("Sep. angles")
      :attach(screenBox, sx, sy, 0, 0)

   sepAnglesCheck = CXBox:new()
      :init(0, 0, 0, 0)
      :bordercolor(cbubordoff)
      :textfont(smallfont)
      :textcolor(ctext)
      :textpos("center")
      :movetext(0, 10)
      :text("")
      :movable(false)
      :active(true)
      :attach(sepAnglesBox, boxWidth - 40, 4, 29, 5)

else -- Lua Plug-ins: simple "dummy" box
   sepAnglesBox = CXBox:new()
      :init(0, 0, 0, 0)
      :movable(false)
      :attach(screenBox, sx, sy, 0, 0)
end

And for the activation we get:

Code: Select all

-- activation
if lua_edu_tools then  -- activate with mouse
   sepAnglesCheck.Action = function(this)
      toggleAnglesDisplay()
      if sepAnglesFrame.Visible then
         this:text("x")
      else
         this:text("")
      end
   end
else -- Lua Plug-ins: activate with keyboard
   keymap["A"] = toggleAnglesDisplay
end

Pay attention to replace the matching part of the code and do not change the order of the declarations.
For example, as the sepAnglesCheck.Action function calls toggleAnglesDisplay(), it is important to declare toggleAnglesDisplay before the sepAnglesCheck.Action function.

Whith these simple modifications you are now able to enjoy your separation angles tool as well with Lua Plug-ins and Lua Edu Tools. :!:

The complete code at this point is:

Code: Select all

----------------------------------------------
-- Set initial values
----------------------------------------------

local sx, sy = celestia:getscreendimension()
local lspace = normalfont:getheight() + 1
local boxW, boxH = 120,lspace * 2  -- display width = 120 pixels, display height = 2 lines
local posX, posT = 0, 170  -- near the left margin, 170 pixels from the top of the screen.

local sel1, sel2 = nil, nil -- no object selected

local renderflags = celestia:getrenderflags()
renderflags["markers"] = true
celestia:setrenderflags(renderflags)

----------------------------------------------
-- local functions
----------------------------------------------

local getnewselection = function()
   local obj = celestia:getselection()
   -- test if there is a new selection
   local sel = true 
   if obj:type() == "null" then
      sel = false
   elseif sel2 and sel2:name() == obj:name() then
      sel = false
   elseif sel1 and sel1:name() == obj:name() then
      sel = false
   end
   
    -- record and mark selection
   if sel then
      if sel2 then
         sel1:unmark(); sel2:unmark()
         sel1=obj; sel2 = nil
         sel1:mark()
      elseif sel1 then
         sel2=obj; sel2:mark()
      else
         sel1=obj; sel1:mark()
      end
   end
end

local angle = function()
   if sel1 and sel2 then
      local obs = celestia:getobserver()
      local a= sel1:getposition() - obs:getposition()
      local b= sel2:getposition() - obs:getposition()
      local sepangle = math.acos(a:normalize() * b:normalize())*180/math.pi
      return string.format(" %0.2f°",sepangle)
   else
      return "- - - - - -"
   end
end

----------------------------------------------
-- Set up and Draw the boxes
----------------------------------------------
if lua_edu_tools then   
   sepAnglesBox = CXBox:new()
      :init(0, 0, 0, 0)
      :movable(false)
      :textfont(normalfont)
      :textcolor(ctext)
      :movetext(20, 4)
      :text("Sep. angles")
      :attach(screenBox, sx, sy, 0, 0)

   sepAnglesCheck = CXBox:new()
      :init(0, 0, 0, 0)
      :bordercolor(cbubordoff)
      :textfont(smallfont)
      :textcolor(ctext)
      :textpos("center")
      :movetext(0, 10)
      :text("")
      :movable(false)
      :active(true)
      :attach(sepAnglesBox, boxWidth - 40, 4, 29, 5)

else -- Lua Plug-ins: simple "dummy" box
   sepAnglesBox = CXBox:new()
      :init(0, 0, 0, 0)
      :movable(false)
      :attach(screenBox, sx, sy, 0, 0)
end

sepAnglesFrame = CXBox:new()
    :init(0, 0, 0, 0)
    :textfont(normalfont)
    :textcolor({0.7, 0.7, 1.0, 0.9})
   :text("Angular separation")
    :movetext(-5,4)
    :movable(false)
    :clickable(true)
    :visible(false)
    :attach(screenBox, posX, sy-posT-boxH, sx-boxW-posX, posT)
   
sepAnglesFrame.Customdraw = function(this)
   getnewselection()
   
   textlayout:setfont(normalfont)
   textlayout:setfontcolor({0.7, 0.7, 1, 0.9})
   textlayout:setlinespacing(lspace);
   textlayout:setpos(this.lb,this.tb-lspace*2+2) -- second line of the box
   textlayout:println(angle())

   sx, sy = celestia:getscreendimension()
   sepAnglesFrame:attach(screenBox, posX, sy-posT-boxH, sx-boxW-posX, posT)
end;

local toggleAnglesDisplay = function()
   sepAnglesFrame.Visible = not sepAnglesFrame.Visible
   celestia:unmarkall()
   sel1=nil
   sel2=nil
end

-- activation
if lua_edu_tools then  -- activate with mouse
   sepAnglesCheck.Action = function(this)
      toggleAnglesDisplay()
      if sepAnglesFrame.Visible then
         this:text("x")
      else
         this:text("")
      end
   end
else -- Lua Plug-ins: activate with keyboard
   keymap["A"] = toggleAnglesDisplay
end


:mrgreen:

Avatar
Topic author
jogad
Posts: 458
Joined: 17.09.2008
With us: 16 years 2 months
Location: Paris France

Re: Add your own plugin for both Lua Plug-ins and Lua Edu To

Post #16by jogad » 03.01.2013, 16:57

 
XI) Installation, Customization, and other considerations

To install a new tool you have to write its name in the file "config.lua"
There is a file "config.lua" for Lua Plug-ins and another file for Lua Edu Tools which is different but whose name is also "config.lua"

I don't like this very much because these files are very sensitive and the install procedure is different for each one.
But unfortunately there is no way to do otherwise. :(

We are in the same situation when it comes with personalization or localization.
But in this case we can avoid the pitfall of touching sensitive files and the personalization procedure can be the same, whatever the environment.

What I propose is to create a dedicated config file for the pluging.
This way we can define how to handle the personalizations without worrying about managing the specifics of Lua Lua Edu Tools and Plug-ins.
The installation (and above all, uninstallation) becomes much simpler: we can consider the new tool as a simple addon rather than a part of Lua Plug-ins or Lua Edu Tools. Its place comes naturally in the "extras" directory of Celestia.

If you have already custumized the Lua Plug-ins version by modifying the "enable_plugin" and the "key_shortcut" sections of the file "config.lua", undo these modifications.
And forget this method :!: In my opinion this should be reserved for tools that are part of Lua Plug-ins and are installed with it.
If you convince Vincent that your tools is essential and must be integrated into the next version of Lua Plug-ins, you can return to the previous method.

Because we are about to use several files for the addon,
:arrow: create a new sub-directory in the celestia/extras directory. Name it separation_angles or any name you prefer. The name of the directory doesn't matter: Lua Plug-ins and Lua Edu Tools are able find your script anywhere in the "extras" tree directory.

:arrow: Put the fle sepAnglesBox.lua in the newly created directory.


We want the same customizations as before:
    - The activation key (for the Lua Plug-ins version only)
    - whether the tool is visible or not at startup (for Lua Edu and Lua Plug-ins)

As it is your own config file, you can add any customization you want. :)
For example let's add the custimization of the text color for the resulting display.

Code: Select all

-------------------------------------------------------------
-- config file for the separation angles addon
-- change the values to customize the tool
-- if an item is not defined, a default value will be used
-------------------------------------------------------------

-- activating key for Lua Plug-ins (no effect with Lua Edu Tools)
sepAngles_shortcut = "A"

-- indicates if the tool is visible at startup
sepAngles_visible = false

-- angle display color (red, green, blue, transparency)
sepAngles_color = {0.7, 0.7, 1, 0.9}

Save this code as "sepAngles_config.lua" in the directory of your addon.

Note that the variables used here must be global variables.
This is why they are prefixed with the name of the addon.
In this way there is less chance that the name is already in use and causes problems with other variables defined elsewhere. :roll:

Back to sepAnglesBox.lua
In order to use the variables defined in sepAngles_config.lua, you must add the following declaration at the very beginning of sepAnglesBox.lua

Code: Select all

require "sepAngles_config"


Add this code at the end of the initializations:

Code: Select all

-- personalization
local display_angle = sepAngles_visible or false
local textcolor = sepAngles_color or {0.7, 0.7, 1, 0.9}
local shortcut = sepAngles_shortcut or "A"

and update the existing code to take this into account

Update of sepAnglesCheck:

Code: Select all

   -- determines if checked at startup
   local checktext = ""
   if display_angle then checktext = "x" end

   sepAnglesCheck = CXBox:new()
         :init(0, 0, 0, 0)
         :bordercolor(cbubordoff)
         :textfont(smallfont)
         :textcolor(ctext)
         :textpos("center")
         :movetext(0, 10)
         :text(checktext)  -- modif here
         :movable(false)
         :active(true)
         :attach(sepAnglesBox, boxWidth - 40, 4, 29, 5)

In the sepAnglesFrame definition, update the color of the text and the visibility at startup
you must also update the color in sepAnglesFrame.Customdraw

Code: Select all

sepAnglesFrame = CXBox:new()
    :init(0, 0, 0, 0)
    :textfont(normalfont)
    :textcolor(textcolor)
   :text("Angular separation")
    :movetext(-5,4)
    :movable(false)
    :clickable(true)
    :visible(display_angle)
    :attach(screenBox, posX, sy-posT-boxH, sx-boxW-posX, posT)
   
sepAnglesFrame.Customdraw = function(this)
   getnewselection()
   
   textlayout:setfont(normalfont)
   textlayout:setfontcolor(textcolor)
   textlayout:setlinespacing(lspace)
   textlayout:setpos(this.lb,this.tb-lspace*2+2) -- second line of the box
   textlayout:println(angle())

   sx, sy = celestia:getscreendimension()
   sepAnglesFrame:attach(screenBox, posX, sy-posT-boxH, sx-boxW-posX, posT)
end

And lastly, at the end of the script, replace

Code: Select all

keymap["A"] = toggleAnglesDisplay

with

Code: Select all

keymap[shortcut] = toggleAnglesDisplay

to acknowledge the customized key to activate the tool in Lua Plug-ins.

At this time the full code of sepAnglesBox.lua is:

Code: Select all

require "sepAngles_config"
----------------------------------------------
-- Set initial values
----------------------------------------------

local sx, sy = celestia:getscreendimension()
local lspace = normalfont:getheight() + 1
local boxW, boxH = 120,lspace * 2  -- display width = 120 pixels, display height = 2 lines
local posX, posT = 0, 170  -- near the left margin, 170 pixels from the top of the screen.

local sel1, sel2 = nil, nil -- no object selected

local renderflags = celestia:getrenderflags()
renderflags["markers"] = true
celestia:setrenderflags(renderflags)

-- personalization
local display_angle = sepAngles_visible or false
local textcolor = sepAngles_color or {0.7, 0.7, 1, 0.9}
local shortcut = sepAngles_shortcut or "A"

----------------------------------------------
-- local functions
----------------------------------------------

local getnewselection = function()
   local obj = celestia:getselection()
   -- test if there is a new selection
   local sel = true 
   if obj:type() == "null" then
      sel = false
   elseif sel2 and sel2:name() == obj:name() then
      sel = false
   elseif sel1 and sel1:name() == obj:name() then
      sel = false
   end
   
    -- record and mark selection
   if sel then
      if sel2 then
         sel1:unmark(); sel2:unmark()
         sel1=obj; sel2 = nil
         sel1:mark()
      elseif sel1 then
         sel2=obj; sel2:mark()
      else
         sel1=obj; sel1:mark()
      end
   end
end

local angle = function()
   if sel1 and sel2 then
      local obs = celestia:getobserver()
      local a= sel1:getposition() - obs:getposition()
      local b= sel2:getposition() - obs:getposition()
      local sepangle = math.acos(a:normalize() * b:normalize())*180/math.pi
      return string.format(" %0.2f°",sepangle)
   else
      return "- - - - - -"
   end
end

----------------------------------------------
-- Set up and Draw the boxes
----------------------------------------------
if lua_edu_tools then   
   sepAnglesBox = CXBox:new()
      :init(0, 0, 0, 0)
      :movable(false)
      :textfont(normalfont)
      :textcolor(ctext)
      :movetext(20, 4)
      :text("Sep. angles")
      :attach(screenBox, sx, sy, 0, 0)
      
   -- defines if checked at startup
   local checktext = ""
   if display_angle then checktext = "x" end

   sepAnglesCheck = CXBox:new()
         :init(0, 0, 0, 0)
         :bordercolor(cbubordoff)
         :textfont(smallfont)
         :textcolor(ctext)
         :textpos("center")
         :movetext(0, 10)
         :text(checktext)  -- modif here
         :movable(false)
         :active(true)
         :attach(sepAnglesBox, boxWidth - 40, 4, 29, 5)

else -- Lua Plug-ins: simple "dummy" box
   sepAnglesBox = CXBox:new()
      :init(0, 0, 0, 0)
      :movable(false)
      :attach(screenBox, sx, sy, 0, 0)
end

sepAnglesFrame = CXBox:new()
    :init(0, 0, 0, 0)
    :textfont(normalfont)
    :textcolor(textcolor)
   :text("Angular separation")
    :movetext(-5,4)
    :movable(false)
    :clickable(true)
    :visible(display_angle)
    :attach(screenBox, posX, sy-posT-boxH, sx-boxW-posX, posT)
   
sepAnglesFrame.Customdraw = function(this)
   getnewselection()
   
   textlayout:setfont(normalfont)
   textlayout:setfontcolor(textcolor)
   textlayout:setlinespacing(lspace)
   textlayout:setpos(this.lb,this.tb-lspace*2+2) -- second line of the box
   textlayout:println(angle())

   sx, sy = celestia:getscreendimension()
   sepAnglesFrame:attach(screenBox, posX, sy-posT-boxH, sx-boxW-posX, posT)
end

local toggleAnglesDisplay = function()
   sepAnglesFrame.Visible = not sepAnglesFrame.Visible
   celestia:unmarkall()
   sel1=nil
   sel2=nil
end

-- activation
if lua_edu_tools then  -- activate with mouse
   sepAnglesCheck.Action = function(this)
      toggleAnglesDisplay()
      if sepAnglesFrame.Visible then
         this:text("x")
      else
         this:text("")
      end
   end
else -- Lua Plug-ins: activate with keyboard
   keymap[shortcut] = toggleAnglesDisplay
end


It lays in the celestia/extras/separation_angles directory with its configuration file (sepAngles_config.lua)

Try it
:mrgreen:

Avatar
Topic author
jogad
Posts: 458
Joined: 17.09.2008
With us: 16 years 2 months
Location: Paris France

Re: Add your own plugin for both Lua Plug-ins and Lua Edu To

Post #17by jogad » 08.01.2013, 17:51

XII) Localization

Localization is important for people whose native language is not English, even if they are able to read an English text.

As French I would be happy to see "s?paration angulaire" instead of "angular separation" and "rapporteur" instead of "Sep. angles" in the toolbox of Lua Edu Tools.

With Lua Edu Tool, the localization is implemented.
You have just to place this incantation at the beginning of the program (don't do this for the moment 8O )

Code: Select all

   require "locale"

and a function underscore _() lets you translate any text you want. The correct locale of the computer is found automatically.

At this time, your program is as it was at the end of the previous post.

In the definition of sepAnglesBox, replace (yes, do it now :P )

Code: Select all

      :text("Sep. angles")
with

Code: Select all

      :text(_("Sep. angles"))


In the definition of sepAnglesFrame, replace

Code: Select all

   :text("Angular separation")
with

Code: Select all

   :text(_("Angular separation"))


Now you must tell your program where to search for the translation.
And a serious problem arise. :twisted:

If the underscore function exists in Lua Edu Tools, there is not such a function in Lua Plug-ins. :(
Anyway, with Lua Edu Tools we have to write our translation in the system files of Lua Edu Tools and I hate this idea.

As for customization(previous post) and for the same reasons:
    - same procedure for Lua Edu and Lua Plug-ins
    - easy installation and uninstallation
    - minimal risk for the Lua Edu Tools original files
I propose to build our own procedure suitable for both environments to handle the localization.

At this point it would be tedious to explain in detail how it works.
Just follow the instructions and believe me if I say that it should work!

:arrow: unzip the little module "plugins_locale.lua" in your addon directory.
This is the tranlation engine. It is a simplified version of the corresponding Lua Edu Tools module that I made to solve this issue.
plugins_locale.zip

:arrow: create the translation file(s)
This is the tranlation file for French

Code: Select all

-- Separation angles
-- French translation file

sepAngles_loc_str =
{
   ["Angular separation"] = "S?paration angulaire";
   ["Sep. angles"] = "Rapporteur";
}

Call this file sepAngles_locale_fr.lua and save it in the addon directory
If you have other translation files save them as "sepAngles_locale_xx" where "xx" are the letter that identify the locale
e.g. de for German, it for Italian, ru for Russian etc..

The name of the file (sepAngles_locale_xx) must match the name of the tranlation list (sepAngles_loc_str)
If you have characters that are not pure ascii like accents, you must save your file encoded as UTF-8 (without BOM)

:arrow: The final effort is to copy this code just after the require "sepAngles_config" statement

Code: Select all

if  lua_edu_tools and lang ~= "en"
   then require "locale"
   else require "plugins_locale"
end
if pcall( function() require ("sepAngles_locale_"..lang) end ) then
   for engl, transl in pairs(sepAngles_loc_str) do
      loc_str[engl]=transl
   end
end


:idea: Localization files are also useful if you are using only English. It is a way to change the displayed text without modifying the code of the program.
For example if you think that "Protractor" is better than "Sep. angles", just create the file sepAngles_locale_en.lua with this code:

Code: Select all

-- Separation angles
-- English translation file

sepAngles_loc_str =
{
   ["Sep. angles"] = "Protractor";
}


The program is now completed. Give it a try :!:

:mrgreen:

Avatar
Topic author
jogad
Posts: 458
Joined: 17.09.2008
With us: 16 years 2 months
Location: Paris France

Re: Add your own plugin for both Lua Plug-ins and Lua Edu To

Post #18by jogad » 08.01.2013, 18:06

XIII) Completed addon and conclusion

We have now a tool to measure the angle between two objects in Celestia. :D

We have seen the basic features which are specific to the graphics celestia lua interface:
    - how to position graphic objects like boxes and write in them
    - how to take advantage of the functions Customdraw and Action which are bound to the graphic boxes.
    - how to bind a function to a key. In this tuto it was only for Lua Plug-ins. But you can use exactly the same method with Lua Edu Tools.
    - How to customize and localize the tool
The tool is made of several files in one diectory (e.g. sepAngles):
    - sepAnglesBox.lua (the main program)
    - sepAngles_config.lua (configuration file for custumization)
    - plugins_locale.lua (a utility file for localization)
    - sepAngles_locale_fr.lua (French translation)
    - sepAngles_locale_en.lua (to modify the original text of the program if necessary)

A mandatory file is still missing: the readme file which explains at least what is this tool, how to install it, gives credits etc...
An example of such a file is provided in the attached add-on. I think it is an acceptalbe model for this kind of addon.

I join the completed addon to this post.
To install, unzip in the celestia/extras directory
Add the line
    sepAnglesBox, --(don't forget the comma)
in the proper section of config.lua (in the directory lua_edu_tools or lua_plugins)
That's it!

Uninstallation is just as easy: remove the sepAngles directory and the added line in the congig.lua.

Of course I can't explain anything. Now you have the essential principles. As the documentation for Lua Edu and Lua Plug-ins is rare, you must learn from the already existing tools.

Good luck if you want to make you own add-on :D , and if you do not want, do not hesitate to download this addon and use it if it is useful for you. :wink:
.
Attachments
sepAngles.zip
(3.65 KiB) Downloaded 330 times
Last edited by jogad on 23.08.2016, 14:31, edited 1 time in total.

darius
Posts: 18
Joined: 11.03.2013
With us: 11 years 8 months

Re: Add your own plugin for both Lua Plug-ins and Lua Edu To

Post #19by darius » 20.03.2013, 17:03

jogad,

thank you for your excellent tutorial.

Trying to install slideshow (I am low on memory, 100% processor busy with Celestia run)

Tell me how fast can slides be updated to get an impression of live video ?

darius

Avatar
Topic author
jogad
Posts: 458
Joined: 17.09.2008
With us: 16 years 2 months
Location: Paris France

Re: Add your own plugin for both Lua Plug-ins and Lua Edu To

Post #20by jogad » 20.03.2013, 18:02

darius wrote:Trying to install slideshow (I am low on memory, 100% processor busy with Celestia run)
Tell me how fast can slides be updated to get an impression of live video ?
The simple way is to try with a low value of the slideshow delay in the configuration file.
But don't expect anything spectacular. The silideshow plug-in is absolutely not designed for that. It's rather a kind of "powerpoint for celestia" to make presentations.

If you have questions about how to make an add-on for Lua Tools or Lua Plug-ins, it is the right place! :) (even though I am not sure to be able to answer all questions)
If you have questions about a particular add-on, please ask preferably in the thread dedicated to this add-on. :wink:
:mrgreen:


Return to “Tutorials”