PreviewIdentifyTool.cpp

Go to the documentation of this file.
00001 // -*- c-basic-offset: 4 -*-
00022 #include "panoinc_WX.h"
00023 #include "panoinc.h"
00024 
00025 #include "PreviewIdentifyTool.h"
00026 #include <config.h>
00027 
00028 #include "base_wx/platform.h"
00029 #include "GLPreviewFrame.h"
00030 #include "MainFrame.h"
00031 
00032 #include <wx/platform.h>
00033 
00034 //multitexture feature requires glew on some systems
00035 #if !defined Hugin_shared || !defined _WINDOWS
00036 #define GLEW_STATIC
00037 #endif
00038 #include <GL/glew.h>
00039 
00040 #ifdef __WXMAC__
00041 #include <OpenGL/gl.h>
00042 #include <OpenGL/glu.h>
00043 #else
00044 #include <GL/gl.h>
00045 #include <GL/glu.h>
00046 #endif
00047 
00048 #include <algorithm>
00049 #include <vector>
00050 
00051 // the size of the rectangular texture. Must be a power of two, and at least 8.
00052 #define rect_ts 64
00053 // the number of times larger the circular texture is, must be a power of 2, and
00054 // at least 1. Making better improves the appearance of the circle.
00055 #define circle_ts_multiple 4
00056 #define circle_ts (rect_ts * circle_ts_multiple)
00057 #define circle_middle ((float) (circle_ts - 1) / 2.0)
00058 #define circle_border_outer (circle_middle - 0.5 * (float) circle_ts_multiple)
00059 #define circle_border_inner (circle_middle - 2.5 * (float) circle_ts_multiple)
00060 #define circle_border_peak (circle_middle - 1.5 * (float) circle_ts_multiple)
00061 
00062 bool PreviewIdentifyTool::texture_created = false;
00063 unsigned int PreviewIdentifyTool::circle_border_tex;
00064 unsigned int PreviewIdentifyTool::rectangle_border_tex;
00065 
00066 PreviewIdentifyTool::PreviewIdentifyTool(ToolHelper *helper,
00067                                          GLPreviewFrame *owner)
00068     : Tool(helper)
00069 {
00070     holdControl = false;
00071     constantOn = false;
00072     holdLeft = false;
00073     stopUpdating = true;
00074     preview_frame = owner;
00075     if (!texture_created) {
00076         // make the textures. We have a circle border and a square one.
00077         // the textures are white with a the alpha chanel forming a border.
00078         glGenTextures(1, (GLuint*) &rectangle_border_tex);
00079         glGenTextures(1, (GLuint*) &circle_border_tex);
00080         // we only want to specify alpha, but using just alpha in opengl attaches 0
00081         // for the luminosity. I tried biasing the red green and blue values to get
00082         // them to 1.0, but it didn't work under OS X for some reason. Instead we
00083         // use a luminance alpha pair, and use 1.0 for luminance all the time.
00084         {
00085             // In the rectangle texture, the middle is 1/4 opaque, the outer pixels
00086             // are completely transparent, and one pixel in from the edges is
00087             // a completly opaque line.
00088             unsigned char rect_tex_data[rect_ts][rect_ts][2];
00089             // make everything white
00090             for (unsigned int x = 0; x < rect_ts; x++)
00091             {
00092                 for (unsigned int y = 0; y < rect_ts; y++)
00093                 {
00094                     rect_tex_data[x][y][0] = 255;
00095                 }
00096             }
00097             // now set the middle of the mask semi transparent
00098             for (unsigned int x = 2; x < rect_ts - 2; x++)
00099             {
00100                 for (unsigned int y = 2; y < rect_ts - 2; y++)
00101                 {
00102                     rect_tex_data[x][y][1] = 63;
00103                 }
00104             }
00105             // make an opaque border
00106             for (unsigned int d = 1; d < rect_ts - 1; d++)
00107             {
00108                 rect_tex_data[d][1][1] = 255;
00109                 rect_tex_data[d][rect_ts - 2][1] = 255;
00110                 rect_tex_data[1][d][1] = 255;
00111                 rect_tex_data[rect_ts - 2][d][1] = 255;
00112             }
00113             // make a transparent border around that
00114             for (unsigned int d = 0; d < rect_ts; d++)
00115             {
00116                 rect_tex_data[d][0][1] = 0;
00117                 rect_tex_data[d][rect_ts - 1][1] = 0;
00118                 rect_tex_data[0][d][1] = 0;
00119                 rect_tex_data[rect_ts - 1][d][1] = 0;
00120             }
00121             glBindTexture(GL_TEXTURE_2D, rectangle_border_tex);
00122             gluBuild2DMipmaps(GL_TEXTURE_2D, GL_LUMINANCE_ALPHA, rect_ts, rect_ts,
00123                               GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, rect_tex_data);
00124             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
00125                             GL_LINEAR_MIPMAP_LINEAR);
00126             // clamp texture so it won't wrap over the border.
00127             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
00128             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
00129         }
00130         {
00131             // the circular one should look similar to the rectangle one, but we
00132             // enlarge it so that the circle apears less blocky. We don't want to
00133             // make it equally sharper however, so we make it a bit fuzzier by
00134             // blending.
00135             unsigned char circle_tex_data[circle_ts][circle_ts][2];
00136             for (unsigned int x = 0; x < circle_ts; x++)
00137             {
00138                 for (unsigned int y = 0; y < circle_ts; y++)
00139                 {
00140                     float x_offs = (float) x - circle_middle,
00141                           y_offs = (float) y - circle_middle,
00142                           radius = sqrt(x_offs * x_offs + y_offs * y_offs),
00143                           intensity;
00144                     if (radius < circle_border_inner)
00145                     {
00146                         intensity = 63.0;
00147                     } else if (radius < circle_border_peak)
00148                     {
00149                         intensity = (radius - circle_border_inner) /
00150                               (float) circle_ts_multiple * 255.0 * 3.0 / 4.0 + 64.0;
00151                     } else if (radius < circle_border_outer)
00152                     {
00153                         intensity = (radius - circle_border_peak) /
00154                                         (float) circle_ts_multiple * -255.0 + 256.0;
00155                     } else
00156                     {
00157                         intensity = 0.0;
00158                     }
00159                     circle_tex_data[x][y][0] = 255;
00160                     circle_tex_data[x][y][1] = (unsigned char) intensity;
00161                 }
00162             }
00163             glBindTexture(GL_TEXTURE_2D, circle_border_tex);
00164             gluBuild2DMipmaps(GL_TEXTURE_2D,
00165                               GL_LUMINANCE_ALPHA, circle_ts, circle_ts,
00166                               GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, circle_tex_data);
00167             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
00168                             GL_LINEAR_MIPMAP_LINEAR);
00169             // clamp texture so it won't wrap over the border.
00170             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
00171             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
00172         }
00173         texture_created = true;
00174     }
00175 }
00176 PreviewIdentifyTool::~PreviewIdentifyTool()
00177 {
00178     // free the textures
00179     glDeleteTextures(1, (GLuint*) &rectangle_border_tex);
00180     glDeleteTextures(1, (GLuint*) &circle_border_tex);
00181 }
00182 
00183 void PreviewIdentifyTool::Activate()
00184 {
00185     // register notifications
00186     helper->NotifyMe(PreviewToolHelper::MOUSE_MOVE, this);
00187     helper->NotifyMe(PreviewToolHelper::DRAW_OVER_IMAGES, this);
00188     helper->NotifyMe(PreviewToolHelper::IMAGES_UNDER_MOUSE_CHANGE, this);
00189     helper->NotifyMe(PreviewToolHelper::MOUSE_PRESS, this);
00190     
00191     // clear up
00192     // Assume that there are no images under the mouse when the tool is
00193     // activated. This should be fine if the user clicks the button to activate
00194     // the tool.
00195     image_set.clear();
00196     mouse_is_over_button = false;
00197     /* TODO if it becomes possible to activate the tool by a keyboard shortcut
00198      * or something, call ImagesUnderMouseChangedEvent() to make sure we display
00199      * indicators for images currently under the cursor. */
00200     ImagesUnderMouseChangedEvent();
00201 
00202     helper->SetStatusMessage(_("Move the mouse over the images or image buttons to identify them."));
00203 }
00204 
00205 void PreviewIdentifyTool::StopUpdating() {
00206     if (image_set.size() > 0) {
00207         std::set<unsigned int>::iterator iterator;
00208         for (iterator = image_set.begin(); iterator != image_set.end(); iterator++)
00209         {
00210             DEBUG_ASSERT(*iterator < helper->GetPanoramaPtr()->getNrOfImages());
00211             // reset this button to its default system colour.
00212             preview_frame->SetImageButtonColour(*iterator, 0, 0, 0);
00213             // remove the notification
00214             helper->DoNotNotifyMeBeforeDrawing(*iterator, this);
00215         }
00216     }
00217     image_set.clear();
00218     preview_frame->UpdateIdentifyTools(image_set);
00219     stopUpdating = true;
00220     ForceRedraw();
00221 }
00222 
00223 void PreviewIdentifyTool::ContinueUpdating() {
00224     stopUpdating = false;
00225     ImagesUnderMouseChangedEvent();
00226 }
00227 
00228 void PreviewIdentifyTool::MouseMoveEvent(double x, double y, wxMouseEvent & e)
00229 {
00230 
00231     bool stop = false;
00232     bool start = false;
00233 
00234     if (constantOn) {
00235         if (e.Dragging() && !(holdLeft)) {
00236             stop = true;
00237             stopUpdating = true;
00238         }
00239 
00240         if (stopUpdating && !e.LeftIsDown() && !e.MiddleIsDown() && !e.RightIsDown()) {
00241             start = true;
00242         }
00243     }
00244 
00245     if (holdControl && !e.m_controlDown) {
00246         holdControl = false;
00247         if (!constantOn) {
00248             stop = true;
00249         }
00250     }
00251     
00252     if (!holdControl && e.m_controlDown) {
00253         holdControl = true;
00254         stop = false;
00255         if (stopUpdating) {
00256             start = true;
00257         }
00258     }
00259     
00260     if (stop) {
00261         this->StopUpdating();
00262     } else if(start) {
00263         this->ContinueUpdating();
00264     }
00265 
00266 
00267 }
00268 
00269 void PreviewIdentifyTool::KeypressEvent(int keycode, int modifierss, int pressed) {
00270 
00271     if (keycode == WXK_CONTROL) {
00272         if (pressed) {
00273             holdControl = true;
00274             ContinueUpdating();
00275         } else {
00276             if(holdControl) {
00277                 StopUpdating();
00278             }
00279             holdControl = false;
00280         }
00281     }
00282 }
00283 
00284 void PreviewIdentifyTool::setConstantOn(bool constant_on_in) {
00285     constantOn = constant_on_in;
00286     if (constant_on_in) {
00287         stopUpdating = false;
00288         ContinueUpdating();
00289     } else {
00290         stopUpdating = true;
00291         StopUpdating();
00292     }
00293 
00294 }
00295 
00296 
00297 void PreviewIdentifyTool::ImagesUnderMouseChangedEvent()
00298 {
00299     if (stopUpdating) {
00300         return;
00301     }
00302     
00303     std::set<unsigned int> new_image_set = helper->GetImageNumbersUnderMouse();
00304     
00305     //UpdateIdentifyTools() will unrequest notification for the old indicators,
00306     //reset the button colors, request notification for the new ones, swap in
00307     //image_set with new_image_set, and force a redraw for all three
00308     //PreviewIdentifyTool objects in GLPreviewFrame. This has the effect of 
00309     //displaying the indicators in both the preview and overview when you move
00310     //your mouse over either when the Identify button is toggled on. 
00311     preview_frame->UpdateIdentifyTools(new_image_set);
00312     
00313     // if there is exactly two images, tell the user they can click to edit CPs.
00314     if (image_set.size() == 2)
00315     {
00316          helper->SetStatusMessage(_("Click to create or edit control points here."));
00317     } else {
00318          helper->SetStatusMessage(_("Move the mouse over the images or image buttons to identify them."));
00319     }
00320 }
00321 
00322 void PreviewIdentifyTool::AfterDrawImagesEvent()
00323 {
00324     // we draw the partly transparent identification boxes over the top of the
00325     // entire stack of images in image_set so that the extents of images in the
00326     // background are clearly marked.
00327     unsigned int num_images = image_set.size();
00328     // draw the actual images
00329     // the preview draws them in reverse order, so the lowest numbered appears
00330     // on top. We will folow this convention to avoid confusion.
00331     glMatrixMode(GL_MODELVIEW);
00332     std::set<unsigned int>::reverse_iterator it;
00333     for (it = image_set.rbegin(); it != image_set.rend(); it++)
00334     {
00335         DEBUG_ASSERT(*it < helper->GetPanoramaPtr()->getNrOfImages());
00336         helper->GetViewStatePtr()->GetTextureManager()->
00337                 DrawImage(*it,
00338                          helper->GetVisualizationStatePtr()->GetMeshDisplayList(*it));
00339     }
00340     glMatrixMode(GL_TEXTURE);
00341     
00342     // now draw the identification boxes
00343     glEnable(GL_BLEND);
00344     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00345     unsigned int image_counter = 0;
00346     for (it = image_set.rbegin(); it != image_set.rend(); it++)
00347     {
00348         glMatrixMode(GL_TEXTURE);
00349         // Use the mask to alter the shape of the identification boxes, but
00350         // replace the normal image texture with the identification box itself.
00351         if (helper->GetViewStatePtr()->GetSupportMultiTexture())
00352         {
00353             helper->GetViewStatePtr()->GetTextureManager()->BindTexture(*it);
00354             glActiveTexture(GL_TEXTURE0);
00355         }
00356         // we want to shift the texture so it lines up with the cropped region.
00357         glPushMatrix();
00358         HuginBase::SrcPanoImage *src = helper->GetViewStatePtr()->
00359                                                                GetSrcImage(*it);
00360         int width = src->getSize().width(), height = src->getSize().height();
00361         vigra::Rect2D crop_region = src->getCropRect();
00362         // pick a texture depending on crop mode and move it to the cropped area
00363         switch (src->getCropMode())
00364         {
00365             case HuginBase::SrcPanoImage::CROP_CIRCLE:
00366                 glBindTexture(GL_TEXTURE_2D, circle_border_tex);
00367                 // change the crop region to a square around the circle.
00368                 if (crop_region.width() < crop_region.height())
00369                 {
00370                     // too tall, move top and bottom.
00371                     int diff = (crop_region.width() - crop_region.height()) / 2;
00372                     // diff is negative, so we will shrink the border in the y
00373                     // direction.
00374                     crop_region.addBorder(0, diff);
00375                 } else if (crop_region.width() > crop_region.height())
00376                 {
00377                     // too wide, move left and right
00378                     int diff = (crop_region.height() - crop_region.width()) / 2;
00379                     crop_region.addBorder(diff, 0);
00380                 }
00381                 {
00382                     float diameter = (float) crop_region.width();
00383                     glScalef((float) width / diameter,
00384                              (float) height / diameter, 1.0);
00385                     glTranslatef(-(float) crop_region.left() / (float) width,
00386                                  -(float) crop_region.top() / (float) height,
00387                                  0.0);
00388                 }
00389                 break;
00390             case HuginBase::SrcPanoImage::CROP_RECTANGLE:
00391                 // get the biggest rectangle contained by both the image 
00392                 // and the cropped area.
00393                 crop_region &= vigra::Rect2D(src->getSize());
00394                 glBindTexture(GL_TEXTURE_2D, rectangle_border_tex);
00395                 glScalef((float) width / (float) crop_region.width(),
00396                          (float) height / (float) crop_region.height(),
00397                          1.0);
00398                 glTranslatef(-(float) crop_region.left() / (float) width,
00399                              -(float) crop_region.top() / (float) height,
00400                              0.0);
00401                 break;
00402             case HuginBase::SrcPanoImage::NO_CROP:
00403                 glBindTexture(GL_TEXTURE_2D, rectangle_border_tex);
00404                 break;
00405         }
00406         // draw the image in this texture
00407         glMatrixMode(GL_MODELVIEW);
00408         unsigned char r,g,b;
00409         HighlightColour(image_counter, num_images, r, g, b);
00410         image_counter++;
00411         glColor3ub(r,g,b);
00412         glCallList(helper->GetVisualizationStatePtr()->GetMeshDisplayList(*it));
00413         glMatrixMode(GL_TEXTURE);
00414         glPopMatrix();
00415         // tell the preview frame to update the button to show the same colour.
00416         preview_frame->SetImageButtonColour(*it, r, g, b);
00417     }
00418     // set stuff back how we found it.
00419     glMatrixMode(GL_MODELVIEW);
00420     glDisable(GL_BLEND);
00421     glColor3ub(255, 255, 255);
00422 }
00423 
00424 bool PreviewIdentifyTool::BeforeDrawImageEvent(unsigned int image)
00425 {
00426     // Delay drawing of images, so we can show them on top of the others.
00427     DEBUG_ASSERT(image < helper->GetPanoramaPtr()->getNrOfImages());
00428     if (image_set.count(image)) return false;
00429     return true;
00430 }
00431 
00432 void PreviewIdentifyTool::ShowImageNumber(unsigned int image)
00433 {
00434     DEBUG_ASSERT(image < helper->GetPanoramaPtr()->getNrOfImages());
00435     // Add this image to the set of images drawn.
00436     if (!image_set.count(image))
00437     {
00438         // it is not already in the set. Add it now
00439         image_set.insert(image);
00440         // we want notification of when it is drawn so we can delay drawing.
00441         helper->NotifyMeBeforeDrawing(image, this);
00442         //  now we want a redraw.
00443         ForceRedraw();
00444     }
00445     mouse_over_image = image;
00446     mouse_is_over_button = true;
00447 }
00448 
00449 void PreviewIdentifyTool::StopShowingImages()
00450 {
00451     if (mouse_is_over_button)
00452     {
00453         // set the colour to the image the user just moused off to the default.
00454         preview_frame->SetImageButtonColour(mouse_over_image, 0, 0, 0);
00455         helper->DoNotNotifyMeBeforeDrawing(mouse_over_image, this);
00456         image_set.erase(mouse_over_image);
00457         // now redraw without the indicator.
00458         ForceRedraw();
00459         mouse_is_over_button = false;
00460     }    
00461 }
00462 
00463 // generate a colour given how many colours we need and an index.
00464 void PreviewIdentifyTool::HighlightColour(unsigned int index,
00465                                           unsigned int count,
00466                                           unsigned char &red,
00467                                           unsigned char &green,
00468                                           unsigned char &blue)
00469 {
00470     DEBUG_ASSERT(index < count && index >= 0);
00471     // the first one is red, the rest are evenly spaced throughout the spectrum 
00472     float hue = ((float) index / (float) count) * 6.0;
00473     if (hue < 1.0)
00474     {
00475         // red to yellow
00476         red = 255;
00477         green = (unsigned char) (hue * 255.0);
00478         blue = 0;
00479     } else if (hue < 2.0) {
00480         // yellow to green
00481         red = (unsigned char) ((-hue + 2.0) * 255.0);
00482         green = 255;
00483         blue = 0;
00484     } else if (hue < 3.0) {
00485         // green to cyan
00486         red = 0;
00487         green = 255;
00488         blue = (unsigned char) ((hue - 2.0) * 255.0);
00489     } else if (hue < 4.0) {
00490         // cyan to blue
00491         red = 0;
00492         green = (unsigned char) ((-hue + 4.0) * 255.0);
00493         blue = 255;
00494     } else if (hue < 5.0) {
00495         // blue to magenta
00496         red = (unsigned char) ((hue - 4.0) * 255.0);
00497         green = 0;
00498         blue = 255;
00499     } else {
00500         // magenta to red
00501         red = 255;
00502         green = 0;
00503         blue = (unsigned char) ((-hue + 6.0) * 255.0);
00504     }
00505 }
00506 
00507 void PreviewIdentifyTool::MouseButtonEvent(wxMouseEvent & e)
00508 {
00509 
00510 
00511     if ( e.LeftDown() && helper->IsMouseOverPano())
00512     {   
00513         holdLeft = true;
00514     } 
00515 
00516     if (holdLeft && e.LeftUp() && (image_set.size()==1 || image_set.size() == 2)) 
00517     {
00518         holdLeft = false;
00519         if (constantOn || e.CmdDown())
00520         {
00521             // when there are only two images with indicators shown, show the
00522             // control point editor with those images when left clicked.
00523             if(image_set.size()==2)
00524             {
00525                 MainFrame::Get()->ShowCtrlPointEditor(*(image_set.begin()),
00526                                                         *(++image_set.begin()));
00527             }
00528             else
00529             {
00530                 MainFrame::Get()->ShowMaskEditor(*image_set.begin());
00531             };
00532             MainFrame::Get()->Raise();
00533         }
00534     }
00535 
00536     if (holdLeft && !(e.LeftIsDown())) {
00537         holdLeft = false;
00538     }
00539 
00540     if (constantOn) {
00541         if (e.ButtonUp() && !e.MiddleIsDown() && !e.RightIsDown()) {
00542             stopUpdating = false;
00543             ImagesUnderMouseChangedEvent();
00544         }
00545     }
00546 
00547 }
00548 
00549 void PreviewIdentifyTool::UpdateWithNewImageSet(std::set<unsigned int> new_image_set)
00550 {
00551     // If we are currently showing indicators for some of the images, we want
00552     // to work out which ones are not in the new set, so we can set their
00553     // buttons back to the system colour.
00554     {
00555         UIntSet difference;
00556         std::set_difference (image_set.begin(), image_set.end(),
00557                              new_image_set.begin(), new_image_set.end(),
00558                              std::inserter(difference,difference.end()));
00559         if (difference.size()>0)
00560         {
00561             for (UIntSet::iterator iterator = difference.begin(); iterator != difference.end(); iterator++)
00562             {
00563                 DEBUG_ASSERT(*iterator < helper->GetPanoramaPtr()->getNrOfImages());
00564                 // reset this button to its default system colour.
00565                 preview_frame->SetImageButtonColour(*iterator, 0, 0, 0);
00566                 // remove the notification
00567                 helper->DoNotNotifyMeBeforeDrawing(*iterator, this);
00568             }
00569         }
00570     }
00571 
00572     // now request to be notified when drawing the new ones.
00573     UIntSet difference;
00574     std::set_difference (new_image_set.begin(), new_image_set.end(),
00575                          image_set.begin(), image_set.end(),
00576                          std::inserter(difference,difference.end()));
00577     if (difference.size()>0)
00578     {
00579         for (UIntSet::iterator iterator = difference.begin(); iterator != difference.end(); iterator++)
00580         {
00581             DEBUG_ASSERT(*iterator < helper->GetPanoramaPtr()->getNrOfImages());
00582             // get notification of when this is about to be drawn.
00583             helper->NotifyMeBeforeDrawing(*iterator, this);
00584         }
00585     }
00586     // remember the new set.
00587     image_set.swap(new_image_set);
00588 
00589     // Redraw with new indicators. Since the indicators aren't part of the
00590     // panorama and none of it is likely to change, we have to persuade the
00591     // viewstate that a redraw is required.
00592     ForceRedraw();
00593 }
00594 
00595 void PreviewIdentifyTool::ForceRedraw()
00596 {
00597     helper->GetVisualizationStatePtr()->ForceRequireRedraw();
00598     helper->GetVisualizationStatePtr()->Redraw();
00599 }

Generated on 31 Oct 2014 for Hugintrunk by  doxygen 1.4.7