Page 1 of 1

A brief introduction to Celx vectors

Posted: 26.04.2004, 23:44
by don
This message begins a new thread where it left off in another thread (Got an Idea for a Celx Utility Script?
-- http://www.celestiaproject.net/forum/viewtopic.php?t=4903)

The code below was modified further by Don G., for clarification (mostly the variable names).

Toti, what is the purpose of computing a seemingly arbitrary number (6 in this case) and calling it rot_speed? What is "rot_speed"? ...

Code: Select all

  -- Complete one orbit (360 degrees):
  local rot_speed = 360 / orbitTime


The next line takes this seemingly arbitrary number, multiplies it by scriptTime and multiplies it by 5. What does "5" represent? Wouldn't we want to increase the longitude by a fixed value for each render pass?

Code: Select all

    -- This is the longitude we pass to the function. It's a function of
    -- time, like many parameter trajectories:
    local longitude = rot_speed * scriptTime * 5


Earlier, orbitTime is defined so that an orbit is supposed to take 60 seconds...

Code: Select all

  -- Define an orbit to take 60 seconds:
  local orbitTime = 60

However, in 60 seconds, we orbit the Earth FIVE times, not just once.

So, I was wondering if you could clarify things a bit more? :D

Thank you Toti!

-Don G.


Code: Select all

--******************************************************
--
--  This script is a modification of Harald Schmidt's
--  "Around the Earth" script.
--
--******************************************************

--************************************************
--  UCS = Universal Coordinate System
--************************************************

-- This function puts the viewer at longitiude on myObject.
-- All is performed in UCS:
   function around(myObject, longitude, distance, upvector)

     -- These two equations look rather complicated, but they are only a 2-D
     -- polar-cartesian conversion, that is, given a (radius, angle) = (r,
     -- pos), convert it to (x, y) values.  They are a "classic":
     local x = distance * math.cos(math.rad(longitude))
     local z = distance * math.sin(math.rad(longitude))

     -- Keep the up pointing coordinate on the UCS "horizontal" plane:
     local y = 0

     -- Create a vector with the above coordinates. We create a vector because
     -- they don't need to be that precise (they are pretty small coordinates,
     -- with length = distance at most):
     local offsetVector = celestia:newvector(x,y,z)

     -- Obtain a vector that is the center of myObject in UCS. This is a
     -- 'position' vector, so it is very precise. Why is this? Because UCS
     -- places its center at about 200 AU from Sol. If we do not assure enough
     -- precision, our centerBodyPosition would not be placed on myObject but
     -- a few thousand km. from it.
     local centerBodyPositionVector = myObject:getposition ()

     -- Viewer's position: Let's go to the center of myObject and then go to
     -- ourVector (that is what a vector addition does, really). This adds a
     -- 'common vector' (offsetVector) to a 'position vector'
     -- (centerBodyPositionVector), so the maximun precision is retained. We
     -- want that, because we want to place the viewer exactly where it's
     -- needed (and not a few thousand km. away).
     local viewerPositionVector = centerBodyPositionVector + offsetVector

     -- let's place the observer there:
     local obs = celestia:getobserver()
     obs:setposition (viewerPositionVector)

     -- Look at earth centered with upvector as "ceiling". This is complex...
     -- Close your hand. Extend your thumb. Then extend the first finger, and
     -- point it to this -> x. (Keep the angle between both fingers fixed.)
     -- This behaves like the viewer: your thumb is the 'up' axis. Your first
     -- finger is the viewer's 'look_at' axis. When you point it to the 'x',
     -- you are transforming the 'up' vector (your thumb moves with your hand),
     -- but it doesn't matter, because you have already defined where it is in
     -- relation with the rest of your hand. I believe that Celestia does this
     -- same thing that you have done with your hand: First creates an
     -- "abstract" viewer, then defines where is 'up' and last, moves/rotates
     -- the viewer to point its "camera" to the selected body. The 'up' vector
     -- signals the viewer's "ceiling" BEFORE Celestia rotates it in order to
     -- look at some place.
     obs:lookat (centerBodyPositionVector, upvector)

     -- print the viewer's position (in UCS):
     celestia:flash("Viewer pos.: " .. viewerPositionVector.x ..
                    " " .. viewerPositionVector.y ..
                    " " .. viewerPositionVector.z , 1)

     -- return control to Celestia for a moment (to refresh screen, etc.):
     wait(0.0)
   end


