Possible bug(s) in celx/lua

All about writing scripts for Celestia in Lua and the .cel system
Topic author
jan stegehuis
Posts: 27
Joined: 13.12.2010
Age: 70
With us: 13 years 6 months
Location: Hoogmade, The Netherlands

Possible bug(s) in celx/lua

Post #1by jan stegehuis » 26.12.2010, 12:46

Celestia's navigation function has a quality that I find awkward. It will happily: show the label of, 'select' and 'goto' objects that aren't really there; simply because the current time of the simulation clock is outside the object's timeline. It won't even show the object's timeline in the info text or anywhere else on the screen.
But Celestia has the celx/lua scripting facility so why not create an alternate 'goto' (using [Shift]+[g]) that signals this situation and offers to (re)set the simulation to a point (begin, middle, end) within the objects timeline. Celestia has all the information needed and with 30 odd years experience in ICT and programming this should be doable right? Wrong, the first setup for the script immediately got me into trouble. The problems I encountered are:

Code: Select all

-- Title: Goto object within it's timeframe

-- ***************************************************************************
--                      Jan Stegehuis GotoTimeframe script                   *
--                              (version 0.1)                                *
--                                                           *
-- Forms an alternative to celestia's "goto" function                   *
-- Reacts to [shift] + [g]                                        *
-- Checks whether the actual simulation time is within the selected          *
-- object's lifetime. If not, signals the user and asks whether the clock    *
-- should be reset to a point in time within the object's lifetime          *
-- For the time being only applicable to spacecrafts                   *
--                                                                           *
-- ***************************************************************************

function showmessage_G()
   selectedObject = celestia:getselection()
   if selectedObject:type() == "null" then
      return false
   else
      if selectedObject:type() ~= "spacecraft" then
         return false
      else
         count = 0
         objectBegin = 0.0
         objectEnd = 0.0
         for phase in selectedObject:phases() do
            count = count + 1
            begintime, endtime = phase:timespan()
            -- 30: celestia:print("Count = " .. count .. "; beginTime = " .. beginTime .. "; endTime = " .. endTime, 2)
            -- 31: celestia:print("Count = " .. count .. "; beginTime.type = " .. type(beginTime) .. "; endTime.type = " .. type(endTime), 2)
            -- 32: celestia:print(string:format("Count = %i; beginTime.type = %s; endTime.type = %s ", count, type(beginTime), type(endTime)), 2)
            if count == 1 then
               objectBegin = beginTime
            end
            objectEnd = endTime
         end
         -- 38: celestia:print("End for loop; objectBegin = " .. objectBegin .. "; objectEnd = " .. objectEnd, 2)
         -- 39: celestia:print("End for loop; objectBegin.type = " .. type(objectBegin) .. "; objectEnd.type = " .. type(objectEnd), 2)
         -- 40: wait(3)
         
         -- **************************************************************************************************
         -- add logic here to set the simulation clock to a point in time within the objects lifespan
         -- => check whether simulation clock is outside object's lifetime
         -- => tell the user, ask him to goto start, middle or end of object lifetime
         -- => expand to aditional choice of lifetime:phase if there are multiple
         -- => set simulation clock and goto object
         -- **************************************************************************************************
         
         return true
      end
   end
end

-- The table mapping key names to handlers
keyhandlers = { G = showmessage_G }

function handlekey(eventInfo)
   handler = keyhandlers[eventInfo.char]
   if (handler ~= nil) then
      return handler()
   else
      return false
   end
end

celestia:registereventhandler("key", handlekey)


Return value phase:timespan function:

The phase:timespan function (line 29) does not return a datatype 'number' as specified in the documentation but a datatype 'nil'. Uncommenting line 31 and running the script will show this. Any attempt to use it in a lua function - like in line 32 - causes an error and a return from the eventhandler with a return value of 'false'. Of course the loop's endvalues 'objectBegin' and 'objectEnd' are of type 'nil' also, as is shown by uncommenting line 39. Apparently Celestia itself does not know what to do with the variables either, since the statements 30 and 38 won't print. I tested this with Cassini and Huygens (celestia/extras standard/ folder), two crafts of which I know that they have multi phase timelines specified in the cassini.ssc file.
I take it this is a bug in the phase:timespan function? Or am I missing something here?

Wait function:
Any use of the lua 'wait' function in the script causes an immediate return from the event handler with a return value of 'false'. Uncommenting line 40 will show that Celestia takes it's default action and travels to the selected object (return false) instead if doing nothing (return true).
Is this a bug or simply behaviour we have to live with? I.e. you can't use wait in an eventhandler?

I would be gratefull if anybody could shed some light on these problems.

Jan Stegehuis

Celestia 1.6.0 (downloaded 8 dec 2010)
Windows 7 64 bit
jan.stegehuis at google mail
win 7 hp 64bit, intel I5, 2.53 GHz, 4GB , ATI MR HD 5470
celestia 1.6.0

Not smoking still "sucks".

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

Re: Possible bug(s) in celx/lua

Post #2by Marco Klunder » 26.12.2010, 15:00

Jan,

I can fully reproduce your problem here.
To me, it looks like a problem which has something to do with the combination of using the phase:timespan() and celestia:registereventhandler() methods together.

The following simple code, works fine here:

Code: Select all

