CPImageCtrl.cpp

Go to the documentation of this file.
00001 // -*- c-basic-offset: 4 -*-
00002 
00027 // standard wx include
00028 #include <config.h>
00029 #include "panoinc_WX.h"
00030 
00031 // standard hugin include
00032 #include "panoinc.h"
00033 #include "base_wx/platform.h"
00034 
00035 #include <vigra/inspectimage.hxx>
00036 #include <vigra/transformimage.hxx>
00037 
00038 #include "hugin/config_defaults.h"
00039 #include "hugin/CPImageCtrl.h"
00040 #include "base_wx/wxImageCache.h"
00041 #include "hugin/CPEditorPanel.h"
00042 #include "hugin/MainFrame.h"
00043 #include "hugin/huginApp.h"
00044 
00045 #include "vigra_ext/ImageTransforms.h"
00046 
00047 using namespace std;
00048 using namespace hugin_utils;
00049 
00050 // event stuff
00051 
00052 
00053 // definition of the control point event
00054 
00055 IMPLEMENT_DYNAMIC_CLASS( CPEvent, wxEvent )
00056 #if _WINDOWS && defined Hugin_shared
00057 DEFINE_LOCAL_EVENT_TYPE( EVT_CPEVENT )
00058 #else
00059 DEFINE_EVENT_TYPE( EVT_CPEVENT )
00060 #endif
00061 
00062 CPEvent::CPEvent( )
00063 {
00064     SetEventType( EVT_CPEVENT );
00065     SetEventObject( (wxWindow *) NULL );
00066     mode = NONE;
00067 }
00068 
00069 CPEvent::CPEvent(wxWindow* win, FDiff2D & p)
00070 {
00071     SetEventType( EVT_CPEVENT );
00072     SetEventObject( win );
00073     mode = NEW_POINT_CHANGED;
00074     point = p;
00075 }
00076 
00077 CPEvent::CPEvent(wxWindow *win, unsigned int cpNr)
00078 {
00079     SetEventType( EVT_CPEVENT );
00080     SetEventObject( win );
00081     mode = POINT_SELECTED;
00082     pointNr = cpNr;
00083 }
00084 
00085 CPEvent::CPEvent(wxWindow* win, unsigned int cpNr, const FDiff2D & p)
00086 {
00087     SetEventType( EVT_CPEVENT );
00088     SetEventObject( win );
00089     mode = POINT_CHANGED;
00090     pointNr = cpNr;
00091     point = p;
00092 }
00093 
00094 CPEvent::CPEvent(wxWindow* win, const hugin_utils::FDiff2D & p1, const hugin_utils::FDiff2D & p2)
00095 {
00096     SetEventType(EVT_CPEVENT);
00097     SetEventObject(win);
00098     mode=DELETE_REGION_SELECTED;
00099     region=wxRect(roundi(min(p1.x,p2.x)),roundi(min(p1.y,p2.y)),abs(roundi(p2.x-p1.x)),abs(roundi(p2.y-p1.y)));
00100 };
00101 
00102 CPEvent::CPEvent(wxWindow* win, CPEventMode evt_mode, const FDiff2D & p)
00103 {
00104     SetEventType(EVT_CPEVENT);
00105     SetEventObject(win);
00106     mode = evt_mode;
00107     point = p;
00108 }
00109 
00110 CPEvent::CPEvent(wxWindow* win, CPEventMode evt_mode, const HuginBase::ControlPoint cp)
00111 {
00112     SetEventType(EVT_CPEVENT);
00113     SetEventObject(win);
00114     mode=evt_mode;
00115     m_cp=cp;
00116 };
00117 
00118 CPEvent::CPEvent(wxWindow* win, CPEventMode evt_mode, size_t cpNr, const HuginBase::ControlPoint cp)
00119 {
00120     SetEventType(EVT_CPEVENT);
00121     SetEventObject(win);
00122     mode=evt_mode;
00123     pointNr=cpNr;
00124     m_cp=cp;
00125 };
00126 
00127 wxEvent * CPEvent::Clone() const
00128 {
00129     return new CPEvent(*this);
00130 }
00131 
00132 DisplayedControlPoint::DisplayedControlPoint(const HuginBase::ControlPoint& cp, CPImageCtrl* control, bool mirrored)
00133 {
00134     m_cp=cp;
00135     m_control=control;
00136     m_mirrored=mirrored;
00137     m_line=(m_cp.mode!=HuginBase::ControlPoint::X_Y) && (m_cp.image1Nr==m_cp.image2Nr);
00138 };
00139 
00140 void DisplayedControlPoint::SetColour(wxColour pointColour, wxColour textColour)
00141 {
00142     m_pointColour=pointColour;
00143     m_textColour=textColour;
00144 };
00145 
00146 void DisplayedControlPoint::SetLabel(wxString newLabel)
00147 {
00148     m_label=newLabel;
00149 };
00150 
00151 void DisplayedControlPoint::SetControl(CPImageCtrl* control)
00152 {
00153     m_control=control;
00154 };
00155 
00156 void DrawCross(wxDC& dc, wxPoint p, int l)
00157 {
00158     dc.DrawLine(p + wxPoint(-l, 0),
00159                 p + wxPoint(-1, 0));
00160     dc.DrawLine(p + wxPoint(2, 0),
00161                 p + wxPoint(l+1, 0));
00162     dc.DrawLine(p + wxPoint(0, -l),
00163                 p + wxPoint(0, -1));
00164     dc.DrawLine(p + wxPoint(0, 2),
00165                 p + wxPoint(0, l+1));
00166 };
00167 
00168 void DisplayedControlPoint::Draw(wxDC& dc, bool selected, bool newPoint)
00169 {
00170     if(m_control==NULL)
00171     {
00172         return;
00173     };
00174     // select color
00175     wxColour bgColor = m_pointColour;
00176     wxColour textColor = m_textColour;
00177     bool drawMag = false;
00178     if (selected)
00179     {
00180         bgColor = wxTheColourDatabase->Find(wxT("RED"));
00181         textColor = wxTheColourDatabase->Find(wxT("WHITE"));
00182         drawMag = !m_control->GetMouseInWindow() || m_control->GetForceMagnifier();
00183     }
00184     if (newPoint)
00185     {
00186         bgColor = wxTheColourDatabase->Find(wxT("YELLOW"));
00187         textColor = wxTheColourDatabase->Find(wxT("BLACK"));
00188         drawMag = true;
00189     }
00190 
00191     dc.SetPen(wxPen(wxT("WHITE"), 1, wxSOLID));
00192     dc.SetBrush(wxBrush(wxT("BLACK"),wxTRANSPARENT));
00193 
00194     FDiff2D pointInput=m_mirrored ? hugin_utils::FDiff2D(m_cp.x2, m_cp.y2) : hugin_utils::FDiff2D(m_cp.x1, m_cp.y1);
00195     FDiff2D point = m_control->applyRot(pointInput);
00196     wxPoint p = m_control->roundP(m_control->scale(point));
00197     FDiff2D pointInput2;
00198     wxPoint p2;
00199     if(m_line)
00200     {
00201         pointInput2=m_mirrored ? hugin_utils::FDiff2D(m_cp.x1, m_cp.y1) : hugin_utils::FDiff2D(m_cp.x2, m_cp.y2);
00202         FDiff2D point2 = m_control->applyRot(pointInput2);
00203         p2 = m_control->roundP(m_control->scale(point2));
00204     };
00205     int l = 6;
00206     // draw cursor line, choose white or black
00207     vigra::Rect2D box;
00208     if(m_line)
00209     {
00210         box.setUpperLeft(vigra::Point2D(roundi(std::min(m_cp.x1, m_cp.x2))-l, roundi(std::min(m_cp.y1, m_cp.y2))-l));
00211         box.setSize(roundi(abs(m_cp.x1-m_cp.x2)+2.0*l), roundi(abs(m_cp.y1-m_cp.y2)+2.0*l)); 
00212     }
00213     else
00214     {
00215         box.setUpperLeft(vigra::Point2D(roundi(pointInput.x-l), roundi(pointInput.y-l)));
00216         box.setSize(2*l, 2*l);
00217     };                
00218     // only use part inside.
00219     box &= vigra::Rect2D(m_control->GetImg()->size());
00220     if(box.width()<=0 || box.height()<=0)
00221     {
00222         return;
00223     };
00224     // calculate mean "luminance value"
00225     vigra::FindAverage<vigra::UInt8> average;   // init functor
00226     vigra::RGBToGrayAccessor<vigra::RGBValue<vigra::UInt8> > lumac;
00227     vigra::inspectImage(m_control->GetImg()->upperLeft()+ box.upperLeft(),
00228                         m_control->GetImg()->upperLeft()+ box.lowerRight(),
00229                         lumac, average);
00230     if (average() < 150)
00231     {
00232         dc.SetPen(wxPen(wxT("WHITE"), 1, wxSOLID));
00233     }
00234     else
00235     {
00236         dc.SetPen(wxPen(wxT("BLACK"), 1, wxSOLID));
00237     }
00238 
00239     if(m_line)
00240     {
00241         DrawLine(dc);
00242         DrawCross(dc, p, l);
00243         DrawCross(dc, p2, l);
00244     }
00245     else
00246     {
00247         if(m_cp.mode!=ControlPoint::X_Y)
00248         {
00249             DrawLineSegment(dc);
00250         };
00251         DrawCross(dc, p, l);
00252     };
00253     // calculate distance to the image boundaries,
00254     // decide where to put the label and magnifier
00255     m_labelPos=DrawTextMag(dc, p, pointInput, drawMag, textColor, bgColor);
00256     if(m_line)
00257     {
00258         m_labelPos2=DrawTextMag(dc, p2, pointInput2, drawMag, textColor, bgColor);
00259     };
00260 }
00261 
00262 wxRect DisplayedControlPoint::DrawTextMag(wxDC& dc, wxPoint p, hugin_utils::FDiff2D pointInput, bool drawMag, wxColour textColour, wxColour bgColour)
00263 {
00264     wxRect labelPos;
00265     int l = 6;
00266     wxSize clientSize = m_control->GetClientSize();
00267     int vx0, vy0;
00268     m_control->GetViewStart(&vx0, &vy0);
00269     wxFont font(8, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_LIGHT);
00270     dc.SetFont(font);
00271     wxPoint pClient(p.x - vx0, p.y - vy0);
00272     // space in upper left, upper right, lower left, lower right
00273     int maxDistUR = std::min(clientSize.x - pClient.x, pClient.y);
00274     int maxDistLL = std::min(pClient.x, clientSize.y - pClient.y);
00275     int maxDistLR = std::min(clientSize.x - pClient.x, clientSize.y - pClient.y);
00276 
00277     // text and magnifier offset
00278     int toff = l-1;
00279     // default to lower right
00280     wxPoint tul = p + wxPoint(toff,toff);
00281 
00282     // calculate text position and extend
00283     // width of border around text label
00284     int tB = 2;
00285     wxCoord tw, th;
00286     dc.GetTextExtent(m_label, &tw, &th);
00287 
00288     if (drawMag)
00289     {
00290         wxBitmap magBitmap = m_control->generateMagBitmap(pointInput, p);
00291         // TODO: select position depending on visible part of canvas
00292         wxPoint ulMag = tul;
00293         // choose placement of the magnifier
00294         int w = toff  + magBitmap.GetWidth()+3;
00295         int db = 5;
00296         if ( maxDistLR > w + db  )
00297         {
00298             ulMag = p + wxPoint(toff,toff);
00299         }
00300         else
00301         {
00302             if (maxDistLL > w + db)
00303             {
00304                 ulMag = p + wxPoint(-w, toff);
00305             }
00306             else
00307             {
00308                 if (maxDistUR > w + db)
00309                 {
00310                     ulMag = p + wxPoint(toff, -w);
00311                 }
00312                 else
00313                 {
00314                     ulMag = p + wxPoint(-w, -w);
00315                 }
00316             }
00317         };
00318 
00319         dc.DrawBitmap(magBitmap, ulMag);
00320         dc.SetPen(wxPen(wxT("BLACK"), 1, wxSOLID));
00321         dc.SetBrush(wxBrush(wxT("WHITE"),wxTRANSPARENT));
00322 
00323         // draw Bevel
00324         int bw = magBitmap.GetWidth();
00325         int bh = magBitmap.GetHeight();
00326         dc.DrawLine(ulMag.x-1, ulMag.y+bh, 
00327                     ulMag.x+bw+1, ulMag.y+bh);
00328         dc.DrawLine(ulMag.x+bw, ulMag.y+bh, 
00329                     ulMag.x+bw, ulMag.y-2);
00330         dc.SetPen(wxPen(wxT("WHITE"), 1, wxSOLID));
00331         dc.DrawLine(ulMag.x-1, ulMag.y-1, 
00332                     ulMag.x+bw+1, ulMag.y-1);
00333         dc.DrawLine(ulMag.x-1, ulMag.y+bh, 
00334                     ulMag.x-1, ulMag.y-2);
00335     }
00336     // choose placement of text.
00337     int db = 5;
00338     int w = toff+tw+2*tB;
00339     if ( maxDistLR > w + db && (!drawMag) )
00340     {
00341         tul = p + wxPoint(toff,toff);
00342     }
00343     else
00344     {
00345         if (maxDistLL > w + db)
00346         {
00347             tul = p + wxPoint(-w, toff);
00348         }
00349         else
00350         {
00351             if (maxDistUR > w + db)
00352             {
00353                 tul = p + wxPoint(toff, -(toff) - (th+2*tB));
00354             }
00355             else
00356             {
00357                 tul = p + wxPoint(-w, -(toff) - (th+2*tB));
00358             };
00359         };
00360     };
00361 
00362     // draw background
00363     dc.SetPen(wxPen(textColour, 1, wxSOLID));
00364     dc.SetBrush(wxBrush(bgColour, wxSOLID));
00365     dc.DrawRectangle(tul.x, tul.y, tw+2*tB+1, th+2*tB);
00366     labelPos.SetLeft(tul.x);
00367     labelPos.SetTop(tul.y);
00368     labelPos.SetWidth(tw+2*tB+1);
00369     labelPos.SetHeight(th+2*tB);
00370     // draw number
00371     dc.SetTextForeground(textColour);
00372     dc.DrawText(m_label, tul + wxPoint(tB,tB));
00373     return labelPos;
00374 };
00375 
00376 void DisplayedControlPoint::DrawLine(wxDC& dc)
00377 {
00378     if(m_control==NULL)
00379     {
00380         return;
00381     };
00382     FDiff2D p1, p2, dp;
00383     // transform end points to pano space
00384     if(!m_control->getFirstInvTrans()->transformImgCoord(p1, hugin_utils::FDiff2D(m_cp.x1, m_cp.y1)))
00385     {
00386         return;
00387     };
00388     if(!m_control->getFirstInvTrans()->transformImgCoord(p2, hugin_utils::FDiff2D(m_cp.x2, m_cp.y2)))
00389     {
00390         return;
00391     };
00392     dp=p2-p1;
00393     int len=roundi(sqrt((m_cp.x1-m_cp.x2)*(m_cp.x1-m_cp.x2)+(m_cp.y1-m_cp.y2)*(m_cp.y1-m_cp.y2)))+1;
00394     if(len<5)
00395     {
00396         //very short line, draw straight line
00397         dc.DrawLine(m_control->roundP(m_control->scale(m_control->applyRot(hugin_utils::FDiff2D(m_cp.x1, m_cp.y1)))),
00398             m_control->roundP(m_control->scale(m_control->applyRot(hugin_utils::FDiff2D(m_cp.x2, m_cp.y2)))));
00399     }
00400     else
00401     {
00402         //longer line, draw correct line, taking output projection into account
00403         wxPoint* points=new wxPoint[len+1];
00404         for(size_t i=0; i<len+1; i++)
00405         {
00406             FDiff2D p=p1+dp*((double)i/len);
00407             // transform line coordinates back to image space
00408             if(!m_control->getFirstTrans()->transformImgCoord(p2, p))
00409             {
00410                 delete []points;
00411                 //fall through, draw direct line, not exact, but better than no line
00412                 dc.DrawLine(m_control->roundP(m_control->scale(m_control->applyRot(hugin_utils::FDiff2D(m_cp.x1, m_cp.y1)))),
00413                     m_control->roundP(m_control->scale(m_control->applyRot(hugin_utils::FDiff2D(m_cp.x2, m_cp.y2)))));
00414                 return;
00415             };
00416             points[i]=m_control->roundP(m_control->scale(m_control->applyRot(p2)));
00417         };
00418         dc.DrawLines(len+1, points);
00419         delete []points;
00420     };
00421 };
00422 
00423 void DisplayedControlPoint::DrawLineSegment(wxDC& dc)
00424 {
00425     if(m_control==NULL)
00426     {
00427         return;
00428     };
00429     // calculate line equation
00430     FDiff2D p1_image=m_mirrored ? hugin_utils::FDiff2D(m_cp.x2, m_cp.y2) : hugin_utils::FDiff2D(m_cp.x1, m_cp.y1);
00431     FDiff2D p2_image=m_mirrored ? hugin_utils::FDiff2D(m_cp.x1, m_cp.y1) : hugin_utils::FDiff2D(m_cp.x2, m_cp.y2);
00432     FDiff2D p1, p2, dp;
00433     if(!m_control->getFirstInvTrans()->transformImgCoord(p1, p1_image))
00434     {
00435         return;
00436     };
00437     if(!m_control->getSecondInvTrans()->transformImgCoord(p2, p2_image))
00438     {
00439         return;
00440     };
00441     dp=p2-p1;
00442     // now find the parameter to draw an appropriate long line segment
00443     double f=1.0;
00444     int image_width=m_control->GetRealImageSize().GetWidth();
00445     int image_height=m_control->GetRealImageSize().GetHeight();
00446     int image_dimension=std::min(image_width, image_height);
00447     int line_length=-1;
00448     while(f>1e-4)
00449     {
00450         p2=p1+dp*f;
00451         if(m_control->getFirstTrans()->transformImgCoord(p2_image, p2))
00452         {
00453             double length=sqrt(p1_image.squareDistance(p2_image));
00454             if(length > 0.05f * image_dimension && length < 0.75f * image_dimension)
00455             {
00456                 line_length=hugin_utils::roundi(length);
00457                 break;
00458             };
00459         };
00460         f*=0.9;
00461     };
00462     // found no suitable length, don't draw line
00463     if(line_length<1)
00464     {
00465         return;
00466     };
00467     // now calc line positions
00468     wxPoint* points=new wxPoint[2*line_length+1];
00469     for(size_t i=0; i<2*line_length+1; i++)
00470     {
00471         FDiff2D p=p1+dp*f*(((double)i-line_length)/(2.0f*line_length));
00472         if(!m_control->getFirstTrans()->transformImgCoord(p2, p))
00473         {
00474             delete []points;
00475             return;
00476         };
00477         points[i]=m_control->roundP(m_control->scale(m_control->applyRot(p2)));
00478     };
00479     //and finally draw line segment
00480     dc.SetClippingRegion(wxPoint(0,0), m_control->GetBitmapSize());
00481     dc.DrawLines(2*line_length+1, points);
00482     dc.DestroyClippingRegion();
00483     delete []points;
00484 };
00485 
00486 const bool DisplayedControlPoint::isOccupiedLabel(const wxPoint mousePos) const
00487 {
00488     if(m_line)
00489     {
00490         return m_labelPos.Contains(mousePos) || m_labelPos2.Contains(mousePos);
00491     }
00492     else
00493     {
00494         return m_labelPos.Contains(mousePos);
00495     };
00496 };
00497 
00498 const bool DisplayedControlPoint::isOccupiedPos(const hugin_utils::FDiff2D &p) const
00499 {
00500     double d=m_control->invScale(3.0);
00501     if(m_line)
00502     {
00503         return (p.x < m_cp.x1 + d && p.x > m_cp.x1 - d && p.y < m_cp.y1 + d && p.y > m_cp.y1 - d) ||
00504                (p.x < m_cp.x2 + d && p.x > m_cp.x2 - d && p.y < m_cp.y2 + d && p.y > m_cp.y2 - d);
00505     }
00506     else
00507     {
00508         if(m_mirrored)
00509         {
00510             return (p.x < m_cp.x2 + d && p.x > m_cp.x2 - d && p.y < m_cp.y2 + d && p.y > m_cp.y2 - d);
00511         }
00512         else
00513         {
00514             return (p.x < m_cp.x1 + d && p.x > m_cp.x1 - d && p.y < m_cp.y1 + d && p.y > m_cp.y1 - d);
00515         };
00516     };
00517 };
00518 
00519 void DisplayedControlPoint::CheckSelection(const wxPoint mousePos, const hugin_utils::FDiff2D& p)
00520 {
00521     if(!m_line)
00522     {
00523         return;
00524     };
00525     double d=m_control->invScale(3.0);
00526     m_mirrored=m_labelPos2.Contains(mousePos) || 
00527         (p.x < m_cp.x2 + d && p.x > m_cp.x2 - d && p.y < m_cp.y2 + d && p.y > m_cp.y2 - d);
00528 };
00529 
00530 void DisplayedControlPoint::UpdateControlPointX(double x)
00531 {
00532     if(m_mirrored)
00533     {
00534         m_cp.x2=x;
00535     }
00536     else
00537     {
00538         m_cp.x1=x;
00539     };
00540 };
00541 
00542 void DisplayedControlPoint::UpdateControlPointY(double y)
00543 {
00544     if(m_mirrored)
00545     {
00546         m_cp.y2=y;
00547     }
00548     else
00549     {
00550         m_cp.y1=y;
00551     };
00552 };
00553 
00554 void DisplayedControlPoint::UpdateControlPoint(hugin_utils::FDiff2D newPoint)
00555 {
00556     if(m_mirrored)
00557     {
00558         m_cp.x2=newPoint.x;
00559         m_cp.y2=newPoint.y;
00560     }
00561     else
00562     {
00563         m_cp.x1=newPoint.x;
00564         m_cp.y1=newPoint.y;
00565     };
00566 };
00567 
00568 void DisplayedControlPoint::ShiftControlPoint(hugin_utils::FDiff2D shift)
00569 {
00570     if(m_mirrored)
00571     {
00572         m_cp.x2+=shift.x;
00573         m_cp.y2+=shift.y;
00574     }
00575     else
00576     {
00577         m_cp.x1+=shift.x;
00578         m_cp.y1+=shift.y;
00579     };
00580 };
00581 
00582 void DisplayedControlPoint::StartLineControlPoint(hugin_utils::FDiff2D newPoint)
00583 {
00584     //start a new line control point
00585     m_line=true;
00586     m_mirrored=true;
00587     m_label=_("new");
00588     m_cp.image1Nr=UINT_MAX;
00589     m_cp.x1=newPoint.x;
00590     m_cp.y1=newPoint.y;
00591     m_cp.image2Nr=m_cp.image1Nr;
00592     m_cp.x2=m_cp.x1;
00593     m_cp.y2=m_cp.y1;
00594     m_cp.mode=ControlPoint::X;
00595 };
00596 
00597 hugin_utils::FDiff2D DisplayedControlPoint::GetPos() const
00598 {
00599     return m_mirrored ? FDiff2D(m_cp.x2, m_cp.y2) : FDiff2D(m_cp.x1, m_cp.y1);
00600 };
00601 
00602 bool DisplayedControlPoint::operator==(const DisplayedControlPoint other)
00603 {
00604     return m_cp==other.GetControlPoint() && m_mirrored == other.IsMirrored() && m_label == other.GetLabel();
00605 };
00606 
00607 // our image control
00608 BEGIN_EVENT_TABLE(CPImageCtrl, wxScrolledWindow)
00609     EVT_SIZE(CPImageCtrl::OnSize)
00610     EVT_CHAR(CPImageCtrl::OnKey)
00611 //    EVT_KEY_UP(CPImageCtrl::OnKeyUp)
00612     EVT_KEY_DOWN(CPImageCtrl::OnKeyDown)
00613     EVT_LEAVE_WINDOW(CPImageCtrl::OnMouseLeave)
00614     EVT_ENTER_WINDOW(CPImageCtrl::OnMouseEnter)
00615     EVT_MOTION(CPImageCtrl::mouseMoveEvent)
00616     EVT_LEFT_DOWN(CPImageCtrl::mousePressLMBEvent)
00617     EVT_LEFT_UP(CPImageCtrl::mouseReleaseLMBEvent)
00618     EVT_RIGHT_DOWN(CPImageCtrl::mousePressRMBEvent)
00619     EVT_RIGHT_UP(CPImageCtrl::mouseReleaseRMBEvent)
00620     EVT_MIDDLE_DOWN(CPImageCtrl::mousePressMMBEvent)
00621     EVT_MIDDLE_UP(CPImageCtrl::mouseReleaseMMBEvent)
00622     EVT_TIMER(-1, CPImageCtrl::OnTimer)
00623 END_EVENT_TABLE()
00624 
00625 bool CPImageCtrl::Create(wxWindow * parent, wxWindowID id,
00626                          const wxPoint& pos,
00627                          const wxSize& size,
00628                          long style,
00629                          const wxString& name)
00630 {
00631     wxScrolledWindow::Create(parent, id, pos, size, style, name);
00632     selectedPointNr = 0;
00633     editState = NO_IMAGE;
00634     scaleFactor = 1;
00635     fitToWindow = false;
00636     m_showSearchArea = false;
00637     m_searchRectWidth = 0;
00638     m_showTemplateArea = false;
00639     m_templateRectWidth = 0;
00640     m_editPanel = 0;
00641     m_imgRotation = ROT0;
00642     m_sameImage = false;
00643 
00644     wxString filename;
00645 
00646 #if defined(__WXMSW__) 
00647     wxString cursorPath = huginApp::Get()->GetXRCPath() + wxT("/data/cursor_cp_pick.cur");
00648     m_CPSelectCursor = new wxCursor(cursorPath, wxBITMAP_TYPE_CUR);
00649 #else
00650     m_CPSelectCursor = new wxCursor(wxCURSOR_CROSS);
00651 #endif
00652     SetCursor(*m_CPSelectCursor);
00653 
00654     // TODO: define custom, light background colors.
00655     pointColors.push_back(wxTheColourDatabase->Find(wxT("BLUE")));
00656     textColours.push_back(wxTheColourDatabase->Find(wxT("WHITE")));
00657 
00658     pointColors.push_back(wxTheColourDatabase->Find(wxT("GREEN")));
00659     textColours.push_back(wxTheColourDatabase->Find(wxT("WHITE")));
00660 
00661     pointColors.push_back(wxTheColourDatabase->Find(wxT("CYAN")));
00662     textColours.push_back(wxTheColourDatabase->Find(wxT("BLACK")));
00663     pointColors.push_back(wxTheColourDatabase->Find(wxT("GOLD")));
00664     textColours.push_back(wxTheColourDatabase->Find(wxT("BLACK")));
00665 
00666     pointColors.push_back(wxTheColourDatabase->Find(wxT("NAVY")));
00667     textColours.push_back(wxTheColourDatabase->Find(wxT("WHITE")));
00668 
00669     pointColors.push_back(wxTheColourDatabase->Find(wxT("DARK TURQUOISE")));
00670     textColours.push_back(wxTheColourDatabase->Find(wxT("BLACK")));
00671 
00672     pointColors.push_back(wxTheColourDatabase->Find(wxT("SALMON")));
00673     textColours.push_back(wxTheColourDatabase->Find(wxT("BLACK")));
00674 
00675     pointColors.push_back(wxTheColourDatabase->Find(wxT("MAROON")));
00676     textColours.push_back(wxTheColourDatabase->Find(wxT("BLACK")));
00677 
00678     pointColors.push_back(wxTheColourDatabase->Find(wxT("KHAKI")));
00679     textColours.push_back(wxTheColourDatabase->Find(wxT("BLACK")));
00680 
00681     m_searchRectWidth = 120;
00682     m_mouseInWindow = false;
00683     m_forceMagnifier = false;
00684     m_timer.SetOwner(this);
00685 
00686     return true;
00687 }
00688 
00689 void CPImageCtrl::Init(CPEditorPanel * parent)
00690 {
00691     m_editPanel = parent;
00692     m_sameImage = false;
00693 }
00694 
00695 CPImageCtrl::~CPImageCtrl()
00696 {
00697     DEBUG_TRACE("dtor");
00698     this->SetCursor(wxNullCursor);
00699     delete m_CPSelectCursor;
00700     DEBUG_TRACE("dtor end");
00701 }
00702 
00703 void CPImageCtrl::OnDraw(wxDC & dc)
00704 {
00705     wxSize vSize = GetClientSize();
00706     // draw image (FIXME, redraw only visible regions.)
00707     if (editState != NO_IMAGE && m_img.get()) {
00708                 //clear the blank rectangle to the left of the image
00709         if (bitmap.GetWidth() < vSize.GetWidth()) {
00710             dc.SetPen(wxPen(GetBackgroundColour(), 1, wxSOLID));
00711             dc.SetBrush(wxBrush(GetBackgroundColour(),wxSOLID));
00712             dc.DrawRectangle(bitmap.GetWidth(), 0,
00713                              vSize.GetWidth() - bitmap.GetWidth(),vSize.GetHeight());
00714         }
00715                 //clear the blank rectangle below the image
00716         if (bitmap.GetHeight() < vSize.GetHeight()) {
00717             dc.SetPen(wxPen(GetBackgroundColour(), 1, wxSOLID));
00718             dc.SetBrush(wxBrush(GetBackgroundColour(),wxSOLID));
00719                         dc.DrawRectangle(0, bitmap.GetHeight(),
00720                              vSize.GetWidth(), vSize.GetHeight() - bitmap.GetHeight());
00721         }
00722         dc.DrawBitmap(bitmap,0,0);
00723         } else {
00724                 // clear the rectangle and exit
00725         dc.SetPen(wxPen(GetBackgroundColour(), 1, wxSOLID));
00726         dc.SetBrush(wxBrush(GetBackgroundColour(),wxSOLID));
00727         dc.Clear();
00728                 return;
00729         }
00730 
00731     // draw known points.
00732     for(size_t i=0; i<m_points.size(); i++)
00733     {
00734         if (!(editState == KNOWN_POINT_SELECTED && i==selectedPointNr))
00735         {
00736             m_points[i].Draw(dc, false);
00737         };
00738     }
00739 
00740     switch(editState) {
00741     case NEW_POINT_SELECTED:
00742         // Boundary check
00743         if ((newPoint.x < 0) || (newPoint.y < 0)) {
00744             // Tried to create a point outside of the canvas.  Ignore it.
00745             break;
00746         } 
00747         {
00748             DisplayedControlPoint dsp(HuginBase::ControlPoint(0, newPoint.x, newPoint.y, 0, 0, 0), this, false);
00749             dsp.SetLabel(_("new"));
00750             dsp.Draw(dc, false, true);
00751         }
00752         if (m_showTemplateArea) {
00753             dc.SetLogicalFunction(wxINVERT);
00754             dc.SetPen(wxPen(wxT("RED"), 1, wxSOLID));
00755             dc.SetBrush(wxBrush(wxT("WHITE"),wxTRANSPARENT));
00756             wxPoint upperLeft = applyRot(roundP(newPoint));
00757             upperLeft = scale(upperLeft);
00758 
00759             int width = scale(m_templateRectWidth);
00760 
00761             dc.DrawRectangle(upperLeft.x-width, upperLeft.y-width, 2*width, 2*width);
00762             dc.SetLogicalFunction(wxCOPY);
00763         }
00764 
00765         break;
00766     case NEW_LINE_CREATING:
00767         m_selectedPoint.Draw(dc, false, true);
00768         break;
00769     case KNOWN_POINT_SELECTED:
00770         m_points[selectedPointNr].Draw(dc, true);
00771         break;
00772     case NO_SELECTION:
00773     case NO_IMAGE:
00774         break;
00775     }
00776 
00777     if (m_showSearchArea && m_mousePos.x != -1){
00778         dc.SetLogicalFunction(wxINVERT);
00779         dc.SetPen(wxPen(wxT("WHITE"), 1, wxSOLID));
00780         dc.SetBrush(wxBrush(wxT("WHITE"),wxTRANSPARENT));
00781 
00782         FDiff2D upperLeft = applyRot(m_mousePos);
00783         upperLeft = scale(upperLeft);
00784         int width = scale(m_searchRectWidth);
00785         DEBUG_DEBUG("drawing rect " << upperLeft << " with width " << 2*width << " orig: " << m_searchRectWidth*2  << " scale factor: " << getScaleFactor());
00786 
00787         dc.DrawRectangle(roundi(upperLeft.x - width), roundi(upperLeft.y-width), 2*width, 2*width);
00788         dc.SetLogicalFunction(wxCOPY);
00789     }
00790 }
00791 
00792 class ScalingTransform
00793 {
00794 public:
00795     ScalingTransform(double scale)
00796     : m_scale(scale) {};
00797 
00798     bool transformImgCoord(double & sx, double & sy, double x, double y)
00799     {
00800         sx = m_scale*x;
00801         sy = m_scale*y;
00802         return true;
00803     }
00804 
00805     double m_scale;
00806 };
00807 
00808 wxBitmap CPImageCtrl::generateMagBitmap(FDiff2D point, wxPoint canvasPos) const
00809 {
00810     typedef vigra::RGBValue<vigra::UInt8> VT;
00811     DEBUG_TRACE("")
00812 
00813     // draw magnified image (TODO: warp!)
00814     double magScale = 3.0;
00815     wxConfigBase::Get()->Read(wxT("/CPEditorPanel/MagnifierScale"), &magScale);
00816     // width (and height) of magnifier region (output), should be odd
00817     int magWidth = wxConfigBase::Get()->Read(wxT("/CPEditorPanel/MagnifierWidth"),61l);
00818     int hw = magWidth/2;
00819     magWidth = hw*2+1;
00820 
00821     // setup simple scaling transformation function.
00822     ScalingTransform transform(1.0/magScale);
00823     ScalingTransform invTransform(magScale);
00824     wxImage img(magWidth, magWidth);
00825     vigra::BasicImageView<VT> magImg((VT*)img.GetData(), magWidth,magWidth);
00826     vigra::BImage maskImg(magWidth, magWidth);
00827     vigra_ext::PassThroughFunctor<vigra::UInt8> ptf;
00828 
00829 
00830     // middle pixel
00831     double mx, my;
00832     invTransform.transformImgCoord(mx, my, point.x, point.y);
00833 
00834     // apply the transform
00835     AppBase::MultiProgressDisplay progDisp;
00836     vigra_ext::transformImageIntern(vigra::srcImageRange(*(m_img->image8)),
00837                          vigra::destImageRange(magImg),
00838                          vigra::destImage(maskImg),
00839                          transform,
00840                          ptf,
00841                          vigra::Diff2D(hugin_utils::roundi(mx - hw),
00842                                        hugin_utils::roundi(my - hw)),
00843                          vigra_ext::interp_cubic(),
00844                          false,
00845                          progDisp);
00846 
00847     // TODO: contrast enhancement
00848     vigra::FindMinMax<vigra::UInt8> minmax;
00849     vigra::inspectImage(vigra::srcImageRange(magImg), minmax);
00850 
00851     // transform to range 0...255
00852     vigra::transformImage(vigra::srcImageRange(magImg), vigra::destImage(magImg),
00853                           vigra::linearRangeMapping(
00854                             VT(minmax.min), VT(minmax.max),               // src range
00855                             VT(0), VT(255)) // dest range
00856                           );
00857 //    vigra::transformImage(srcImageRange(magImg), destImage(magImg),
00858 //       vigra::BrightnessContrastFunctor<float>(brightness, contrast, minmax.min, minmax.max));
00859 
00860     // draw cursor
00861     for(int x=0; x < magWidth; x++) {
00862         VT p =magImg(x,hw+1);
00863         vigra::UInt8 v = 0.3/255*p.red() + 0.6/255*p.green() + 0.1/255*p.blue() < 0.5 ? 255 : 0;
00864         p[0] = v;
00865         p[1] = v;
00866         p[2] = v;
00867         magImg(x,hw+1) = p;
00868         p = magImg(hw+1, x);
00869         v = 0.3/255*p.red() + 0.6/255*p.green() + 0.1/255*p.blue() < 0.5 ? 255 : 0;
00870         p[0] = v;
00871         p[1] = v;
00872         p[2] = v;
00873         magImg(hw+1, x) = p;
00874     }
00875 
00876     // rotate image according to current display
00877     switch(m_imgRotation) {
00878         case ROT90:
00879             img = img.Rotate90(true);
00880             break;
00881         case ROT180:
00882             // this is slower than it needs to be...
00883             img = img.Rotate90(true);
00884             img = img.Rotate90(true);
00885             break;
00886         case ROT270:
00887             img = img.Rotate90(false);
00888             break;
00889         default:
00890             break;
00891     }
00892     return wxBitmap (img);
00893 }
00894 
00895 wxSize CPImageCtrl::DoGetBestSize() const
00896 {
00897     return wxSize(imageSize.GetWidth(),imageSize.GetHeight());
00898 }
00899 
00900 
00901 void CPImageCtrl::setImage(const std::string & file, ImageRotation imgRot)
00902 {
00903     DEBUG_TRACE("setting Image " << file);
00904     imageFilename = file;
00905     m_sameImage=false;
00906     wxString fn(imageFilename.c_str(),HUGIN_CONV_FILENAME);
00907     if (wxFileName::FileExists(fn)) {
00908         m_imgRotation = imgRot;
00909         m_img = ImageCache::getInstance().getImageIfAvailable(imageFilename);
00910         editState = NO_SELECTION;
00911         if (m_img.get()) {
00912             rescaleImage();
00913         } else {
00914             // load the image in the background.
00915             m_imgRequest = ImageCache::getInstance().requestAsyncImage(imageFilename);
00916             m_imgRequest->ready.connect(
00917                 boost::bind(&CPImageCtrl::OnImageLoaded, this, _1, _2, _3));
00918             // With m_img.get() 0, everything will act as normal except drawing.
00919         }
00920     } else {
00921         editState = NO_IMAGE;
00922         bitmap = wxBitmap();
00923         // delete the image (release shared_ptr)
00924         // create an empty image.
00925         m_img = ImageCache::EntryPtr(new ImageCache::Entry);
00926     }
00927 }
00928 
00929 void CPImageCtrl::setTransforms(PTools::Transform* firstTrans, PTools::Transform* firstInvTrans, PTools::Transform* secondInvTrans)
00930 {
00931     m_firstTrans=firstTrans;
00932     m_firstInvTrans=firstInvTrans;
00933     m_secondInvTrans=secondInvTrans;
00934 };
00935 
00936 void CPImageCtrl::setSameImage(bool sameImage)
00937 {
00938     m_sameImage=sameImage;
00939 };
00940 
00941 void CPImageCtrl::OnImageLoaded(ImageCache::EntryPtr entry, std::string filename, bool load_small)
00942 {
00943     // check we are still displaying this image
00944     if (imageFilename == filename)
00945     {
00946         m_img = entry;
00947         rescaleImage();
00948     }
00949 }
00950 
00951 void CPImageCtrl::rescaleImage()
00952 {
00953     if (editState == NO_IMAGE || !m_img.get()) {
00954         return;
00955     }
00956     wxImage img = imageCacheEntry2wxImage(m_img);
00957     if (img.GetWidth() == 0) {
00958         return;
00959     }
00960     imageSize = wxSize(img.GetWidth(), img.GetHeight());
00961     m_realSize = imageSize;
00962     if (fitToWindow) {
00963         scaleFactor = calcAutoScaleFactor(imageSize);
00964     }
00965     DEBUG_DEBUG("src image size "
00966                 << imageSize.GetHeight() << "x" << imageSize.GetWidth());
00967     if (getScaleFactor() == 1.0) {
00968         // need to rotate full image. warning. this can be very memory intensive
00969         if (m_imgRotation != ROT0) {
00970             wxImage tmp(img);
00971             switch(m_imgRotation) {
00972                 case ROT90:
00973                     tmp = tmp.Rotate90(true);
00974                     break;
00975                 case ROT180:
00976                     // this is slower than it needs to be...
00977                     tmp = tmp.Rotate90(true);
00978                     tmp = tmp.Rotate90(true);
00979                     break;
00980                 case ROT270:
00981                     tmp = tmp.Rotate90(false);
00982                     break;
00983                 default:
00984                     break;
00985             }
00986             bitmap = wxBitmap(tmp);
00987         } else {
00988             bitmap = wxBitmap(img);
00989         }
00990     } else {
00991         imageSize.SetWidth( scale(imageSize.GetWidth()) );
00992         imageSize.SetHeight( scale(imageSize.GetHeight()) );
00993         DEBUG_DEBUG("rescaling to " << imageSize.GetWidth() << "x"
00994                     << imageSize.GetHeight() );
00995 
00996         wxImage tmp= img.Scale(imageSize.GetWidth(), imageSize.GetHeight());
00997         switch(m_imgRotation) {
00998             case ROT90:
00999                 tmp = tmp.Rotate90(true);
01000                 break;
01001             case ROT180:
01002                     // this is slower than it needs to be...
01003                 tmp = tmp.Rotate90(true);
01004                 tmp = tmp.Rotate90(true);
01005                 break;
01006             case ROT270:
01007                 tmp = tmp.Rotate90(false);
01008                 break;
01009             default:
01010                 break;
01011         }
01012 
01013         bitmap = wxBitmap(tmp);
01014         DEBUG_DEBUG("rescaling finished");
01015     }
01016 
01017     if (m_imgRotation == ROT90 || m_imgRotation == ROT270) {
01018         SetVirtualSize(imageSize.GetHeight(), imageSize.GetWidth());
01019     } else {
01020         SetVirtualSize(imageSize.GetWidth(), imageSize.GetHeight());
01021     }
01022     SetScrollRate(1,1);
01023     Refresh(FALSE);
01024 }
01025 
01026 void CPImageCtrl::setCtrlPoint(const HuginBase::ControlPoint& cp, const bool mirrored)
01027 {
01028     DisplayedControlPoint dcp(cp, this, mirrored);
01029     dcp.SetColour(pointColors[m_points.size() % pointColors.size()], textColours[m_points.size() % textColours.size()]);
01030     dcp.SetLabel(wxString::Format(wxT("%d"), m_points.size()));
01031     m_points.push_back(dcp);
01032 }
01033 
01034 void CPImageCtrl::clearCtrlPointList()
01035 {
01036     m_points.clear();
01037     if(editState == KNOWN_POINT_SELECTED)
01038     {
01039         editState = NO_SELECTION;
01040     };
01041     selectedPointNr = UINT_MAX;
01042 };
01043 
01044 void CPImageCtrl::clearNewPoint()
01045 {
01046     DEBUG_TRACE("clearNewPoint");
01047     if (editState != NO_IMAGE) {
01048         editState = NO_SELECTION;
01049     }
01050 }
01051 
01052 
01053 void CPImageCtrl::selectPoint(unsigned int nr)
01054 {
01055     DEBUG_TRACE("nr: " << nr);
01056     if (nr < m_points.size()) {
01057         selectedPointNr = nr;
01058         editState = KNOWN_POINT_SELECTED;
01059         showPosition(m_points[nr].GetPos());
01060         update();
01061     } else {
01062         DEBUG_DEBUG("trying to select invalid point nr: " << nr << ". Nr of points: " << points.size());
01063     }
01064 }
01065 
01066 void CPImageCtrl::deselect()
01067 {
01068     DEBUG_TRACE("deselecting points");
01069     if (editState == KNOWN_POINT_SELECTED) {
01070         editState = NO_SELECTION;
01071     }
01072     // update view
01073     update();
01074 }
01075 
01076 void CPImageCtrl::showPosition(FDiff2D point, bool warpPointer)
01077 {
01078     DEBUG_DEBUG("x: " << point.x  << " y: " << point.y);
01079     // transform and scale the co-ordinate to the screen.
01080     point = applyRot(point);
01081     point = scale(point);
01082     int x = roundi(point.x);
01083     int y = roundi(point.y);
01084 
01085     wxSize sz = GetClientSize();
01086     int scrollx = x - sz.GetWidth()/2;
01087     int scrolly = y - sz.GetHeight()/2;
01088     Scroll(scrollx, scrolly);
01089     if (warpPointer) {
01090         int sx,sy;
01091         GetViewStart(&sx, &sy);
01092         DEBUG_DEBUG("relative coordinages: " << x-sx << "," << y-sy);
01093         WarpPointer(x-sx,y-sy);
01094     }
01095 }
01096 
01097 CPImageCtrl::EditorState CPImageCtrl::isOccupied(wxPoint mousePos, const FDiff2D &p, unsigned int & pointNr) const
01098 {
01099     // check if mouse is hovering over a label
01100     if(m_points.size()>0)
01101     {
01102         for(int i=m_points.size()-1; i>=0; i--)
01103         {
01104             if(m_points[i].isOccupiedLabel(mousePos))
01105             {
01106                 pointNr = i;
01107                 return KNOWN_POINT_SELECTED;
01108             }
01109         };
01110         // check if mouse is over a known point
01111         for(vector<DisplayedControlPoint>::const_iterator it=m_points.begin(); it!=m_points.end(); it++)
01112         {
01113             if(it->isOccupiedPos(p))
01114             {
01115                 pointNr = it - m_points.begin();
01116                 return KNOWN_POINT_SELECTED;
01117             }
01118         };
01119     };
01120 
01121     return NEW_POINT_SELECTED;
01122 }
01123 
01124 void CPImageCtrl::DrawSelectionRectangle(hugin_utils::FDiff2D pos1,hugin_utils::FDiff2D pos2)
01125 {
01126     wxClientDC dc(this);
01127     PrepareDC(dc);
01128     dc.SetLogicalFunction(wxINVERT);
01129     dc.SetPen(wxPen(*wxWHITE,1,wxDOT));
01130     dc.SetBrush(*wxTRANSPARENT_BRUSH);
01131     wxPoint p1=roundP(scale(applyRot(pos1)));
01132     wxPoint p2=roundP(scale(applyRot(pos2)));
01133     dc.DrawRectangle(p1.x,p1.y,p2.x-p1.x,p2.y-p1.y);
01134 };
01135 
01136 void CPImageCtrl::mouseMoveEvent(wxMouseEvent& mouse)
01137 {
01138     if (!m_img.get()) return; // ignore events if no image loaded.
01139     wxPoint unScrolledMousePos;
01140     CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y,
01141                            &unScrolledMousePos.x, & unScrolledMousePos.y);
01142     FDiff2D mpos(unScrolledMousePos.x, unScrolledMousePos.y);
01143     bool doUpdate = false;
01144     mpos = applyRotInv(invScale(mpos));
01145     wxPoint mpos_ = applyRotInv(invScale(unScrolledMousePos));
01146     // if mouseclick is out of image, ignore
01147     if ((mpos.x >= m_realSize.GetWidth() || mpos.y >= m_realSize.GetHeight()) && editState!=SELECT_DELETE_REGION)
01148     {
01149         return;
01150     }
01151 
01152 //    DEBUG_DEBUG(" pos:" << mpos.x << ", " << mpos.y);
01153     // only if the shift key is not pressed.
01154     if (mouse.LeftIsDown() && ! mouse.ShiftDown()) {
01155         switch(editState) {
01156         case NO_SELECTION:
01157             DEBUG_DEBUG("mouse down movement without selection, in NO_SELECTION state!");
01158             break;
01159         case KNOWN_POINT_SELECTED:
01160             if (mpos.x >= 0 && mpos.x <= m_realSize.GetWidth()){
01161                 m_points[selectedPointNr].UpdateControlPointX(mpos.x);
01162             } else if (mpos.x < 0) {
01163                 m_points[selectedPointNr].UpdateControlPointX(0);
01164             } else if (mpos.x > m_realSize.GetWidth()) {
01165                 m_points[selectedPointNr].UpdateControlPointX(m_realSize.GetWidth());
01166             }
01167 
01168             if (mpos.y >= 0 && mpos.y <= m_realSize.GetHeight()){
01169                 m_points[selectedPointNr].UpdateControlPointY(mpos.y);
01170             } else if (mpos.y < 0) {
01171                 m_points[selectedPointNr].UpdateControlPointY(0);
01172             } else if (mpos.y > m_realSize.GetHeight()) {
01173                 m_points[selectedPointNr].UpdateControlPointY(m_realSize.GetHeight());
01174             }
01175             // emit a notify event here.
01176             //
01177             //emit(pointMoved(selectedPointNr, points[selectedPointNr]));
01178             // do more intelligent updating here?
01179             doUpdate = true;
01180             break;
01181             // not possible.
01182         case NEW_POINT_SELECTED:
01183             DEBUG_DEBUG("WARNING: mouse move in new point state")
01184             newPoint = mpos;
01185             doUpdate = true;
01186             break;
01187         case NEW_LINE_CREATING:
01188             m_selectedPoint.UpdateControlPoint(mpos);
01189             doUpdate = true;
01190             break;
01191         case NO_IMAGE:
01192             break;
01193         }
01194     }
01195 
01196     if ((mouse.MiddleIsDown() || mouse.ShiftDown() || mouse.m_controlDown ) && editState!=SELECT_DELETE_REGION) {
01197         // scrolling with the mouse
01198         if (m_mouseScrollPos !=mouse.GetPosition()) {
01199             wxPoint delta_ = mouse.GetPosition() - m_mouseScrollPos;
01200             double speed = (double)GetVirtualSize().GetHeight() / GetClientSize().GetHeight();
01201 //          int speed = wxConfigBase::Get()->Read(wxT("/CPEditorPanel/scrollSpeed"),5);
01202             wxPoint delta;
01203             delta.x = roundi(delta_.x * speed);
01204             delta.y =  roundi(delta_.y * speed);
01205             // scrolling is done later
01206             if (mouse.ShiftDown()) {
01207                 // emit scroll event, so that other window can be scrolled
01208                 // as well.
01209                 CPEvent e(this, CPEvent::SCROLLED, FDiff2D(delta.x, delta.y));
01210                 emit(e);
01211             } else {
01212                 // scroll only our window
01213                 ScrollDelta(delta);
01214             }
01215             m_mouseScrollPos = mouse.GetPosition();
01216         }
01217     }
01218 
01219     if(mouse.RightIsDown() && editState==SELECT_DELETE_REGION)
01220     {
01221         //update selection rectangle
01222         DrawSelectionRectangle(rectStartPos,m_mousePos);
01223         DrawSelectionRectangle(rectStartPos,mpos);
01224     }
01225 //    DEBUG_DEBUG("ImageDisplay: mouse move, state: " << editState);
01226 
01227     // draw a rectangle
01228     if (m_showSearchArea) {
01229         doUpdate = true;
01230     }
01231 
01232     unsigned int selPointNr;
01233     if (isOccupied(unScrolledMousePos, mpos, selPointNr) == KNOWN_POINT_SELECTED &&
01234         (! (editState == KNOWN_POINT_SELECTED && selectedPointNr == selPointNr) ) ) {
01235         SetCursor(wxCursor(wxCURSOR_ARROW));
01236     } else {
01237         SetCursor(*m_CPSelectCursor);
01238     }
01239 
01240     m_mousePos = mpos;
01241     // repaint
01242     if (doUpdate) {
01243         update();
01244     }
01245 }
01246 
01247 
01248 void CPImageCtrl::mousePressLMBEvent(wxMouseEvent& mouse)
01249 {
01250     DEBUG_DEBUG("LEFT MOUSE DOWN");
01251     if (!m_img.get()) return; // ignore events if no image loaded.
01252     //ignore left mouse button if selecting region with right mouse button
01253     if(editState==SELECT_DELETE_REGION) 
01254         return;
01255     wxPoint unScrolledMousePos;
01256     CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y,
01257                            &unScrolledMousePos.x, & unScrolledMousePos.y);
01258     FDiff2D mpos(unScrolledMousePos.x, unScrolledMousePos.y);
01259     mpos = applyRotInv(invScale(mpos));
01260     wxPoint mpos_ = applyRotInv(invScale(unScrolledMousePos));
01261     DEBUG_DEBUG("mousePressEvent, pos:" << mpos.x
01262                 << ", " << mpos.y);
01263     // if mouseclick is out of image, ignore
01264     if (mpos.x >= m_realSize.GetWidth() || mpos.y >= m_realSize.GetHeight()) {
01265         return;
01266     }
01267     unsigned int selPointNr = 0;
01268     EditorState clickState = isOccupied(unScrolledMousePos, mpos, selPointNr);
01269     if (mouse.LeftDown() && editState != NO_IMAGE
01270         && mpos.x < m_realSize.x && mpos.y < m_realSize.y)
01271     {
01272         // we can always select a new point
01273         if (clickState == KNOWN_POINT_SELECTED) {
01274             DEBUG_DEBUG("click on point: " << selPointNr);
01275             selectedPointNr = selPointNr;
01276             m_points[selectedPointNr].CheckSelection(unScrolledMousePos, mpos);
01277             m_selectedPoint = m_points[selectedPointNr];
01278             editState = clickState;
01279             CPEvent e( this, selectedPointNr);
01280             m_forceMagnifier = true;
01281             emit(e);
01282         } else if (clickState == NEW_POINT_SELECTED) {
01283             DEBUG_DEBUG("click on new space, select new point");
01284             if(m_sameImage && mouse.AltDown())
01285             {
01286                 editState = NEW_LINE_CREATING;
01287                 m_selectedPoint.StartLineControlPoint(mpos);
01288                 m_selectedPoint.SetControl(this);
01289             }
01290             else
01291             {
01292                 editState = NEW_POINT_SELECTED;
01293             };
01294             newPoint = mpos;
01295         } else {
01296             DEBUG_ERROR("invalid state " << clickState << " on mouse down");
01297         }
01298 //        DEBUG_DEBUG("ImageDisplay: mouse down, state change: " << oldstate
01299 //                    << " -> " << editState);
01300     }
01301     m_mousePos = mpos;
01302 }
01303 
01304 void CPImageCtrl::OnTimer(wxTimerEvent & e)
01305 {
01306     if (!m_img.get()) return; // ignore events if no image loaded.
01307     m_forceMagnifier = false;
01308     update();
01309 }
01310 
01311 void CPImageCtrl::mouseReleaseLMBEvent(wxMouseEvent& mouse)
01312 {
01313     DEBUG_DEBUG("LEFT MOUSE UP");
01314     if (!m_img.get()) return; // ignore events if no image loaded.
01315     //ignore left mouse button if selecting region with right mouse button
01316     if(editState==SELECT_DELETE_REGION) 
01317         return;
01318 
01319     m_timer.Start(2000, true);
01320 
01321     wxPoint mpos_;
01322     CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y,
01323                            &mpos_.x, & mpos_.y);
01324     FDiff2D mpos(mpos_.x, mpos_.y);
01325     mpos = applyRotInv(invScale(mpos));
01326     DEBUG_DEBUG("mouseReleaseEvent, pos:" << mpos.x
01327                 << ", " << mpos.y);
01328     // if mouseclick is out of image, ignore
01329     if (mpos.x >= m_realSize.GetWidth() || mpos.y >= m_realSize.GetHeight()) {
01330         return;
01331     }
01332 //    EditorState oldState = editState;
01333     if (mouse.LeftUp()) {
01334         switch(editState) {
01335         case NO_SELECTION:
01336             DEBUG_DEBUG("mouse release without selection");
01337             break;
01338         case KNOWN_POINT_SELECTED:
01339         {
01340             DEBUG_DEBUG("mouse release with known point " << selectedPointNr);
01341             if (! (m_selectedPoint == m_points[selectedPointNr]) ) {
01342                 CPEvent e( this, CPEvent::POINT_CHANGED, selectedPointNr, m_points[selectedPointNr].GetControlPoint());
01343                 emit(e);
01344             }
01345             break;
01346         }
01347         case NEW_POINT_SELECTED:
01348         {
01349             DEBUG_DEBUG("new Point changed (event fire): x:" << mpos.x << " y:" << mpos.y);
01350             // fire the wxWin event
01351             CPEvent e( this, newPoint);
01352             emit(e);
01353             break;
01354         }
01355         case NEW_LINE_CREATING:
01356         {
01357             //notify parent
01358             CPEvent e(this, CPEvent::NEW_LINE_ADDED, m_selectedPoint.GetControlPoint());
01359             emit(e);
01360             break;
01361         }
01362         case NO_IMAGE:
01363             break;
01364 
01365         }
01366 //        DEBUG_DEBUG("ImageDisplay: mouse release, state change: " << oldState
01367 //                    << " -> " << editState);
01368     }
01369 
01370 }
01371 
01372 
01373 void CPImageCtrl::mouseReleaseMMBEvent(wxMouseEvent& mouse)
01374 {
01375     DEBUG_DEBUG("middle mouse button released, leaving scroll mode")
01376 //    SetCursor(wxCursor(wxCURSOR_BULLSEYE));
01377 }
01378 
01379 
01380 void CPImageCtrl::mousePressMMBEvent(wxMouseEvent& mouse)
01381 {
01382     DEBUG_DEBUG("middle mouse button pressed, entering scroll mode")
01383     if (!m_img.get()) return; // ignore events if no image loaded.
01384     m_mouseScrollPos = mouse.GetPosition();
01385 //    SetCursor(wxCursor(wxCURSOR_HAND));
01386 }
01387 
01388 void CPImageCtrl::mousePressRMBEvent(wxMouseEvent& mouse)
01389 {
01390     //ignore event if no image loaded
01391     if(!m_img.get()) 
01392         return;
01393     wxPoint mpos_;
01394     CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y, &mpos_.x, & mpos_.y);
01395     FDiff2D mpos(mpos_.x, mpos_.y);
01396     mpos = applyRotInv(invScale(mpos));
01397     // if mouseclick is out of image, ignore
01398     if (mpos.x >= m_realSize.GetWidth() || mpos.y >= m_realSize.GetHeight())
01399     {
01400         return;
01401     }
01402     if(mouse.CmdDown() && (editState==NO_SELECTION || editState==KNOWN_POINT_SELECTED || editState==NEW_POINT_SELECTED))
01403     {
01404         rectStartPos=mpos;
01405         editState=SELECT_DELETE_REGION;
01406         DrawSelectionRectangle(mpos,mpos);
01407     };
01408 };
01409 
01410 void CPImageCtrl::mouseReleaseRMBEvent(wxMouseEvent& mouse)
01411 {
01412     if (!m_img.get()) return; // ignore events if no image loaded.
01413     wxPoint mpos_;
01414     CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y,
01415                            &mpos_.x, & mpos_.y);
01416     FDiff2D mpos(mpos_.x, mpos_.y);
01417     mpos = applyRotInv(invScale(mpos));
01418     DEBUG_DEBUG("mouseReleaseEvent, pos:" << mpos.x
01419                 << ", " << mpos.y);
01420 
01421     if (mouse.RightUp())
01422     {
01423         if(editState==SELECT_DELETE_REGION)
01424         {
01425             DrawSelectionRectangle(rectStartPos,mpos);
01426             editState=NO_SELECTION;
01427             CPEvent e(this,rectStartPos,mpos);
01428             emit(e);
01429         }
01430         else
01431         {
01432             // if mouseclick is out of image, ignore
01433             if (mpos.x >= m_realSize.GetWidth() || mpos.y >= m_realSize.GetHeight()) {
01434                 return;
01435             }
01436             // set right up event
01437             DEBUG_DEBUG("Emitting right click (rmb release)");
01438             CPEvent e(this, CPEvent::RIGHT_CLICK, mpos);
01439             emit(e);
01440         }
01441     }
01442 }
01443 
01444 void CPImageCtrl::update()
01445 {
01446     DEBUG_TRACE("edit state:" << editState);
01447     wxClientDC dc(this);
01448     PrepareDC(dc);
01449     OnDraw(dc);
01450 }
01451 
01452 bool CPImageCtrl::emit(CPEvent & ev)
01453 {
01454     if ( ProcessEvent( ev ) == FALSE ) {
01455         wxLogWarning( _("Could not process event!") );
01456         return false;
01457     } else {
01458         return true;
01459     }
01460 }
01461 
01462 void CPImageCtrl::setScale(double factor)
01463 {
01464     if (factor == 0) {
01465         fitToWindow = true;
01466         factor = calcAutoScaleFactor(imageSize);
01467     } else {
01468         fitToWindow = false;
01469     }
01470     DEBUG_DEBUG("new scale factor:" << factor);
01471     // update if factor changed
01472     if (factor != scaleFactor) {
01473         scaleFactor = factor;
01474         // keep existing scale focussed.
01475         rescaleImage();
01476     }
01477 }
01478 
01479 double CPImageCtrl::calcAutoScaleFactor(wxSize size)
01480 {
01481     // TODO correctly autoscale rotated iamges
01482     int w = size.GetWidth();
01483     int h = size.GetHeight();
01484     if (m_imgRotation ==  ROT90 || m_imgRotation == ROT270) {
01485         int t = w;
01486         w = h;
01487         h = t;
01488     }
01489 
01490 //    wxSize csize = GetClientSize();
01491     wxSize csize = GetSize();
01492     DEBUG_DEBUG("csize: " << csize.GetWidth() << "x" << csize.GetHeight() << "image: " << w << "x" << h);
01493     double s1 = (double)csize.GetWidth()/w;
01494     double s2 = (double)csize.GetHeight()/h;
01495     DEBUG_DEBUG("s1: " << s1 << "  s2:" << s2);
01496     return s1 < s2 ? s1 : s2;
01497 }
01498 
01499 double CPImageCtrl::getScaleFactor() const
01500 {
01501     return scaleFactor;
01502 }
01503 
01504 void CPImageCtrl::OnSize(wxSizeEvent &e)
01505 {
01506     DEBUG_TRACE("size: " << e.GetSize().GetWidth() << "x" << e.GetSize().GetHeight());
01507     // rescale bitmap if needed.
01508     if (imageFilename != "") {
01509         if (fitToWindow) {
01510             setScale(0);
01511         }
01512     }
01513 }
01514 
01515 void CPImageCtrl::OnKey(wxKeyEvent & e)
01516 {
01517     if (!m_img.get()) return; // ignore events if no image loaded.
01518     DEBUG_TRACE(" OnKey, key:" << e.m_keyCode);
01519     wxPoint delta(0,0);
01520     // check for cursor keys, if control is not pressed
01521     if ((!e.CmdDown()) && e.GetKeyCode() == WXK_LEFT ) delta.x = -1;
01522     if ((!e.CmdDown()) && e.GetKeyCode() == WXK_RIGHT ) delta.x = 1;
01523     if ((!e.CmdDown()) && e.GetKeyCode() == WXK_UP ) delta.y = -1;
01524     if ((!e.CmdDown()) && e.GetKeyCode() == WXK_DOWN ) delta.y = 1;
01525     if ( (delta.x != 0 || delta.y != 0 ) && (e.ShiftDown() || e.CmdDown())) {
01526         // move to the left
01527         double speed = (double) GetClientSize().GetWidth()/10;
01528         delta.x = (int) (delta.x * speed);
01529         delta.y = (int) (delta.y * speed);
01530         if (e.ShiftDown()) {
01531             // emit scroll event, so that other window can be scrolled
01532             // as well.
01533             CPEvent e(this, CPEvent::SCROLLED, FDiff2D(delta.x, delta.y));
01534             emit(e);
01535         } else if (e.CmdDown()) {
01536             ScrollDelta(delta);
01537         }
01538     } else if (delta.x != 0 || delta.y != 0 ) {
01539 
01540         FDiff2D shift(delta.x/3.0, delta.y/3.0);
01541         // rotate shift according to current display
01542         double t;
01543         switch (m_imgRotation) {
01544             case ROT90:
01545                 t = shift.x;
01546                 shift.x = shift.y;
01547                 shift.y = -t;
01548                 break;
01549             case ROT180:
01550                 shift.x = -shift.x;
01551                 shift.y = -shift.y;
01552                 break;
01553             case ROT270:
01554                 t = shift.x;
01555                 shift.x = -shift.y;
01556                 shift.y = t;
01557             default:
01558                 break;
01559         }
01560         // move control point by half a pixel, if a point is selected
01561         if (editState == KNOWN_POINT_SELECTED ) {
01562             DisplayedControlPoint updatedCp=m_points[selectedPointNr];
01563             updatedCp.ShiftControlPoint(shift);
01564             CPEvent e( this, CPEvent::POINT_CHANGED, selectedPointNr, updatedCp.GetControlPoint());
01565             emit(e);
01566             m_forceMagnifier = true;
01567             m_timer.Stop();
01568             m_timer.Start(2000, true);
01569         } else if (editState == NEW_POINT_SELECTED) {
01570             newPoint = newPoint + shift;
01571             // update display.
01572             update();
01573         }
01574 
01575     } else if (e.m_keyCode == 'a') {
01576         DEBUG_DEBUG("adding point with a key, faking right click");
01577         // faking right mouse button with "a"
01578         // set right up event
01579         CPEvent ev(this, CPEvent::RIGHT_CLICK, FDiff2D(0,0));
01580         emit(ev);
01581     } else {
01582         // forward some keys...
01583         bool forward = false;
01584         switch (e.GetKeyCode())
01585         {
01586             case 'g':
01587             case '0':
01588             case '1':
01589             case '2':
01590             case 'f':
01591             case WXK_RIGHT:
01592             case WXK_LEFT:
01593             case WXK_UP:
01594             case WXK_DOWN:
01595             case WXK_DELETE:
01596                 forward = true;
01597                 break;
01598             default:
01599                 break;
01600         }
01601 
01602         if (forward) {
01603             // dangelo: I don't understand why some keys are forwarded and others are not..
01604             // Delete is forwared under wxGTK, and g not..
01605             // wxWidgets 2.6.1 using gtk 2 doesn't set the event object
01606             // properly.. do it here by hand
01607             e.SetEventObject(this);
01608             DEBUG_DEBUG("forwarding key " << e.GetKeyCode()
01609                         << " origin: id:" << e.GetId() << " obj: "
01610                         << e.GetEventObject());
01611             // forward all keys to our parent
01612             //GetParent()->GetEventHandler()->ProcessEvent(e);
01613             m_editPanel->GetEventHandler()->ProcessEvent(e);
01614         } else {
01615             e.Skip();
01616         }
01617     }
01618 }
01619 
01620 void CPImageCtrl::OnKeyDown(wxKeyEvent & e)
01621 {
01622     DEBUG_TRACE("key:" << e.m_keyCode);
01623     if (!m_img.get()) return; // ignore events if no image loaded.
01624     if (e.m_keyCode == WXK_SHIFT || e.m_keyCode == WXK_CONTROL) {
01625         DEBUG_DEBUG("shift or control down, reseting scoll position");
01626         m_mouseScrollPos = e.GetPosition();
01627     }
01628     e.Skip();
01629 }
01630 
01631 void CPImageCtrl::OnMouseLeave(wxMouseEvent & e)
01632 {
01633     DEBUG_TRACE("MOUSE LEAVE");
01634     m_mousePos = FDiff2D(-1,-1);
01635     m_mouseInWindow = false;
01636     update();
01637 }
01638 
01639 void CPImageCtrl::OnMouseEnter(wxMouseEvent & e)
01640 {
01641     DEBUG_TRACE("MOUSE Enter, setting focus");
01642     m_mouseInWindow = true;
01643     SetFocus();
01644     update();
01645 }
01646 
01647 FDiff2D CPImageCtrl::getNewPoint()
01648 {
01649     // only possible if a new point is actually selected
01650     // DEBUG_ASSERT(editState == NEW_POINT_SELECTED);
01651     return newPoint;
01652 }
01653 
01654 void CPImageCtrl::setNewPoint(const FDiff2D & p)
01655 {
01656     DEBUG_DEBUG("setting new point " << p.x << "," << p.y);
01657     // should we need to check for some precondition?
01658     newPoint = p;
01659     editState = NEW_POINT_SELECTED;
01660 
01661     // show new point.
01662     showPosition(p);
01663 
01664     // we do not send an event, since CPEditorPanel
01665     // caused the change.. so it doesn't need to filter
01666     // out its own change messages.
01667 }
01668 
01669 void CPImageCtrl::showSearchArea(bool show)
01670 {
01671     m_showSearchArea = show;
01672     if (show)
01673     {
01674         int templSearchAreaPercent = wxConfigBase::Get()->Read(wxT("/Finetune/SearchAreaPercent"), HUGIN_FT_SEARCH_AREA_PERCENT);
01675         m_searchRectWidth = (m_realSize.GetWidth() * templSearchAreaPercent) / 200;
01676         DEBUG_DEBUG("Setting new search area: w in %:" << templSearchAreaPercent << " bitmap width: " << bitmap.GetWidth() << "  resulting size: " << m_searchRectWidth);
01677         m_mousePos = FDiff2D(-1,-1);
01678     }
01679 }
01680 
01681 void CPImageCtrl::showTemplateArea(bool show)
01682 {
01683     m_showTemplateArea = show;
01684     if (show)
01685     {
01686         m_templateRectWidth = wxConfigBase::Get()->Read(wxT("/Finetune/TemplateSize"),HUGIN_FT_TEMPLATE_SIZE) / 2;
01687     }
01688 }
01689 
01690 wxPoint CPImageCtrl::MaxScrollDelta(wxPoint delta)
01691 {
01692     int x,y;
01693     GetViewStart( &x, &y );
01694 
01695     wxSize winSize = GetClientSize();
01696     wxSize imgSize;
01697     imgSize.x = bitmap.GetWidth();
01698     imgSize.y = bitmap.GetHeight();
01699     // check for top and left border
01700     if (x + delta.x < 0) {
01701         delta.x = -x;
01702     }
01703     if (y + delta.y < 0) {
01704         delta.y = -y;
01705     }
01706     // check for right and bottom border
01707     int right = x + delta.x + winSize.x ;
01708     if (right > imgSize.x) {
01709         delta.x = imgSize.x - right;
01710         if (delta.x < 0) {
01711             delta.x = 0;
01712         }
01713     }
01714     int bottom = y + delta.y + winSize.y ;
01715     if (bottom > imgSize.y) {
01716         delta.y = imgSize.y - bottom;
01717         if (delta.y < 0) {
01718             delta.y = 0;
01719         }
01720     }
01721     return delta;
01722 }
01723 
01724 void CPImageCtrl::ScrollDelta(const wxPoint & delta)
01725 {
01726     // TODO: adjust
01727     if (delta.x == 0 && delta.y == 0) {
01728         return;
01729     }
01730     int x,y;
01731     GetViewStart( &x, &y );
01732     x = x + delta.x;
01733     y = y + delta.y;
01734     if (x<0) x = 0;
01735     if (y<0) y = 0;
01736     Scroll( x, y);
01737 }
01738 
01739 const wxSize CPImageCtrl::GetBitmapSize() const
01740 {
01741 #if wxCHECK_VERSION(2,9,0)
01742     return bitmap.GetSize();
01743 #else
01744     return wxSize(bitmap.GetWidth(), bitmap.GetHeight());
01745 #endif
01746 };
01747 
01748 IMPLEMENT_DYNAMIC_CLASS(CPImageCtrl, wxScrolledWindow)
01749 
01750 CPImageCtrlXmlHandler::CPImageCtrlXmlHandler()
01751                 : wxXmlResourceHandler()
01752 {
01753     AddWindowStyles();
01754 }
01755 
01756 wxObject *CPImageCtrlXmlHandler::DoCreateResource()
01757 {
01758     XRC_MAKE_INSTANCE(cp, CPImageCtrl)
01759 
01760     cp->Create(m_parentAsWindow,
01761                    GetID(),
01762                    GetPosition(), GetSize(),
01763                    GetStyle(wxT("style")),
01764                    GetName());
01765 
01766     SetupWindow( cp);
01767 
01768     return cp;
01769 }
01770 
01771 bool CPImageCtrlXmlHandler::CanHandle(wxXmlNode *node)
01772 {
01773     return IsOfClass(node, wxT("CPImageCtrl"));
01774 }
01775 
01776 IMPLEMENT_DYNAMIC_CLASS(CPImageCtrlXmlHandler, wxXmlResourceHandler)

Generated on 22 Nov 2014 for Hugintrunk by  doxygen 1.4.7