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