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