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