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