Commit ef0fb3f9 authored by Simon Heybrock's avatar Simon Heybrock
Browse files

Merge pull request #14935 from mantidproject/13907_instrument_view_peak_intensity

Display relative peak intensities in InstrumentView
parents 565e4c42 80ec5ce4
......@@ -330,10 +330,12 @@ void InstrumentWindow::setSurfaceType(int type) {
int peakLabelPrecision = 6;
bool showPeakRow = true;
bool showPeakLabels = true;
bool showPeakRelativeIntensity = true;
if (surface) {
peakLabelPrecision = surface->getPeakLabelPrecision();
showPeakRow = surface->getShowPeakRowsFlag();
showPeakLabels = surface->getShowPeakLabelsFlag();
showPeakRelativeIntensity = surface->getShowPeakRelativeIntensityFlag();
} else {
QSettings settings;
peakLabelPrecision =
......@@ -341,9 +343,13 @@ void InstrumentWindow::setSurfaceType(int type) {
.toInt();
showPeakRow =
settings.value("Mantid/InstrumentWindow/ShowPeakRows", true).toBool();
showPeakLabels =
settings.value("Mantid/InstrumentWindow/ShowPeakLabels", true)
.toBool();
showPeakLabels = settings.value("Mantid/InstrumentWindow/ShowPeakLabels",
true).toBool();
// By default this is should be off for now.
showPeakRelativeIntensity =
settings.value("Mantid/InstrumentWindow/ShowPeakRelativeIntensities",
false).toBool();
}
// Surface factory
......@@ -409,6 +415,7 @@ void InstrumentWindow::setSurfaceType(int type) {
surface->setPeakLabelPrecision(peakLabelPrecision);
surface->setShowPeakRowsFlag(showPeakRow);
surface->setShowPeakLabelsFlag(showPeakLabels);
surface->setShowPeakRelativeIntensityFlag(showPeakRelativeIntensity);
// set new surface
setSurface(surface);
......@@ -690,6 +697,8 @@ void InstrumentWindow::saveSettings() {
getSurface()->getPeakLabelPrecision());
settings.setValue("ShowPeakRows", getSurface()->getShowPeakRowsFlag());
settings.setValue("ShowPeakLabels", getSurface()->getShowPeakLabelsFlag());
settings.setValue("ShowPeakRelativeIntensities",
getSurface()->getShowPeakRelativeIntensityFlag());
foreach (InstrumentWindowTab *tab, m_tabs) { tab->saveSettings(settings); }
}
settings.endGroup();
......@@ -1056,6 +1065,16 @@ void InstrumentWindow::setShowPeakLabelsFlag(bool on) {
updateInstrumentView();
}
/**
* Enable or disable indication of relative peak intensities
*
* @param on :: True to show, false to hide.
*/
void InstrumentWindow::setShowPeakRelativeIntensity(bool on) {
getSurface()->setShowPeakRelativeIntensityFlag(on);
updateInstrumentView();
}
/**
* Set background color of the instrument display
* @param color :: New background colour.
......
......@@ -166,6 +166,8 @@ public slots:
void setPeakLabelPrecision(int n);
void setShowPeakRowFlag(bool on);
void setShowPeakLabelsFlag(bool on);
void setShowPeakRelativeIntensity(bool on);
/// Enable OpenGL. Slot called from render tab only - doesn't update the checkbox.
void enableGL( bool on );
void updateInfoText();
......
......@@ -567,6 +567,12 @@ QMenu* InstrumentWindowRenderTab::createPeaksMenu()
connect(signalMapper, SIGNAL(mapped(int)), m_instrWindow, SLOT(setPeakLabelPrecision(int)));
menu->addMenu(setPrecision);
QAction *showRelativeIntensity = new QAction("Indicate relative intensity", this);
showRelativeIntensity->setCheckable(true);
showRelativeIntensity->setChecked(settings.value("ShowPeakRelativeIntensities", false).toBool());
connect(showRelativeIntensity, SIGNAL(toggled(bool)), m_instrWindow, SLOT(setShowPeakRelativeIntensity(bool)));
menu->addAction(showRelativeIntensity);
// Clear peaks action
QAction* clearPeaks = new QAction("Clear peaks",this);
connect(clearPeaks,SIGNAL(triggered()),m_instrWindow, SLOT(clearPeakOverlays()));
......
......@@ -3,6 +3,8 @@
#include "MantidAPI/IPeaksWorkspace.h"
#include "MantidAPI/AlgorithmManager.h"
#include "MantidKernel/make_unique.h"
#include <QPainter>
#include <QList>
#include <cmath>
......@@ -14,18 +16,11 @@ QList<PeakMarker2D::Style> PeakOverlay::g_defaultStyles;
/**
* Constructor.
*/
PeakHKL::PeakHKL(PeakMarker2D* m,const QRectF& trect,bool sr):
p(m->origin()),
rect(trect),
//rectTopLeft(m->getLabelRect().topLeft()),
h(m->getH()),
k(m->getK()),
l(m->getL()),
nh(true),
nk(true),
nl(true),
showRows(sr)
{
PeakHKL::PeakHKL(PeakMarker2D *m, const QRectF &trect, bool sr)
: p(m->origin()), rect(trect),
// rectTopLeft(m->getLabelRect().topLeft()),
h(m->getH()), k(m->getK()), l(m->getL()), nh(true), nk(true), nl(true),
showRows(sr) {
rows.append(m->getRow());
}
......@@ -35,22 +30,17 @@ p(m->origin()),
* @param trect :: Transformed marker's label rect
* @return True if labels were combined, false otherwise.
*/
bool PeakHKL::add(PeakMarker2D* marker,const QRectF& trect)
{
if ( !rect.intersects(trect) )
{
bool PeakHKL::add(PeakMarker2D *marker, const QRectF &trect) {
if (!rect.intersects(trect)) {
return false;
}
if (nh && marker->getH() != h)
{
if (nh && marker->getH() != h) {
nh = false;
}
if (nk && marker->getK() != k)
{
if (nk && marker->getK() != k) {
nk = false;
}
if (nl && marker->getL() != l)
{
if (nl && marker->getL() != l) {
nl = false;
}
rows.append(marker->getRow());
......@@ -61,42 +51,33 @@ bool PeakHKL::add(PeakMarker2D* marker,const QRectF& trect)
* @param painter :: QPainter to draw with
* @param prec :: precision
*/
void PeakHKL::draw(QPainter& painter,int prec)
{
void PeakHKL::draw(QPainter &painter, int prec) {
QString label;
if (nh)
{
label += formatNumber( h, prec ) + " ";
}
else
if (nh) {
label += formatNumber(h, prec) + " ";
} else
label = "h ";
if (nk)
{
label += formatNumber( k, prec ) + " ";
}
else
if (nk) {
label += formatNumber(k, prec) + " ";
} else
label += "k ";
if (nl)
{
label += formatNumber( l, prec ) + " ";
}
else
if (nl) {
label += formatNumber(l, prec) + " ";
} else
label += "l";
if (showRows)
{
if (showRows) {
label += " [" + QString::number(rows[0]);
for(int i=1; i < rows.size(); ++i)
{
for (int i = 1; i < rows.size(); ++i) {
label += "," + QString::number(rows[i]);
}
label += "]";
}
painter.drawText(rect.bottomLeft(),label);
painter.drawText(rect.bottomLeft(), label);
}
void PeakHKL::print()const
{
std::cerr << " " << p.x() << ' ' << p.y() << '('<<h<<','<<k<<','<<l<<")("<<nh<<','<<nk<<','<<nl<<')' << std::endl;
void PeakHKL::print() const {
std::cerr << " " << p.x() << ' ' << p.y() << '(' << h << ',' << k << ','
<< l << ")(" << nh << ',' << nk << ',' << nl << ')' << std::endl;
}
/**
......@@ -105,67 +86,129 @@ void PeakHKL::print()const
* @param h :: Value to output.
* @param prec :: Precision as a number of decimal places.
*/
QString PeakHKL::formatNumber(double h, int prec)
{
if (h == 0) return "0";
int max_prec = std::max(prec,int(log10(h)+1));
QString str = QString::number(h,'f',max_prec);
if ( str.contains('.') )
{
while (str.endsWith('0')) str.chop(1);
if (str.endsWith('.')) str.chop(1);
QString PeakHKL::formatNumber(double h, int prec) {
if (h == 0)
return "0";
int max_prec = std::max(prec, int(log10(h) + 1));
QString str = QString::number(h, 'f', max_prec);
if (str.contains('.')) {
while (str.endsWith('0'))
str.chop(1);
if (str.endsWith('.'))
str.chop(1);
}
return str;
}
/// Extract minimum and maximum intensity from peaks workspace for scaling.
void AbstractIntensityScale::setPeaksWorkspace(
const boost::shared_ptr<Mantid::API::IPeaksWorkspace> &pws) {
if (pws) {
int peakCount = pws->getNumberPeaks();
std::vector<double> intensities;
intensities.reserve(peakCount);
for (int i = 0; i < peakCount; ++i) {
intensities.push_back(pws->getPeak(i).getIntensity());
}
return str;
auto minMaxIntensity =
std::minmax_element(intensities.begin(), intensities.end());
m_maxIntensity = *minMaxIntensity.second;
m_minIntensity = *minMaxIntensity.first;
} else {
m_maxIntensity = 0.0;
m_minIntensity = 0.0;
}
}
/// Returns the scaled style by intensity. Only size is changed, the other
/// properties are kept the same. If the max intensity is 0 or less, the
/// style is returned as it is.
PeakMarker2D::Style QualitativeIntensityScale::getScaledMarker(
double intensity, const PeakMarker2D::Style &baseStyle) const {
if (m_maxIntensity <= 0.0) {
return baseStyle;
}
return PeakMarker2D::Style(baseStyle.symbol, baseStyle.color,
3 * getIntensityLevel(intensity) + 1);
}
/**
* Returns the marker size corresponding to the supplied intensity
*
* Intensity levels are specified in m_intensityLevels. The method looks for
* the first element >= than the relative intensity and returns the distance
* from the beginning of list to that element + 1.
*
* For values less than the first element, 0 is returned.
*
* @param intensity :: Absolute intensity.
* @return Intensity level between 0 and the number of intensity levels + 1
*/
int QualitativeIntensityScale::getIntensityLevel(double intensity) const {
auto intensityGreaterThan =
std::lower_bound(m_intensityLevels.cbegin(), m_intensityLevels.cend(),
intensity / m_maxIntensity);
// For weak peaks below first intensity
if (intensityGreaterThan == m_intensityLevels.cend()) {
return 0;
}
return static_cast<int>(
std::distance(m_intensityLevels.cbegin(), intensityGreaterThan)) +
1;
}
/**---------------------------------------------------------------------
* Constructor
*/
PeakOverlay::PeakOverlay(UnwrappedSurface* surface, boost::shared_ptr<Mantid::API::IPeaksWorkspace> pws):
Shape2DCollection(),
m_peaksWorkspace(pws),
m_surface(surface),
m_precision(6),
m_showRows(true),
m_showLabels(true)
{
if (g_defaultStyles.isEmpty())
{
g_defaultStyles << PeakMarker2D::Style(PeakMarker2D::Circle,Qt::red);
g_defaultStyles << PeakMarker2D::Style(PeakMarker2D::Diamond,Qt::green);
g_defaultStyles << PeakMarker2D::Style(PeakMarker2D::Square,Qt::magenta);
PeakOverlay::PeakOverlay(UnwrappedSurface *surface,
boost::shared_ptr<Mantid::API::IPeaksWorkspace> pws)
: Shape2DCollection(), m_peaksWorkspace(pws), m_surface(surface),
m_precision(6), m_showRows(true), m_showLabels(true),
m_peakIntensityScale(
Mantid::Kernel::make_unique<QualitativeIntensityScale>(pws)) {
if (g_defaultStyles.isEmpty()) {
g_defaultStyles << PeakMarker2D::Style(PeakMarker2D::Circle, Qt::red);
g_defaultStyles << PeakMarker2D::Style(PeakMarker2D::Diamond, Qt::green);
g_defaultStyles << PeakMarker2D::Style(PeakMarker2D::Square, Qt::magenta);
}
observeAfterReplace();
}
/**---------------------------------------------------------------------
* Overridden virtual function to remove peaks from the workspace along with
* Overridden virtual function to remove peaks from the workspace along with
* the shapes.
* @param shapeList :: Shapes to remove.
*/
void PeakOverlay::removeShapes(const QList<Shape2D*>& shapeList)
{
void PeakOverlay::removeShapes(const QList<Shape2D *> &shapeList) {
// vectors of rows to delete from the peaks workspace.
std::vector<size_t> rows;
foreach(Shape2D* shape, shapeList)
{
PeakMarker2D* marker = dynamic_cast<PeakMarker2D*>(shape);
if ( !marker ) throw std::logic_error("Wrong shape type found.");
rows.push_back( static_cast<size_t>( marker->getRow() ) );
foreach (Shape2D *shape, shapeList) {
PeakMarker2D *marker = dynamic_cast<PeakMarker2D *>(shape);
if (!marker)
throw std::logic_error("Wrong shape type found.");
rows.push_back(static_cast<size_t>(marker->getRow()));
}
// Run the DeleteTableRows algorithm to delete the peak.
auto alg = Mantid::API::AlgorithmManager::Instance().create("DeleteTableRows",-1);
auto alg =
Mantid::API::AlgorithmManager::Instance().create("DeleteTableRows", -1);
alg->setPropertyValue("TableWorkspace", m_peaksWorkspace->name());
alg->setProperty("Rows",rows);
alg->setProperty("Rows", rows);
emit executeAlgorithm(alg);
}
/**---------------------------------------------------------------------
* Not implemented yet.
*/
void PeakOverlay::clear()
{
void PeakOverlay::clear() {
Shape2DCollection::clear();
m_det2marker.clear();
}
......@@ -174,10 +217,9 @@ void PeakOverlay::clear()
* Add new marker to the overlay.
* @param m :: Pointer to the new marker
*/
void PeakOverlay::addMarker(PeakMarker2D* m)
{
addShape(m,false);
m_det2marker.insert(m->getDetectorID(),m);
void PeakOverlay::addMarker(PeakMarker2D *m) {
addShape(m, false);
m_det2marker.insert(m->getDetectorID(), m);
}
/**---------------------------------------------------------------------
......@@ -186,23 +228,25 @@ void PeakOverlay::addMarker(PeakMarker2D* m)
* prior calling this method.
* @param style :: A style of drawing the markers.
*/
void PeakOverlay::createMarkers(const PeakMarker2D::Style& style)
{
void PeakOverlay::createMarkers(const PeakMarker2D::Style &style) {
int nPeaks = getNumberPeaks();
this->clear();
for(int i = 0; i < nPeaks; ++i)
{
Mantid::Geometry::IPeak& peak = getPeak(i);
const Mantid::Kernel::V3D & pos = peak.getDetPos();
for (int i = 0; i < nPeaks; ++i) {
Mantid::Geometry::IPeak &peak = getPeak(i);
const Mantid::Kernel::V3D &pos = peak.getDetPos();
// Project the peak (detector) position onto u,v coords
double u,v, uscale, vscale;
m_surface->project(pos, u,v, uscale, vscale );
double u, v, uscale, vscale;
m_surface->project(pos, u, v, uscale, vscale);
// Create a peak marker at this position
PeakMarker2D* r = new PeakMarker2D(*this,u,v,style);
r->setPeak(peak,i);
PeakMarker2D *r = new PeakMarker2D(
*this, u, v,
m_peakIntensityScale->getScaledMarker(peak.getIntensity(), style));
r->setPeak(peak, i);
addMarker(r);
}
deselectAll();
}
......@@ -210,27 +254,28 @@ void PeakOverlay::createMarkers(const PeakMarker2D::Style& style)
* Draw peaks on screen.
* @param painter :: The QPainter to draw with.
*/
void PeakOverlay::draw(QPainter& painter) const
{
void PeakOverlay::draw(QPainter &painter) const {
// Draw symbols
Shape2DCollection::draw(painter);
if ( !m_showLabels ) return;
if (!m_showLabels)
return;
// Sort the labels to avoid overlapping
QColor color(Qt::red);
if ( !m_shapes.isEmpty() )
{
if (!m_shapes.isEmpty()) {
color = m_shapes[0]->getColor();
}
QRectF clipRect(painter.viewport());
m_labels.clear();
foreach(Shape2D* shape,m_shapes)
{
if ( !shape->isVisible() ) continue;
if (!clipRect.contains(m_transform.map(shape->origin()))) continue;
PeakMarker2D* marker = dynamic_cast<PeakMarker2D*>(shape);
if (!marker) continue;
foreach (Shape2D *shape, m_shapes) {
if (!shape->isVisible())
continue;
if (!clipRect.contains(m_transform.map(shape->origin())))
continue;
PeakMarker2D *marker = dynamic_cast<PeakMarker2D *>(shape);
if (!marker)
continue;
QPointF p0 = marker->origin();
QPointF p1 = m_transform.map(p0);
......@@ -242,26 +287,23 @@ void PeakOverlay::draw(QPainter& painter) const
bool overlap = false;
// if current label overlaps with another
// combine them substituting differing numbers with letter 'h','k', or 'l'
for(int i = 0; i < m_labels.size(); ++i)
{
PeakHKL& hkl = m_labels[i];
overlap = hkl.add(marker,rect);
if ( overlap ) break;
for (int i = 0; i < m_labels.size(); ++i) {
PeakHKL &hkl = m_labels[i];
overlap = hkl.add(marker, rect);
if (overlap)
break;
}
if (!overlap)
{
PeakHKL hkl(marker,rect,m_showRows);
if (!overlap) {
PeakHKL hkl(marker, rect, m_showRows);
m_labels.append(hkl);
}
}
//std::cerr << m_labels.size() << " labels " << color.red() << ' ' << color.green() << ' ' << color.blue() << "\n";
painter.setPen(color);
for(int i = 0; i < m_labels.size(); ++i)
{
PeakHKL& hkl = m_labels[i];
hkl.draw(painter,m_precision);
//hkl.print();
for (int i = 0; i < m_labels.size(); ++i) {
PeakHKL &hkl = m_labels[i];
hkl.draw(painter, m_precision);
}
}
......@@ -270,16 +312,14 @@ void PeakOverlay::draw(QPainter& painter) const
* @param detID :: A detector ID for which markers are to be returned.
* @return :: A list of zero ot more markers.
*/
QList<PeakMarker2D*> PeakOverlay::getMarkersWithID(int detID)const
{
QList<PeakMarker2D *> PeakOverlay::getMarkersWithID(int detID) const {
return m_det2marker.values(detID);
}
/**---------------------------------------------------------------------
* Return the total number of peaks.
*/
int PeakOverlay::getNumberPeaks()const
{
int PeakOverlay::getNumberPeaks() const {
return m_peaksWorkspace->getNumberPeaks();
}
......@@ -288,36 +328,65 @@ int PeakOverlay::getNumberPeaks()const
* @param i :: Peak index.
* @return A reference to the peak.
*/
Mantid::Geometry::IPeak& PeakOverlay::getPeak(int i)
{
Mantid::Geometry::IPeak &PeakOverlay::getPeak(int i) {
return m_peaksWorkspace->getPeak(i);
}
/// Sets the scaler that is used to determine the size of peak markers.
void PeakOverlay::setShowRelativeIntensityFlag(bool yes) {
if (yes) {
m_peakIntensityScale =
Mantid::Kernel::make_unique<QualitativeIntensityScale>(
m_peaksWorkspace);
} else {
m_peakIntensityScale =
Mantid::Kernel::make_unique<DefaultIntensityScale>(m_peaksWorkspace);
}
recreateMarkers(getCurrentStyle());
}
/// Returns the current style or the default style is no markers are present.
PeakMarker2D::Style PeakOverlay::getCurrentStyle() const {
auto baseStyle = getDefaultStyle(0);
if (isEmpty()) {
return baseStyle;
}
auto currentStyle = m_det2marker.begin().value()->getStyle();
return PeakMarker2D::Style(currentStyle.symbol, currentStyle.color,
baseStyle.size);
}
/** ---------------------------------------------------------------------
* Handler of the AfterReplace notifications. Updates the markers.
* @param wsName :: The name of the modified workspace.
* @param ws :: The shared pointer to the modified workspace.
*/
void PeakOverlay::afterReplaceHandle(const std::string& wsName,
const Mantid::API::Workspace_sptr ws)
{
void PeakOverlay::afterReplaceHandle(const std::string &wsName,
const Mantid::API::Workspace_sptr ws) {
Q_UNUSED(wsName);
auto peaksWS = boost::dynamic_pointer_cast<Mantid::API::IPeaksWorkspace>( ws );
if ( peaksWS && peaksWS == m_peaksWorkspace && m_surface )
{
auto style = isEmpty() ? getDefaultStyle(0) : m_det2marker.begin().value()->getStyle();
clear();
createMarkers( style );
m_surface->requestRedraw(true);
auto peaksWS = boost::dynamic_pointer_cast<Mantid::API::IPeaksWorkspace>(ws);
if (peaksWS && peaksWS == m_peaksWorkspace && m_surface) {
m_peakIntensityScale->setPeaksWorkspace(peaksWS);
recreateMarkers(getCurrentStyle());
}
}
void PeakOverlay::recreateMarkers(const PeakMarker2D::Style &style) {
clear();
createMarkers(style);
m_surface->requestRedraw(true);
}
/** ---------------------------------------------------------------------
* Return a default style for creating markers by index.