--************************************************
--      Now call the function
--************************************************

  -- Define which direction we should call 'up'. Since this is only a rough
  -- direction, we can use a 'common vector'. We don't need precision here:
  local upvector   = celestia:newvector (0,1,0)

  -- select body:
  local myObject   = celestia:find("Sol/Earth")

  -- Define an orbit to take 60 seconds:
  local orbitTime = 60

  -- Complete one orbit (360 degrees):
  local rot_speed = 360 / orbitTime

  -- Fix the viewer's distance to the body's center:
  local distance = 0.005

  -- loop for the time specified in orbitTime:
  local scriptTime = 0
  while scriptTime < orbitTime do
    scriptTime = celestia:getscripttime()

    -- This is the longitude we pass to the function. It's a function of
    -- time, like many parameter trajectories:
    local longitude = rot_speed * scriptTime * 5

    -- Call the function:
    around(myObject, longitude, distance, upvector)
  end

Posted: 27.04.2004, 01:31
by Toti
Don:
you must always consider the units. They are a good hint of what's going on.
In the code, there is "360 degrees divided by a time (t0)". This is space (angle) divided by time (seconds) = speed (in angle per seconds).
This is called "angular speed". 360 deg. / 60 sec. = 6 deg. / sec. = 6 degrees per second. In other words, for each second, a rotation of 6 degrees is made.
So it will take you 60 seconds to do a complete rotation.
The funny thing is that I completely forgot about the 5. The proper place for this 5 is in the above expression. I didn't notice it, even with an obvious, very visible "very fast Earth rotation" on screen. Please remove it, or add it to the speed definition.
This 5 multiplies rot_speed, so rotation speed becomes 5 times higher, so you can do 5 more rotations in the same time (60 sec.). That is, if you could do 1 rotation in that time, now you can do 5 rotations...that's all.

I'm sorry for the confusion. I will fix the post on the other thread.

Can you change the title of the thread, please? It's too sounding..."A brief introduction to Celx vectors" will be more appropriate. ;)

Bye :)

Posted: 27.04.2004, 07:45
by Harry
I can explain where the factor 5 originally was for:
There are two components in the movement:
One is a rotation around the earth (changing longitude), the other is a slow change from south to north (changing latitude). I wanted to have the observer make a few rotations around the earth in the time it takes the script to take you from south to north, and the easiest way was to multiply the effective angular speed for the change in longitude by 5.

Harald

Posted: 27.04.2004, 08:50
by don
Thank you for your further clarification Toti. For whatever reason, I simply could not fathom what that value was supposed to be <sigh>.

Thank you Harald, for explaining how the "5" got there and what it was for.

Now I can continue "playing". 8)

Posted: 27.04.2004, 15:46
by Toti
Harald and Don:
We are talking about different things here :D

Harry wrote:One is a rotation around the earth (changing longitude), the other is a slow change from south to north (changing latitude). I wanted to have the observer make a few rotations around the earth in the time it takes the script to take you from south to north, and the easiest way was to multiply the effective angular speed for the change in longitude by 5.

Yes, but this is in your "Around the Earth" script. I slightly modified it: this modified version is the one that Don posted above and it should be the one that he is referring to. In my rearrangements, I deliberately erased the latitude variable, and changed your gotolonglat() function (that I called around() ): now it has only three parameters: body, pos (an angle value) and r (radius). (Don added the 'upvector' one, which was meant to be global)
I did it in order to simplify things, and make the script easier to understand. So in this script there's no need for a separate entry for the 5 factor. It should be placed in the speed_rot expression.
Another change I made is the coordinate system: this version works entirely in UCS, hence the viewer does not rotate around Earth on a local "equatorial" plane (planetographic, I should say), but on the UCS "horizontal" plane.


Bye

Posted: 28.04.2004, 00:19
by don
I think I have a partial handle on this simple example, but not *knowing* deep inside how to handle rotational math (sine, cosine, etc.) I'm afraid I'm at the mercy of you expert folks.

Here is a slightly modified version of the earlier script, that allows the script user to select a frame of reference before running the script...

Code: Select all

