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