Skip to content

Commit

Permalink
Add chart to Currie detection limit, "more info" dialog.
Browse files Browse the repository at this point in the history
Work in progress, but getting most of the pieces together.
  • Loading branch information
wcjohns committed May 28, 2024
1 parent 8339866 commit 59f6f41
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 21 deletions.
38 changes: 34 additions & 4 deletions InterSpec/D3SpectrumDisplayDiv.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ class D3SpectrumDisplayDiv : public Wt::WContainerWidget


//setDisplayScaleFactor(): set the effective live time of 'spectrum_type'
// to be 'sf' timess the live time of 'spectrum_type'.
// to be 'sf' times the live time of 'spectrum_type'.
void setDisplayScaleFactor( const float sf,
const SpecUtils::SpectrumType spectrum_type );

Expand Down Expand Up @@ -232,9 +232,22 @@ class D3SpectrumDisplayDiv : public Wt::WContainerWidget
void setSearchEnergies( const std::vector<std::pair<double,double>> &energy_windows );

bool removeDecorativeHighlightRegion( size_t regionid );

/** How the highlight region is to be drawn. */
enum class HighlightRegionFill : int
{
/** Fills the full y-range of the chart. */
Full,

/** Fills just the y-range of the chart, below the data. */
BelowData
};//enum class HighlightRegionFill

size_t addDecorativeHighlightRegion( const float lowerx,
const float upperx,
const Wt::WColor &color );
const Wt::WColor &color,
const HighlightRegionFill fillType,
const Wt::WString &txt );
void removeAllDecorativeHighlightRegions();

//For the case of auto-ranging x-axis, the below _may_ return 0 when auto
Expand Down Expand Up @@ -388,11 +401,28 @@ class D3SpectrumDisplayDiv : public Wt::WContainerWidget
bool m_showYAxisScalers;

std::vector<std::pair<double,double> > m_searchEnergies;
std::vector<SpectrumChart::HighlightRegion> m_highlights;

//HighlightRegion is what is used to overlay a color on the chart.
// The "Energy Range Sum" tool uses them, as well as the "Nuclide Search"
// tool.
// These regions are added and removed by addDecorativeHighlightRegion(...), and
// removeDecorativeHighlightRegion(...). When a region is added, a
// unique ID is returned, so that region can be removed, without
// effecting other decorative regions.
struct HighlightRegion
{
float lowerx, upperx;
size_t hash;
Wt::WColor color;

HighlightRegionFill fill_type;
Wt::WString display_txt;
};//HighlightRegion

std::vector<HighlightRegion> m_highlights;

std::map<SpectrumChart::PeakLabels,bool> m_peakLabelsToShow;

// Make all these WStrings, and initialize to their default WString::tr(...) strings, and then account for letting them be empty.
Wt::WString m_xAxisTitle;
Wt::WString m_yAxisTitle;
Wt::WString m_yAxisTitleMulti;
Expand Down
2 changes: 1 addition & 1 deletion InterSpec/SpectrumChart.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class SpectrumChart : public Wt::Chart::WCartesianChart
{
Occupied,
NotOccupied
};//enum HighlightRegionType
};//enum OccupancyRegionType

//HighlightRegion is what is used to overlay a color over the chart, and are
// used for roughly two different usages:
Expand Down
8 changes: 8 additions & 0 deletions InterSpec_resources/DetectionLimitTool.css
Original file line number Diff line number Diff line change
Expand Up @@ -263,3 +263,11 @@
border-collapse: separate;
border-spacing: 5px 5px;
}


.MdaCurrieChart
{
width: 100%;
height: 200px;
margin-bottom: 15px;
}
39 changes: 30 additions & 9 deletions src/D3SpectrumDisplayDiv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -362,10 +362,6 @@ void D3SpectrumDisplayDiv::defineJavaScript()

callJavaScriptMember( "resizeObserver.observe", jsRef() );

//updateReferncePhotoPeakLines();

//setHighlightRegionsToClient();

setSearchEnergies( m_searchEnergies );

if( !m_xRangeChangedJS )
Expand Down Expand Up @@ -808,6 +804,8 @@ void D3SpectrumDisplayDiv::refresh()

