]> git.tdb.fi Git - r2c2.git/blobdiff - source/libmarklin/train.cpp
Add TrackIter and BlockIter classes
[r2c2.git] / source / libmarklin / train.cpp
index 5bc50d85ff23ee5011535e34e5e6142f6b08fccc..cdb8e67d169297925a627c4b7556338316c0d9b6 100644 (file)
@@ -16,6 +16,7 @@ Distributed under the GPL
 #include "route.h"
 #include "simplecontroller.h"
 #include "timetable.h"
+#include "trackiter.h"
 #include "tracktype.h"
 #include "train.h"
 #include "vehicle.h"
@@ -214,18 +215,15 @@ void Train::set_route(const Route *r)
 
        if(r && !cur_blocks.empty())
        {
-               BlockRef &first = cur_blocks.front();
-               BlockRef &last = (rsv_blocks.empty() ? cur_blocks.back() : rsv_blocks.back());
-               BlockRef next = last.next();
-               const Block::Endpoint &first_ep = first.block->get_endpoints()[first.entry];
-               const Block::Endpoint &next_ep = next.block->get_endpoints()[next.entry];
-               if(!r->has_track(*next_ep.track))
+               TrackIter first = cur_blocks.front().track_iter();
+               TrackIter next = (rsv_blocks.empty() ? cur_blocks : rsv_blocks).back().next().track_iter();
+               if(!r->has_track(*next))
                {
-                       Route *lead = Route::find(*next_ep.track, next_ep.track_ep, *r);
+                       Route *lead = Route::find(*next, next.entry(), *r);
                        create_lead_route(lead, lead);
                        routes.push_front(lead);
                }
-               else if(!r->has_track(*first_ep.track))
+               else if(!r->has_track(*first))
                        routes.push_front(create_lead_route(0, r));
        }
 
@@ -236,8 +234,8 @@ void Train::set_route(const Route *r)
 
 void Train::go_to(Track &to)
 {
-       for(list<BlockRef>::const_iterator i=cur_blocks.begin(); i!=cur_blocks.end(); ++i)
-               if(i->block->has_track(to))
+       for(BlockList::const_iterator i=cur_blocks.begin(); i!=cur_blocks.end(); ++i)
+               if((*i)->has_track(to))
                {
                        signal_arrived.emit();
                        set_route(0);
@@ -246,11 +244,9 @@ void Train::go_to(Track &to)
 
        free_noncritical_blocks();
 
-       BlockRef &last = (rsv_blocks.empty() ? cur_blocks.back() : rsv_blocks.back());
-       BlockRef next = last.next();
-       const Block::Endpoint &ep = next.block->get_endpoints()[next.entry];
+       TrackIter next = (rsv_blocks.empty() ? cur_blocks : rsv_blocks).back().next().track_iter();
 
-       Route *route = Route::find(*ep.track, ep.track_ep, to);
+       Route *route = Route::find(*next, next.entry(), to);
        create_lead_route(route, route);
        set_route(route);
 }
@@ -265,18 +261,15 @@ bool Train::divert(Track &from)
        int path = -1;
        unsigned from_ep = 0;
        list<RouteRef>::iterator route = routes.begin();
-       Block *block = cur_blocks.back().block;
-       unsigned entry = cur_blocks.back().entry;
+       BlockIter block = cur_blocks.back();
        set<const Track *> visited;
 
        // Follow our routes to find out where we're entering the turnout
        while(1)
        {
-               Block *link = block->get_link(block->traverse(entry, route->route));
-               entry = link->get_endpoint_by_link(*block);
-               block = link;
+               block = block.next(route->route);
 
-               const Block::Endpoint &entry_ep = block->get_endpoints()[entry];
+               const Block::Endpoint &entry_ep = block->get_endpoints()[block.entry()];
 
                if(visited.count(entry_ep.track))
                        return false;
@@ -309,7 +302,7 @@ bool Train::divert(Track &from)
                        break;
                }
 
-       Track *track = from.get_link(from.traverse(from_ep, path));
+       TrackIter track = TrackIter(&from, from_ep).next(path);
        if(!track)
                return false;
 
@@ -342,22 +335,17 @@ bool Train::divert(Track &from)
        list<RouteRef>::iterator end = routes.end();
        while(1)
        {
-               path = 0;
-               if(track->get_turnout_id())
-                       path = diversion->get_turnout(track->get_turnout_id());
-               Track *next = track->get_link(track->traverse(ep, path));
-
                for(list<RouteRef>::iterator i=route; (end==routes.end() && i!=routes.end()); ++i)
-                       if(i->route->has_track(*next))
+                       if(i->route->has_track(*track))
                                end = i;
 
                if(end!=routes.end())
                        break;
-               else if(!diversion->has_track(*next))
+               else if(!diversion->has_track(*track))
                        throw Exception("Pathfinder returned a bad route");
 
-               ep = next->get_endpoint_by_link(*track);
-               track = next;
+               unsigned tid = track->get_turnout_id();
+               track = track.next(tid ? diversion->get_turnout(tid) : 0);
        }
 
        if(route==end)
@@ -397,14 +385,11 @@ void Train::place(Block &block, unsigned entry)
                return;
        }
 
