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