2017-12-18 12:44:36 -08:00
-- path.lua
-- Functions for pathpredicting, put in a separate file.
function advtrains . conway ( midreal , prev , drives_on ) --in order prev,mid,return
local mid = advtrains.round_vector_floor_y ( midreal )
local midnode_ok , midconns = advtrains.get_rail_info_at ( mid , drives_on )
if not midnode_ok then
return nil
end
local pconnid
for connid , conn in ipairs ( midconns ) do
local tps = advtrains.dirCoordSet ( mid , conn.c )
if tps.x == prev.x and tps.z == prev.z then
pconnid = connid
end
end
local nconnid = advtrains.get_matching_conn ( pconnid , # midconns )
local next , next_connid , _ , nextrailheight = advtrains.get_adjacent_rail ( mid , midconns , nconnid , drives_on )
if not next then
return nil
end
return vector.add ( advtrains.round_vector_floor_y ( next ) , { x = 0 , y = nextrailheight , z = 0 } ) , midconns [ nconnid ] . c
end
2018-02-21 10:32:41 -08:00
function advtrains . pathpredict ( id , train , gen_front , gen_back )
2017-12-18 12:44:36 -08:00
local maxn = train.path_extent_max or 0
while maxn < gen_front do --pregenerate
local conway
if train.max_index_on_track == maxn then
--atprint("maxn conway for ",maxn,train.path[maxn],maxn-1,train.path[maxn-1])
conway = advtrains.conway ( train.path [ maxn ] , train.path [ maxn - 1 ] , train.drives_on )
end
if conway then
train.path [ maxn + 1 ] = conway
train.max_index_on_track = maxn + 1
else
--do as if nothing has happened and preceed with path
--but do not update max_index_on_track
atprint ( " over-generating path max to index " , ( maxn + 1 ) , " (position " , train.path [ maxn ] , " ) " )
train.path [ maxn + 1 ] = vector.add ( train.path [ maxn ] , vector.subtract ( train.path [ maxn ] , train.path [ maxn - 1 ] ) )
end
train.path_dist [ maxn ] = vector.distance ( train.path [ maxn + 1 ] , train.path [ maxn ] )
maxn = maxn + 1
end
train.path_extent_max = maxn
local minn = train.path_extent_min or - 1
while minn > gen_back do
local conway
if train.min_index_on_track == minn then
--atprint("minn conway for ",minn,train.path[minn],minn+1,train.path[minn+1])
conway = advtrains.conway ( train.path [ minn ] , train.path [ minn + 1 ] , train.drives_on )
end
if conway then
train.path [ minn - 1 ] = conway
train.min_index_on_track = minn - 1
else
--do as if nothing has happened and preceed with path
--but do not update min_index_on_track
atprint ( " over-generating path min to index " , ( minn - 1 ) , " (position " , train.path [ minn ] , " ) " )
train.path [ minn - 1 ] = vector.add ( train.path [ minn ] , vector.subtract ( train.path [ minn ] , train.path [ minn + 1 ] ) )
end
train.path_dist [ minn - 1 ] = vector.distance ( train.path [ minn ] , train.path [ minn - 1 ] )
minn = minn - 1
end
train.path_extent_min = minn
if not train.min_index_on_track then train.min_index_on_track =- 1 end
if not train.max_index_on_track then train.max_index_on_track = 0 end
end
2018-02-21 10:32:41 -08:00
-- Naming conventions:
-- 'index' - An index of the train.path table.
-- 'offset' - A value in meters that determines how far on the path to walk relative to a certain index
-- 'n' - Referring or pointing towards the 'next' path item, the one with index+1
-- 'p' - Referring or pointing towards the 'prev' path item, the one with index-1
-- 'f' - Referring to the positive end of the path (the end with the higher index)
-- 'b' - Referring to the negative end of the path (the end with the lower index)
-- New path structure of trains:
--Tables:
-- path - path positions. 'indices' are relative to this. At the moment, at.round_vector_floor_y(path[i])
-- is the node this item corresponds to, however, this will change in the future.
-- path_node - (reserved)
-- path_cn - Connid of the current node that points towards path[i+1]
-- path_cp - Connid of the current node that points towards path[i-1]
-- When the day comes on that path!=node, these will only be set if this index represents a transition between rail nodes
-- path_dist - The distance (in meters) between this (path[i]) and the next (path[i+1]) item of the path
-- path_dir - The direction of this path item's transition to the next path item, which is the angle of conns[path_cn[i]].c
--Variables:
-- path_ext_f/b - how far path[i] is set
-- path_trk_f/b - how far the path extends along a track. beyond those values, paths are generated in a straight line.
-- path_req_f/b - how far path items were requested in the last step
-- creates the path data structure, reconstructing the train from a position and a connid
-- Important! train.drives_on must exist while calling this method
-- returns: true - successful
-- nil - node not yet available/unloaded, please wait
-- false - node definitely gone, remove train
function advtrains . path_create ( train , pos , connid , rel_index )
local posr = advtrains.round_vector_floor_y ( pos )
local node_ok , conns , rhe = advtrains.get_rail_info_at ( pos , train.drives_on )
if not node_ok then
return node_ok
end
local mconnid = advtrains.get_matching_conn ( connid , # conns )
train.index = rel_index
train.path = { [ 0 ] = { x = posr.x , y = posr.y + rhe , z = posr.z } }
train.path_cn = { [ 0 ] = connid }
train.path_cp = { [ 0 ] = mconnid }
train.path_dist = { }
train.path_dir = {
[ 0 ] = conns [ connid ] ,
[ - 1 ] = conns [ mconnid ]
}
train.path_ext_f = 0
train.path_ext_b = 0
train.path_trk_f = 0
train.path_trk_b = 0
train.path_req_f = 0
train.path_req_b = 0
end
-- Function to get path entry at a position. This function will automatically calculate more of the path when required.
-- returns: pos, on_track
function advtrains . path_get ( train , index )
if index ~= atfloor ( index ) then
error ( " For train " .. train.id .. " : Called path_get() but index= " .. index .. " is not a round number " )
end
while index > train.path_ext_f do
local pos = train.path [ train.path_ext_f ]
local connid = train.path_cn [ train.path_ext_f ]
local node_ok , this_conns , adj_pos , adj_connid , conn_idx , nextrail_y
if train.path_ext_f == train.path_trk_f then
node_ok , this_conns = advtrains.get_rail_info_at ( this_pos )
if not node_ok then error ( " For train " .. train.id .. " : Path item " .. train.path_ext_f .. " on-track but not a valid node! " ) end
adj_pos , adj_connid , conn_idx , nextrail_y = advtrains.get_adjacent_rail ( pos , this_conns , connid , train.drives_on )
end
train.path_ext_f = train.path_ext_f + 1
if adj_pos then
adj_pos.y = adj_pos.y + nextrail_y
train.path_cp [ train.path_ext_f ] = adj_connid
local mconnid = advtrains.get_matching_conn ( adj_connid )
train.path_cn [ train.path_ext_f ] = mconnid
train.path_dir [ train.path_ext_f ] = this_conns [ mconnid ]
train.path_trk_f = train.path_ext_f
else
-- off-track fallback behavior
adj_pos = advtrains.pos_add_dir ( pos , train.path_dir [ train.path_ext_f - 1 ] )
train.path_dir [ train.path_ext_f ] = train.path_dir [ train.path_ext_f - 1 ]
end
train.path [ train.path_ext_f ] = adj_pos
train.path_dist [ train.path_ext_f - 1 ] = vector.distance ( pos , adj_pos )
end
while index < train.path_ext_b do
local pos = train.path [ train.path_ext_b ]
local connid = train.path_cp [ train.path_ext_b ]
local node_ok , this_conns , adj_pos , adj_connid , conn_idx , nextrail_y
if train.path_ext_b == train.path_trk_b then
node_ok , this_conns = advtrains.get_rail_info_at ( this_pos )
if not node_ok then error ( " For train " .. train.id .. " : Path item " .. train.path_ext_f .. " on-track but not a valid node! " ) end
adj_pos , adj_connid , conn_idx , nextrail_y = advtrains.get_adjacent_rail ( pos , this_conns , connid , train.drives_on )
end
train.path_ext_b = train.path_ext_b - 1
if adj_pos then
adj_pos.y = adj_pos.y + nextrail_y
train.path_cp [ train.path_ext_b ] = adj_connid
local mconnid = advtrains.get_matching_conn ( adj_connid )
train.path_cn [ train.path_ext_b ] = mconnid
train.path_dir [ train.path_ext_b ] = advtrains.oppd ( this_conns [ mconnid ] ) --we need to rotate this here so that it points in positive path direction
train.path_trk_b = train.path_ext_b
else
-- off-track fallback behavior
adj_pos = advtrains.pos_add_dir ( pos , train.path_dir [ train.path_ext_b - 1 ] )
train.path_dir [ train.path_ext_b ] = train.path_dir [ train.path_ext_b - 1 ]
end
train.path [ train.path_ext_b ] = adj_pos
train.path_dist [ train.path_ext_b ] = vector.distance ( pos , adj_pos )
end
return train.path [ index ] , ( index <= train.path_trk_f and index >= train.path_trk_b )
end
-- interpolated position to fractional index given, and angle based on path_dir
2018-04-19 02:38:00 -07:00
-- returns: pos, angle(yaw), p_floor, p_ceil
2018-02-21 10:32:41 -08:00
function advtrains . path_get_interpolated ( train , index )
local i_floor = atfloor ( index )
local i_ceil = i_floor + 1
local frac = index - i_floor
local p_floor , = advtrains.path_get ( train , i_floor )
local p_ceil = advtrains.path_get ( train , i_ceil )
2018-04-19 02:38:00 -07:00
-- Note: minimal code duplication to path_get_adjacent, for performance
2018-02-21 10:32:41 -08:00
local d_floor = train.path_dir [ i_floor ]
local d_ceil = train.path_dir [ i_ceil ]
local a_floor = advtrains.dir_to_angle ( d_floor )
local a_ceil = advtrains.dir_to_angle ( d_ceil )
local ang = advtrains.minAngleDiffRad ( a_floor , a_ceil )
2018-04-19 02:38:00 -07:00
return vector.add ( p_floor , vector.multiply ( vector.subtract ( npos2 , npos ) , frac ) , ( a_floor + frac * ang ) % ( 2 * math.pi ) , p_floor , p_ceil -- TODO does this behave correctly?
end
-- returns the 2 path positions directly adjacent to index and the fraction on how to interpolate between them
-- returns: pos_floor, pos_ceil, fraction
function advtrains . path_get_adjacent ( train , index )
local i_floor = atfloor ( index )
local i_ceil = i_floor + 1
local frac = index - i_floor
local p_floor , = advtrains.path_get ( train , i_floor )
local p_ceil = advtrains.path_get ( train , i_ceil )
return p_floor , p_ceil , frac
2018-02-21 10:32:41 -08:00
end
2018-04-19 02:38:00 -07:00
function advtrains . path_get_index_by_offset ( train , index , offset )
2018-02-21 10:32:41 -08:00
local pos_in_train_left = pit
local index = train.index
if pos_in_train_left > ( index - math.floor ( index ) ) * ( train.path_dist [ math.floor ( index ) ] or 1 ) then
pos_in_train_left = pos_in_train_left - ( index - math.floor ( index ) ) * ( train.path_dist [ math.floor ( index ) ] or 1 )
index = math.floor ( index )
while pos_in_train_left > ( train.path_dist [ index - 1 ] or 1 ) do
pos_in_train_left = pos_in_train_left - ( train.path_dist [ index - 1 ] or 1 )
index = index - 1
end
index = index - ( pos_in_train_left / ( train.path_dist [ index - 1 ] or 1 ) )
else
index = index - ( pos_in_train_left / ( train.path_dist [ math.floor ( index - 1 ) ] or 1 ) )
end
return index
end