function showmessage_G()
   object = celestia:getselection()
   celestia:flash(object:name(), 2)
   wait(2)
   count = 0
   for phase in object:phases() do
      count = count + 1
      begintime, endtime = phase:timespan()
      celestia:print("Phase [" .. count .. "] of " .. object:name() ..
                     "\nStart: " .. begintime .. " End: " .. endtime, 5.0, -1, -1, 2, 4)
      wait(5.0)
   end
end

showmessage_G()


But implementing it in combination with the celestia:registereventhandler() method, indeed gives a wrong result for the phase:timespan() method in that case.
The added code:

Code: Select all

celestia:flash(object:name(), 2)
wait(2)
proves that the function is called correctly when pressing the [shift] + [G] keys, but the phasetimes indeed will NOT show up this time, where they did in the simple code above.

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

Topic author
jan stegehuis
Posts: 27
Joined: 13.12.2010
Age: 70
With us: 13 years 6 months
Location: Hoogmade, The Netherlands

Re: Possible bug(s) in celx/lua

Post #3by jan stegehuis » 27.12.2010, 11:36

Thanks for confirming my problem Marco. Do you know what the procedure is from here on?

And since I'm at it, would you - or anybody else - know whether it is possible to use celestia:requestkeyboard (+ keyboard callback, etc.) in conjunction with celestia:registereventhandler? I have severe doubts in that area. Even beside the point that at this moment in time it is impossible anyway, since keyboard_callback "can only happen while the script is executing wait()" (see notes on celestia:requestkeyboard). And 'wait' would immediately return from the eventhandler, as we both have noticed, making the combined use impossible.

If from a design standpoint or celx and/or lua restrictions it is not possible to user both functions together, it is no use trying to pursue this perticular script and I had better issue a feature request. I have my doubts anyway, since this type of function would actually have to be implemented as an addon, meaning the use of a lua-hook (correct?) and the only lua-hook I've seen documented is the one for screen-refresh. Implementing it as a script would meen that I have to restart it everytime I need to run a second script. Something I could live with but would rather not do. ?r to incorporate every oher script in this 'goto' script.

Best regards
Jan
jan.stegehuis at google mail
win 7 hp 64bit, intel I5, 2.53 GHz, 4GB , ATI MR HD 5470
celestia 1.6.0

Not smoking still "sucks".

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

Re: Possible bug(s) in celx/lua

Post #4by Marco Klunder » 28.12.2010, 10:25

jan stegehuis wrote:Thanks for confirming my problem Marco. Do you know what the procedure is from here on?
You're welcome...
1) Wait till a member of the Celestia development team also recognizes this as a problem and put an action/priotity on it to solve (Vincent ?). In that case probably also report this issue as a threat in the "Celestia Development/Bugs" section.
2) Other forum members probably know a solution/circumvention and report it as an answer to this threat...

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

Topic author
jan stegehuis
Posts: 27
Joined: 13.12.2010
Age: 70
With us: 13 years 6 months
Location: Hoogmade, The Netherlands

Re: Possible bug(s) in celx/lua

Post #5by jan stegehuis » 28.12.2010, 16:06

OK thanks Marco,

I'll put the whole thing on ice until further developments occur.

Jan
jan.stegehuis at google mail
win 7 hp 64bit, intel I5, 2.53 GHz, 4GB , ATI MR HD 5470
celestia 1.6.0

Not smoking still "sucks".

Vincent
Developer
Posts: 1356
Joined: 07.01.2005
With us: 19 years 5 months
Location: Nancy, France

Re: Possible bug(s) in celx/lua

Post #6by Vincent » 29.12.2010, 14:19

jan stegehuis wrote:Return value phase:timespan function:
The phase:timespan function (line 29) does not return a datatype 'number' as specified in the documentation but a datatype 'nil'. Uncommenting line 31 and running the script will show this. Any attempt to use it in a lua function - like in line 32 - causes an error and a return from the eventhandler with a return value of 'false'. Of course the loop's endvalues 'objectBegin' and 'objectEnd' are of type 'nil' also, as is shown by uncommenting line 39. Apparently Celestia itself does not know what to do with the variables either, since the statements 30 and 38 won't print. I tested this with Cassini and Huygens (celestia/extras standard/ folder), two crafts of which I know that they have multi phase timelines specified in the cassini.ssc file.
I take it this is a bug in the phase:timespan function? Or am I missing something here?
Hi Jan,

The phase:timespan function actually correctly returns a number.
The problem is that you used a lower case to define begintime and endtime in line 29

Code: Select all

begintime, endtime = phase:timespan()
and an upper case to call these variables further in the script...

So the fix simply consists in changing line 29 to the following :

Code: Select all

beginTime, endTime = phase:timespan()

I'll have a look at the wait() function issue ASAP...
@+
Vincent

Celestia Qt4 SVN / Celestia 1.6.1 + Lua Edu Tools v1.2
GeForce 8600 GT 1024MB / AMD Athlon 64 Dual Core / 4Go DDR2 / XP SP3

Topic author
jan stegehuis
Posts: 27
Joined: 13.12.2010
Age: 70
With us: 13 years 6 months
Location: Hoogmade, The Netherlands

Re: Possible bug(s) in celx/lua

Post #7by jan stegehuis » 29.12.2010, 19:37

Thanks Vincent,

