Zone::Zone(Layout &l):
TrackChain(l),
- number(0)
+ number(0),
+ up_end(-1),
+ preferred_dir(UNSPECIFIED)
{
layout.add(*this);
}
TrackChain::set_name(full_name);
}
+void Zone::on_track_added(Track &track)
+{
+ if(preferred_dir)
+ {
+ TrackIter iter = iter_for(track, preferred_dir==UP ? DOWN : UP);
+ track.set_preferred_exit(iter.entry());
+ }
+}
+
+void Zone::set_direction_towards(Track &track, Direction dir)
+{
+ if(dir==UNSPECIFIED)
+ throw invalid_argument("Zone::set_direction_towards");
+ if(tracks.empty())
+ throw logic_error("no tracks");
+ if(tracks.count(&track))
+ throw invalid_argument("Zone::set_direction_towards");
+
+ Validity valid = check_validity(track);
+ if(valid!=VALID)
+ throw_bad_chain(valid);
+
+ for(unsigned i=0; i<2; ++i)
+ if(ends[i] && ends[i]->get_link_slot(track)>=0)
+ {
+ up_end = (dir==UP ? i : 1-i);
+ return;
+ }
+
+ throw logic_error("internal error (valid track not linked to ends)");
+}
+
+void Zone::set_preferred_direction(Direction d)
+{
+ if(up_end<0)
+ throw logic_error("no direction");
+
+ preferred_dir = d;
+
+ if(preferred_dir)
+ {
+ TrackIter iter = get_end(preferred_dir).reverse();
+ while(iter && tracks.count(iter.track()))
+ {
+ iter->set_preferred_exit(iter.entry());
+ iter = next_iter(iter);
+ }
+ }
+ else
+ {
+ for(TrackSet::iterator i=tracks.begin(); i!=tracks.end(); ++i)
+ (*i)->set_preferred_exit(-1);
+ }
+}
+
+void Zone::clear_direction()
+{
+ up_end = -1;
+ preferred_dir = UNSPECIFIED;
+}
+
+TrackIter Zone::iter_for(Track &track, Direction dir) const
+{
+ if(!tracks.count(&track))
+ return TrackIter();
+ else if(dir==UNSPECIFIED)
+ return TrackIter(&track, 0);
+ else if(up_end<0)
+ return TrackIter();
+
+ for(int i=0; i<2; ++i)
+ if(&track==ends[i].track())
+ {
+ if((i==up_end)==(dir==UP))
+ return ends[i];
+ else
+ return ends[i].reverse();
+ }
+
+ TrackIter iter = ends[up_end^(dir==UP)].reverse();
+ while(iter && tracks.count(iter.track()))
+ {
+ if(iter.track()==&track)
+ return iter;
+
+ iter = next_iter(iter);
+ }
+
+ return TrackIter();
+}
+
+TrackIter Zone::next_iter(const TrackIter &iter) const
+{
+ TrackIter next_outside;
+ const TrackType::Endpoint &ep = iter.endpoint();
+ for(unsigned i=0; ep.paths>>i; ++i)
+ if(ep.has_path(i))
+ if(TrackIter next = iter.next(i))
+ {
+ if(tracks.count(next.track()))
+ return next;
+ else
+ next_outside = next;
+ }
+
+ return next_outside;
+}
+
+TrackIter Zone::get_end(Direction dir) const
+{
+ if(dir==UNSPECIFIED)
+ return ends[0];
+ if(up_end<0)
+ return TrackIter();
+
+ return ends[up_end^(dir==DOWN)];
+}
+
void Zone::save(list<DataFile::Statement> &st) const
{
st.push_back((DataFile::Statement("group"), group));
if(number)
st.push_back((DataFile::Statement("number"), number));
- set<unsigned> block_ids;
- for(TrackSet::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
- block_ids.insert((*i)->get_block().get_id());
+ unsigned last_block = 0;
+ TrackIter iter = ends[0].reverse();
+ while(iter && tracks.count(iter.track()))
+ {
+ unsigned block_id = iter->get_block().get_id();
+ if(block_id!=last_block)
+ {
+ st.push_back((DataFile::Statement("block"), block_id));
+ last_block = block_id;
+ }
- for(set<unsigned>::const_iterator i=block_ids.begin(); i!=block_ids.end(); ++i)
- st.push_back((DataFile::Statement("block"), *i));
+ iter = next_iter(iter);
+ }
+
+ if(up_end>=0)
+ {
+ for(unsigned i=0; i<2; ++i)
+ {
+ TrackIter hint = next_iter(ends[up_end^i]);
+ if(hint && !tracks.count(hint.track()))
+ {
+ st.push_back((DataFile::Statement("direction_hint"), hint->get_block().get_id(), (i==0 ? UP : DOWN)));
+ break;
+ }
+ }
+
+ if(preferred_dir)
+ st.push_back((DataFile::Statement("preferred_direction"), preferred_dir));
+ }
}
DataFile::Statement Zone::save_reference() const
{
- return (DataFile::Statement("zone"), group, number);
+ DataFile::Statement st("zone");
+ st.append(group);
+ if(number)
+ st.append(number);
+ return st;
}
DataFile::ObjectLoader<Zone>(z)
{
add("block", &Loader::block);
+ add("direction_hint", &Loader::direction_hint);
add("group", &Zone::group);
add("number", &Zone::number);
+ add("preferred_direction", &Loader::preferred_direction);
add("qualifier", &Zone::qualifier);
}
void Zone::Loader::block(unsigned b)
{
Block &blk = obj.layout.get_block(b);
- const set<Track *> &btracks = blk.get_tracks();
- obj.tracks.insert(btracks.begin(), btracks.end());
+ obj.add_tracks(blk.get_tracks());
+}
+
+void Zone::Loader::direction_hint(unsigned b, Direction d)
+{
+ Block &blk = obj.layout.get_block(b);
+ const TrackSet &btracks = blk.get_tracks();
+ for(TrackSet::const_iterator i=btracks.begin(); i!=btracks.end(); ++i)
+ if(obj.check_validity(**i)==VALID)
+ {
+ obj.set_direction_towards(**i, d);
+ return;
+ }
+
+ throw invalid_argument("Zone::Loader::direction_hint");
+}
+
+void Zone::Loader::preferred_direction(Direction d)
+{
+ obj.set_preferred_direction(d);
}
} // namespace R2C2