if( isRendered() )
doJavaScript( m_jsgraph + ".setLocalizations( " + localizedStringsJson() + ",\n false);" );

m_renderFlags |= UpdateHighlightRegions;
}//void refresh();


Expand Down Expand Up @@ -1313,19 +1311,26 @@ void D3SpectrumDisplayDiv::removeAllDecorativeHighlightRegions()

size_t D3SpectrumDisplayDiv::addDecorativeHighlightRegion( const float lowerx,
const float upperx,
const Wt::WColor &color )
const Wt::WColor &color,
const HighlightRegionFill fillType,
const Wt::WString &txt )
{
SpectrumChart::HighlightRegion region;
D3SpectrumDisplayDiv::HighlightRegion region;
region.lowerx = lowerx;
region.upperx = upperx;
region.color = color;
region.fill_type = fillType;
region.display_txt = txt;
region.hash = 0;

boost::hash_combine( region.hash, lowerx );
boost::hash_combine( region.hash, upperx );
boost::hash_combine( region.hash, color.red() );
boost::hash_combine( region.hash, color.green() );
boost::hash_combine( region.hash, color.blue() );
boost::hash_combine( region.hash, color.alpha() );
boost::hash_combine( region.hash, static_cast<int>(fillType) );
boost::hash_combine( region.hash, hash<string>()(txt.toUTF8()) );

if( region.hash <= 2 )
region.hash += 3;
Expand Down Expand Up @@ -1354,14 +1359,30 @@ void D3SpectrumDisplayDiv::setHighlightRegionsToClient()
jsstrm << "([";
for( size_t i = 0; i < m_highlights.size(); ++i )
{
const SpectrumChart::HighlightRegion &region = m_highlights[i];
const D3SpectrumDisplayDiv::HighlightRegion &region = m_highlights[i];
jsstrm << std::string(i ? ",{" : "{")
<< "lowerEnergy:" << static_cast<double>(region.lowerx)
<< ", upperEnergy:" << static_cast<double>(region.upperx)
<< ", fill: 'rgba(" << region.color.red() << "," << region.color.green() //bug in Wt 3.3.4 means need to manually write out rgba()
<< "," << region.color.blue()
<< "," << (region.color.alpha()/255.0) << ")'"
<< ", hash:" << std::to_string(region.hash)
<< "," << (region.color.alpha()/255.0) << ")'";

switch( region.fill_type )
{
case HighlightRegionFill::Full:
// We'll skip putting this in - previous to 20240527 this field didnt exist, so we'll keep compatiblity
//jsstrm << ", drawRegion: 'All'";
break;

case HighlightRegionFill::BelowData:
jsstrm << ", drawRegion: 'BelowData'";
break;
}//switch( region.fill_type )

if( !region.display_txt.empty() )
jsstrm << ", text: " << region.display_txt.jsStringLiteral('\'');

jsstrm << ", hash:" << std::to_string(region.hash)
<< "}";
}//for( loop over m_highlights )

Expand Down
2 changes: 1 addition & 1 deletion src/DetectionLimitCalc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -884,7 +884,7 @@ CurieMdaResult currie_mda_calc( const CurieMdaInput &input )
// Arbitrary chosen precision tests, for development
const double eq_dens = fabs(peak_cont_eq_integral - peak_cont_sum);
assert( (eq_dens < 0.1)
|| (eq_dens < 1.0E-5*std::max(peak_cont_eq_integral, peak_cont_sum)) );
|| (eq_dens < 1.0E-4*std::max(peak_cont_eq_integral, peak_cont_sum)) );

const double eq_diff = fabs(peak_cont_eq_integral - peak_cont_sum);
assert( eq_diff < 0.1 || eq_diff < 1.0E-5*std::max(peak_cont_eq_integral, peak_cont_sum) );
Expand Down
83 changes: 78 additions & 5 deletions src/DetectionLimitTool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -960,7 +960,70 @@ class MdaPeakRow : public WContainerWidget
const DetectionLimitCalc::CurieMdaInput input = currieInput();
const DetectionLimitCalc::CurieMdaResult result = DetectionLimitCalc::currie_mda_calc( input );


