Skip to content

Commit

Permalink
Add option to right-click on peak and fix it to DRF FWHM.
Browse files Browse the repository at this point in the history
Now when you right-click on a peak you can select "Use DRF FWHM" to set, and fix, the peaks FWHM to the DRFs value, and then refit the peak.  If no FWHM for the peak is defined, then the `MakeFwhmForDrf` tool will be created to allow making one.
The undo/redo around this probably needs a little more testing, as well as the actual functionality as well.
  • Loading branch information
wcjohns committed Oct 27, 2023
1 parent 67a47e2 commit 974d899
Show file tree
Hide file tree
Showing 5 changed files with 214 additions and 0 deletions.
2 changes: 2 additions & 0 deletions InterSpec/InterSpec.h
Original file line number Diff line number Diff line change
Expand Up @@ -1009,6 +1009,7 @@ class InterSpec : public Wt::WContainerWidget

void peakEditFromRightClick();
void refitPeakFromRightClick();
void refitPeakWithDrfFwhm();
void deletePeakFromRightClick();
void addPeakFromRightClick();
void makePeakFromRightClickHaveOwnContinuum();
Expand Down Expand Up @@ -1382,6 +1383,7 @@ class InterSpec : public Wt::WContainerWidget
kPeakEdit,
kRefitPeak,
kRefitROI,
kRefitPeakWithDrfFwhm,
kChangeNuclide,
kChangeContinuum,
kDeletePeak,
Expand Down
2 changes: 2 additions & 0 deletions InterSpec/MakeFwhmForDrf.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@

#include <Wt/WContainerWidget>

#include "InterSpec/AuxWindow.h"

