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

Generated on 5 Feb 2016 for Hugintrunk by  doxygen 1.4.7