--****************************************************************************
--
--  Original script: "Around the Earth" by Harald Schmidt
--      Modified by: Toti
--      Modified by: Don G.
--
--  Allows you to select a frame of reference (coordinate system) and then
--  orbits the Earth along the Y / horizontal axis of that frame.
--
--****************************************************************************

-- This function places the viewer at longitiude on myObject at distance
-- from the object's center, in the defined frame of reference...
   function newPosition(myObject, longitude, distance, relativeUpvector,
                        viewerFrameOfRef, objCenter)

     -- These two equations look rather complicated, but they are only a 2-D
     -- polar-cartesian conversion. Given distance and longitude, convert them
     -- to X and Z values...
     local x = distance * math.cos(math.rad(longitude))
     local z = distance * math.sin(math.rad(longitude))

     -- Keep the "up" pointing coordinate on the "horizontal" plane. Recall
     -- that we selected Y as the 'upvector' earlier...
     local y = 0

     -- Create a vector for the above coordinates...
     local offsetVector = celestia:newvector(x, y, z)

     -- Define the viewer position by adding the offsetVector to the vector at
     -- the center of myObject, converted to the desired frame of reference...
     local viewerPositionVector =
           viewerFrameOfRef:from( viewerFrameOfRef:to(objCenter)
           + offsetVector )

     -- Place the observer at that position...
     local obs = celestia:getobserver()
     obs:setposition (viewerPositionVector)

     -- Look at the object, centered on the screen, with the upvector pointing
     -- towards the "ceiling". This is complex ...
     -- Close your hand. Extend your thumb. Then extend the first finger, and
     -- point it to this -> x. (Keep the angle between both fingers fixed.)
     -- This behaves like the viewer: your thumb is the 'up' axis. Your first
     -- finger is the viewer's 'look_at' axis. When you point it to the 'x',
     -- you are transforming the 'up' vector (your thumb moves with your hand),
     -- but it doesn't matter, because you have already defined where it is in
     -- relation with the rest of your hand. I believe that Celestia does this
     -- same thing that you have done with your hand: First creates an
     -- "abstract" viewer, then defines where is 'up' and last, moves/rotates
     -- the viewer to point its "camera" to the selected object. The 'up' vector
     -- signals the viewer's "ceiling" BEFORE Celestia rotates it in order to
     -- look at some place.
     obs:lookat(objCenter, relativeUpvector)

     -- Display the viewer's position...
     celestia:flash("Viewer position...\nX: " ..
       string.format("%014.9f", viewerPositionVector.x) .. "\nY: " ..
       string.format("%014.9f", viewerPositionVector.y) .. "\nZ: " ..
       string.format("%014.9f", viewerPositionVector.z) )

     -- Return control to Celestia for a moment (to refresh screen, etc.)...
     wait(0.0)
   end


--****************************************************************************
--                           Script Body Code
--****************************************************************************
  -- Select an object...
  local myObject = celestia:find("Sol/Earth")

  -- Define a frame of reference (coordinate system) by uncommenting a line...
  local viewerFrameOfRef = celestia:newframe("planetographic", myObject)
