Hugin trunk 0.1
Loading...
Searching...
No Matches
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"
30
31// hugin's
32#include "base_wx/platform.h"
33#include "hugin/MainFrame.h"
36#include "base_wx/PanoCommand.h"
39#include <wx/clipbrd.h>
41#include "base_wx/wxutils.h"
42
50
52 const wxPoint& pos,
53 const wxSize& size,
54 long style,
55 const wxString& name)
56{
57 DEBUG_TRACE(" Create called *************");
58 if (! wxPanel::Create(parent, id, pos, size, style, name))
59 {
60 return false;
61 }
62
64 m_File="";
65
66 wxXmlResource::Get()->LoadPanel(this, "mask_panel");
67 wxPanel * panel = XRCCTRL(*this, "mask_panel", wxPanel);
68
70 topsizer->Add(panel, 1, wxEXPAND, 0);
72
73 m_editImg = XRCCTRL(*this, "mask_editor_polygon_editor", MaskImageCtrl);
75 m_editImg->Init(this);
76
77 // images list
78 m_imagesListMask = XRCCTRL(*this, "mask_editor_images_list", wxListCtrl);
79 m_imagesListMask->InsertColumn(0, "#", wxLIST_FORMAT_RIGHT, 35);
80 m_imagesListMask->InsertColumn(1, _("Filename"), wxLIST_FORMAT_LEFT, 200);
81 m_imagesListMask->InsertColumn(2, _("Number of masks"), wxLIST_FORMAT_RIGHT, 120);
82 m_imagesListMask->InsertColumn(3, _("Crop"), wxLIST_FORMAT_RIGHT, 120);
87
88 //get saved width
89 wxConfigBase* config = wxConfigBase::Get();
90 for (int j = 0; j < m_imagesListMask->GetColumnCount(); j++)
91 {
92 // -1 is auto
93 int width = config->Read(wxString::Format("/ImagesListMask/ColumnWidth%d", j), -1);
94 if (width != -1)
95 {
96 m_imagesListMask->SetColumnWidth(j, width);
97 }
98 }
99 // mask list
100 m_maskList = XRCCTRL(*this, "mask_editor_mask_list", wxListCtrl);
101 m_maskList->InsertColumn( 0, "#", wxLIST_FORMAT_RIGHT, 35);
102 m_maskList->InsertColumn( 1, _("Mask type"), wxLIST_FORMAT_LEFT, 120);
106
107 m_maskCropCtrl = XRCCTRL(*this, "mask_editor_mask_crop_notebook", wxNotebook);
109 m_maskCropCtrl->SetSelection(0);
111 m_maskMode=true;
112
113 //get saved width
114 for ( int j=0; j < m_maskList->GetColumnCount() ; j++ )
115 {
116 // -1 is auto
117 int width = config->Read(wxString::Format( "/MaskEditorPanel/ColumnWidth%d", j ), -1);
118 if(width != -1)
119 m_maskList->SetColumnWidth(j, width);
120 }
121 bool activeMasks;
122 config->Read("/MaskEditorPanel/ShowActiveMasks",&activeMasks,false);
123 XRCCTRL(*this,"mask_editor_show_active_masks",wxCheckBox)->SetValue(activeMasks);
125
126 //load and set colours
129 wxColour colour = config->Read("/MaskEditorPanel/ColourPolygonNegative", defaultColour.GetAsString(wxC2S_HTML_SYNTAX));
130 XRCCTRL(*this,"mask_editor_colour_polygon_negative",wxColourPickerCtrl)->SetColour(colour);
133 colour = config->Read("/MaskEditorPanel/ColourPolygonPositive", defaultColour.GetAsString(wxC2S_HTML_SYNTAX));
134 XRCCTRL(*this,"mask_editor_colour_polygon_positive",wxColourPickerCtrl)->SetColour(colour);
137 colour = config->Read("/MaskEditorPanel/ColourPointSelected", defaultColour.GetAsString(wxC2S_HTML_SYNTAX));
138 XRCCTRL(*this,"mask_editor_colour_point_selected",wxColourPickerCtrl)->SetColour(colour);
141 colour = config->Read("/MaskEditorPanel/ColourPointUnselected", defaultColour.GetAsString(wxC2S_HTML_SYNTAX));
142 XRCCTRL(*this,"mask_editor_colour_point_unselected",wxColourPickerCtrl)->SetColour(colour);
145
146 // other controls
147 m_maskType = XRCCTRL(*this, "mask_editor_choice_masktype", wxChoice);
148 m_defaultMaskType=(HuginBase::MaskPolygon::MaskType)config->Read("/MaskEditorPanel/DefaultMaskType", 0l);
149 m_maskType->SetSelection((int)m_defaultMaskType);
151 // disable some controls
152 m_maskType->Disable();
153 XRCCTRL(*this, "mask_editor_choice_zoom", wxChoice)->Disable();
154 XRCCTRL(*this, "mask_editor_add", wxButton)->Disable();
155 XRCCTRL(*this, "mask_editor_load", wxButton)->Disable();
156 XRCCTRL(*this, "mask_editor_save", wxButton)->Disable();
157 XRCCTRL(*this, "mask_editor_copy", wxButton)->Disable();
158 XRCCTRL(*this, "mask_editor_paste", wxButton)->Disable();
159 XRCCTRL(*this, "mask_editor_delete", wxButton)->Disable();
160
161 m_left_textctrl = XRCCTRL(*this,"crop_left_text", wxTextCtrl);
163 m_left_textctrl->PushEventHandler(new TextKillFocusHandler(this));
165
166 m_cropLens = XRCCTRL(*this, "crop_all_images_lens", wxCheckBox);
168 bool doCropImagesLens;
169 config->Read("/MaskEditorPanel/CropImagesLens", &doCropImagesLens, true);
170 m_cropLens->SetValue(doCropImagesLens);
171 m_top_textctrl = XRCCTRL(*this,"crop_top_text", wxTextCtrl);
173 m_top_textctrl->PushEventHandler(new TextKillFocusHandler(this));
175
176 m_right_textctrl = XRCCTRL(*this,"crop_right_text", wxTextCtrl);
178 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));
185
186
187 m_autocenter_cb = XRCCTRL(*this,"crop_autocenter_cb", wxCheckBox);
190
191 //set shortcuts
193 entries[0].Set(wxACCEL_CMD,(int)'C',XRCID("mask_editor_copy"));
194 entries[1].Set(wxACCEL_CMD,(int)'V',XRCID("mask_editor_paste"));
197
198 // apply zoom specified in xrc file
199 wxCommandEvent dummy;
200 dummy.SetInt(XRCCTRL(*this,"mask_editor_choice_zoom",wxChoice)->GetSelection());
201 OnZoom(dummy);
202 // bind remaining event handler
204 Bind(wxEVT_BUTTON, &MaskEditorPanel::OnMaskAdd, this, XRCID("mask_editor_add"));
205 Bind(wxEVT_BUTTON, &MaskEditorPanel::OnMaskLoad, this, XRCID("mask_editor_load"));
206 Bind(wxEVT_BUTTON, &MaskEditorPanel::OnMaskSave, this, XRCID("mask_editor_save"));
207 Bind(wxEVT_BUTTON, &MaskEditorPanel::OnMaskCopy, this, XRCID("mask_editor_copy"));
208 Bind(wxEVT_BUTTON, &MaskEditorPanel::OnMaskPaste, this, XRCID("mask_editor_paste"));
209 Bind(wxEVT_BUTTON, &MaskEditorPanel::OnMaskDelete, this, XRCID("mask_editor_delete"));
210 Bind(wxEVT_CHECKBOX, &MaskEditorPanel::OnShowActiveMasks, this, XRCID("mask_editor_show_active_masks"));
211 Bind(wxEVT_BUTTON, &MaskEditorPanel::OnResetButton, this, XRCID("crop_reset_button"));
212 return true;
213}
214
216{
217 m_pano=pano;
219 // observe the panorama
220 m_pano->addObserver(this);
221}
222
224{
225 m_left_textctrl->PopEventHandler(true);
226 m_right_textctrl->PopEventHandler(true);
227 m_top_textctrl->PopEventHandler(true);
228 m_bottom_textctrl->PopEventHandler(true);
229 wxConfigBase* config = wxConfigBase::Get();
230 config->Write("/MaskEditorPanel/ShowActiveMasks",XRCCTRL(*this,"mask_editor_show_active_masks",wxCheckBox)->GetValue());
231 config->Write("/MaskEditorPanel/DefaultMaskType",(long)m_defaultMaskType);
232 config->Write("/MaskEditorPanel/CropImagesLens", m_cropLens->GetValue());
233 config->Flush();
234
235 DEBUG_TRACE("dtor");
236 if (m_imageGroups)
237 {
238 delete m_imageGroups;
239 }
240 m_pano->removeObserver(this);
241}
242
244{
245 if(m_imagesListMask->GetSelectedItemCount()==0)
246 {
247 return UINT_MAX;
248 }
249 else
250 {
251 return *(GetSelectedImages().begin());
252 };
253};
254
255void SelectSingleImage(wxListCtrl* list, unsigned int imgNr)
256{
257 unsigned int nrItems = list->GetItemCount();
258 // remove potentially existing selection
259 for (unsigned int i = 0; i < nrItems; i++)
260 {
261 if (i == imgNr)
262 {
263 continue;
264 };
265 list->SetItemState(i, 0, wxLIST_STATE_SELECTED);
266 }
267 if (imgNr != UINT_MAX)
268 {
270 };
271}
272
273void MaskEditorPanel::setImage(unsigned int imgNr, bool updateListSelection)
274{
275 DEBUG_TRACE("image " << imgNr);
276 bool restoreMaskSelection=(imgNr==GetImgNr());
277 bool updateImage=true;
279 if (imgNr == UINT_MAX)
280 {
281 m_File = "";
283 m_currentMasks=mask;
285 }
286 else
287 {
288 const HuginBase::SrcPanoImage& image=m_pano->getImage(imgNr);
289 updateImage=(m_File!=image.getFilename());
290 if(updateImage)
291 m_File=image.getFilename();
292 else
293 if(GetRot(imgNr)!=m_editImg->getCurrentRotation())
294 {
295 updateImage=true;
296 m_File=image.getFilename();
297 };
298 m_currentMasks=image.getMasks();
299 masksToDraw=image.getActiveMasks();
300 m_editImg->setCrop(image.getCropMode(),image.getCropRect(), image.getAutoCenterCrop(), image.getRadialDistortionCenter(), image.isCircularCrop());
301 };
302 // update mask editor
303 if(updateImage)
305 else
307 if (m_currentMasks.empty() || m_MaskNr >= m_currentMasks.size())
309 // enables or disables controls
310 bool enableCtrl=(imgNr<UINT_MAX);
311 XRCCTRL(*this, "mask_editor_choice_zoom", wxChoice)->Enable(enableCtrl);
312 XRCCTRL(*this, "mask_editor_add", wxButton)->Enable(enableCtrl);
313 XRCCTRL(*this, "mask_editor_delete", wxButton)->Enable(enableCtrl && m_MaskNr<UINT_MAX);
314 XRCCTRL(*this, "mask_editor_load", wxButton)->Enable(enableCtrl);
315 XRCCTRL(*this, "mask_editor_save", wxButton)->Enable(enableCtrl && m_MaskNr<UINT_MAX);
316 XRCCTRL(*this, "mask_editor_paste", wxButton)->Enable(enableCtrl);
317 XRCCTRL(*this, "mask_editor_copy", wxButton)->Enable(enableCtrl && m_MaskNr<UINT_MAX);
319 // FIXME: lets hope that nobody holds references to these images..
320 ImageCache::getInstance().softFlush();
322 {
324 m_imagesListMask->EnsureVisible(imgNr);
325 };
326}
327
329{
333 XRCCTRL(*this,"mask_editor_delete", wxButton)->Enable(m_MaskNr<UINT_MAX);
334 XRCCTRL(*this, "mask_editor_save", wxButton)->Enable(m_MaskNr<UINT_MAX);
335 XRCCTRL(*this, "mask_editor_copy", wxButton)->Enable(m_MaskNr<UINT_MAX);
337 m_maskType->SetSelection(m_currentMasks[m_MaskNr].getMaskType());
338 else
339 m_maskType->SetSelection((int)m_defaultMaskType);
340};
341
350
363
365{
366 if (GetImgNr() < UINT_MAX)
367 {
368 if (newMaskNr < m_currentMasks.size())
369 {
371 }
372 else
373 {
374 if (m_MaskNr < UINT_MAX)
375 {
376 m_maskList->SetItemState(m_MaskNr, 0, wxLIST_STATE_SELECTED);
377 };
378 };
379 };
380};
381
385
387{
388 const HuginBase::SrcPanoImage& img = m_pano->getImage(imgNr);
389 wxFileName fn(wxString(img.getFilename().c_str(), HUGIN_CONV_FILENAME));
390 m_imagesListMask->SetItem(imgNr, 1, fn.GetFullName());
391
393 if (img.hasMasks())
394 {
395 maskstr = wxString::Format("%lu", (unsigned long int) m_pano->getImage(imgNr).getMasks().size());
396 }
397 else
398 {
399 maskstr = wxString("-");
400 };
401 m_imagesListMask->SetItem(imgNr, 2, maskstr);
402
403 wxString cropstr("-");
404 if (img.getCropMode() != HuginBase::SrcPanoImage::NO_CROP)
405 {
406 vigra::Rect2D c = img.getCropRect();
407 cropstr.Printf("%d,%d,%d,%d", c.left(), c.right(), c.top(), c.bottom());
408 }
409 m_imagesListMask->SetItem(imgNr, 3, cropstr);
410}
411
413{
414 const long nrImages = pano.getNrOfImages();
416 ImageCache::getInstance().softFlush();
418 {
419 m_imagesListMask->Freeze();
420 const size_t nrItems = m_imagesListMask->GetItemCount();
421 // remove items for nonexisting images
422 if (nrItems > nrImages)
423 {
424 for (long i = nrItems - 1; i >= nrImages; i--)
425 {
426 m_imagesListMask->DeleteItem(i);
427 }
428 }
429 // add newly added images
430 if (nrImages >= nrItems)
431 {
432 for (HuginBase::UIntSet::const_iterator it = changed.begin(); it != changed.end(); ++it)
433 {
434 if (*it >= nrItems)
435 {
436 m_imagesListMask->InsertItem(*it, wxString::Format("%d", *it));
437 }
438 }
439 }
440 // update existing items
441 for (HuginBase::UIntSet::const_iterator it = changed.begin(); it != changed.end(); ++it)
442 {
443 UpdateImage(*it);
444 }
445 m_imagesListMask->Thaw();
446 }
447
448 if (nrImages == 0)
449 {
451 }
452 else
453 {
454 // select some other image if we deleted the current image
455 if ((GetImgNr() < UINT_MAX) && (GetImgNr() >= nrImages))
456 {
457 setImage(nrImages - 1);
458 }
459 else
460 // update changed images
462 {
463 unsigned int countOldMasks=m_currentMasks.size();
465 if(countOldMasks!=pano.getImage(GetImgNr()).getMasks().size())
467 };
468 };
469
470 if (m_imagesListMask->GetSelectedItemCount() > 0)
471 {
473 {
475 }
476 }
477 else
478 {
480 }
481
482}
483
485{
486 // only for multi selection mode
487 if ((m_imagesListMask->GetWindowStyle() & wxLC_SINGLE_SEL) == 0)
488 {
489 // ctrl + a
490 if (e.GetKeyCode() == 1 && e.CmdDown())
491 {
492 // select all
493 for (int i = 0; i < m_imagesListMask->GetItemCount(); i++)
494 {
496 }
497 }
498 }
499 e.Skip();
500}
501
503{
505 //select no mask
508
509 const bool hasImage = !selectedImages.empty();
510 m_left_textctrl->Enable(hasImage);
511 m_top_textctrl->Enable(hasImage);
513 m_right_textctrl->Enable(hasImage);
514 if (hasImage)
515 {
516 // show first image.
518 };
519};
520
525
535
536void MaskEditorPanel::OnMaskAdd(wxCommandEvent &e)
537{
538 if(GetImgNr()<UINT_MAX)
539 {
540 //deselect current selected mask
544 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);
546 };
547};
548
549void MaskEditorPanel::OnMaskSave(wxCommandEvent &e)
550{
552 {
553 wxFileDialog dlg(this, _("Save mask"),
554 wxConfigBase::Get()->Read("/actualPath", wxEmptyString),
555 wxEmptyString, _("Mask files (*.msk)|*.msk|All files (*)|*"),
557 if (dlg.ShowModal() == wxID_OK)
558 {
559 wxString fn = dlg.GetPath();
560 if (fn.Right(4) != ".msk")
561 {
562 fn.Append(".msk");
563 if (wxFile::Exists(fn))
564 {
565 if (!hugin_utils::AskUserOverwrite(fn, _("Hugin"), this))
566 {
567 return;
568 }
569 }
570 };
571 wxFileName filename = fn;
572 std::ofstream maskFile(filename.GetFullPath().mb_str(HUGIN_CONV_FILENAME));
573 SaveMaskToStream(maskFile, m_pano->getImage(GetImgNr()).getSize(), m_currentMasks[m_MaskNr], GetImgNr());
574 maskFile.close();
575 };
576 }
577};
578
579void MaskEditorPanel::OnMaskLoad(wxCommandEvent &e)
580{
581 if (GetImgNr()<UINT_MAX)
582 {
583 wxFileDialog dlg(this,_("Load mask"),
584 wxConfigBase::Get()->Read("/actualPath",wxEmptyString),
585 wxEmptyString,_("Mask files (*.msk)|*.msk|All files (*)|*"),
587 if (dlg.ShowModal() != wxID_OK)
588 {
589 MainFrame::Get()->SetStatusText(_("Load mask: cancel"));
590 return;
591 }
592 wxFileName filename(dlg.GetPath());
593 std::ifstream in(filename.GetFullPath().mb_str(HUGIN_CONV_FILENAME));
594 vigra::Size2D maskImageSize;
596 LoadMaskFromStream(in, maskImageSize, loadedMasks, GetImgNr());
597 in.close();
598 if(maskImageSize.area()==0 || loadedMasks.empty())
599 {
600 hugin_utils::HuginMessageBox(wxString::Format(_("Could not parse mask from file %s."), dlg.GetPath()), _("Hugin"), wxOK | wxICON_EXCLAMATION, this);
601 return;
602 };
603 // compare image size from file with that of current image alert user
604 // if different.
606 {
607 MaskLoadDialog dlg(this);
609 if(dlg.ShowModal()!=wxID_OK)
610 {
611 // abort
612 return;
613 }
614 loadedMasks=dlg.getProcessedMask();
615 }
616 for(unsigned int i=0;i<loadedMasks.size();i++)
617 m_currentMasks.push_back(loadedMasks[i]);
618 // Update the pano with the imported masks
620 }
621};
622
623void MaskEditorPanel::OnMaskCopy(wxCommandEvent &e)
624{
626 {
627 std::ostringstream stream;
629 if (wxTheClipboard->Open())
630 {
631 // This data objects are held by the clipboard,
632 // so do not delete them in the app.
633 wxTheClipboard->SetData(new wxTextDataObject(wxString(stream.str().c_str(),wxConvLocal)));
634 wxTheClipboard->Close();
635 };
636 };
637};
638
639void MaskEditorPanel::OnMaskPaste(wxCommandEvent &e)
640{
642 {
643 if (wxTheClipboard->Open())
644 {
645 vigra::Size2D maskImageSize;
647 if (wxTheClipboard->IsSupported( wxDF_TEXT ))
648 {
649 wxTextDataObject data;
650 wxTheClipboard->GetData(data);
651 std::istringstream stream(std::string(data.GetText().mb_str()));
652 LoadMaskFromStream(stream, maskImageSize, loadedMasks, GetImgNr());
653 }
654 wxTheClipboard->Close();
655 if(maskImageSize.area()==0 || loadedMasks.empty())
656 {
657 wxBell();
658 return;
659 };
660 // compare image size from file with that of current image alert user
661 // if different.
663 {
664 MaskLoadDialog dlg(this);
666 if(dlg.ShowModal()!=wxID_OK)
667 {
668 // abort
669 return;
670 }
671 loadedMasks=dlg.getProcessedMask();
672 }
673 for(unsigned int i=0;i<loadedMasks.size();i++)
674 m_currentMasks.push_back(loadedMasks[i]);
675 // Update the pano with the imported masks
677 };
678 };
679};
680
691
692void MaskEditorPanel::OnZoom(wxCommandEvent & e)
693{
694 int posX = 0;
695 int posY = 0;
696 const wxSize ctrlSize = m_editImg->GetClientSize();
697 if (m_editImg->getScale() > 0)
698 {
699 // remember old scroll position
700 posX = (m_editImg->GetScrollPos(wxHORIZONTAL) + ctrlSize.GetWidth() / 2) / m_editImg->getScale();
701 posY = (m_editImg->GetScrollPos(wxVERTICAL) + ctrlSize.GetHeight() / 2) / m_editImg->getScale();
702 };
703 double factor;
704 switch (e.GetSelection())
705 {
706 case 0:
707 factor = 1;
708 break;
709 case 1:
710 // fit to window
711 factor = 0;
712 break;
713 case 2:
714 factor = 2;
715 break;
716 case 3:
717 factor = 1.5;
718 break;
719 case 4:
720 factor = 0.75;
721 break;
722 case 5:
723 factor = 0.5;
724 break;
725 case 6:
726 factor = 0.25;
727 break;
728 default:
729 DEBUG_ERROR("unknown scale factor");
730 factor = 1;
731 }
732 m_editImg->setScale(factor);
733 if (factor > 0)
734 {
735 m_editImg->Scroll(posX*factor - ctrlSize.GetWidth() / 2, posY*factor - ctrlSize.GetHeight() / 2);
736 };
737 if (e.GetString() == "update_selection")
738 {
739 XRCCTRL(*this, "mask_editor_choice_zoom", wxChoice)->SetSelection(e.GetSelection());
740 };
741}
742
744{
745 if(e.GetId()==XRCID("mask_editor_colour_polygon_negative"))
746 {
748 wxConfigBase::Get()->Write("/MaskEditorPanel/ColourPolygonNegative",e.GetColour().GetAsString(wxC2S_HTML_SYNTAX));
749 }
750 else
751 if(e.GetId()==XRCID("mask_editor_colour_polygon_positive"))
752 {
754 wxConfigBase::Get()->Write("/MaskEditorPanel/ColourPolygonPositive",e.GetColour().GetAsString(wxC2S_HTML_SYNTAX));
755 }
756 else
757 if(e.GetId()==XRCID("mask_editor_colour_point_selected"))
758 {
760 wxConfigBase::Get()->Write("/MaskEditorPanel/ColourPointSelected",e.GetColour().GetAsString(wxC2S_HTML_SYNTAX));
761 }
762 else
763 {
765 wxConfigBase::Get()->Write("/MaskEditorPanel/ColourPointUnselected",e.GetColour().GetAsString(wxC2S_HTML_SYNTAX));
766 }
767 m_editImg->Refresh(true);
768};
769
771{
772 unsigned int oldSelection=GetSelectedMask();
773 m_maskList->Freeze();
774 if(GetImgNr()<UINT_MAX)
775 {
776 if(!m_currentMasks.empty())
777 {
778 if(m_maskList->GetItemCount()!=m_currentMasks.size())
779 {
780 if(m_maskList->GetItemCount()<(int)m_currentMasks.size())
781 {
782 //added masks
783 for(int i=m_maskList->GetItemCount();i<(int)m_currentMasks.size();i++)
784 m_maskList->InsertItem(i,wxString::Format("%d",i));
785 }
786 else
787 {
788 //deleted masks
789 for(int i=m_maskList->GetItemCount()-1;i>=(int)m_currentMasks.size();i--)
790 m_maskList->DeleteItem(i);
791 };
792 };
793 for(unsigned int i=0;i<m_currentMasks.size();i++)
794 {
795 m_maskList->SetItem(i,1,m_maskType->GetString(m_currentMasks[i].getMaskType()));
797 m_maskList->SetItemState(i,0, wxLIST_STATE_SELECTED);
798 };
799 }
800 else
801 m_maskList->DeleteAllItems();
802 }
803 else
804 m_maskList->DeleteAllItems();
805 m_maskList->Thaw();
806 m_maskType->Enable(m_maskList->GetSelectedItemCount()>0);
807}
808
810{
811 for(unsigned int i=0;i<(unsigned int)m_maskList->GetItemCount();i++)
812 {
814 return i;
815 };
816 return UINT_MAX;
817};
818
820{
822 for (unsigned int i = 0; i < (unsigned int)m_imagesListMask->GetItemCount(); i++)
823 {
825 {
826 selectedImages.insert(i);
827 }
828 }
829 return selectedImages;
830}
831
833{
834 int colNum = e.GetColumn();
835 wxConfigBase::Get()->Write(wxString::Format("/ImagesListMask/ColumnWidth%d", colNum), m_imagesListMask->GetColumnWidth(colNum));
836}
837
839{
840 int colNum = e.GetColumn();
841 wxConfigBase::Get()->Write( wxString::Format("/MaskEditorPanel/ColumnWidth%d",colNum), m_maskList->GetColumnWidth(colNum) );
842}
843
845{
846 if(imgNr==UINT_MAX)
847 return MaskImageCtrl::ROT0;
848
849 double pitch=m_pano->getImage(imgNr).getPitch();
850 double roll=m_pano->getImage(imgNr).getRoll();
851
853 // normalize roll angle
854 while (roll > 360) roll-= 360;
855 while (roll < 0) roll += 360;
856
857 while (pitch > 180) pitch -= 360;
858 while (pitch < -180) pitch += 360;
859 const bool headOver = (pitch > 90 || pitch < -90);
860
861 if (wxConfig::Get()->Read("/CPEditorPanel/AutoRot", 1L))
862 {
863 if (roll >= 315 || roll < 45)
864 {
866 }
867 else
868 {
869 if (roll >= 45 && roll < 135)
870 {
872 }
873 else
874 {
875 if (roll >= 135 && roll < 225)
876 {
878 }
879 else
880 {
882 };
883 };
884 };
885 };
886 return rot;
887}
888
890{
891 m_editImg->setDrawingActiveMasks(e.IsChecked());
892};
893
895{
896 const HuginBase::SrcPanoImage & img = m_pano->getImage(imgNr);
897 m_cropMode=img.getCropMode();
898 m_cropRect=img.getCropRect();
899 m_autoCenterCrop=img.getAutoCenterCrop();
900
901 int dx = hugin_utils::roundi(img.getRadialDistortionCenterShift().x);
902 int dy = hugin_utils::roundi(img.getRadialDistortionCenterShift().y);
904 m_cropCenter = vigra::Point2D(img.getSize().width()/2 + dx, img.getSize().height()/2 + dy);
905
907}
908
909// transfer our state to panorama
911{
912 // set crop image options.
914 {
916 };
917 std::vector<HuginBase::SrcPanoImage> srcImgs;
919 if (m_cropLens->IsChecked())
920 {
922 for (auto i : GetSelectedImages())
923 {
924 for (auto j : lensImageVector)
925 {
926 if (set_contains(j, i))
927 {
928 std::copy(j.begin(), j.end(), std::inserter(imgs, imgs.begin()));
929 break;
930 };
931 };
932 };
933 }
934 else
935 {
936 imgs = GetSelectedImages();
937 }
938 for (auto i:imgs)
939 {
941 img.setCropRect(m_cropRect);
942 img.setAutoCenterCrop(m_autoCenterCrop);
943 srcImgs.push_back(img);
944 };
945
948 );
949}
950
956
957// redraw display with new information
959{
960 DEBUG_TRACE("")
962 m_left_textctrl->SetValue(wxString::Format("%d",m_cropRect.left()));
963 m_right_textctrl->SetValue(wxString::Format("%d",m_cropRect.right()));
964 m_top_textctrl->SetValue(wxString::Format("%d",m_cropRect.top()));
965 m_bottom_textctrl->SetValue(wxString::Format("%d",m_cropRect.bottom()));
966}
967
968
969void MaskEditorPanel::OnSetTop(wxCommandEvent & e)
970{
971 long val;
972 if (m_top_textctrl->GetValue().ToLong(&val))
973 {
974 m_cropRect.setUpperLeft(vigra::Point2D(m_cropRect.left(), val));
976 {
977 CenterCrop();
979 };
980 UpdateCrop();
981 }
982 else
983 {
984 wxLogError(_("Please enter a valid number"));
985 };
986};
987
988void MaskEditorPanel::OnSetBottom(wxCommandEvent & e)
989{
990 long val;
991 if (m_bottom_textctrl->GetValue().ToLong(&val))
992 {
993 m_cropRect.setLowerRight(vigra::Point2D(m_cropRect.right(), val));
995 {
996 CenterCrop();
998 }
999 UpdateCrop();
1000 }
1001 else
1002 {
1003 wxLogError(_("Please enter a valid number"));
1004 };
1005};
1006
1007void MaskEditorPanel::OnSetLeft(wxCommandEvent & e)
1008{
1009 long val = 0;
1010 if (m_left_textctrl->GetValue().ToLong(&val))
1011 {
1012 m_cropRect.setUpperLeft(vigra::Point2D(val, m_cropRect.top()));
1013 if (m_autoCenterCrop)
1014 {
1015 CenterCrop();
1017 }
1018 UpdateCrop();
1019 }
1020 else
1021 {
1022 wxLogError(_("Please enter a valid number"));
1023 };
1024};
1025
1026void MaskEditorPanel::OnSetRight(wxCommandEvent & e)
1027{
1028 long val = 0;
1029 if (m_right_textctrl->GetValue().ToLong(&val))
1030 {
1031 m_cropRect.setLowerRight(vigra::Point2D(val, m_cropRect.bottom()));
1032 if (m_autoCenterCrop)
1033 {
1034 CenterCrop();
1036 };
1037 UpdateCrop();
1038 }
1039 else
1040 {
1041 wxLogError(_("Please enter a valid number"));
1042 };
1043};
1044
1045void MaskEditorPanel::OnResetButton(wxCommandEvent & e)
1046{
1047 // suitable defaults.
1048 m_cropRect.setUpperLeft(vigra::Point2D(0,0));
1049 m_cropRect.setLowerRight(vigra::Point2D(0,0));
1050 m_autoCenterCrop = true;
1053 UpdateCrop();
1054}
1055
1056void MaskEditorPanel::OnAutoCenter(wxCommandEvent & e)
1057{
1058 m_autoCenterCrop = e.IsChecked();
1059 if (m_autoCenterCrop)
1060 {
1061 CenterCrop();
1063 };
1064 UpdateCrop();
1065}
1066
1068{
1069 vigra::Diff2D d(m_cropRect.width()/2, m_cropRect.height() / 2);
1070 m_cropRect.setUpperLeft( m_cropCenter - d);
1071 m_cropRect.setLowerRight( m_cropCenter + d);
1072}
1073
1075{
1076 const size_t imgNr = GetImgNr();
1077#ifdef __WXMSW__
1079#else
1080 // updating the style flags invalidates the items in the list box
1081 // so first delete all items, then change style and add them again
1082 m_imagesListMask->Freeze();
1083 m_imagesListMask->DeleteAllItems();
1085 for (size_t i = 0; i < m_pano->getNrOfImages(); ++i)
1086 {
1087 m_imagesListMask->InsertItem(m_imagesListMask->GetItemCount(), wxEmptyString);
1088 UpdateImage(i);
1089 };
1090 m_imagesListMask->Thaw();
1091#endif
1093}
1094
1096{
1097 if(m_maskCropCtrl==NULL)
1098 {
1099 return;
1100 };
1101 if(m_maskCropCtrl->GetSelection()==0)
1102 {
1103 m_maskMode=true;
1104 SetSingleSelection(true);
1105 m_editImg->SetMaskMode(true);
1106 }
1107 else
1108 {
1109 m_maskMode=false;
1110 SetSingleSelection(false);
1111 m_editImg->SetMaskMode(false);
1113 };
1114 m_editImg->Refresh();
1115};
1116
1118{
1119 m_maskCropCtrl->SetSelection(1);
1122}
1123
1124IMPLEMENT_DYNAMIC_CLASS(MaskEditorPanel, wxPanel)
1125
1127 : wxXmlResourceHandler()
1128{
1130}
1131
1133{
1135
1136 cp->Create(m_parentAsWindow,
1137 GetID(),
1138 GetPosition(), GetSize(),
1139 GetStyle("style"),
1140 GetName());
1141
1142 SetupWindow(cp);
1143
1144 return cp;
1145}
1146
1148{
1149 return IsOfClass(node, "MaskEditorPanel");
1150}
1151
1152IMPLEMENT_DYNAMIC_CLASS(MaskEditorPanelXmlHandler, wxXmlResourceHandler)
options getSize().area()) int wxCALLBACK SortFieldOfViewAscending(wxIntPtr item1
void SelectSingleImage(wxListCtrl *list, unsigned int imgNr)
Definition of mask load dialog.
UIntSetVector getPartsSet() const
return a vector which contains a HuginBase::UIntSet for each group with the corresponding images numb...
Make an ImageVariableGroup for lenses and other common concepts.
void update()
Update part numbers for each variable group.
ConstImageVariableGroup & getLenses()
Get the ImageVariableGroup representing the group of lens variables.
MaskType
enumeration with type of possible masks
Definition Mask.h:57
Model for a panorama.
Definition Panorama.h:153
const SrcPanoImage & getImage(std::size_t nr) const
get a panorama image, counting starts with 0
Definition Panorama.h:211
void addObserver(PanoramaObserver *o)
add a panorama observer.
SrcPanoImage getSrcImage(unsigned imgNr) const
get a description of a source image
bool removeObserver(PanoramaObserver *observer)
remove a panorama observer.
std::size_t getNrOfImages() const
number of images.
Definition Panorama.h:205
All variables of a source image.
bool isCircularCrop() const
returns true, if projection requires cicular crop
hugin_utils::FDiff2D getRadialDistortionCenter() const
bool hasMasks() const
returns true, if image has masks associated
static MainFrame * Get()
hack.. kind of a pseudo singleton...
xrc handler for handling mask editor panel
virtual wxObject * DoCreateResource()
virtual bool CanHandle(wxXmlNode *node)
mask editor panel.
void OnMaskColumnWidthChange(wxListEvent &e)
called, when column with of mask list box was changed
void UpdateCropFromImage()
updates the displayed crop in the text boxes (for dragging)
unsigned int m_MaskNr
void UpdateImage(size_t imgNr)
update the list box for the given imgNr
void OnMaskCopy(wxCommandEvent &e)
called when user wants to copy a mask to clipboard
wxListCtrl * m_imagesListMask
void OnMaskTypeChange(wxCommandEvent &e)
handler when mask type was changed
void UpdateMask()
called when mask where changed in MaskImageCtrl
void OnAutoCenter(wxCommandEvent &e)
void OnImageListChar(wxKeyEvent &e)
key handler for images list
void OnMaskAdd(wxCommandEvent &e)
called when user wants to create new polygon
void Init(HuginBase::Panorama *pano)
void OnSetRight(wxCommandEvent &e)
void DisplayCrop(int imgNr)
copies the crop information from the Panorama object to GUI
HuginBase::SrcPanoImage::CropMode m_cropMode
void AddMask()
called when new mask added in MaskImageCtrl
void OnSetLeft(wxCommandEvent &e)
void OnMaskDelete(wxCommandEvent &e)
called when user wants to delete active mask
wxCheckBox * m_autocenter_cb
void SelectMask(unsigned int newMaskNr)
selects the mask with index newMaskNr in the listbox
void UpdateMaskList(bool restoreSelection=false)
updates the display after another image has been selected.
void SwitchToCropMode()
switches the controls to crop mode
vigra::Rect2D m_cropRect
void setMask(unsigned int maskNr)
sets active mask number, set to UINT_MAX, if no mask is currently editing
wxTextCtrl * m_left_textctrl
void OnMaskPaste(wxCommandEvent &e)
called when user wants to paste a mask from clipboard
void OnImageSelect(wxListEvent &e)
called when user selected another image
wxCheckBox * m_cropLens
wxTextCtrl * m_top_textctrl
void OnShowActiveMasks(wxCommandEvent &e)
event handler for changing option if active masks should be drawn
wxListCtrl * m_maskList
const HuginBase::UIntSet GetSelectedImages()
return the currently selected image(s)
void OnSetBottom(wxCommandEvent &e)
unsigned int GetSelectedMask()
return index of currently selected masks, return UINT_MAX if no mask is selected
void panoramaChanged(HuginBase::Panorama &pano)
called when the panorama changes and we should update our display
void OnSetTop(wxCommandEvent &e)
void SetSingleSelection(bool singleSelMode)
void panoramaImagesChanged(HuginBase::Panorama &pano, const HuginBase::UIntSet &imgNr)
notifies about changes to images
MaskImageCtrl::ImageRotation GetRot(const unsigned int imgNr)
determines, if the image should be rotated for display
void UpdateCrop(bool updateFromImgCtrl=false)
updated the crop in the Panorama object with the current values from GUI
void OnZoom(wxCommandEvent &e)
sets the actual zoom factor
HuginBase::Panorama * m_pano
void OnMaskLoad(wxCommandEvent &e)
called when user wants to load a mask into the selected image
wxTextCtrl * m_bottom_textctrl
HuginBase::MaskPolygon::MaskType m_defaultMaskType
void OnImagesColumnWidthChange(wxListEvent &e)
called, when column with of images list box was changed
HuginBase::MaskPolygonVector m_currentMasks
HuginBase::ConstStandardImageVariableGroups * m_imageGroups
virtual ~MaskEditorPanel()
dtor.
void UpdateCropDisplay()
update GUI display
void OnColourChanged(wxColourPickerEvent &e)
event handler for changing colours
void OnModeChanged(wxNotebookEvent &e)
void OnMaskSave(wxCommandEvent &e)
called when user wants to save active mask
wxChoice * m_maskType
wxTextCtrl * m_right_textctrl
wxNotebook * m_maskCropCtrl
MaskImageCtrl * m_editImg
bool Create(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxTAB_TRAVERSAL, const wxString &name="panel")
vigra::Point2D m_cropCenter
void setImage(unsigned int imgNr, bool updateListSelection=false)
sets the image, which is currently edited
void OnMaskSelect(wxListEvent &e)
called when user selected another mask
void OnResetButton(wxCommandEvent &e)
mask editor
void setActiveMask(unsigned int newMask, bool doUpdate=true)
mark mask with image as beeing editing
void SetUserColourPointUnselected(wxColour newColour)
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
double getScale()
return scale factor, 0 for autoscale
void SetUserColourPolygonPositive(wxColour newColour)
void setScale(double factor)
set the scaling factor for mask editing display.
HuginBase::MaskPolygonVector getNewMask() const
returns the vector of all mask (including new created mask)
void setDrawingActiveMasks(bool newDrawActiveMasks)
set if active masks should be drawn
void setNewMasks(HuginBase::MaskPolygonVector newMasks, HuginBase::MaskPolygonVector masksToDraw)
updates masks for currently selected image
void startNewPolygon()
starts creating a new polygon
ImageRotation
image rotation.
void Init(MaskEditorPanel *parent)
vigra::Rect2D getCrop()
returns the current crop rect
void SetMaskMode(bool newMaskMode)
sets the control to mask (newMaskMode=true) or crop (newMaskMode=false) mode
ImageRotation getCurrentRotation()
returns the current rotation of displayed image
void selectAllMarkers()
select all points of active mask
void SetUserColourPointSelected(wxColour newColour)
void setCrop(HuginBase::SrcPanoImage::CropMode newCropMode, vigra::Rect2D newCropRect, bool isCentered, hugin_utils::FDiff2D center, bool isCircleCrop)
updates the crop mode and crop rect
void SetUserColourPolygonNegative(wxColour newColour)
sets the colour for different parts
Dialog for loading masks.
void addCommand(PanoCommand *command, bool execute=true)
Adds a command to the history.
static GlobalCmdHist & getInstance()
update mask for given image
Handle EVT_KILL_FOCUS and convert it to a EVT_TEXT_ENTER event.
#define HUGIN_MASK_COLOUR_POINT_SELECTED
#define HUGIN_MASK_COLOUR_POINT_UNSELECTED
#define HUGIN_MASK_COLOUR_POLYGON_NEGATIVE
#define HUGIN_MASK_COLOUR_POLYGON_POSITIVE
implementation of huginApp Class
#define HUGIN_CONV_FILENAME
Definition platform.h:40
#define DEBUG_ERROR(msg)
Definition utils.h:76
#define DEBUG_ASSERT(cond)
Definition utils.h:80
#define DEBUG_TRACE(msg)
Definition utils.h:67
std::vector< MaskPolygon > MaskPolygonVector
Definition Mask.h:150
std::vector< UIntSet > UIntSetVector
std::set< unsigned int > UIntSet
TDiff2D< double > FDiff2D
Definition hugin_math.h:162
bool AskUserOverwrite(const wxString &filename, const wxString &caption, wxWindow *parent)
ask user if the given file should be overwritten, return true if the user confirmed the overwritting
Definition wxutils.cpp:233
int HuginMessageBox(const wxString &message, const wxString &caption, int style, wxWindow *parent)
Definition wxutils.cpp:176
int roundi(T x)
Definition hugin_math.h:73
include file for the hugin project
include file for the hugin project
bool set_contains(const _Container &c, const typename _Container::key_type &key)
Definition stl_utils.h:74
std::vector< deghosting::BImagePtr > threshold(const std::vector< deghosting::FImagePtr > &inputImages, const double threshold, const uint16_t flags)
Threshold function used for creating alpha masks for images.
Definition threshold.h:41