]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/trackchain.cpp
Create a base class to handle common operations in Block, Route and Zone
[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 }
27
28 void TrackChain::add_tracks(const TrackSet &trks)
29 {
30         TrackSet pending;
31         for(TrackSet::const_iterator i=trks.begin(); i!=trks.end(); ++i)
32                 if(!tracks.count(*i))
33                         pending.insert(*i);
34
35         while(!pending.empty())
36         {
37                 Validity valid = UNLINKED;
38                 for(TrackSet::iterator i=pending.begin(); i!=pending.end(); ++i)
39                         if((valid=check_validity(**i))==VALID)
40                         {
41                                 tracks.insert(*i);
42                                 update_ends(**i);
43                                 on_track_added(**i);
44                                 pending.erase(i);
45                                 break;
46                         }
47
48                 if(valid!=VALID)
49                         throw_bad_chain(valid);
50         }
51 }
52
53 TrackChain::Validity TrackChain::check_validity(Track &track) const
54 {
55         if(tracks.empty())
56                 return VALID;
57
58         if(!ends[1])
59         {
60                 if(!ends[0])
61                         return UNLINKED;
62                 return ends[0]->get_link_slot(track)>=0 ? VALID : UNLINKED;
63         }
64
65         Validity result = UNLINKED;
66         for(unsigned i=0; i<2; ++i)
67         {
68                 int j = ends[i]->get_link_slot(track);
69                 if(j>=0)
70                 {
71                         const TrackType::Endpoint &ep1 = ends[i].endpoint();
72                         const TrackType::Endpoint &ep2 = ends[i]->get_type().get_endpoint(j);
73                         if(!ep1.has_common_paths(ep2))
74                                 return BAD_PATH;
75
76                         result = VALID;
77                 }
78         }
79
80         return result;
81 }
82
83 void TrackChain::throw_bad_chain(Validity v)
84 {
85         if(v==UNLINKED)
86                 throw bad_chain("unlinked");
87         else if(v==BAD_PATH)
88                 throw bad_chain("bad path");
89         else if(v==INCOMPATIBLE)
90                 throw bad_chain("incompatible");
91 }
92
93 void TrackChain::update_ends(Track &track)
94 {
95         if(!ends[0])
96                 // We don't really know the proper endpoint yet; just pick something
97                 ends[0] = TrackIter(&track, 0);
98         else if(!ends[1])
99         {
100                 // We're adding the second track; determine endpoints for both
101                 ends[0] = TrackIter(&*ends[0], ends[0]->get_link_slot(track));
102                 ends[1] = TrackIter(&track, track.get_link_slot(*ends[0]));
103         }
104         else
105         {
106                 unsigned linked = 0;
107                 for(unsigned i=0; i<2; ++i)
108                         if(ends[i]->get_link_slot(track)>=0)
109                                 linked |= 1<<i;
110
111                 if(linked==3)
112                 {
113                         // Closed loop; clear the ends
114                         ends[0] = TrackIter();
115                         ends[1] = TrackIter();
116                 }
117                 else
118                         ends[linked-1] = TrackIter(&track, track.get_link_slot(*ends[linked-1]));
119         }
120 }
121
122 bool TrackChain::has_track(Track &t) const
123 {
124         return tracks.count(&t);
125 }
126
127 void TrackChain::object_removed(Object &obj)
128 {
129         if(Track *track = dynamic_cast<Track *>(&obj))
130                 tracks.erase(track);
131                 /* TODO If the track was in the middle of the chain, keep only the
132                 longest fragment */
133 }
134
135 } // namespace R2C2