The below code is for a function I created some time ago called
travel_to. It is definitely over-powered for what you want to do, but it will position your observer as you indicated with only a few parameter settings. Unfortunately, I am pressed for time at the moment and can't take the time to write out a smaller code snippet.
Once you include the function you will be able to position the observer to view the sunlit portion of Mars as follows:
Code: Select all
MARS = celestia:find("Sol/Mars")
travel_to{ body=MARS, location="over subsolar point"}
Look at the comments in the function for how to adjust the distance from the planet (e.g. try changing location to
- "above subsolar point"
- "viewing subsolar point"
- ".5AU above subsolar point"
- "2RADII above subsolar point"
- "5000KM above subsolar point"
You could also play with the duration parameter to speed up/slow down the trip:
Code: Select all
travel_to{ body=MARS, location="over subsolar point", duration=20}
You may also want to make sure you are facing Mars:
Code: Select all
travel_to{ body=MARS, location="over subsolar point", duration=20, tracking=MARS}
or
Code: Select all
travel_to{ body=MARS, location="over subsolar point", duration=20, final_tracking=MARS}
The code for the function is:
Code: Select all
--[[ Move observer to a given position using the parameters supplied as the table travel_parms. Items in travel_parms
are:
adjust_time - Determines if travel_to() will use a shorter duration when the start and ending positions are close
to each other.
e.g. travel_to({ body=EARTH, duration=60, tracking=EARTH });wait(10)
travel_to({ body=EARTH, duration=60, tracking=EARTH });wait(10)
travel_to({ body=EARTH, duration=60, tracking=EARTH, adjust_time=false });wait(10)
the second travel_to() only has to go a few km and will use a shorter value for duration.
the third travel_to() also has to go a few km, but will take a full 60 seconds to do so.
default: true
at - The time (specified in julian days) that the simulation should be set to when the trip completes.
default: the (current time + duration) * current timescale
body - String or object indicating the body that placement will be relative to.
default: the point indicated by the universal coordinates 0, 0, 0.
calc_only - If present and set to true, then travel_to does not move the observer. Instead the location that
the observer would be moved to is calculated and returned as a position.
default: false.
location - Text description of where the observer will be placed. May contain any of the following locales:
"covisual" - A position 10 degrees from the subcynthion antipodal point and 9 radii from the
body, from which it is possible to view both the body and the reference_body
(within a standard field of view of approximately 24 degrees).
"pole" - The approximate north pole of the body. Note: a known bug in Celestia causes this
point to be calculated as vertical to the ecliptic, not the equator. This means
that "pole" points to a point on the body's "arctic circle".
"subcynthion" - The point on the surface of the body where the reference_body will appear at
zenith. "subcynthion" is my own term derived from "subpolar point" (the point on
Earth directly under the Sun) and "pericynthion" (the closest point in an object's
orbit around an artificial body [the closest term I could find])
"subsolar" - Same as "subcynthion". Provided for convienience since the most common usage of
"subcynthion" will be a reference to the "subsolar point".
"terminator" - A position 89 degrees from the line between the body and the reference_body.
Since the default location_reference will usually be a star (the Sun), this will
usually place the observer over the day-night terminator.
"longlat x,y" - A position at longitude x, latitude y on the surface of the body. Note: x any y
are given in degrees, may be negative and may include decimals. x and y must be
seperated only by a comma - no spaces are allowed.
and any combination of the following modifiers:
"above" - The observer will be placed 1.1 radii from the body.
"antipodal" - Rotates the vector from the body to the observer by 180 degrees.
"antipous" - The same as "antipodal" ("anitpous" is the singular of "antipodes").
"diagonal" - Rotates the vector from the body to the observer by 45 degrees.
"from" - Ignored. Provided to allow expressions such as "viewing terminator from 5AU".
"left" - Indicates that rotation of the vector from the body to the observer should be
"left-handed" (or clockwise as seen from 'above') (e.g. "over left terminator"
places the observer over the object's leading terminator (for prograde orbits).
"over" - The observer will be placed 6 radii from the body.
"point" - Ignored. Provided to allow expressions such as "over subsolar point" and
"diagonal to subcynthion point".
"right" - Indicates that rotation of the vector from the body to the observer should be
"right-handed" (or counter-clockwise as seen from 'above') (c.f. "left").
"to" - Ignored. Provided to allow expressions such as "diagonal to terminator".
"viewing" - The observer's distance from the body will be 98% of the distance to the
reference_body.
Note: "over" and "above" are ignored if "viewing" is used.
"xAU" - Where x is an number, the observer will be placed x astronomical units from body.
"xKM" - Where x is an number, the observer will be placed x kilometres from body.
"xRADII" - Where x is an number, the observer will be placed x times the radius of the body
from body.
Note: "over", "above" and "viewing" become ignored words if "xAU", "xKM" or "xRADII" are used.
Defaults are "subsolar", "right" and "over".
reference_body -
- a second body which is used in calculating some locations (i.e. "terminator", "subcynthion").
default: the primary of the system to which body belongs, or Sol if body is nil
position - a position (in universal coordinates) to which the observer will be moved.
Note: if position is specified, then body, location, and reference_body cannot be used.
observer - indicates which observer should travel.
default: the current observer.
duration - the number of seconds this trip will take.
default: 10.
initial_tracking
- a body to be centred and remain centred on the screen at the start of the trip. Note that up to
1/10 of the trip time or 3 seconds (whichever is less) may be devoted to turning the observer
towards the body before the actual movement begins. The body will remain centred until the
time specified by start_interpolation is reached.
initial_tracking cannot be used with tracking or initial_orientation.
default: no tracking. The observer remains in its initial orientation until the
start_interpolation time is reached.
final_tracking
- a body to be centred and remain centred on the screen during the latter portion of the trip. The
observer will begin turning toward the body once the start_interpolation time is reached and be
fully turned when the end_interpolation time is reached.
final_tracking cannot be used with tracking or final_orientation
default: no tracking. The observer remains in the same orientation it was in at the
start_interpolation time.
tracking - a body to be used as both the initial_tracking body and the final_tracking body.
tracking cannot be used with initial_tracking, initial_orientation, final_tracking or
final_orientation.
start_interpolation and end_interpolation are ignored when tracking is specified.
final_up - a vector indicating which direction will be "up" (towards the top of the screen) when then journey
is complete. final_up cannot be used if final_orientation is given.
default: "up" for the initial_orientation
initial_orientation
- the orientation the observer should have at the start of the trip. Note that up to 1/10 of the trip
time or 3 seconds (whichever is less) may be devoted to truning the observer this orientation. This
orientation will be maintained until the time specified by start_interpolation is reached.
initial_orientation cannot be used with tracking or initial_tracking.
default: the current orientation for the observer.
final_orientation
- the orientation the observer should have at the end of the trip. The observer will begin turning
toward the final_orientation once the start_interpolation time is reached and be fully turned when
the end_interpolation time is reached.
final_orientation cannot be used with tracking, final_tracking or final_up
default: no orientation change. The observer remains in the same orientation it was in at the
start_interpolation time.
start_interpolation
- the point in the trip (expressed as a percent) at which the observer should begin turning from the
the initial_orientation or intial_tracking body to the final_orientation or final_tracking body.
default: 0.25
end_interpolation
- the point in the trip (expressed as a percent) at which the observer should finish turning from the
initial_orienation or intial_tracking body and now fully face the final_orientation or
final_tracking body.
default: 0.75
accelTime - indicates (as a percentage) how much of the trip should be spent accelerating away from the initial
location. Also represents the amount of time that will be spent decelerating towards the final
location. The remainder of the trip is spend cruising.
default: 0.25. minimum: 0.01 maximum 0.5
jerk - indicates whether acceleration and deceleration should be at constant value (jerk=false) or at
an accelerating amount (jerk=true). Jerk is the term for the rate of change of acceleration.
default: true
The following constants may also be defined before calling travel_to():
TRAVEL_TO_DURATION: when specified, this becomes the default value for travel_parms.duration.
TRAVEL_TO_JERK: when specified, this becomes the default value for travel_parms.jerk
Note: control is not returned until the trip is complete, so wait() is not necessary with travel_to() ]]
function travel_to(travel_parms)
-- obs = which of the open observers is to be moved
local obs = travel_parms.observer
if obs == nil then
obs = celestia:getobserver()
elseif not(obs:isvalid()) then
obs = celestia:getobserver()
end
-- travel_time = seconds spent moving from start position to end position
local travel_time = travel_parms.duration
if travel_time == nil then
travel_time = TRAVEL_TIME_DURATION
if travel_time == nil then
travel_time = 10
end
end
-- endtime = simulation time once the journey is complete
local endtime = travel_parms.at
if endtime == nil then
endtime = obs:gettime() + travel_time * celestia:gettimescale() / SEC_PER_JDAY
end
-- initial_tracking = which object is to be tracked at the start of the journey
-- final_tracking = which object is to be tracked at the end of the journey
-- start_interpolation = when to start switching from intial_tracking/_orientation to final_tracking/_orientation
-- end_interpolation = when to complete switching from intial_tracking/_orientation to final_tracking/_orientation
-- centre_time = time devoted to reorienting the observer to the proper initial orientation
local initial_tracking = nil
local initial_orientation = nil
local final_tracking = nil
local final_orientation = nil
local start_interpolation = .25
local end_interpolation = .75
local centre_time = 0
local vpixels_per_sec = 200 -- max turn rate while reorienting
local radians_per_sec = 1 -- max roll rate while reorienting
local final_up = travel_parms.final_up
if final_up ~= nil and travel_parms.final_orientation ~= nil then
celestia:flash("Illegial combination of parameters: final_up may not be used with final_orientation", 60);wait(60)
error(1)
end
if travel_parms.tracking == nil then
initial_tracking = travel_parms.initial_tracking
if initial_tracking == nil then
initial_orientation = travel_parms.initial_orientation
elseif travel_parms.initial_orientation ~= nil then
celesita:flash("Illegial combination of parameters: initial_orientation may not be used with initial_tracking",
60);wait(60);error(1)
end
final_tracking = travel_parms.final_tracking
if final_tracking == nil then
final_orientation = travel_parms.final_orientation
elseif travel_parms.final_orientation ~= nil then
celesita:flash("Illegial combination of parameters: final_orientation may not be used with final_tracking",
60);wait(60);error(1)
end
else
-- tracking means we are to track one object through the entire journey
if travel_parms.initial_tracking ~= nil or travel_parms.final_tracking ~= nil then
celestia:flash("Illegial combination of parameters: initial_/final_tracking may not be used with tracking", 60)
wait(60);error(1)
elseif travel_parms.start_interpolation ~= nil or travel_parms.end_interpolation ~= nil then
celestia:flash("Illegial combination of parameters: start_/end_interpolation may not be used with tracking",
60);wait(60);error(1)
elseif travel_parms.initial_orientation ~= nil or travel_parms.final_orientation ~= nil then
celestia:flash("Illegial combination of parameters: initial_/final_orientation may not be used with tracking",
60);wait(60);error(1)
end
-- set the tracking object as both the intial_tracking and final_tracking objects
initial_tracking = travel_parms.tracking
final_tracking = travel_parms.tracking
-- since we are tracking a single object, if there is no final_up parameter then there is no need for an
-- interpolation phase
if final_up == nil then
start_interpolation = 0
end_interpolation = 0
end
end
-- goto_position = destination is a specific set of coordinates in space in the universal frame of reference
local goto_position = travel_parms.position
if goto_position == nil then
goto_position = celestia:newposition(0, 0, 0)
elseif travel_parms.body ~= nil then
celestia:flash("Illegial combination of parameters: body may not be used with position", 60);wait(60);error(1)
elseif travel_parms.location ~= nil then
celestia:flash("Illegial combination of parameters: location may not be used with position", 60);wait(60);
error(2)
elseif travel_parms.reference_body ~= nil then
celestia:flash("Illegial combination of parameters: reference_body may not be used with position", 60);
wait(60);error(3)
end
-- body = destination will be a point located relative to the given body
-- body_radius = size of the body
-- body_up = direction from the body's centre to its north pole
local body = travel_parms.body
if type(body) == "string" then
body = celestia:find(body)
end
local body_radius, body_up
if body == nil then
body_radius = 0
body_up = celestia:newvector(0, 1, 0)
else
goto_position = body:getposition(endtime)
body_radius = body:radius()
body_up = celestia:newframe("equatorial", body):from(celestia:newposition(.001, 1, 0), endtime) -
goto_position
end
-- reference_position = coorinates of a second body sometimes needed to specify the desired destination
local reference = travel_parms.reference_body
local reference_position
if type(reference) == "string" then
reference = celestia:find(reference)
elseif reference == nil then
if body == nil then
reference = celestia:find("Sol")
else
reference = find_system_parent(body)
if body == reference then
reference = nil
end
end
end
if reference == nil then
reference_position = celestia:newposition(0, 0, 0)
else
reference_position = reference:getposition(endtime)
end
-- parse the locale and modifiers
local action = ""
local angle = 0
local angle_factor = 1
local distance_factor = 6
local viewing = false
local viewing_AU = goto_position:distanceto(reference_position) * .98 / KM_PER_AU
local ignore_tokens = { from=0, over=0, point=0, right=0, subcynthion=0, subsolar=0, to=0 }
local token, token_value
local token_is_longlat = false
if travel_parms.location ~= nil then
for token in string.gfind(travel_parms.location, "([%w%.,%-]+)%s*") do
token = string.lower(token)
token_value = tonumber(string.sub(token, 1, -3))
if token_is_longlat then
for long, lat in string.gfind(token, "([%d%.%-]+),([%d%.%-]+)") do
local distance = reference_position:distanceto(goto_position)
local x = - distance * math.cos(math.rad(long)) * math.cos(math.rad(lat))
local y = distance * math.sin(math.rad(lat))
local z = distance * math.sin(math.rad(long)) * math.cos(math.rad(lat))
reference_position =
celestia:newframe("planetographic", body):from(celestia:newposition(x, y, z), endtime)
end
token_is_longlat = false
elseif token == "above" then
distance_factor = 1.1
elseif token == "antipodal" or token == "antipous" then
angle = angle + 180
elseif token == "covisual" then
angle = angle + 173
distance_factor = 9
elseif token == "diagonal" then
angle = angle - 45
elseif token == "left" then
angle_factor = -1
elseif token == "longlat" then
token_is_longlat=true
elseif token == "pole" then
if body == nil then
celestia:flash("Illegial location: pole without body\in: " .. travel_parms.location, 60);wait(60);
error(2)
end
local distance = reference_position:distanceto(goto_position)
reference_position =
celestia:newframe("planetographic", body):from(celestia:newposition(.001, distance, 0), endtime)
elseif token == "terminator" then
angle = angle - 89
elseif token == "viewing" then
viewing = true
elseif string.sub(token, -2) == "au" and token_value ~= nil then
viewing = true
viewing_AU = token_value
elseif string.sub(token, -2) == "km" and token_value ~= nil then
viewing = true
viewing_AU = token_value / KM_PER_AU
elseif string.sub(token, -1) == "r" and tonumber(string.sub(token, 1, -2)) ~= nil then
viewing = true
viewing_AU = body_radius * tonumber(string.sub(token, 1, -2)) / KM_PER_AU
elseif ignore_tokens[token] == nil then
celestia:flash("Unknown location word: " .. token .. "\nin: " .. travel_parms.location, 60);wait(60);
error(3)
end
end
end
-- calculate the endposition: coordinates to travel to
local vector_endpoint_to_reference = reference_position - goto_position
local normalised_vector_endpoint_to_reference = vector_endpoint_to_reference:normalize()
local vector_from_endpoint_towards_reference
if viewing then
vector_from_endpoint_towards_reference = viewing_AU / AU_PER_uLY * normalised_vector_endpoint_to_reference
else
vector_from_endpoint_towards_reference =
(body_radius * distance_factor) / KM_PER_uLY * normalised_vector_endpoint_to_reference
end
local desired_rotation = celestia:newrotation(body_up, math.rad(angle * angle_factor))
local endposition = goto_position + desired_rotation:transform(vector_from_endpoint_towards_reference)
-- calc_only = don't travel, just return the destination coordinates
if travel_parms.calc_only == true then
return endposition
end
-- intial_tracking = object to watch as we start the journey
if initial_tracking ~= nil then
if type(initial_tracking) == "string" then
initial_tracking = celestia:find(initial_tracking)
end
end
-- determine what direction we should face at the start of the journey, and how long to devote to turning to that
-- direction
if initial_tracking ~= nil or initial_orientation ~= nil then
-- shave up to 3 seconds or 10% off the the total travel time to spend turning to the initial_tracking object or
-- initial_orientation
local look_vector = celestia:newvector(0,0,-1)
local current_look_vector = (obs:getorientation():transform(look_vector)):normalize()
local current_up_vector = (obs:getorientation():transform(UP)):normalize()
local desired_look_vector
local desired_up_vector
if initial_tracking == nil then
desired_look_vector = (initial_orientation:transform(look_vector)):normalize()
desired_up_vector = (initial_orientation:transform(UP)):normalize()
else
desired_look_vector = (initial_tracking:getposition() - obs:getposition()):normalize()
desired_up_vector = current_up_vector
end
-- see if we are looking in the desired direction, if not, determine how much we have to turn and how long
-- to allow in doing so
local screenh, screenv = celestia:getscreendimension()
if (current_look_vector - desired_look_vector):length() > 1e-8 then
local vpixels_from_centre = screenv * math.acos(current_look_vector * desired_look_vector) / obs:getfov()
centre_time = math.min(vpixels_from_centre / vpixels_per_sec, travel_time * 0.1, 3)
end
-- see if we need to roll.
if (current_up_vector - desired_up_vector):length() > 1e-8 then
local radians_to_roll = math.acos(current_up_vector * desired_up_vector)
local roll_time = math.min(radians_to_roll / radians_per_sec, travel_time * 0.1, 3)
centre_time = math.max(centre_time, roll_time)
end
end
-- final_tracking = object to watch as we end the journey
if final_tracking ~= nil then
if type(final_tracking) == "string" then
final_tracking = celestia:find(final_tracking)
end
end
-- recalculate the time to actually be spent travelling
travel_time = travel_time - centre_time
-- if we are already near the endpoint and there is no timetravel involved then shorten the journey time.
if travel_parms.at == nil and travel_parms.adjust_time ~= false then
local tolerance
if body == nil then
tolerance = obs:getposition():distanceto(celestia:newposition(0,0,0)) * .02
else
tolerance = obs:getposition():distanceto(body:getposition()) * .02
end
local travel_distance = obs:getposition():distanceto(endposition)
if travel_distance < tolerance then
local new_travel_time = travel_distance / tolerance
if new_travel_time < travel_time then
travel_time = new_travel_time
end
end
end
-- start applying changes to the observer
-- first, display data about the body
if body ~= nil then
celestia:select(travel_parms.body)
end
-- centre the body to be initially tracked on the screen
if initial_tracking ~= nil then
initial_tracking:preloadtexture()
if initial_tracking:getinfo().parent ~= nil then
-- to load the planet for moons, satellites, etc.
initial_tracking:getinfo().parent:preloadtexture()
end
obs:track(nil)
obs:center(initial_tracking, centre_time)
wait(centre_time)
obs:track(initial_tracking)
end
-- turn to the initial_orientation
if initial_orientation ~= nil and centre_time > 0 then
local start_orientation = obs:getorientation()
local end_reorient_scripttime = celestia:getscripttime() + centre_time
local reorient_percent = 1
while reorient_percent > 0 do
wait(0.0)
reorient_percent = math.max((end_reorient_scripttime - celestia:getscripttime()) / centre_time, 0)
obs:setorientation(initial_orientation:slerp(start_orientation, reorient_percent))
end
end
-- final calculations before we travel...
-- Note: Measurements are made relative to the endstate, not the startstate. This is so that multiplicative
-- errors will reduce to 0 as we approach the endstate, rather than increase as we get farther from the
-- startstate
-- Note: accel/decel math is implemented as follows
-- j: jerk (acceleration of acceleration)
-- a: acceleration ia: initial acceleration
-- s: speed is: initial speed
-- d: distance from origin id: initial distance from origin
-- acceleration at time t:
-- a[t] = j t + ia
-- the antiderivative of acceleration is speed s (not velocity):
-- s[t] = .5 j t^2 + ia t + is
-- the antiderivative of speed is displacement (which I am calling distance for convenience):
-- d[t] = j t^3 / 6 + .5 ia t^2 + is t + id
-- (can someone please check my unused-for-20-years calculus?)
-- since we are considering the observer's current position as the starting point of the journey: id = 0
-- since we are not accounting for the observer's current motion: is = 0
-- therefore:
-- s[t] = .5 j t^2 + ia t + 0 = .5 j t^2 + ia t
-- d[t] = j t^3 / 6 + .5 ia t^2 + 0 t + 0 = j t^3 / 6 + .5 ia t^2
-- D: total travel distance T: total travel time
-- da: total distance spent accelerating ta: total time spent accelerating
-- dc: total distance spent cruising tc: total time spent cruising
-- dd: total distance spend decelerating td: total time spent decelerating
-- d[t]: distance covered by time t
-- da[t]: distance covered in acceleration phase by time t ta[t] = total time spent accelerating at time t
-- dc[t]: distance covered in cruising phase by time t tc[t] = total time spent cruising at time t
-- dd[t]: distance covered in deceleration phase by time t td[t] = total time spent decelerating at time t
-- tc = T - ta - td ; td = ta ; tc = T - 2 ta
-- da = d[ta] = j ta^3 / 6 + .5 ia ta^2
-- dc = cruising speed * time cruising = speed after acceleration * time cruising
-- = s[ta] tc = (.5 j ta^2 + ia ta) tc = .5 j tc ta^2 + ia tc ta
-- dd = d[ta] = j ta^3 / 6 + .5 ia ta^2
-- D = da + dc + dd = j ta^3 / 6 + .5 ia ta^2 + .5 j tc ta^2 + ia tc ta + j ta^3 / 6 + .5 ia ta^2
-- if constant acceleration is to be used ( { accel=true } ), then j = 0 and ia is determined as follows:
-- D = 0 + .5 ia ta^2 + 0 + ia tc ta + 0 + .5 ia ta^2 = ia ta^2 + ia tc ta = ia (ta^2 + tc ta)
-- ia = D / (ta^2 + tc ta)
-- if accelerating acceleration is to be used ( { jerk=true } ), then ia = 0 and j is determined as follows:
-- D = j ta^3 / 6 + 0 + .5 j tc ta^2 + 0 + j ta^3 / 6 + 0
-- = j (ta^3 / 3 + .5 tc ta^2)
-- j = D / (ta^3 / 3 + .5 tc ta^2)
-- when d and t are percentages of D and T then D = 1 and T = 1, therefore
-- tc = 1 - 2 ta
-- ta[t] = min(t, ta)
-- tc[t] = min(max(t - ta, 0), tc)
-- td[t] = max(t - ta - tc, 0) = max(t - ta - (1 - 2 ta), 0) = max(t - 1 + ta, 0)
-- da[t] = j ta[t]^3 / 6 + .5 ia ta[t]^2
-- dc[t] = .5 j tc[t] ta[t]^2 + ia ta tc[t]
-- dd[t] = dd - (j (td - td[t])^3 / 6 + .5 ia (td - td[t])^2)
-- = j ta^3 / 6 + .5 ia ta^2 - j (td - td[t])^3 / 6 - .5 ia (td - td[t])^2
-- = j (ta^3 - (td - td[t])^3) / 6 + .5 ia(ta^2 - (td - td[t])^2)
-- d[t] = da[t] + dc[t] + dt[t] > 0 ? dd[t] : 0
-- when t is changed to measure from 1 (start of journey) to 0, end of journey, then
-- ta[t] = 1 - max(1 - ta, t)
-- td[t] = td - min(td, t) = ta - min(ta, t)
-- tc[t] = 1 - t - ta[t] - td[t]
local accel_percent = travel_parms.accelTime -- = ta
if accel_percent == nil then
accel_percent = .25
end
accel_percent = math.min(math.max(accel_percent, .001), .5)
local cruise_percent = 1 - 2 * accel_percent -- = tc
local jerk, acceleration -- = j, ia
local use_jerk = travel_parms.jerk
if use_jerk == nil then
use_jerk = TRAVEL_TO_JERK
if use_jerk == nil then
use_jerk = true
end
end
if use_jerk then
jerk = 1 / ((accel_percent^3) / 3 + .5 * cruise_percent * accel_percent^2)
acceleration = 0
else
jerk = 0
acceleration = 1 / (accel_percent^2 + cruise_percent * accel_percent)
end
local current_up_vector = (obs:getorientation():transform(UP)):normalize()
local timechange = endtime - obs:gettime()
local end_scripttime = celestia:getscripttime() + travel_time
local travel_vector = obs:getposition() - endposition
local start_interpolation_orientation, end_interpolation_orientation
local time_percent = 1 -- = t
local accel_time_percent, cruise_time_percent, decel_time_percent -- = tat, tct, tdt
local accel_dist_percent, cruise_dist_percent, decel_dist_percent -- = da[t], dc[t], dd[t]
local distance_percent -- = dt
-- end_ and start_interpolation are measured from the start of the journey, change them to measure from the end
end_interpolation = 1 - end_interpolation
start_interpolation = 1 - start_interpolation
-- final rotational calculations
local interpolation_started = false
local interpolation_ended = false
local need_final_up = (final_up ~= nil)
if final_orientation == nil then
if final_tracking == nil or end_interpolation == 1 then
end_interpolation_orientation = obs:getorientation()
else
-- where will we be when it is time to stop turning the observer (ei), and where should the observer point to?
accel_time_percent = math.min(end_interpolation, accel_percent) -- ta[ei]
cruise_time_percent = math.min(math.max(end_interpolation - accel_percent, 0), cruise_percent) -- tc[ei]
decel_time_percent = math.max(end_interpolation - 1 + accel_percent, 0) -- td[ei]
accel_dist_percent = (jerk * accel_time_percent^3) / 6 + .5 * acceleration * accel_time_percent^2 -- da[ei]
cruise_dist_percent = .5 * jerk * cruise_time_percent * accel_time_percent^2 +
acceleration * accel_percent * cruise_time_percent -- dc[ei]
decel_dist_percent = jerk * (accel_percent^3 - (accel_percent - decel_time_percent)^3) / 6 + -- dd[ei]
.5 * acceleration * (accel_percent^2 - (accel_percent - decel_time_percent)^2)
distance_percent = accel_dist_percent + cruise_dist_percent + decel_dist_percent -- dt[ei]
local end_interpolation_position = endposition + travel_vector * distance_percent
local end_interpolation_time
if travel_parms.at == nil then
-- no at given: time change is linear
end_interpolation_time = endtime - timechange * end_interpolation
else
-- with at, time change will accel/cruise/decel
end_interpolation_time = endtime - timechange * distance_percent
end
local final_up_vector = current_up_vector
if need_final_up then
final_up_vector = final_up
need_final_up = false
end
end_interpolation_orientation =
end_interpolation_position:orientationto(final_tracking:getposition(end_interpolation_time),
final_up_vector)
end
if need_final_up then
-- we still need to rotate the final orientation so that final_up will point to the top of the screen
local origin = celestia:newposition(0,0,0)
local look_vector = celestia:newvector(0,0,-1)
local final_look_vector = (end_interpolation_orientation:transform(look_vector)):normalize()
end_interpolation_orientation = origin:orientationto(origin + final_look_vector, final_up)
end
else
end_interpolation_orientation = final_orientation
end
local interpolation_duration = start_interpolation - end_interpolation
-- do the actual travelling
while time_percent > 0 do
time_percent = math.max((end_scripttime - celestia:getscripttime()) / travel_time, 0) -- t
accel_time_percent = math.min(time_percent, accel_percent) -- ta[t]
cruise_time_percent = math.min(math.max(time_percent - accel_percent, 0), cruise_percent) -- tc[t]
decel_time_percent = math.max(time_percent - 1 + accel_percent, 0) -- td[tei]
accel_dist_percent = (jerk * accel_time_percent^3) / 6 + .5 * acceleration * accel_time_percent^2 -- da[t]
cruise_dist_percent = .5 * jerk * cruise_time_percent * accel_time_percent^2 +
acceleration * accel_percent * cruise_time_percent -- dc[t]
decel_dist_percent = jerk * (accel_percent^3 - (accel_percent - decel_time_percent)^3) / 6 + -- dd[t]
.5 * acceleration * (accel_percent^2 - (accel_percent - decel_time_percent)^2)
distance_percent = accel_dist_percent + cruise_dist_percent + decel_dist_percent -- dt[t]
--celestia:flash(string.format("j=%3.3f a=%3.3f\nt[%1.4f] = %1.4f + %1.4f + %1.4f\ndt[%1.4f] = %1.4f + %1.4f + %1.4f",
-- jerk, acceleration,
-- time_percent, accel_time_percent, cruise_time_percent, decel_time_percent,
-- distance_percent, accel_dist_percent, cruise_dist_percent, decel_dist_percent))
obs:setposition(endposition + travel_vector * distance_percent)
if travel_parms.at == nil then
celestia:settime(endtime - timechange * time_percent) -- no at given: time change is linear
else
celestia:settime(endtime - timechange * distance_percent) -- with at, time change will accel/cruise/decel
end
if time_percent > end_interpolation then
if start_interpolation > time_percent then
if interpolation_started == false then
start_interpolation_orientation = obs:getorientation()
obs:track(nil)
interpolation_started = true
end
obs:setorientation(end_interpolation_orientation:slerp(start_interpolation_orientation,
(time_percent - end_interpolation) / interpolation_duration))
end
elseif interpolation_ended == false then
obs:setorientation(end_interpolation_orientation)
obs:track(final_tracking)
interpolation_ended = true
end
wait(0.0)
end
-- re-set the endstates (for accuraccy)
celestia:settime(endtime)
obs:setposition(endposition)
wait(0.0)
obs:track(nil)
if body ~= nil then
obs:follow(body)
end
end -- travel_to()