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