]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/tracktype.cpp
Rework article numbers
[r2c2.git] / source / libr2c2 / tracktype.cpp
1 #include <cmath>
2 #include <msp/datafile/collection.h>
3 #include <msp/geometry/union.h>
4 #include "trackappearance.h"
5 #include "tracktype.h"
6
7 using namespace std;
8 using namespace Msp;
9
10 namespace R2C2 {
11
12 TrackType::TrackType():
13         appearance(0),
14         state_bits(0),
15         autofit_preference(1)
16 { }
17
18 const TrackAppearance &TrackType::get_appearance() const
19 {
20         if(!appearance)
21                 throw logic_error("no appearance");
22         return *appearance;
23 }
24
25 float TrackType::get_gauge() const
26 {
27         return get_appearance().get_gauge();
28 }
29
30 float TrackType::get_total_length() const
31 {
32         return get_path_length(-1);
33 }
34
35 float TrackType::get_path_length(int p) const
36 {
37         float len = 0;
38         for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
39                 if(p<0 || i->get_path()==static_cast<unsigned>(p))
40                         len += i->get_length();
41         return len;
42 }
43
44 unsigned TrackType::get_paths() const
45 {
46         unsigned mask = 0;
47         for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
48                 mask |= 1<<i->get_path();
49         return mask;
50 }
51
52 unsigned TrackType::get_n_paths() const
53 {
54         unsigned n = 0;
55         for(unsigned mask = get_paths(); mask; ++n)
56                 mask &= mask-1;
57         return n;
58 }
59
60 unsigned TrackType::coerce_path(unsigned entry, unsigned path) const
61 {
62         const Endpoint &ep = get_endpoint(entry);
63         if(ep.has_path(path))
64                 return path;
65
66         unsigned paths = get_paths();
67         if(paths>>(1<<state_bits))
68         {
69                 /* There are more paths than can be expressed with state_bits, so
70                 multiple paths are set at once.  See if one of the alternatives fits. */
71                 unsigned step = 1<<state_bits;
72                 for(unsigned p=path+step; paths>>p; p+=step)
73                         if(ep.has_path(p))
74                                 return p;
75         }
76
77         // Find an endpoint that's connected to the entry and has the requested path
78         for(vector<Endpoint>::const_iterator i=endpoints.begin(); i!=endpoints.end(); ++i)
79                 if(i->has_path(path) && i->has_common_paths(ep))
80                 {
81                         unsigned p = 1;
82                         for(unsigned m=i->paths&ep.paths; m>>p; ++p) ;
83                         return p-1;
84                 }
85
86         // TODO crossings fall here
87         throw logic_error("TrackType::coerce_path");
88 }
89
90 bool TrackType::is_turnout() const
91 {
92         return endpoints.size()>2;
93 }
94
95 bool TrackType::is_dead_end() const
96 {
97         return endpoints.size()<2;
98 }
99
100 const TrackType::Endpoint &TrackType::get_endpoint(unsigned i) const
101 {
102         if(i>=endpoints.size())
103                 throw out_of_range("TrackType::get_endpoint");
104
105         return endpoints[i];
106 }
107
108 OrientedPoint TrackType::get_point(unsigned epi, unsigned path, float d) const
109 {
110         if(epi>=endpoints.size())
111                 throw out_of_range("TrackType::get_point");
112
113         const TrackPart *part = 0;
114         unsigned part_ep = 0;
115         for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
116         {
117                 if(endpoints[epi].has_path(path) && i->get_path()!=path)
118                         continue;
119
120                 unsigned n_part_eps = (i->is_dead_end() ? 1 : 2);
121                 for(unsigned j=0; j<n_part_eps; ++j)
122                 {
123                         OrientedPoint p = i->get_point(j ? i->get_length() : 0);
124                         Vector span = p.position-endpoints[epi].pos;
125                         if(dot(span, span)<1e-6)
126                         {
127                                 part = &*i;
128                                 part_ep = j;
129                         }
130                 }
131         }
132
133         if(!part)
134                 throw logic_error("internal error (endpoint does not match any part)");
135
136         while(1)
137         {
138                 float plen = part->get_length();
139                 if(d<=plen)
140                 {
141                         if(part_ep==1)
142                                 d = plen-d;
143                         OrientedPoint p = part->get_point(d);
144                         if(part_ep==1)
145                                 p.rotation += Angle::half_turn();
146                         return p;
147                 }
148                 else
149                 {
150                         d -= plen;
151                         TrackPart *next = part->get_link(1-part_ep);
152                         if(!next)
153                                 throw invalid_argument("TrackType::get_point");
154                         part_ep = (next->get_link(0)==part ? 0 : 1);
155                         part = next;
156                 }
157         }
158 }
159
160 OrientedPoint TrackType::get_nearest_point(const Vector &p) const
161 {
162         OrientedPoint result;
163         float dist = -1;
164
165         for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
166         {
167                 OrientedPoint n = i->get_nearest_point(p);
168                 float d = distance(n.position, p);
169                 if(d<dist || dist<0)
170                 {
171                         result = n;
172                         dist = d;
173                 }
174         }
175
176         return result;
177 }
178
179 void TrackType::collect_endpoints()
180 {
181         endpoints.clear();
182
183         for(vector<TrackPart>::iterator i=parts.begin(); i!=parts.end(); ++i)
184         {
185                 for(vector<TrackPart>::iterator j=i; ++j!=parts.end();)
186                         i->check_link(*j);
187
188                 unsigned n_part_eps = (i->is_dead_end() ? 1 : 2);
189                 for(unsigned j=0; j<n_part_eps; ++j)
190                         if(!i->get_link(j))
191                         {
192                                 OrientedPoint p = i->get_point(j ? i->get_length() : 0);
193                                 if(j==0)
194                                         p.rotation += Angle::half_turn();
195
196                                 bool found = false;
197                                 for(vector<Endpoint>::iterator k=endpoints.begin(); k!=endpoints.end(); ++k)
198                                 {
199                                         Vector d = k->pos-p.position;
200
201                                         Angle da = wrap_balanced(k->dir-p.rotation);
202
203                                         if(dot(d, d)<1e-6 && abs(da).radians()<0.01)
204                                         {
205                                                 k->paths |= 1<<i->get_path();
206                                                 found = true;
207                                                 break;
208                                         }
209                                 }
210
211                                 if(!found)
212                                         endpoints.push_back(Endpoint(p.position.x, p.position.y, p.rotation, 1<<i->get_path()));
213                         }
214         }
215 }
216
217 TrackType::Endpoint::Endpoint(float x, float y, const Angle &d, unsigned p):
218         pos(x, y, 0),
219         dir(d),
220         paths(p)
221 { }
222
223
224 TrackType::Loader::Loader(TrackType &t, Collection &c):
225         DataFile::DerivedObjectLoader<TrackType, ObjectType::Loader>(t),
226         coll(c),
227         state_bits_set(false)
228 {
229         add("appearance",  &TrackType::appearance);
230         add("autofit_preference", &TrackType::autofit_preference);
231         add("object",      &TrackType::object);
232         add("state_bits",  &Loader::state_bits);
233         add("part",        &Loader::part);
234 }
235
236 void TrackType::Loader::finish()
237 {
238         obj.collect_endpoints();
239         vector<const Shape *> shapes;
240         for(vector<TrackPart>::iterator i=obj.parts.begin(); i!=obj.parts.end(); ++i)
241         {
242                 i->create_shape();
243                 shapes.push_back(&i->get_shape());
244         }
245         obj.shape = Geometry::Union<float, 3>::from_iterator_range(shapes.begin(), shapes.end()).clone();
246 }
247
248 void TrackType::Loader::part()
249 {
250         TrackPart p;
251         load_sub(p);
252         obj.parts.push_back(p);
253         if(!state_bits_set && p.get_path())
254                 while(p.get_path()>=(1U<<obj.state_bits))
255                         ++obj.state_bits;
256 }
257
258 void TrackType::Loader::state_bits(unsigned b)
259 {
260         obj.state_bits = b;
261         state_bits_set = true;
262 }
263
264 } // namespace R2C2