]> git.tdb.fi Git - r2c2.git/blob - source/libmarklin/track.cpp
Code reformatting: add spaces around assignment operators
[r2c2.git] / source / libmarklin / track.cpp
1 /* $Id$
2
3 This file is part of the MSP Märklin suite
4 Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
5 Distributed under the GPL
6 */
7
8 #include <cmath>
9 #include "track.h"
10 #include "tracktype.h"
11
12 using namespace std;
13 using namespace Msp;
14
15 namespace Marklin {
16
17 Track::Track(const TrackType &t):
18         type(t),
19         rot(0),
20         slope(0),
21         flex(false),
22         turnout_id(0),
23         sensor_id(0),
24         links(t.get_endpoints().size())
25 { }
26
27 Track::~Track()
28 {
29         break_links();
30 }
31
32 void Track::set_position(const Point &p)
33 {
34         pos = p;
35 }
36
37 void Track::set_rotation(float r)
38 {
39         rot = r;
40         while(rot<0)
41                 rot += M_PI*2;
42         while(rot>M_PI*2)
43                 rot -= M_PI*2;
44 }
45
46 void Track::set_slope(float s)
47 {
48         if(links.size()!=2) return;
49
50         slope = s;
51 }
52
53 void Track::set_flex(bool f)
54 {
55         flex = f;
56 }
57
58 void Track::check_slope()
59 {
60         if(links.size()!=2)
61                 return;
62
63         if(links[0] && links[1])
64         {
65                 Point epp0 = links[0]->get_endpoint_position(links[0]->get_endpoint_by_link(*this));
66                 Point epp1 = links[1]->get_endpoint_position(links[1]->get_endpoint_by_link(*this));
67                 pos.z = epp0.z;
68                 slope = epp1.z-pos.z;
69         }
70         else
71         {
72                 slope = 0;
73                 if(links[0])
74                 {
75                         Point epp = links[0]->get_endpoint_position(links[0]->get_endpoint_by_link(*this));
76                         pos.z = epp.z;
77                 }
78                 else if(links[1])
79                 {
80                         Point epp = links[1]->get_endpoint_position(links[1]->get_endpoint_by_link(*this));
81                         pos.z = epp.z;
82                 }
83         }
84 }
85
86 void Track::set_turnout_id(unsigned i)
87 {
88         turnout_id = i;
89 }
90
91 void Track::set_sensor_id(unsigned i)
92 {
93         sensor_id = i;
94 }
95
96 int Track::get_endpoint_by_link(const Track &other) const
97 {
98         for(unsigned i=0; i<links.size(); ++i)
99                 if(links[i]==&other)
100                         return i;
101
102         return -1;
103 }
104
105 Point Track::get_endpoint_position(unsigned epi) const
106 {
107         const vector<Endpoint> &eps = type.get_endpoints();
108         if(epi>=eps.size())
109                 throw InvalidParameterValue("Endpoint index out of range");
110
111         const Endpoint &ep = eps[epi];
112
113         float c = cos(rot);
114         float s = sin(rot);
115
116         Point p(pos.x+c*ep.pos.x-s*ep.pos.y, pos.y+s*ep.pos.x+c*ep.pos.y, pos.z);
117         if(eps.size()==2 && epi==1)
118                 p.z += slope;
119         return p;
120 }
121
122 float Track::get_endpoint_direction(unsigned epi) const
123 {
124         const vector<Endpoint> &eps = type.get_endpoints();
125         if(epi>=eps.size())
126                 throw InvalidParameterValue("Endpoint index out of range");
127
128         const Endpoint &ep = eps[epi];
129
130         return rot+ep.dir;
131 }
132
133 bool Track::snap_to(Track &other, bool link)
134 {
135         float limit = (link && !flex) ? 1e-6 : 1e-4;
136         const vector<Endpoint> &eps = type.get_endpoints();
137         const vector<Endpoint> &other_eps = other.get_type().get_endpoints();
138
139         for(unsigned i=0; i<eps.size(); ++i)
140         {
141                 Point epp = get_endpoint_position(i);
142
143                 for(unsigned j=0; j<other_eps.size(); ++j)
144                 {
145                         if(other.get_link(j))
146                                 continue;
147
148                         Point epp2 = other.get_endpoint_position(j);
149                         float dx = epp2.x-epp.x;
150                         float dy = epp2.y-epp.y;
151                         if(dx*dx+dy*dy<limit)
152                         {
153                                 set_rotation(other.rot+other_eps[j].dir-eps[i].dir+M_PI);
154                                 set_position(Point(epp2.x-(eps[i].pos.x*cos(rot)-eps[i].pos.y*sin(rot)), epp2.y-(eps[i].pos.y*cos(rot)+eps[i].pos.x*sin(rot)), epp2.z));
155
156                                 if(link)
157                                 {
158                                         if(links[i])
159                                                 break_link(*links[i]);
160                                         links[i] = &other;
161                                         other.links[j] = this;
162                                 }
163
164                                 return true;
165                         }
166                 }
167         }
168
169         return false;
170 }
171
172 bool Track::snap(Point &pt, float &d) const
173 {
174         const vector<Endpoint> &eps = type.get_endpoints();
175
176         for(unsigned i=0; i<eps.size(); ++i)
177         {
178                 Point epp = get_endpoint_position(i);
179                 float dx = pt.x-epp.x;
180                 float dy = pt.y-epp.y;
181                 if(dx*dx+dy*dy<1e-4)
182                 {
183                         pt = epp;
184                         d = rot+eps[i].dir;
185                         return true;
186                 }
187         }
188
189         return false;
190 }
191
192 void Track::break_link(Track &trk)
193 {
194         for(vector<Track *>::iterator i=links.begin(); i!=links.end(); ++i)
195                 if(*i==&trk)
196                 {
197                         *i = 0;
198                         trk.break_link(*this);
199                         return;
200                 }
201 }
202
203 void Track::break_links()
204 {
205         for(vector<Track *>::iterator i=links.begin(); i!=links.end(); ++i)
206                 if(Track *trk=*i)
207                 {
208                         *i = 0;
209                         trk->break_link(*this);
210                 }
211 }
212
213 Track *Track::get_link(unsigned i) const
214 {
215         if(i>links.size())
216                 throw InvalidParameterValue("Link index out of range");
217
218         return links[i];
219 }
220
221 int Track::traverse(unsigned i, unsigned route) const
222 {
223         const vector<Endpoint> &eps = type.get_endpoints();
224         if(i>=eps.size())
225                 throw InvalidParameterValue("Endpoint index out of range");
226
227         const Endpoint &ep = eps[i];
228         
229         if(ep.routes&(1<<route))
230         {
231                 // Find the other endpoint for this route
232                 for(unsigned j=0; j<eps.size(); ++j)
233                         if((eps[j].routes&(1<<route)) && j!=i)
234                                 return j;
235         }
236         else
237         {
238                 // Find an endpoint that's connected to this one and has the requested route
239                 for(unsigned j=0; j<eps.size(); ++j)
240                         if((eps[j].routes&(1<<route)) && (eps[j].routes&ep.routes))
241                                 return j;
242         }
243
244         return -1;
245 }
246
247 Point Track::get_point(unsigned epi, unsigned route, float d) const
248 {
249         const vector<Endpoint> &eps = type.get_endpoints();
250         if(epi>=eps.size())
251                 throw InvalidParameterValue("Endpoint index out of range");
252
253         float x = eps[epi].pos.x;
254         float y = eps[epi].pos.y;
255
256         const vector<TrackPart> &parts = type.get_parts();
257         const TrackPart *last_part = 0;
258         while(1)
259         {
260                 for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
261                 {
262                         if((eps[epi].routes&(1<<route)) && i->route!=route)
263                                 continue;
264                         if(&*i==last_part)
265                                 continue;
266
267                         vector<Endpoint> part_eps;
268                         i->collect_endpoints(part_eps);
269                         for(unsigned j=0; j<part_eps.size(); ++j)
270                         {
271                                 float dx = part_eps[j].pos.x-x;
272                                 float dy = part_eps[j].pos.y-y;
273                                 if(dx*dx+dy*dy<1e-6)
274                                 {
275                                         float plen = i->length;
276                                         if(i->radius)
277                                                 plen *= abs(i->radius);
278                                         if(d<plen)
279                                         {
280                                                 if(j==1)
281                                                         d = plen-d;
282                                                 Point p = i->get_point(d);
283                                                 float c = cos(rot);
284                                                 float s = sin(rot);
285                                                 return Point(pos.x+c*p.x-s*p.y, pos.y+c*p.y+s*p.x);
286                                         }
287                                         else if(part_eps.size()>1)
288                                         {
289                                                 d -= plen;
290                                                 x = part_eps[1-j].pos.x;
291                                                 y = part_eps[1-j].pos.y;
292                                                 last_part = &*i;
293                                                 i = parts.begin();
294                                                 break;
295                                         }
296                                         else
297                                                 return pos;
298                                 }
299                         }
300                 }
301
302                 if(!last_part)
303                         throw Exception("Internal error (Endpoint does not match any part)");
304                 else
305                         return pos;
306         }
307 }
308
309 Track *Track::copy() const
310 {
311         Track *trk = new Track(type);
312         trk->set_position(pos);
313         trk->set_rotation(rot);
314         trk->set_slope(slope);
315         trk->set_flex(flex);
316
317         return trk;
318 }
319
320 void Track::save(list<DataFile::Statement> &st) const
321 {
322         st.push_back((DataFile::Statement("position"), pos.x, pos.y, pos.z));
323         st.push_back((DataFile::Statement("rotation"), rot));
324         st.push_back((DataFile::Statement("slope"), slope));
325         if(turnout_id)
326                 st.push_back((DataFile::Statement("turnout_id"), turnout_id));
327         if(sensor_id)
328                 st.push_back((DataFile::Statement("sensor_id"), sensor_id));
329         if(flex)
330                 st.push_back((DataFile::Statement("flex"), true));
331 }
332
333
334 Track::Loader::Loader(Track &t):
335         DataFile::BasicLoader<Track>(t)
336 {
337         add("position",   &Loader::position);
338         add("rotation",   &Track::rot);
339         add("slope",      &Track::slope);
340         add("turnout_id", &Track::turnout_id);
341         add("sensor_id",  &Track::sensor_id);
342         add("flex",       &Track::flex);
343 }
344
345 void Track::Loader::position(float x, float y, float z)
346 {
347         obj.pos = Point(x, y, z);
348 }
349
350 } // namespace Marklin