Hugintrunk  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MaskEditorPanel.cpp
Go to the documentation of this file.
1 // -*- c-basic-offset: 4 -*-
2 
11 /* This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public
13  * License as published by the Free Software Foundation; either
14  * version 2 of the License, or (at your option) any later version.
15  *
16  * This software is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public
22  * License along with this software. If not, see
23  * <http://www.gnu.org/licenses/>.
24  *
25  */
26 
27 #include "panoinc_WX.h"
28 #include "panoinc.h"
29 #include <hugin_utils/stl_utils.h>
30 
31 // hugin's
32 #include "base_wx/platform.h"
33 #include "hugin/MainFrame.h"
34 #include "hugin/config_defaults.h"
35 #include "base_wx/CommandHistory.h"
36 #include "base_wx/PanoCommand.h"
37 #include "hugin/MaskEditorPanel.h"
38 #include "hugin/MaskLoadDialog.h"
39 #include <wx/clipbrd.h>
41 
42 BEGIN_EVENT_TABLE(MaskEditorPanel, wxPanel)
43  EVT_LIST_ITEM_SELECTED(XRCID("mask_editor_images_list"), MaskEditorPanel::OnImageSelect)
44  EVT_LIST_ITEM_DESELECTED(XRCID("mask_editor_images_list"), MaskEditorPanel::OnImageSelect)
45  EVT_LIST_ITEM_SELECTED(XRCID("mask_editor_mask_list"), MaskEditorPanel::OnMaskSelect)
46  EVT_LIST_ITEM_DESELECTED(XRCID("mask_editor_mask_list"), MaskEditorPanel::OnMaskSelect)
47  EVT_LIST_COL_END_DRAG(XRCID("mask_editor_mask_list"), MaskEditorPanel::OnColumnWidthChange)
48  EVT_CHOICE(XRCID("mask_editor_choice_zoom"), MaskEditorPanel::OnZoom)
49  EVT_CHOICE(XRCID("mask_editor_choice_masktype"), MaskEditorPanel::OnMaskTypeChange)
50  EVT_BUTTON(XRCID("mask_editor_add"), MaskEditorPanel::OnMaskAdd)
51  EVT_BUTTON(XRCID("mask_editor_load"), MaskEditorPanel::OnMaskLoad)
52  EVT_BUTTON(XRCID("mask_editor_save"), MaskEditorPanel::OnMaskSave)
53  EVT_BUTTON(XRCID("mask_editor_copy"), MaskEditorPanel::OnMaskCopy)
54  EVT_BUTTON(XRCID("mask_editor_paste"), MaskEditorPanel::OnMaskPaste)
55  EVT_BUTTON(XRCID("mask_editor_delete"), MaskEditorPanel::OnMaskDelete)
56  EVT_CHECKBOX(XRCID("mask_editor_show_active_masks"), MaskEditorPanel::OnShowActiveMasks)
57  EVT_COLOURPICKER_CHANGED(XRCID("mask_editor_colour_polygon_negative"),MaskEditorPanel::OnColourChanged)
58  EVT_COLOURPICKER_CHANGED(XRCID("mask_editor_colour_polygon_positive"),MaskEditorPanel::OnColourChanged)
59  EVT_COLOURPICKER_CHANGED(XRCID("mask_editor_colour_point_selected"),MaskEditorPanel::OnColourChanged)
60  EVT_COLOURPICKER_CHANGED(XRCID("mask_editor_colour_point_unselected"),MaskEditorPanel::OnColourChanged)
61  EVT_TEXT_ENTER (XRCID("crop_left_text") ,MaskEditorPanel::OnSetLeft )
62  EVT_TEXT_ENTER (XRCID("crop_right_text") ,MaskEditorPanel::OnSetRight )
63  EVT_TEXT_ENTER (XRCID("crop_top_text") ,MaskEditorPanel::OnSetTop )
64  EVT_TEXT_ENTER (XRCID("crop_bottom_text") ,MaskEditorPanel::OnSetBottom )
65  EVT_BUTTON ( XRCID("crop_reset_button") , MaskEditorPanel::OnResetButton )
66  EVT_CHECKBOX( XRCID("crop_autocenter_cb") , MaskEditorPanel::OnAutoCenter)
67  EVT_NOTEBOOK_PAGE_CHANGED(XRCID("mask_editor_mask_crop_notebook"), MaskEditorPanel::OnModeChanged)
69 
71 {
72  DEBUG_TRACE("**********************");
73  m_pano = 0;
74  m_maskCropCtrl=NULL;
75  m_defaultMaskType=HuginBase::MaskPolygon::Mask_negative;
76 }
77 
78 bool MaskEditorPanel::Create(wxWindow* parent, wxWindowID id,
79  const wxPoint& pos,
80  const wxSize& size,
81  long style,
82  const wxString& name)
83 {
84  DEBUG_TRACE(" Create called *************");
85  if (! wxPanel::Create(parent, id, pos, size, style, name))
86  {
87  return false;
88  }
89 
90  m_selectedImages.clear();
91  m_MaskNr=UINT_MAX;
92  m_File="";
93 
94  wxXmlResource::Get()->LoadPanel(this, wxT("mask_panel"));
95  wxPanel * panel = XRCCTRL(*this, "mask_panel", wxPanel);
96 
97  wxBoxSizer *topsizer = new wxBoxSizer( wxVERTICAL );
98  topsizer->Add(panel, 1, wxEXPAND, 0);
99  SetSizer(topsizer);
100 
101  m_editImg = XRCCTRL(*this, "mask_editor_polygon_editor", MaskImageCtrl);
102  assert(m_editImg);
103  m_editImg->Init(this);
104 
105  // images list
106  m_imagesListMask = XRCCTRL(*this, "mask_editor_images_list", ImagesListMask);
107  // mask list
108  m_maskList = XRCCTRL(*this, "mask_editor_mask_list", wxListCtrl);
109  m_maskList->InsertColumn( 0, wxT("#"), wxLIST_FORMAT_RIGHT, 35);
110  m_maskList->InsertColumn( 1, _("Mask type"), wxLIST_FORMAT_LEFT, 120);
111 
112  m_maskCropCtrl = XRCCTRL(*this, "mask_editor_mask_crop_notebook", wxNotebook);
114  m_maskCropCtrl->SetSelection(0);
115  m_maskMode=true;
116 
117  //get saved width
118  wxConfigBase *config=wxConfigBase::Get();
119  for ( int j=0; j < m_maskList->GetColumnCount() ; j++ )
120  {
121  // -1 is auto
122  int width = config->Read(wxString::Format( wxT("/MaskEditorPanel/ColumnWidth%d"), j ), -1);
123  if(width != -1)
124  m_maskList->SetColumnWidth(j, width);
125  }
126  bool activeMasks;
127  config->Read(wxT("/MaskEditorPanel/ShowActiveMasks"),&activeMasks,false);
128  XRCCTRL(*this,"mask_editor_show_active_masks",wxCheckBox)->SetValue(activeMasks);
129  m_editImg->setDrawingActiveMasks(activeMasks);
130 
131  //load and set colours
132  wxColour defaultColour;
133  defaultColour.Set(wxT(HUGIN_MASK_COLOUR_POLYGON_NEGATIVE));
134  wxColour colour = config->Read(wxT("/MaskEditorPanel/ColourPolygonNegative"), defaultColour.GetAsString(wxC2S_HTML_SYNTAX));
135  XRCCTRL(*this,"mask_editor_colour_polygon_negative",wxColourPickerCtrl)->SetColour(colour);
137  defaultColour.Set(wxT(HUGIN_MASK_COLOUR_POLYGON_POSITIVE));
138  colour = config->Read(wxT("/MaskEditorPanel/ColourPolygonPositive"), defaultColour.GetAsString(wxC2S_HTML_SYNTAX));
139  XRCCTRL(*this,"mask_editor_colour_polygon_positive",wxColourPickerCtrl)->SetColour(colour);
141  defaultColour.Set(wxT(HUGIN_MASK_COLOUR_POINT_SELECTED));
142  colour = config->Read(wxT("/MaskEditorPanel/ColourPointSelected"), defaultColour.GetAsString(wxC2S_HTML_SYNTAX));
143  XRCCTRL(*this,"mask_editor_colour_point_selected",wxColourPickerCtrl)->SetColour(colour);
145  defaultColour.Set(wxT(HUGIN_MASK_COLOUR_POINT_UNSELECTED));
146  colour = config->Read(wxT("/MaskEditorPanel/ColourPointUnselected"), defaultColour.GetAsString(wxC2S_HTML_SYNTAX));
147  XRCCTRL(*this,"mask_editor_colour_point_unselected",wxColourPickerCtrl)->SetColour(colour);
149 
150  // other controls
151  m_maskType = XRCCTRL(*this, "mask_editor_choice_masktype", wxChoice);
152  m_defaultMaskType=(HuginBase::MaskPolygon::MaskType)config->Read(wxT("/MaskEditorPanel/DefaultMaskType"), 0l);
153  m_maskType->SetSelection((int)m_defaultMaskType);
154  // disable some controls
155  m_maskType->Disable();
156  XRCCTRL(*this, "mask_editor_choice_zoom", wxChoice)->Disable();
157  XRCCTRL(*this, "mask_editor_add", wxButton)->Disable();
158  XRCCTRL(*this, "mask_editor_load", wxButton)->Disable();
159  XRCCTRL(*this, "mask_editor_save", wxButton)->Disable();
160  XRCCTRL(*this, "mask_editor_copy", wxButton)->Disable();
161  XRCCTRL(*this, "mask_editor_paste", wxButton)->Disable();
162  XRCCTRL(*this, "mask_editor_delete", wxButton)->Disable();
163 
164  m_left_textctrl = XRCCTRL(*this,"crop_left_text", wxTextCtrl);
166  m_left_textctrl->PushEventHandler(new TextKillFocusHandler(this));
167 
168  m_cropLens = XRCCTRL(*this, "crop_all_images_lens", wxCheckBox);
170  bool doCropImagesLens;
171  config->Read(wxT("/MaskEditorPanel/CropImagesLens"), &doCropImagesLens, true);
172  m_cropLens->SetValue(doCropImagesLens);
173  m_top_textctrl = XRCCTRL(*this,"crop_top_text", wxTextCtrl);
175  m_top_textctrl->PushEventHandler(new TextKillFocusHandler(this));
176 
177  m_right_textctrl = XRCCTRL(*this,"crop_right_text", wxTextCtrl);
179  m_right_textctrl->PushEventHandler(new TextKillFocusHandler(this));
180 
181  m_bottom_textctrl = XRCCTRL(*this,"crop_bottom_text", wxTextCtrl);
183  m_bottom_textctrl->PushEventHandler(new TextKillFocusHandler(this));
184 
185  m_autocenter_cb = XRCCTRL(*this,"crop_autocenter_cb", wxCheckBox);
187 
188  //set shortcuts
189  wxAcceleratorEntry entries[2];
190  entries[0].Set(wxACCEL_CMD,(int)'C',XRCID("mask_editor_copy"));
191  entries[1].Set(wxACCEL_CMD,(int)'V',XRCID("mask_editor_paste"));
192  wxAcceleratorTable accel(2, entries);
193  SetAcceleratorTable(accel);
194 
195  // apply zoom specified in xrc file
196  wxCommandEvent dummy;
197  dummy.SetInt(XRCCTRL(*this,"mask_editor_choice_zoom",wxChoice)->GetSelection());
198  OnZoom(dummy);
199  return true;
200 }
201 
203 {
204  m_pano=pano;
207  // observe the panorama
208  m_pano->addObserver(this);
209 }
210 
212 {
213  m_left_textctrl->PopEventHandler(true);
214  m_right_textctrl->PopEventHandler(true);
215  m_top_textctrl->PopEventHandler(true);
216  m_bottom_textctrl->PopEventHandler(true);
217  wxConfigBase* config = wxConfigBase::Get();
218  config->Write(wxT("/MaskEditorPanel/ShowActiveMasks"),XRCCTRL(*this,"mask_editor_show_active_masks",wxCheckBox)->GetValue());
219  config->Write(wxT("/MaskEditorPanel/DefaultMaskType"),(long)m_defaultMaskType);
220  config->Write(wxT("/MaskEditorPanel/CropImagesLens"), m_cropLens->GetValue());
221  config->Flush();
222 
223  DEBUG_TRACE("dtor");
224  if (m_imageGroups)
225  {
226  delete m_imageGroups;
227  }
228  m_pano->removeObserver(this);
229 }
230 
232 {
233  if(m_selectedImages.empty())
234  {
235  return UINT_MAX;
236  }
237  else
238  {
239  return *(m_selectedImages.begin());
240  };
241 };
242 
243 void MaskEditorPanel::setImage(unsigned int imgNr, bool updateListSelection)
244 {
245  DEBUG_TRACE("image " << imgNr);
246  bool restoreMaskSelection=(imgNr==GetImgNr());
247  bool updateImage=true;
248  if(imgNr==UINT_MAX)
249  {
250  m_selectedImages.clear();
251  }
252  else
253  {
254  m_selectedImages.insert(imgNr);
255  };
256  HuginBase::MaskPolygonVector masksToDraw;
257  if (imgNr == UINT_MAX)
258  {
259  m_File = "";
261  m_currentMasks=mask;
262  m_editImg->setCrop(HuginBase::SrcPanoImage::NO_CROP,vigra::Rect2D(), false, hugin_utils::FDiff2D(), false);
263  }
264  else
265  {
266  const HuginBase::SrcPanoImage& image=m_pano->getImage(imgNr);
267  updateImage=(m_File!=image.getFilename());
268  if(updateImage)
269  m_File=image.getFilename();
270  else
271  if(GetRot(imgNr)!=m_editImg->getCurrentRotation())
272  {
273  updateImage=true;
274  m_File=image.getFilename();
275  };
276  m_currentMasks=image.getMasks();
277  masksToDraw=image.getActiveMasks();
278  m_editImg->setCrop(image.getCropMode(),image.getCropRect(), image.getAutoCenterCrop(), image.getRadialDistortionCenter(), image.isCircularCrop());
279  };
280  // update mask editor
281  if(updateImage)
282  m_editImg->setImage(m_File,m_currentMasks,masksToDraw,GetRot(imgNr));
283  else
284  m_editImg->setNewMasks(m_currentMasks,masksToDraw);
285  if (m_currentMasks.empty() || m_MaskNr >= m_currentMasks.size())
286  setMask(UINT_MAX);
287  // enables or disables controls
288  bool enableCtrl=(imgNr<UINT_MAX);
289  XRCCTRL(*this, "mask_editor_choice_zoom", wxChoice)->Enable(enableCtrl);
290  XRCCTRL(*this, "mask_editor_add", wxButton)->Enable(enableCtrl);
291  XRCCTRL(*this, "mask_editor_delete", wxButton)->Enable(enableCtrl && m_MaskNr<UINT_MAX);
292  XRCCTRL(*this, "mask_editor_load", wxButton)->Enable(enableCtrl);
293  XRCCTRL(*this, "mask_editor_save", wxButton)->Enable(enableCtrl && m_MaskNr<UINT_MAX);
294  XRCCTRL(*this, "mask_editor_paste", wxButton)->Enable(enableCtrl);
295  XRCCTRL(*this, "mask_editor_copy", wxButton)->Enable(enableCtrl && m_MaskNr<UINT_MAX);
296  UpdateMaskList(restoreMaskSelection);
297  // FIXME: lets hope that nobody holds references to these images..
298  ImageCache::getInstance().softFlush();
299  if(updateListSelection)
300  {
302  m_imagesListMask->EnsureVisible(imgNr);
303  };
304 }
305 
306 void MaskEditorPanel::setMask(unsigned int maskNr)
307 {
308  m_MaskNr=maskNr;
309  m_maskType->Enable(m_MaskNr<UINT_MAX);
311  XRCCTRL(*this,"mask_editor_delete", wxButton)->Enable(m_MaskNr<UINT_MAX);
312  XRCCTRL(*this, "mask_editor_save", wxButton)->Enable(m_MaskNr<UINT_MAX);
313  XRCCTRL(*this, "mask_editor_copy", wxButton)->Enable(m_MaskNr<UINT_MAX);
314  if(GetImgNr()<UINT_MAX && m_MaskNr<UINT_MAX)
315  m_maskType->SetSelection(m_currentMasks[m_MaskNr].getMaskType());
316  else
317  m_maskType->SetSelection((int)m_defaultMaskType);
318 };
319 
321 {
322  if(GetImgNr()<UINT_MAX)
323  {
326  };
327 };
328 
330 {
331  if(GetImgNr()<UINT_MAX)
332  {
334  m_currentMasks[m_currentMasks.size()-1].setMaskType(m_defaultMaskType);
336  //select added mask
337  SelectMask(m_currentMasks.size()-1);
339  };
340 };
341 
342 void MaskEditorPanel::SelectMask(unsigned int newMaskNr)
343 {
344  if (GetImgNr() < UINT_MAX)
345  {
346  if (newMaskNr < m_currentMasks.size())
347  {
348  m_maskList->SetItemState(newMaskNr, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
349  }
350  else
351  {
352  if (m_MaskNr < UINT_MAX)
353  {
354  m_maskList->SetItemState(m_MaskNr, 0, wxLIST_STATE_SELECTED);
355  };
356  };
357  };
358 };
359 
361 {
362 };
363 
365 {
366  unsigned int nrImages = pano.getNrOfImages();
368  ImageCache::getInstance().softFlush();
369  if (nrImages==0)
370  setImage(UINT_MAX);
371  else
372  {
373  // select some other image if we deleted the current image
374  if ((GetImgNr() < UINT_MAX) && (GetImgNr() >= nrImages))
375  {
376  for (auto i : m_selectedImages)
377  {
378  if (i >= nrImages)
379  {
380  m_selectedImages.erase(i);
381  };
382  };
383  setImage(nrImages - 1);
384  }
385  else
386  // update changed images
387  if(set_contains(changed,GetImgNr()))
388  {
389  unsigned int countOldMasks=m_currentMasks.size();
390  setImage(GetImgNr());
391  if(countOldMasks!=pano.getImage(GetImgNr()).getMasks().size())
392  SelectMask(UINT_MAX);
393  };
394  };
395 
396  if (!m_selectedImages.empty())
397  {
398  if (set_contains(changed, GetImgNr()))
399  {
401  }
402  }
403  else
404  {
406  }
407 
408 }
409 
410 void MaskEditorPanel::OnImageSelect(wxListEvent &e)
411 {
413  //select no mask
414  setMask(UINT_MAX);
415  setImage(GetImgNr());
416 
417  const bool hasImage = !m_selectedImages.empty();
418  m_left_textctrl->Enable(hasImage);
419  m_top_textctrl->Enable(hasImage);
420  m_bottom_textctrl->Enable(hasImage);
421  m_right_textctrl->Enable(hasImage);
422  if (hasImage)
423  {
424  // show first image.
426  };
427 };
428 
429 void MaskEditorPanel::OnMaskSelect(wxListEvent &e)
430 {
432 };
433 
434 void MaskEditorPanel::OnMaskTypeChange(wxCommandEvent &e)
435 {
436  if(GetImgNr()<UINT_MAX && m_MaskNr<UINT_MAX)
437  {
438  m_currentMasks[m_MaskNr].setMaskType((HuginBase::MaskPolygon::MaskType)e.GetSelection());
441  };
442 };
443 
444 void MaskEditorPanel::OnMaskAdd(wxCommandEvent &e)
445 {
446  if(GetImgNr()<UINT_MAX)
447  {
448  //deselect current selected mask
449  if(m_MaskNr<UINT_MAX)
450  m_maskList->SetItemState(m_MaskNr,0,wxLIST_STATE_SELECTED);
451  setMask(UINT_MAX);
452  MainFrame::Get()->SetStatusText(_("Create a polygon mask by clicking with the left mouse button on image, set the last point with the right mouse button."),0);
454  };
455 };
456 
457 void MaskEditorPanel::OnMaskSave(wxCommandEvent &e)
458 {
459  if(GetImgNr()<UINT_MAX && m_MaskNr<UINT_MAX)
460  {
461  wxFileDialog dlg(this, _("Save mask"),
462  wxConfigBase::Get()->Read(wxT("/actualPath"), wxT("")),
463  wxT(""), _("Mask files (*.msk)|*.msk|All files (*)|*"),
464  wxFD_SAVE | wxFD_OVERWRITE_PROMPT, wxDefaultPosition);
465  if (dlg.ShowModal() == wxID_OK)
466  {
467  wxString fn = dlg.GetPath();
468  if (fn.Right(4) != wxT(".msk"))
469  {
470  fn.Append(wxT(".msk"));
471  if (wxFile::Exists(fn))
472  {
473  int d = wxMessageBox(wxString::Format(_("File %s exists. Overwrite?"),
474  fn.c_str()), _("Save mask"),
475  wxYES_NO | wxICON_QUESTION);
476  if (d != wxYES) {
477  return;
478  }
479  }
480  };
481  wxFileName filename = fn;
482  std::ofstream maskFile(filename.GetFullPath().mb_str(HUGIN_CONV_FILENAME));
484  maskFile.close();
485  };
486  }
487 };
488 
489 void MaskEditorPanel::OnMaskLoad(wxCommandEvent &e)
490 {
491  if (GetImgNr()<UINT_MAX)
492  {
493  wxFileDialog dlg(this,_("Load mask"),
494  wxConfigBase::Get()->Read(wxT("/actualPath"),wxT("")),
495  wxT(""),_("Mask files (*.msk)|*.msk|All files (*)|*"),
496  wxFD_OPEN, wxDefaultPosition);
497  if (dlg.ShowModal() != wxID_OK)
498  {
499  MainFrame::Get()->SetStatusText(_("Load mask: cancel"));
500  return;
501  }
502  wxFileName filename(dlg.GetPath());
503  std::ifstream in(filename.GetFullPath().mb_str(HUGIN_CONV_FILENAME));
504  vigra::Size2D maskImageSize;
505  HuginBase::MaskPolygonVector loadedMasks;
506  LoadMaskFromStream(in, maskImageSize, loadedMasks, GetImgNr());
507  in.close();
508  if(maskImageSize.area()==0 || loadedMasks.empty())
509  {
510  wxMessageBox(wxString::Format(_("Could not parse mask from file %s."),dlg.GetPath().c_str()),_("Warning"),wxOK | wxICON_EXCLAMATION,this);
511  return;
512  };
513  // compare image size from file with that of current image alert user
514  // if different.
515  if (maskImageSize != m_pano->getImage(GetImgNr()).getSize())
516  {
517  MaskLoadDialog dlg(this);
518  dlg.initValues(m_pano->getImage(GetImgNr()),loadedMasks,maskImageSize);
519  if(dlg.ShowModal()!=wxID_OK)
520  {
521  // abort
522  return;
523  }
524  loadedMasks=dlg.getProcessedMask();
525  }
526  for(unsigned int i=0;i<loadedMasks.size();i++)
527  m_currentMasks.push_back(loadedMasks[i]);
528  // Update the pano with the imported masks
530  }
531 };
532 
533 void MaskEditorPanel::OnMaskCopy(wxCommandEvent &e)
534 {
535  if(GetImgNr()<UINT_MAX && m_MaskNr<UINT_MAX && m_maskMode)
536  {
537  std::ostringstream stream;
539  if (wxTheClipboard->Open())
540  {
541  // This data objects are held by the clipboard,
542  // so do not delete them in the app.
543  wxTheClipboard->SetData(new wxTextDataObject(wxString(stream.str().c_str(),wxConvLocal)));
544  wxTheClipboard->Close();
545  };
546  };
547 };
548 
549 void MaskEditorPanel::OnMaskPaste(wxCommandEvent &e)
550 {
551  if(GetImgNr()<UINT_MAX && m_maskMode)
552  {
553  if (wxTheClipboard->Open())
554  {
555  vigra::Size2D maskImageSize;
556  HuginBase::MaskPolygonVector loadedMasks;
557  if (wxTheClipboard->IsSupported( wxDF_TEXT ))
558  {
559  wxTextDataObject data;
560  wxTheClipboard->GetData(data);
561  std::istringstream stream(std::string(data.GetText().mb_str()));
562  LoadMaskFromStream(stream, maskImageSize, loadedMasks, GetImgNr());
563  }
564  wxTheClipboard->Close();
565  if(maskImageSize.area()==0 || loadedMasks.empty())
566  {
567  wxBell();
568  return;
569  };
570  // compare image size from file with that of current image alert user
571  // if different.
572  if (maskImageSize != m_pano->getImage(GetImgNr()).getSize())
573  {
574  MaskLoadDialog dlg(this);
575  dlg.initValues(m_pano->getImage(GetImgNr()),loadedMasks,maskImageSize);
576  if(dlg.ShowModal()!=wxID_OK)
577  {
578  // abort
579  return;
580  }
581  loadedMasks=dlg.getProcessedMask();
582  }
583  for(unsigned int i=0;i<loadedMasks.size();i++)
584  m_currentMasks.push_back(loadedMasks[i]);
585  // Update the pano with the imported masks
587  };
588  };
589 };
590 
591 void MaskEditorPanel::OnMaskDelete(wxCommandEvent &e)
592 {
593  if(GetImgNr()<UINT_MAX && m_MaskNr<UINT_MAX)
594  {
596  editedMasks.erase(editedMasks.begin()+m_MaskNr);
597  //setMask(UINT_MAX);
599  };
600 };
601 
602 void MaskEditorPanel::OnZoom(wxCommandEvent & e)
603 {
604  int posX = 0;
605  int posY = 0;
606  const wxSize ctrlSize = m_editImg->GetClientSize();
607  if (m_editImg->getScale() > 0)
608  {
609  // remember old scroll position
610  posX = (m_editImg->GetScrollPos(wxHORIZONTAL) + ctrlSize.GetWidth() / 2) / m_editImg->getScale();
611  posY = (m_editImg->GetScrollPos(wxVERTICAL) + ctrlSize.GetHeight() / 2) / m_editImg->getScale();
612  };
613  double factor;
614  switch (e.GetSelection())
615  {
616  case 0:
617  factor = 1;
618  break;
619  case 1:
620  // fit to window
621  factor = 0;
622  break;
623  case 2:
624  factor = 2;
625  break;
626  case 3:
627  factor = 1.5;
628  break;
629  case 4:
630  factor = 0.75;
631  break;
632  case 5:
633  factor = 0.5;
634  break;
635  case 6:
636  factor = 0.25;
637  break;
638  default:
639  DEBUG_ERROR("unknown scale factor");
640  factor = 1;
641  }
642  m_editImg->setScale(factor);
643  if (factor > 0)
644  {
645  m_editImg->Scroll(posX*factor - ctrlSize.GetWidth() / 2, posY*factor - ctrlSize.GetHeight() / 2);
646  };
647  if (e.GetString() == "update_selection")
648  {
649  XRCCTRL(*this, "mask_editor_choice_zoom", wxChoice)->SetSelection(e.GetSelection());
650  };
651 }
652 
653 void MaskEditorPanel::OnColourChanged(wxColourPickerEvent &e)
654 {
655  if(e.GetId()==XRCID("mask_editor_colour_polygon_negative"))
656  {
657  m_editImg->SetUserColourPolygonNegative(e.GetColour());
658  wxConfigBase::Get()->Write(wxT("/MaskEditorPanel/ColourPolygonNegative"),e.GetColour().GetAsString(wxC2S_HTML_SYNTAX));
659  }
660  else
661  if(e.GetId()==XRCID("mask_editor_colour_polygon_positive"))
662  {
663  m_editImg->SetUserColourPolygonPositive(e.GetColour());
664  wxConfigBase::Get()->Write(wxT("/MaskEditorPanel/ColourPolygonPositive"),e.GetColour().GetAsString(wxC2S_HTML_SYNTAX));
665  }
666  else
667  if(e.GetId()==XRCID("mask_editor_colour_point_selected"))
668  {
669  m_editImg->SetUserColourPointSelected(e.GetColour());
670  wxConfigBase::Get()->Write(wxT("/MaskEditorPanel/ColourPointSelected"),e.GetColour().GetAsString(wxC2S_HTML_SYNTAX));
671  }
672  else
673  {
674  m_editImg->SetUserColourPointUnselected(e.GetColour());
675  wxConfigBase::Get()->Write(wxT("/MaskEditorPanel/ColourPointUnselected"),e.GetColour().GetAsString(wxC2S_HTML_SYNTAX));
676  }
677  m_editImg->Refresh(true);
678 };
679 
680 void MaskEditorPanel::UpdateMaskList(bool restoreSelection)
681 {
682  unsigned int oldSelection=GetSelectedMask();
683  m_maskList->Freeze();
684  if(GetImgNr()<UINT_MAX)
685  {
686  if(!m_currentMasks.empty())
687  {
688  if(m_maskList->GetItemCount()!=m_currentMasks.size())
689  {
690  if(m_maskList->GetItemCount()<(int)m_currentMasks.size())
691  {
692  //added masks
693  for(int i=m_maskList->GetItemCount();i<(int)m_currentMasks.size();i++)
694  m_maskList->InsertItem(i,wxString::Format(wxT("%d"),i));
695  }
696  else
697  {
698  //deleted masks
699  for(int i=m_maskList->GetItemCount()-1;i>=(int)m_currentMasks.size();i--)
700  m_maskList->DeleteItem(i);
701  };
702  };
703  for(unsigned int i=0;i<m_currentMasks.size();i++)
704  {
705  m_maskList->SetItem(i,1,m_maskType->GetString(m_currentMasks[i].getMaskType()));
706  if(!restoreSelection && i==oldSelection)
707  m_maskList->SetItemState(i,0, wxLIST_STATE_SELECTED);
708  };
709  }
710  else
711  m_maskList->DeleteAllItems();
712  }
713  else
714  m_maskList->DeleteAllItems();
715  m_maskList->Thaw();
716  m_maskType->Enable(m_maskList->GetSelectedItemCount()>0);
717 }
718 
720 {
721  for(unsigned int i=0;i<(unsigned int)m_maskList->GetItemCount();i++)
722  {
723  if(m_maskList->GetItemState(i,wxLIST_STATE_SELECTED) & wxLIST_STATE_SELECTED)
724  return i;
725  };
726  return UINT_MAX;
727 };
728 
730 {
731  int colNum = e.GetColumn();
732  wxConfigBase::Get()->Write( wxString::Format(wxT("/MaskEditorPanel/ColumnWidth%d"),colNum), m_maskList->GetColumnWidth(colNum) );
733 }
734 
736 {
737  if(imgNr==UINT_MAX)
738  return MaskImageCtrl::ROT0;
739 
740  double pitch=m_pano->getImage(imgNr).getPitch();
741  double roll=m_pano->getImage(imgNr).getRoll();
742 
744  // normalize roll angle
745  while (roll > 360) roll-= 360;
746  while (roll < 0) roll += 360;
747 
748  while (pitch > 180) pitch -= 360;
749  while (pitch < -180) pitch += 360;
750  const bool headOver = (pitch > 90 || pitch < -90);
751 
752  if (wxConfig::Get()->Read("/CPEditorPanel/AutoRot", 1L))
753  {
754  if (roll >= 315 || roll < 45)
755  {
756  rot = headOver ? MaskImageCtrl::ROT180 : MaskImageCtrl::ROT0;
757  }
758  else
759  {
760  if (roll >= 45 && roll < 135)
761  {
762  rot = headOver ? MaskImageCtrl::ROT270 : MaskImageCtrl::ROT90;
763  }
764  else
765  {
766  if (roll >= 135 && roll < 225)
767  {
768  rot = headOver ? MaskImageCtrl::ROT0 : MaskImageCtrl::ROT180;
769  }
770  else
771  {
772  rot = headOver ? MaskImageCtrl::ROT90 : MaskImageCtrl::ROT270;
773  };
774  };
775  };
776  };
777  return rot;
778 }
779 
780 void MaskEditorPanel::OnShowActiveMasks(wxCommandEvent &e)
781 {
782  m_editImg->setDrawingActiveMasks(e.IsChecked());
783 };
784 
786 {
787  const HuginBase::SrcPanoImage & img = m_pano->getImage(imgNr);
788  m_cropMode=img.getCropMode();
789  m_cropRect=img.getCropRect();
790  m_autoCenterCrop=img.getAutoCenterCrop();
791 
792  int dx = hugin_utils::roundi(img.getRadialDistortionCenterShift().x);
793  int dy = hugin_utils::roundi(img.getRadialDistortionCenterShift().y);
795  m_cropCenter = vigra::Point2D(img.getSize().width()/2 + dx, img.getSize().height()/2 + dy);
796 
798 }
799 
800 // transfer our state to panorama
801 void MaskEditorPanel::UpdateCrop(bool updateFromImgCtrl)
802 {
803  // set crop image options.
804  if(updateFromImgCtrl)
805  {
807  };
808  std::vector<HuginBase::SrcPanoImage> srcImgs;
809  HuginBase::UIntSet imgs;
810  if (m_cropLens->IsChecked())
811  {
812  const HuginBase::UIntSetVector lensImageVector = m_imageGroups->getLenses().getPartsSet();
813  for (auto i : m_selectedImages)
814  {
815  for (auto j : lensImageVector)
816  {
817  if (set_contains(j, i))
818  {
819  std::copy(j.begin(), j.end(), std::inserter(imgs, imgs.begin()));
820  break;
821  };
822  };
823  };
824  }
825  else
826  {
827  imgs = m_selectedImages;
828  }
829  for (auto i:imgs)
830  {
832  img.setCropRect(m_cropRect);
833  img.setAutoCenterCrop(m_autoCenterCrop);
834  srcImgs.push_back(img);
835  };
836 
838  new PanoCommand::UpdateSrcImagesCmd(*m_pano, imgs, srcImgs)
839  );
840 }
841 
843 {
846 };
847 
848 // redraw display with new information
850 {
851  DEBUG_TRACE("")
853  m_left_textctrl->SetValue(wxString::Format(wxT("%d"),m_cropRect.left()));
854  m_right_textctrl->SetValue(wxString::Format(wxT("%d"),m_cropRect.right()));
855  m_top_textctrl->SetValue(wxString::Format(wxT("%d"),m_cropRect.top()));
856  m_bottom_textctrl->SetValue(wxString::Format(wxT("%d"),m_cropRect.bottom()));
857 }
858 
859 
860 void MaskEditorPanel::OnSetTop(wxCommandEvent & e)
861 {
862  long val;
863  if (m_top_textctrl->GetValue().ToLong(&val))
864  {
865  m_cropRect.setUpperLeft(vigra::Point2D(m_cropRect.left(), val));
866  if (m_autoCenterCrop)
867  {
868  CenterCrop();
870  };
871  UpdateCrop();
872  }
873  else
874  {
875  wxLogError(_("Please enter a valid number"));
876  };
877 };
878 
879 void MaskEditorPanel::OnSetBottom(wxCommandEvent & e)
880 {
881  long val;
882  if (m_bottom_textctrl->GetValue().ToLong(&val))
883  {
884  m_cropRect.setLowerRight(vigra::Point2D(m_cropRect.right(), val));
885  if (m_autoCenterCrop)
886  {
887  CenterCrop();
889  }
890  UpdateCrop();
891  }
892  else
893  {
894  wxLogError(_("Please enter a valid number"));
895  };
896 };
897 
898 void MaskEditorPanel::OnSetLeft(wxCommandEvent & e)
899 {
900  long val = 0;
901  if (m_left_textctrl->GetValue().ToLong(&val))
902  {
903  m_cropRect.setUpperLeft(vigra::Point2D(val, m_cropRect.top()));
904  if (m_autoCenterCrop)
905  {
906  CenterCrop();
908  }
909  UpdateCrop();
910  }
911  else
912  {
913  wxLogError(_("Please enter a valid number"));
914  };
915 };
916 
917 void MaskEditorPanel::OnSetRight(wxCommandEvent & e)
918 {
919  long val = 0;
920  if (m_right_textctrl->GetValue().ToLong(&val))
921  {
922  m_cropRect.setLowerRight(vigra::Point2D(val, m_cropRect.bottom()));
923  if (m_autoCenterCrop)
924  {
925  CenterCrop();
927  };
928  UpdateCrop();
929  }
930  else
931  {
932  wxLogError(_("Please enter a valid number"));
933  };
934 };
935 
936 void MaskEditorPanel::OnResetButton(wxCommandEvent & e)
937 {
938  // suitable defaults.
939  m_cropRect.setUpperLeft(vigra::Point2D(0,0));
940  m_cropRect.setLowerRight(vigra::Point2D(0,0));
941  m_autoCenterCrop = true;
944  UpdateCrop();
945 }
946 
947 void MaskEditorPanel::OnAutoCenter(wxCommandEvent & e)
948 {
949  m_autoCenterCrop = e.IsChecked();
950  if (m_autoCenterCrop)
951  {
952  CenterCrop();
954  };
955  UpdateCrop();
956 }
957 
959 {
960  vigra::Diff2D d(m_cropRect.width()/2, m_cropRect.height() / 2);
961  m_cropRect.setUpperLeft( m_cropCenter - d);
962  m_cropRect.setLowerRight( m_cropCenter + d);
963 }
964 
965 void MaskEditorPanel::OnModeChanged(wxNotebookEvent& e)
966 {
967  if(m_maskCropCtrl==NULL)
968  {
969  return;
970  };
971  if(m_maskCropCtrl->GetSelection()==0)
972  {
973  m_maskMode=true;
974  size_t imgNr=GetImgNr();
975  m_selectedImages.clear();
976  m_selectedImages.insert(imgNr);
979  m_editImg->SetMaskMode(true);
980  }
981  else
982  {
983  m_maskMode=false;
985  m_editImg->SetMaskMode(false);
986  SelectMask(UINT_MAX);
987  };
988  m_editImg->Refresh();
989 };
990 
992 {
993  m_maskCropCtrl->SetSelection(1);
994  wxNotebookEvent dummy;
995  OnModeChanged(dummy);
996 }
997 
999 
1001  : wxXmlResourceHandler()
1002 {
1003  AddWindowStyles();
1004 }
1005 
1007 {
1008  XRC_MAKE_INSTANCE(cp, MaskEditorPanel)
1009 
1010  cp->Create(m_parentAsWindow,
1011  GetID(),
1012  GetPosition(), GetSize(),
1013  GetStyle(wxT("style")),
1014  GetName());
1015 
1016  SetupWindow(cp);
1017 
1018  return cp;
1019 }
1020 
1022 {
1023  return IsOfClass(node, wxT("MaskEditorPanel"));
1024 }
1025 
void OnResetButton(wxCommandEvent &e)
update source images
Definition: PanoCommand.h:404
ImageRotation
image rotation.
Definition: MaskImageCtrl.h:55
HuginBase::ConstStandardImageVariableGroups * m_imageGroups
void OnSetTop(wxCommandEvent &e)
void SaveMaskToStream(std::ostream &stream, vigra::Size2D imageSize, MaskPolygon &maskToWrite, size_t imgNr)
save the mask into stream
Definition: Mask.cpp:685
implementation of huginApp Class
hugin_utils::FDiff2D getRadialDistortionCenter() const
std::vector< UIntSet > UIntSetVector
Definition: PanoramaData.h:56
HuginBase::UIntSet m_selectedImages
void UpdateMaskList(bool restoreSelection=false)
updates the display after another image has been selected.
void UpdateCropFromImage()
updates the displayed crop in the text boxes (for dragging)
unsigned int m_MaskNr
int roundi(T x)
Definition: hugin_math.h:73
SrcPanoImage getSrcImage(unsigned imgNr) const
get a description of a source image
Definition: Panorama.cpp:1620
wxListCtrl * m_maskList
bool isCircularCrop() const
returns true, if projection requires cicular crop
bool removeObserver(PanoramaObserver *observer)
remove a panorama observer.
Definition: Panorama.cpp:1551
#define HUGIN_CONV_FILENAME
Definition: platform.h:40
void setActiveMask(unsigned int newMask, bool doUpdate=true)
mark mask with image as beeing editing
HuginBase::Panorama * m_pano
void LoadMaskFromStream(std::istream &stream, vigra::Size2D &imageSize, MaskPolygonVector &newMasks, size_t imgNr)
load the mask from stream
Definition: Mask.cpp:640
#define DEBUG_TRACE(msg)
Definition: utils.h:67
void OnMaskPaste(wxCommandEvent &e)
called when user wants to paste a mask from clipboard
void SelectMask(unsigned int newMaskNr)
selects the mask with index newMaskNr in the listbox
void SetUserColourPointUnselected(wxColour newColour)
xrc handler for handling mask editor panel
MaskImageCtrl * m_editImg
HuginBase::MaskPolygonVector getProcessedMask() const
return the processed mask
void OnImageSelect(wxListEvent &e)
called when user selected another image
bool set_contains(const _Container &c, const typename _Container::key_type &key)
Definition: stl_utils.h:74
virtual bool CanHandle(wxXmlNode *node)
#define DEBUG_ASSERT(cond)
Definition: utils.h:80
wxChoice * m_maskType
HuginBase::SrcPanoImage::CropMode m_cropMode
END_EVENT_TABLE()
include file for the hugin project
mask editor panel.
ImagesListMask * m_imagesListMask
void setImage(unsigned int imgNr, bool updateListSelection=false)
sets the image, which is currently edited
void SetSingleSelect(bool isSingleSelect)
sets the listbox to single item select or multiply item select
Definition: ImagesList.cpp:485
vigra::Rect2D getCrop()
returns the current crop rect
Definition: MaskImageCtrl.h:66
void OnMaskLoad(wxCommandEvent &e)
called when user wants to load a mask into the selected image
HuginBase::MaskPolygonVector m_currentMasks
void OnAutoCenter(wxCommandEvent &e)
wxTextCtrl * m_right_textctrl
void OnSetRight(wxCommandEvent &e)
void OnSetBottom(wxCommandEvent &e)
void SetUserColourPolygonNegative(wxColour newColour)
sets the colour for different parts
void SetUserColourPointSelected(wxColour newColour)
mask editor
Definition: MaskImageCtrl.h:38
void setMask(unsigned int maskNr)
sets active mask number, set to UINT_MAX, if no mask is currently editing
vigra::Rect2D m_cropRect
std::set< unsigned int > UIntSet
Definition: PanoramaData.h:51
void OnZoom(wxCommandEvent &e)
sets the actual zoom factor
update mask for given image
Definition: PanoCommand.h:612
Model for a panorama.
Definition: Panorama.h:152
void SelectSingleImage(unsigned int imgNr)
Select an image.
Definition: ImagesList.cpp:252
void setImage(const std::string &filename, HuginBase::MaskPolygonVector newMask, HuginBase::MaskPolygonVector masksToDraw, ImageRotation rot)
set the current image and mask list, this loads also the image from cache
ConstImageVariableGroup & getLenses()
Get the ImageVariableGroup representing the group of lens variables.
UIntSetVector getPartsSet() const
return a vector which contains a HuginBase::UIntSet for each group with the corresponding images numb...
void panoramaImagesChanged(HuginBase::Panorama &pano, const HuginBase::UIntSet &imgNr)
notifies about changes to images
const HuginBase::UIntSet & GetSelected() const
get the currently selected images
Definition: ImagesList.cpp:181
HuginBase::MaskPolygon::MaskType m_defaultMaskType
void OnMaskTypeChange(wxCommandEvent &e)
handler when mask type was changed
HuginBase::MaskPolygonVector getNewMask() const
returns the vector of all mask (including new created mask)
Definition: MaskImageCtrl.h:70
std::size_t getNrOfImages() const
number of images.
Definition: Panorama.h:205
static MainFrame * Get()
hack.. kind of a pseudo singleton...
Definition: MainFrame.cpp:2183
void SwitchToCropMode()
switches the controls to crop mode
MaskType
enumeration with type of possible masks
Definition: Mask.h:56
void OnColourChanged(wxColourPickerEvent &e)
event handler for changing colours
IMPLEMENT_DYNAMIC_CLASS(wxTreeListHeaderWindow, wxWindow)
void OnModeChanged(wxNotebookEvent &e)
ImageRotation getCurrentRotation()
returns the current rotation of displayed image
vigra::Point2D m_cropCenter
void OnColumnWidthChange(wxListEvent &e)
called, when column with of mask list box was changed
double getScale()
return scale factor, 0 for autoscale
void UpdateMask()
called when mask where changed in MaskImageCtrl
Make an ImageVariableGroup for lenses and other common concepts.
static GlobalCmdHist & getInstance()
void UpdateCrop(bool updateFromImgCtrl=false)
updated the crop in the Panorama object with the current values from GUI
void Init(HuginBase::Panorama *pano)
Definition: ImagesList.cpp:458
void addCommand(PanoCommand *command, bool execute=true)
Adds a command to the history.
wxCheckBox * m_autocenter_cb
void OnMaskSave(wxCommandEvent &e)
called when user wants to save active mask
std::vector< MaskPolygon > MaskPolygonVector
Definition: Mask.h:147
#define DEBUG_ERROR(msg)
Definition: utils.h:76
void Init(HuginBase::Panorama *pano)
wxCheckBox * m_cropLens
#define HUGIN_MASK_COLOUR_POINT_UNSELECTED
bool Create(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxTAB_TRAVERSAL, const wxString &name=wxT("panel"))
void selectAllMarkers()
select all points of active mask
std::string m_File
Dialog for loading masks.
void setNewMasks(HuginBase::MaskPolygonVector newMasks, HuginBase::MaskPolygonVector masksToDraw)
updates masks for currently selected image
void OnSetLeft(wxCommandEvent &e)
void addObserver(PanoramaObserver *o)
add a panorama observer.
Definition: Panorama.cpp:1546
include file for the hugin project
Handle EVT_KILL_FOCUS and convert it to a EVT_TEXT_ENTER event.
specialized to display the mask aspect of images
Definition: ImagesList.h:167
wxNotebook * m_maskCropCtrl
virtual ~MaskEditorPanel()
dtor.
options wxIntPtr wxIntPtr sortData std::vector< PanoInfo > * data
void DisplayCrop(int imgNr)
copies the crop information from the Panorama object to GUI
void setScale(double factor)
set the scaling factor for mask editing display.
#define HUGIN_MASK_COLOUR_POINT_SELECTED
void Init(MaskEditorPanel *parent)
void UpdateCropDisplay()
update GUI display
wxTextCtrl * m_bottom_textctrl
void SetMaskMode(bool newMaskMode)
sets the control to mask (newMaskMode=true) or crop (newMaskMode=false) mode
#define HUGIN_MASK_COLOUR_POLYGON_NEGATIVE
MaskImageCtrl::ImageRotation GetRot(const unsigned int imgNr)
determines, if the image should be rotated for display
void OnMaskDelete(wxCommandEvent &e)
called when user wants to delete active mask
void update()
Update part numbers for each variable group.
Definition of mask load dialog.
const SrcPanoImage & getImage(std::size_t nr) const
get a panorama image, counting starts with 0
Definition: Panorama.h:211
void SetUserColourPolygonPositive(wxColour newColour)
void OnMaskSelect(wxListEvent &e)
called when user selected another mask
void OnMaskAdd(wxCommandEvent &e)
called when user wants to create new polygon
void panoramaChanged(HuginBase::Panorama &pano)
called when the panorama changes and we should update our display
void OnShowActiveMasks(wxCommandEvent &e)
event handler for changing option if active masks should be drawn
#define HUGIN_MASK_COLOUR_POLYGON_POSITIVE
void startNewPolygon()
starts creating a new polygon
void AddMask()
called when new mask added in MaskImageCtrl
wxTextCtrl * m_top_textctrl
All variables of a source image.
Definition: SrcPanoImage.h:194
void setCrop(HuginBase::SrcPanoImage::CropMode newCropMode, vigra::Rect2D newCropRect, bool isCentered, hugin_utils::FDiff2D center, bool isCircleCrop)
updates the crop mode and crop rect
virtual wxObject * DoCreateResource()
void initValues(const HuginBase::SrcPanoImage image, const HuginBase::MaskPolygonVector newMask, const vigra::Size2D maskSize)
sets the default values
wxTextCtrl * m_left_textctrl
unsigned int GetSelectedMask()
return index of currently selected masks, return UINT_MAX if no mask is selected
void setDrawingActiveMasks(bool newDrawActiveMasks)
set if active masks should be drawn
void OnMaskCopy(wxCommandEvent &e)
called when user wants to copy a mask to clipboard