Distributed under the GPL
*/
+#include <algorithm>
#include <msp/core/except.h>
#include "track.h"
#include "trackiter.h"
return _track==other._track && _entry==other._entry;
}
+
+TrackLoopIter::TrackLoopIter():
+ _looped(false)
+{ }
+
+TrackLoopIter::TrackLoopIter(Track *t, unsigned e):
+ TrackIter(t, e),
+ _visited(new TrackList()),
+ _last(_visited->insert(_visited->end(), track())),
+ _looped(false)
+{ }
+
+TrackLoopIter::TrackLoopIter(const TrackIter &i):
+ TrackIter(i),
+ _visited(new TrackList()),
+ _last(_visited->insert(_visited->end(), track())),
+ _looped(false)
+{ }
+
+TrackLoopIter::TrackLoopIter(const TrackIter &i, RefPtr<TrackList> v, const TrackList::iterator &l):
+ TrackIter(i),
+ _looped(false)
+{
+ if(track())
+ {
+ _visited = v;
+ _last = l;
+ _looped = (_visited && find(_visited->begin(), _last, track())!=_last);
+
+ ++_last;
+ if(_last!=_visited->end())
+ {
+ _visited = new TrackList(_visited->begin(), _last);
+ _last = _visited->end();
+ }
+ _visited->push_back(track());
+ --_last;
+ }
+}
+
+TrackLoopIter TrackLoopIter::next() const
+{
+ return TrackLoopIter(TrackIter::next(), _visited, _last);
+}
+
+TrackLoopIter TrackLoopIter::next(unsigned path) const
+{
+ return TrackLoopIter(TrackIter::next(path), _visited, _last);
+}
+
} // namespace Marklin
Track &operator*() const;
Track *operator->() const { return _track; }
bool operator==(const TrackIter &) const;
+ bool operator!=(const TrackIter &other) const { return !(*this==other); }
operator bool() const { return _track!=0; }
};
+
+/**
+A track iterator that detects looping.
+
+A list of visited tracks is maintained internally to the iterator. This list
+is shared between iterators as long as next() is only called once per iterator.
+Subsequent calls to next() cause the head of the list to be copied.
+*/
+class TrackLoopIter: public TrackIter
+{
+private:
+ typedef std::list<Track *> TrackList;
+
+ Msp::RefPtr<TrackList> _visited;
+ TrackList::iterator _last;
+ bool _looped;
+
+public:
+ TrackLoopIter();
+ TrackLoopIter(Track *, unsigned);
+ TrackLoopIter(const TrackIter &);
+private:
+ TrackLoopIter(const TrackIter &, Msp::RefPtr<TrackList>, const TrackList::iterator &);
+
+public:
+ bool looped() const { return _looped; }
+
+ TrackLoopIter next() const;
+ TrackLoopIter next(unsigned) const;
+};
+
} // namespace Marklin
#endif
if(routes.empty())
return false;
- int path = -1;
- unsigned from_ep = 0;
+ unsigned path = 0;
+ unsigned entry = 0;
list<RouteRef>::iterator route = routes.begin();
- BlockIter block = cur_blocks.back();
- set<const Track *> visited;
// Follow our routes to find out where we're entering the turnout
- while(1)
+ for(TrackLoopIter track = cur_blocks.back().track_iter();;)
{
- block = block.next(route->route);
-
- const Block::Endpoint &entry_ep = block->get_endpoints()[block.entry()];
-
- if(visited.count(entry_ep.track))
- return false;
- visited.insert(entry_ep.track);
-
- if(!advance_route(route, *entry_ep.track))
+ if(!advance_route(route, *track))
return false;
- if(entry_ep.track==&from)
+ if(&*track==&from)
{
- if(block->get_train()==this && !free_block(*block))
+ Block &block = track->get_block();
+ if(block.get_train()==this && !free_block(block))
return false;
- from_ep = entry_ep.track_ep;
- path = route->route->get_turnout(from.get_turnout_id());
- break;
- }
- }
+ int route_path = route->route->get_turnout(from.get_turnout_id());
- // Check that more than one path is available
- unsigned ep_paths = from.get_type().get_endpoints()[from_ep].paths;
- if(!(ep_paths&(ep_paths-1)))
- return false;
+ // Check that more than one path is available
+ unsigned ep_paths = track->get_type().get_endpoints()[track.entry()].paths;
+ if(!(ep_paths&(ep_paths-1)))
+ return false;
- // Choose some other path
- for(int i=0; ep_paths>>i; ++i)
- if((ep_paths&(1<<i)) && i!=path)
- {
- path = i;
+ // Choose some other path
+ for(int i=0; ep_paths>>i; ++i)
+ if((ep_paths&(1<<i)) && i!=route_path)
+ {
+ path = i;
+ break;
+ }
+
+ entry = track.entry();
break;
}
- TrackIter track = TrackIter(&from, from_ep).next(path);
+ unsigned tid = track->get_turnout_id();
+ track = track.next(tid ? route->route->get_turnout(tid) : 0);
+
+ if(!track || track.looped())
+ return false;
+ }
+
+ TrackIter track = TrackIter(&from, entry).next(path);
if(!track)
return false;
diversion->add_track(from);
diversion->set_turnout(from.get_turnout_id(), path);
- if(!is_valid_diversion(*diversion, from, from_ep))
+ if(!is_valid_diversion(*diversion, TrackIter(&from, entry)))
return false;
// Follow the diversion route until we get back to the original route
return lead;
}
-bool Train::is_valid_diversion(const Route &diversion, Track &from, unsigned from_ep)
+bool Train::is_valid_diversion(const Route &diversion, const TrackIter &from)
{
float diversion_len = 0;
- TrackIter track(&from, from_ep);
- while(diversion.has_track(*track))
+ TrackLoopIter track1 = from;
+ while(diversion.has_track(*track1))
{
- unsigned tid = track->get_turnout_id();
+ unsigned tid = track1->get_turnout_id();
unsigned path = (tid ? diversion.get_turnout(tid) : 0);
- diversion_len += track->get_type().get_path_length(path);
+ diversion_len += track1->get_type().get_path_length(path);
- track = track.next(path);
+ track1 = track1.next(path);
- if(&*track==&from)
+ if(track1.looped())
return false;
}
list<RouteRef>::iterator route = routes.begin();
- if(!advance_route(route, from))
+ if(!advance_route(route, *from))
return false;
- set<Track *> visited;
float route_len = 0;
- track = TrackIter(&from, from_ep);
+ TrackLoopIter track2 = from;
while(1)
{
- unsigned tid = track->get_turnout_id();
+ unsigned tid = track2->get_turnout_id();
unsigned path = (tid ? route->route->get_turnout(tid) : 0);
- route_len += track->get_type().get_path_length(path);
+ route_len += track2->get_type().get_path_length(path);
+
+ bool ok = (track2!=from && diversion.has_track(*track2));
- if(&*track!=&from && diversion.has_track(*track))
+ track2 = track2.next(path);
+
+ if(ok)
break;
- if(visited.count(&*track))
+ if(track2.looped())
return false;
- visited.insert(&*track);
- track = track.next(path);
-
- if(!advance_route(route, *track))
+ if(!advance_route(route, *track2))
return false;
}
+ // Must end up at the same place through both routes
+ if(track2!=track1)
+ return false;
+
return diversion_len<route_len*1.2;
}