CPListFrame.cpp

Go to the documentation of this file.
00001 // -*- c-basic-offset: 4 -*-
00002 
00027 #include <config.h>
00028 #include "panoinc_WX.h"
00029 #include "panoinc.h"
00030 
00031 #include <algorithm>
00032 #include <utility>
00033 #include <functional>
00034 
00035 #include "base_wx/wxPlatform.h"
00036 #include "hugin/CPListFrame.h"
00037 #include "hugin/MainFrame.h"
00038 #include "base_wx/CommandHistory.h"
00039 #include "base_wx/PanoCommand.h"
00040 #include "hugin/huginApp.h"
00041 #include "hugin_base/panotools/PanoToolsUtils.h"
00042 #include "algorithms/basic/CalculateCPStatistics.h"
00043 
00044 using namespace HuginBase;
00045 using namespace std;
00046 using namespace hugin_utils;
00047 
00048 BEGIN_EVENT_TABLE(CPListCtrl, wxListCtrl)
00049     EVT_CHAR(CPListCtrl::OnChar)
00050     EVT_LIST_ITEM_SELECTED(wxID_ANY, CPListCtrl::OnCPListSelectionChanged)
00051     EVT_LIST_ITEM_DESELECTED(wxID_ANY, CPListCtrl::OnCPListSelectionChanged)
00052     EVT_LIST_COL_CLICK(wxID_ANY, CPListCtrl::OnCPListHeaderClick)
00053     EVT_LIST_COL_END_DRAG(wxID_ANY, CPListCtrl::OnColumnWidthChange)
00054 END_EVENT_TABLE()
00055 
00056 std::string makePairId(unsigned int id1, unsigned int id2)
00057 {
00058     // Control points from same image pair, regardless of which is left or right
00059     // are counted the same so return the identical hash id.
00060     std::ostringstream oss;
00061 
00062     if (id1 < id2) {
00063         oss << id1 << "_" << id2;
00064     }
00065     else if (id2 < id1)  {
00066         oss << id2 << "_" << id1;
00067     }
00068     else {
00069         // Control points are from same image.
00070         oss << id1;
00071     }
00072     return oss.str();
00073 }
00074 
00075 CPListCtrl::CPListCtrl() : m_pano(NULL)
00076 {
00077     m_sortCol = 0;
00078     m_sortAscend = true;
00079 };
00080 
00081 CPListCtrl::~CPListCtrl()
00082 {
00083     if (m_pano)
00084     {
00085         m_pano->removeObserver(this);
00086     };
00087 };
00088 
00089 bool CPListCtrl::Create(wxWindow *parent, wxWindowID id, const wxPoint& pos,
00090     const wxSize& size, long style, const wxValidator& validator, const wxString& name)
00091 {
00092     if (!wxListCtrl::Create(parent, id, pos, size, style))
00093     {
00094         return false;
00095     };
00096     InsertColumn(0, _("G CP#"), wxLIST_FORMAT_RIGHT, 25);
00097     InsertColumn(1, _("Left Img."), wxLIST_FORMAT_RIGHT, 65);
00098     InsertColumn(2, _("Right Img."), wxLIST_FORMAT_RIGHT, 65);
00099     InsertColumn(3, _("P CP#"), wxLIST_FORMAT_RIGHT, 25);
00100     InsertColumn(4, _("Alignment"), wxLIST_FORMAT_LEFT, 80);
00101     InsertColumn(5, _("Distance"), wxLIST_FORMAT_RIGHT, 80);
00102 
00103     //get saved width
00104     for (int j = 0; j < GetColumnCount(); j++)
00105     {
00106         // -1 is auto
00107         int width = wxConfigBase::Get()->Read(wxString::Format(wxT("/CPListFrame/ColumnWidth%d"), j), -1);
00108         if (width != -1)
00109         {
00110             SetColumnWidth(j, width);
00111         };
00112     };
00113 #if wxCHECK_VERSION(3,0,0)
00114     EnableAlternateRowColours(true);
00115 #endif
00116     return true;
00117 };
00118 
00119 void CPListCtrl::Init(Panorama* pano)
00120 {
00121     m_pano = pano;
00122     m_pano->addObserver(this);
00123     panoramaChanged(*pano);
00124 };
00125 
00126 wxString CPListCtrl::OnGetItemText(long item, long column) const
00127 {
00128     if (item > m_internalCPList.size())
00129     {
00130         return wxEmptyString;
00131     };
00132     const HuginBase::ControlPoint& cp = m_pano->getCtrlPoint(m_internalCPList[item].globalIndex);
00133     switch (column)
00134     {
00135         case 0:
00136             return wxString::Format(wxT("%lu"), static_cast<unsigned long>(m_internalCPList[item].globalIndex));
00137             break;
00138         case 1:
00139             return wxString::Format(wxT("%u"), cp.image1Nr);
00140             break;
00141         case 2:
00142             return wxString::Format(wxT("%u"), cp.image2Nr);
00143             break;
00144         case 3:
00145             return wxString::Format(wxT("%lu"), static_cast<unsigned long>(m_internalCPList[item].localNumber));
00146             break;
00147         case 4:
00148             switch (cp.mode)
00149             {
00150                 case HuginBase::ControlPoint::X_Y:
00151                     return wxString(_("normal"));
00152                     break;
00153                 case HuginBase::ControlPoint::X:
00154                     return wxString(_("vert. Line"));
00155                     break;
00156                 case HuginBase::ControlPoint::Y:
00157                     return wxString(_("horiz. Line"));
00158                     break;
00159                 default:
00160                     return wxString::Format(_("Line %d"), cp.mode);
00161                     break;
00162             };
00163             break;
00164         case 5:
00165             return wxString::Format(wxT("%.2f"), cp.error);
00166             break;
00167         default:
00168             return wxEmptyString;
00169     };
00170     return wxEmptyString;
00171 };
00172 
00173 void CPListCtrl::panoramaChanged(HuginBase::Panorama &pano)
00174 {
00175     UpdateInternalCPList();
00176     SetItemCount(m_pano->getNrOfCtrlPoints());
00177     Refresh();
00178 };
00179 
00180 void CPListCtrl::UpdateInternalCPList()
00181 {
00182     const HuginBase::CPVector& cps = m_pano->getCtrlPoints();
00183     // Rebuild the global->local CP map on each update as CPs might have been
00184     // removed.
00185     m_localIds.clear();
00186 
00187     if (m_internalCPList.size() != cps.size())
00188     {
00189         m_internalCPList.resize(cps.size());
00190     };
00191     for (size_t i = 0; i < cps.size(); i++)
00192     {
00193         m_internalCPList[i].globalIndex = i;
00194         const HuginBase::ControlPoint& cp = cps[i];
00195         string pairId = makePairId(cp.image1Nr, cp.image2Nr);
00196         std::map<std::string, int>::iterator it = m_localIds.find(pairId);
00197         if (it != m_localIds.end())
00198         {
00199             ++(it->second);
00200         }
00201         else
00202         {
00203             m_localIds[pairId] = 0;
00204         }
00205         m_internalCPList[i].localNumber=m_localIds[pairId];
00206     };
00207     SortInternalList(true);
00208 };
00209 
00210 // sort helper function
00211 #define CompareStruct(VAR) \
00212 struct Compare##VAR\
00213 {\
00214     bool operator()(const CPListItem& item1, const CPListItem& item2)\
00215     {\
00216         return item1.VAR < item2.VAR;\
00217     };\
00218 };
00219 CompareStruct(globalIndex)
00220 CompareStruct(localNumber)
00221 #undef CompareStruct
00222 
00223 #define CompareStruct(VAR) \
00224 struct Compare##VAR##Greater\
00225 {\
00226     bool operator()(const CPListItem& item1, const CPListItem& item2)\
00227     {\
00228         return item1.VAR > item2.VAR;\
00229     };\
00230 };
00231 CompareStruct(globalIndex)
00232 CompareStruct(localNumber)
00233 #undef CompareStruct
00234 
00235 #define CompareStruct(VAR)\
00236 struct Compare##VAR\
00237 {\
00238     explicit Compare##VAR(const HuginBase::CPVector& cps) : m_cps(cps) {};\
00239     bool operator()(const CPListItem& item1, const CPListItem& item2)\
00240     {\
00241          return m_cps[item1.globalIndex].VAR < m_cps[item2.globalIndex].VAR; \
00242     }\
00243 private:\
00244     const HuginBase::CPVector& m_cps;\
00245 };
00246 CompareStruct(image1Nr)
00247 CompareStruct(image2Nr)
00248 CompareStruct(mode)
00249 CompareStruct(error)
00250 #undef CompareStruct
00251 
00252 #define CompareStruct(VAR)\
00253 struct Compare##VAR##Greater\
00254 {\
00255     explicit Compare##VAR##Greater(const HuginBase::CPVector& cps) : m_cps(cps) {};\
00256     bool operator()(const CPListItem& item1, const CPListItem& item2)\
00257     {\
00258          return m_cps[item1.globalIndex].VAR > m_cps[item2.globalIndex].VAR; \
00259     }\
00260 private:\
00261     const HuginBase::CPVector& m_cps;\
00262 };
00263 CompareStruct(image1Nr)
00264 CompareStruct(image2Nr)
00265 CompareStruct(mode)
00266 CompareStruct(error)
00267 #undef CompareStruct
00268 
00269 void CPListCtrl::SortInternalList(bool isAscending)
00270 {
00271     // nothing to sort
00272     if (m_internalCPList.empty())
00273     {
00274         return;
00275     };
00276 
00277     switch (m_sortCol)
00278     {
00279         case 0:
00280             if (m_sortAscend)
00281             {
00282                 if (!isAscending)
00283                 {
00284                     std::sort(m_internalCPList.begin(), m_internalCPList.end(), CompareglobalIndex());
00285                 };
00286             }
00287             else
00288             {
00289                 std::sort(m_internalCPList.begin(), m_internalCPList.end(), CompareglobalIndexGreater());
00290             };
00291             break;
00292         case 1:
00293             if (m_sortAscend)
00294             {
00295                 std::sort(m_internalCPList.begin(), m_internalCPList.end(), Compareimage1Nr(m_pano->getCtrlPoints()));
00296             }
00297             else
00298             {
00299                 std::sort(m_internalCPList.begin(), m_internalCPList.end(), Compareimage1NrGreater(m_pano->getCtrlPoints()));
00300             };
00301             break;
00302         case 2:
00303             if (m_sortAscend)
00304             {
00305                 std::sort(m_internalCPList.begin(), m_internalCPList.end(), Compareimage2Nr(m_pano->getCtrlPoints()));
00306             }
00307             else
00308             {
00309                 std::sort(m_internalCPList.begin(), m_internalCPList.end(), Compareimage2NrGreater(m_pano->getCtrlPoints()));
00310             };
00311             break;
00312         case 3:
00313             if (m_sortAscend)
00314             {
00315                 std::sort(m_internalCPList.begin(), m_internalCPList.end(), ComparelocalNumber());
00316             }
00317             else
00318             {
00319                 std::sort(m_internalCPList.begin(), m_internalCPList.end(), ComparelocalNumberGreater());
00320             };
00321             break;
00322         case 4:
00323             if (m_sortAscend)
00324             {
00325                 std::sort(m_internalCPList.begin(), m_internalCPList.end(), Comparemode(m_pano->getCtrlPoints()));
00326             }
00327             else
00328             {
00329                 std::sort(m_internalCPList.begin(), m_internalCPList.end(), ComparemodeGreater(m_pano->getCtrlPoints()));
00330             };
00331             break;
00332         case 5:
00333             if (m_sortAscend)
00334             {
00335                 std::sort(m_internalCPList.begin(), m_internalCPList.end(), Compareerror(m_pano->getCtrlPoints()));
00336             }
00337             else
00338             {
00339                 std::sort(m_internalCPList.begin(), m_internalCPList.end(), CompareerrorGreater(m_pano->getCtrlPoints()));
00340             };
00341             break;
00342     };
00343 };
00344 
00345 void CPListCtrl::OnCPListSelectionChanged(wxListEvent & e)
00346 {
00347     if (GetSelectedItemCount() == 1)
00348     {
00349         if (e.GetIndex() < m_internalCPList.size())
00350         {
00351             MainFrame::Get()->ShowCtrlPoint(m_internalCPList[e.GetIndex()].globalIndex);
00352         };
00353     };
00354 };
00355 
00356 void CPListCtrl::OnCPListHeaderClick(wxListEvent& e)
00357 {
00358     const int newCol = e.GetColumn();
00359     if (m_sortCol == newCol)
00360     {
00361         m_sortAscend = !m_sortAscend;
00362     }
00363     else
00364     {
00365         m_sortCol = newCol;
00366         m_sortAscend = true;
00367     }
00368     SortInternalList(false);
00369     Refresh();
00370 };
00371 
00372 void CPListCtrl::OnColumnWidthChange(wxListEvent& e)
00373 {
00374     const int colNum = e.GetColumn();
00375     wxConfigBase::Get()->Write(wxString::Format(wxT("/CPListFrame/ColumnWidth%d"), colNum), GetColumnWidth(colNum));
00376 };
00377 
00378 void CPListCtrl::DeleteSelected()
00379 {
00380     // no selected item.
00381     const int nSelected = GetSelectedItemCount();
00382     if (nSelected == 0)
00383     {
00384         wxBell();
00385         return;
00386     };
00387 
00388     UIntSet selected;
00389     long item = GetFirstSelected();
00390     long newSelection = -1;
00391     if (m_internalCPList.size() - nSelected > 0)
00392     {
00393         newSelection = item;
00394         if (item >= m_internalCPList.size() - nSelected)
00395         {
00396             newSelection = m_internalCPList.size() - nSelected - 1;
00397         };
00398     };
00399     while (item>=0)
00400     {
00401         // deselect item
00402         Select(item, false);
00403         selected.insert(m_internalCPList[item].globalIndex);
00404         item = GetNextSelected(item);
00405     }
00406     DEBUG_DEBUG("about to delete " << selected.size() << " points");
00407     PanoCommand::GlobalCmdHist::getInstance().addCommand(new PanoCommand::RemoveCtrlPointsCmd(*m_pano, selected));
00408 
00409     if (newSelection >= 0)
00410     {
00411         MainFrame::Get()->ShowCtrlPoint(m_internalCPList[newSelection].globalIndex);
00412         Select(newSelection, true);
00413     };
00414 };
00415 
00416 void CPListCtrl::SelectDistanceThreshold(double threshold)
00417 {
00418     const bool invert = threshold < 0;
00419     if (invert)
00420     {
00421         threshold = -threshold;
00422     };
00423     const HuginBase::CPVector& cps = m_pano->getCtrlPoints();
00424     Freeze();
00425     for (size_t i = 0; i < m_internalCPList.size(); i++)
00426     {
00427         const double error = cps[m_internalCPList[i].globalIndex].error;
00428         Select(i, ((error > threshold) && (!invert)) || ((error < threshold) && (invert)));
00429     };
00430     Thaw();
00431 };
00432 
00433 void CPListCtrl::SelectAll()
00434 {
00435     for (long i = 0; i < m_internalCPList.size(); i++)
00436     {
00437         Select(i, true);
00438     };
00439 };
00440 
00441 #if !wxCHECK_VERSION(3,0,0)
00442 #define WXK_CONTROL_A 1
00443 #endif
00444 
00445 void CPListCtrl::OnChar(wxKeyEvent& e)
00446 {
00447     switch (e.GetKeyCode())
00448     {
00449         case WXK_DELETE:
00450         case WXK_NUMPAD_DELETE:
00451             DeleteSelected();
00452             break;
00453         case WXK_CONTROL_A:
00454             SelectAll();
00455             break;
00456         default:
00457             e.Skip();
00458     };
00459 };
00460 
00461 
00462 IMPLEMENT_DYNAMIC_CLASS(CPListCtrl, wxListCtrl)
00463 
00464 IMPLEMENT_DYNAMIC_CLASS(CPListCtrlXmlHandler, wxListCtrlXmlHandler)
00465 
00466 CPListCtrlXmlHandler::CPListCtrlXmlHandler()
00467 : wxListCtrlXmlHandler()
00468 {
00469     AddWindowStyles();
00470 }
00471 
00472 wxObject *CPListCtrlXmlHandler::DoCreateResource()
00473 {
00474     XRC_MAKE_INSTANCE(cp, CPListCtrl)
00475     cp->Create(m_parentAsWindow, GetID(), GetPosition(), GetSize(), GetStyle(wxT("style")), wxDefaultValidator, GetName());
00476     SetupWindow(cp);
00477     return cp;
00478 }
00479 
00480 bool CPListCtrlXmlHandler::CanHandle(wxXmlNode *node)
00481 {
00482     return IsOfClass(node, wxT("CPListCtrl"));
00483 }
00484 
00485 
00486 BEGIN_EVENT_TABLE(CPListFrame, wxFrame)
00487     EVT_CLOSE(CPListFrame::OnClose)
00488     EVT_BUTTON(XRCID("cp_list_delete"), CPListFrame::OnDeleteButton)
00489     EVT_BUTTON(XRCID("cp_list_select"), CPListFrame::OnSelectButton)
00490 END_EVENT_TABLE()
00491 
00492 CPListFrame::CPListFrame(wxFrame* parent, Panorama& pano) : m_pano(pano)
00493 {
00494     DEBUG_TRACE("");
00495     bool ok = wxXmlResource::Get()->LoadFrame(this, parent, wxT("cp_list_frame"));
00496     DEBUG_ASSERT(ok);
00497     m_list = XRCCTRL(*this, "cp_list_frame_list", CPListCtrl);
00498     DEBUG_ASSERT(m_list);
00499     m_list->Init(&m_pano);
00500 
00501 #ifdef __WXMSW__
00502     // wxFrame does have a strange background color on Windows, copy color from a child widget
00503     this->SetBackgroundColour(XRCCTRL(*this, "cp_list_select", wxButton)->GetBackgroundColour());
00504 #endif
00505 #ifdef __WXMSW__
00506     wxIcon myIcon(huginApp::Get()->GetXRCPath() + wxT("data/hugin.ico"),wxBITMAP_TYPE_ICO);
00507 #else
00508     wxIcon myIcon(huginApp::Get()->GetXRCPath() + wxT("data/hugin.png"),wxBITMAP_TYPE_PNG);
00509 #endif
00510     SetIcon(myIcon);
00511 
00512     //set minumum size
00513     SetSizeHints(200, 300);
00514     //size
00515     RestoreFramePosition(this, wxT("CPListFrame"));
00516 }
00517 
00518 CPListFrame::~CPListFrame()
00519 {
00520     DEBUG_TRACE("dtor");
00521     StoreFramePosition(this, wxT("CPListFrame"));
00522     DEBUG_TRACE("dtor end");
00523 }
00524 
00525 void CPListFrame::OnClose(wxCloseEvent& event)
00526 {
00527     DEBUG_DEBUG("OnClose");
00528     MainFrame::Get()->OnCPListFrameClosed();
00529     DEBUG_DEBUG("closing");
00530     Destroy();
00531 }
00532 
00533 void CPListFrame::OnDeleteButton(wxCommandEvent & e)
00534 {
00535     m_list->DeleteSelected();
00536 }
00537 
00538 void CPListFrame::OnSelectButton(wxCommandEvent & e)
00539 {
00540     // calculate the mean error and the standard deviation
00541     HuginBase::PTools::calcCtrlPointErrors(m_pano);
00542     double min, max, mean, var;
00543     HuginBase::CalculateCPStatisticsError::calcCtrlPntsErrorStats(m_pano, min, max, mean, var);
00544 
00545     // select points whos distance is greater than the mean
00546     // hmm, maybe some theory would be nice.. this is just a
00547     // guess.
00548     double threshold = mean + sqrt(var);
00549     wxString t;
00550     do
00551     {
00552         t = wxGetTextFromUser(_("Enter minimum control point error.\nAll points with a higher error will be selected"), _("Select Control Points"),
00553             doubleTowxString(threshold, 2));
00554         if (t == wxEmptyString) {
00555             // do not select anything
00556             return;
00557         }
00558     }
00559     while (!str2double(t, threshold));
00560 
00561     m_list->SelectDistanceThreshold(threshold);
00562 };

Generated on 27 Jul 2015 for Hugintrunk by  doxygen 1.4.7