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 }
Generated at Mon Sep 6 00:41:13 2010 by Doxygen version 1.4.7 for Racer version svn335.