View.cpp

Go to the documentation of this file.
00001 
00005 /* Copyright © 2009, 2010 James Legg.
00006     This program is free software: you can redistribute it and/or modify
00007     it under the terms of the GNU General Public License as published by
00008     the Free Software Foundation, either version 3 of the License, or
00009     (at your option) any later version.
00010 */
00011 #include "View.h"
00012 #include <iostream>
00013 #include <Debug.h>
00014 
00015 #include <GL/gl.h>
00016 #include <GL/glu.h>
00017 
00018 #include <gtkmm/main.h>
00019 #include <gtkmm/radioaction.h>
00020 #include <gtkmm/stock.h>
00021 
00022 #include <libtrack/document/ChangeVertexSegmentDelta.h>
00023 #include <libtrack/document/ChangeEdgeSegmentDelta.h>
00024 #include <libtrack/document/InsertVertexDelta.h>
00025 #include <libtrack/document/InsertEdgeDelta.h>
00026 #include <libtrack/document/FlipEdgeDelta.h>
00027 #include <libtrack/document/SetStartEdgeDelta.h>
00028 #include <libtrack/document/InsertTrackAttachmentDelta.h>
00029 #include <libtrack/edit_base/TrackAttachmentHandle.h>
00030 #include <libtrack/TrackBooster.h>
00031 #include <libtrack/StartingPosition.h>
00032 
00033 const btScalar back_depth = 5000.0;
00034 const btScalar front_depth = -5000.0;
00035 
00036 View::View()
00037     : Gtk::GL::DrawingArea(Gdk::GL::Config::create(Gdk::GL::MODE_RGB |
00038                                                    Gdk::GL::MODE_DEPTH |
00039                                                    Gdk::GL::MODE_DOUBLE))
00040     ,   document(0)
00041     ,   scale(1.0)
00042     ,   handle_length(64.0 / scale)
00043     ,   centre(0.0, 0.0, 0.0)
00044     ,   view_angle(VIEW_TOP)
00045     ,   new_position(centre)
00046     ,   drag_object(0)
00047     ,   m_ui_manager(Gtk::UIManager::create())
00048     ,   m_menu_id(0)
00049     ,   m_popup_menu(0)
00050 {
00051     setup_ui();
00052 }
00053 
00054 View::View(View & view)
00055     :   Gtk::GL::DrawingArea(Gdk::GL::Config::create(Gdk::GL::MODE_RGB |
00056                                                      Gdk::GL::MODE_DEPTH |
00057                                                      Gdk::GL::MODE_DOUBLE),
00058                              view.get_gl_context(),
00059                              false, Gdk::GL::RGBA_TYPE)
00060     ,   document(view.document)
00061     ,   scale(view.scale)
00062     ,   handle_length(64.0 / scale)
00063     ,   centre(view.centre)
00064     ,   view_angle(VIEW_TOP)
00065     ,   new_position(centre)
00066     ,   drag_object(0)
00067     ,   m_ui_manager(Gtk::UIManager::create())
00068     ,   m_menu_id(0)
00069     ,   m_popup_menu(0)
00070 {
00071     setup_ui();
00072 }
00073 
00074 View::~View()
00075 {
00076 }
00077 
00078 void View::setup_ui()
00079 {
00080     add_events(  Gdk::SCROLL_MASK
00081            | Gdk::BUTTON_PRESS_MASK
00082            | Gdk::BUTTON_RELEASE_MASK
00083            | Gdk::POINTER_MOTION_MASK
00084           );
00085     
00086 }
00087 
00088 void View::set_document(Document::Document & document_in)
00089 {
00090     // clear up from last document if there was one.
00091     if (document)
00092     {
00093         m_ui_manager->remove_ui(m_menu_id);
00094         m_ui_manager->remove_action_group(m_action_group);
00095         m_menu_id = 0;
00096         m_popup_menu = 0;
00097         m_empty_menu = 0;
00098     }
00099     
00100     // new document
00101     document = &document_in;
00102     
00103     // actions which always exist
00104     m_action_group = Gtk::ActionGroup::create();
00105     m_action_group->add(Gtk::Action::create("popup-menu", "Vertex popup"));
00106     m_action_group->add(Gtk::Action::create("segments-menu", Gtk::Stock::CONVERT, "Segments"));
00107     m_action_group->add(Gtk::Action::create("delete-vertex", Gtk::Stock::DELETE, "Delete Vertex"),
00108         sigc::mem_fun(*this, &View::on_delete_vertex));
00109     m_action_group->add(Gtk::Action::create("delete-edge", Gtk::Stock::DELETE, "Delete Edge"),
00110         sigc::mem_fun(*this, &View::on_delete_edge));
00111     m_action_group->add(Gtk::Action::create("flip-edge", Gtk::Stock::ORIENTATION_REVERSE_PORTRAIT, "Reverse Edge direction"),
00112         sigc::mem_fun(*this, &View::on_flip_edge));
00113     m_action_group->add(Gtk::Action::create("add-booster", Gtk::Stock::ADD, "Add booster"),
00114         sigc::mem_fun(*this, &View::on_add_booster));
00115     m_action_group->add(Gtk::Action::create("set-start-edge", Gtk::Stock::GOTO_FIRST, "Make starting positions"),
00116         sigc::mem_fun(*this, &View::on_set_start_edge));
00117     // use the document's theme's segments for a menu item and an action.
00118     Glib::ustring ui_buffer =
00119         "<ui>"
00120         "  <popup name='popup-menu' action='popup-menu'>"
00121         "     <menuitem name='delete-vertex' action='delete-vertex'/>"
00122         "     <menuitem name='delete-edge' action='delete-edge'/>"
00123         "     <menuitem name='flip-edge' action='flip-edge'/>"
00124         "     <menuitem name='set-start-edge' action='set-start-edge'/>"
00125         "     <menuitem name='add-booster' action='add-booster'/>"
00126         "     <menu name='segments-menu' action='segments-menu'>";
00127     
00128     Gtk::RadioAction::Group radio_group;
00129     const Track::Theme & theme(document->get_track().get_theme());
00130     std::size_t number_of_segments = theme.get_number_of_segments();
00131     for (std::size_t segment_index = 0;
00132          segment_index < number_of_segments; segment_index++)
00133     {
00134         const Track::Segment & segment(theme.get_segment(segment_index));
00135         ui_buffer += "<menuitem name=\"";
00136         ui_buffer += Glib::ustring(segment.get_name());
00137         ui_buffer += "\" action=\"segment-";
00138         ui_buffer += Glib::ustring(segment.get_name());
00139         ui_buffer += "\"/>";
00140         // make the action that uses this.
00141         Glib::ustring action_name = "segment-";
00142         action_name += Glib::ustring(segment.get_name());
00143         m_action_group->add(Gtk::RadioAction::create(radio_group, action_name, segment.get_name()),
00144                             sigc::bind(sigc::mem_fun(*this, &View::pick_segment), segment_index));
00145     }
00146     ui_buffer +=
00147         "     </menu>"
00148         "  </popup>"
00149         "  <popup name='empty-menu' action='empty-menu'>"
00150         "     <menuitem name='insert-vertex' action='insert-vertex'/>"
00151         "  </popup>"
00152         "  <popup name='attachment-menu' action='attachment-menu'>"
00153         "     <menuitem name='delete-attachment' action='delete-attachment'/>"
00154         "  </popup>"
00155         "</ui>";
00156     m_action_group->add(Gtk::Action::create("insert-vertex", Gtk::Stock::ADD, "Insert Vertex"),
00157         sigc::mem_fun(*this, &View::on_insert_vertex));
00158     m_action_group->add(Gtk::Action::create("delete-attachment", Gtk::Stock::DELETE, "Delete Attachment"),
00159         sigc::mem_fun(*this, &View::on_delete_attachment));
00160     
00161     m_ui_manager->insert_action_group(m_action_group);
00162     m_menu_id = m_ui_manager->add_ui_from_string(ui_buffer);
00163     m_popup_menu = (Gtk::Menu *)m_ui_manager->get_widget("/popup-menu");
00164     assert(m_popup_menu);
00165     m_empty_menu = (Gtk::Menu *)m_ui_manager->get_widget("/empty-menu");
00166     assert(m_empty_menu);
00167     m_attachment_menu = (Gtk::Menu *)m_ui_manager->get_widget("/attachment-menu");
00168     assert(m_attachment_menu);
00169     set_scale(1.0);
00170 }
00171 
00172 void View::set_angle(const ViewAngle angle_in)
00173 {
00174     view_angle = angle_in;
00175     needs_recentre = true;
00176     queue_draw();
00177     
00178 }
00179 
00180 View::ViewAngle View::get_angle() const
00181 {
00182     return view_angle;
00183 }
00184 
00185 void View::set_scale(const float scale_in)
00186 {
00187     scale = scale_in;
00188     needs_recentre = true;
00189     handle_length = 64.0 / scale;
00191     queue_draw();
00192 }
00193 
00194 float View::get_scale() const
00195 {
00196     return scale;
00197 }
00198 
00199 void View::set_centre(const btVector3 centre_in)
00200 {
00201     // ignore request if dragging, since it will be confusing to have the
00202     // scene move while placing something on it.
00203     if (!drag_object)
00204     {
00205         centre = centre_in;
00206         needs_recentre = true;
00207         queue_draw();
00208         DEBUG_MESSAGE("Queued draw.");
00209     }
00210 }
00211 
00212 btVector3 View::get_centre() const
00213 {
00214     return centre;
00215 }
00216 
00217 void View::on_realize()
00218 {
00219     Gtk::GL::DrawingArea::on_realize();
00220     Glib::RefPtr<Gdk::GL::Drawable> gldrawable = get_gl_drawable();
00221     if (!gldrawable->gl_begin(get_gl_context()))
00222     {
00223         return;
00224     }
00225     
00226     gldrawable->gl_end();
00227 }
00228 
00229 bool View::on_configure_event(GdkEventConfigure* event)
00230 {
00231     Glib::RefPtr<Gdk::GL::Drawable> gldrawable = get_gl_drawable();
00232     if (!gldrawable->gl_begin(get_gl_context()))
00233     {
00234         return false;
00235     }
00236     DEBUG_MESSAGE("Reconfiguring view.");
00237     glViewport(0, 0, get_width(), get_height());
00238     glMatrixMode(GL_PROJECTION);
00239     glLoadIdentity();
00240     glOrtho(0.5, get_width() + 0.5,
00241             0.5, get_height() + 0.5,
00242             front_depth, back_depth);
00243     glMatrixMode(GL_MODELVIEW);
00244     recentre_view();
00245     gldrawable->gl_end();
00246     return true;    
00247 }
00248 
00249 bool View::on_scroll_event(GdkEventScroll* event)
00250 {
00251     if (drag_object)
00252     {
00253         // scrolling interferes with dragging objects
00255         return true;
00256     }
00257     return false;
00258 }
00259 
00260 void View::recentre_view()
00261 {
00262     glLoadIdentity();
00263     // translate half screen width, so we can deal with the centre rather
00264     // than the corner.
00265     glTranslatef(get_width() / 2, get_height() / 2, 0);
00266     glScalef(scale, scale, scale);
00267     // rotations
00268     switch (view_angle)
00269     {
00270         case VIEW_TOP:
00271             // xy plane is already in view.
00272             break;
00273         case VIEW_SIDE:
00274             // rotate 90 degrees along the y axis to get xy plane in view.
00275             glRotatef(90.0, -1.0, 0.0, 0.0);
00276             // Then rotate about the user to get z up the screen.
00277             glRotatef(-90.0, 0.0, 0.0, 1.0);
00278             break;
00279         case VIEW_FRONT:
00280             // rotate 90 degrees along the x axis to get yz plane in view.
00281             glRotatef(90.0, -1.0, 0.0, 0.0);
00282             break;
00283     }
00284     glTranslatef(-centre.x(), -centre.y(), -centre.z());
00285     // The depth into the screen will be snapped while dragging an object,
00286     // so leave it alone while dragging.
00287     // Otherwise, use a fixed depth.
00288     if (!drag_object)
00289     {
00290         set_relative_depth(new_position, back_depth);
00291     }
00292     needs_recentre = false;
00293 }
00294 
00295 bool View::on_expose_event(GdkEventExpose* event)
00296 {
00297     assert(document);
00298     Glib::RefPtr<Gdk::GL::Drawable> gldrawable = get_gl_drawable();
00299     if (!gldrawable->gl_begin(get_gl_context()))
00300     {
00301         DEBUG_MESSAGE("    No gl_drawable.");
00302         return false;
00303     }
00304     
00305     if (needs_recentre)
00306     {
00307         recentre_view();
00308         DEBUG_MESSAGE("    Recentered.");
00309     }
00310     
00311     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
00312     
00313     // draw grid lines
00314     glEnable(GL_BLEND);
00315     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00316     glPushMatrix();
00317     // rotate so the xy plane is on the screen.
00318     switch (view_angle)
00319     {
00320         case VIEW_TOP:
00321             // nothing needed
00322             break;
00323         case VIEW_SIDE:
00324             glRotatef(90.0, 0.0, 1.0, 0.0);
00325             break;
00326         case VIEW_FRONT:
00327             glRotatef(90.0, 1.0, 0.0, 0.0);
00328             break;
00329     }
00330     // draw grid lines in xy plane.
00331     glBegin(GL_LINES);
00332         for (int i = -256; i <= 256; i += 8)
00333         {
00334             // vary intensity to make minor grid lines less distinct.
00335             unsigned int l = (i + 256);
00336             unsigned char s;
00337             if (l % 16) s = 29;
00338             else if (l % 32) s = 40;
00339             else if (l % 64) s = 53;
00340             else if (l % 128) s = 71;
00341             else if (l % 256) s = 95;
00342             else s = 127;
00343             glColor4ub(255, 255, 255, s);
00344             // draw lines
00345             glVertex2d(i, -256);
00346             glVertex2d(i,  256);
00347             glVertex2d(-256, i);
00348             glVertex2d( 256, i);
00349         }
00350     glEnd();
00351     glPopMatrix();
00352     glDisable(GL_BLEND);
00353     
00354     // get the track to draw.
00355     const Track::Track & track = document->get_track();
00356     
00357     draw_path(track.get_path());
00358     
00359     // Swap buffers.
00360     if (gldrawable->is_double_buffered())
00361     {
00362         gldrawable->swap_buffers();
00363     }
00364     else
00365     {
00366         glFlush();
00367     }
00368     
00369     gldrawable->gl_end();
00370     return true;
00371 }
00372 
00373 inline void glVertex(const btVector3 & position)
00374 {
00375     glVertex3f(position.x(), position.y(), position.z());
00376 }
00377 
00378 void View::draw_path(const Track::Path & path)
00379 {
00382     const_cast<Track::Path &>(document->get_track().get_path()).set_handle_lengths(handle_length);
00383     glPointSize(5.0);
00384     
00385     glColor3ub(191, 191, 191);
00386     path.draw();
00387     
00388     // draw lines between the vertices.
00389     typedef boost::graph_traits<Track::Path::Graph>::edge_iterator EdgeIterator;
00390     std::pair<EdgeIterator, EdgeIterator> edge_range;
00391     for (edge_range = boost::edges(path.graph);
00392          edge_range.first != edge_range.second;
00393          edge_range.first++)
00394     {
00395         Track::PathVertex vertex_1 = path.graph[source(*(edge_range.first), path.graph)];
00396         Track::PathVertex vertex_2 = path.graph[target(*(edge_range.first), path.graph)];
00397         Track::PathEdge edge = path.graph[*(edge_range.first)];
00398         glColor3ub(255, 255, 255);
00399         glBegin(GL_LINE_STRIP);
00400             for (float t = 0.0; t <= 1.0; t += 0.015625)
00401             {
00402                 glVertex(edge.get_transform(t).getOrigin());
00403             }
00404         glEnd();
00405         
00406         edge.draw_control_points();
00407     }
00408     
00409     // draw markers at the vertices.
00410     glClear(GL_DEPTH_BUFFER_BIT);
00415         //const float marker_size = 3.0;
00416         // float offset = scale * marker_size;
00417         
00418     typedef boost::graph_traits<Track::Path::Graph>::vertex_iterator VertexIterator;
00419     std::pair<VertexIterator, VertexIterator> vertex_range;
00420     for (vertex_range = boost::vertices(path.graph);
00421          vertex_range.first != vertex_range.second;
00422          vertex_range.first++)
00423     {
00424         const Track::PathVertex & vertex = path.graph[*(vertex_range.first)];
00425         vertex.draw_control_points();
00426         
00427         // Draw the move handle at the vertex centre.
00428         glColor3ub(255, 255, 0);
00429         float dim = 8.0 / scale;
00430         static Track::Texture move_handle("data/generic/move_handle.png");
00431         move_handle.bind();
00432         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
00433         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
00434         glEnable(GL_TEXTURE_2D);
00435         glEnable(GL_BLEND);
00436         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00437         glPushMatrix();
00438             const btVector3 & pos = vertex.get_position();
00439             glTranslatef(0.5 / scale + pos.x(),
00440                          0.5 / scale + pos.y(),
00441                          0.5 / scale + pos.z());
00442             glBegin(GL_QUADS);
00443                 glTexCoord2f(0.0, 0.0); glVertex2f(-dim, -dim);
00444                 glTexCoord2f(1.0, 0.0); glVertex2f(dim, -dim);
00445                 glTexCoord2f(1.0, 1.0); glVertex2f(dim, dim);
00446                 glTexCoord2f(0.0, 1.0); glVertex2f(-dim, dim);
00447             glEnd();
00448             glBegin(GL_QUADS);
00449                 glTexCoord2f(0.0, 0.0); glVertex3f(0, -dim, -dim);
00450                 glTexCoord2f(1.0, 0.0); glVertex3f(0, dim, -dim);
00451                 glTexCoord2f(1.0, 1.0); glVertex3f(0, dim, dim);
00452                 glTexCoord2f(0.0, 1.0); glVertex3f(0, -dim, dim);
00453             glEnd();
00454             glBegin(GL_QUADS);
00455                 glTexCoord2f(0.0, 0.0); glVertex3f(-dim, 0, -dim);
00456                 glTexCoord2f(1.0, 0.0); glVertex3f(dim, 0, -dim);
00457                 glTexCoord2f(1.0, 1.0); glVertex3f(dim, 0, dim);
00458                 glTexCoord2f(0.0, 1.0); glVertex3f(-dim, 0, dim);
00459             glEnd();
00460         glPopMatrix();
00461         glDisable(GL_TEXTURE_2D);
00462         glDisable(GL_BLEND);
00463     }
00464     glColor3ub(255, 255, 255);
00465     
00466     #ifndef NDEBUG
00467         // mark new position location
00468         glBegin(GL_POINTS);
00469             glVertex(new_position);
00470         glEnd();
00471     #endif
00472 }
00473 
00474 bool View::on_button_press_event(GdkEventButton * event)
00475 {
00476     if (drag_object)
00477     {
00478         // Ignore presses from all buttons until the user lets go of the
00479         // object they are already dragging.
00480         return false;
00481     }
00482     // Is there something under the mouse pointer we can drag?
00483     if (event->button == 1)
00484     {
00485         drag_object = dynamic_cast<const Track::EditAssist::Dragable *>(get_selectable_under_mouse());
00486         return true;
00487     }
00488     else if (event->button == 3)
00489     {
00490         const Track::EditAssist::Selectable * found_object = get_selectable_under_mouse();
00491         if (m_popup_menu && found_object)
00492         {
00493             // set the menu item reflecting the current segment.
00494             // we don't want to attribute the following change to any object
00495             popup_object = 0;
00496             // Does the select object have a segment?
00497             bool has_segment = false;
00498             std::size_t segment_index;
00504             const Track::PathVertex * vertex = dynamic_cast<const Track::PathVertex *>(found_object);
00505             if (vertex)
00506             {
00507                 segment_index = vertex->get_segment_index();
00508                 has_segment = true;
00509                 m_action_group->get_action("delete-vertex")->set_visible();
00510                 m_action_group->get_action("delete-edge")->set_visible(false);
00511                 m_action_group->get_action("flip-edge")->set_visible(false);
00512                 m_action_group->get_action("set-start-edge")->set_visible(false);
00513                 m_action_group->get_action("add-booster")->set_visible(false);
00514                 // allow all segments
00515                 for (unsigned int i = 0; i < document->get_track().get_theme().get_number_of_segments(); i++)
00516                 {
00517                     const Track::Segment & segment = document->get_track().get_theme().get_segment(i);
00518                     Glib::ustring widget_path("/popup-menu/segments-menu/");
00519                     widget_path += Glib::ustring(segment.get_name());
00520                     Gtk::RadioMenuItem * menu_item = (Gtk::RadioMenuItem *)m_ui_manager->get_widget(widget_path);
00521                     assert(menu_item);
00522                     menu_item->set_sensitive();
00523                 }
00524             }
00525             else
00526             {
00527                 const Track::PathEdge * edge = dynamic_cast<const Track::PathEdge *>(found_object);
00528                 if (edge)
00529                 {
00530                     segment_index = edge->segment_index;
00531                     has_segment = true;
00532                     m_action_group->get_action("delete-edge")->set_visible();
00533                     m_action_group->get_action("flip-edge")->set_visible();
00534                     m_action_group->get_action("delete-vertex")->set_visible(false);
00535                     // enable set-start-edge iff edge is not already the start edge.
00536                     m_action_group->get_action("set-start-edge")->set_visible(true);
00537                     m_action_group->get_action("set-start-edge")->set_sensitive(document->get_track().get_path().get_starting_edge() != edge->get_name());
00538                     m_action_group->get_action("add-booster")->set_visible();
00539                     // filter segments by edge suitablility.
00540                     for (unsigned int i = 0; i < document->get_track().get_theme().get_number_of_segments(); i++)
00541                     {
00542                         const Track::Segment & segment = document->get_track().get_theme().get_segment(i);
00543                         Glib::ustring widget_path("/popup-menu/segments-menu/");
00544                         widget_path += Glib::ustring(segment.get_name());
00545                         Gtk::RadioMenuItem * menu_item = (Gtk::RadioMenuItem *)m_ui_manager->get_widget(widget_path);
00546                         assert(menu_item);
00547                         menu_item->set_sensitive(segment.edges_allowed());
00548                     }
00549                 }
00550                 else
00551                 {
00552                     const Track::EditAssist::TrackAttachmentHandle * attachment =
00553                         dynamic_cast<const Track::EditAssist::TrackAttachmentHandle *>(found_object);
00554                     if (attachment)
00555                     {
00556                         popup_object = found_object;
00557                         m_attachment_menu->popup(event->button, event->time);
00558                         return true;
00559                     }
00560                 }
00561             }
00562             if (has_segment)
00563             {
00564                 // Find the segment's widget.
00565                 const Track::Segment & segment = document->get_track().get_theme().get_segment(segment_index);
00566                 Glib::ustring widget_path("/popup-menu/segments-menu/");
00567                 widget_path += Glib::ustring(segment.get_name());
00568                 Gtk::RadioMenuItem * menu_item = (Gtk::RadioMenuItem *)m_ui_manager->get_widget(widget_path);
00569                 assert(menu_item);
00570                 menu_item->set_active();
00571                 // show the menu
00572                 popup_object = found_object;
00573                 m_popup_menu->popup(event->button, event->time);
00574                 return true;
00575             }
00576         }
00577         // No object, show empty space menu.
00578         m_empty_menu->popup(event->button, event->time);
00579         return true;
00580     }
00581     return false;
00582 }
00583 
00584 bool View::on_button_release_event(GdkEventButton * event)
00585 {
00586     if (event->button == 1 && drag_object)
00587     {
00588         // release the object
00589         m_signal_preview_cancel.emit();
00590         boost::shared_ptr<Document::DocumentDelta> delta(
00591             drag_object->make_delta(new_position)
00592         );
00593         // anonunce the delta so it can be applied.
00594         m_signal_command.emit(delta);
00595         // Go back to coordinates from back plane.
00596         set_relative_depth(new_position, back_depth);
00597         // reset drag_object to 0 to avoid crash if drag object is later
00598         // removed with a mouse button held down.
00599         drag_object = 0;
00600     }
00601     return false;
00602 }
00603 
00604 void View::set_relative_depth(btVector3 & position, btScalar depth)
00605 {
00606     switch (view_angle)
00607     {
00608         case VIEW_TOP:
00609             new_position.setZ(depth + centre.getZ());
00610             break;
00611         case VIEW_FRONT:
00612             new_position.setY(depth + centre.getY());
00613             break;
00614         case VIEW_SIDE:
00615             new_position.setX(depth + centre.getX());
00616             break;
00617     }
00618 }
00619 
00620 bool View::on_motion_notify_event(GdkEventMotion * event)
00621 {
00622     new_position = mouse_to_scene(event->x, event->y);
00623     if (drag_object)
00624     {
00625         btVector3 normal
00626         (
00627             view_angle == VIEW_SIDE ? 1 : 0,
00628             view_angle == VIEW_FRONT ? 1 : 0,
00629             view_angle == VIEW_TOP ? 1 : 0
00630         );
00631         drag_object->snap(new_position, normal);
00632         if (!Gtk::Main::events_pending())
00633         {
00634             m_signal_preview_cancel.emit();
00635             boost::shared_ptr<Document::DocumentDelta> delta
00636             (
00637                 drag_object->make_delta(new_position)
00638             );
00639             // anonunce the delta so it can be previewed.
00640             m_signal_preview_command.emit(delta);
00641         }
00642     }
00643     return false;
00644 }
00645 
00646 btVector3 View::mouse_to_scene(btScalar x, btScalar y) const
00647 {
00648     btScalar screen_x = (x - get_width() / 2) / scale;
00649     btScalar screen_y = (btScalar(get_height() / 2) - y) / scale;
00650     // coordinates are from the corner of the window, but we keep the centre.
00651     switch (view_angle)
00652     {
00653         case VIEW_TOP:
00654             return btVector3(screen_x + centre.x(), screen_y + centre.y(), new_position.z());
00655         case VIEW_FRONT:
00656             return btVector3(screen_x + centre.x(), new_position.y(), screen_y + centre.z());
00657         case VIEW_SIDE:
00658             return btVector3(new_position.x(), screen_x + centre.y(), screen_y + centre.z());
00659         default:
00660             DEBUG_MESSAGE("Warning: unhandled view");
00661             return new_position;
00662     }
00663 }
00664 
00665 const Track::EditAssist::Selectable * View::get_selectable_under_mouse()
00666 {
00667     btVector3 start = new_position;
00668     set_relative_depth(start, front_depth);
00669     btVector3 stop = new_position;
00670     set_relative_depth(stop, back_depth);
00671     // find the radius of a few pixels close enough to the line
00672     btScalar radius = 8.0 / scale;
00673     const Track::Path & path = document->get_track().get_path();
00674     
00675     // edge control points
00676     typedef boost::graph_traits<Track::Path::Graph>::edge_iterator EdgeIterator;
00677     std::pair<EdgeIterator, EdgeIterator> edge_range;
00678     for (edge_range = boost::edges(path.graph);
00679          edge_range.first != edge_range.second;
00680          edge_range.first++)
00681     {
00682         const Track::PathEdge & edge = path.graph[*(edge_range.first)];
00683         const Track::EditAssist::ControlPoint * control_point
00684             (edge.get_control_point_here(start, stop, radius));
00685         if (control_point)
00686         {
00687             // Use this control point
00688             DEBUG_MESSAGE("Found edge control point on edge " << edge.get_name());
00689             return control_point;
00690         }
00691     }
00692     
00693     // vertex centres
00694     typedef boost::graph_traits<Track::Path::Graph>::vertex_iterator VertexIterator;
00695     std::pair<VertexIterator, VertexIterator> vertex_range;
00696     for (vertex_range = boost::vertices(path.graph);
00697          vertex_range.first != vertex_range.second;
00698          vertex_range.first++)
00699     {
00700         const Track::PathVertex & vertex = path.graph[*(vertex_range.first)];
00701         if (vertex.is_here(start, stop, radius))
00702         {
00703             // Use this vertex
00704             new_position = vertex.get_position();
00705             DEBUG_MESSAGE("Found vertex " << vertex.get_name());
00706             return &vertex;
00707         }
00708     }
00709     
00710     //vertex control points
00711     for (vertex_range = boost::vertices(path.graph);
00712          vertex_range.first != vertex_range.second;
00713          vertex_range.first++)
00714     {
00715         const Track::PathVertex & vertex = path.graph[*(vertex_range.first)];
00716         const Track::EditAssist::ControlPoint * control_point
00717             (vertex.get_control_point_here(start, stop, radius));
00718         if (control_point)
00719         {
00720             new_position = control_point->get_position();
00721             DEBUG_MESSAGE("Found control point @ " << control_point << " on vertex " << vertex.get_name());
00722             return control_point;
00723         }
00724     }
00725     
00726     // not a vertex or control point, try an edge.
00727     for (edge_range = boost::edges(path.graph);
00728          edge_range.first != edge_range.second;
00729          edge_range.first++)
00730     {
00731         const Track::PathEdge & edge = path.graph[*(edge_range.first)];
00732         if (edge.is_here(start, stop, radius))
00733         {
00734             // Use this edge
00735             DEBUG_MESSAGE("Found edge " << edge.get_name());
00736             return &edge;
00737         }
00738     }
00739     // nothing suitable found.
00740     DEBUG_MESSAGE("Could not find suitable object under mouse pointer.");
00741     return 0;
00742 }
00743 
00744 sigc::signal<void, boost::shared_ptr<Document::DocumentDelta> > View::signal_command()
00745 {
00746     return m_signal_command;
00747 }
00748 
00749 sigc::signal<void, boost::shared_ptr<Document::DocumentDelta> > View::signal_preview_command()
00750 {
00751     return m_signal_preview_command;
00752 }
00753 
00754 sigc::signal<void> View::signal_preview_cancel()
00755 {
00756     return m_signal_preview_cancel;
00757 }
00758 
00759 void View::pick_segment(std::size_t index)
00760 {
00761     /* This is event is recieved when changing the selected menu to reflect a
00762      * a different vertex. At this point, popup_object is not pointing at
00763      * either vertex.
00764      */
00765     if (popup_object != 0)
00766     {
00767         /* This function is called when something is no longer active, as well
00768          * as when it is activated. Check which state we are in by finding the
00769          * menu item.
00770          */
00771         const Track::Segment & segment = document->get_track().get_theme().get_segment(index);
00772         Glib::ustring widget_path("/popup-menu/segments-menu/" + Glib::ustring(segment.get_name()));
00773         Gtk::RadioMenuItem * menu_item = (Gtk::RadioMenuItem *)m_ui_manager->get_widget(widget_path);
00774         assert(menu_item);
00775         if (menu_item->get_active())
00776         {
00777             const Track::PathVertex * vertex = dynamic_cast<const Track::PathVertex *>(popup_object);
00778             if (vertex)
00779             {
00780                 DEBUG_MESSAGE("Switch vertex to segment " << index);
00781                 boost::shared_ptr<Document::DocumentDelta> delta(
00782                     new Document::ChangeVertexSegmentDelta(vertex->get_name(), index)
00783                 );
00784                 m_signal_command.emit(delta);
00785             }
00786             else
00787             {
00788                 // Not a vertex, so should be an edge.
00789                 const Track::PathEdge * edge = dynamic_cast<const Track::PathEdge *>(popup_object);
00790                 assert(edge); // Isn't an edge either. What else has segments?
00791                 DEBUG_MESSAGE("Switch edge to segment " << index);
00792                 boost::shared_ptr<Document::DocumentDelta> delta(
00793                     new Document::ChangeEdgeSegmentDelta(edge->get_name(), index)
00794                 );
00795                 m_signal_command.emit(delta);
00796             }
00797         }
00798     }
00799 }
00800 
00801 void View::on_delete_vertex()
00802 {
00803     const Track::PathVertex * vertex = dynamic_cast<const Track::PathVertex *>(popup_object);
00804     assert(vertex);
00805     boost::shared_ptr<Document::DocumentDelta> delta
00806     (
00807         new Document::RemoveVertexDelta(vertex->get_name())
00808     );
00809     m_signal_command.emit(delta);
00810 }
00811 
00812 void View::on_delete_edge()
00813 {
00814     const Track::PathEdge * edge = dynamic_cast<const Track::PathEdge *>(popup_object);
00815     assert(edge);
00816     boost::shared_ptr<Document::DocumentDelta> delta
00817     (
00818         new Document::RemoveEdgeDelta(edge->get_name())
00819     );
00820     m_signal_command.emit(delta);
00821 }
00822 
00823 void View::on_delete_attachment()
00824 {
00825     const Track::EditAssist::TrackAttachmentHandle * attachment_handle =
00826         dynamic_cast<const Track::EditAssist::TrackAttachmentHandle *>(popup_object);
00827     assert(attachment_handle);
00828     boost::shared_ptr<Document::DocumentDelta> delta
00829     (
00830         attachment_handle->make_remove_delta()
00831     );
00832     m_signal_command.emit(delta);
00833 }
00834 
00835 void View::on_flip_edge()
00836 {
00837     const Track::PathEdge * edge = dynamic_cast<const Track::PathEdge *>(popup_object);
00838     assert(edge);
00839     boost::shared_ptr<Document::DocumentDelta> delta
00840     (
00841         new Document::FlipEdgeDelta(edge->get_name())
00842     );
00843     m_signal_command.emit(delta);
00844 }
00845 
00846 void View::on_insert_vertex()
00847 {
00848     Track::PathVertex vertex(&(document->get_track()));
00849      switch (view_angle)
00850     {
00851         case VIEW_TOP:
00852             new_position.setZ(0);
00853             break;
00854         case VIEW_FRONT:
00855             new_position.setY(0);
00856             break;
00857         case VIEW_SIDE:
00858             new_position.setX(0);
00859             break;
00860         default:
00861             DEBUG_MESSAGE("Warning: unhandled view");
00862     }
00863     vertex.set_position(new_position);
00864     const Track::Theme & theme = document->get_track().get_theme();
00865     vertex.set_segment(theme.get_default_segment_index(),
00866                        &theme.get_segment(theme.get_default_segment_index()));
00867     vertex.set_angle(btQuaternion::getIdentity());
00868     vertex.set_handle_lengths(handle_length);
00869     boost::shared_ptr<Document::DocumentDelta> delta
00870     (
00871         new Document::InsertVertexDelta(vertex)
00872     );
00873     m_signal_command.emit(delta);
00874 }
00875 
00876 void View::on_set_start_edge()
00877 {
00878     const Track::PathEdge * edge = dynamic_cast<const Track::PathEdge *>(popup_object);
00879     assert(edge);
00880     boost::shared_ptr<Document::DocumentDelta> delta
00881     (
00882         new Document::SetStartPositionsDelta(edge->get_name())
00883     );
00884     m_signal_command.emit(delta);
00885 }
00886 
00887 void View::on_add_booster()
00888 {
00889     const Track::PathEdge * edge = dynamic_cast<const Track::PathEdge *>(popup_object);
00890     assert(edge);
00891     
00892     boost::shared_ptr<Track::TrackAttachment> booster(new Track::TrackBooster);
00893     booster->set_lateral_position(0.0);
00894     booster->set_t_position(0.5);
00895     booster->edge_name = edge->get_name();
00896     
00897     boost::shared_ptr<Document::DocumentDelta> delta
00898     (
00899         new Document::InsertTrackAttachmentDelta(booster)
00900     );
00901     m_signal_command.emit(delta);
00902 }

Get Racer at SourceForge.net. Fast, secure and Free Open Source software downloads

Generated at Mon Sep 6 00:41:13 2010 by Doxygen version 1.4.7 for Racer version svn335.