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