]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/zone.cpp
Support directionality for zones
[r2c2.git] / source / libr2c2 / zone.cpp
1 #include <msp/strings/format.h>
2 #include <msp/strings/regex.h>
3 #include "block.h"
4 #include "layout.h"
5 #include "track.h"
6 #include "zone.h"
7
8 using namespace std;
9 using namespace Msp;
10
11 namespace R2C2 {
12
13 Zone::Zone(Layout &l):
14         TrackChain(l),
15         number(0),
16         up_end(-1)
17 {
18         layout.add(*this);
19 }
20
21 Zone::~Zone()
22 {
23         layout.remove(*this);
24 }
25
26 void Zone::set_name(const string &n)
27 {
28         Regex r_name("(.+) ([^ ]+) ([1-9][0-9]*)");
29         RegMatch m = r_name.match(n);
30         if(!m)
31                 throw invalid_argument("Zone::set_name");
32
33         set_name(m[1].str, m[2].str, lexical_cast<unsigned>(m[3].str));
34 }
35
36 void Zone::set_name(const string &g, const string &q, unsigned n)
37 {
38         group = g;
39         qualifier = q;
40         number = n;
41
42         update_name();
43 }
44
45 void Zone::update_name()
46 {
47         string full_name = group;
48         if(!qualifier.empty())
49         {
50                 full_name += ' ';
51                 full_name += qualifier;
52         }
53         if(number)
54                 full_name += format(" %d", number);
55         TrackChain::set_name(full_name);
56 }
57
58 void Zone::set_direction_towards(Track &track, Direction dir)
59 {
60         if(dir==UNSPECIFIED)
61                 throw invalid_argument("Zone::set_direction_towards");
62         if(tracks.empty())
63                 throw logic_error("no tracks");
64         if(tracks.count(&track))
65                 throw invalid_argument("Zone::set_direction_towards");
66
67         Validity valid = check_validity(track);
68         if(valid!=VALID)
69                 throw_bad_chain(valid);
70
71         for(unsigned i=0; i<2; ++i)
72                 if(ends[i] && ends[i]->get_link_slot(track)>=0)
73                 {
74                         up_end = (dir==UP ? i : 1-i);
75                         return;
76                 }
77
78         throw logic_error("internal error (valid track not linked to ends)");
79 }
80
81 void Zone::clear_direction()
82 {
83         up_end = -1;
84 }
85
86 TrackIter Zone::iter_for(Track &track, Direction dir) const
87 {
88         if(!tracks.count(&track))
89                 return TrackIter();
90         else if(dir==UNSPECIFIED)
91                 return TrackIter(&track, 0);
92         else if(up_end<0)
93                 return TrackIter();
94
95         for(int i=0; i<2; ++i)
96                 if(&track==ends[i].track())
97                 {
98                         if((i==up_end)==(dir==UP))
99                                 return ends[i];
100                         else
101                                 return ends[i].reverse();
102                 }
103
104         TrackIter iter = ends[up_end^(dir==UP)].reverse();
105         while(iter && tracks.count(iter.track()))
106         {
107                 if(iter.track()==&track)
108                         return iter;
109
110                 iter = next_iter(iter);
111         }
112
113         return TrackIter();
114 }
115
116 TrackIter Zone::get_end(Direction dir) const
117 {
118         if(dir==UNSPECIFIED)
119                 return ends[0];
120         if(up_end<0)
121                 return TrackIter();
122
123         return ends[up_end^(dir==DOWN)];
124 }
125
126 TrackIter Zone::next_iter(const TrackIter &iter) const
127 {
128         TrackIter next_outside;
129         const TrackType::Endpoint &ep = iter.endpoint();
130         for(unsigned i=0; ep.paths>>i; ++i)
131                 if(ep.has_path(i))
132                         if(TrackIter next = iter.next(i))
133                         {
134                                 if(tracks.count(next.track()))
135                                         return next;
136                                 else
137                                         next_outside = next;
138                         }
139
140         return next_outside;
141 }
142
143 void Zone::save(list<DataFile::Statement> &st) const
144 {
145         st.push_back((DataFile::Statement("group"), group));
146         if(!qualifier.empty())
147                 st.push_back((DataFile::Statement("qualifier"), qualifier));
148         if(number)
149                 st.push_back((DataFile::Statement("number"), number));
150
151         unsigned last_block = 0;
152         TrackIter iter = ends[0].reverse();
153         while(iter && tracks.count(iter.track()))
154         {
155                 unsigned block_id = iter->get_block().get_id();
156                 if(block_id!=last_block)
157                 {
158                         st.push_back((DataFile::Statement("block"), block_id));
159                         last_block = block_id;
160                 }
161
162                 iter = next_iter(iter);
163         }
164
165         if(up_end>=0)
166         {
167                 for(unsigned i=0; i<2; ++i)
168                 {
169                         TrackIter hint = next_iter(ends[up_end^i]);
170                         if(hint && !tracks.count(hint.track()))
171                         {
172                                 st.push_back((DataFile::Statement("direction_hint"), hint->get_block().get_id(), (i==0 ? UP : DOWN)));
173                                 break;
174                         }
175                 }
176         }
177 }
178
179 DataFile::Statement Zone::save_reference() const
180 {
181         return (DataFile::Statement("zone"), group, number);
182 }
183
184
185 Zone::Loader::Loader(Zone &z):
186         DataFile::ObjectLoader<Zone>(z)
187 {
188         add("block",     &Loader::block);
189         add("direction_hint", &Loader::direction_hint);
190         add("group",     &Zone::group);
191         add("number",    &Zone::number);
192         add("qualifier", &Zone::qualifier);
193 }
194
195 void Zone::Loader::finish()
196 {
197         obj.update_name();
198 }
199
200 void Zone::Loader::block(unsigned b)
201 {
202         Block &blk = obj.layout.get_block(b);
203         obj.add_tracks(blk.get_tracks());
204 }
205
206 void Zone::Loader::direction_hint(unsigned b, Direction d)
207 {
208         Block &blk = obj.layout.get_block(b);
209         const TrackSet &btracks = blk.get_tracks();
210         for(TrackSet::const_iterator i=btracks.begin(); i!=btracks.end(); ++i)
211                 if(obj.check_validity(**i)==VALID)
212                 {
213                         obj.set_direction_towards(**i, d);
214                         return;
215                 }
216
217         throw invalid_argument("Zone::Loader::direction_hint");
218 }
219
220 } // namespace R2C2