]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/trackchain.cpp
New approach for displaying track state
[r2c2.git] / source / libr2c2 / trackchain.cpp
1 #include <msp/strings/utils.h>
2 #include "layout.h"
3 #include "track.h"
4 #include "trackchain.h"
5
6 namespace R2C2 {
7
8 TrackChain::TrackChain(Layout &l):
9         layout(l)
10 {
11         layout.signal_object_removed.connect(sigc::mem_fun(this, &TrackChain::object_removed));
12 }
13
14 void TrackChain::add_track(Track &track)
15 {
16         if(tracks.count(&track))
17                 return;
18
19         Validity valid = check_validity(track);
20         if(valid!=VALID)
21                 throw_bad_chain(valid);
22
23         tracks.insert(&track);
24         update_ends(track);
25         on_track_added(track);
26         signal_track_added.emit(track);
27 }
28
29 void TrackChain::add_tracks(const TrackSet &trks)
30 {
31         TrackSet pending;
32         for(TrackSet::const_iterator i=trks.begin(); i!=trks.end(); ++i)
33                 if(!tracks.count(*i))
34                         pending.insert(*i);
35
36         while(!pending.empty())
37         {
38                 Validity valid = UNLINKED;
39                 for(TrackSet::iterator i=pending.begin(); i!=pending.end(); ++i)
40                         if((valid=check_validity(**i))==VALID)
41                         {
42                                 Track *t = *i;
43                                 pending.erase(i);
44                                 tracks.insert(t);
45                                 update_ends(*t);
46                                 on_track_added(*t);
47                                 signal_track_added.emit(*t);
48                                 break;
49                         }
50
51                 if(valid!=VALID)
52                         throw_bad_chain(valid);
53         }
54 }
55
56 TrackChain::Validity TrackChain::check_validity(Track &track) const
57 {
58         if(tracks.empty())
59                 return VALID;
60
61         if(!ends[1])
62         {
63                 if(!ends[0])
64                         return UNLINKED;
65                 return ends[0]->get_link_slot(track)>=0 ? VALID : UNLINKED;
66         }
67
68         Validity result = UNLINKED;
69         for(unsigned i=0; i<2; ++i)
70         {
71                 int j = ends[i]->get_link_slot(track);
72                 if(j>=0)
73                 {
74                         const TrackType::Endpoint &ep1 = ends[i].endpoint();
75                         const TrackType::Endpoint &ep2 = ends[i]->get_type().get_endpoint(j);
76                         if(!ep1.has_common_paths(ep2))
77                                 return BAD_PATH;
78
79                         result = VALID;
80                 }
81         }
82
83         return result;
84 }
85
86 void TrackChain::throw_bad_chain(Validity v)
87 {
88         if(v==UNLINKED)
89                 throw bad_chain("unlinked");
90         else if(v==BAD_PATH)
91                 throw bad_chain("bad path");
92         else if(v==INCOMPATIBLE)
93                 throw bad_chain("incompatible");
94 }
95
96 void TrackChain::update_ends(Track &track)
97 {
98         if(!ends[0])
99                 // We don't really know the proper endpoint yet; just pick something
100                 ends[0] = TrackIter(&track, 0);
101         else if(!ends[1])
102         {
103                 // We're adding the second track; determine endpoints for both
104                 ends[0] = TrackIter(&*ends[0], ends[0]->get_link_slot(track));
105                 ends[1] = TrackIter(&track, track.get_link_slot(*ends[0]));
106         }
107         else
108         {
109                 unsigned linked = 0;
110                 for(unsigned i=0; i<2; ++i)
111                         if(ends[i]->get_link_slot(track)>=0)
112                                 linked |= 1<<i;
113
114                 if(linked==3)
115                 {
116                         // Closed loop; clear the ends
117                         ends[0] = TrackIter();
118                         ends[1] = TrackIter();
119                 }
120                 else
121                         ends[linked-1] = TrackIter(&track, track.get_link_slot(*ends[linked-1]));
122         }
123 }
124
125 bool TrackChain::has_track(Track &t) const
126 {
127         return tracks.count(&t);
128 }
129
130 void TrackChain::object_removed(Object &obj)
131 {
132         if(Track *track = dynamic_cast<Track *>(&obj))
133         {
134                 tracks.erase(track);
135                 /* TODO If the track was in the middle of the chain, keep only the
136                 longest fragment */
137                 signal_track_removed.emit(*track);
138         }
139 }
140
141 } // namespace R2C2