Well that's a humbling experience. A rookie mistake! Must have seen that tens of times while testing, trying and debugging and it never even caught my eye.
Must be spoiled by things like Visual Studio, that signal this type of mistake. Will never forget anymore that Lua is a case sensitive language. I hope :?

Am looking forward to your findings about the wait function.

Best regards
Jan
jan.stegehuis at google mail
win 7 hp 64bit, intel I5, 2.53 GHz, 4GB , ATI MR HD 5470
celestia 1.6.0

Not smoking still "sucks".

Vincent
Developer
Posts: 1356
Joined: 07.01.2005
With us: 19 years 5 months
Location: Nancy, France

Re: Possible bug(s) in celx/lua

Post #8by Vincent » 30.12.2010, 11:10

Jan,

You're welcome.
JFYI, You can bring up the debug console by pressing the tilde '~' key,
and then scroll it with the up and down arrow keys. On my French keyboard,
I have to press the spacebar just after the tilde key, though.

As to the the wait() function, it doesn't exist in Lua and has been added to
Celx scripting to avoid script writers having to deal with Lua coroutines.
The event handler code uses pure Lua, that's why it doesn't allow the use
of the wait() function.

So you might want to try running the phase iterator in a separate
thread. Then, you would be able to use the 'coroutine.create',
'coroutine.yield' and 'coroutine.resume' Lua functions.

I can see a quite simpler way to get your script working, though.
It consists in storing the beginTime, endTime values in a table.
Then, you'll be able to display them all using a single print call
That's what the following modified version of your script does :

Code: Select all

local timespan_t = {}

function showmessage_G()
   local selectedObject = celestia:getselection()
   if selectedObject:type() == "null" then
      return false
   else
      if selectedObject:type() ~= "spacecraft" then
         return false
      else
         local count = 0
         for phase in selectedObject:phases() do
            count = count + 1
            local beginTime, endTime = phase:timespan()
            timespan_t[count] = {beginTime, endTime};
         end
        local tstr = ""
        for k, v in pairs(timespan_t) do
            tstr = tstr..string.format("Phase: %i   beginTime: %0.2f   endTime: %0.2f", k, v[1], v[2]).."\n"
        end
        celestia:print(tstr, 20)
      end
   end
end


-- The table mapping key names to handlers
keyhandlers = { G = showmessage_G }

function handlekey(eventInfo)
   handler = keyhandlers[eventInfo.char]
   if (handler ~= nil) then
      handler()
      return true
   else
      return false
   end
end

celestia:registereventhandler("key", handlekey)
@+
Vincent

Celestia Qt4 SVN / Celestia 1.6.1 + Lua Edu Tools v1.2
GeForce 8600 GT 1024MB / AMD Athlon 64 Dual Core / 4Go DDR2 / XP SP3

Topic author
jan stegehuis
Posts: 27
Joined: 13.12.2010
Age: 70
With us: 13 years 6 months
Location: Hoogmade, The Netherlands

Re: Possible bug(s) in celx/lua

Post #9by jan stegehuis » 04.01.2011, 20:07

Merci Vincent,

