]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/trackchain.cpp
Support directionality for zones
[r2c2.git] / source / libr2c2 / trackchain.cpp
1 #include <msp/strings/format.h>
2 #include <msp/strings/utils.h>
3 #include "layout.h"
4 #include "track.h"
5 #include "trackchain.h"
6
7 using namespace std;
8 using namespace Msp;
9
10 namespace R2C2 {
11
12 TrackChain::TrackChain(Layout &l):
13         layout(l)
14 {
15         layout.signal_object_removed.connect(sigc::mem_fun(this, &TrackChain::object_removed));
16 }
17
18 void TrackChain::set_name(const string &n)
19 {
20         name = n;
21         signal_name_changed.emit(name);
22 }
23
24 void TrackChain::add_track(Track &track)
25 {
26         if(tracks.count(&track))
27                 return;
28
29         Validity valid = check_validity(track);
30         if(valid!=VALID)
31                 throw_bad_chain(valid);
32
33         tracks.insert(&track);
34         update_ends(track);
35         on_track_added(track);
36         signal_track_added.emit(track);
37 }
38
39 void TrackChain::add_tracks(const TrackSet &trks)
40 {
41         TrackSet pending;
42         for(TrackSet::const_iterator i=trks.begin(); i!=trks.end(); ++i)
43                 if(!tracks.count(*i))
44                         pending.insert(*i);
45
46         while(!pending.empty())
47         {
48                 Validity valid = UNLINKED;
49                 for(TrackSet::iterator i=pending.begin(); i!=pending.end(); ++i)
50                         if((valid=check_validity(**i))==VALID)
51                         {
52                                 Track *t = *i;
53                                 pending.erase(i);
54                                 tracks.insert(t);
55                                 update_ends(*t);
56                                 on_track_added(*t);
57                                 signal_track_added.emit(*t);
58                                 break;
59                         }
60
61                 if(valid!=VALID)
62                         throw_bad_chain(valid);
63         }
64 }
65
66 TrackChain::Validity TrackChain::check_validity(Track &track) const
67 {
68         if(tracks.empty())
69                 return VALID;
70
71         if(!ends[1])
72         {
73                 if(!ends[0])
74                         return UNLINKED;
75                 return ends[0]->get_link_slot(track)>=0 ? VALID : UNLINKED;
76         }
77
78         Validity result = UNLINKED;
79         for(unsigned i=0; i<2; ++i)
80         {
81                 int j = ends[i]->get_link_slot(track);
82                 if(j>=0)
83                 {
84                         const TrackType::Endpoint &ep1 = ends[i].endpoint();
85                         const TrackType::Endpoint &ep2 = ends[i]->get_type().get_endpoint(j);
86                         if(!ep1.has_common_paths(ep2))
87                                 return BAD_PATH;
88
89                         result = VALID;
90                 }
91         }
92
93         return result;
94 }
95
96 void TrackChain::throw_bad_chain(Validity v)
97 {
98         if(v==UNLINKED)
99                 throw bad_chain("unlinked");
100         else if(v==BAD_PATH)
101                 throw bad_chain("bad path");
102         else if(v==INCOMPATIBLE)
103                 throw bad_chain("incompatible");
104 }
105
106 void TrackChain::update_ends(Track &track)
107 {
108         if(!ends[0])
109                 // We don't really know the proper endpoint yet; just pick something
110                 ends[0] = TrackIter(&track, 0);
111         else if(!ends[1])
112         {
113                 // We're adding the second track; determine endpoints for both
114                 ends[0] = TrackIter(&*ends[0], ends[0]->get_link_slot(track));
115                 ends[1] = TrackIter(&track, track.get_link_slot(*ends[0]));
116         }
117         else
118         {
119                 unsigned linked = 0;
120                 for(unsigned i=0; i<2; ++i)
121                         if(ends[i]->get_link_slot(track)>=0)
122                                 linked |= 1<<i;
123
124                 if(linked==3)
125                 {
126                         // Closed loop; clear the ends
127                         ends[0] = TrackIter();
128                         ends[1] = TrackIter();
129                 }
130                 else
131                         ends[linked-1] = TrackIter(&track, track.get_link_slot(*ends[linked-1]));
132         }
133 }
134
135 bool TrackChain::has_track(Track &t) const
136 {
137         return tracks.count(&t);
138 }
139
140 TrackIter TrackChain::iter_for(Track &t, Direction d) const
141 {
142         if(!tracks.count(&t))
143                 return TrackIter();
144         else if(d==UNSPECIFIED)
145                 return TrackIter(&t, 0);
146         else
147                 return TrackIter();
148 }
149
150 TrackIter TrackChain::get_end(unsigned i) const
151 {
152         if(i>=2)
153                 throw invalid_argument("TrackChain::get_end");
154
155         if(!ends[0])
156                 return TrackIter();
157         else if(i==1 && !ends[1])
158                 return ends[0].reverse();
159         else
160                 return ends[i];
161 }
162
163 bool TrackChain::is_loop() const
164 {
165         return !tracks.empty() && !ends[0] && !ends[1];
166 }
167
168 void TrackChain::object_removed(Object &obj)
169 {
170         if(Track *track = dynamic_cast<Track *>(&obj))
171         {
172                 tracks.erase(track);
173                 /* TODO If the track was in the middle of the chain, keep only the
174                 longest fragment */
175                 signal_track_removed.emit(*track);
176         }
177 }
178
179
180 void operator<<(LexicalConverter &conv, TrackChain::Direction dir)
181 {
182         switch(dir)
183         {
184         case TrackChain::UNSPECIFIED: conv.result("UNSPECIFIED"); return;
185         case TrackChain::UP: conv.result("UP"); return;
186         case TrackChain::DOWN: conv.result("DOWN"); return;
187         default: throw lexical_error(format("conversion of Direction(%d) to string", dir));
188         }
189 }
190
191 void operator>>(const LexicalConverter &conv, TrackChain::Direction &dir)
192 {
193         const string &str = conv.get();
194         if(str=="UNSPECIFIED")
195                 dir = TrackChain::UNSPECIFIED;
196         else if(str=="UP")
197                 dir = TrackChain::UP;
198         else if(str=="DOWN")
199                 dir = TrackChain::DOWN;
200         else
201                 throw lexical_error(format("conversion of '%s' to Direction", str));
202 }
203
204 } // namespace R2C2