]> git.tdb.fi Git - r2c2.git/blob - source/libmarklin/tracktype.cpp
Turn TrackPart into a proper class
[r2c2.git] / source / libmarklin / tracktype.cpp
1 /* $Id$
2
3 This file is part of the MSP Märklin suite
4 Copyright © 2006-2010  Mikkosoft Productions, Mikko Rasa
5 Distributed under the GPL
6 */
7
8 #include <cmath>
9 #include "tracktype.h"
10
11 using namespace std;
12 using namespace Msp;
13
14 namespace Marklin {
15
16 TrackType::TrackType(unsigned a):
17         art_nr(a)
18 { }
19
20 float TrackType::get_total_length() const
21 {
22         return get_path_length(-1);
23 }
24
25 float TrackType::get_path_length(int p) const
26 {
27         float len = 0;
28         for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
29                 if(p<0 || i->get_path()==static_cast<unsigned>(p))
30                         len += i->get_length();
31         return len;
32 }
33
34 unsigned TrackType::get_paths() const
35 {
36         unsigned mask = 0;
37         for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
38                 mask |= 1<<i->get_path();
39         return mask;
40 }
41
42 unsigned TrackType::get_n_paths() const
43 {
44         unsigned n = 0;
45         for(unsigned mask = get_paths(); mask; ++n)
46                 mask &= mask-1;
47         return n;
48 }
49
50 bool TrackType::is_turnout() const
51 {
52         return endpoints.size()>2;
53 }
54
55 bool TrackType::is_double_address() const
56 {
57         return get_n_paths()>2;
58 }
59
60 TrackPoint TrackType::get_point(unsigned epi, unsigned path, float d) const
61 {
62         if(epi>=endpoints.size())
63                 throw InvalidParameterValue("Endpoint index out of range");
64
65         const TrackPart *part = 0;
66         unsigned part_ep = 0;
67         for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
68         {
69                 if((endpoints[epi].paths&(1<<path)) && i->get_path()!=path)
70                         continue;
71
72                 unsigned n_part_eps = (i->is_dead_end() ? 1 : 2);
73                 for(unsigned j=0; j<n_part_eps; ++j)
74                 {
75                         TrackPoint p = i->get_point(j ? i->get_length() : 0);
76                         float dx = p.pos.x-endpoints[epi].pos.x;
77                         float dy = p.pos.y-endpoints[epi].pos.y;
78                         if(dx*dx+dy*dy<1e-6)
79                         {
80                                 part = &*i;
81                                 part_ep = j;
82                         }
83                 }
84         }
85
86         if(!part)
87                 throw Exception("Internal error (endpoint does not match any part)");
88
89         while(1)
90         {
91                 float plen = part->get_length();
92                 if(d<plen)
93                 {
94                         if(part_ep==1)
95                                 d = plen-d;
96                         TrackPoint p = part->get_point(d);
97                         if(part_ep==1)
98                                 p.dir += M_PI;
99                         return p;
100                 }
101                 else
102                 {
103                         d -= plen;
104                         TrackPart *next = part->get_link(1-part_ep);
105                         if(!next)
106                                 throw InvalidParameterValue("Distance out of range");
107                         part_ep = (next->get_link(0)==part ? 0 : 1);
108                         part = next;
109                 }
110         }
111 }
112
113 void TrackType::collect_endpoints()
114 {
115         endpoints.clear();
116
117         for(vector<TrackPart>::iterator i=parts.begin(); i!=parts.end(); ++i)
118         {
119                 for(vector<TrackPart>::iterator j=i; ++j!=parts.end();)
120                         i->check_link(*j);
121
122                 unsigned n_part_eps = (i->is_dead_end() ? 1 : 2);
123                 for(unsigned j=0; j<n_part_eps; ++j)
124                         if(!i->get_link(j))
125                         {
126                                 TrackPoint p = i->get_point(j ? i->get_length() : 0);
127                                 if(j==0)
128                                         p.dir += M_PI;
129
130                                 bool found = false;
131                                 for(vector<Endpoint>::iterator k=endpoints.begin(); k!=endpoints.end(); ++k)
132                                 {
133                                         float dx = k->pos.x-p.pos.x;
134                                         float dy = k->pos.y-p.pos.y;
135
136                                         float da = k->dir-p.dir;
137                                         while(da>M_PI)
138                                                 da -= M_PI*2;
139                                         while(da<-M_PI)
140                                                 da += M_PI*2;
141
142                                         if(dx*dx+dy*dy<1e-6 && da>-0.01 && da<0.01)
143                                         {
144                                                 k->paths |= 1<<i->get_path();
145                                                 found = true;
146                                                 break;
147                                         }
148                                 }
149
150                                 if(!found)
151                                         endpoints.push_back(Endpoint(p.pos.x, p.pos.y, p.dir, 1<<i->get_path()));
152                         }
153         }
154 }
155
156
157 TrackType::Loader::Loader(TrackType &t):
158         Msp::DataFile::BasicLoader<TrackType>(t)
159 {
160         add("description", &TrackType::description);
161         add("part",        &Loader::part);
162 }
163
164 void TrackType::Loader::finish()
165 {
166         obj.collect_endpoints();
167 }
168
169 void TrackType::Loader::part()
170 {
171         TrackPart p;
172         load_sub(p);
173         obj.parts.push_back(p);
174 }
175
176 } // namespace Marklin