]> git.tdb.fi Git - r2c2.git/blobdiff - source/libr2c2/zone.cpp
Allow zones with no qualifier or no number
[r2c2.git] / source / libr2c2 / zone.cpp
index 4b95da54efb375053facd900ea705831a8e12dfc..eb5d102b8414570ff6d01578b04994d675701205 100644 (file)
@@ -1,11 +1,5 @@
-/* $Id$
-
-This file is part of R²C²
-Copyright © 2010  Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include <msp/strings/formatter.h>
+#include <msp/strings/format.h>
+#include <msp/strings/regex.h>
 #include "block.h"
 #include "layout.h"
 #include "track.h"
@@ -17,15 +11,27 @@ using namespace Msp;
 namespace R2C2 {
 
 Zone::Zone(Layout &l):
-       layout(l),
-       number(0)
+       TrackChain(l),
+       number(0),
+       up_end(-1),
+       preferred_dir(UNSPECIFIED)
 {
-       layout.add_zone(*this);
+       layout.add(*this);
 }
 
 Zone::~Zone()
 {
-       layout.remove_zone(*this);
+       layout.remove(*this);
+}
+
+void Zone::set_name(const string &n)
+{
+       Regex r_name("(.+) ([^ ]+) ([1-9][0-9]*)");
+       RegMatch m = r_name.match(n);
+       if(!m)
+               throw invalid_argument("Zone::set_name");
+
+       set_name(m[1].str, m[2].str, lexical_cast<unsigned>(m[3].str));
 }
 
 void Zone::set_name(const string &g, const string &q, unsigned n)
@@ -34,55 +40,138 @@ void Zone::set_name(const string &g, const string &q, unsigned n)
        qualifier = q;
        number = n;
 
-       signal_name_changed.emit(group, qualifier, number);
+       update_name();
 }
 
-string Zone::get_name() const
+void Zone::update_name()
 {
-       string result = group;
+       string full_name = group;
        if(!qualifier.empty())
        {
-               result += ' ';
-               result += qualifier;
+               full_name += ' ';
+               full_name += qualifier;
        }
        if(number)
-               result += format(" %d", number);
-       return result;
+               full_name += format(" %d", number);
+       TrackChain::set_name(full_name);
 }
 
-void Zone::add_track(Track &track)
+void Zone::on_track_added(Track &track)
 {
-       if(!is_valid(track))
-               throw InvalidParameterValue("Can't add track to zone");
+       if(preferred_dir)
+       {
+               TrackIter iter = iter_for(track, preferred_dir==UP ? DOWN : UP);
+               track.set_preferred_exit(iter.entry());
+       }
+}
 
-       tracks.insert(&track);
+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)");
 }
 
-bool Zone::add_tracks(const TrackSet &trks)
+void Zone::set_preferred_direction(Direction d)
 {
-       TrackSet pending = trks;
-       bool first = true;
-       while(1)
+       if(up_end<0)
+               throw logic_error("no direction");
+
+       preferred_dir = d;
+
+       if(preferred_dir)
        {
-               bool ok = false;
-               for(TrackSet::const_iterator i=pending.begin(); i!=pending.end(); ++i)
-                       if(is_valid(**i))
-                       {
-                               tracks.insert(*i);
-                               pending.erase(i);
-                               ok = true;
-                               break;
-                       }
+               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);
+       }
+}
 
-               if(!ok)
+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(first)
-                               throw InvalidParameterValue("Cound not add any tracks to zone");
-                       return pending.empty();
+                       if((i==up_end)==(dir==UP))
+                               return ends[i];
+                       else
+                               return ends[i].reverse();
                }
 
-               first = false;
+       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
@@ -93,25 +182,44 @@ void Zone::save(list<DataFile::Statement> &st) const
        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);
+       }
 
-bool Zone::is_valid(Track &t) const
-{
-       if(tracks.empty())
-               return true;
+       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;
+                       }
+               }
 
-       const vector<Track *> &links = t.get_links();
-       for(vector<Track *>::const_iterator i=links.begin(); i!=links.end(); ++i)
-               if(*i && tracks.count(*i))
-                       return true;
+               if(preferred_dir)
+                       st.push_back((DataFile::Statement("preferred_direction"), preferred_dir));
+       }
+}
 
-       return false;
+DataFile::Statement Zone::save_reference() const
+{
+       DataFile::Statement st("zone");
+       st.append(group);
+       if(number)
+               st.append(number);
+       return st;
 }
 
 
@@ -119,16 +227,41 @@ Zone::Loader::Loader(Zone &z):
        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::finish()
+{
+       obj.update_name();
+}
+
 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