class PeakDef;
class AuxWindow;
class InterSpec;
Expand Down
6 changes: 6 additions & 0 deletions InterSpec/PeakSearchGuiUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@ void assign_srcs_from_ref_lines( const std::shared_ptr<const SpecUtils::Measurem
*/
void refit_peaks_from_right_click( InterSpec * const interspec, const double rightClickEnergy );

/** Sets all peaks in the ROI pointed to by the passed in energy, to the FWHM specified by the DRF, then refits the peaks.
Assumes you are in the Wt app primary thread.
*/
void refit_peaks_with_drf_fwhm( InterSpec * const interspec, const double rightClickEnergy );

/** Changes the continuum type and causes a refit of ROI.
@param interspec The InterSpec instance to work with - it is assumed this function is being called from that apps primary thread.
Expand Down
14 changes: 14 additions & 0 deletions src/InterSpec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1038,6 +1038,10 @@ InterSpec::InterSpec( WContainerWidget *parent )
m_rightClickMenutItems[i] = m_rightClickMenu->addMenuItem( "Refit Peak" );
m_rightClickMenutItems[i]->triggered().connect( this, &InterSpec::refitPeakFromRightClick );
break;
case kRefitPeakWithDrfFwhm:
m_rightClickMenutItems[i] = m_rightClickMenu->addMenuItem( "Use DRF FWHM" );
m_rightClickMenutItems[i]->triggered().connect( this, &InterSpec::refitPeakWithDrfFwhm );
break;
case kRefitROI:
m_rightClickMenutItems[i] = m_rightClickMenu->addMenuItem( "Refit ROI" );
m_rightClickMenutItems[i]->triggered().connect( this, &InterSpec::refitPeakFromRightClick );
Expand Down Expand Up @@ -1804,6 +1808,12 @@ void InterSpec::refitPeakFromRightClick()
}//void refitPeakFromRightClick()


void InterSpec::refitPeakWithDrfFwhm()
{
PeakSearchGuiUtils::refit_peaks_with_drf_fwhm( this, m_rightClickEnergy );
}//void InterSpec::refitPeakWithDrfFwhm()


void InterSpec::addPeakFromRightClick()
{
UndoRedoManager::PeakModelChange peak_undo_creator;
Expand Down Expand Up @@ -2519,6 +2529,10 @@ void InterSpec::handleRightClick( double energy, double counts,
m_rightClickMenutItems[i]->setHidden( !peak->gausPeak() || npeaksInRoi<2 );
break;

case kRefitPeakWithDrfFwhm:
m_rightClickMenutItems[i]->setHidden( !peak->gausPeak() );
break;

case kShareContinuumWithLeftPeak:
{
iter = std::find( peaks->begin(), peaks->end(), peak );
Expand Down
190 changes: 190 additions & 0 deletions src/PeakSearchGuiUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,13 @@
#include "InterSpec/InterSpecApp.h"
#include "InterSpec/SpectrumChart.h"
#include "InterSpec/WarningWidget.h"
#include "InterSpec/MakeFwhmForDrf.h"
#include "InterSpec/PeakInfoDisplay.h"
#include "InterSpec/UndoRedoManager.h"
#include "InterSpec/SpectrumDataModel.h"
#include "InterSpec/PeakSearchGuiUtils.h"
#include "InterSpec/DecayDataBaseServer.h"
#include "InterSpec/DetectorPeakResponse.h"
#include "InterSpec/ReferencePhotopeakDisplay.h"

using namespace Wt;
Expand Down Expand Up @@ -2544,7 +2546,195 @@ void refit_peaks_from_right_click( InterSpec * const interspec, const double rig
}
}//void refit_peaks_from_right_click(...)


void refit_peaks_with_drf_fwhm( InterSpec * const interspec, const double rightClickEnergy )
{
try
{
PeakModel * const model = interspec->peakModel();
const shared_ptr<const SpecUtils::Measurement> data = interspec->displayedHistogram(SpecUtils::SpectrumType::Foreground);
const shared_ptr<const SpecMeas> foreground = interspec->measurment( SpecUtils::SpectrumType::Foreground);


if( !model || !data || !foreground ) //shouldnt ever happen
{
passMessage( "No data loaded to refit peaks", WarningWidget::WarningMsgInfo );
return;
}

shared_ptr<const DetectorPeakResponse> drf = foreground->detector();
if( !drf || !drf->hasResolutionInfo() )
{
const char *title = "FWHM Information is Needed";
string content;
if( drf)
content = "<div>Current detector response does not contain FWHM info.</div>";
else
content = "<div>No detector response is selected.</div>";
content += "<div>Would you like to fit FWHM info from current spectra?</div>";
SimpleDialog *msg = new SimpleDialog( title, content );
// Setting object name of the SimpleDialog causes a javascript error - so we'll set
// the SimpleDialog contents name, and then get the dialog from that.
// This is all for undo/redo support, so we dont have to store a pointer to the dialog
// somewhere.
msg->contents()->setObjectName( "AskToFitFwhmDialog" );
msg->rejectWhenEscapePressed();
WPushButton *yes_btn = msg->addButton( "Yes" );
WPushButton *no_btn = msg->addButton( "No" );

no_btn->clicked().connect( std::bind([interspec,rightClickEnergy](){
auto undo = [interspec,rightClickEnergy](){
refit_peaks_with_drf_fwhm( interspec, rightClickEnergy );
};
auto redo = [](){
auto w = dynamic_cast<WContainerWidget *>( wApp->findWidget("AskToFitFwhmDialog") );
WWidget *p = w ? w->parent() : nullptr;
WWidget *pp = p ? p->parent() : nullptr;
WWidget *ppp = pp ? pp->parent() : nullptr;
SimpleDialog *d = dynamic_cast<SimpleDialog *>( ppp );

if( d )
d->done(Wt::WDialog::DialogCode::Accepted);
wApp->doJavaScript( "$('.Wt-dialogcover').hide();" );
};

UndoRedoManager *undoManager = interspec->undoRedoManager();
if( undoManager && undoManager->canAddUndoRedoNow() )
undoManager->addUndoRedoStep( undo, redo, "Cancel refit peak with DRF FWHM." );
} ) );


yes_btn->clicked().connect( std::bind( [interspec,rightClickEnergy](){
MakeFwhmForDrfWindow *window = interspec->fwhmFromForegroundWindow(true);
if( window )
{
window->tool()->updatedDrf().connect(
boost::bind( &refit_peaks_with_drf_fwhm, interspec, rightClickEnergy
) );
}//if( window )

auto undo = [interspec,rightClickEnergy](){
interspec->deleteFwhmFromForegroundWindow();
refit_peaks_with_drf_fwhm( interspec, rightClickEnergy );
};
auto redo = [interspec,rightClickEnergy](){
auto w = dynamic_cast<WContainerWidget *>( wApp->findWidget("AskToFitFwhmDialog") );
WWidget *p = w ? w->parent() : nullptr;
WWidget *pp = p ? p->parent() : nullptr;
WWidget *ppp = pp ? pp->parent() : nullptr;
SimpleDialog *d = dynamic_cast<SimpleDialog *>( ppp );
if( d )
d->done(Wt::WDialog::DialogCode::Accepted);
wApp->doJavaScript( "$('.Wt-dialogcover').hide();" );

MakeFwhmForDrfWindow *window = interspec->fwhmFromForegroundWindow(true);
if( window )
{
window->tool()->updatedDrf().connect(
boost::bind( &refit_peaks_with_drf_fwhm, interspec, rightClickEnergy
) );
}//if( window )
};

UndoRedoManager *undoManager = interspec->undoRedoManager();
if( undoManager && undoManager->canAddUndoRedoNow() )
undoManager->addUndoRedoStep( undo, redo, "Start fit FWHM function." );
} ) );

return;
}//if( !drf || !drf->hasResolutionInfo() )

shared_ptr<const PeakDef> peak = model->nearestPeak( rightClickEnergy );
if( !peak )
{
passMessage( "There was no peak to refit with fixed FWHM", WarningWidget::WarningMsgInfo );
return;
}

UndoRedoManager::PeakModelChange peak_undo_creator;

vector<PeakDef> inputPeak, fixedPeaks, outputPeak;
const vector<shared_ptr<const PeakDef>> peaksInRoi = model->peaksSharingRoi( peak );
const vector<shared_ptr<const PeakDef>> peaksNotInRoi = model->peaksNotSharingRoi( peak );

assert( peaksInRoi.size() >= 1 );

for( const auto &m : peaksInRoi )
{
PeakDef p = *m;

p.setSigma( drf->peakResolutionSigma(p.mean()) );
p.setSigmaUncert( 0.0 );
//p.setSigmaUncert( ... DRF FWHM uncert not implemented ... )
p.setFitFor( PeakDef::CoefficientType::Sigma, false );

inputPeak.push_back( p );
}

for( const auto &m : peaksNotInRoi )
fixedPeaks.push_back( *m );

std::sort( inputPeak.begin(), inputPeak.end(), &PeakDef::lessThanByMean );

if( inputPeak.size() > 1 )
{
const shared_ptr<const DetectorPeakResponse> &detector = foreground->detector();
const PeakShrdVec result = refitPeaksThatShareROI( data, detector, peaksInRoi, 0.25 );

if( result.size() == inputPeak.size() )
{
for( size_t i = 0; i < result.size(); ++i )
fixedPeaks.push_back( *result[i] );
std::sort( fixedPeaks.begin(), fixedPeaks.end(), &PeakDef::lessThanByMean );
model->setPeaks( fixedPeaks );
return;
}else
{
cerr << "refit_peaks_with_drf_fwhm was not successful" << endl;
}//if( result.size() == inputPeak.size() ) / else
}//if( inputPeak.size() > 1 )


// const double lowE = peak->mean() - 0.1;
// const double upE = peak->mean() + 0.1;
const double lowE = inputPeak.front().mean() - 0.1;
const double upE = inputPeak.back().mean() + 0.1;
const double ncausalitysigma = 0.0;
const double stat_threshold = -1000.0;
const double hypothesis_threshold = -1000.0;

const bool isRefit = false;
outputPeak = fitPeaksInRange( lowE, upE, ncausalitysigma, stat_threshold,
hypothesis_threshold, inputPeak, data,
fixedPeaks, isRefit );
if( outputPeak.size() != inputPeak.size() )
{
WStringStream msg;
msg << "Failed to refit peak (became insignificant), from "
<< int(inputPeak.size()) << " to " << int(outputPeak.size()) << " peaks";
passMessage( msg.str(), WarningWidget::WarningMsgInfo );
return;
}//if( outputPeak.size() != 1 )

if( inputPeak.size() > 1 )
{
fixedPeaks.insert( fixedPeaks.end(), outputPeak.begin(), outputPeak.end() );
std::sort( fixedPeaks.begin(), fixedPeaks.end(), &PeakDef::lessThanByMean );
model->setPeaks( fixedPeaks );
}else
{
assert( !outputPeak.empty() );

model->updatePeak( peak, outputPeak[0] );
}//if( inputPeak.size() > 1 )
}catch( std::exception &e )
{
passMessage( "Sorry, error encountered refitting ROI with fixed FWHM.", WarningWidget::WarningMsgInfo );
cerr << "Error encountered refitting ROI: " << e.what() << endl;
}
}//void refit_peaks_with_drf_fwhm( InterSpec * const interspec, const double rightClickEnergy )


void change_continuum_type_from_right_click( InterSpec * const interspec,
const double rightClickEnergy,
const int continuum_type )
Expand Down

0 comments on commit 974d899

Please sign in to comment.