const double lower_lower_energy = m_input.measurement->gamma_channel_lower( result.first_lower_continuum_channel );
const double lower_upper_energy = m_input.measurement->gamma_channel_lower( result.first_upper_continuum_channel );
const double upper_lower_energy = m_input.measurement->gamma_channel_upper( result.last_lower_continuum_channel );
const double upper_upper_energy = m_input.measurement->gamma_channel_upper( result.last_upper_continuum_channel );

// Add chart
InterSpec *viewer = InterSpec::instance();
assert( viewer );
D3SpectrumDisplayDiv *chart = new D3SpectrumDisplayDiv( dialog->contents() );
chart->addStyleClass( "MdaCurrieChart" );
chart->setXAxisTitle( "" );
chart->setYAxisTitle( "", "" );
shared_ptr<const SpecUtils::Measurement> hist = viewer->displayedHistogram(SpecUtils::SpectrumType::Foreground);
chart->setData( hist, true );
chart->setYAxisLog( false );
chart->applyColorTheme( viewer->getColorTheme() );
viewer->colorThemeChanged().connect( boost::bind( &D3SpectrumDisplayDiv::applyColorTheme, chart, boost::placeholders::_1 ) );
chart->disableLegend();
const double dx = upper_upper_energy - lower_lower_energy;
chart->setXAxisRange( lower_lower_energy - 0.5*dx, upper_upper_energy + 0.5*dx );
chart->setShowPeakLabel( SpectrumChart::PeakLabels::kShowPeakUserLabel, true );

//TODO: set no interaction, and implement drawing the various areas...

const int lower_ndec = 1 + static_cast<int>( std::ceil( fabs( std::log10( fabs(result.lower_continuum_counts_sum) ) ) ) );
const int mid_ndec = 1 + static_cast<int>( std::ceil( fabs( std::log10( fabs(result.peak_region_counts_sum) ) ) ) );
const int upper_ndec = 1 + static_cast<int>( std::ceil( fabs( std::log10( fabs(result.upper_continuum_counts_sum) ) ) ) );
const string lower_txt = SpecUtils::printCompact( result.lower_continuum_counts_sum, lower_ndec );
const string mid_txt = SpecUtils::printCompact( result.peak_region_counts_sum, mid_ndec );
const string upper_txt = SpecUtils::printCompact( result.upper_continuum_counts_sum, upper_ndec );

shared_ptr<const ColorTheme> theme = viewer->getColorTheme();

chart->addDecorativeHighlightRegion( lower_lower_energy, upper_lower_energy,
theme->timeHistoryBackgroundHighlight,
D3SpectrumDisplayDiv::HighlightRegionFill::BelowData,
lower_txt );

chart->addDecorativeHighlightRegion( upper_lower_energy, lower_upper_energy,
theme->timeHistoryForegroundHighlight,
D3SpectrumDisplayDiv::HighlightRegionFill::BelowData,
mid_txt );

chart->addDecorativeHighlightRegion( lower_upper_energy, upper_upper_energy,
theme->timeHistoryBackgroundHighlight,
D3SpectrumDisplayDiv::HighlightRegionFill::BelowData,
upper_txt );

PeakModel *pmodel = new PeakModel( chart );
pmodel->setNoSpecMeasBacking();
chart->setPeakModel( pmodel );
pmodel->setForeground( hist );
const double peak_sigma = m_input.drf->hasResolutionInfo()
? m_input.drf->peakResolutionSigma(input.gamma_energy)
: 0.25*(lower_upper_energy - upper_lower_energy);
PeakDef peak( input.gamma_energy, peak_sigma, 0.0 );
peak.continuum()->setRange( upper_lower_energy, lower_upper_energy );

peak.continuum()->setType(PeakContinuum::OffsetType::Linear);
peak.continuum()->setParameters( input.gamma_energy, {result.continuum_eqn[0], result.continuum_eqn[1]}, {} );
// We will set the peak area, and user label using text from below, then add to the PeakModel
peak.setUserLabel( "Some Label" );
//peak.setUserLabel( "Cont. Est: " + result.estimated_peak_continuum_counts + " +- " + result.estimated_peak_continuum_uncert );