--  local viewerFrameOfRef = celestia:newframe("ecliptic", myObject)
--  local viewerFrameOfRef = celestia:newframe("observer", myObject)
--  local viewerFrameOfRef = celestia:newframe("universal", myObject)
--  local viewerFrameOfRef = celestia:newframe("equatorial", myObject)

  -- Obtain a position vector for the center of myObject...
  local objCenter = myObject:getposition()

  -- Define which direction we should call 'up'. Since this is only a rough
  -- direction, we can use a 'common vector'. This selects Y as up...
  local upvector = celestia:newvector (0, 1, 0)

  -- Convert objCenter to our selected coordinate system...
  local objCenterOurCS = viewerFrameOfRef:to(objCenter) + upvector
  local relativeUpvector = objCenter:vectorto(viewerFrameOfRef:from(objCenterOurCS))

  -- Define how many seconds a single orbit (360 degrees) should take...
  local orbitTime = 45

  -- Define how far we move (angular speed) each second...
  local degreesPerSecond = 360 / orbitTime

  -- Define the viewer's distance from the object's center (in au's?)...
  local distanceFromObjCenter = 0.004

  -- Loop for the time specified in orbitTime...
  local scriptTime = 0
  while scriptTime < orbitTime do
    scriptTime = celestia:getscripttime()

    -- Compute longitude movement as a function of time...
    local longitude = degreesPerSecond * scriptTime

    -- Call the function...
    newPosition(myObject, longitude, distanceFromObjCenter, relativeUpvector,
                viewerFrameOfRef, objCenter)
  end

Posted: 28.04.2004, 03:16
by Toti
Don:
That's a fine modification :)

A little further explanation:

Code: Select all

-- Keep the "up" pointing coordinate on the "horizontal" plane. Recall
-- that we selected Y as the 'upvector' earlier...
local y = 0

This selects your position on the axis that points in your current CS 'up' direction. (for example: if the CS is "planetographic" this will describe motion along the planet's N-S axis). Y= 0 means that there is no motion along this axis.

Code: Select all

-- Define which direction we should call 'up'. Since this is only a rough
-- direction, we can use a 'common vector'. This selects Y as up...
local upvector = celestia:newvector (0, 1, 0)

When you create your viewer/camera, you need to define which part of it goes "up". This takes care of it.


On the circle trajectory:
sin(angle) and cos(angle) always return values in the range [-1..1]. They are hard-bounded functions so no matter how big is angle they won't outpass those limits.
In addition, you know that the trajectory:

Code: Select all

x = distance * cos(angle)
z = distance * sin(angle)

draws a circle of radius = 'distance'. It's drawn in what it's called the XZ plane, that's the plane that contains both the X and the Z coordinates. The Y coordinate cuts this plane at a 90 degree angle (it's perpendicular)
So, X will get values in the [-distance..distance] range. Same for Z.
Now imagine that distance = 1.
Then the vector 'offset' (you call it 'offsetVector') will draw a circle of radius 1.
If 'objCenter' (as you called it) is equal to (0,0,0) then the circle will be at "universal" frame's origin: a circle of radius 1 centered at (0,0,0) UCS. I think that distances are in microLightYears, so that should be a circle of about 9.5 million km.
The script sets the distance value in about 0.004, so when this multiplies the sin() and cos() functions, the circle is reduced to 0.004 mLY radius (that's about 37500 km).
(Remember that the distance readout that Celestia prints in blue letters is relative to the surface, hence the difference)
Of course, if 'objCenter' is not (0,0,0), the circle will have the same size, but it will be centered in another point.

A little more on the offset expression:
offset = celestia:newvector (r * sin(pos), r * cos(pos, 0)
is a parameterized vectorial trajectory. It has the same form of a vector (A,B,C), but here A, B and C are functions instead of simple numbers. So you give an argument to the functions and each one returns you a number: the tree numbers form a vector (hence the name vectorial trajectory).
In the above example of the circle trajectory, there are two parameters: a radius (r, constant) and an angle (pos, variable). In fact, because r is constant, we can consider this trajectory as only having one "true" parameter: pos.
Replacing r and pos with more complex expressions, you can build different curves: some of them can be very interesting:

the default circle:

Code: Select all

--the default circle
local x = r * math.cos(math.rad(pos))
local z = r * math.sin(math.rad(pos))
local y = 0


If we replace r with r * some number > 1, we get a bigger circle:

Code: Select all

--a greater radius circle:
local x = r * 2.71182818 * math.cos(math.rad(pos))
local z = r * 2.71182818 * math.sin(math.rad(pos))
local y = 0


Raising the above expression for radius to a positive, variable power gives an upward spiral:

Code: Select all

-- a growing radius circle (an "upward logarithmic spiral")
local x = r * 2.71182818^(0.8 * pos/360) * math.cos(math.rad(pos))
local z = r * 2.71182818^(0.8 * pos/360) * math.sin(math.rad(pos))
local y = 0


And raising it to a negative, variable power gives a downward spiral:

Code: Select all

--a shrinking radius circle (a "downward logarithmic spiral")
local x = 15 * r * 2.71182818^(- 0.8 * pos/360) * math.cos(math.rad(pos))
local z = 15 * r * 2.71182818^(- 0.8 * pos/360) * math.sin(math.rad(pos))
local y = 0


We can also give independent radius values to X and Z, and we get an ellipse:

Code: Select all

-- a constant speed ellipse:
local x = r * math.cos(math.rad(pos))
local z = 0.3 * r * math.sin(math.rad(pos))
local y = 0


And with the same arrangement as before, but replacing Z with a constant, we get a fluctuating movement:

Code: Select all

-- a constant speed oscillation:
local x = r * math.cos(math.rad(pos))
local z = 0.3 * r
local y = 0


Finally, we can accelerate the above movement:

Code: Select all

-- an "accelerated oscillation:
local x = r * math.cos(math.rad(1.5^(pos/360) * pos))
local z = 0.3 * r
local y = 0


All this movements are on the current reference frame "horizontal" (y = 0) plane. Of course, movement in the "vertical" direction (Y coordinate) can add a lot of variation to the final result. Try replacing Y = 0 with some functions to see the result.
On the internet you can find parametric equations for a lot of curves: cardioids (ie: heart-shaped) curves, conical sections (ie: parabolic, hyperbolic, elliptic curves), more spirals, astroids (ie: star-shaped), lemniscates, Lissajous curves, etc.

Bye

Posted: 29.04.2004, 07:10
by don
Toti, thank you for the added explanation and variations. I could spend hours reading through all the parametric equation web sites and playing with the interactive Java applets. Not to mention playing with the x, y, and z values of this Celx example. Amazing stuff! 8O

Posted: 30.04.2004, 00:20
by Toti
don wrote:Toti, thank you for the added explanation and variations.

You are welcome. I hope that they can make this part of the problem a bit easier to understand.

A little more: this script moves the user in a straight line, while looking at the travel direction:

Code: Select all

startPos   = celestia:newposition(513,8233,634)
flyDir   = celestia:newvector(3,4,2)

upDir      = celestia:newvector(0,1,0)

obs      = celestia:getobserver()

t      = 0
repeat
   -- to fully understand this, I recommend you to draw this vectors and learn about the "parallelogram law" for adding them
   -- it's a very simple rule that makes easy to understand vector addition:
   newObsPos   = startPos + (t * flyDir)
   newViewDir   = startPos + ((t+1) * flyDir)
   obs:setposition(newObsPos)
   obs:lookat(newViewDir,upDir)
   t   = t + 200000
   wait()
until 1 == 2


Bye

Posted: 30.04.2004, 06:23
by don
Thank you Toti. More fun code to play with! :D

I'll get serious in a day or two ... maybe ... and actually *use* some of this new code to try different things.

Posted: 02.05.2004, 01:43
by Toti
This variation of the above script gives us a more interesting travel.
The flyDir vector is slightly rotated each time, so the direction of movement is constantly changing. The viewer always looks in the direction of movement.

Code: Select all

--**********************************************************************
--            rotation functions
--**********************************************************************
-- those functions rotate vectors on an specified axis (they are the result of aplying a rotation matrix to the vector)
-- you can forget about the details of these (just formulae)

function rotateX (v, ang)
   local t = math.rad (ang)
   local c = math.cos(t)
   local s = math.sin(t)
   local u = celestia:newvector(v.x, c*v.y+s*v.z, -s*v.y+c*v.z)
   return u
end

function rotateY (v, ang)
   local t = math.rad (ang)
   local c = math.cos(t)
   local s = math.sin(t)
   local u = celestia:newvector(c*v.x-s*v.z, v.y, s*v.x+c*v.z)
   return u
end

function rotateZ (v, ang)
   local t = math.rad (ang)
   local c = math.cos(t)
   local s = math.sin(t)
   local u = celestia:newvector(c*v.x+s*v.y, -s*v.x+c*v.y, v.z)
   return u
end

--**********************************************************************
--               the script
--**********************************************************************
speed      = 200000
a      = 0.05      

startPos   = celestia:newposition(513,8233,634)

-- if you uncomment these lines, you will be placed near Jupiter. If the 'offset' vector is not added to startPos,
-- you will be placed at Jupiter's center:
--offset   = celestia:newvector(0.03,0.03,0.00)
--startPos   = celestia:find("Sol/Jupiter"):getposition() + offset
--speed   = 0.00007

flyDir   = celestia:newvector(-1,-1,0)   
-- because this is a direction, a large vector and a short one will do the same: let's resize this to length=1 (so the
-- speed factor will have more physical sense)
flyDir:normalize()

upDir      = celestia:newvector(0,1,0)

obs      = celestia:getobserver()

newObsPos   = startPos
repeat
   -- let's rotate 'flyDir' the same angle along each axis.
   -- you can try adding two new variables (b,c) in order to make independent
   -- rotations on each axis:
   flyDir   = rotateY(flyDir,a) 
   flyDir   = rotateX(flyDir,a)
   flyDir   = rotateZ(flyDir,a)

   -- this is the same as in the original script, but shorter:
   newObsPos   = newObsPos + (speed * flyDir)
   newViewDir   = newObsPos + flyDir
   obs:setposition(newObsPos)
   obs:lookat(newViewDir,upDir)
   wait()
until 1 == 2


Bye

Posted: 03.05.2004, 05:22
by don
Howdy Toti,

Wow, that does indeed provide some very different views. I had to slow it down quite a bit since I use a flat-panel screen (slow refresh rates compared to CRTs). I changed two values:

speed = 70000 (from 200000)
a = 0.005 (from 0.05)

... and it looks very nice.

I hope to play with some of this code this week, to get a better first-hand understanding of it and to see what I can come up with, if I can even get any "creative juices" flowing <grin>.

Thanks for the great education Toti!

Posted: 04.05.2004, 03:15
by Toti
don wrote:I had to slow it down quite a bit since I use a flat-panel screen (slow refresh rates compared to CRTs). I changed two values:

speed = 70000 (from 200000)
a = 0.005 (from 0.05)

... and it looks very nice.
Also, for simplicity reasons, the above script doesn't include any time control mechanism (it's just a vector manipulation example), so motion speed directly depends on CPU speed.

don wrote:I hope to play with some of this code this week, to get a better first-hand understanding of it and to see what I can come up with

Since you are a programmer, you just need to familiarize yourself with vector essentials:
*] vector addition: see the most intuitive parallelogram's rule
*] vector substraction: the result is a vector connecting the tips of the operand vectors (that you must mentally transport (without changing its orientation) so it starts at the same origin as them)
*] vector multiplication by a number k: this extend the vector's length ( k>1 or k<-1) or contracts it ( 0<k<1 or -1<k<0).
If k < 0 the vector's orientation is inverted (now it goes in the opposite direction)

