#include "libr2c2/zone.h"
#include "3d/path.h"
#include "designer.h"
+#include "elevatetool.h"
+#include "extendtool.h"
#include "input.h"
#include "layoutbar.h"
#include "manipulator.h"
#include "measure.h"
+#include "movetool.h"
+#include "rotatetool.h"
#include "routebar.h"
#include "selection.h"
+#include "slopetool.h"
#include "svgexporter.h"
#include "trackbar.h"
#include "trackproperties.h"
cur_zone(0),
mode(SELECT),
sel_wrap(selection),
- manipulator(*this, mouse, selection),
- measure(*this)
+ cur_tool(0)
{
window.set_title("Railway Designer");
window.signal_close.connect(sigc::bind(sigc::mem_fun(this, &Designer::exit), 0));
selection.signal_changed.connect(sigc::mem_fun(this, &Designer::selection_changed));
- manipulator.signal_status.connect(sigc::mem_fun(this, &Designer::manipulation_status));
- manipulator.signal_done.connect(sigc::mem_fun(this, &Designer::manipulation_done));
- measure.signal_changed.connect(sigc::mem_fun(this, &Designer::measure_changed));
- measure.signal_done.connect(sigc::mem_fun(this, &Designer::measure_done));
// Setup catalogue and layout
DataFile::load(catalogue, "tracks.dat");
// Setup UI
keyboard.signal_button_press.connect(sigc::bind_return(sigc::mem_fun(this, &Designer::key_press), false));
mouse.signal_button_press.connect(sigc::bind_return(sigc::mem_fun(this, &Designer::button_press), false));
- mouse.signal_axis_motion.connect(sigc::bind_return(sigc::mem_fun(this, &Designer::axis_motion), false));
root.signal_tooltip.connect(sigc::mem_fun(this, &Designer::tooltip));
root_layout = new GLtk::Layout;
root_layout->set_margin(0);
pipeline->add_renderable_for_pass(sel_wrap, "unlit");
overlay = new Overlay3D(ui_res.get_default_font());
pipeline->add_renderable_for_pass(*overlay, "overlay");
- pipeline->add_renderable_for_pass(measure, "unlit");
camera_ctl = new CameraController(*main_view, keyboard, mouse);
cat_view->get_camera().set_look_direction(GL::Vector3(0, 0.13053, -0.99144));
void Designer::extend_track()
{
- if(mode!=SELECT)
- return;
-
- if(manipulator.start_extend())
- mode = MANIPULATE;
+ use_tool<ExtendTool>();
}
void Designer::connect_tracks()
{
- if(mode!=SELECT)
- return;
-
- manipulator.connect();
+ use_tool<ExtendTool>()->connect();
}
void Designer::flatten_tracks()
{
- if(mode!=SELECT)
- return;
-
- manipulator.flatten();
+ use_tool<SlopeTool>()->flatten();
}
void Designer::svg_export()
float dt = (t-last_tick)/Msp::Time::sec;
last_tick = t;
- if(mode==MANIPULATE_DONE)
+ if(cur_tool && cur_tool->is_done())
+ {
+ cur_tool->update_selection(selection);
+ delete cur_tool;
+ cur_tool = 0;
mode = SELECT;
+ }
window.tick();
root.tick();
bool shift = keyboard.get_button_state(Input::KEY_SHIFT_L) || keyboard.get_button_state(Input::KEY_SHIFT_R);
if(key==Msp::Input::KEY_N && shift)
- extend_track();
+ use_tool<ExtendTool>();
else if(key==Msp::Input::KEY_N)
new_track();
else if(key==Msp::Input::KEY_G)
- {
- manipulator.start_move();
- mode = MANIPULATE;
- }
+ use_tool<MoveTool>();
else if(key==Msp::Input::KEY_R)
- {
- manipulator.start_rotate();
- mode = MANIPULATE;
- }
+ use_tool<RotateTool>();
else if(key==Msp::Input::KEY_D)
{
- manipulator.duplicate();
- manipulator.start_move();
- mode = MANIPULATE;
+ const set<Object *> &sel_objs = selection.get_objects();
+ list<Object *> new_objs;
+ for(set<Object *>::iterator i=sel_objs.begin(); i!=sel_objs.end(); ++i)
+ {
+ Object *obj = (*i)->clone(layout);
+ for(list<Object *>::iterator j=new_objs.begin(); j!=new_objs.end(); ++j)
+ obj->link_to(**j);
+ new_objs.push_back(obj);
+ }
+
+ selection.replace(new_objs.begin(), new_objs.end());
+
+ cur_tool = new MoveTool(*this, mouse, selection.get_objects());
}
else if(key==Msp::Input::KEY_W)
save();
else if(key==Msp::Input::KEY_L)
selection.select_linked();
else if(key==Msp::Input::KEY_M)
- {
- measure.start();
- mode = MEASURE;
- }
+ use_tool<Measure>();
else if(key==Msp::Input::KEY_Z)
- {
- manipulator.start_elevate();
- mode = MANIPULATE;
- }
+ use_tool<ElevateTool>();
else if(key==Msp::Input::KEY_ESC)
{
- if(mode==MANIPULATE)
- manipulator.cancel();
+ if(cur_tool)
+ {
+ delete cur_tool;
+ cur_tool = 0;
+ }
else if(mode==CATALOGUE)
mode = SELECT;
else
}
}
else if(key==Msp::Input::KEY_F)
- flatten_tracks();
+ use_tool<SlopeTool>()->flatten();
else if(key==Msp::Input::KEY_E && shift)
- manipulator.even_slope(true);
+ use_tool<SlopeTool>()->even_slope(true);
else if(key==Msp::Input::KEY_E)
- manipulator.even_slope();
+ use_tool<SlopeTool>()->even_slope();
else if(key==Msp::Input::KEY_A)
{
if(cur_route)
add_selection_to_zone();
}
else if(key==Msp::Input::KEY_C)
- connect_tracks();
+ use_tool<ExtendTool>()->connect();
else if(key==Msp::Input::KEY_V)
svg_export();
else if(key==Msp::Input::KEY_P)
track_properties();
}
+template<typename T>
+T *Designer::use_tool()
+{
+ if(cur_tool)
+ {
+ delete cur_tool;
+ cur_tool = 0;
+ }
+
+ T *tool = new T(*this, mouse, selection.get_objects());
+ cur_tool = tool;
+ cur_tool->signal_status.connect(sigc::mem_fun(this, &Designer::manipulation_status));
+ manipulation_status(cur_tool->get_status());
+ mode = TOOL;
+
+ return tool;
+}
+
void Designer::button_press(unsigned btn)
{
bool shift = keyboard.get_button_state(Input::KEY_SHIFT_L) || keyboard.get_button_state(Input::KEY_SHIFT_R);
}
}
}
- else if(mode==MEASURE)
- measure.button_press(ground.x, ground.y, btn);
-}
-
-void Designer::axis_motion(unsigned, float, float)
-{
- Vector pointer(mouse.get_axis_value(0), mouse.get_axis_value(1), 0);
- float wx = pointer.x*window.get_width();
- float wy = pointer.y*window.get_height();
-
- if(!root.get_child_at(wx, wy))
- {
- Vector ground = map_pointer_to_ground(pointer);
- measure.pointer_motion(ground.x, ground.y);
- }
}
void Designer::render()
lbl_status->set_text(status);
}
-void Designer::manipulation_done(bool)
-{
- mode = MANIPULATE_DONE;
- selection_changed();
-}
-
-void Designer::measure_changed()
-{
- float pard = measure.get_parallel_distance()*1000;
- float perpd = measure.get_perpendicular_distance()*1000;
- float d = sqrt(pard*pard+perpd*perpd);
- const Angle &adiff = measure.get_angle_difference();
- string info = format("Par %.1fmm - Perp %.1fmm - Total %.1fmm - Angle %.1f°", pard, perpd, d, adiff.degrees());
- lbl_status->set_text(info);
-}
-
void Designer::measure_done()
{
mode = SELECT;
{
SELECT,
CATALOGUE,
- MANIPULATE,
- MANIPULATE_DONE,
- MEASURE
+ TOOL
};
Msp::Graphics::SimpleGLWindow window;
Mode mode;
Selection selection;
SelectionWrap sel_wrap;
- Manipulator manipulator;
- Measure measure;
+ Tool *cur_tool;
CameraController *camera_ctl;
Msp::Time::TimeStamp last_tick;
private:
void tick();
void key_press(unsigned);
+ template<typename T>
+ T *use_tool();
void button_press(unsigned);
- void axis_motion(unsigned, float, float);
void apply_camera();
void render();
R2C2::Object *pick_object(const R2C2::Vector &);
--- /dev/null
+#include <msp/strings/format.h>
+#include "elevatetool.h"
+
+using namespace std;
+using namespace Msp;
+using namespace R2C2;
+
+ElevateTool::ElevateTool(Designer &d, Input::Mouse &m, const set<Object *> &o):
+ Manipulator(d, m, o)
+{
+ origin = pointer.y;
+}
+
+void ElevateTool::axis_motion(unsigned axis, float value, float)
+{
+ if(axis==1)
+ {
+ Vector offset(0, 0, value-origin);
+
+ signal_status.emit(format("Elevation: %+.0fmm (%.0fmm)", offset.z*1000, (center.z+offset.z)*1000));
+
+ for(ObjectArray::iterator i=objects.begin(); i!=objects.end(); ++i)
+ i->object->set_position(center+i->original_position+offset);
+ }
+}
--- /dev/null
+#ifndef ELEVATETOOL_H_
+#define ELEVATETOOL_H_
+
+#include "manipulator.h"
+
+class ElevateTool: public Manipulator
+{
+private:
+ float origin;
+
+public:
+ ElevateTool(Designer &, Msp::Input::Mouse &, const std::set<R2C2::Object *> &);
+
+ virtual void axis_motion(unsigned, float, float);
+};
+
+#endif
--- /dev/null
+#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());
+}
--- /dev/null
+#ifndef EXTENDTOOL_H_
+#define EXTENDTOOL_H_
+
+#include <set>
+#include <vector>
+#include "libr2c2/track.h"
+#include "tool.h"
+
+class ExtendTool: public Tool
+{
+private:
+ std::set<R2C2::Object *> objects;
+ std::vector<R2C2::Track *> extend_tracks;
+ bool accepted;
+
+public:
+ ExtendTool(Designer &, Msp::Input::Mouse &, const std::set<R2C2::Object *> &);
+ virtual ~ExtendTool();
+
+ void connect();
+
+ virtual void button_press(unsigned);
+ virtual void axis_motion(unsigned, float, float);
+
+private:
+ std::vector<R2C2::Track *> create_straight(const R2C2::Vector &, const R2C2::Angle &, float, float);
+
+public:
+ virtual void update_selection(Selection &) const;
+};
+
+#endif
using namespace R2C2;
using namespace Msp;
-Manipulator::Manipulator(Designer &d, Input::Mouse &m, Selection &s):
- designer(d),
- mouse(m),
- selection(s),
- mode(NONE)
+Manipulator::Manipulator(Designer &d, Input::Mouse &m, const set<Object *> &objs):
+ Tool(d, m),
+ accepted(false)
{
- mouse.signal_button_press.connect(sigc::bind_return(sigc::mem_fun(this, &Manipulator::button_press), false));
- mouse.signal_axis_motion.connect(sigc::bind_return(sigc::mem_fun(this, &Manipulator::axis_motion), false));
- selection.signal_changed.connect(sigc::mem_fun(this, &Manipulator::selection_changed));
-}
-
-void Manipulator::start_move()
-{
- if(mode)
- cancel();
-
- move_origin = gpointer;
-
- mode = MOVE;
-}
-
-void Manipulator::start_rotate()
-{
- if(mode)
- cancel();
-
- rot_origin = Geometry::atan2(gpointer.y-center.y, gpointer.x-center.x);
-
- mode = ROTATE;
-}
-
-void Manipulator::start_elevate()
-{
- if(mode)
- cancel();
-
- mode = ELEVATE;
-
- elev_origin = pointer.y;
-}
-
-bool Manipulator::start_extend()
-{
- if(mode)
- cancel();
-
- bool ok = false;
- const set<Track *> &stracks = selection.get_objects<Track>();
- for(set<Track *>::const_iterator i=stracks.begin(); (!ok && i!=stracks.end()); ++i)
- {
- const vector<Track *> &links = (*i)->get_links();
- for(vector<Track *>::const_iterator j=links.begin(); (!ok && j!=links.end()); ++j)
- ok = !*j;
- }
-
- if(ok)
- mode = EXTEND;
- else
- signal_status.emit("No free endpoints");
-
- return ok;
-}
-
-void Manipulator::duplicate()
-{
- if(mode)
- cancel();
-
- list<Object *> new_objs;
- for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
- {
- Object *obj = i->object->clone(&designer.get_layout());
- for(list<Object *>::iterator j=new_objs.begin(); j!=new_objs.end(); ++j)
- obj->link_to(**j);
- new_objs.push_back(obj);
- }
-
- selection.replace(new_objs.begin(), new_objs.end());
-}
-
-void Manipulator::flatten()
-{
- if(mode)
- cancel();
-
- if(objects.empty())
- return;
-
- float z = 0;
- for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
- {
- unsigned nsn = i->object->get_n_snap_nodes();
- for(unsigned j=0; j<nsn; ++j)
- z += i->object->get_snap_node(j).position.z/nsn;
- }
- z /= static_cast<int>(objects.size());
-
- for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
- {
- Vector p = i->object->get_position();
- i->object->set_position(Vector(p.x, p.y, z));
- if(Track *track = dynamic_cast<Track *>(i->object))
- track->set_tilt(Angle::zero());
- }
-
- update_objects();
-}
-
-void Manipulator::even_slope(bool smooth)
-{
- if(mode)
- cancel();
-
- if(neighbors.size()!=2)
- return;
-
- for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
- if(i->object->get_n_link_slots()!=2)
- return;
-
- list<Track *> tracks2;
- for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
- if(Track *track = dynamic_cast<Track *>(i->object))
- tracks2.push_back(track);
-
- float total_len = 0;
-
- list<TrackOrder> order;
- Track *cur = dynamic_cast<Track *>(*neighbors.begin());
- while(tracks2.size())
- {
- bool rev = false;
- for(list<Track *>::iterator i=tracks2.begin(); i!=tracks2.end(); ++i)
- {
- const vector<Track *> &links = (*i)->get_links();
- if(links[0]==cur)
- {
- cur = *i;
- tracks2.erase(i);
- break;
- }
- else if(links[1]==cur)
- {
- cur = *i;
- rev = true;
- tracks2.erase(i);
- break;
- }
- }
- order.push_back(TrackOrder(cur, rev));
- total_len += cur->get_type().get_total_length();
- }
-
- set<Object *>::iterator nb = neighbors.begin();
- int epi = (*nb)->get_link_slot(*order.front().track);
- float start_z = (*nb)->get_snap_node(epi).position.z;
- ++nb;
- epi = (*nb)->get_link_slot(*order.back().track);
- float end_z = (*nb)->get_snap_node(epi).position.z;
-
- if(smooth)
- {
- float dir = (end_z>start_z)?1:-1;
- float cur_slope = 0;
- while((end_z-start_z)*dir/total_len>cur_slope+0.025 && order.size()>2)
- {
- cur_slope += 0.025;
- Angle tilt = Geometry::atan(cur_slope);
-
- set_slope(order.front(), start_z, tilt);
- start_z += order.front().track->get_type().get_path_length(0)*dir*cur_slope;
- total_len -= order.front().track->get_type().get_path_length(0);
- order.pop_front();
-
- end_z -= order.back().track->get_type().get_path_length(0)*dir*cur_slope;
- set_slope(order.back(), end_z, tilt);
- total_len -= order.back().track->get_type().get_path_length(0);
- order.pop_back();
- }
- }
-
- float cur_z = start_z;
- Angle tilt = Geometry::atan((end_z-start_z)/total_len);
- for(list<TrackOrder>::iterator i=order.begin(); i!=order.end(); ++i)
- {
- set_slope(*i, cur_z, tilt);
- cur_z += i->track->get_type().get_path_length(0)*(end_z-start_z)/total_len;
- }
-
- update_objects();
-}
-
-void Manipulator::connect()
-{
- if(objects.size()!=2)
- {
- signal_status.emit("Exactly two tracks must be selected");
- return;
- }
-
- Track *track1 = dynamic_cast<Track *>(objects.front().object);
- Track *track2 = dynamic_cast<Track *>(objects.back().object);
- 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;
- for(unsigned i=0; i<track1->get_type().get_endpoints().size(); ++i)
- {
- if(track1->get_link(i))
- continue;
-
- sn1 = track1->get_snap_node(i);
-
- for(unsigned j=0; j<track2->get_type().get_endpoints().size(); ++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)
- {
- signal_status.emit("No aligned endpoints found");
- return;
- }
-
- vector<Track *> trks = create_straight(sn1.position, sn1.rotation, gap, limit);
-
- if(trks.empty())
- {
- signal_status.emit("No connection possible");
- return;
- }
-
- trks.front()->link_to(*track1);
- trks.back()->link_to(*track2);
-
- selection.replace(trks.begin(), trks.end());
-}
-
-void Manipulator::cancel()
-{
- if(!mode)
- return;
- mode = NONE;
-
- for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
- {
- i->object->set_position(center+i->pos);
- i->object->set_rotation(i->rot);
- }
-
- for(vector<Track *>::iterator i=extend_tracks.begin(); i!=extend_tracks.end(); ++i)
- delete *i;
- extend_tracks.clear();
-
- angle = Angle::zero();
-
- signal_done.emit(false);
-}
-
-void Manipulator::button_press(unsigned btn)
-{
- if(!mode)
- return;
-
- if(btn==3)
- cancel();
- else if(btn==1)
- {
- Mode m = mode;
- mode = NONE;
- angle = Angle::zero();
-
- if(m!=EXTEND)
- {
- for(set<Object *>::iterator i=neighbors.begin(); i!=neighbors.end(); ++i)
- for(vector<MObject>::iterator j=objects.begin(); j!=objects.end(); ++j)
- j->object->break_link(**i);
- }
-
- const set<Track *> <racks = designer.get_layout().get_all<Track>();
- for(set<Track *>::const_iterator i=ltracks.begin(); i!=ltracks.end(); ++i)
- {
- bool ok = true;
- for(vector<MObject>::iterator j=objects.begin(); (j!=objects.end() && ok); ++j)
- ok = (j->object!=*i);
- if(!ok) continue;
-
- for(vector<MObject>::iterator j=objects.begin(); j!=objects.end(); ++j)
- j->object->link_to(**i);
- }
-
- if(m==EXTEND)
- {
- selection.replace(extend_tracks.begin(), extend_tracks.end());
- extend_tracks.clear();
- }
- else
- {
- update_objects();
- update_neighbors();
- }
-
- signal_done.emit(true);
- }
-}
-
-void Manipulator::axis_motion(unsigned axis, float value, float)
-{
- if(axis==0)
- pointer.x = value;
- else if(axis==1)
- pointer.y = value;
- gpointer = designer.map_pointer_to_ground(pointer);
-
- if(mode==MOVE)
- {
- Vector offset = center+gpointer-move_origin;
- for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
- {
- i->object->set_position(offset+i->pos);
- i->object->set_rotation(i->rot);
- }
-
- const set<Track *> <racks = designer.get_layout().get_all<Track>();
- float limit = max(designer.get_layout().get_catalogue().get_gauge(),
- designer.get_camera_controller().get_view_scale()/100.0f);
- MObject *snapped = 0;
- for(set<Track *>::const_iterator i=ltracks.begin(); (i!=ltracks.end() && !snapped); ++i)
- {
- bool ok = true;
- for(vector<MObject>::iterator j=objects.begin(); (j!=objects.end() && ok); ++j)
- ok = (j->object!=*i);
- if(!ok)
- continue;
-
- for(vector<MObject>::iterator j=objects.begin(); (j!=objects.end() && !snapped); ++j)
- if(j->object->snap_to(**i, limit))
- snapped = &*j;
- }
-
- if(snapped)
- {
- Angle da = snapped->object->get_rotation()-snapped->rot;
- Transform trans = Transform::rotation(da, Vector(0, 0, 1));
- const Vector &sp = snapped->object->get_position();
- for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
- {
- if(&*i==snapped)
- continue;
-
- i->object->set_position(sp+trans.transform(i->pos-snapped->pos));
- i->object->set_rotation(i->rot+da);
- }
- }
- }
- else if(mode==ROTATE)
- {
- Angle a = Geometry::atan2(gpointer.y-center.y, gpointer.x-center.x);
- angle += a-rot_origin;
- rot_origin = a;
-
- Transform trans = Transform::rotation(angle, Vector(0, 0, 1));
- for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
- {
- i->object->set_position(center+trans.transform(i->pos));
- i->object->set_rotation(angle+i->rot);
- }
- }
- else if(mode==ELEVATE && axis==1)
- {
- float dz = pointer.y-elev_origin;
-
- signal_status.emit(format("Elevation: %+.0fmm (%.0fmm)", dz*1000, (center.z+dz)*1000));
-
- for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
- i->object->set_position(center+i->pos+Vector(0, 0, dz));
- }
- else if(mode==EXTEND)
- {
- Vector pos;
- Angle dir;
- float length = 0;
- for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
- {
- unsigned nls = i->object->get_n_link_slots();
- for(unsigned j=0; j<nls; ++j)
- {
- if(i->object->get_link(j))
- continue;
-
- Snap sn = i->object->get_snap_node(j);
- Vector delta = rotated_vector(gpointer-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));
- }
- }
- }
-}
-
-void Manipulator::selection_changed()
-{
- if(mode)
- cancel();
-
- objects.clear();
- set<Object *> pending = selection.get_objects();
+ set<Object *> pending = objs;
while(!pending.empty())
{
for(set<Object *>::iterator i=pending.begin(); i!=pending.end(); )
}
}
- update_neighbors();
- update_objects();
-}
-
-void Manipulator::update_objects()
-{
Geometry::BoundingBox<float, 3> bbox;
for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
bbox = bbox|i->object->get_bounding_box();
center = (minp+maxp)/2.0f;
center.z = minp.z;
- for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
+ for(ObjectArray::iterator i=objects.begin(); i!=objects.end(); ++i)
{
- i->pos = i->object->get_position()-center;
- i->rot = i->object->get_rotation();
+ i->original_position = i->object->get_position()-center;
+ i->original_rotation = i->object->get_rotation();
}
}
-void Manipulator::update_neighbors()
+Manipulator::~Manipulator()
{
- neighbors.clear();
- for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
+ if(!accepted)
{
- unsigned nls = i->object->get_n_link_slots();
- for(unsigned j=0; j<nls; ++j)
+ for(ObjectArray::iterator i=objects.begin(); i!=objects.end(); ++i)
{
- Object *linked = i->object->get_link(j);
- if(!linked)
- continue;
- if(neighbors.count(linked))
- continue;
-
- bool ok = true;
- for(vector<MObject>::iterator k=objects.begin(); (k!=objects.end() && ok); ++k)
- ok = (k->object!=linked);
-
- if(ok)
- neighbors.insert(linked);
+ i->object->set_position(center+i->original_position);
+ i->object->set_rotation(i->original_rotation);
}
}
}
-void Manipulator::set_slope(TrackOrder &track, float z, const Angle &tilt)
-{
- const Vector &p = track.track->get_position();
- float dz = tan(tilt)*track.track->get_type().get_path_length(0);
- if(track.rev)
- {
- track.track->set_position(Vector(p.x, p.y, z+dz));
- track.track->set_tilt(-tilt);
- }
- else
- {
- track.track->set_position(Vector(p.x, p.y, z));
- track.track->set_tilt(tilt);
- }
-}
-
-vector<Track *> Manipulator::create_straight(const R2C2::Vector &start, const Angle &dir, float length, float limit)
+void Manipulator::button_press(unsigned btn)
{
- const Catalogue::TrackMap &track_types = designer.get_catalogue().get_tracks();
- std::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)
+ if(btn==3)
+ set_done();
+ else if(btn==1)
{
- 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())
+ set<Object *> object_set;
+ for(ObjectArray::iterator i=objects.begin(); i!=objects.end(); ++i)
+ object_set.insert(i->object);
+ for(ObjectArray::iterator i=objects.begin(); i!=objects.end(); ++i)
{
- if(preference>0)
- {
- --preference;
- removed = 0;
- continue;
- }
- break;
+ unsigned nls = i->object->get_n_link_slots();
+ for(unsigned j=0; j<nls; ++j)
+ if(Object *link = i->object->get_link(j))
+ if(!object_set.count(link))
+ i->object->break_link(j);
}
- 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)
+ const set<Track *> <racks = designer.get_layout().get_all<Track>();
+ for(set<Track *>::const_iterator i=ltracks.begin(); i!=ltracks.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);
+ bool ok = true;
+ for(vector<MObject>::iterator j=objects.begin(); (j!=objects.end() && ok); ++j)
+ ok = (j->object!=*i);
+ if(!ok) continue;
- pos += trans.transform(Vector(*i, 0, 0));
+ for(vector<MObject>::iterator j=objects.begin(); j!=objects.end(); ++j)
+ j->object->link_to(**i);
}
- }
- return trks;
+ accepted = true;
+ set_done();
+ }
}
Manipulator::MObject::MObject(Object *o):
object(o),
- pos(object->get_position()),
- rot(object->get_rotation())
+ original_position(object->get_position()),
+ original_rotation(object->get_rotation())
{ }
#ifndef MANIPULATOR_H_
#define MANIPULATOR_H_
-#include <sigc++/sigc++.h>
-#include "libr2c2/geometry.h"
+#include <set>
+#include <vector>
+#include "libr2c2/object.h"
+#include "tool.h"
-class Designer;
class Selection;
-class Manipulator
+class Manipulator: public Tool
{
-private:
- enum Mode
- {
- NONE,
- MOVE,
- ROTATE,
- ELEVATE,
- EXTEND
- };
-
+protected:
struct MObject
{
R2C2::Object *object;
- R2C2::Vector pos;
- R2C2::Angle rot;
+ R2C2::Vector original_position;
+ R2C2::Angle original_rotation;
MObject(R2C2::Object *);
};
- struct TrackOrder
- {
- R2C2::Track *track;
- bool rev;
-
- TrackOrder(R2C2::Track *t, bool r): track(t), rev(r) { }
- };
-
public:
- sigc::signal<void, const std::string &> signal_status;
sigc::signal<void, bool> signal_done;
-private:
- Designer &designer;
- Msp::Input::Mouse &mouse;
- Selection &selection;
- std::vector<MObject> objects;
- R2C2::Vector center;
+protected:
+ typedef std::vector<MObject> ObjectArray;
- R2C2::Vector pointer;
- R2C2::Vector gpointer;
- Mode mode;
- R2C2::Vector move_origin;
- R2C2::Angle angle;
- R2C2::Angle rot_origin;
- float elev_origin;
- std::set<R2C2::Object *> neighbors;
- std::vector<R2C2::Track *> extend_tracks;
+ ObjectArray objects;
+ R2C2::Vector center;
+ bool accepted;
public:
- Manipulator(Designer &, Msp::Input::Mouse &, Selection &);
+ Manipulator(Designer &, Msp::Input::Mouse &, const std::set<R2C2::Object *> &);
+ virtual ~Manipulator();
- void start_move();
- void start_rotate();
- void start_elevate();
- bool start_extend();
- void duplicate();
- void flatten();
- void even_slope(bool =false);
- void connect();
- void cancel();
private:
- void button_press(unsigned);
- void axis_motion(unsigned, float, float);
- void selection_changed();
- void update_objects();
- void update_neighbors();
- void set_slope(TrackOrder &, float, const R2C2::Angle &);
- std::vector<R2C2::Track *> create_straight(const R2C2::Vector &, const R2C2::Angle &, float, float);
+ virtual void button_press(unsigned);
};
#endif
#include <cmath>
#include <msp/gl/meshbuilder.h>
#include <msp/gl/renderer.h>
+#include <msp/strings/format.h>
#include "designer.h"
#include "3d/layout.h"
#include "measure.h"
using namespace R2C2;
using namespace Msp;
-Measure::Measure(Designer &d):
- designer(d),
- state(NONE),
+Measure::Measure(Designer &d, Input::Mouse &m, const set<Object *> &):
+ Tool(d, m),
+ start_pinned(false),
mesh((GL::COLOR4_UBYTE, GL::VERTEX3))
-{ }
-
-void Measure::start()
{
- state = STARTING;
update_mesh();
+
+ designer.get_layout_3d().get_scene().add(*this);
}
-void Measure::button_press(float gx, float gy, unsigned btn)
+Measure::~Measure()
{
- if(!state)
- return;
+ designer.get_layout_3d().get_scene().remove(*this);
+}
+void Measure::button_press(unsigned btn)
+{
if(btn==1)
{
- ssnap.position = Vector(gx, gy, 0);
- ssnap.rotation = Angle::zero();
- snap_to_tracks(ssnap);
+ start.position = ground_pointer;
+ start.rotation = Angle::zero();
+ snap_to_tracks(start);
- state = ACTIVE;
+ start_pinned = true;
}
else if(btn==3)
{
- if(state==ACTIVE)
+ if(start_pinned)
{
- state = STARTING;
+ start_pinned = false;
update_mesh();
}
else
{
- state = NONE;
+ done = true;
signal_done.emit();
}
}
}
-void Measure::pointer_motion(float gx, float gy)
+void Measure::axis_motion(unsigned axis, float x, float y)
{
- if(!state)
- return;
+ Tool::axis_motion(axis, x, y);
- Snap sn = ssnap;
- sn.position = Vector(gx, gy, 0);
+ Snap sn = start;
+ sn.position = ground_pointer;
snap_to_tracks(sn);
- pointer = sn.position;
+ ground_pointer = sn.position;
- if(state!=STARTING)
+ if(start_pinned)
{
- Vector delta = rotated_vector(pointer-ssnap.position, -ssnap.rotation);
+ Vector delta = rotated_vector(ground_pointer-start.position, -start.rotation);
par_dist = delta.x;
perp_dist = delta.y;
- adiff = wrap_balanced(sn.rotation-ssnap.rotation+Angle::half_turn());
+ adiff = wrap_balanced(sn.rotation-start.rotation+Angle::half_turn());
update_mesh();
- signal_changed.emit();
+ string info = format("Par %.1fmm - Perp %.1fmm - Total %.1fmm - Angle %.1f°", par_dist*1000, perp_dist*1000, delta.norm()*1000, adiff.degrees());
+ signal_status.emit(info);
}
}
void Measure::render(GL::Renderer &renderer, const GL::Tag &) const
{
- if(state==NONE)
- return;
-
GL::Renderer::Push push(renderer);
- const Vector &pos = (state==ACTIVE ? ssnap.position : pointer);
+ const Vector &pos = (start_pinned ? start.position : ground_pointer);
renderer.matrix_stack() *= GL::Matrix::translation(pos);
mesh.draw(renderer);
}
bld.end();
- if(state==ACTIVE)
+ if(start_pinned)
{
- float c = cos(ssnap.rotation);
- float s = sin(ssnap.rotation);
+ float c = cos(start.rotation);
+ float s = sin(start.rotation);
bld.begin(GL::QUAD_STRIP);
bld.vertex(0, 0, 0);
bld.vertex(0, 0, 0.01);
bld.vertex(c*par_dist, s*par_dist, 0);
bld.vertex(c*par_dist, s*par_dist, 0.01);
- bld.vertex(pointer.x-ssnap.position.x, pointer.y-ssnap.position.y, 0);
- bld.vertex(pointer.x-ssnap.position.x, pointer.y-ssnap.position.y, 0.01);
+ bld.vertex(ground_pointer.x-start.position.x, ground_pointer.y-start.position.y, 0);
+ bld.vertex(ground_pointer.x-start.position.x, ground_pointer.y-start.position.y, 0.01);
bld.vertex(0, 0, 0);
bld.vertex(0, 0, 0.01);
bld.end();
#include <sigc++/sigc++.h>
#include <msp/gl/renderable.h>
#include "libr2c2/geometry.h"
+#include "tool.h"
class Designer;
-class Measure: public Msp::GL::Renderable
+class Measure: public Tool, public Msp::GL::Renderable
{
private:
- enum State
- {
- NONE,
- STARTING,
- ACTIVE
- };
-
-public:
- sigc::signal<void> signal_done;
- sigc::signal<void> signal_changed;
-
-private:
- Designer &designer;
- R2C2::Vector pointer;
- R2C2::Snap ssnap;
+ R2C2::Snap start;
+ bool start_pinned;
float par_dist;
float perp_dist;
R2C2::Angle adiff;
- State state;
Msp::GL::Mesh mesh;
public:
- Measure(Designer &);
+ Measure(Designer &, Msp::Input::Mouse &, const std::set<R2C2::Object *> &);
+ virtual ~Measure();
+
float get_parallel_distance() const { return par_dist; }
float get_perpendicular_distance() const { return perp_dist; }
const R2C2::Angle &get_angle_difference() const { return adiff; }
- void start();
- void button_press(float, float, unsigned);
- void pointer_motion(float, float);
+
+ virtual void button_press(unsigned);
+ virtual void axis_motion(unsigned, float, float);
+
virtual void render(Msp::GL::Renderer &, const Msp::GL::Tag &) const;
private:
void update_mesh();
--- /dev/null
+#include "libr2c2/layout.h"
+#include "designer.h"
+#include "movetool.h"
+
+using namespace std;
+using namespace Msp;
+using namespace R2C2;
+
+MoveTool::MoveTool(Designer &d, Input::Mouse &m, const set<Object *> &o):
+ Manipulator(d, m, o),
+ origin(ground_pointer)
+{ }
+
+void MoveTool::axis_motion(unsigned axis, float value, float rel)
+{
+ Manipulator::axis_motion(axis, value, rel);
+
+ Vector offset = center+ground_pointer-origin;
+ for(vector<MObject>::iterator i=objects.begin(); i!=objects.end(); ++i)
+ {
+ i->object->set_position(offset+i->original_position);
+ i->object->set_rotation(i->original_rotation);
+ }
+
+ const set<Track *> <racks = designer.get_layout().get_all<Track>();
+ float limit = max(designer.get_layout().get_catalogue().get_gauge(),
+ designer.get_camera_controller().get_view_scale()/100.0f);
+ MObject *snapped = 0;
+ for(set<Track *>::const_iterator i=ltracks.begin(); (i!=ltracks.end() && !snapped); ++i)
+ {
+ bool ok = true;
+ for(ObjectArray::iterator j=objects.begin(); (j!=objects.end() && ok); ++j)
+ ok = (j->object!=*i);
+ if(!ok)
+ continue;
+
+ for(ObjectArray::iterator j=objects.begin(); (j!=objects.end() && !snapped); ++j)
+ if(j->object->snap_to(**i, limit))
+ snapped = &*j;
+ }
+
+ if(snapped)
+ {
+ Angle da = snapped->object->get_rotation()-snapped->original_rotation;
+ Transform trans = Transform::rotation(da, Vector(0, 0, 1));
+ const Vector &sp = snapped->object->get_position();
+ for(ObjectArray::iterator i=objects.begin(); i!=objects.end(); ++i)
+ {
+ if(&*i==snapped)
+ continue;
+
+ i->object->set_position(sp+trans.transform(i->original_position-snapped->original_position));
+ i->object->set_rotation(i->original_rotation+da);
+ }
+ }
+}
--- /dev/null
+#ifndef MOVETOOL_H_
+#define MOVETOOL_H_
+
+#include "manipulator.h"
+
+class MoveTool: public Manipulator
+{
+private:
+ R2C2::Vector origin;
+
+public:
+ MoveTool(Designer &, Msp::Input::Mouse &, const std::set<R2C2::Object *> &);
+
+ virtual void axis_motion(unsigned, float, float);
+};
+
+#endif
--- /dev/null
+#include "rotatetool.h"
+
+using namespace std;
+using namespace Msp;
+using namespace R2C2;
+
+RotateTool::RotateTool(Designer &d, Input::Mouse &m, const set<Object *> &o):
+ Manipulator(d, m, o),
+ origin(Geometry::atan2(ground_pointer.y-center.y, ground_pointer.x-center.x))
+{ }
+
+void RotateTool::axis_motion(unsigned axis, float value, float rel)
+{
+ Manipulator::axis_motion(axis, value, rel);
+
+ Angle a = Geometry::atan2(ground_pointer.y-center.y, ground_pointer.x-center.x);
+ angle += a-origin;
+ origin = a;
+
+ Transform trans = Transform::rotation(angle, Vector(0, 0, 1));
+ for(ObjectArray::iterator i=objects.begin(); i!=objects.end(); ++i)
+ {
+ i->object->set_position(center+trans.transform(i->original_position));
+ i->object->set_rotation(angle+i->original_rotation);
+ }
+}
--- /dev/null
+#ifndef ROTATETOOL_H_
+#define ROTATETOOL_H_
+
+#include "manipulator.h"
+
+class RotateTool: public Manipulator
+{
+private:
+ R2C2::Angle origin;
+ R2C2::Angle angle;
+
+public:
+ RotateTool(Designer &, Msp::Input::Mouse &, const std::set<R2C2::Object *> &);
+
+ virtual void axis_motion(unsigned, float, float);
+};
+
+#endif
--- /dev/null
+#include "slopetool.h"
+
+using namespace std;
+using namespace Msp;
+using namespace R2C2;
+
+SlopeTool::SlopeTool(Designer &d, Input::Mouse &m, const set<Object *> &objects):
+ Tool(d, m),
+ total_length(0)
+{
+ for(set<Object *>::const_iterator i=objects.begin(); i!=objects.end(); ++i)
+ {
+ unsigned nls = (*i)->get_n_link_slots();
+ if(nls!=2 || !dynamic_cast<Track *>(*i))
+ {
+ set_status("Must have linear tracks only");
+ return;
+ }
+ }
+
+ 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(Track *link = dynamic_cast<Track *>((*i)->get_link(j)))
+ if(!objects.count(link))
+ neighbors.push_back(link);
+ }
+
+ list<Track *> tmp_tracks;
+ for(set<Object *>::iterator i=objects.begin(); i!=objects.end(); ++i)
+ if(Track *track = dynamic_cast<Track *>(*i))
+ tmp_tracks.push_back(track);
+
+ Track *cur = neighbors.front();
+ while(!tmp_tracks.empty())
+ {
+ bool rev = false;
+ for(list<Track *>::iterator i=tmp_tracks.begin(); i!=tmp_tracks.end(); ++i)
+ {
+ const vector<Track *> &links = (*i)->get_links();
+ if(links[0]==cur)
+ {
+ cur = *i;
+ tmp_tracks.erase(i);
+ break;
+ }
+ else if(links[1]==cur)
+ {
+ cur = *i;
+ rev = true;
+ tmp_tracks.erase(i);
+ break;
+ }
+ }
+ tracks.push_back(TrackOrder(cur, rev));
+ total_length += cur->get_type().get_total_length();
+ }
+}
+
+void SlopeTool::even_slope(bool smooth)
+{
+ list<Track *>::iterator nb = neighbors.begin();
+ int epi = (*nb)->get_link_slot(*tracks.front().track);
+ float start_z = (*nb)->get_snap_node(epi).position.z;
+ ++nb;
+ epi = (*nb)->get_link_slot(*tracks.back().track);
+ float end_z = (*nb)->get_snap_node(epi).position.z;
+
+ float length = total_length;
+ if(smooth)
+ {
+ float dir = (end_z>start_z)?1:-1;
+ float cur_slope = 0;
+ while((end_z-start_z)*dir/length>cur_slope+0.025 && tracks.size()>2)
+ {
+ cur_slope += 0.025;
+ Angle tilt = Geometry::atan(cur_slope);
+
+ set_slope(tracks.front(), start_z, tilt);
+ start_z += tracks.front().track->get_type().get_path_length(0)*dir*cur_slope;
+ length -= tracks.front().track->get_type().get_path_length(0);
+ tracks.pop_front();
+
+ end_z -= tracks.back().track->get_type().get_path_length(0)*dir*cur_slope;
+ set_slope(tracks.back(), end_z, tilt);
+ length -= tracks.back().track->get_type().get_path_length(0);
+ tracks.pop_back();
+ }
+ }
+
+ float cur_z = start_z;
+ Angle tilt = Geometry::atan((end_z-start_z)/length);
+ for(list<TrackOrder>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
+ {
+ set_slope(*i, cur_z, tilt);
+ cur_z += i->track->get_type().get_path_length(0)*(end_z-start_z)/length;
+ }
+}
+
+void SlopeTool::flatten()
+{
+ float z = 0;
+ for(list<TrackOrder>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
+ {
+ unsigned nsn = i->track->get_n_snap_nodes();
+ for(unsigned j=0; j<nsn; ++j)
+ z += i->track->get_snap_node(j).position.z/nsn;
+ }
+ z /= static_cast<int>(tracks.size());
+
+ for(list<TrackOrder>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
+ {
+ Vector p = i->track->get_position();
+ i->track->set_position(Vector(p.x, p.y, z));
+ i->track->set_tilt(Angle::zero());
+ }
+}
+
+void SlopeTool::set_slope(TrackOrder &track, float z, const Angle &tilt)
+{
+ const Vector &p = track.track->get_position();
+ float dz = tan(tilt)*track.track->get_type().get_path_length(0);
+ if(track.rev)
+ {
+ track.track->set_position(Vector(p.x, p.y, z+dz));
+ track.track->set_tilt(-tilt);
+ }
+ else
+ {
+ track.track->set_position(Vector(p.x, p.y, z));
+ track.track->set_tilt(tilt);
+ }
+}
--- /dev/null
+#ifndef SLOPETOOL_H_
+#define SLOPETOOL_H_
+
+#include "tool.h"
+#include "libr2c2/track.h"
+
+class SlopeTool: public Tool
+{
+private:
+ struct TrackOrder
+ {
+ R2C2::Track *track;
+ bool rev;
+
+ TrackOrder(R2C2::Track *t, bool r): track(t), rev(r) { }
+ };
+
+ std::list<R2C2::Track *> neighbors;
+ std::list<TrackOrder> tracks;
+ float total_length;
+
+public:
+ SlopeTool(Designer &, Msp::Input::Mouse &, const std::set<R2C2::Object *> &);
+
+ void even_slope(bool =false);
+ void flatten();
+private:
+ void set_slope(TrackOrder &, float, const R2C2::Angle &);
+};
+
+#endif
--- /dev/null
+#include <sigc++/bind.h>
+#include "designer.h"
+#include "tool.h"
+
+using namespace Msp;
+
+Tool::Tool(Designer &d, Input::Mouse &mouse):
+ designer(d),
+ done(false)
+{
+ mouse.signal_button_press.connect(sigc::bind_return(sigc::mem_fun(this, &Tool::button_press), false));
+ mouse.signal_axis_motion.connect(sigc::bind_return(sigc::mem_fun(this, &Tool::axis_motion), false));
+
+ pointer.x = mouse.get_axis_value(0);
+ pointer.y = mouse.get_axis_value(1);
+ ground_pointer = designer.map_pointer_to_ground(pointer);
+}
+
+void Tool::set_status(const std::string &s)
+{
+ status = s;
+ signal_status.emit(status);
+}
+
+void Tool::set_done()
+{
+ done = true;
+ signal_done.emit();
+}
+
+void Tool::axis_motion(unsigned axis, float value, float)
+{
+ if(axis==0)
+ pointer.x = value;
+ else if(axis==1)
+ pointer.y = value;
+ ground_pointer = designer.map_pointer_to_ground(pointer);
+}
--- /dev/null
+#ifndef TOOL_H_
+#define TOOL_H_
+
+#include <string>
+#include <sigc++/signal.h>
+#include <sigc++/trackable.h>
+#include <msp/input/mouse.h>
+#include "libr2c2/geometry.h"
+
+class Designer;
+class Selection;
+
+class Tool: public sigc::trackable
+{
+public:
+ sigc::signal<void> signal_done;
+ sigc::signal<void, const std::string &> signal_status;
+
+protected:
+ Designer &designer;
+ R2C2::Vector pointer;
+ R2C2::Vector ground_pointer;
+ std::string status;
+ bool done;
+
+ Tool(Designer &, Msp::Input::Mouse &);
+public:
+ virtual ~Tool() { }
+
+protected:
+ void set_status(const std::string &);
+ void set_done();
+public:
+ const std::string &get_status() const { return status; }
+ bool is_done() const { return done; }
+
+ virtual void button_press(unsigned) { }
+ virtual void axis_motion(unsigned, float, float);
+
+ virtual void update_selection(Selection &) const { }
+};
+
+#endif