Have not been able to reach this forum since sometime last year. Server down for maintenance maybe :?
At this moment I myself am down with a severe case of bronchitis :(
Will get back on this as soon as my brains are capable of doing something remotely intelligent again.

One thing though: how is this linked to the relationship between wait and requestkeyboard? See my post of 27 dec.

Best wishes for 2011
Jan
jan.stegehuis at google mail
win 7 hp 64bit, intel I5, 2.53 GHz, 4GB , ATI MR HD 5470
celestia 1.6.0

Not smoking still "sucks".

Topic author
jan stegehuis
Posts: 27
Joined: 13.12.2010
Age: 70
With us: 13 years 6 months
Location: Hoogmade, The Netherlands

Re: Possible bug(s) in celx/lua

Post #10by jan stegehuis » 13.01.2011, 12:20

Well, the virus is on it's retreat; finally!

Have finished the script though. Because of the Wait() question and resulting problems with keyboards etc. I had to fall back to using the key-eventhandler exclusively. This meant implementing some sort of state machine and because of that the script became somewhat larger then I had anticipated (600 lines, including comments). But it works and there don't seem to be any performance penalties.

Thanks for pointing out the "tilde thing" Vincent; it was a great help. On my dutch (us-international) keyboard I too have to press the spacebar.

I tried to add the final script as an attachement, but that doesn't seem to be possible; whatever extension I use, I get the message "The extension ... is not allowed". So I'll add it as code to this post.

Jan Stegehuis



Code: Select all

-- Title: Goto for spacecrafts

--[[ Description
========================================================================
Replaces the default Celestia "Goto", but only for spacecrafts.
Reacts to [Shift] + [G]; shows a message and a menu ?f the currently
selected object is a spacecraft and ?f the current simulation time is
outside the spacecrafts lifetime. The menu allows to:

   B - set the clock to the beginning of the crafts lifetime
      - and sets the direction of the clock to forward
   E - set the clock to the end of the crafts lifetime
      - and sets the direction of the clock to backward
   P - show a menu to select a specific phase
      - only if a craft has more than 1 phase specified
      - allows max 9 phases per object to keep the state machine
        from becomming too complex for a script
      - a submenu then allows to set the clock to the
        beginning or end of the chosen phase

If the selected object is not a spacecraft or the current simulation
time is inside the lifetime of the currently selected craft, the script
reverts to Celestia's default Goto function.

Also reacts to: [Shift] + [B], [Shift] + [E] and [Shift] + [P]. These
act as a shortcut into the Goto menu for a currently selected spacecraft,
but also work when the simulation clock in ?nside the craft's lifetime.

Remarks:
   - if a begin- or end time is +infinity or - infinity,
      the menu choice in question is disabled
=======================================================================]]


--[[ Structure: Calendar
========================================================================
   Utility (object) to print date and time in calendar format
=======================================================================]]


--[[ Table: calendar
------------------------------------------------------------------------
   holds (shorthand) names of moths
------------------------------------------------------------------------]]
calendar = { "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" }

         
--[[ Method: calendar:datetime()
------------------------------------------------------------------------
   return tdbTime as printable date & time
------------------------------------------------------------------------]]
function calendar:datetime(tdbTime)
   if tdbTime == -math.huge then
      return "-infinity"
   elseif tdbTime == math.huge then
      return "+infinity"
   else
      local ct = celestia:tdbtoutc(tdbTime)
      return string.format("%i %s %i  %i:%i:%i", ct.year, self[ct.month], ct.day, ct.hour, ct.minute, math.floor(ct.seconds))
   end
end


--[[ Method: calendar:date()
------------------------------------------------------------------------
   return tdbTime as printable date
------------------------------------------------------------------------]]
function calendar:date(tdbTime)
   if tdbTime == -math.huge then
      return "-infinity"
   elseif tdbTime == math.huge then
      return "+infinity"
   else
      local ct = celestia:tdbtoutc(tdbTime)
      return string.format("%i %s %i", ct.year, self[ct.month], ct.day)
   end
end


--[[ Spacecraft Structure
=======================================================================
   a structure/object that holds all the data and logic (functions
   and methods) to perform the 'spacecraft goto' functionality
=======================================================================]]


--[[ Table: spacecraft
------------------------------------------------------------------------
   holds the spacecraft's details
   also holds the tables:
   - menuKeys,
   - text and
   - state
   see below
------------------------------------------------------------------------]]
spacecraft          = {
   object          = nil,
   phase         = 0,
   phases          = {},
   }


--[[ Table: Spacecraft.menuKeys
------------------------------------------------------------------------
   holds keyboard keys used for menu choices
------------------------------------------------------------------------]]
spacecraft.menuKeys   = { keyBegin = "B", keyEnd = "E", keyPhase = "P", keyBlank = "**"  }


--[[ Table: Spacecraft.text
------------------------------------------------------------------------
   holds (parts of) display texts
------------------------------------------------------------------------]]
spacecraft.text      = {
   name          = "Spacecraft: %s",
   outside         = "\nSimulation time is outside of object lifetime"
                  .."\nTo adjust simulation clock and go press:",
   lifeBegin      = "\n%s - for begin of object lifetime: = %s",
   lifeEnd         = "\n%s - for end of object lifetime:   = %s",
   timePhase      = "\n%s - to select 1 out of %i phases",
   phase          = "\n%i - phase %i: from %s till %s",
   phaseBegin      = "\n%s - for begin of phase timespan: = %s",
   phaseEnd      = "\n%s - for end of phase timespan:   = %s"
   }

   
--[[ Table: spacecraft.state
------------------------------------------------------------------------
   Holds user-interface state information.
   Serves as a basis for the user-interface state machine.
   This is only part of the state structure and it's table;
   the table is completed towards the end of the script. This, in
   order to let lua correctly resolve the adresses of the various
   state machine functions.
------------------------------------------------------------------------]]
spacecraft.state   = {
   current         = nil,
   digits         = "123456789",                  -- is used to fill selectPhaseMenu.commandKey
   goto          = {   commandKey       = "GBEP"},
   gotoMenu      = {   commandKey      = ""},         -- is defined by method
   selectPhaseMenu   = { commandKey       = ""},         -- is defined by method
   gotoPhaseMenu   = {   commandKey       = ""}         -- is defined by method
   }


--[[ Method: spacecraft.state:reset()
------------------------------------------------------------------------
   reset state machine to the default state
   !! self = spacecraft.state
------------------------------------------------------------------------]]
function spacecraft.state:reset()
   if spacecraft.object == nil and self.current == self.goto then
      return
   else
      spacecraft.object    = nil
      spacecraft.phase   = 0
      spacecraft.phases    = {}
      self.current      = self.goto
      return
   end
end


--[[ Spacecraft structure:
=======================================================================
   spacecraft methods
=======================================================================]]


--[[ Method: spacecraft:getTimeLine()
------------------------------------------------------------------------
   get the spacraft's timeline from celestia and store the timespan
   for each phase in spacecraft.phases{}
------------------------------------------------------------------------]]
function spacecraft:getTimeLine()
   count = 0
   for phase in self.object:phases() do
      count = count + 1
      self.phases[count] = {phase:timespan()}
   end
   if count < 1 or count > 9 then
      return false               -- craft/object has no TimeLine or more than 9 timespans
   else
      return true
   end
end


--[[ Method: spacecraft:gotoTimeSpace()
------------------------------------------------------------------------
   set celestia's simulation clock to to the specified point in time
   direction reverses clock:
      values: +1 forward clock, -1 backward clock, 0 current clock
------------------------------------------------------------------------]]
function spacecraft:gotoTimeSpace(pointInTime, direction)
   local observer = celestia:getobserver()
   if observer == nill then
      celestia:print("CELESTIA ERROR: Can not link to observer")
      return false
   else
      local clockText = ""
      local timescale = math.abs(celestia:gettimescale())
      if direction < 0 then
         timescale = -timescale
         clockText = "\nClock running backward"
      elseif direction > 0 then
         clockText = "\nClock running forward"
      end
      celestia:settimescale(timescale)
      celestia:settime(pointInTime)
      celestia:print("Clock set to: " ..calendar:datetime(pointInTime) ..clockText,3)
      observer:goto(self.object,7)
      return true
   end
end


--[[ Method: spacecraft:gotoMenu()
------------------------------------------------------------------------
   display the 'G'oto menu and adjust the state
   disable choice(es) that denote infinity
------------------------------------------------------------------------]]
function spacecraft:gotoMenu()
   local observationTime = celestia:getobserver():gettime()
   if observationTime <= self.phases[#self.phases][2] and observationTime >= self.phases[1][1] then
      return false
   else
      -- construct the menu
      local menuKey = ""
      local allowedMenuKeys = ""
      local message = string.format(self.text.name .. self.text.outside, self.object:name())
      if self.phases[1][1] <= -math.huge then
         -- negative infinity; can't go there
         menuKey = self.menuKeys.keyBlank
      else
         menuKey = self.menuKeys.keyBegin
         allowedMenuKeys = allowedMenuKeys ..self.menuKeys.keyBegin
      end
      message = message ..string.format(self.text.lifeBegin, menuKey, calendar:datetime(self.phases[1][1]))
      if self.phases[#self.phases][2] >= math.huge then
         -- positive infinity; can't go there
         menuKey = self.menuKeys.keyBlank
      else
         menuKey = self.menuKeys.keyEnd
         allowedMenuKeys = allowedMenuKeys ..self.menuKeys.keyEnd
      end
      message = message ..string.format(self.text.lifeEnd, menuKey, calendar:datetime(self.phases[#self.phases][2]))
      if #self.phases > 1 then
         -- add "select phase" to menu
         allowedMenuKeys = allowedMenuKeys ..self.menuKeys.keyPhase
         message = message ..string.format(self.text.timePhase, self.menuKeys.keyPhase, #self.phases)
      end
      -- set state machine
      self.state.gotoMenu.commandKey = allowedMenuKeys
      self.state.current = self.state.gotoMenu
      -- print the menu
      celestia:print(message, 10, -1, 0, 5, 0)
      return true
   end
end


--[[ Method: spacecraft:gotoLifeBegin()
------------------------------------------------------------------------
   set the clock to the beginning of the object's lifetime
   and have it run forward
------------------------------------------------------------------------]]
function spacecraft:gotoLifeBegin()
   if self.phases[1][1] <= -math.huge then      -- for when gotoMenu got bypassed
      celestia:print("Can not set clock to -infinity", 3)
   else
      self:gotoTimeSpace(self.phases[1][1], 1)
   end
   self.state:reset()
   return true   
end


--[[ Method: spacecraft:gotoLifeEnd()
------------------------------------------------------------------------
   set the clock to the end of the object's lifetime
   and have it run backward
------------------------------------------------------------------------]]
function spacecraft:gotoLifeEnd()
   if self.phases[#self.phases][2] >= math.huge then      -- for when gotoMenu got bypassed
      celestia:print("Can not set clock to +infinity", 3)
   else
      self:gotoTimeSpace(self.phases[#self.phases][2], -1)
   end
   self.state:reset()
   return true   
end


--[[ Method: spacecraft:selectPhaseMenu()
------------------------------------------------------------------------
   display the "select phase" submenu and adjust the state
------------------------------------------------------------------------]]
function spacecraft:selectPhaseMenu()
   if #self.phases <= 1 then      -- for when gotoMenu got bypassed
      return false
   else
      -- construct the menu
      local message = string.format(self.text.name, self.object:name())
      -- add the phases
      for i=1, #self.phases do
         message = message ..string.format(self.text.phase, i, i, calendar:date(self.phases[i][1]), calendar:date(self.phases[i][2]))
      end
      -- set state machine
      self.state.selectPhaseMenu.commandKey = string.sub(self.state.digits, 1, #self.phases)
      self.state.current = self.state.selectPhaseMenu
      -- print the menu
      celestia:print(message, 10, -1, 0, 5, 0)
      return true
   end
end


--[[ Method: spacecraft:gotoPhaseMenu()
------------------------------------------------------------------------
   display the "goto (begin or end of) phase" menu and adjust the state
   disable choice(es) that denote infinity
------------------------------------------------------------------------]]
function spacecraft:gotoPhaseMenu()
   -- construct the menu
   local menuKey = ""
   local allowedMenuKeys = ""
   local message = string.format(self.text.name  ..": Phase %i", self.object:name(), self.phase)
   if self.phases[1][1] <= -math.huge then
      -- negative infinity; can't go there
      menuKey = self.menuKeys.keyBlank
   else
      menuKey = self.menuKeys.keyBegin
      allowedMenuKeys = allowedMenuKeys ..self.menuKeys.keyBegin
   end
   message = message ..string.format(self.text.phaseBegin, menuKey, calendar:datetime(self.phases[self.phase][1]))
   if self.phases[#self.phases][2] >= math.huge then
      -- positive infinity; can't go there
      menuKey = self.menuKeys.keyBlank
   else
      menuKey = self.menuKeys.keyEnd
      allowedMenuKeys = allowedMenuKeys ..self.menuKeys.keyEnd
   end
   message = message ..string.format(self.text.phaseEnd, menuKey, calendar:datetime(self.phases[self.phase][2]))
   -- set state machine
   self.state.gotoPhaseMenu.commandKey = allowedMenuKeys
   self.state.current = self.state.gotoPhaseMenu
   -- print the menu
   celestia:print(message, 10, -1, 0, 5, 0)
   return true
end


--[[ Method: spacecraft:gotoPhaseBegin()
------------------------------------------------------------------------
   set the clock to the begin of the selected phase
------------------------------------------------------------------------]]
function spacecraft:gotoPhaseBegin(eventInfo)
   if self.phase == 1 then
      -- is beginning of first phase; set clock forward
      self:gotoTimeSpace(self.phases[self.phase][1], 1)
   else
      -- leave clock as is
      self:gotoTimeSpace(self.phases[self.phase][1], 0)
   end
   self.state:reset()
   return true   
end


--[[ Method: spacecraft:gotoPhaseEnd()
------------------------------------------------------------------------
   set the clock to the end of the selected phase
------------------------------------------------------------------------]]
function spacecraft:gotoPhaseEnd()
   if self.phase == #self.phases then
      -- is end of last phase; set clock backward
      self:gotoTimeSpace(self.phases[self.phase][2], -1)
   else
      -- leave clock as is
      self:gotoTimeSpace(self.phases[self.phase][2], 0)
   end
   self.state:reset()
   return true   
end


--[[ Spacecraft structure:
=======================================================================
   spacecraft state methods
=======================================================================]]


--[[ State method: spacecraft:gotoWantKey()
------------------------------------------------------------------------
   current state == goto; if key has correct value, accept key as input
------------------------------------------------------------------------]]
function spacecraft:gotoWantKey(eventInfo)
   if string.find(self.state.goto.commandKey, eventInfo.char) == nil then
      return false
   else
      selectedObject = celestia:getselection()
      if selectedObject:type() ~= "spacecraft" then
         return false
      else
         self.state:reset()
         self.object = selectedObject
         return true
      end
   end
end


--[[ State method: spacecraft:gotoProcessKey()
------------------------------------------------------------------------
   current state == goto; process the key entered by the user
------------------------------------------------------------------------]]
function spacecraft:gotoProcessKey(eventInfo)
   if self.object == nil then
      return false
   elseif not self:getTimeLine() then
      return false
   else
      return self.state.goto.key[string.upper(eventInfo.char)](self)
   -- ".key[](self)" is needed here; Lua will n?t resolve: ":key[]()" correctly
   end
end


--[[ State method: spacecraft:gotoMenuWantKey()
------------------------------------------------------------------------
   current state == gotoMenu; if key has correct value, accept key as input
------------------------------------------------------------------------]]
function spacecraft:gotoMenuWantKey(eventInfo)
   if string.find(self.state.gotoMenu.commandKey, string.upper(eventInfo.char)) == nil then
      self.state:reset()
      return false
   else
      return true
   end
end


--[[ State method: spacecraft:gotoMenuProcessKey()
------------------------------------------------------------------------
   current state == gotoMenu; process the key entered by the user
------------------------------------------------------------------------]]
function spacecraft:gotoMenuProcessKey(eventInfo)
   return self.state.gotoMenu.key[string.lower(eventInfo.char)](self)
   -- ".key[](self)" is needed here; Lua will n?t resolve: ":key[]()" correctly
end


--[[ State method: spacecraft:selectPhaseMenuWantKey()
------------------------------------------------------------------------
   current state == selectPhaseMenu; if key has correct value, accept key as input
------------------------------------------------------------------------]]
function spacecraft:selectPhaseMenuWantKey(eventInfo)
   if string.find(self.state.selectPhaseMenu.commandKey, eventInfo.char) == nil then
      self.state:reset()
      return false
   else
      return true
   end
end


--[[ State method: spacecraft:selectPhaseMenuProcessKey()
------------------------------------------------------------------------
   current state == selectPhaseMenu; process the key entered by the user
------------------------------------------------------------------------]]
function spacecraft:selectPhaseMenuProcessKey(eventInfo)
   self.phase = tonumber(eventInfo.char)
   return self.state.selectPhaseMenu.key(self)
   -- ".key(self)" is needed here; Lua will n?t resolve: ":key()" correctly
end


--[[ State method: spacecraft:gotoPhaseMenuWantKey()
------------------------------------------------------------------------
   current state == gotoPhaseMenu; if key has correct value, accept key as input
------------------------------------------------------------------------]]
function spacecraft:gotoPhaseMenuWantKey(eventInfo)
   if string.find(self.state.gotoPhaseMenu.commandKey, string.upper(eventInfo.char)) == nil then
      self.state:reset()
      return false
   else
      return true
   end
end


--[[ State method: spacecraft:gotoPhaseMenuProcessKey()
------------------------------------------------------------------------
   current state == gotoPhaseMenu; process the key entered by the user
------------------------------------------------------------------------]]
function spacecraft:gotoPhaseMenuProcessKey(eventInfo)
   return self.state.gotoPhaseMenu.key[string.lower(eventInfo.char)](self)
end


--[[ Table spacecraft.state      part two
------------------------------------------------------------------------
-- complete spacecraft.state table
-- has to be done here, after all state methods are specified
-- otherwise Lua can't resolve function adresses correctly
-- links methods to user-input, dependent on the current state
------------------------------------------------------------------------]]
spacecraft.state.goto.wantKey               = spacecraft.gotoWantKey
spacecraft.state.goto.processKey            = spacecraft.gotoProcessKey
spacecraft.state.goto.key                  = {   G = spacecraft.gotoMenu,
                                       B = spacecraft.gotoLifeBegin,
                                       E = spacecraft.gotoLifeEnd,
                                       P = spacecraft.selectPhaseMenu}
spacecraft.state.gotoMenu.wantKey            = spacecraft.gotoMenuWantKey
spacecraft.state.gotoMenu.processKey         = spacecraft.gotoMenuProcessKey
spacecraft.state.gotoMenu.key               = {   b = spacecraft.gotoLifeBegin,
                                       e = spacecraft.gotoLifeEnd,
                                       p = spacecraft.selectPhaseMenu}
spacecraft.state.selectPhaseMenu.wantKey      = spacecraft.selectPhaseMenuWantKey
spacecraft.state.selectPhaseMenu.processKey      = spacecraft.selectPhaseMenuProcessKey
spacecraft.state.selectPhaseMenu.key         = spacecraft.gotoPhaseMenu
spacecraft.state.gotoPhaseMenu.wantKey         = spacecraft.gotoPhaseMenuWantKey
spacecraft.state.gotoPhaseMenu.processKey      = spacecraft.gotoPhaseMenuProcessKey
spacecraft.state.gotoPhaseMenu.key            = {   b = spacecraft.gotoPhaseBegin,
                                       e = spacecraft.gotoPhaseEnd}

                                       
--[[ Method: spacecraft:wantKey()
------------------------------------------------------------------------
   check if spacecraft wants to process the key the user pressed
------------------------------------------------------------------------]]
function spacecraft:wantKey(eventInfo)
   celestia:print("")
   if self.state.current == nil then
      return false
   else
      return self.state.current.wantKey(self, eventInfo)
      -- ".wantKey(self," is needed here; Lua will n?t resolve: ":wantKey(" correctly
   end
end


--[[ Method: spacecraft:processKey()
------------------------------------------------------------------------
   allow spacecraft to process the key the user pressed
------------------------------------------------------------------------]]
function spacecraft:processKey(eventInfo)
   if self.state.current == nil then
      return false
   else
      return self.state.current.processKey(self, eventInfo)
      -- ".processKey(self," is needed here; Lua will n?t resolve: ":processKey(" correctly
   end
end


--[[ Script Main:
=======================================================================
   The main functions
=======================================================================]]


--[[ Function: keyEvent()
------------------------------------------------------------------------
   the keyboard event handler
------------------------------------------------------------------------]]
function keyEvent(eventInfo)
   if spacecraft:wantKey(eventInfo) then
      return spacecraft:processKey(eventInfo)
   -- elseif otherObject:wantKey(eventInfo) then
      -- return otherObject:processKey(eventInfo)
   else
      return false
   end
end


--[[ Function: Main()
------------------------------------------------------------------------
   the function Main
------------------------------------------------------------------------]]
function Main()
   spacecraft.state:reset()
   celestia:registereventhandler("key", keyEvent)
   celestia:print("Spacecraft Goto is active", 3)
end


--[[ Script
------------------------------------------------------------------------
   start
------------------------------------------------------------------------]]
Main()
jan.stegehuis at google mail
win 7 hp 64bit, intel I5, 2.53 GHz, 4GB , ATI MR HD 5470
celestia 1.6.0

Not smoking still "sucks".

Avatar
Fenerit M
Posts: 1880
Joined: 26.03.2007
Age: 17
With us: 17 years 3 months
Location: Thyrrenian sea

Re: Possible bug(s) in celx/lua

Post #11by Fenerit » 13.01.2011, 12:44

Thanks Jan for your script and the precious time you did spent with the issues. 8)

About the attachment, you must find a (free?) file hosting service in which to place the zipped file to link at as URL from here.
Never at rest.
Massimo

Avatar
selden
Developer
Posts: 10190
Joined: 04.09.2002
With us: 21 years 10 months
Location: NY, USA

Re: Possible bug(s) in celx/lua

Post #12by selden » 13.01.2011, 14:44

The easiest way to post dis-allowed files is to put them into a zip archive and post that.

You might consider having the script hosted by the CelestiaMotherlode.
http://www.celestiamotherlode.net/
Selden

Avatar
jogad
Posts: 458
Joined: 17.09.2008
With us: 15 years 9 months
Location: Paris France

Re: Possible bug(s) in celx/lua

Post #13by jogad » 13.01.2011, 17:20

Hello,

Works like a charm. :D

This is smart, simple to use and very useful :!:
We really need good ideas like that.

Thank you very much.
:mrgreen:

Topic author
jan stegehuis
Posts: 27
Joined: 13.12.2010
Age: 70
With us: 13 years 6 months
Location: Hoogmade, The Netherlands

Re: Possible bug(s) in celx/lua

Post #14by jan stegehuis » 13.01.2011, 19:55

Works like a charm.
I would hope so. If anybody finds a bug, please let me know and I'll try to correct it.

There is one thing about the script that I have a problem with and that I have not yet been able to find a solution for.
If I use it to move between different points in time for the same spacecraft, the observer:goto(<object>) keeps moving me
closer and closer to the craft whereas I would rather stay at aproximately the same distance. That is, after the initial goto has taken place.
If anybody knows how I can use celx to see if the observer - in general - is so close to an object that a "goto" is not needed, please let me know.

Selden
You might consider having the script hosted by the CelestiaMotherlode.
I will be happy to do so. Will try to upload the current version this evening. I take it that after I did that I can change it anytime I need to?

Jan
jan.stegehuis at google mail
win 7 hp 64bit, intel I5, 2.53 GHz, 4GB , ATI MR HD 5470
celestia 1.6.0

Not smoking still "sucks".

Avatar
selden
Developer
Posts: 10190
Joined: 04.09.2002
With us: 21 years 10 months
Location: NY, USA

Re: Possible bug(s) in celx/lua

Post #15by selden » 13.01.2011, 20:54

Sometimes it can take a few days before uploaded files (whether new or updates) are made available on the server. It depends on how busy the people managing it are.
Selden

Avatar
jogad
Posts: 458
Joined: 17.09.2008
With us: 15 years 9 months
Location: Paris France

Re: Possible bug(s) in celx/lua

Post #16by jogad » 13.01.2011, 22:29

jan stegehuis wrote:If anybody knows how I can use celx to see if the observer - in general - is so close to an object that a "goto" is not needed, please let me know.

You can test the distance from the observer's position with the position:distanceto() method.

-- find the observer
observ = celestia:getobserver()
-- find the position of the observer
obspos = observ:getposition()

-- find your selected object
spacecraft = celestia:getselection()
-- find the position of the selected object
craftpos = spacecraft:getposition()

-- the distance in km from the observer to the center of selected object is
dist = obspos:distanceto(craftpos)

--You can fix the desired minimal distance in respect to the radius of the selected object that is given by:
radius = spacecraft:radius()

Topic author
jan stegehuis
Posts: 27
Joined: 13.12.2010
Age: 70
With us: 13 years 6 months
Location: Hoogmade, The Netherlands

Re: Possible bug(s) in celx/lua

Post #17by jan stegehuis » 14.01.2011, 10:35

Jogad
You can test the distance from the observer's position with the position:distanceto() method.

Had already found the position page in the documentation, but thanks a lot anyway. For some reason this post of your's isn't visible in normal view mode; it ?s however in "post a reply" mode. I would never have known about it's existence if I hadn't want to post a reply that I had found the answer. Weird :!:

Am playing around with a test script to see what position, distance, etc. can do for me. Will leave a note here when there is a new version of the goto script.

Jan
jan.stegehuis at google mail
win 7 hp 64bit, intel I5, 2.53 GHz, 4GB , ATI MR HD 5470
celestia 1.6.0

Not smoking still "sucks".

Topic author
jan stegehuis
Posts: 27
Joined: 13.12.2010
Age: 70
With us: 13 years 6 months
Location: Hoogmade, The Netherlands

Re: Possible bug(s) in celx/lua

Post #18by jan stegehuis » 14.01.2011, 10:39

For some reason this post of your's isn't visible in normal view mode; it ?s however in "post a reply" mode.
Wasn't awake yet :? . Hadn't seen that the subject spreads over two pages by now. So forget about it.

Jan
jan.stegehuis at google mail
win 7 hp 64bit, intel I5, 2.53 GHz, 4GB , ATI MR HD 5470
celestia 1.6.0

Not smoking still "sucks".

Topic author
jan stegehuis
Posts: 27
Joined: 13.12.2010
Age: 70
With us: 13 years 6 months
Location: Hoogmade, The Netherlands

Re: Possible bug(s) in celx/lua

Post #19by jan stegehuis » 16.01.2011, 11:51

Changed the script. Added the options:

    M = middle of object lifetime
    N = now (system time)

Changed the goto-logic. Now only travels space when the distance between the observer and the target object's centre is more than 10 times the object's radius. Otherwise it only sets the simulation clock.
Also changed a few small user interaction things since other people than me are going te be using it.

Uploaded version 1 to the motherlode last thursday but have not seen it appear in the scripts catalog yet :( . Uploaded the changed script just now as version 2. :?

Jan
jan.stegehuis at google mail
win 7 hp 64bit, intel I5, 2.53 GHz, 4GB , ATI MR HD 5470
celestia 1.6.0

Not smoking still "sucks".

Topic author
jan stegehuis
Posts: 27
Joined: 13.12.2010
Age: 70
With us: 13 years 6 months
Location: Hoogmade, The Netherlands

Re: Possible bug(s) in celx/lua

Post #20by jan stegehuis » 30.01.2011, 19:23

jan.stegehuis at google mail
win 7 hp 64bit, intel I5, 2.53 GHz, 4GB , ATI MR HD 5470
celestia 1.6.0

Not smoking still "sucks".


Return to “Scripting”