]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/tracktype.cpp
Rename TrackPoint to a more generic OrientedPoint
[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):
11         ObjectType(an),
12         state_bits(0),
13         autofit_preference(1)
14 { }
15
16 float TrackType::get_total_length() const
17 {
18         return get_path_length(-1);
19 }
20
21 float TrackType::get_path_length(int p) const
22 {
23         float len = 0;
24         for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
25                 if(p<0 || i->get_path()==static_cast<unsigned>(p))
26                         len += i->get_length();
27         return len;
28 }
29
30 unsigned TrackType::get_paths() const
31 {
32         unsigned mask = 0;
33         for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
34                 mask |= 1<<i->get_path();
35         return mask;
36 }
37
38 unsigned TrackType::get_n_paths() const
39 {
40         unsigned n = 0;
41         for(unsigned mask = get_paths(); mask; ++n)
42                 mask &= mask-1;
43         return n;
44 }
45
46 bool TrackType::is_turnout() const
47 {
48         return endpoints.size()>2;
49 }
50
51 bool TrackType::is_dead_end() const
52 {
53         return endpoints.size()<2;
54 }
55
56 const TrackType::Endpoint &TrackType::get_endpoint(unsigned i) const
57 {
58         if(i>=endpoints.size())
59                 throw out_of_range("TrackType::get_endpoint");
60
61         return endpoints[i];
62 }
63
64 OrientedPoint TrackType::get_point(unsigned epi, unsigned path, float d) const
65 {
66         if(epi>=endpoints.size())
67                 throw out_of_range("TrackType::get_point");
68
69         const TrackPart *part = 0;
70         unsigned part_ep = 0;
71         for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
72         {
73                 if(endpoints[epi].has_path(path) && i->get_path()!=path)
74                         continue;
75
76                 unsigned n_part_eps = (i->is_dead_end() ? 1 : 2);
77                 for(unsigned j=0; j<n_part_eps; ++j)
78                 {
79                         OrientedPoint p = i->get_point(j ? i->get_length() : 0);
80                         Vector span = p.position-endpoints[epi].pos;
81                         if(dot(span, span)<1e-6)
82                         {
83                                 part = &*i;
84                                 part_ep = j;
85                         }
86                 }
87         }
88
89         if(!part)
90                 throw logic_error("internal error (endpoint does not match any part)");
91
92         while(1)
93         {
94                 float plen = part->get_length();
95                 if(d<=plen)
96                 {
97                         if(part_ep==1)
98                                 d = plen-d;
99                         OrientedPoint p = part->get_point(d);
100                         if(part_ep==1)
101                                 p.rotation += Angle::half_turn();
102                         return p;
103                 }
104                 else
105                 {
106                         d -= plen;
107                         TrackPart *next = part->get_link(1-part_ep);
108                         if(!next)
109                                 throw invalid_argument("TrackType::get_point");
110                         part_ep = (next->get_link(0)==part ? 0 : 1);
111                         part = next;
112                 }
113         }
114 }
115
116 OrientedPoint TrackType::get_nearest_point(const Vector &p) const
117 {
118         OrientedPoint result;
119         float dist = -1;
120
121         for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
122         {
123                 OrientedPoint n = i->get_nearest_point(p);
124                 float d = distance(n.position, p);
125                 if(d<dist || dist<0)
126                 {
127                         result = n;
128                         dist = d;
129                 }
130         }
131
132         return result;
133 }
134
135 void TrackType::collect_endpoints()
136 {
137         endpoints.clear();
138
139         for(vector<TrackPart>::iterator i=parts.begin(); i!=parts.end(); ++i)
140         {
141                 for(vector<TrackPart>::iterator j=i; ++j!=parts.end();)
142                         i->check_link(*j);
143
144                 unsigned n_part_eps = (i->is_dead_end() ? 1 : 2);
145                 for(unsigned j=0; j<n_part_eps; ++j)
146                         if(!i->get_link(j))
147                         {
148                                 OrientedPoint p = i->get_point(j ? i->get_length() : 0);
149                                 if(j==0)
150                                         p.rotation += Angle::half_turn();
151
152                                 bool found = false;
153                                 for(vector<Endpoint>::iterator k=endpoints.begin(); k!=endpoints.end(); ++k)
154                                 {
155                                         Vector d = k->pos-p.position;
156
157                                         Angle da = wrap_balanced(k->dir-p.rotation);
158
159                                         if(dot(d, d)<1e-6 && abs(da).radians()<0.01)
160                                         {
161                                                 k->paths |= 1<<i->get_path();
162                                                 found = true;
163                                                 break;
164                                         }
165                                 }
166
167                                 if(!found)
168                                         endpoints.push_back(Endpoint(p.position.x, p.position.y, p.rotation, 1<<i->get_path()));
169                         }
170         }
171 }
172
173 TrackType::Endpoint::Endpoint(float x, float y, const Angle &d, unsigned p):
174         pos(x, y, 0),
175         dir(d),
176         paths(p)
177 { }
178
179
180 TrackType::Loader::Loader(TrackType &t):
181         DataFile::DerivedObjectLoader<TrackType, ObjectType::Loader>(t),
182         state_bits_set(false)
183 {
184         add("autofit_preference", &TrackType::autofit_preference);
185         add("object",      &TrackType::object);
186         add("state_bits",  &Loader::state_bits);
187         add("part",        &Loader::part);
188 }
189
190 void TrackType::Loader::finish()
191 {
192         obj.collect_endpoints();
193         vector<const Shape *> shapes;
194         for(vector<TrackPart>::iterator i=obj.parts.begin(); i!=obj.parts.end(); ++i)
195         {
196                 i->create_shape();
197                 shapes.push_back(&i->get_shape());
198         }
199         obj.shape = Geometry::Union<float, 3>::from_iterator_range(shapes.begin(), shapes.end()).clone();
200 }
201
202 void TrackType::Loader::part()
203 {
204         TrackPart p;
205         load_sub(p);
206         obj.parts.push_back(p);
207         if(!state_bits_set && p.get_path())
208                 while(p.get_path()>=(1U<<obj.state_bits))
209                         ++obj.state_bits;
210 }
211
212 void TrackType::Loader::state_bits(unsigned b)
213 {
214         obj.state_bits = b;
215         state_bits_set = true;
216 }
217
218 } // namespace R2C2