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