//Gross Counts in Peak Foreground: 1389.813
WTable *table = new WTable( dialog->contents() );
table->addStyleClass( "MdaCurrieMoreInfoTable" );
Expand Down Expand Up @@ -1012,6 +1075,9 @@ class MdaPeakRow : public WContainerWidget
cell = table->elementAt( table->rowCount() - 1, 1 );
new WText( "[" + lowerstr + ", " + upperstr + "]", cell );
addTooltipToRow( "The activity range estimate, to the " + confidence_level + " confidence level." );

peak.setPeakArea( result.source_counts );
peak.setUserLabel( "Obs: " + nomstr + " " + "[" + lowerstr + ", " + upperstr + "]" );
}else if( result.upper_limit < 0 )
{
// This can happen when there are a lot fewer counts in the peak region than predicted
Expand All @@ -1022,6 +1088,9 @@ class MdaPeakRow : public WContainerWidget
new WText( "Activity &le; 0 " + unitstr, cell );
addTooltipToRow( "Significantly fewer counts in peak region were observed,"
" than predicted by the neighboring regions." );

peak.setPeakArea( 0.0 );
peak.setUserLabel( "Ac. &le; 0 " + unitstr );
}else
{
// We will provide the upper bound on activity.
Expand All @@ -1035,6 +1104,9 @@ class MdaPeakRow : public WContainerWidget

addTooltipToRow( "The upper limit on how much activity could be present, to the "
+ confidence_level + " confidence level." );

peak.setPeakArea( result.upper_limit );
peak.setUserLabel( "Upper Bound: " + mdastr + " @" + confidence_level );
}

break;
Expand All @@ -1049,6 +1121,9 @@ class MdaPeakRow : public WContainerWidget
}
}//switch( m_input.limit_type )

if( hist )
pmodel->addPeaks( {peak} );

// Add a blank row
cell = table->elementAt( table->rowCount(), 0 );
WText *txt = new WText( "&nbsp;", TextFormat::XHTMLText, cell );
Expand All @@ -1060,8 +1135,7 @@ class MdaPeakRow : public WContainerWidget
cell = table->elementAt( table->rowCount() - 1, 1 );
txt = new WText( val, cell );

const double lower_lower_energy = m_input.measurement->gamma_channel_lower( result.first_lower_continuum_channel );
const double lower_upper_energy = m_input.measurement->gamma_channel_lower( result.last_lower_continuum_channel );

snprintf( buffer, sizeof(buffer), "The region above the peak in energy, that is being used"
" to estimate the expected continuum counts in the peak region;"
" corresponds to %.2f to %.2f keV", lower_lower_energy, lower_upper_energy );
Expand All @@ -1082,8 +1156,6 @@ class MdaPeakRow : public WContainerWidget
+ std::to_string(result.last_upper_continuum_channel) + "]";
cell = table->elementAt( table->rowCount() - 1, 1 );
txt = new WText( val, cell );
const double upper_lower_energy = m_input.measurement->gamma_channel_lower( result.first_upper_continuum_channel );
const double upper_upper_energy = m_input.measurement->gamma_channel_lower( result.last_lower_continuum_channel );
snprintf( buffer, sizeof(buffer), "The region above the peak in energy, that is being used"
" to estimate the expected continuum counts in the peak region;"
" corresponds to %.2f to %.2f keV", upper_lower_energy, upper_upper_energy );
Expand Down Expand Up @@ -1211,6 +1283,7 @@ class MdaPeakRow : public WContainerWidget
" per decay of the parent nuclide." );
}catch( std::exception &e )
{
cerr << "Error computing Currie limit information: " << e.what() << endl;
WText *msg = new WText( "Error computing Currie limit information", dialog->contents() );
msg->addStyleClass( "content" );
msg->setInline( false );
Expand Down
4 changes: 3 additions & 1 deletion src/InterSpec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11374,7 +11374,9 @@ size_t InterSpec::addHighlightedEnergyRange( const float lowerEnergy,
const float upperEnergy,
const WColor &color )
{
return m_spectrum->addDecorativeHighlightRegion( lowerEnergy, upperEnergy, color );
return m_spectrum->addDecorativeHighlightRegion( lowerEnergy, upperEnergy, color,
D3SpectrumDisplayDiv::HighlightRegionFill::Full,
"" );
}//void setHighlightedEnergyRange( double lowerEnergy, double upperEnergy )


Expand Down

0 comments on commit 59f6f41

Please sign in to comment.