]> git.tdb.fi Git - r2c2.git/blobdiff - source/designer/extendtool.cpp
Split the Manipulator class into several Tools
[r2c2.git] / source / designer / extendtool.cpp
diff --git a/source/designer/extendtool.cpp b/source/designer/extendtool.cpp
new file mode 100644 (file)
index 0000000..ec4e7f6
--- /dev/null
@@ -0,0 +1,276 @@
+#include <msp/strings/format.h>
+#include "libr2c2/articlenumber.h"
+#include "designer.h"
+#include "extendtool.h"
+
+using namespace std;
+using namespace Msp;
+using namespace R2C2;
+
+ExtendTool::ExtendTool(Designer &d, Input::Mouse &m, const set<Object *> &o):
+       Tool(d, m),
+       objects(o),
+       accepted(false)
+{
+       bool ok = false;
+       for(set<Object *>::const_iterator i=objects.begin(); (!ok && i!=objects.end()); ++i)
+               if(Track *track = dynamic_cast<Track *>(*i))
+               {
+                       const vector<Track *> &links = track->get_links();
+                       for(vector<Track *>::const_iterator j=links.begin(); (!ok && j!=links.end()); ++j)
+                               ok = !*j;
+               }
+
+       if(!ok)
+       {
+               done = true;
+               set_status("No free endpoints");
+       }
+}
+
+ExtendTool::~ExtendTool()
+{
+       if(!accepted)
+       {
+               for(vector<R2C2::Track *>::iterator i=extend_tracks.begin(); i!=extend_tracks.end(); ++i)
+                       delete *i;
+       }
+}
+
+void ExtendTool::connect()
+{
+       if(objects.size()!=2)
+       {
+               signal_status.emit("Exactly two tracks must be selected");
+               return;
+       }
+
+       Track *track1 = dynamic_cast<Track *>(*objects.begin());
+       Track *track2 = dynamic_cast<Track *>(*--objects.end());
+       if(!track1 || !track2)
+       {
+               signal_status.emit("Exactly two tracks must be selected");
+               return;
+       }
+
+       float limit = designer.get_layout().get_catalogue().get_gauge()/10;
+
+       Snap sn1;
+       bool ok = false;
+       float gap = 0;
+       unsigned nls1 = track1->get_n_link_slots();
+       unsigned nls2 = track2->get_n_link_slots();
+       for(unsigned i=0; i<nls1; ++i)
+       {
+               if(track1->get_link(i))
+                       continue;
+
+               sn1 = track1->get_snap_node(i);
+               
+               for(unsigned j=0; j<nls2; ++j)
+               {
+                       if(track2->get_link(j))
+                               continue;
+
+                       Snap sn2 = track2->get_snap_node(j);
+
+                       float dz = sn2.position.z-sn1.position.z;
+                       if(abs(dz)>0.02)
+                               continue;
+
+                       Angle adiff = wrap_balanced(sn1.rotation+Angle::half_turn()-sn2.rotation);
+                       if(abs(adiff).radians()>0.01)
+                               continue;
+
+                       Vector delta = rotated_vector(sn2.position-sn1.position, -sn1.rotation);
+                       if(abs(delta.y)>limit)
+                               continue;
+
+                       gap = delta.x;
+                       if(gap<0)
+                               continue;
+
+                       ok = true;
+               }
+
+               if(ok)
+                       break;
+       }
+
+       if(!ok)
+       {
+               set_status("No aligned endpoints found");
+               return;
+       }
+
+       extend_tracks = create_straight(sn1.position, sn1.rotation, gap, limit);
+
+       if(extend_tracks.empty())
+       {
+               set_status("No connection possible");
+               return;
+       }
+
+       extend_tracks.front()->link_to(*track1);
+       extend_tracks.back()->link_to(*track2);
+
+       accepted = true;
+       set_done();
+}
+
+void ExtendTool::button_press(unsigned btn)
+{
+       if(btn==1)
+       {
+               for(set<Object *>::const_iterator i=objects.begin(); i!=objects.end(); ++i)
+                       if(extend_tracks.front()->link_to(**i))
+                               break;
+               accepted = true;
+               set_done();
+       }
+       else if(btn==3)
+               set_done();
+}
+
+void ExtendTool::axis_motion(unsigned axis, float value, float rel)
+{
+       Tool::axis_motion(axis, value, rel);
+
+       Vector pos;
+       Angle dir;
+       float length = 0;
+
+       for(set<Object *>::const_iterator i=objects.begin(); i!=objects.end(); ++i)
+       {
+               unsigned nls = (*i)->get_n_link_slots();
+               for(unsigned j=0; j<nls; ++j)
+               {
+                       if((*i)->get_link(j))
+                               continue;
+
+                       Snap sn = (*i)->get_snap_node(j);
+                       Vector delta = rotated_vector(ground_pointer-sn.position, -sn.rotation);
+
+                       if(delta.x<length)
+                               continue;
+
+                       pos = sn.position;
+                       dir = sn.rotation;
+                       length = delta.x;
+               }
+       }
+
+       if(length)
+       {
+               vector<Track *> trks = create_straight(pos, dir, length, max(length/500, 0.001f));
+
+               if(!trks.empty())
+               {
+                       for(vector<Track *>::iterator i=extend_tracks.begin(); i!=extend_tracks.end(); ++i)
+                               delete *i;
+                       extend_tracks = trks;
+
+                       map<ArticleNumber, unsigned> counts;
+                       length = 0;
+                       for(vector<Track *>::iterator i=extend_tracks.begin(); i!=extend_tracks.end(); ++i)
+                       {
+                               length += (*i)->get_type().get_total_length();
+                               ++counts[(*i)->get_type().get_article_number()];
+                       }
+
+                       string detail;
+                       for(map<ArticleNumber, unsigned>::const_iterator i=counts.begin(); i!=counts.end(); ++i)
+                       {
+                               if(!detail.empty())
+                                       detail += ", ";
+                               detail += format("%dx %s", i->second, i->first);
+                       }
+
+                       signal_status.emit(format("Extend: %.0fmm (%s)", length*1000, detail));
+               }
+       }
+}
+
+vector<Track *> ExtendTool::create_straight(const Vector &start, const Angle &dir, float length, float limit)
+{
+       const Catalogue::TrackMap &track_types = designer.get_catalogue().get_tracks();
+       map<float, const TrackType *> types_by_length;
+       unsigned preference = 0;
+       for(Catalogue::TrackMap::const_iterator i=track_types.begin(); i!=track_types.end(); ++i)
+       {
+               const vector<TrackPart> &parts = i->second->get_parts();
+               if(parts.size()!=1)
+                       continue;
+               if(parts.front().is_curved() || parts.front().is_dead_end())
+                       continue;
+
+               types_by_length[parts.front().get_length()] = i->second;
+               preference = max(preference, i->second->get_autofit_preference());
+       }
+
+       vector<float> lengths;
+       float removed = 0;
+       while(length>limit)
+       {
+               bool found = false;
+               for(map<float, const TrackType *>::iterator i=types_by_length.end(); i!=types_by_length.begin(); )
+               {
+                       --i;
+                       if(i->second->get_autofit_preference()<preference)
+                               continue;
+                       if((!removed || i->first<removed) && i->first<length+limit)
+                       {
+                               unsigned n = static_cast<unsigned>((length+limit)/i->first);
+                               lengths.insert(lengths.end(), n, i->first);
+                               length -= n*i->first;
+                               found = true;
+                               break;
+                       }
+               }
+
+               if(found)
+                       continue;
+
+               if(lengths.empty())
+               {
+                       if(preference>0)
+                       {
+                               --preference;
+                               removed = 0;
+                               continue;
+                       }
+                       break;
+               }
+
+               length += lengths.back();
+               removed = lengths.back();
+               lengths.pop_back();
+       }
+
+       vector<Track *> trks;
+
+       if(!lengths.empty())
+       {
+               Vector pos = start;
+               Transform trans = Transform::rotation(dir, Vector(0, 0, 1));
+               for(vector<float>::iterator i=lengths.begin(); i!=lengths.end(); ++i)
+               {
+                       Track *track = new Track(designer.get_layout(), *get_item(types_by_length, *i));
+                       track->set_position(pos);
+                       track->set_rotation(dir);
+
+                       if(!trks.empty())
+                               track->link_to(*trks.back());
+                       trks.push_back(track);
+
+                       pos += trans.transform(Vector(*i, 0, 0));
+               }
+       }
+
+       return trks;
+}
+
+void ExtendTool::update_selection(Selection &sel) const
+{
+       sel.replace(extend_tracks.begin(), extend_tracks.end());
+}