-       cur_blocks.push_back(BlockRef(&block, entry));
+       cur_blocks.push_back(BlockIter(&block, entry));
        if(reverse)
        {
-               unsigned exit = block.traverse(entry);
-               const Block::Endpoint &bep = block.get_endpoints()[exit];
-               Track *track = bep.track->get_link(bep.track_ep);
-               unsigned ep = track->get_endpoint_by_link(*bep.track);
-               vehicles.front()->place(*track, ep, 0, Vehicle::FRONT_BUFFER);
+               TrackIter track = BlockIter(&block, entry).reverse().track_iter();
+               vehicles.front()->place(*track, track.entry(), 0, Vehicle::FRONT_BUFFER);
        }
        else
        {
@@ -437,16 +422,16 @@ bool Train::free_block(Block &block)
                return false;
 
        unsigned nsens = 0;
-       for(list<BlockRef>::iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
+       for(BlockList::iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
        {
-               if(i->block==&block)
+               if(i->block()==&block)
                {
                        if(nsens<1)
                                return false;
                        release_blocks(rsv_blocks, i, rsv_blocks.end());
                        return true;
                }
-               else if(i->block->get_sensor_id())
+               else if((*i)->get_sensor_id())
                        ++nsens;
        }
 
@@ -469,10 +454,10 @@ void Train::free_noncritical_blocks()
 
        Vehicle &veh = *(reverse ? vehicles.back() : vehicles.front());
 
-       Track *track = veh.get_track();
-       list<BlockRef>::iterator block = cur_blocks.begin();
+       TrackIter track(veh.get_track(), veh.get_entry());
+       BlockList::iterator block = cur_blocks.begin();
        bool in_rsv = false;
-       while(block!=rsv_blocks.end() && !block->block->has_track(*track))
+       while(block!=rsv_blocks.end() && !(*block)->has_track(*track))
        {
                ++block;
                if(block==cur_blocks.end())
@@ -482,10 +467,9 @@ void Train::free_noncritical_blocks()
                }
        }
 
-       unsigned entry = veh.get_entry();
        float dist = veh.get_offset();
        if(reverse)
-               entry = track->traverse(entry);
+               track.reverse();
        else
                dist = track->get_type().get_path_length(track->get_active_path())-dist;
        dist -= veh.get_type().get_length()/2;
@@ -493,11 +477,9 @@ void Train::free_noncritical_blocks()
        bool nsens = 0;
        while(1)
        {
-               Track *next = track->get_link(track->traverse(entry));
-               entry = next->get_endpoint_by_link(*track);
-               track = next;
+               track = track.next();
 
-               if(!block->block->has_track(*track))
+               if(!(*block)->has_track(*track))
                {
                        ++block;
                        if(block==cur_blocks.end())
@@ -514,7 +496,7 @@ void Train::free_noncritical_blocks()
                                return;
                        }
 
-                       if(in_rsv && block->block->get_sensor_id())
+                       if(in_rsv && (*block)->get_sensor_id())
                                ++nsens;
                }
 
@@ -524,12 +506,12 @@ void Train::free_noncritical_blocks()
 
 int Train::get_entry_to_block(Block &block) const
 {
-       for(list<BlockRef>::const_iterator i=cur_blocks.begin(); i!=cur_blocks.end(); ++i)
-               if(i->block==&block)
-                       return i->entry;
-       for(list<BlockRef>::const_iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
-               if(i->block==&block)
-                       return i->entry;
+       for(BlockList::const_iterator i=cur_blocks.begin(); i!=cur_blocks.end(); ++i)
+               if(i->block()==&block)
+                       return i->entry();
+       for(BlockList::const_iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
+               if(i->block()==&block)
+                       return i->entry();
        return -1;
 }
 
@@ -587,8 +569,8 @@ void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt)
                Track *track = vehicle.get_track();
 
                bool ok = false;
-               for(list<BlockRef>::const_iterator i=cur_blocks.begin(); (!ok && i!=cur_blocks.end()); ++i)
-                       ok = i->block->has_track(*track);
+               for(BlockList::const_iterator i=cur_blocks.begin(); (!ok && i!=cur_blocks.end()); ++i)
+                       ok = (*i)->has_track(*track);
 
                float d = get_real_speed(current_speed)*(dt/Time::sec);
                if(ok)
@@ -613,13 +595,13 @@ void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt)
                set_route(0);
        }
 
-       if(!cur_blocks.empty() && !cur_blocks.front().block->get_sensor_id())
+       if(!cur_blocks.empty() && !cur_blocks.front()->get_sensor_id())
        {
-               float dist = get_reserved_distance_until(cur_blocks.front().block, true);
+               float dist = get_reserved_distance_until(&*cur_blocks.front(), true);
 
                if(dist>10*layout.get_catalogue().get_scale())
                {
-                       cur_blocks.front().block->reserve(0);
+                       cur_blocks.front()->reserve(0);
                        cur_blocks.pop_front();
                }
        }
@@ -641,15 +623,15 @@ void Train::save(list<DataFile::Statement> &st) const
 
        if(!cur_blocks.empty())
        {
-               list<BlockRef> blocks = cur_blocks;
+               BlockList blocks = cur_blocks;
                if(reverse)
                        reverse_blocks(blocks);
 
-               Block *prev = blocks.front().block->get_endpoints()[blocks.front().entry].link;
+               BlockIter prev = blocks.front().flip();
                st.push_back((DataFile::Statement("block_hint"), prev->get_id()));
 
-               for(list<BlockRef>::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
-                       st.push_back((DataFile::Statement("block"), i->block->get_id()));
+               for(BlockList::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
+                       st.push_back((DataFile::Statement("block"), (*i)->get_id()));
        }
 
        if(!routes.empty())
@@ -703,12 +685,12 @@ void Train::sensor_event(unsigned addr, bool state)
        if(state)
        {
                // Find the first sensor block from our reserved blocks that isn't this sensor
-               list<BlockRef>::iterator i;
+               BlockList::iterator i;
                unsigned result = 0;
                for(i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
-                       if(i->block->get_sensor_id())
+                       if((*i)->get_sensor_id())
                        {
-                               if(i->block->get_sensor_id()!=addr)
+                               if((*i)->get_sensor_id()!=addr)
                                {
                                        if(result==0)
                                                result = 2;
@@ -737,23 +719,20 @@ void Train::sensor_event(unsigned addr, bool state)
                        }
 
                        travel_dist = 0;
-                       float block_len;
-                       for(list<BlockRef>::iterator j=rsv_blocks.begin(); j!=i; ++j)
+                       for(BlockList::iterator j=rsv_blocks.begin(); j!=i; ++j)
                        {
-                               j->block->traverse(j->entry, &block_len);
-                               travel_dist += block_len;
+                               travel_dist += (*j)->get_path_length(j->entry());
 
-                               if(j->block->get_sensor_id()==addr && !advancing)
+                               if((*j)->get_sensor_id()==addr && !advancing)
                                {
-                                       const Block::Endpoint &bep = j->block->get_endpoints()[j->entry];
+                                       TrackIter track = j->track_iter();
                                        if(reverse)
                                        {
-                                               Track *track = bep.track->get_link(bep.track_ep);
-                                               unsigned ep = track->get_endpoint_by_link(*bep.track);
-                                               vehicles.back()->place(*track, ep, 0, Vehicle::BACK_AXLE);
+                                               track = track.flip();
+                                               vehicles.back()->place(*track, track.entry(), 0, Vehicle::BACK_AXLE);
                                        }
                                        else
-                                               vehicles.front()->place(*bep.track, bep.track_ep, 0, Vehicle::FRONT_AXLE);
+                                               vehicles.front()->place(*track, track.entry(), 0, Vehicle::FRONT_AXLE);
                                }
                        }
                        last_entry_time = Time::now();
@@ -765,8 +744,8 @@ void Train::sensor_event(unsigned addr, bool state)
                        if(routes.size()>1)
                        {
                                const set<Track *> &rtracks = (++routes.begin())->route->get_tracks();
-                               for(list<BlockRef>::iterator j=rsv_blocks.begin(); j!=i; ++j)
-                                       if(rtracks.count(j->block->get_endpoints()[j->entry].track))
+                               for(BlockList::iterator j=rsv_blocks.begin(); j!=i; ++j)
+                                       if(rtracks.count((*j)->get_endpoints()[j->entry()].track))
                                        {
                                                routes.pop_front();
                                                // XXX Exceptions?
@@ -790,14 +769,14 @@ void Train::sensor_event(unsigned addr, bool state)
                const Vehicle &veh = *(reverse ? vehicles.front() : vehicles.back());
 
                // Find the first sensor in our current blocks that's still active
-               list<BlockRef>::iterator end = cur_blocks.begin();
-               for(list<BlockRef>::iterator i=cur_blocks.begin(); i!=cur_blocks.end(); ++i)
+               BlockList::iterator end = cur_blocks.begin();
+               for(BlockList::iterator i=cur_blocks.begin(); i!=cur_blocks.end(); ++i)
                {
-                       if(i->block->has_track(*veh.get_track()))
+                       if((*i)->has_track(*veh.get_track()))
                                break;
-                       if(i->block->get_sensor_id())
+                       if((*i)->get_sensor_id())
                        {
-                               if(layout.get_driver().get_sensor(i->block->get_sensor_id()))
+                               if(layout.get_driver().get_sensor((*i)->get_sensor_id()))
                                        break;
                                else
                                {
@@ -846,12 +825,12 @@ unsigned Train::reserve_more()
        if(!active)
                return 0;
 
-       BlockRef *start = 0;
+       BlockIter start;
        if(!rsv_blocks.empty())
-               start = &rsv_blocks.back();
+               start = rsv_blocks.back();
        else if(!cur_blocks.empty())
-               start = &cur_blocks.back();
-       if(!start)
+               start = cur_blocks.back();
+       else
                return 0;
 
        pending_block = 0;
@@ -859,60 +838,46 @@ unsigned Train::reserve_more()
        // See how many sensor blocks and how much track we already have
        unsigned nsens = 0;
        float dist = 0;
-       for(list<BlockRef>::const_iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
+       for(BlockList::const_iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
        {
-               if(i->block->get_sensor_id())
+               if((*i)->get_sensor_id())
                        ++nsens;
                if(nsens>0)
-               {
-                       float length = 0;
-                       i->block->traverse(i->entry, &length);
-                       dist += length;
-               }
+                       dist += (*i)->get_path_length(i->entry());
        }
        
        if(end_of_route)
                return nsens;
 
        list<RouteRef>::iterator cur_route = routes.begin();
-       advance_route(cur_route, *start->block->get_endpoints()[start->entry].track);
+       advance_route(cur_route, *start.track_iter());
 
        float approach_margin = 50*layout.get_catalogue().get_scale();
        float min_dist = controller->get_braking_distance()*1.3+approach_margin*2;
 
-       BlockRef *last = start;
-       BlockRef *good = start;
+       BlockIter block = start;
+       BlockIter good = start;
        Track *divert_track = 0;
        bool try_divert = false;
        unsigned good_sens = nsens;
        float good_dist = dist;
        Train *blocking_train = 0;
-       std::list<BlockRef> contested_blocks;
+       BlockList contested_blocks;
 
        SetFlag setf(reserving);
 
        while(good_sens<3 || good_dist<min_dist || !contested_blocks.empty())
        {
-               // Traverse to the next block
-               float length = 0;
-               Block *link = 0;
-               {
-                       const Route *route = (cur_route!=routes.end() ? cur_route->route : 0);
-                       unsigned exit = last->block->traverse(last->entry, route, &length);
-                       link = last->block->get_link(exit);
-               }
-               if(!link)
+               BlockIter last = block;
+               block = block.next(cur_route!=routes.end() ? cur_route->route : 0);
+               if(!block)
                        break;
 
-               int entry = link->get_endpoint_by_link(*last->block);
-               if(entry<0)
-                       throw LogicError("Block links are inconsistent!");
-
-               const Block::Endpoint &entry_ep = link->get_endpoints()[entry];
+               TrackIter track = block.track_iter();
 
                if(cur_route!=routes.end())
                {
-                       if(!advance_route(cur_route, *entry_ep.track))
+                       if(!advance_route(cur_route, *track))
                        {
                                // Keep the blocks if we arrived at the end of the route
                                if(!blocking_train)
@@ -925,10 +890,10 @@ unsigned Train::reserve_more()
                                break;
                        }
                }
-               else if(!routes.empty() && routes.front().route->has_track(*entry_ep.track))
+               else if(!routes.empty() && routes.front().route->has_track(*track))
                        cur_route = routes.begin();
 
-               if(link->get_endpoints().size()<2)
+               if(block->get_endpoints().size()<2)
                {
                        if(!blocking_train)
                        {
@@ -941,14 +906,14 @@ unsigned Train::reserve_more()
 
                if(blocking_train)
                {
-                       if(link->get_train()!=blocking_train)
+                       if(block->get_train()!=blocking_train)
                        {
-                               if(blocking_train->free_block(*contested_blocks.back().block))
+                               if(blocking_train->free_block(*contested_blocks.back()))
                                {
                                        // Roll back and start actually reserving the blocks
-                                       last = &rsv_blocks.back();
+                                       block = rsv_blocks.back();
                                        cur_route = routes.begin();
-                                       advance_route(cur_route, *last->block->get_endpoints()[last->entry].track);
+                                       advance_route(cur_route, *block.track_iter().track());
                                        if(blocking_train->get_priority()==priority)
                                                blocking_train->yield_to(*this);
                                        blocking_train = 0;
@@ -957,34 +922,35 @@ unsigned Train::reserve_more()
                                else
                                {
                                        yield_to(*blocking_train);
-                                       pending_block = contested_blocks.front().block;
+                                       pending_block = contested_blocks.front().block();
                                        try_divert = divert_track;
                                        break;
                                }
                        }
                        else
                        {
-                               contested_blocks.push_back(BlockRef(link, entry));
-                               last = &contested_blocks.back();
+                               contested_blocks.push_back(block);
                                continue;
                        }
                }
 
-               bool reserved = link->reserve(this);
+               bool reserved = block->reserve(this);
                if(!reserved)
                {
                        /* We've found another train.  If it wants to exit the block from the
                        same endpoint we're trying to enter from or the other way around,
                        treat it as coming towards us.  Otherwise treat it as going in the
                        same direction. */
-                       Train *other_train = link->get_train();
-                       int other_entry = other_train->get_entry_to_block(*link);
+                       Train *other_train = block->get_train();
+                       int other_entry = other_train->get_entry_to_block(*block);
                        if(other_entry<0)
                                throw LogicError("Block reservation inconsistency");
 
-                       bool entry_conflict = (static_cast<unsigned>(entry)==link->traverse(other_entry));
-                       bool exit_conflict = (link->traverse(entry)==static_cast<unsigned>(other_entry));
-                       if(!entry_conflict && !last->block->get_turnout_id())
+                       unsigned exit = block.reverse().entry();
+                       unsigned other_exit = BlockIter(block.block(), other_entry).reverse().entry();
+                       bool entry_conflict = (block.entry()==other_exit);
+                       bool exit_conflict = (exit==static_cast<unsigned>(other_entry));
+                       if(!entry_conflict && !last->get_turnout_id())
                        {
                                /* The other train is not coming to the blocks we're holding, so we
                                can keep them. */
@@ -999,8 +965,8 @@ unsigned Train::reserve_more()
                        {
                                /* Ask a lesser priority train going to the same direction to free
                                the block for us */
-                               if(other_train->free_block(*link))
-                                       reserved = link->reserve(this);
+                               if(other_train->free_block(*block))
+                                       reserved = block->reserve(this);
                        }
                        else if(other_train!=yielding_to && (other_prio<priority || (other_prio==priority && entry_conflict)))
                        {
@@ -1008,8 +974,7 @@ unsigned Train::reserve_more()
                                enough blocks to get clear of it to avoid a potential deadlock */
                                blocking_train = other_train;
                                contested_blocks.clear();
-                               contested_blocks.push_back(BlockRef(link, entry));
-                               last = &contested_blocks.back();
+                               contested_blocks.push_back(block);
                                continue;
                        }
                        else if(divert_track && (entry_conflict || exit_conflict))
@@ -1018,17 +983,17 @@ unsigned Train::reserve_more()
 
                        if(!reserved)
                        {
-                               pending_block = link;
+                               pending_block = &*block;
                                break;
                        }
                }
 
-               if(link->get_turnout_id())
+               if(block->get_turnout_id())
                {
-                       const Endpoint &track_ep = entry_ep.track->get_type().get_endpoints()[entry_ep.track_ep];
+                       const Endpoint &track_ep = track->get_type().get_endpoints()[track.entry()];
                        bool multiple_paths = (track_ep.paths&(track_ep.paths-1));
 
-                       if(multiple_paths || !last->block->get_turnout_id())
+                       if(multiple_paths || !last->get_turnout_id())
                        {
                                /* We can keep the blocks reserved so far if we are facing the
                                points or if there was no turnout immediately before this one.
@@ -1042,9 +1007,9 @@ unsigned Train::reserve_more()
                        // Figure out what path we'd like to take on the turnout
                        int path = -1;
                        for(list<RouteRef>::iterator i=cur_route; (path<0 && i!=routes.end()); ++i)
-                               path = i->route->get_turnout(link->get_turnout_id());
+                               path = i->route->get_turnout(block->get_turnout_id());
                        if(path<0)
-                               path = entry_ep.track->get_active_path();
+                               path = track->get_active_path();
                        if(!(track_ep.paths&(1<<path)))
                        {
                                for(unsigned i=0; track_ep.paths>>i; ++i)
@@ -1052,49 +1017,48 @@ unsigned Train::reserve_more()
                                                path = i;
                        }
 
-                       if(path!=static_cast<int>(entry_ep.track->get_active_path()))
+                       if(path!=static_cast<int>(track->get_active_path()))
                        {
                                // The turnout is set to wrong path - switch and wait for it
-                               pending_block = link;
-                               entry_ep.track->set_active_path(path);
+                               pending_block = &*block;
+                               track->set_active_path(path);
                                if(pending_block)
                                {
-                                       link->reserve(0);
+                                       block->reserve(0);
                                        break;
                                }
                        }
 
-                       if(multiple_paths && cur_route!=routes.end() && cur_route->diversion!=link->get_turnout_id())
+                       if(multiple_paths && cur_route!=routes.end() && cur_route->diversion!=block->get_turnout_id())
                                /* There's multiple paths to be taken and we are on a route - take
                                note of the diversion possibility */
-                               divert_track = entry_ep.track;
+                               divert_track = &*track;
                }
 
-               if(!contested_blocks.empty() && contested_blocks.front().block==link)
+               if(!contested_blocks.empty() && contested_blocks.front()==block)
                        contested_blocks.pop_front();
 
-               rsv_blocks.push_back(BlockRef(link, entry));
-               last = &rsv_blocks.back();
-               if(last->block->get_sensor_id())
+               rsv_blocks.push_back(block);
+               if(block->get_sensor_id())
                        ++nsens;
                if(nsens>0)
-                       dist += length;
+                       dist += block->get_path_length(block.entry());
        }
 
        // Unreserve blocks that were not good
-       while(!rsv_blocks.empty() && &rsv_blocks.back()!=good)
+       while(!rsv_blocks.empty() && rsv_blocks.back()!=good)
        {
-               rsv_blocks.back().block->reserve(0);
+               rsv_blocks.back()->reserve(0);
                rsv_blocks.pop_back();
        }
 
-       if(!rsv_blocks.empty() && &rsv_blocks.back()!=start)
+       if(!rsv_blocks.empty() && rsv_blocks.back()!=start)
                // We got some new blocks, so no longer need to yield
                yielding_to = 0;
 
        // Make any sensorless blocks at the beginning immediately current
-       list<BlockRef>::iterator i;
-       for(i=rsv_blocks.begin(); (i!=rsv_blocks.end() && !i->block->get_sensor_id()); ++i) ;
+       BlockList::iterator i;
+       for(i=rsv_blocks.begin(); (i!=rsv_blocks.end() && !(*i)->get_sensor_id()); ++i) ;
        if(i!=rsv_blocks.begin())
                cur_blocks.splice(cur_blocks.end(), rsv_blocks, rsv_blocks.begin(), i);
 
@@ -1112,12 +1076,12 @@ float Train::get_reserved_distance_until(const Block *until_block, bool back) co
        Vehicle &veh = *(reverse!=back ? vehicles.back() : vehicles.front());
        const VehicleType &vtype = veh.get_type();
 
-       Track *track = veh.get_track();
+       TrackIter track(veh.get_track(), veh.get_entry());
        if(!track)
                return 0;
 
-       list<BlockRef>::const_iterator block = cur_blocks.begin();
-       while(block!=rsv_blocks.end() && !block->block->has_track(*track))
+       BlockList::const_iterator block = cur_blocks.begin();
+       while(block!=rsv_blocks.end() && !(*block)->has_track(*track))
        {
                ++block;
                if(block==cur_blocks.end())
@@ -1127,26 +1091,23 @@ float Train::get_reserved_distance_until(const Block *until_block, bool back) co
                        block = rsv_blocks.begin();
                }
        }
-       if(block==rsv_blocks.end() || block->block==until_block)
+       if(block==rsv_blocks.end() || &**block==until_block)
                return 0;
 
-       unsigned entry = veh.get_entry();
-
        float result = veh.get_offset();
        if(reverse!=back)
-               entry = track->traverse(entry);
+               track = track.reverse();
        else
                result = track->get_type().get_path_length(track->get_active_path())-result;
        result -= vtype.get_length()/2;
 
        while(1)
        {
-               if(track->get_type().get_endpoints().size()<2)
+               track = track.next();
+               if(!track)
                        break;
 
-               Track *next = track->get_link(track->traverse(entry));
-
-               if(!block->block->has_track(*next))
+               if(!(*block)->has_track(*track))
                {
                        if(back)
                        {
@@ -1163,13 +1124,10 @@ float Train::get_reserved_distance_until(const Block *until_block, bool back) co
                                        break;
                        }
 
-                       if(block->block==until_block)
+                       if(&**block==until_block)
                                break;
                }
 
-               entry = next->get_endpoint_by_link(*track);
-               track = next;
-
                result += track->get_type().get_path_length(track->get_active_path());
        }
 
@@ -1252,26 +1210,26 @@ void Train::set_status(const string &s)
        signal_status_changed.emit(s);
 }
 
-void Train::release_blocks(list<BlockRef> &blocks)
+void Train::release_blocks(BlockList &blocks)
 {
        release_blocks(blocks, blocks.begin(), blocks.end());
 }
 
-void Train::release_blocks(list<BlockRef> &blocks, list<BlockRef>::iterator begin, list<BlockRef>::iterator end)
+void Train::release_blocks(BlockList &blocks, BlockList::iterator begin, BlockList::iterator end)
 {
        while(begin!=end)
        {
-               Block *block = begin->block;
+               Block &block = **begin;
                blocks.erase(begin++);
-               block->reserve(0);
+               block.reserve(0);
        }
 }
 
-void Train::reverse_blocks(list<BlockRef> &blocks) const
+void Train::reverse_blocks(BlockList &blocks) const
 {
        blocks.reverse();
-       for(list<BlockRef>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
-               i->entry = i->block->traverse(i->entry);
+       for(BlockList::iterator i=blocks.begin(); i!=blocks.end(); ++i)
+               *i = i->reverse();
 }
 
 bool Train::advance_route(list<RouteRef>::iterator &iter, Track &track)
@@ -1299,9 +1257,9 @@ Route *Train::create_lead_route(Route *lead, const Route *target)
        }
 
        set<Track *> tracks;
-       for(list<BlockRef>::iterator i=cur_blocks.begin(); i!=rsv_blocks.end(); )
+       for(BlockList::iterator i=cur_blocks.begin(); i!=rsv_blocks.end(); )
        {
-               const set<Track *> &btracks = i->block->get_tracks();
+               const set<Track *> &btracks = (*i)->get_tracks();
                for(set<Track *>::const_iterator j=btracks.begin(); j!=btracks.end(); ++j)
                        if(!target || !target->has_track(**j))
                                tracks.insert(*j);
@@ -1318,20 +1276,16 @@ Route *Train::create_lead_route(Route *lead, const Route *target)
 bool Train::is_valid_diversion(const Route &diversion, Track &from, unsigned from_ep)
 {
        float diversion_len = 0;
-       Track *track = &from;
-       unsigned ep = from_ep;
+       TrackIter track(&from, from_ep);
        while(diversion.has_track(*track))
        {
-               unsigned path = 0;
-               if(track->get_turnout_id())
-                       path = diversion.get_turnout(track->get_turnout_id());
+               unsigned tid = track->get_turnout_id();
+               unsigned path = (tid ? diversion.get_turnout(tid) : 0);
                diversion_len += track->get_type().get_path_length(path);
 
-               Track *next = track->get_link(track->traverse(ep, path));
-               ep = next->get_endpoint_by_link(*track);
-               track = next;
+               track = track.next(path);
 
-               if(track==&from)
+               if(&*track==&from)
                        return false;
        }
 
@@ -1341,25 +1295,21 @@ bool Train::is_valid_diversion(const Route &diversion, Track &from, unsigned fro
 
        set<Track *> visited;
        float route_len = 0;
-       track = &from;
-       ep = from_ep;
+       track = TrackIter(&from, from_ep);
        while(1)
        {
-               unsigned path = 0;
-               if(track->get_turnout_id())
-                       path = route->route->get_turnout(track->get_turnout_id());
+               unsigned tid = track->get_turnout_id();
+               unsigned path = (tid ? route->route->get_turnout(tid) : 0);
                route_len += track->get_type().get_path_length(path);
 
-               if(track!=&from && diversion.has_track(*track))
+               if(&*track!=&from && diversion.has_track(*track))
                        break;
 
-               if(visited.count(track))
+               if(visited.count(&*track))
                        return false;
-               visited.insert(track);
+               visited.insert(&*track);
 
-               Track *next = track->get_link(track->traverse(ep, path));
-               ep = next->get_endpoint_by_link(*track);
-               track = next;
+               track = track.next(path);
 
                if(!advance_route(route, *track))
                        return false;
@@ -1369,25 +1319,6 @@ bool Train::is_valid_diversion(const Route &diversion, Track &from, unsigned fro
 }
 
 
-Train::BlockRef::BlockRef(Block *b, unsigned e):
-       block(b),
-       entry(e)
-{ }
-
-Train::BlockRef Train::BlockRef::next() const
-{
-       Block *blk = block->get_endpoints()[block->traverse(entry)].link;
-       if(!blk)
-               throw InvalidState("At end of line");
-
-       int ep = blk->get_endpoint_by_link(*block);
-       if(ep<0)
-               throw LogicError("Block links are inconsistent");
-
-       return BlockRef(blk, ep);
-}
-
-
 Train::RouteRef::RouteRef(const Route *r, unsigned d):
        route(r),
        diversion(d)
@@ -1425,10 +1356,9 @@ void Train::Loader::finish()
 {
        if(!obj.cur_blocks.empty())
        {
-               const BlockRef &blkref = obj.cur_blocks.front();
-               const Block::Endpoint &bep = blkref.block->get_endpoints()[blkref.entry];
+               TrackIter track = obj.cur_blocks.front().track_iter();
                float offset = 2*obj.layout.get_catalogue().get_scale();
-               obj.vehicles.back()->place(*bep.track, bep.track_ep, offset, Vehicle::BACK_BUFFER);
+               obj.vehicles.back()->place(*track, track.entry(), offset, Vehicle::BACK_BUFFER);
 
                obj.set_status("Stopped");
        }
@@ -1457,7 +1387,7 @@ void Train::Loader::block(unsigned id)
                entry = 0;
 
        blk->reserve(&obj);
-       obj.cur_blocks.push_back(BlockRef(blk, entry));
+       obj.cur_blocks.push_back(BlockIter(blk, entry));
 
        if(blk->get_sensor_id())
                obj.layout.get_driver().set_sensor(blk->get_sensor_id(), true);