]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/zone.cpp
e2a5177ba96e0cf97af7eed450189b1ef0349ff5
[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         return (DataFile::Statement("zone"), group, number);
219 }
220
221
222 Zone::Loader::Loader(Zone &z):
223         DataFile::ObjectLoader<Zone>(z)
224 {
225         add("block",     &Loader::block);
226         add("direction_hint", &Loader::direction_hint);
227         add("group",     &Zone::group);
228         add("number",    &Zone::number);
229         add("preferred_direction", &Loader::preferred_direction);
230         add("qualifier", &Zone::qualifier);
231 }
232
233 void Zone::Loader::finish()
234 {
235         obj.update_name();
236 }
237
238 void Zone::Loader::block(unsigned b)
239 {
240         Block &blk = obj.layout.get_block(b);
241         obj.add_tracks(blk.get_tracks());
242 }
243
244 void Zone::Loader::direction_hint(unsigned b, Direction d)
245 {
246         Block &blk = obj.layout.get_block(b);
247         const TrackSet &btracks = blk.get_tracks();
248         for(TrackSet::const_iterator i=btracks.begin(); i!=btracks.end(); ++i)
249                 if(obj.check_validity(**i)==VALID)
250                 {
251                         obj.set_direction_towards(**i, d);
252                         return;
253                 }
254
255         throw invalid_argument("Zone::Loader::direction_hint");
256 }
257
258 void Zone::Loader::preferred_direction(Direction d)
259 {
260         obj.set_preferred_direction(d);
261 }
262
263 } // namespace R2C2