Which reminds me of the earth-orbiting script, but now with an equatorial orbit:

Code: Select all

function pos2vect(p)
-- converts a position into a vector
   local earth      = celestia:find("Sol/Earth")
   local earthPos   = earth:getposition()
   local frame      = celestia:newframe("planetographic", earth)
   local zero      = frame:to(earthPos)       -- this is (0,0,0)
   return p - zero                  -- substracting a NULL position from another position,
                           -- converts p into a vector with the "same" value as p
end


function vect2pos(v)
-- converts a vector into a position
   local earth      = celestia:find("Sol/Earth")
   local earthPos   = earth:getposition()
   local frame      = celestia:newframe("planetographic", earth)
   local zero      = frame:to(earthPos)       -- this is (0,0,0)
   return v + zero                  -- adding v to a NULL position, converts a vector into a position
                           -- with the same value as v
end


function around(body, pos, r)
-- moves around body
   -- This one is a simple circular trajectory (given by X and Z).
   -- Y=0 avoids the viewer's motion in the vertical direction:
   local x = r * math.cos(math.rad(pos))
   local z = r * math.sin(math.rad(pos))
   local y = 0

   -- let's create a vector with those coordinates:
   local offset = celestia:newvector(x,y,z)

   -- 'offset' is a vector, and we need a position, so we convert it:
   rel_pos    = vect2pos(offset)

   -- to understand this, draw a point representing UCS's origin and another point representing our planet.
   -- draw a CS at each one of them (CS = the three small arrows at 90 degrees one from another)
   -- remember to draw the planet's CS inclined.
   -- 'rel_pos' represents our position around UCS's origin (it draws a circle on UCS's horizontal plane (y = 0))
   -- you can draw a vector starting at UCS's origin and ending somewhere in the circle.

   -- this induces Celestia to believe that 'rel_pos' is originally centered at 'body' and oriented like it
   -- so the above y = 0 doesn't mean anymore "in the horizontal plane of UCS" but "in horizontal plane of 'body'
   -- (equatorial plane)"
   local univ_pos   = obs_frame:from (rel_pos)
   
   -- so now, the circle defined by 'rel_pos' has been translated: now it's centered at planet and inclined like it.
   -- that is, now you must draw a vector starting at UCS's origin and ending somewhere in the translated and inclined
   -- circle.
   
   -- then create and place the viewer at this point:
   local obs = celestia:getobserver()
   obs:setposition(univ_pos)

   -- inform the viewer about his position:
   celestia:print("Viewer's position: \n(body's geographical system): " .. rel_pos.x .. "  " .. rel_pos.y .. "  " .. rel_pos.z .."\n(universal coordinate system): " .. univ_pos.x .. "  " .. univ_pos.y .. "  " .. univ_pos.z, 2, -1, -1)

   -- we will look at body's center:
   obs:lookat(bodyPos, upVec_rel)

   -- return control to Celestia (for screen refresh purposes):
   wait(0.0)
