]> git.tdb.fi Git - r2c2.git/blob - source/designer/extendtool.cpp
3af4cf6cd0b1398f719e79346e3151794ff52939
[r2c2.git] / source / designer / extendtool.cpp
1 #include <msp/strings/format.h>
2 #include "libr2c2/articlenumber.h"
3 #include "designer.h"
4 #include "extendtool.h"
5
6 using namespace std;
7 using namespace Msp;
8 using namespace R2C2;
9
10 ExtendTool::ExtendTool(Designer &d, Input::Keyboard &k, Input::Mouse &m, const set<Object *> &objects):
11         Tool(d, k, m)
12 {
13         for(set<Object *>::const_iterator i=objects.begin(); i!=objects.end(); ++i)
14                 if(Track *t = dynamic_cast<Track *>(*i))
15                 {
16                         unsigned nls = t->get_n_link_slots();
17                         for(unsigned j=0; j<nls; ++j)
18                                 if(!t->get_link(j))
19                                         unlinked_endpoints.push_back(TrackIter(t, j));
20                 }
21
22         if(unlinked_endpoints.empty())
23         {
24                 done = true;
25                 set_status("No free endpoints");
26         }
27 }
28
29 ExtendTool::~ExtendTool()
30 {
31         if(!accepted)
32         {
33                 for(vector<R2C2::Track *>::iterator i=extend_tracks.begin(); i!=extend_tracks.end(); ++i)
34                         delete *i;
35         }
36 }
37
38 void ExtendTool::connect()
39 {
40         float limit = designer.get_layout().get_catalogue().get_gauge()/10;
41
42         Track *start_track = 0;
43         Track *end_track = 0;
44         Snap start_sn;
45         bool ok = false;
46         float gap = 0;
47         for(vector<TrackIter>::const_iterator i=unlinked_endpoints.begin(); i!=unlinked_endpoints.end(); ++i)
48         {
49                 start_sn = (*i)->get_snap_node(i->entry());
50                 
51                 for(vector<TrackIter>::const_iterator j=i; ++j!=unlinked_endpoints.end(); )
52                 {
53                         Snap end_sn = (*j)->get_snap_node(j->entry());
54
55                         float dz = end_sn.position.z-start_sn.position.z;
56                         if(abs(dz)>0.02)
57                                 continue;
58
59                         Angle adiff = wrap_balanced(start_sn.rotation+Angle::half_turn()-end_sn.rotation);
60                         if(abs(adiff).radians()>0.01)
61                                 continue;
62
63                         Vector delta = rotated_vector(end_sn.position-start_sn.position, -start_sn.rotation);
64                         if(abs(delta.y)>limit)
65                                 continue;
66
67                         gap = delta.x;
68                         if(gap<0)
69                                 continue;
70
71                         ok = true;
72                 }
73
74                 if(ok)
75                         break;
76         }
77
78         if(!ok)
79         {
80                 set_status("No aligned endpoints found");
81                 return;
82         }
83
84         extend_tracks = create_straight(start_sn.position, start_sn.rotation, gap, limit);
85
86         if(extend_tracks.empty())
87         {
88                 set_status("No connection possible");
89                 return;
90         }
91
92         extend_tracks.front()->link_to(*start_track);
93         extend_tracks.back()->link_to(*end_track);
94
95         set_done(true);
96 }
97
98 void ExtendTool::pointer_motion()
99 {
100         Vector pos;
101         Angle dir;
102         float length = 0;
103
104         for(vector<TrackIter>::const_iterator i=unlinked_endpoints.begin(); i!=unlinked_endpoints.end(); ++i)
105         {
106                 Snap sn = (*i)->get_snap_node(i->entry());
107                 Vector delta = rotated_vector(ground_pointer-sn.position, -sn.rotation);
108
109                 if(delta.x<length)
110                         continue;
111
112                 pos = sn.position;
113                 dir = sn.rotation;
114                 length = delta.x;
115         }
116
117         if(length)
118         {
119                 vector<Track *> trks = create_straight(pos, dir, length, max(length/500, 0.001f));
120
121                 if(!trks.empty())
122                 {
123                         for(vector<Track *>::iterator i=extend_tracks.begin(); i!=extend_tracks.end(); ++i)
124                                 delete *i;
125                         extend_tracks = trks;
126
127                         map<ArticleNumber, unsigned> counts;
128                         length = 0;
129                         for(vector<Track *>::iterator i=extend_tracks.begin(); i!=extend_tracks.end(); ++i)
130                         {
131                                 length += (*i)->get_type().get_total_length();
132                                 ++counts[(*i)->get_type().get_article_number()];
133                         }
134
135                         string detail;
136                         for(map<ArticleNumber, unsigned>::const_iterator i=counts.begin(); i!=counts.end(); ++i)
137                         {
138                                 if(!detail.empty())
139                                         detail += ", ";
140                                 detail += format("%dx %s", i->second, i->first);
141                         }
142
143                         signal_status.emit(format("Extend: %.0fmm (%s)", length*1000, detail));
144                 }
145         }
146 }
147
148 void ExtendTool::finish()
149 {
150         for(vector<TrackIter>::const_iterator i=unlinked_endpoints.begin(); i!=unlinked_endpoints.end(); ++i)
151                 if(extend_tracks.front()->link_to(**i))
152                         break;
153 }
154
155 vector<Track *> ExtendTool::create_straight(const Vector &start, const Angle &dir, float length, float limit)
156 {
157         const Catalogue::TrackMap &track_types = designer.get_catalogue().get_tracks();
158         map<float, const TrackType *> types_by_length;
159         unsigned preference = 0;
160         for(Catalogue::TrackMap::const_iterator i=track_types.begin(); i!=track_types.end(); ++i)
161         {
162                 const vector<TrackPart> &parts = i->second->get_parts();
163                 if(parts.size()!=1)
164                         continue;
165                 if(parts.front().is_curved() || parts.front().is_dead_end())
166                         continue;
167
168                 types_by_length[parts.front().get_length()] = i->second;
169                 preference = max(preference, i->second->get_autofit_preference());
170         }
171
172         vector<float> lengths;
173         float removed = 0;
174         while(length>limit)
175         {
176                 bool found = false;
177                 for(map<float, const TrackType *>::iterator i=types_by_length.end(); i!=types_by_length.begin(); )
178                 {
179                         --i;
180                         if(i->second->get_autofit_preference()<preference)
181                                 continue;
182                         if((!removed || i->first<removed) && i->first<length+limit)
183                         {
184                                 unsigned n = static_cast<unsigned>((length+limit)/i->first);
185                                 lengths.insert(lengths.end(), n, i->first);
186                                 length -= n*i->first;
187                                 found = true;
188                                 break;
189                         }
190                 }
191
192                 if(found)
193                         continue;
194
195                 if(lengths.empty())
196                 {
197                         if(preference>0)
198                         {
199                                 --preference;
200                                 removed = 0;
201                                 continue;
202                         }
203                         break;
204                 }
205
206                 length += lengths.back();
207                 removed = lengths.back();
208                 lengths.pop_back();
209         }
210
211         vector<Track *> trks;
212
213         if(!lengths.empty())
214         {
215                 Vector pos = start;
216                 Transform trans = Transform::rotation(dir, Vector(0, 0, 1));
217                 for(vector<float>::iterator i=lengths.begin(); i!=lengths.end(); ++i)
218                 {
219                         Track *track = new Track(designer.get_layout(), *get_item(types_by_length, *i));
220                         track->set_position(pos);
221                         track->set_rotation(dir);
222
223                         if(!trks.empty())
224                                 track->link_to(*trks.back());
225                         trks.push_back(track);
226
227                         pos += trans.transform(Vector(*i, 0, 0));
228                 }
229         }
230
231         return trks;
232 }
233
234 void ExtendTool::update_selection(Selection &sel) const
235 {
236         if(accepted)
237                 sel.replace(extend_tracks.begin(), extend_tracks.end());
238 }