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