end



-- let's define the body of interest:
body      = celestia:find("Sol/Earth")

-- let's get its position in UCS
bodyPos   = body:getposition()

-- let's create a new (aligned with body's equator) coordinate system (CS):
obs_frame   = celestia:newframe("planetographic", body)


-- this is the trickiest part: if you do not catch it at first, don't worry
-- we need to define the 'up' direction of our camera. Place yourself at UCS's origin. If you define 'up' like a vector
-- in the direction of earth, then the top of your head will be oriented towards earth.
-- in this particular script, we want the top of our head/camera to be aligned like the N-S axis of earth (parallel to it)
-- start pointing the 'top' of our camera along the N-S axis of UCS
upVector   = celestia:newvector(0,1,0)
-- let's convert it to a position. (because we can't do 'vector' CS conversions):
upPos      = vect2pos(upVector)
-- now 'upPos' is a position vector. Now we will align it with earth's N-S axis.
-- with this instruction, we make Celestia believe that it starts in earth and is aligned like it.
upVec_rel   = obs_frame:from(upPos)
-- but remember, this position starts at earth's center. we need it to start at UCS's origin. If not, the top of our UCS's
-- center-placed head will be aligned with some point near earth, which is not what we want. This "translation" is done
-- in the next step
upVec_rel   = upVec_rel - bodyPos
-- also, because we substracted two positions, we obtained a vector. This is still oriented along N-S axis of body, but
-- it is placed in UCS's origin.


