]> git.tdb.fi Git - r2c2.git/blob - source/libmarklin/route.cpp
Change terminology to better distinguish routes on the layout from paths across track...
[r2c2.git] / source / libmarklin / route.cpp
1 /* $Id$
2
3 This file is part of the MSP Märklin suite
4 Copyright © 2007-2009  Mikkosoft Productions, Mikko Rasa
5 Distributed under the GPL
6 */
7
8 #include "layout.h"
9 #include "route.h"
10 #include "track.h"
11 #include "tracktype.h"
12
13 using namespace std;
14 using namespace Msp;
15
16 namespace Marklin {
17
18 Route::Route(Layout &layout, const string &n):
19         name(n)
20 {
21         layout.signal_track_removed.connect(sigc::mem_fun(this, &Route::track_removed));
22 }
23
24 int Route::get_turnout(unsigned id) const
25 {
26         map<unsigned, int>::const_iterator i = turnouts.find(id);
27         if(i!=turnouts.end())
28                 return i->second;
29         return -1;
30 }
31
32 void Route::add_track(const Track &trk)
33 {
34         if(tracks.count(&trk))
35                 return;
36
37         if(!tracks.empty())
38         {
39                 unsigned valid = check_validity(trk);
40                 if(!(valid&1))
41                         throw Exception("Not linked to existing tracks");
42                 else if(!(valid&2))
43                         throw Exception("Branching routes not allowed");
44                 else if(!(valid&4))
45                         throw Exception("Route must be smooth");
46         }
47
48         tracks.insert(&trk);
49         update_turnouts();
50 }
51
52 void Route::add_tracks(const set<const Track *> &trks)
53 {
54         set<const Track *> pending;
55         for(set<const Track *>::const_iterator i=trks.begin(); i!=trks.end(); ++i)
56                 if(!tracks.count(*i))
57                         pending.insert(*i);
58
59         while(!pending.empty())
60         {
61                 bool found = false;
62                 for(set<const Track *>::const_iterator i=pending.begin(); i!=pending.end(); ++i)
63                         if(tracks.empty() || check_validity(**i)==7)
64                         {
65                                 tracks.insert(*i);
66                                 pending.erase(*i);
67                                 found = true;
68                                 break;
69                         }
70
71                 if(!found)
72                         throw Exception("Could not add all tracks to route");
73         }
74
75         update_turnouts();
76 }
77
78 void Route::save(list<DataFile::Statement> &st) const
79 {
80         for(map<unsigned, int>::const_iterator i=turnouts.begin(); i!=turnouts.end(); ++i)
81                 st.push_back((DataFile::Statement("turnout"), i->first, i->second));
82 }
83
84 void Route::update_turnouts()
85 {
86         set<unsigned> found;
87         for(set<const Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
88                 if(unsigned tid=(*i)->get_turnout_id())
89                 {
90                         found.insert(tid);
91
92                         const vector<Endpoint> &endpoints = (*i)->get_type().get_endpoints();
93                         const vector<Track *> &links = (*i)->get_links();
94
95                         unsigned mask = 15;
96                         for(unsigned j=0; j<endpoints.size(); ++j)
97                         {
98                                 if(!tracks.count(links[j]))
99                                         continue;
100                                 if(unsigned tid2=links[j]->get_turnout_id())
101                                 {
102                                         const Endpoint &ep = links[j]->get_type().get_endpoints()[links[j]->get_endpoint_by_link(**i)];
103                                         int p = get_turnout(tid2);
104                                         if(p>=0 && !(ep.paths&(1<<p)))
105                                         {
106                                                 mask &= ~endpoints[j].paths;
107                                                 continue;
108                                         }
109                                 }
110                                 mask &= endpoints[j].paths;
111                         }
112
113                         if(!(mask&(mask-1)))
114                         {
115                                 unsigned path = 0;
116                                 for(; (mask && !(mask&1)); mask>>=1, ++path) ;
117                                 turnouts[tid] = path;
118                         }
119                         else
120                                 turnouts[tid] = -1;
121                 }
122
123         for(map<unsigned, int>::iterator i=turnouts.begin(); i!=turnouts.end();)
124         {
125                 if(!found.count(i->first))
126                         turnouts.erase(i++);
127                 else
128                         ++i;
129         }
130 }
131
132 unsigned Route::check_validity(const Track &trk) const
133 {
134         unsigned result = 4;
135         for(set<const Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
136         {
137                 int epi=(*i)->get_endpoint_by_link(trk);
138                 if(epi>=0)
139                 {
140                         result |= 1;
141                         const vector<Endpoint> &endpoints = (*i)->get_type().get_endpoints();
142                         if(unsigned tid=(*i)->get_turnout_id())
143                         {
144                                 int r = get_turnout(tid);
145                                 if(r>=0)
146                                 {
147                                         if(endpoints[epi].paths&(1<<r))
148                                                 result |= 2;
149                                 }
150                                 else
151                                 {
152                                         unsigned count = 0;
153                                         const vector<Track *> &links = (*i)->get_links();
154                                         int epj = -1;
155                                         for(unsigned k=0; k<endpoints.size(); ++k)
156                                                 if(tracks.count(links[k]))
157                                                 {
158                                                         ++count;
159                                                         epj = k;
160                                                 }
161                                         if(count<=1)
162                                         {
163                                                 result |= 2;
164                                                 if(epj>=0 && !(endpoints[epi].paths&endpoints[epj].paths))
165                                                         result &= 3;
166                                         }
167                                 }
168                         }
169                         else
170                                 result |= 2;
171                 }
172         }
173
174         return result;
175 }
176
177 void Route::track_removed(Track &t)
178 {
179         tracks.erase(&t);
180 }
181
182
183 Route::Loader::Loader(Route &r):
184         DataFile::BasicLoader<Route>(r)
185 {
186         add("turnout", &Loader::turnout);
187 }
188
189 void Route::Loader::turnout(unsigned id, unsigned path)
190 {
191         obj.turnouts[id] = path;
192 }
193
194 } // namespace Marklin