-- The best way to understand what we have done here is drawing it: one point for UCS's origin. another for body.
-- From UCS's origin draw a small "vertical" arrow: our 'upvector', and an arrow that connects with 'body': our 'bodyPos'
-- vector. Now let's assume that 'upvector' was converted to body's CS. Draw it: an N-S inclined arrow starting from 'body':
-- this is our 'upPos' position vector.
-- Now convert 'upPos' to UCS: this will be a vector starting at UCS's origin and ending at the tip of 'upPos'. Again, draw it.
-- Now let's substract: 'upPos' - bodyPos. This will be an arrow starting at 'body' and ending at the tip of 'upPos'.
-- Now "translate" it to UCS's origin without changing its inclination.
-- This is upVec_rel, parallel to 'body' N-S axis and starting at UCS's origin.


-- this was already explained:
t0 = 0
t1 = 60
rot_speed = 360 / t1
-- this is a small modification. Now you can watch all planets with the same apparent diameter on screen:
R = body:radius() / 1000000
while t0 < t1 do
  t0 = celestia:getscripttime()
  a = rot_speed * t0 * 5
  around(body, a, R)
end 



Bye :)

Posted: 06.05.2004, 04:58
by don
Thank you Toti. Can't seem to find a long enough time slot to be able to load, look at and run scripts right now, let alone playing with the code. Been a busy week with other things. :( ... Hope to play with it real soon.

Posted: 07.05.2004, 00:13
by don
Hi Toti,

Thanks for the script modifications to study. :)

Since Lua has built-in number formatting, like C, I changed the print statement so it doesn't "jitter" so much...

Code: Select all

   -- inform the viewer about his position:
   celestia:print("Viewer's position: \n(body's geographical system): " ..
     string.format("%014.9f", rel_pos.x) .. "  " ..
     string.format("%014.9f", rel_pos.y) .. "  " ..
     string.format("%014.9f", rel_pos.z) ..
     "\n(universal coordinate system): " ..
     string.format("%014.9f", univ_pos.x) .. "  " ..
     string.format("%014.9f", univ_pos.y) .. "  " ..
     string.format("%014.9f", univ_pos.z), 2, -1, -1)

Thanks to you and Harald for your examples. I will try my best to play with vectors on my own and see what I can come up with. :D